winebuild: Add a common helper to find an imported dll.
[wine.git] / dlls / dwrite / analyzer.c
blob29557a686d9334fc67663550a140fa35731d0b21
1 /*
2 * Text analyzer
4 * Copyright 2011 Aric Stewart for CodeWeavers
5 * Copyright 2012, 2014 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include <math.h>
26 #include "dwrite_private.h"
27 #include "scripts.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
31 extern const unsigned short wine_linebreak_table[] DECLSPEC_HIDDEN;
32 extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN;
34 struct dwritescript_properties {
35 DWRITE_SCRIPT_PROPERTIES props;
36 UINT32 scripttag; /* OpenType script tag */
37 UINT32 scriptalttag; /* Version 2 tag, 0 if not defined */
38 BOOL is_complex;
39 const struct scriptshaping_ops *ops;
42 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
44 /* NOTE: keep this array synced with script ids from scripts.h */
45 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
46 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
47 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
48 { /* Ahom */ { 0x6d6f6841, 338, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
49 { /* Hluw */ { 0x77756c48, 80, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE },
51 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
52 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
53 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
54 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
55 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
56 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
57 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE },
58 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
59 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
60 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE },
61 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
62 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
63 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE },
64 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
65 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
66 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('c','a','k','m') },
67 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
68 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE },
69 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
70 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
71 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
72 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
73 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE },
74 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE },
75 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
76 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
77 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
78 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE },
79 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
80 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
81 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
82 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
83 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
84 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE },
85 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE },
86 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
87 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE },
88 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
89 { /* Hatr */ { 0x72746148, 127, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
90 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE },
91 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
92 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
93 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
94 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
95 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
96 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
97 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE },
98 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
99 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
100 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
101 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE },
102 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
103 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
104 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE },
105 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE, &latn_shaping_ops },
106 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
107 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
108 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
109 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
110 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
111 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
112 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
113 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE },
115 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
116 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
117 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
118 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
119 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','c') },
120 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','o') },
121 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
122 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
123 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE },
124 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
125 { /* Mult */ { 0x746c754d, 323, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
126 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE },
127 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
128 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE },
129 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE },
130 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE },
131 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
132 { /* Hung */ { 0x676e7548, 176, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
133 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
134 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
135 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
136 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE },
137 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
138 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
139 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE },
140 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE },
141 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
142 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
143 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
144 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE },
145 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
146 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
147 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
148 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE },
149 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
150 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
151 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','h','r','d') },
152 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
153 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
154 { /* Sgnw */ { 0x776e6753, 95, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
155 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE },
156 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','o','r','a') },
157 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
158 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
159 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE },
160 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
161 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
162 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE },
163 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
164 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
165 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('t','a','k','r') },
166 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE },
167 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE },
168 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE },
169 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE },
170 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE },
171 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE },
172 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
173 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
174 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE },
175 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
176 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE }
178 #undef _OT
180 const char *debugstr_sa_script(UINT16 script)
182 return script < Script_LastId ? debugstr_an((char*)&dwritescripts_properties[script].props.isoScriptCode, 4): "not defined";
185 /* system font falback configuration */
186 static const WCHAR meiryoW[] = {'M','e','i','r','y','o',0};
188 struct fallback_mapping {
189 DWRITE_UNICODE_RANGE range;
190 const WCHAR *family;
193 static const struct fallback_mapping fontfallback_neutral_data[] = {
194 { { 0x3000, 0x30ff }, meiryoW }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
195 { { 0x31f0, 0x31ff }, meiryoW }, /* Katakana Phonetic Extensions */
196 { { 0x4e00, 0x9fff }, meiryoW }, /* CJK Unified Ideographs */
199 struct dwrite_fontfallback {
200 IDWriteFontFallback IDWriteFontFallback_iface;
201 IDWriteFactory2 *factory;
202 IDWriteFontCollection *systemcollection;
203 const struct fallback_mapping *mappings;
204 UINT32 count;
207 struct dwrite_numbersubstitution {
208 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
209 LONG ref;
211 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
212 WCHAR *locale;
213 BOOL ignore_user_override;
216 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
218 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
221 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback(IDWriteFontFallback *iface)
223 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback_iface);
226 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
228 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
229 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
230 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
231 return ch;
233 return 0;
236 static inline UINT16 get_char_script(WCHAR c)
238 return get_table_entry(wine_scripts_table, c);
241 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
243 DWRITE_SCRIPT_ANALYSIS sa;
245 sa.script = get_char_script(c);
246 sa.shapes = iscntrlW(c) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
247 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
248 return sa;
251 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
253 DWRITE_SCRIPT_ANALYSIS sa;
254 UINT32 pos, i, seq_length;
256 if (!length) return S_OK;
258 sa = get_char_sa(*text);
260 pos = position;
261 seq_length = 1;
263 for (i = 1; i < length; i++)
265 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
267 /* Unknown type is ignored when preceded or followed by another script */
268 switch (sa.script) {
269 case Script_Unknown:
270 sa.script = cur_sa.script;
271 break;
272 case Script_Common:
273 if (cur_sa.script == Script_Unknown)
274 cur_sa.script = sa.script;
275 else if (cur_sa.script != Script_Common)
276 sa.script = cur_sa.script;
277 break;
278 default:
279 if (cur_sa.script == Script_Unknown || cur_sa.script == Script_Common)
280 cur_sa.script = sa.script;
283 /* this is a length of a sequence to be reported next */
284 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
285 seq_length++;
286 else {
287 HRESULT hr;
289 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
290 if (FAILED(hr)) return hr;
291 pos = position + i;
292 seq_length = 1;
293 sa = cur_sa;
297 /* one char length case or normal completion call */
298 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
301 struct linebreaking_state {
302 DWRITE_LINE_BREAKPOINT *breakpoints;
303 UINT32 count;
306 enum BreakConditionLocation {
307 BreakConditionBefore,
308 BreakConditionAfter
311 enum linebreaking_classes {
312 b_BK = 1,
313 b_CR,
314 b_LF,
315 b_CM,
316 b_SG,
317 b_GL,
318 b_CB,
319 b_SP,
320 b_ZW,
321 b_NL,
322 b_WJ,
323 b_JL,
324 b_JV,
325 b_JT,
326 b_H2,
327 b_H3,
328 b_XX,
329 b_OP,
330 b_CL,
331 b_CP,
332 b_QU,
333 b_NS,
334 b_EX,
335 b_SY,
336 b_IS,
337 b_PR,
338 b_PO,
339 b_NU,
340 b_AL,
341 b_ID,
342 b_IN,
343 b_HY,
344 b_BB,
345 b_BA,
346 b_SA,
347 b_AI,
348 b_B2,
349 b_HL,
350 b_CJ,
351 b_RI
354 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
355 set to "can break" and could only be changed once. */
356 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
357 struct linebreaking_state *state)
359 if (location == BreakConditionBefore) {
360 if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK)
361 return;
362 state->breakpoints[pos].breakConditionBefore = condition;
363 if (pos > 0)
364 state->breakpoints[pos-1].breakConditionAfter = condition;
366 else {
367 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
368 return;
369 state->breakpoints[pos].breakConditionAfter = condition;
370 if (pos + 1 < state->count)
371 state->breakpoints[pos+1].breakConditionBefore = condition;
375 BOOL lb_is_newline_char(WCHAR ch)
377 short c = get_table_entry(wine_linebreak_table, ch);
378 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
381 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
383 struct linebreaking_state state;
384 short *break_class;
385 int i, j;
387 break_class = heap_alloc(count*sizeof(short));
388 if (!break_class)
389 return E_OUTOFMEMORY;
391 state.breakpoints = breakpoints;
392 state.count = count;
394 /* LB31 - allow breaks everywhere. It will be overridden if needed as
395 other rules dictate. */
396 for (i = 0; i < count; i++)
398 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
400 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK;
401 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK;
402 breakpoints[i].isWhitespace = !!isspaceW(text[i]);
403 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
404 breakpoints[i].padding = 0;
406 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
407 switch (break_class[i])
409 case b_AI:
410 case b_SA:
411 case b_SG:
412 case b_XX:
413 break_class[i] = b_AL;
414 break;
415 case b_CJ:
416 break_class[i] = b_NS;
417 break;
421 /* LB2 - never break at the start */
422 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
423 /* LB3 - always break at the end. This one is ignored. */
425 for (i = 0; i < count; i++)
427 switch (break_class[i])
429 /* LB4 - LB6 */
430 case b_CR:
431 /* LB5 - don't break CR x LF */
432 if (i < count-1 && break_class[i+1] == b_LF)
434 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
435 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
436 break;
438 case b_LF:
439 case b_NL:
440 case b_BK:
441 /* LB4 - LB5 - always break after hard breaks */
442 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
443 /* LB6 - do not break before hard breaks */
444 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
445 break;
446 /* LB7 - do not break before spaces */
447 case b_SP:
448 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
449 break;
450 case b_ZW:
451 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
452 /* LB8 - break before character after zero-width space, skip spaces in-between */
453 while (i < count-1 && break_class[i+1] == b_SP)
454 i++;
455 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
456 break;
460 /* LB9 - LB10 */
461 for (i = 0; i < count; i++)
463 if (break_class[i] == b_CM)
465 if (i > 0)
467 switch (break_class[i-1])
469 case b_SP:
470 case b_BK:
471 case b_CR:
472 case b_LF:
473 case b_NL:
474 case b_ZW:
475 break_class[i] = b_AL;
476 break;
477 default:
478 break_class[i] = break_class[i-1];
481 else break_class[i] = b_AL;
485 for (i = 0; i < count; i++)
487 switch (break_class[i])
489 /* LB11 - don't break before and after word joiner */
490 case b_WJ:
491 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
492 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
493 break;
494 /* LB12 - don't break after glue */
495 case b_GL:
496 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
497 /* LB12a */
498 if (i > 0)
500 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
501 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
503 break;
504 /* LB13 */
505 case b_CL:
506 case b_CP:
507 case b_EX:
508 case b_IS:
509 case b_SY:
510 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
511 break;
512 /* LB14 */
513 case b_OP:
514 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
515 while (i < count-1 && break_class[i+1] == b_SP) {
516 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
517 i++;
519 break;
520 /* LB15 */
521 case b_QU:
522 j = i+1;
523 while (j < count-1 && break_class[j] == b_SP)
524 j++;
525 if (break_class[j] == b_OP)
526 for (; j > i; j--)
527 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
528 break;
529 /* LB16 */
530 case b_NS:
531 j = i-1;
532 while(j > 0 && break_class[j] == b_SP)
533 j--;
534 if (break_class[j] == b_CL || break_class[j] == b_CP)
535 for (j++; j <= i; j++)
536 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
537 break;
538 /* LB17 */
539 case b_B2:
540 j = i+1;
541 while (j < count && break_class[j] == b_SP)
542 j++;
543 if (break_class[j] == b_B2)
544 for (; j > i; j--)
545 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
546 break;
550 for (i = 0; i < count; i++)
552 switch(break_class[i])
554 /* LB18 - break is allowed after space */
555 case b_SP:
556 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
557 break;
558 /* LB19 - don't break before or after quotation mark */
559 case b_QU:
560 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
561 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
562 break;
563 /* LB20 */
564 case b_CB:
565 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
566 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
567 break;
568 /* LB21 */
569 case b_BA:
570 case b_HY:
571 case b_NS:
572 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
573 break;
574 case b_BB:
575 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
576 break;
577 /* LB21a, LB21b */
578 case b_HL:
579 /* LB21a */
580 if (i < count-1)
581 switch (break_class[i+1])
583 case b_HY:
584 case b_BA:
585 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
587 /* LB21b */
588 if (i > 0 && break_class[i-1] == b_SY)
589 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
590 break;
591 /* LB22 */
592 case b_IN:
593 if (i > 0)
595 switch (break_class[i-1])
597 case b_AL:
598 case b_HL:
599 case b_EX:
600 case b_ID:
601 case b_IN:
602 case b_NU:
603 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
606 break;
609 if (i < count-1)
611 /* LB23 */
612 if ((break_class[i] == b_ID && break_class[i+1] == b_PO) ||
613 (break_class[i] == b_AL && break_class[i+1] == b_NU) ||
614 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
615 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
616 (break_class[i] == b_NU && break_class[i+1] == b_HL))
617 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
618 /* LB24 */
619 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
620 (break_class[i] == b_PR && break_class[i+1] == b_AL) ||
621 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
622 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
623 (break_class[i] == b_PO && break_class[i+1] == b_HL))
624 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
626 /* LB25 */
627 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
628 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
629 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
630 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
631 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
632 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
633 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
634 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
635 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
636 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
637 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
638 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
639 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
640 (break_class[i] == b_SY && break_class[i+1] == b_NU))
641 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
643 /* LB26 */
644 if (break_class[i] == b_JL)
646 switch (break_class[i+1])
648 case b_JL:
649 case b_JV:
650 case b_H2:
651 case b_H3:
652 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
655 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
656 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
657 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
658 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
659 break_class[i+1] == b_JT)
660 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
662 /* LB27 */
663 switch (break_class[i])
665 case b_JL:
666 case b_JV:
667 case b_JT:
668 case b_H2:
669 case b_H3:
670 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
671 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
673 if (break_class[i] == b_PO)
675 switch (break_class[i+1])
677 case b_JL:
678 case b_JV:
679 case b_JT:
680 case b_H2:
681 case b_H3:
682 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
686 /* LB28 */
687 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
688 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
689 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
690 (break_class[i] == b_HL && break_class[i+1] == b_HL))
691 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
693 /* LB29 */
694 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
695 (break_class[i] == b_IS && break_class[i+1] == b_HL))
696 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
698 /* LB30 */
699 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
700 break_class[i+1] == b_OP)
701 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
702 if (break_class[i] == b_CP &&
703 (break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU))
704 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
706 /* LB30a */
707 if (break_class[i] == b_RI && break_class[i+1] == b_RI)
708 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
712 heap_free(break_class);
713 return S_OK;
716 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
718 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
720 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
721 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
722 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
723 IsEqualIID(riid, &IID_IUnknown))
725 *obj = iface;
726 return S_OK;
729 *obj = NULL;
730 return E_NOINTERFACE;
733 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
735 return 2;
738 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
740 return 1;
743 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
744 data after a first request. */
745 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
747 HRESULT hr;
748 UINT32 len;
750 *buff = NULL;
751 *text = NULL;
752 len = 0;
753 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
754 if (FAILED(hr)) return hr;
756 if (len < length) {
757 UINT32 read;
759 *buff = heap_alloc(length*sizeof(WCHAR));
760 if (!*buff)
761 return E_OUTOFMEMORY;
762 memcpy(*buff, *text, len*sizeof(WCHAR));
763 read = len;
765 while (read < length && *text) {
766 *text = NULL;
767 len = 0;
768 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
769 if (FAILED(hr)) {
770 heap_free(*buff);
771 return hr;
773 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
774 read += len;
777 *text = *buff;
780 return hr;
783 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
784 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
786 WCHAR *buff = NULL;
787 const WCHAR *text;
788 HRESULT hr;
790 TRACE("(%p %u %u %p)\n", source, position, length, sink);
792 if (length == 0)
793 return S_OK;
795 hr = get_text_source_ptr(source, position, length, &text, &buff);
796 if (FAILED(hr))
797 return hr;
799 hr = analyze_script(text, position, length, sink);
800 heap_free(buff);
802 return hr;
805 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
806 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
808 UINT8 *levels = NULL, *explicit = NULL;
809 UINT8 baselevel, level, explicit_level;
810 UINT32 pos, i, seq_length;
811 WCHAR *buff = NULL;
812 const WCHAR *text;
813 HRESULT hr;
815 TRACE("(%p %u %u %p)\n", source, position, length, sink);
817 if (!length)
818 return S_OK;
820 hr = get_text_source_ptr(source, position, length, &text, &buff);
821 if (FAILED(hr))
822 return hr;
824 levels = heap_alloc(length*sizeof(*levels));
825 explicit = heap_alloc(length*sizeof(*explicit));
827 if (!levels || !explicit) {
828 hr = E_OUTOFMEMORY;
829 goto done;
832 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
833 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
834 if (FAILED(hr))
835 goto done;
837 level = levels[0];
838 explicit_level = explicit[0];
839 pos = position;
840 seq_length = 1;
842 for (i = 1; i < length; i++) {
843 if (levels[i] == level && explicit[i] == explicit_level)
844 seq_length++;
845 else {
846 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
847 if (FAILED(hr))
848 goto done;
850 pos += seq_length;
851 seq_length = 1;
852 level = levels[i];
853 explicit_level = explicit[i];
856 /* one char length case or normal completion call */
857 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
859 done:
860 heap_free(explicit);
861 heap_free(levels);
862 heap_free(buff);
864 return hr;
867 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
868 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
870 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
871 return S_OK;
874 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
875 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
877 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
878 WCHAR *buff = NULL;
879 const WCHAR *text;
880 HRESULT hr;
881 UINT32 len;
883 TRACE("(%p %u %u %p)\n", source, position, length, sink);
885 if (length == 0)
886 return S_OK;
888 /* get some, check for length */
889 text = NULL;
890 len = 0;
891 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
892 if (FAILED(hr)) return hr;
894 if (len < length) {
895 UINT32 read;
897 buff = heap_alloc(length*sizeof(WCHAR));
898 if (!buff)
899 return E_OUTOFMEMORY;
900 memcpy(buff, text, len*sizeof(WCHAR));
901 read = len;
903 while (read < length && text) {
904 text = NULL;
905 len = 0;
906 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
907 if (FAILED(hr))
908 goto done;
909 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
910 read += len;
913 text = buff;
916 breakpoints = heap_alloc(length*sizeof(*breakpoints));
917 if (!breakpoints) {
918 hr = E_OUTOFMEMORY;
919 goto done;
922 hr = analyze_linebreaks(text, length, breakpoints);
923 if (FAILED(hr))
924 goto done;
926 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
928 done:
929 heap_free(breakpoints);
930 heap_free(buff);
932 return hr;
935 static UINT32 get_opentype_language(const WCHAR *locale)
937 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
939 if (locale) {
940 WCHAR tag[5];
941 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
942 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
945 return language;
948 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
949 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
950 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
951 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
952 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
953 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
954 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
956 const struct dwritescript_properties *scriptprops;
957 struct scriptshaping_context context;
958 struct scriptshaping_cache *cache = NULL;
959 BOOL update_cluster, need_vertical;
960 IDWriteFontFace1 *fontface1;
961 WCHAR *string;
962 UINT32 i, g;
963 HRESULT hr = S_OK;
964 UINT16 script;
966 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
967 length, fontface, is_sideways, is_rtl, analysis, debugstr_w(locale), substitution, features, feature_range_len,
968 feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices, glyph_props, actual_glyph_count);
970 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
972 if (max_glyph_count < length)
973 return E_NOT_SUFFICIENT_BUFFER;
975 if (substitution)
976 FIXME("number substitution is not supported.\n");
978 for (i = 0; i < length; i++) {
979 /* FIXME: set to better values */
980 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
981 glyph_props[i].isClusterStart = 1;
982 glyph_props[i].isDiacritic = 0;
983 glyph_props[i].isZeroWidthSpace = 0;
984 glyph_props[i].reserved = 0;
986 /* FIXME: have the shaping engine set this */
987 text_props[i].isShapedAlone = 0;
988 text_props[i].reserved = 0;
990 clustermap[i] = i;
993 for (; i < max_glyph_count; i++) {
994 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
995 glyph_props[i].isClusterStart = 0;
996 glyph_props[i].isDiacritic = 0;
997 glyph_props[i].isZeroWidthSpace = 0;
998 glyph_props[i].reserved = 0;
1001 string = heap_alloc(sizeof(WCHAR)*length);
1002 if (!string)
1003 return E_OUTOFMEMORY;
1005 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1006 if (FAILED(hr))
1007 WARN("failed to get IDWriteFontFace1\n");
1009 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
1011 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
1012 UINT32 codepoint;
1014 if (!update_cluster) {
1015 codepoint = decode_surrogate_pair(text, i, length);
1016 if (!codepoint) {
1017 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
1018 string[i] = codepoint;
1020 else {
1021 string[i] = text[i];
1022 string[i+1] = text[i+1];
1023 update_cluster = TRUE;
1026 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
1027 if (FAILED(hr))
1028 goto done;
1030 if (need_vertical) {
1031 UINT16 vertical;
1033 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
1034 if (hr == S_OK)
1035 glyph_indices[g] = vertical;
1038 g++;
1040 else {
1041 INT32 k;
1043 update_cluster = FALSE;
1044 /* mark surrogate halves with same cluster */
1045 clustermap[i] = clustermap[i-1];
1046 /* update following clusters */
1047 for (k = i + 1; k >= 0 && k < length; k++)
1048 clustermap[k]--;
1051 *actual_glyph_count = g;
1053 hr = create_scriptshaping_cache(fontface, &cache);
1054 if (FAILED(hr))
1055 goto done;
1057 context.cache = cache;
1058 context.text = text;
1059 context.length = length;
1060 context.is_rtl = is_rtl;
1061 context.max_glyph_count = max_glyph_count;
1062 context.language_tag = get_opentype_language(locale);
1064 scriptprops = &dwritescripts_properties[script];
1065 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
1066 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
1067 if (FAILED(hr))
1068 goto done;
1071 /* FIXME: apply default features */
1073 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1074 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1075 else
1076 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1078 done:
1079 if (fontface1)
1080 IDWriteFontFace1_Release(fontface1);
1081 release_scriptshaping_cache(cache);
1082 heap_free(string);
1084 return hr;
1087 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1088 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1089 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1090 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1091 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1092 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1094 DWRITE_FONT_METRICS metrics;
1095 IDWriteFontFace1 *fontface1;
1096 HRESULT hr;
1097 UINT32 i;
1099 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %p %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1100 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1101 is_rtl, analysis, debugstr_w(locale), features, feature_range_len, feature_ranges, advances, offsets);
1103 if (glyph_count == 0)
1104 return S_OK;
1106 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1107 if (FAILED(hr)) {
1108 WARN("failed to get IDWriteFontFace1.\n");
1109 return hr;
1112 IDWriteFontFace_GetMetrics(fontface, &metrics);
1113 for (i = 0; i < glyph_count; i++) {
1114 if (glyph_props[i].isZeroWidthSpace)
1115 advances[i] = 0.0f;
1116 else {
1117 INT32 a;
1119 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1120 if (FAILED(hr))
1121 a = 0;
1122 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1124 offsets[i].advanceOffset = 0.0f;
1125 offsets[i].ascenderOffset = 0.0f;
1128 /* FIXME: actually apply features */
1130 IDWriteFontFace1_Release(fontface1);
1131 return S_OK;
1134 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1135 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1136 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1137 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1138 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1139 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1140 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1142 DWRITE_FONT_METRICS metrics;
1143 IDWriteFontFace1 *fontface1;
1144 HRESULT hr;
1145 UINT32 i;
1147 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %p %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1148 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1149 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
1150 feature_ranges, advances, offsets);
1152 if (glyph_count == 0)
1153 return S_OK;
1155 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1156 if (FAILED(hr)) {
1157 WARN("failed to get IDWriteFontFace1.\n");
1158 return hr;
1161 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1162 if (FAILED(hr)) {
1163 IDWriteFontFace1_Release(fontface1);
1164 WARN("failed to get compat metrics, 0x%08x\n", hr);
1165 return hr;
1167 for (i = 0; i < glyph_count; i++) {
1168 INT32 a;
1170 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1171 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1172 if (FAILED(hr))
1173 advances[i] = 0.0f;
1174 else
1175 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1176 offsets[i].advanceOffset = 0.0f;
1177 offsets[i].ascenderOffset = 0.0f;
1180 /* FIXME: actually apply features */
1182 IDWriteFontFace1_Release(fontface1);
1183 return S_OK;
1186 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1188 FLOAT advance = 0.0f;
1189 for (; start < end; start++)
1190 advance += advances[start];
1191 return advance;
1194 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1195 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1196 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1198 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1199 FLOAT advance = advances[g];
1200 FLOAT origin = 0.0f;
1202 if (props[g].isZeroWidthSpace) {
1203 modified_advances[g] = advances[g];
1204 modified_offsets[g] = offsets[g];
1205 return;
1208 /* first apply negative spacing and check if we hit minimum width */
1209 if (leading_spacing < 0.0f) {
1210 advance += leading_spacing;
1211 origin -= leading_spacing;
1213 if (trailing_spacing < 0.0f)
1214 advance += trailing_spacing;
1216 if (advance < min_advance_width) {
1217 FLOAT half = (min_advance_width - advance) / 2.0f;
1219 if (!reduced)
1220 origin -= half;
1221 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1222 origin -= half;
1223 else if (leading_spacing < 0.0f)
1224 origin -= min_advance_width - advance;
1226 advance = min_advance_width;
1229 /* now apply positive spacing adjustments */
1230 if (leading_spacing > 0.0f) {
1231 advance += leading_spacing;
1232 origin -= leading_spacing;
1234 if (trailing_spacing > 0.0f)
1235 advance += trailing_spacing;
1237 modified_advances[g] = advance;
1238 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1239 /* ascender is never touched, it's orthogonal to reading direction and is not
1240 affected by advance adjustments */
1241 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1244 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1245 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1246 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1248 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1249 FLOAT advance = get_cluster_advance(advances, start, end);
1250 FLOAT origin = 0.0f;
1251 UINT16 g;
1253 modified_advances[start] = advances[start];
1254 modified_advances[end-1] = advances[end-1];
1256 /* first apply negative spacing and check if we hit minimum width */
1257 if (leading_spacing < 0.0f) {
1258 advance += leading_spacing;
1259 modified_advances[start] += leading_spacing;
1260 origin -= leading_spacing;
1262 if (trailing_spacing < 0.0f) {
1263 advance += trailing_spacing;
1264 modified_advances[end-1] += trailing_spacing;
1267 advance = min_advance_width - advance;
1268 if (advance > 0.0f) {
1269 /* additional spacing is only applied to leading and trailing glyph */
1270 FLOAT half = advance / 2.0f;
1272 if (!reduced) {
1273 origin -= half;
1274 modified_advances[start] += half;
1275 modified_advances[end-1] += half;
1277 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f) {
1278 origin -= half;
1279 modified_advances[start] += half;
1280 modified_advances[end-1] += half;
1282 else if (leading_spacing < 0.0f) {
1283 origin -= advance;
1284 modified_advances[start] += advance;
1286 else
1287 modified_advances[end-1] += advance;
1290 /* now apply positive spacing adjustments */
1291 if (leading_spacing > 0.0f) {
1292 modified_advances[start] += leading_spacing;
1293 origin -= leading_spacing;
1295 if (trailing_spacing > 0.0f)
1296 modified_advances[end-1] += trailing_spacing;
1298 for (g = start; g < end; g++) {
1299 if (g == start) {
1300 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1301 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1303 else if (g == end - 1)
1304 /* trailing glyph offset is not adjusted */
1305 modified_offsets[g] = offsets[g];
1306 else {
1307 /* for all glyphs within a cluster use original advances and offsets */
1308 modified_advances[g] = advances[g];
1309 modified_offsets[g] = offsets[g];
1314 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1316 UINT16 g = clustermap[start];
1317 UINT32 length = 1;
1319 while (start < text_len && clustermap[++start] == g)
1320 length++;
1321 return length;
1324 /* Applies spacing adjustments to clusters.
1326 Adjustments are applied in the following order:
1328 1. Negative adjustments
1330 Leading and trailing spacing could be negative, at this step
1331 only negative ones are actually applied. Leading spacing is only
1332 applied to leading glyph, trailing - to trailing glyph.
1334 2. Minimum advance width
1336 Advances could only be reduced at this point or unchanged. In any
1337 case it's checked if cluster advance width is less than minimum width.
1338 If it's the case advance width is incremented up to minimum value.
1340 Important part is the direction in which this increment is applied;
1341 it depends on direction from which total cluster advance was trimmed
1342 at step 1. So it could be incremented from leading, trailing, or both
1343 sides. When applied to both sides, each side gets half of difference
1344 that brings advance to minimum width.
1346 3. Positive adjustments
1348 After minimum width rule was applied, positive spacing is applied in the same
1349 way as negative one on step 1.
1351 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1352 keeps its position in coordinate system where initial advance width is counted
1353 from 0.
1355 Glyph properties
1357 It's known that isZeroWidthSpace property keeps initial advance from changing.
1359 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1360 with more than one glyph.
1363 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1364 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1365 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1366 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1368 UINT16 start;
1370 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1371 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1373 if (min_advance_width < 0.0f) {
1374 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1375 return E_INVALIDARG;
1378 /* minimum advance is not applied if no adjustments were made */
1379 if (leading_spacing == 0.0f && trailing_spacing == 0.0f) {
1380 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1381 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1382 return S_OK;
1385 for (start = 0; start < len;) {
1386 UINT32 length = get_cluster_length(clustermap, start, len);
1388 if (length == 1) {
1389 UINT32 g = clustermap[start];
1391 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1392 g, advances, offsets, props, modified_advances, modified_offsets);
1394 else {
1395 UINT32 g_start, g_end;
1397 g_start = clustermap[start];
1398 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1400 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1401 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1404 start += length;
1407 return S_OK;
1410 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1411 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1412 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1414 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1415 baseline_coord, exists);
1416 return E_NOTIMPL;
1419 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1420 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1422 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1423 return E_NOTIMPL;
1426 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1427 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1429 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1430 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1433 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1434 DWRITE_SCRIPT_PROPERTIES *props)
1436 TRACE("(%u %p)\n", sa.script, props);
1438 if (sa.script > Script_LastId)
1439 return E_INVALIDARG;
1441 *props = dwritescripts_properties[sa.script].props;
1442 return S_OK;
1445 static inline BOOL is_char_from_simple_script(WCHAR c)
1447 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1448 return FALSE;
1449 else {
1450 UINT16 script = get_char_script(c);
1451 return !dwritescripts_properties[script].is_complex;
1455 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1456 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1458 HRESULT hr = S_OK;
1459 int i;
1461 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1463 *is_simple = FALSE;
1464 *len_read = 0;
1466 if (!face)
1467 return E_INVALIDARG;
1469 if (len == 0) {
1470 *is_simple = TRUE;
1471 return S_OK;
1474 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1475 for (i = 1; i < len && text[i]; i++) {
1476 if (is_char_from_simple_script(text[i])) {
1477 if (!*is_simple)
1478 break;
1480 else
1481 *is_simple = FALSE;
1484 *len_read = i;
1486 /* fetch indices */
1487 if (*is_simple && indices) {
1488 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1489 if (!codepoints)
1490 return E_OUTOFMEMORY;
1492 for (i = 0; i < *len_read; i++)
1493 codepoints[i] = text[i];
1495 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1496 heap_free(codepoints);
1499 return hr;
1502 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1503 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1504 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1506 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1507 debugstr_wn(text, length), clustermap, prop, jo);
1508 return E_NOTIMPL;
1511 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1512 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1513 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1515 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1516 justifiedoffsets);
1517 return E_NOTIMPL;
1520 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1521 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1522 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1523 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1524 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1525 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1527 FIXME("(%p %.2f %u %u %u %u %p %p %p %p %p %p %p %p %p %p %p): stub\n", face, font_em_size, sa.script,
1528 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1529 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1530 return E_NOTIMPL;
1533 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1534 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1536 static const DWRITE_MATRIX transforms[] = {
1537 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1538 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1539 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1540 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1543 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1545 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1546 memset(m, 0, sizeof(*m));
1547 return E_INVALIDARG;
1550 /* for sideways case simply rotate 90 degrees more */
1551 if (is_sideways) {
1552 switch (angle) {
1553 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1554 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1555 break;
1556 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1557 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1558 break;
1559 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1560 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1561 break;
1562 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1563 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1564 break;
1565 default:
1570 *m = transforms[angle];
1572 /* shift components represent transform necessary to get from original point to
1573 rotated one in new coordinate system */
1574 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1575 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1576 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1579 return S_OK;
1582 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1583 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1584 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1586 const struct dwritescript_properties *props;
1587 HRESULT hr = S_OK;
1588 UINT32 language;
1590 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1591 tags);
1593 if (sa.script > Script_LastId)
1594 return E_INVALIDARG;
1596 language = get_opentype_language(locale);
1597 props = &dwritescripts_properties[sa.script];
1598 *actual_tagcount = 0;
1600 if (props->scriptalttag)
1601 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1603 if (*actual_tagcount == 0)
1604 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1606 return hr;
1609 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1610 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1611 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1613 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1614 feature_applies);
1615 return E_NOTIMPL;
1618 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1619 dwritetextanalyzer_QueryInterface,
1620 dwritetextanalyzer_AddRef,
1621 dwritetextanalyzer_Release,
1622 dwritetextanalyzer_AnalyzeScript,
1623 dwritetextanalyzer_AnalyzeBidi,
1624 dwritetextanalyzer_AnalyzeNumberSubstitution,
1625 dwritetextanalyzer_AnalyzeLineBreakpoints,
1626 dwritetextanalyzer_GetGlyphs,
1627 dwritetextanalyzer_GetGlyphPlacements,
1628 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1629 dwritetextanalyzer1_ApplyCharacterSpacing,
1630 dwritetextanalyzer1_GetBaseline,
1631 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1632 dwritetextanalyzer1_GetGlyphOrientationTransform,
1633 dwritetextanalyzer1_GetScriptProperties,
1634 dwritetextanalyzer1_GetTextComplexity,
1635 dwritetextanalyzer1_GetJustificationOpportunities,
1636 dwritetextanalyzer1_JustifyGlyphAdvances,
1637 dwritetextanalyzer1_GetJustifiedGlyphs,
1638 dwritetextanalyzer2_GetGlyphOrientationTransform,
1639 dwritetextanalyzer2_GetTypographicFeatures,
1640 dwritetextanalyzer2_CheckTypographicFeature
1643 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1645 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1647 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1648 return S_OK;
1651 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1653 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1655 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1657 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1658 IsEqualIID(riid, &IID_IUnknown))
1660 *obj = iface;
1661 IDWriteNumberSubstitution_AddRef(iface);
1662 return S_OK;
1665 *obj = NULL;
1667 return E_NOINTERFACE;
1670 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1672 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1673 ULONG ref = InterlockedIncrement(&This->ref);
1674 TRACE("(%p)->(%d)\n", This, ref);
1675 return ref;
1678 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1680 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1681 ULONG ref = InterlockedDecrement(&This->ref);
1683 TRACE("(%p)->(%d)\n", This, ref);
1685 if (!ref) {
1686 heap_free(This->locale);
1687 heap_free(This);
1690 return ref;
1693 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1694 dwritenumbersubstitution_QueryInterface,
1695 dwritenumbersubstitution_AddRef,
1696 dwritenumbersubstitution_Release
1699 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1700 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1702 struct dwrite_numbersubstitution *substitution;
1704 *ret = NULL;
1706 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1707 return E_INVALIDARG;
1709 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1710 return E_INVALIDARG;
1712 substitution = heap_alloc(sizeof(*substitution));
1713 if (!substitution)
1714 return E_OUTOFMEMORY;
1716 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1717 substitution->ref = 1;
1718 substitution->ignore_user_override = ignore_user_override;
1719 substitution->method = method;
1720 substitution->locale = heap_strdupW(locale);
1721 if (locale && !substitution->locale) {
1722 heap_free(substitution);
1723 return E_OUTOFMEMORY;
1726 *ret = &substitution->IDWriteNumberSubstitution_iface;
1727 return S_OK;
1730 /* IDWriteFontFallback */
1731 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback *iface, REFIID riid, void **obj)
1733 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1735 TRACE("(%p)->(%s %p)\n", fallback, debugstr_guid(riid), obj);
1737 if (IsEqualIID(riid, &IID_IDWriteFontFallback) || IsEqualIID(riid, &IID_IUnknown)) {
1738 *obj = iface;
1739 IDWriteFontFallback_AddRef(iface);
1740 return S_OK;
1743 *obj = NULL;
1744 return E_NOINTERFACE;
1747 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback *iface)
1749 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1750 TRACE("(%p)\n", fallback);
1751 return IDWriteFactory2_AddRef(fallback->factory);
1754 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback *iface)
1756 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1757 TRACE("(%p)\n", fallback);
1758 return IDWriteFactory2_Release(fallback->factory);
1761 static int compare_fallback_mapping(const void *a, const void *b)
1763 UINT32 ch = *(UINT32*)a;
1764 struct fallback_mapping *mapping = (struct fallback_mapping*)b;
1766 if (ch > mapping->range.last)
1767 return 1;
1768 else if (ch < mapping->range.first)
1769 return -1;
1770 else
1771 return 0;
1774 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
1776 return bsearch(&ch, fallback->mappings, fallback->count, sizeof(*fallback->mappings),
1777 compare_fallback_mapping);
1780 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
1781 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
1783 IDWriteFontFamily *family;
1784 BOOL exists = FALSE;
1785 HRESULT hr;
1786 UINT32 i;
1788 *font = NULL;
1790 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
1791 if (FAILED(hr))
1792 return hr;
1794 if (!exists)
1795 return E_FAIL;
1797 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
1798 if (FAILED(hr))
1799 return hr;
1801 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
1802 IDWriteFontFamily_Release(family);
1803 return hr;
1806 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
1808 HRESULT hr = S_OK;
1809 UINT32 i;
1811 for (i = 0; i < length; i++) {
1812 UINT16 script = get_char_script(text[i]);
1813 BOOL exists;
1815 if (script == Script_Unknown || script == Script_Common) {
1816 ++*mapped_length;
1817 continue;
1820 /* stop on first unsupported character */
1821 exists = FALSE;
1822 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
1823 if (hr == S_OK && exists)
1824 ++*mapped_length;
1825 else
1826 break;
1829 return hr;
1832 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
1833 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length, IDWriteFont **mapped_font)
1835 const struct fallback_mapping *mapping;
1836 HRESULT hr;
1838 mapping = find_fallback_mapping(fallback, text[0]);
1839 if (!mapping) {
1840 WARN("no mapping for 0x%x\n", text[0]);
1841 return E_FAIL;
1844 /* now let's see what fallback can handle */
1845 hr = create_matching_font(fallback->systemcollection, mapping->family, weight, style, stretch, mapped_font);
1846 if (FAILED(hr)) {
1847 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping->family),
1848 mapping->range.first, mapping->range.last, hr);
1849 return hr;
1852 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
1853 if (FAILED(hr))
1854 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping->family), hr);
1856 if (!*mapped_length) {
1857 IDWriteFont_Release(*mapped_font);
1858 *mapped_font = NULL;
1861 return *mapped_length ? S_OK : E_FAIL;
1864 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
1865 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
1866 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
1867 IDWriteFont **ret_font, FLOAT *scale)
1869 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1870 WCHAR *buff = NULL;
1871 const WCHAR *text;
1872 HRESULT hr;
1874 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
1875 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
1877 *mapped_length = 0;
1878 *scale = 1.0f;
1879 *ret_font = NULL;
1881 if (!source)
1882 return E_INVALIDARG;
1884 if (length == 0)
1885 return S_OK;
1887 if (!basecollection)
1888 basecollection = fallback->systemcollection;
1890 hr = get_text_source_ptr(source, position, length, &text, &buff);
1891 if (FAILED(hr))
1892 goto done;
1894 if (basefamily && *basefamily) {
1895 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
1896 if (FAILED(hr))
1897 goto done;
1899 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
1900 if (FAILED(hr))
1901 goto done;
1904 if (!*mapped_length) {
1905 IDWriteFont *mapped_font;
1907 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
1908 if (FAILED(hr)) {
1909 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
1910 if (*ret_font) {
1911 *mapped_length = length;
1912 hr = S_OK;
1915 else {
1916 if (*ret_font)
1917 IDWriteFont_Release(*ret_font);
1918 *ret_font = mapped_font;
1922 done:
1923 heap_free(buff);
1924 return hr;
1927 static const IDWriteFontFallbackVtbl fontfallbackvtbl = {
1928 fontfallback_QueryInterface,
1929 fontfallback_AddRef,
1930 fontfallback_Release,
1931 fontfallback_MapCharacters
1934 HRESULT create_system_fontfallback(IDWriteFactory2 *factory, IDWriteFontFallback **ret)
1936 struct dwrite_fontfallback *fallback;
1938 *ret = NULL;
1940 fallback = heap_alloc(sizeof(*fallback));
1941 if (!fallback)
1942 return E_OUTOFMEMORY;
1944 fallback->IDWriteFontFallback_iface.lpVtbl = &fontfallbackvtbl;
1945 fallback->factory = factory;
1946 fallback->mappings = fontfallback_neutral_data;
1947 fallback->count = sizeof(fontfallback_neutral_data)/sizeof(fontfallback_neutral_data[0]);
1948 IDWriteFactory2_GetSystemFontCollection(fallback->factory, &fallback->systemcollection, FALSE);
1950 *ret = &fallback->IDWriteFontFallback_iface;
1951 return S_OK;
1954 void release_system_fontfallback(IDWriteFontFallback *iface)
1956 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1957 IDWriteFontCollection_Release(fallback->systemcollection);
1958 heap_free(fallback);