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 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE
},
49 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
50 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
51 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
52 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
53 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
54 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
55 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE
},
56 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
57 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
58 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE
},
59 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
60 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
61 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE
},
62 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
63 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
64 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('c','a','k','m') },
65 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
66 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE
},
67 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
68 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
69 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
70 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
71 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE
},
72 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE
},
73 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
74 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
75 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
76 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE
},
77 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
78 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
79 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
80 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
81 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
82 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE
},
83 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE
},
84 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
85 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE
},
86 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
87 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE
},
88 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
89 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
90 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
91 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
92 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
93 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
94 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE
},
95 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
96 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
97 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
98 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE
},
99 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
100 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
101 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE
},
102 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE
, &latn_shaping_ops
},
103 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
104 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
105 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
106 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
107 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
108 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
109 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
110 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
111 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE
},
112 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
113 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
115 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
116 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','c') },
117 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','o') },
118 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
119 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
120 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE
},
121 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
122 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE
},
123 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
124 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE
},
125 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE
},
126 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE
},
127 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
128 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
129 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
130 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
131 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE
},
132 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
133 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
134 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE
},
135 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE
},
136 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
137 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
138 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
139 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE
},
140 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
141 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
142 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
143 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE
},
144 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
145 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
146 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','h','r','d') },
147 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
148 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
149 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE
},
150 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','o','r','a') },
151 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
152 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
153 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE
},
154 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
155 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
156 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE
},
157 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
158 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
159 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('t','a','k','r') },
160 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE
},
161 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE
},
162 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE
},
163 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE
},
164 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE
},
165 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE
},
166 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
167 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
168 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE
},
169 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
170 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE
}
174 struct dwrite_numbersubstitution
{
175 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
178 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
180 BOOL ignore_user_override
;
183 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
185 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
188 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
190 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
191 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
192 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
198 static inline UINT16
get_char_script(WCHAR c
)
200 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
201 if (script
== Script_Unknown
) {
203 if (GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
) && (type
& C1_CNTRL
))
204 script
= Script_Common
;
209 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 len
, IDWriteTextAnalysisSink
*sink
)
211 DWRITE_SCRIPT_ANALYSIS sa
;
212 UINT32 pos
, i
, length
;
214 if (!len
) return S_OK
;
216 sa
.script
= get_char_script(*text
);
221 for (i
= 1; i
< len
; i
++)
223 UINT16 script
= get_char_script(text
[i
]);
225 /* Unknown type is ignored when preceded or followed by another script */
226 if (sa
.script
== Script_Unknown
) sa
.script
= script
;
227 if (script
== Script_Unknown
&& sa
.script
!= Script_Common
) script
= sa
.script
;
228 /* this is a length of a sequence to be reported next */
229 if (sa
.script
== script
) length
++;
231 if (sa
.script
!= script
)
235 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
236 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
237 if (FAILED(hr
)) return hr
;
244 /* 1 length case or normal completion call */
245 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
246 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
249 struct linebreaking_state
{
250 DWRITE_LINE_BREAKPOINT
*breakpoints
;
254 enum BreakConditionLocation
{
255 BreakConditionBefore
,
259 enum linebreaking_classes
{
302 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
303 set to "can break" and could only be changed once. */
304 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
305 struct linebreaking_state
*state
)
307 if (location
== BreakConditionBefore
) {
308 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
310 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
312 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
315 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
317 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
318 if (pos
+ 1 < state
->count
)
319 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
323 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
325 struct linebreaking_state state
;
329 break_class
= heap_alloc(count
*sizeof(short));
331 return E_OUTOFMEMORY
;
333 state
.breakpoints
= breakpoints
;
336 /* LB31 - allow breaks everywhere. It will be overridden if needed as
337 other rules dictate. */
338 for (i
= 0; i
< count
; i
++)
340 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
342 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
343 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
344 breakpoints
[i
].isWhitespace
= break_class
[i
] == b_BK
|| break_class
[i
] == b_ZW
|| break_class
[i
] == b_SP
|| isspaceW(text
[i
]);
345 breakpoints
[i
].isSoftHyphen
= FALSE
;
346 breakpoints
[i
].padding
= 0;
348 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
349 switch (break_class
[i
])
355 break_class
[i
] = b_AL
;
358 break_class
[i
] = b_NS
;
363 /* LB2 - never break at the start */
364 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
365 /* LB3 - always break at the end. This one is ignored. */
367 for (i
= 0; i
< count
; i
++)
369 switch (break_class
[i
])
373 /* LB5 - don't break CR x LF */
374 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
376 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
377 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
383 /* LB4 - LB5 - always break after hard breaks */
384 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
385 /* LB6 - do not break before hard breaks */
386 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
388 /* LB7 - do not break before spaces */
390 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
393 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
394 /* LB8 - break before character after zero-width space, skip spaces in-between */
395 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
397 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
403 for (i
= 0; i
< count
; i
++)
405 if (break_class
[i
] == b_CM
)
409 switch (break_class
[i
-1])
417 break_class
[i
] = b_AL
;
420 break_class
[i
] = break_class
[i
-1];
423 else break_class
[i
] = b_AL
;
427 for (i
= 0; i
< count
; i
++)
429 switch (break_class
[i
])
431 /* LB11 - don't break before and after word joiner */
433 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
434 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
436 /* LB12 - don't break after glue */
438 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
442 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
443 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
452 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
456 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
457 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
458 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
465 while (j
< count
-1 && break_class
[j
] == b_SP
)
467 if (break_class
[j
] == b_OP
)
469 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
474 while(j
> 0 && break_class
[j
] == b_SP
)
476 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
477 for (j
++; j
<= i
; j
++)
478 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
483 while (j
< count
&& break_class
[j
] == b_SP
)
485 if (break_class
[j
] == b_B2
)
487 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
492 for (i
= 0; i
< count
; i
++)
494 switch(break_class
[i
])
496 /* LB18 - break is allowed after space */
498 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
500 /* LB19 - don't break before or after quotation mark */
502 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
503 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
507 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
508 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
514 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
517 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
522 switch (break_class
[i
+1])
526 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
533 switch (break_class
[i
-1])
540 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
549 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
550 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
551 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
552 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
553 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
554 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
556 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
557 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
558 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
559 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
560 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
561 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
564 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
565 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
566 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
567 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
568 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
569 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
570 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
571 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
572 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
573 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
574 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
575 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
576 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
577 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
578 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
581 if (break_class
[i
] == b_JL
)
583 switch (break_class
[i
+1])
589 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
592 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
593 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
594 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
595 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
596 break_class
[i
+1] == b_JT
)
597 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
600 switch (break_class
[i
])
607 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
608 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
610 if (break_class
[i
] == b_PO
)
612 switch (break_class
[i
+1])
619 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
624 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
625 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
626 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
627 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
628 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
631 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
632 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
633 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
636 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
637 break_class
[i
+1] == b_OP
)
638 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
639 if (break_class
[i
] == b_CP
&&
640 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
641 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
644 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
645 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
649 heap_free(break_class
);
653 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
655 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
657 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
658 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
659 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
660 IsEqualIID(riid
, &IID_IUnknown
))
667 return E_NOINTERFACE
;
670 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
675 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
680 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
681 data after a first request. */
682 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
690 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
691 if (FAILED(hr
)) return hr
;
696 *buff
= heap_alloc(length
*sizeof(WCHAR
));
698 return E_OUTOFMEMORY
;
699 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
702 while (read
< length
&& *text
) {
705 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
710 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
720 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
721 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
727 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
732 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
736 hr
= analyze_script(text
, position
, length
, sink
);
742 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
743 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
745 UINT8
*levels
= NULL
, *explicit = NULL
;
746 UINT8 baselevel
, level
, explicit_level
;
752 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
757 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
761 levels
= heap_alloc(length
*sizeof(*levels
));
762 explicit = heap_alloc(length
*sizeof(*explicit));
764 if (!levels
|| !explicit) {
769 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
770 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
775 explicit_level
= explicit[0];
777 for (i
= 1; i
< length
; i
++) {
778 if (levels
[i
] != level
|| explicit[i
] != explicit_level
) {
779 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, i
- pos
, explicit_level
, level
);
783 explicit_level
= explicit[i
];
788 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, length
- pos
, explicit_level
, level
);
799 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
800 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
802 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
806 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
807 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
809 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
815 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
820 /* get some, check for length */
823 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
824 if (FAILED(hr
)) return hr
;
829 buff
= heap_alloc(length
*sizeof(WCHAR
));
831 return E_OUTOFMEMORY
;
832 memcpy(buff
, text
, len
*sizeof(WCHAR
));
835 while (read
< length
&& text
) {
838 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
841 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
848 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
854 hr
= analyze_linebreaks(text
, length
, breakpoints
);
858 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
861 heap_free(breakpoints
);
867 static UINT32
get_opentype_language(const WCHAR
*locale
)
869 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
873 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
874 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
880 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
881 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
882 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
883 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
884 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
885 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
886 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
888 const struct dwritescript_properties
*scriptprops
;
889 struct scriptshaping_context context
;
890 struct scriptshaping_cache
*cache
= NULL
;
891 BOOL update_cluster
, need_vertical
;
892 IDWriteFontFace1
*fontface1
;
898 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
899 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
900 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
902 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
904 if (max_glyph_count
< length
)
905 return E_NOT_SUFFICIENT_BUFFER
;
908 FIXME("number substitution is not supported.\n");
910 for (i
= 0; i
< length
; i
++) {
911 /* FIXME: set to better values */
912 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
913 glyph_props
[i
].isClusterStart
= 1;
914 glyph_props
[i
].isDiacritic
= 0;
915 glyph_props
[i
].isZeroWidthSpace
= 0;
916 glyph_props
[i
].reserved
= 0;
918 /* FIXME: have the shaping engine set this */
919 text_props
[i
].isShapedAlone
= 0;
920 text_props
[i
].reserved
= 0;
925 for (; i
< max_glyph_count
; i
++) {
926 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
927 glyph_props
[i
].isClusterStart
= 0;
928 glyph_props
[i
].isDiacritic
= 0;
929 glyph_props
[i
].isZeroWidthSpace
= 0;
930 glyph_props
[i
].reserved
= 0;
933 string
= heap_alloc(sizeof(WCHAR
)*length
);
935 return E_OUTOFMEMORY
;
937 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
939 WARN("failed to get IDWriteFontFace1\n");
941 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
943 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
946 if (!update_cluster
) {
947 codepoint
= decode_surrogate_pair(text
, i
, length
);
949 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
950 string
[i
] = codepoint
;
954 string
[i
+1] = text
[i
+1];
955 update_cluster
= TRUE
;
958 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
965 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
967 glyph_indices
[g
] = vertical
;
975 update_cluster
= FALSE
;
976 /* mark surrogate halves with same cluster */
977 clustermap
[i
] = clustermap
[i
-1];
978 /* update following clusters */
979 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
983 *actual_glyph_count
= g
;
985 hr
= create_scriptshaping_cache(fontface
, &cache
);
989 context
.cache
= cache
;
991 context
.length
= length
;
992 context
.is_rtl
= is_rtl
;
993 context
.max_glyph_count
= max_glyph_count
;
994 context
.language_tag
= get_opentype_language(locale
);
996 scriptprops
= &dwritescripts_properties
[script
];
997 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
998 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1003 /* FIXME: apply default features */
1005 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1006 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1008 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1012 IDWriteFontFace1_Release(fontface1
);
1013 release_scriptshaping_cache(cache
);
1019 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1020 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1021 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1022 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1023 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1024 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1026 DWRITE_FONT_METRICS metrics
;
1027 IDWriteFontFace1
*fontface1
;
1031 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
),
1032 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1033 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, advances
, offsets
);
1035 if (glyph_count
== 0)
1038 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1040 WARN("failed to get IDWriteFontFace1.\n");
1044 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1045 for (i
= 0; i
< glyph_count
; i
++) {
1048 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1052 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1053 offsets
[i
].advanceOffset
= 0.0;
1054 offsets
[i
].ascenderOffset
= 0.0;
1057 /* FIXME: actually apply features */
1059 IDWriteFontFace1_Release(fontface1
);
1063 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1064 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1065 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1066 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT ppdip
,
1067 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1068 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1069 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1071 DWRITE_FONT_METRICS metrics
;
1072 IDWriteFontFace1
*fontface1
;
1076 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
),
1077 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1078 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
1079 feature_ranges
, advances
, offsets
);
1081 if (glyph_count
== 0)
1084 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1086 WARN("failed to get IDWriteFontFace1.\n");
1090 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1092 IDWriteFontFace1_Release(fontface1
);
1093 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1096 for (i
= 0; i
< glyph_count
; i
++) {
1099 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1100 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1104 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1105 offsets
[i
].advanceOffset
= 0.0;
1106 offsets
[i
].ascenderOffset
= 0.0;
1109 /* FIXME: actually apply features */
1111 IDWriteFontFace1_Release(fontface1
);
1115 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1117 FLOAT advance
= 0.0;
1118 for (; start
< end
; start
++)
1119 advance
+= advances
[start
];
1123 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1124 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1125 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1127 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1128 FLOAT advance
= advances
[g
];
1131 if (props
[g
].isZeroWidthSpace
) {
1132 modified_advances
[g
] = advances
[g
];
1133 modified_offsets
[g
] = offsets
[g
];
1137 /* first apply negative spacing and check if we hit minimum width */
1138 if (leading_spacing
< 0.0) {
1139 advance
+= leading_spacing
;
1140 origin
-= leading_spacing
;
1142 if (trailing_spacing
< 0.0)
1143 advance
+= trailing_spacing
;
1145 if (advance
< min_advance_width
) {
1146 FLOAT half
= (min_advance_width
- advance
) / 2.0;
1150 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0)
1152 else if (leading_spacing
< 0.0)
1153 origin
-= min_advance_width
- advance
;
1155 advance
= min_advance_width
;
1158 /* now apply positive spacing adjustments */
1159 if (leading_spacing
> 0.0) {
1160 advance
+= leading_spacing
;
1161 origin
-= leading_spacing
;
1163 if (trailing_spacing
> 0.0)
1164 advance
+= trailing_spacing
;
1166 modified_advances
[g
] = advance
;
1167 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1168 /* ascender is never touched, it's orthogonal to reading direction and is not
1169 affected by advance adjustments */
1170 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1173 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1174 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1175 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1177 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1178 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1182 modified_advances
[start
] = advances
[start
];
1183 modified_advances
[end
-1] = advances
[end
-1];
1185 /* first apply negative spacing and check if we hit minimum width */
1186 if (leading_spacing
< 0.0) {
1187 advance
+= leading_spacing
;
1188 modified_advances
[start
] += leading_spacing
;
1189 origin
-= leading_spacing
;
1191 if (trailing_spacing
< 0.0) {
1192 advance
+= trailing_spacing
;
1193 modified_advances
[end
-1] += trailing_spacing
;
1195 if (advance
< min_advance_width
) {
1196 /* additional spacing is only applied to leading and trailing glyph */
1197 FLOAT half
= (min_advance_width
- advance
) / 2.0;
1201 modified_advances
[start
] += half
;
1202 modified_advances
[end
-1] += half
;
1204 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0) {
1206 modified_advances
[start
] += half
;
1207 modified_advances
[end
-1] += half
;
1209 else if (leading_spacing
< 0.0) {
1210 origin
-= min_advance_width
- advance
;
1211 modified_advances
[start
] += min_advance_width
- advance
;
1214 modified_advances
[end
-1] += min_advance_width
- advance
;
1216 advance
= min_advance_width
;
1219 /* now apply positive spacing adjustments */
1220 if (leading_spacing
> 0.0) {
1221 modified_advances
[start
] += leading_spacing
;
1222 origin
-= leading_spacing
;
1224 if (trailing_spacing
> 0.0)
1225 modified_advances
[end
-1] += trailing_spacing
;
1227 for (g
= start
; g
< end
; g
++) {
1229 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1230 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1232 else if (g
== end
- 1)
1233 /* trailing glyph offset is not adjusted */
1234 modified_offsets
[g
] = offsets
[g
];
1236 /* for all glyphs within a cluster use original advances and offsets */
1237 modified_advances
[g
] = advances
[g
];
1238 modified_offsets
[g
] = offsets
[g
];
1243 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1245 UINT16 g
= clustermap
[start
];
1248 while (start
< text_len
&& clustermap
[++start
] == g
)
1253 /* Applies spacing adjustments to clusters.
1255 Adjustments are applied in the following order:
1257 1. Negative adjustments
1259 Leading and trailing spacing could be negative, at this step
1260 only negative ones are actually applied. Leading spacing is only
1261 applied to leading glyph, trailing - to trailing glyph.
1263 2. Minimum advance width
1265 Advances could only be reduced at this point or unchanged. In any
1266 case it's checked if cluster advance width is less than minimum width.
1267 If it's the case advance width is incremented up to minimum value.
1269 Important part is the direction in which this increment is applied;
1270 it depends on from which directions total cluster advance was trimmed
1271 at step 1. So it could be incremented from leading, trailing, or both
1272 sides. When applied to both sides, each side gets half of difference
1273 that bring advance to minimum width.
1275 3. Positive adjustments
1277 After minimum width rule was applied, positive spacing is applied in same
1278 way as negative ones on step 1.
1280 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1281 keeps its position in coordinate system where initial advance width is counted
1286 It's known that isZeroWidthSpace property keeps initial advance from changing.
1288 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1289 with more than one glyph.
1292 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1293 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1294 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1295 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1299 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1300 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1302 if (min_advance_width
< 0.0) {
1303 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1304 return E_INVALIDARG
;
1307 /* minimum advance is not applied if no adjustments were made */
1308 if (leading_spacing
== 0.0 && trailing_spacing
== 0.0) {
1309 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1310 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1315 for (start
= 0; start
< len
;) {
1316 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1319 UINT32 g
= clustermap
[start
];
1321 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1322 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1325 UINT32 g_start
, g_end
;
1327 g_start
= clustermap
[start
];
1328 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1330 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1331 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1340 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1341 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1342 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1344 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1345 baseline_coord
, exists
);
1349 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1350 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1352 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1356 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1357 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1359 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1360 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1363 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1364 DWRITE_SCRIPT_PROPERTIES
*props
)
1366 TRACE("(%u %p)\n", sa
.script
, props
);
1368 if (sa
.script
> Script_LastId
)
1369 return E_INVALIDARG
;
1371 *props
= dwritescripts_properties
[sa
.script
].props
;
1375 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1377 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1380 UINT16 script
= get_char_script(c
);
1381 return !dwritescripts_properties
[script
].is_complex
;
1385 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1386 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1391 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1397 return E_INVALIDARG
;
1404 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1405 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1406 if (is_char_from_simple_script(text
[i
])) {
1417 if (*is_simple
&& indices
) {
1418 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1420 return E_OUTOFMEMORY
;
1422 for (i
= 0; i
< *len_read
; i
++)
1423 codepoints
[i
] = text
[i
];
1425 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1426 heap_free(codepoints
);
1432 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1433 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1434 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1436 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1437 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1441 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1442 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1443 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1445 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1450 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1451 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1452 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1453 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1454 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1455 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1457 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
,
1458 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1459 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1463 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1464 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1466 static const DWRITE_MATRIX transforms
[] = {
1467 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
1468 { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 },
1469 { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 },
1470 { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 }
1473 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1475 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1476 memset(m
, 0, sizeof(*m
));
1477 return E_INVALIDARG
;
1480 /* for sideways case simply rotate 90 degrees more */
1483 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1484 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1486 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1487 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1489 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1490 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1492 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1493 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1500 *m
= transforms
[angle
];
1502 /* shift components represent transform necessary to get from original point to
1503 rotated one in new coordinate system */
1504 if ((originX
!= 0.0 || originY
!= 0.0) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1505 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1506 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1512 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1513 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1514 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1516 const struct dwritescript_properties
*props
;
1520 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1523 if (sa
.script
> Script_LastId
)
1524 return E_INVALIDARG
;
1526 language
= get_opentype_language(locale
);
1527 props
= &dwritescripts_properties
[sa
.script
];
1528 *actual_tagcount
= 0;
1530 if (props
->scriptalttag
)
1531 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1533 if (*actual_tagcount
== 0)
1534 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1539 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1540 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1541 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1543 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1548 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1549 dwritetextanalyzer_QueryInterface
,
1550 dwritetextanalyzer_AddRef
,
1551 dwritetextanalyzer_Release
,
1552 dwritetextanalyzer_AnalyzeScript
,
1553 dwritetextanalyzer_AnalyzeBidi
,
1554 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1555 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1556 dwritetextanalyzer_GetGlyphs
,
1557 dwritetextanalyzer_GetGlyphPlacements
,
1558 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1559 dwritetextanalyzer1_ApplyCharacterSpacing
,
1560 dwritetextanalyzer1_GetBaseline
,
1561 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1562 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1563 dwritetextanalyzer1_GetScriptProperties
,
1564 dwritetextanalyzer1_GetTextComplexity
,
1565 dwritetextanalyzer1_GetJustificationOpportunities
,
1566 dwritetextanalyzer1_JustifyGlyphAdvances
,
1567 dwritetextanalyzer1_GetJustifiedGlyphs
,
1568 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1569 dwritetextanalyzer2_GetTypographicFeatures
,
1570 dwritetextanalyzer2_CheckTypographicFeature
1573 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1575 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1577 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1581 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1583 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1585 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1587 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1588 IsEqualIID(riid
, &IID_IUnknown
))
1591 IDWriteNumberSubstitution_AddRef(iface
);
1597 return E_NOINTERFACE
;
1600 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1602 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1603 ULONG ref
= InterlockedIncrement(&This
->ref
);
1604 TRACE("(%p)->(%d)\n", This
, ref
);
1608 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1610 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1611 ULONG ref
= InterlockedDecrement(&This
->ref
);
1613 TRACE("(%p)->(%d)\n", This
, ref
);
1616 heap_free(This
->locale
);
1623 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1624 dwritenumbersubstitution_QueryInterface
,
1625 dwritenumbersubstitution_AddRef
,
1626 dwritenumbersubstitution_Release
1629 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1630 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1632 struct dwrite_numbersubstitution
*substitution
;
1636 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1637 return E_INVALIDARG
;
1639 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1640 return E_INVALIDARG
;
1642 substitution
= heap_alloc(sizeof(*substitution
));
1644 return E_OUTOFMEMORY
;
1646 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1647 substitution
->ref
= 1;
1648 substitution
->ignore_user_override
= ignore_user_override
;
1649 substitution
->method
= method
;
1650 substitution
->locale
= heap_strdupW(locale
);
1651 if (locale
&& !substitution
->locale
) {
1652 heap_free(substitution
);
1653 return E_OUTOFMEMORY
;
1656 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;