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
26 #include "dwrite_private.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
31 extern const unsigned short wine_linebreak_table
[];
32 extern const unsigned short wine_scripts_table
[];
34 struct dwritescript_properties
{
35 DWRITE_SCRIPT_PROPERTIES props
;
36 UINT32 scripttag
; /* OpenType script tag */
37 UINT32 scriptalttag
; /* Version 2 tag, 0 is not defined */
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
}
180 struct dwrite_numbersubstitution
{
181 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
184 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
186 BOOL ignore_user_override
;
189 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
191 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
194 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
196 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
197 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
198 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
204 static inline UINT16
get_char_script(WCHAR c
)
206 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
207 if (script
== Script_Unknown
) {
209 if (GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
) && (type
& C1_CNTRL
))
210 script
= Script_Common
;
215 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 len
, IDWriteTextAnalysisSink
*sink
)
217 DWRITE_SCRIPT_ANALYSIS sa
;
218 UINT32 pos
, i
, length
;
220 if (!len
) return S_OK
;
222 sa
.script
= get_char_script(*text
);
227 for (i
= 1; i
< len
; i
++)
229 UINT16 script
= get_char_script(text
[i
]);
231 /* Unknown type is ignored when preceded or followed by another script */
232 if (sa
.script
== Script_Unknown
) sa
.script
= script
;
233 if (script
== Script_Unknown
&& sa
.script
!= Script_Common
) script
= sa
.script
;
234 /* this is a length of a sequence to be reported next */
235 if (sa
.script
== script
) length
++;
237 if (sa
.script
!= script
)
241 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
242 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
243 if (FAILED(hr
)) return hr
;
250 /* 1 length case or normal completion call */
251 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
252 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
255 struct linebreaking_state
{
256 DWRITE_LINE_BREAKPOINT
*breakpoints
;
260 enum BreakConditionLocation
{
261 BreakConditionBefore
,
265 enum linebreaking_classes
{
308 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
309 set to "can break" and could only be changed once. */
310 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
311 struct linebreaking_state
*state
)
313 if (location
== BreakConditionBefore
) {
314 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
316 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
318 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
321 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
323 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
324 if (pos
+ 1 < state
->count
)
325 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
329 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
331 struct linebreaking_state state
;
335 break_class
= heap_alloc(count
*sizeof(short));
337 return E_OUTOFMEMORY
;
339 state
.breakpoints
= breakpoints
;
342 /* LB31 - allow breaks everywhere. It will be overridden if needed as
343 other rules dictate. */
344 for (i
= 0; i
< count
; i
++)
346 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
348 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
349 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
350 breakpoints
[i
].isWhitespace
= break_class
[i
] == b_BK
|| break_class
[i
] == b_ZW
|| break_class
[i
] == b_SP
|| isspaceW(text
[i
]);
351 breakpoints
[i
].isSoftHyphen
= FALSE
;
352 breakpoints
[i
].padding
= 0;
354 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
355 switch (break_class
[i
])
361 break_class
[i
] = b_AL
;
364 break_class
[i
] = b_NS
;
369 /* LB2 - never break at the start */
370 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
371 /* LB3 - always break at the end. This one is ignored. */
373 for (i
= 0; i
< count
; i
++)
375 switch (break_class
[i
])
379 /* LB5 - don't break CR x LF */
380 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
382 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
383 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
389 /* LB4 - LB5 - always break after hard breaks */
390 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
391 /* LB6 - do not break before hard breaks */
392 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
394 /* LB7 - do not break before spaces */
396 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
399 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
400 /* LB8 - break before character after zero-width space, skip spaces in-between */
401 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
403 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
409 for (i
= 0; i
< count
; i
++)
411 if (break_class
[i
] == b_CM
)
415 switch (break_class
[i
-1])
423 break_class
[i
] = b_AL
;
426 break_class
[i
] = break_class
[i
-1];
429 else break_class
[i
] = b_AL
;
433 for (i
= 0; i
< count
; i
++)
435 switch (break_class
[i
])
437 /* LB11 - don't break before and after word joiner */
439 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
440 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
442 /* LB12 - don't break after glue */
444 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
448 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
449 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
458 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
462 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
463 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
464 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
471 while (j
< count
-1 && break_class
[j
] == b_SP
)
473 if (break_class
[j
] == b_OP
)
475 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
480 while(j
> 0 && break_class
[j
] == b_SP
)
482 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
483 for (j
++; j
<= i
; j
++)
484 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
489 while (j
< count
&& break_class
[j
] == b_SP
)
491 if (break_class
[j
] == b_B2
)
493 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
498 for (i
= 0; i
< count
; i
++)
500 switch(break_class
[i
])
502 /* LB18 - break is allowed after space */
504 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
506 /* LB19 - don't break before or after quotation mark */
508 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
509 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
513 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
514 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
520 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
523 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
528 switch (break_class
[i
+1])
532 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
539 switch (break_class
[i
-1])
546 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
555 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
556 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
557 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
558 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
559 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
560 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
562 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
563 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
564 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
565 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
566 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
567 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
570 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
571 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
572 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
573 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
574 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
575 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
576 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
577 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
578 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
579 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
580 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
581 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
582 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
583 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
584 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
587 if (break_class
[i
] == b_JL
)
589 switch (break_class
[i
+1])
595 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
598 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
599 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
600 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
601 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
602 break_class
[i
+1] == b_JT
)
603 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
606 switch (break_class
[i
])
613 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
614 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
616 if (break_class
[i
] == b_PO
)
618 switch (break_class
[i
+1])
625 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
630 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
631 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
632 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
633 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
634 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
637 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
638 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
639 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
642 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
643 break_class
[i
+1] == b_OP
)
644 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
645 if (break_class
[i
] == b_CP
&&
646 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
647 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
650 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
651 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
655 heap_free(break_class
);
659 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
661 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
663 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
664 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
665 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
666 IsEqualIID(riid
, &IID_IUnknown
))
673 return E_NOINTERFACE
;
676 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
681 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
686 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
687 data after a first request. */
688 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
696 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
697 if (FAILED(hr
)) return hr
;
702 *buff
= heap_alloc(length
*sizeof(WCHAR
));
704 return E_OUTOFMEMORY
;
705 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
708 while (read
< length
&& *text
) {
711 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
716 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
726 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
727 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
733 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
738 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
742 hr
= analyze_script(text
, position
, length
, sink
);
748 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
749 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
751 UINT8
*levels
= NULL
, *explicit = NULL
;
752 UINT8 baselevel
, level
, explicit_level
;
758 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
763 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
767 levels
= heap_alloc(length
*sizeof(*levels
));
768 explicit = heap_alloc(length
*sizeof(*explicit));
770 if (!levels
|| !explicit) {
775 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
776 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
781 explicit_level
= explicit[0];
783 for (i
= 1; i
< length
; i
++) {
784 if (levels
[i
] != level
|| explicit[i
] != explicit_level
) {
785 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, i
- pos
, explicit_level
, level
);
789 explicit_level
= explicit[i
];
794 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, length
- pos
, explicit_level
, level
);
805 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
806 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
808 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
812 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
813 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
815 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
821 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
826 /* get some, check for length */
829 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
830 if (FAILED(hr
)) return hr
;
835 buff
= heap_alloc(length
*sizeof(WCHAR
));
837 return E_OUTOFMEMORY
;
838 memcpy(buff
, text
, len
*sizeof(WCHAR
));
841 while (read
< length
&& text
) {
844 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
847 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
854 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
860 hr
= analyze_linebreaks(text
, length
, breakpoints
);
864 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
867 heap_free(breakpoints
);
873 static UINT32
get_opentype_language(const WCHAR
*locale
)
875 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
879 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
880 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
886 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
887 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
888 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
889 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
890 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
891 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
892 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
894 const struct dwritescript_properties
*scriptprops
;
895 struct scriptshaping_context context
;
896 struct scriptshaping_cache
*cache
= NULL
;
897 BOOL update_cluster
, need_vertical
;
898 IDWriteFontFace1
*fontface1
;
904 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
905 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
906 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
908 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
910 if (max_glyph_count
< length
)
911 return E_NOT_SUFFICIENT_BUFFER
;
914 FIXME("number substitution is not supported.\n");
916 for (i
= 0; i
< length
; i
++) {
917 /* FIXME: set to better values */
918 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
919 glyph_props
[i
].isClusterStart
= 1;
920 glyph_props
[i
].isDiacritic
= 0;
921 glyph_props
[i
].isZeroWidthSpace
= 0;
922 glyph_props
[i
].reserved
= 0;
924 /* FIXME: have the shaping engine set this */
925 text_props
[i
].isShapedAlone
= 0;
926 text_props
[i
].reserved
= 0;
931 for (; i
< max_glyph_count
; i
++) {
932 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
933 glyph_props
[i
].isClusterStart
= 0;
934 glyph_props
[i
].isDiacritic
= 0;
935 glyph_props
[i
].isZeroWidthSpace
= 0;
936 glyph_props
[i
].reserved
= 0;
939 string
= heap_alloc(sizeof(WCHAR
)*length
);
941 return E_OUTOFMEMORY
;
943 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
945 WARN("failed to get IDWriteFontFace1\n");
947 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
949 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
952 if (!update_cluster
) {
953 codepoint
= decode_surrogate_pair(text
, i
, length
);
955 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
956 string
[i
] = codepoint
;
960 string
[i
+1] = text
[i
+1];
961 update_cluster
= TRUE
;
964 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
971 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
973 glyph_indices
[g
] = vertical
;
981 update_cluster
= FALSE
;
982 /* mark surrogate halves with same cluster */
983 clustermap
[i
] = clustermap
[i
-1];
984 /* update following clusters */
985 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
989 *actual_glyph_count
= g
;
991 hr
= create_scriptshaping_cache(fontface
, &cache
);
995 context
.cache
= cache
;
997 context
.length
= length
;
998 context
.is_rtl
= is_rtl
;
999 context
.max_glyph_count
= max_glyph_count
;
1000 context
.language_tag
= get_opentype_language(locale
);
1002 scriptprops
= &dwritescripts_properties
[script
];
1003 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
1004 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1009 /* FIXME: apply default features */
1011 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1012 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1014 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1018 IDWriteFontFace1_Release(fontface1
);
1019 release_scriptshaping_cache(cache
);
1025 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1026 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1027 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1028 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1029 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1030 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1032 DWRITE_FONT_METRICS metrics
;
1033 IDWriteFontFace1
*fontface1
;
1037 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
),
1038 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1039 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, advances
, offsets
);
1041 if (glyph_count
== 0)
1044 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1046 WARN("failed to get IDWriteFontFace1.\n");
1050 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1051 for (i
= 0; i
< glyph_count
; i
++) {
1054 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1058 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1059 offsets
[i
].advanceOffset
= 0.0;
1060 offsets
[i
].ascenderOffset
= 0.0;
1063 /* FIXME: actually apply features */
1065 IDWriteFontFace1_Release(fontface1
);
1069 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1070 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1071 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1072 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT ppdip
,
1073 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1074 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1075 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1077 DWRITE_FONT_METRICS metrics
;
1078 IDWriteFontFace1
*fontface1
;
1082 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
),
1083 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1084 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
1085 feature_ranges
, advances
, offsets
);
1087 if (glyph_count
== 0)
1090 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1092 WARN("failed to get IDWriteFontFace1.\n");
1096 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1098 IDWriteFontFace1_Release(fontface1
);
1099 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1102 for (i
= 0; i
< glyph_count
; i
++) {
1105 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1106 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1110 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1111 offsets
[i
].advanceOffset
= 0.0;
1112 offsets
[i
].ascenderOffset
= 0.0;
1115 /* FIXME: actually apply features */
1117 IDWriteFontFace1_Release(fontface1
);
1121 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1123 FLOAT advance
= 0.0;
1124 for (; start
< end
; start
++)
1125 advance
+= advances
[start
];
1129 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1130 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1131 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1133 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1134 FLOAT advance
= advances
[g
];
1137 if (props
[g
].isZeroWidthSpace
) {
1138 modified_advances
[g
] = advances
[g
];
1139 modified_offsets
[g
] = offsets
[g
];
1143 /* first apply negative spacing and check if we hit minimum width */
1144 if (leading_spacing
< 0.0) {
1145 advance
+= leading_spacing
;
1146 origin
-= leading_spacing
;
1148 if (trailing_spacing
< 0.0)
1149 advance
+= trailing_spacing
;
1151 if (advance
< min_advance_width
) {
1152 FLOAT half
= (min_advance_width
- advance
) / 2.0;
1156 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0)
1158 else if (leading_spacing
< 0.0)
1159 origin
-= min_advance_width
- advance
;
1161 advance
= min_advance_width
;
1164 /* now apply positive spacing adjustments */
1165 if (leading_spacing
> 0.0) {
1166 advance
+= leading_spacing
;
1167 origin
-= leading_spacing
;
1169 if (trailing_spacing
> 0.0)
1170 advance
+= trailing_spacing
;
1172 modified_advances
[g
] = advance
;
1173 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1174 /* ascender is never touched, it's orthogonal to reading direction and is not
1175 affected by advance adjustments */
1176 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1179 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1180 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1181 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1183 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1184 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1188 modified_advances
[start
] = advances
[start
];
1189 modified_advances
[end
-1] = advances
[end
-1];
1191 /* first apply negative spacing and check if we hit minimum width */
1192 if (leading_spacing
< 0.0) {
1193 advance
+= leading_spacing
;
1194 modified_advances
[start
] += leading_spacing
;
1195 origin
-= leading_spacing
;
1197 if (trailing_spacing
< 0.0) {
1198 advance
+= trailing_spacing
;
1199 modified_advances
[end
-1] += trailing_spacing
;
1202 advance
= min_advance_width
- advance
;
1203 if (advance
> 0.0f
) {
1204 /* additional spacing is only applied to leading and trailing glyph */
1205 FLOAT half
= advance
/ 2.0f
;
1209 modified_advances
[start
] += half
;
1210 modified_advances
[end
-1] += half
;
1212 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0) {
1214 modified_advances
[start
] += half
;
1215 modified_advances
[end
-1] += half
;
1217 else if (leading_spacing
< 0.0) {
1219 modified_advances
[start
] += advance
;
1222 modified_advances
[end
-1] += advance
;
1225 /* now apply positive spacing adjustments */
1226 if (leading_spacing
> 0.0) {
1227 modified_advances
[start
] += leading_spacing
;
1228 origin
-= leading_spacing
;
1230 if (trailing_spacing
> 0.0)
1231 modified_advances
[end
-1] += trailing_spacing
;
1233 for (g
= start
; g
< end
; g
++) {
1235 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1236 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1238 else if (g
== end
- 1)
1239 /* trailing glyph offset is not adjusted */
1240 modified_offsets
[g
] = offsets
[g
];
1242 /* for all glyphs within a cluster use original advances and offsets */
1243 modified_advances
[g
] = advances
[g
];
1244 modified_offsets
[g
] = offsets
[g
];
1249 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1251 UINT16 g
= clustermap
[start
];
1254 while (start
< text_len
&& clustermap
[++start
] == g
)
1259 /* Applies spacing adjustments to clusters.
1261 Adjustments are applied in the following order:
1263 1. Negative adjustments
1265 Leading and trailing spacing could be negative, at this step
1266 only negative ones are actually applied. Leading spacing is only
1267 applied to leading glyph, trailing - to trailing glyph.
1269 2. Minimum advance width
1271 Advances could only be reduced at this point or unchanged. In any
1272 case it's checked if cluster advance width is less than minimum width.
1273 If it's the case advance width is incremented up to minimum value.
1275 Important part is the direction in which this increment is applied;
1276 it depends on from which directions total cluster advance was trimmed
1277 at step 1. So it could be incremented from leading, trailing, or both
1278 sides. When applied to both sides, each side gets half of difference
1279 that bring advance to minimum width.
1281 3. Positive adjustments
1283 After minimum width rule was applied, positive spacing is applied in same
1284 way as negative ones on step 1.
1286 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1287 keeps its position in coordinate system where initial advance width is counted
1292 It's known that isZeroWidthSpace property keeps initial advance from changing.
1294 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1295 with more than one glyph.
1298 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1299 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1300 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1301 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1305 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1306 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1308 if (min_advance_width
< 0.0) {
1309 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1310 return E_INVALIDARG
;
1313 /* minimum advance is not applied if no adjustments were made */
1314 if (leading_spacing
== 0.0 && trailing_spacing
== 0.0) {
1315 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1316 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1320 for (start
= 0; start
< len
;) {
1321 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1324 UINT32 g
= clustermap
[start
];
1326 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1327 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1330 UINT32 g_start
, g_end
;
1332 g_start
= clustermap
[start
];
1333 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1335 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1336 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1345 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1346 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1347 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1349 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1350 baseline_coord
, exists
);
1354 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1355 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1357 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1361 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1362 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1364 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1365 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1368 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1369 DWRITE_SCRIPT_PROPERTIES
*props
)
1371 TRACE("(%u %p)\n", sa
.script
, props
);
1373 if (sa
.script
> Script_LastId
)
1374 return E_INVALIDARG
;
1376 *props
= dwritescripts_properties
[sa
.script
].props
;
1380 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1382 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1385 UINT16 script
= get_char_script(c
);
1386 return !dwritescripts_properties
[script
].is_complex
;
1390 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1391 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1396 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1402 return E_INVALIDARG
;
1409 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1410 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1411 if (is_char_from_simple_script(text
[i
])) {
1422 if (*is_simple
&& indices
) {
1423 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1425 return E_OUTOFMEMORY
;
1427 for (i
= 0; i
< *len_read
; i
++)
1428 codepoints
[i
] = text
[i
];
1430 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1431 heap_free(codepoints
);
1437 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1438 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1439 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1441 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1442 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1446 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1447 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1448 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1450 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1455 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1456 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1457 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1458 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1459 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1460 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1462 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
,
1463 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1464 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1468 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1469 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1471 static const DWRITE_MATRIX transforms
[] = {
1472 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
1473 { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 },
1474 { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 },
1475 { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 }
1478 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1480 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1481 memset(m
, 0, sizeof(*m
));
1482 return E_INVALIDARG
;
1485 /* for sideways case simply rotate 90 degrees more */
1488 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1489 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1491 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1492 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1494 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1495 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1497 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1498 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1505 *m
= transforms
[angle
];
1507 /* shift components represent transform necessary to get from original point to
1508 rotated one in new coordinate system */
1509 if ((originX
!= 0.0 || originY
!= 0.0) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1510 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1511 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1517 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1518 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1519 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1521 const struct dwritescript_properties
*props
;
1525 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1528 if (sa
.script
> Script_LastId
)
1529 return E_INVALIDARG
;
1531 language
= get_opentype_language(locale
);
1532 props
= &dwritescripts_properties
[sa
.script
];
1533 *actual_tagcount
= 0;
1535 if (props
->scriptalttag
)
1536 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1538 if (*actual_tagcount
== 0)
1539 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1544 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1545 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1546 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1548 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1553 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1554 dwritetextanalyzer_QueryInterface
,
1555 dwritetextanalyzer_AddRef
,
1556 dwritetextanalyzer_Release
,
1557 dwritetextanalyzer_AnalyzeScript
,
1558 dwritetextanalyzer_AnalyzeBidi
,
1559 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1560 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1561 dwritetextanalyzer_GetGlyphs
,
1562 dwritetextanalyzer_GetGlyphPlacements
,
1563 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1564 dwritetextanalyzer1_ApplyCharacterSpacing
,
1565 dwritetextanalyzer1_GetBaseline
,
1566 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1567 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1568 dwritetextanalyzer1_GetScriptProperties
,
1569 dwritetextanalyzer1_GetTextComplexity
,
1570 dwritetextanalyzer1_GetJustificationOpportunities
,
1571 dwritetextanalyzer1_JustifyGlyphAdvances
,
1572 dwritetextanalyzer1_GetJustifiedGlyphs
,
1573 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1574 dwritetextanalyzer2_GetTypographicFeatures
,
1575 dwritetextanalyzer2_CheckTypographicFeature
1578 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1580 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1582 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1586 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1588 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1590 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1592 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1593 IsEqualIID(riid
, &IID_IUnknown
))
1596 IDWriteNumberSubstitution_AddRef(iface
);
1602 return E_NOINTERFACE
;
1605 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1607 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1608 ULONG ref
= InterlockedIncrement(&This
->ref
);
1609 TRACE("(%p)->(%d)\n", This
, ref
);
1613 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1615 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1616 ULONG ref
= InterlockedDecrement(&This
->ref
);
1618 TRACE("(%p)->(%d)\n", This
, ref
);
1621 heap_free(This
->locale
);
1628 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1629 dwritenumbersubstitution_QueryInterface
,
1630 dwritenumbersubstitution_AddRef
,
1631 dwritenumbersubstitution_Release
1634 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1635 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1637 struct dwrite_numbersubstitution
*substitution
;
1641 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1642 return E_INVALIDARG
;
1644 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1645 return E_INVALIDARG
;
1647 substitution
= heap_alloc(sizeof(*substitution
));
1649 return E_OUTOFMEMORY
;
1651 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1652 substitution
->ref
= 1;
1653 substitution
->ignore_user_override
= ignore_user_override
;
1654 substitution
->method
= method
;
1655 substitution
->locale
= heap_strdupW(locale
);
1656 if (locale
&& !substitution
->locale
) {
1657 heap_free(substitution
);
1658 return E_OUTOFMEMORY
;
1661 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;