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
24 #include "dwrite_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
29 extern const unsigned short wine_linebreak_table
[];
30 extern const unsigned short wine_scripts_table
[];
32 struct dwritescript_properties
{
33 DWRITE_SCRIPT_PROPERTIES props
;
34 UINT32 scripttag
; /* OpenType script tag */
35 UINT32 scriptalttag
; /* Version 2 tag, 0 is not defined */
37 const struct scriptshaping_ops
*ops
;
40 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
42 /* NOTE: keep this array synced with script ids from scripts.h */
43 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
44 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
45 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
46 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE
},
47 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
48 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
49 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
50 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
51 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
52 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
53 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE
},
54 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
55 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
56 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE
},
57 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
58 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
59 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE
},
60 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
61 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
62 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('c','a','k','m') },
63 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
64 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE
},
65 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
66 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
67 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
68 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
69 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE
},
70 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE
},
71 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
72 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
73 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
74 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE
},
75 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
76 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
77 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
78 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
79 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
80 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE
},
81 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE
},
82 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
83 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE
},
84 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
85 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE
},
86 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
87 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
88 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
89 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
90 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
91 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
92 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE
},
93 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
94 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
95 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
96 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE
},
97 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
98 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
99 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE
},
100 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE
, &latn_shaping_ops
},
101 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
102 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
103 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
104 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
105 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
106 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
107 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
108 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
109 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE
},
110 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
111 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
112 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
113 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','c') },
115 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','o') },
116 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
117 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
118 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE
},
119 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
120 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE
},
121 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
122 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE
},
123 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE
},
124 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE
},
125 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
126 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
127 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
128 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
129 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE
},
130 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
131 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
132 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE
},
133 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE
},
134 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
135 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
136 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
137 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE
},
138 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
139 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
140 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
141 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE
},
142 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
143 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
144 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','h','r','d') },
145 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
146 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
147 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE
},
148 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','o','r','a') },
149 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
150 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
151 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE
},
152 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
153 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
154 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE
},
155 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
156 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
157 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('t','a','k','r') },
158 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE
},
159 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE
},
160 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE
},
161 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE
},
162 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE
},
163 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE
},
164 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
165 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
166 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE
},
167 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
168 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE
}
172 struct dwrite_numbersubstitution
{
173 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
176 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
178 BOOL ignore_user_override
;
181 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
183 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
186 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
188 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
189 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
190 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
196 static inline UINT16
get_char_script(WCHAR c
)
198 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
199 if (script
== Script_Unknown
) {
201 if (GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
) && (type
& C1_CNTRL
))
202 script
= Script_Common
;
207 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 len
, IDWriteTextAnalysisSink
*sink
)
209 DWRITE_SCRIPT_ANALYSIS sa
;
210 UINT32 pos
, i
, length
;
212 if (!len
) return S_OK
;
214 sa
.script
= get_char_script(*text
);
219 for (i
= 1; i
< len
; i
++)
221 UINT16 script
= get_char_script(text
[i
]);
223 /* Unknown type is ignored when preceded or followed by another script */
224 if (sa
.script
== Script_Unknown
) sa
.script
= script
;
225 if (script
== Script_Unknown
&& sa
.script
!= Script_Common
) script
= sa
.script
;
226 /* this is a length of a sequence to be reported next */
227 if (sa
.script
== script
) length
++;
229 if (sa
.script
!= script
)
233 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
234 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
235 if (FAILED(hr
)) return hr
;
242 /* 1 length case or normal completion call */
243 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
244 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
247 struct linebreaking_state
{
248 DWRITE_LINE_BREAKPOINT
*breakpoints
;
252 enum BreakConditionLocation
{
253 BreakConditionBefore
,
257 enum linebreaking_classes
{
300 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
301 set to "can break" and could only be changed once. */
302 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
303 struct linebreaking_state
*state
)
305 if (location
== BreakConditionBefore
) {
306 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
308 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
310 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
313 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
315 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
316 if (pos
+ 1 < state
->count
)
317 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
321 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
323 struct linebreaking_state state
;
327 break_class
= heap_alloc(count
*sizeof(short));
329 return E_OUTOFMEMORY
;
331 state
.breakpoints
= breakpoints
;
334 /* LB31 - allow breaks everywhere. It will be overridden if needed as
335 other rules dictate. */
336 for (i
= 0; i
< count
; i
++)
338 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
340 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
341 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
342 breakpoints
[i
].isWhitespace
= break_class
[i
] == b_BK
|| break_class
[i
] == b_ZW
|| break_class
[i
] == b_SP
|| isspaceW(text
[i
]);
343 breakpoints
[i
].isSoftHyphen
= FALSE
;
344 breakpoints
[i
].padding
= 0;
346 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
347 switch (break_class
[i
])
353 break_class
[i
] = b_AL
;
356 break_class
[i
] = b_NS
;
361 /* LB2 - never break at the start */
362 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
363 /* LB3 - always break at the end. This one is ignored. */
365 for (i
= 0; i
< count
; i
++)
367 switch (break_class
[i
])
371 /* LB5 - don't break CR x LF */
372 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
374 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
375 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
381 /* LB4 - LB5 - always break after hard breaks */
382 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
383 /* LB6 - do not break before hard breaks */
384 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
386 /* LB7 - do not break before spaces */
388 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
391 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
392 /* LB8 - break before character after zero-width space, skip spaces in-between */
393 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
395 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
401 for (i
= 0; i
< count
; i
++)
403 if (break_class
[i
] == b_CM
)
407 switch (break_class
[i
-1])
415 break_class
[i
] = b_AL
;
418 break_class
[i
] = break_class
[i
-1];
421 else break_class
[i
] = b_AL
;
425 for (i
= 0; i
< count
; i
++)
427 switch (break_class
[i
])
429 /* LB11 - don't break before and after word joiner */
431 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
432 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
434 /* LB12 - don't break after glue */
436 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
440 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
441 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
450 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
454 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
455 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
456 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
463 while (j
< count
-1 && break_class
[j
] == b_SP
)
465 if (break_class
[j
] == b_OP
)
467 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
472 while(j
> 0 && break_class
[j
] == b_SP
)
474 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
475 for (j
++; j
<= i
; j
++)
476 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
481 while (j
< count
&& break_class
[j
] == b_SP
)
483 if (break_class
[j
] == b_B2
)
485 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
490 for (i
= 0; i
< count
; i
++)
492 switch(break_class
[i
])
494 /* LB18 - break is allowed after space */
496 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
498 /* LB19 - don't break before or after quotation mark */
500 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
501 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
505 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
506 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
512 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
515 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
520 switch (break_class
[i
+1])
524 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
531 switch (break_class
[i
-1])
538 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
547 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
548 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
549 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
550 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
551 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
552 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
554 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
555 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
556 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
557 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
558 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
559 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
562 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
563 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
564 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
565 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
566 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
567 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
568 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
569 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
570 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
571 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
572 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
573 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
574 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
575 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
576 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
579 if (break_class
[i
] == b_JL
)
581 switch (break_class
[i
+1])
587 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
590 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
591 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
592 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
593 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
594 break_class
[i
+1] == b_JT
)
595 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
598 switch (break_class
[i
])
605 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
606 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
608 if (break_class
[i
] == b_PO
)
610 switch (break_class
[i
+1])
617 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
622 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
623 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
624 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
625 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
626 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
629 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
630 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
631 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
634 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
635 break_class
[i
+1] == b_OP
)
636 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
637 if (break_class
[i
] == b_CP
&&
638 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
639 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
642 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
643 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
647 heap_free(break_class
);
651 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
653 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
655 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
656 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
657 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
658 IsEqualIID(riid
, &IID_IUnknown
))
665 return E_NOINTERFACE
;
668 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
673 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
678 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
679 data after a first request. */
680 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
688 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
689 if (FAILED(hr
)) return hr
;
694 *buff
= heap_alloc(length
*sizeof(WCHAR
));
696 return E_OUTOFMEMORY
;
697 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
700 while (read
< length
&& *text
) {
703 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
708 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
718 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
719 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
725 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
730 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
734 hr
= analyze_script(text
, position
, length
, sink
);
740 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
741 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
743 UINT8
*levels
= NULL
, *explicit = NULL
;
744 UINT8 baselevel
, level
, explicit_level
;
750 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
755 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
759 levels
= heap_alloc(length
*sizeof(*levels
));
760 explicit = heap_alloc(length
*sizeof(*explicit));
762 if (!levels
|| !explicit) {
767 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
768 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
773 explicit_level
= explicit[0];
775 for (i
= 1; i
< length
; i
++) {
776 if (levels
[i
] != level
|| explicit[i
] != explicit_level
) {
777 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, i
- pos
, explicit_level
, level
);
781 explicit_level
= explicit[i
];
786 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, length
- pos
, explicit_level
, level
);
797 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
798 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
800 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
804 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
805 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
807 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
813 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
818 /* get some, check for length */
821 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
822 if (FAILED(hr
)) return hr
;
827 buff
= heap_alloc(length
*sizeof(WCHAR
));
829 return E_OUTOFMEMORY
;
830 memcpy(buff
, text
, len
*sizeof(WCHAR
));
833 while (read
< length
&& text
) {
836 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
839 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
846 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
852 hr
= analyze_linebreaks(text
, length
, breakpoints
);
856 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
859 heap_free(breakpoints
);
865 static UINT32
get_opentype_language(const WCHAR
*locale
)
867 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
871 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
872 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
878 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
879 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
880 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
881 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
882 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
883 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
884 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
886 const struct dwritescript_properties
*scriptprops
;
887 struct scriptshaping_context context
;
888 struct scriptshaping_cache
*cache
= NULL
;
889 BOOL update_cluster
, need_vertical
;
890 IDWriteFontFace1
*fontface1
;
896 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
897 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
898 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
900 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
902 if (max_glyph_count
< length
)
903 return E_NOT_SUFFICIENT_BUFFER
;
906 FIXME("number substitution is not supported.\n");
908 for (i
= 0; i
< length
; i
++) {
909 /* FIXME: set to better values */
910 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
911 glyph_props
[i
].isClusterStart
= 1;
912 glyph_props
[i
].isDiacritic
= 0;
913 glyph_props
[i
].isZeroWidthSpace
= 0;
914 glyph_props
[i
].reserved
= 0;
916 /* FIXME: have the shaping engine set this */
917 text_props
[i
].isShapedAlone
= 0;
918 text_props
[i
].reserved
= 0;
923 for (; i
< max_glyph_count
; i
++) {
924 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
925 glyph_props
[i
].isClusterStart
= 0;
926 glyph_props
[i
].isDiacritic
= 0;
927 glyph_props
[i
].isZeroWidthSpace
= 0;
928 glyph_props
[i
].reserved
= 0;
931 string
= heap_alloc(sizeof(WCHAR
)*length
);
933 return E_OUTOFMEMORY
;
935 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
937 WARN("failed to get IDWriteFontFace1\n");
939 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
941 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
944 if (!update_cluster
) {
945 codepoint
= decode_surrogate_pair(text
, i
, length
);
947 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
948 string
[i
] = codepoint
;
952 string
[i
+1] = text
[i
+1];
953 update_cluster
= TRUE
;
956 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
963 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
965 glyph_indices
[g
] = vertical
;
973 update_cluster
= FALSE
;
974 /* mark surrogate halves with same cluster */
975 clustermap
[i
] = clustermap
[i
-1];
976 /* update following clusters */
977 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
981 *actual_glyph_count
= g
;
983 hr
= create_scriptshaping_cache(fontface
, &cache
);
987 context
.cache
= cache
;
989 context
.length
= length
;
990 context
.is_rtl
= is_rtl
;
991 context
.max_glyph_count
= max_glyph_count
;
992 context
.language_tag
= get_opentype_language(locale
);
994 scriptprops
= &dwritescripts_properties
[script
];
995 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
996 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1001 /* FIXME: apply default features */
1003 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1004 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1006 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1010 IDWriteFontFace1_Release(fontface1
);
1011 release_scriptshaping_cache(cache
);
1017 static inline FLOAT
get_scaled_advance_width(INT32 advance
, FLOAT emSize
, const DWRITE_FONT_METRICS
*metrics
)
1019 return (FLOAT
)advance
* emSize
/ (FLOAT
)metrics
->designUnitsPerEm
;
1022 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1023 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1024 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1025 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1026 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1027 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1029 DWRITE_FONT_METRICS metrics
;
1030 IDWriteFontFace1
*fontface1
;
1034 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
),
1035 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1036 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, advances
, offsets
);
1038 if (glyph_count
== 0)
1041 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1043 WARN("failed to get IDWriteFontFace1.\n");
1047 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1048 for (i
= 0; i
< glyph_count
; i
++) {
1051 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1055 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1056 offsets
[i
].advanceOffset
= 0.0;
1057 offsets
[i
].ascenderOffset
= 0.0;
1060 /* FIXME: actually apply features */
1064 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1065 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1066 UINT32 text_len
, UINT16
const* glyph_indices
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1067 UINT32 glyph_count
, IDWriteFontFace
* font_face
, FLOAT fontEmSize
, FLOAT pixels_per_dip
,
1068 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1069 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1070 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
* glyph_advances
, DWRITE_GLYPH_OFFSET
* glyph_offsets
)
1072 FIXME("(%s %p %p %u %p %p %u %p %f %f %p %d %d %d %p %s %p %p %u %p %p): stub\n", debugstr_wn(text
, text_len
),
1073 clustermap
, props
, text_len
, glyph_indices
, glyph_props
, glyph_count
, font_face
, fontEmSize
, pixels_per_dip
,
1074 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
1075 feature_ranges
, glyph_advances
, glyph_offsets
);
1079 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1081 FLOAT advance
= 0.0;
1082 for (; start
< end
; start
++)
1083 advance
+= advances
[start
];
1087 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1088 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1089 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1091 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1092 FLOAT advance
= advances
[g
];
1095 if (props
[g
].isZeroWidthSpace
) {
1096 modified_advances
[g
] = advances
[g
];
1097 modified_offsets
[g
] = offsets
[g
];
1101 /* first apply negative spacing and check if we hit minimum width */
1102 if (leading_spacing
< 0.0) {
1103 advance
+= leading_spacing
;
1104 origin
-= leading_spacing
;
1106 if (trailing_spacing
< 0.0)
1107 advance
+= trailing_spacing
;
1109 if (advance
< min_advance_width
) {
1110 FLOAT half
= (min_advance_width
- advance
) / 2.0;
1114 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0)
1116 else if (leading_spacing
< 0.0)
1117 origin
-= min_advance_width
- advance
;
1119 advance
= min_advance_width
;
1122 /* now apply positive spacing adjustments */
1123 if (leading_spacing
> 0.0) {
1124 advance
+= leading_spacing
;
1125 origin
-= leading_spacing
;
1127 if (trailing_spacing
> 0.0)
1128 advance
+= trailing_spacing
;
1130 modified_advances
[g
] = advance
;
1131 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1132 /* ascender is never touched, it's orthogonal to reading direction and is not
1133 affected by advance adjustments */
1134 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1137 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1138 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1139 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1141 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1142 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1146 modified_advances
[start
] = advances
[start
];
1147 modified_advances
[end
-1] = advances
[end
-1];
1149 /* first apply negative spacing and check if we hit minimum width */
1150 if (leading_spacing
< 0.0) {
1151 advance
+= leading_spacing
;
1152 modified_advances
[start
] += leading_spacing
;
1153 origin
-= leading_spacing
;
1155 if (trailing_spacing
< 0.0) {
1156 advance
+= trailing_spacing
;
1157 modified_advances
[end
-1] += trailing_spacing
;
1159 if (advance
< min_advance_width
) {
1160 /* additional spacing is only applied to leading and trailing glyph */
1161 FLOAT half
= (min_advance_width
- advance
) / 2.0;
1165 modified_advances
[start
] += half
;
1166 modified_advances
[end
-1] += half
;
1168 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0) {
1170 modified_advances
[start
] += half
;
1171 modified_advances
[end
-1] += half
;
1173 else if (leading_spacing
< 0.0) {
1174 origin
-= min_advance_width
- advance
;
1175 modified_advances
[start
] += min_advance_width
- advance
;
1178 modified_advances
[end
-1] += min_advance_width
- advance
;
1180 advance
= min_advance_width
;
1183 /* now apply positive spacing adjustments */
1184 if (leading_spacing
> 0.0) {
1185 modified_advances
[start
] += leading_spacing
;
1186 origin
-= leading_spacing
;
1188 if (trailing_spacing
> 0.0)
1189 modified_advances
[end
-1] += trailing_spacing
;
1191 for (g
= start
; g
< end
; g
++) {
1193 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1194 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1196 else if (g
== end
- 1)
1197 /* trailing glyph offset is not adjusted */
1198 modified_offsets
[g
] = offsets
[g
];
1200 /* for all glyphs within a cluster use original advances and offsets */
1201 modified_advances
[g
] = advances
[g
];
1202 modified_offsets
[g
] = offsets
[g
];
1207 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1209 UINT16 g
= clustermap
[start
];
1212 while (start
< text_len
&& clustermap
[++start
] == g
)
1217 /* Applies spacing adjustments to clusters.
1219 Adjustments are applied in the following order:
1221 1. Negative adjustments
1223 Leading and trailing spacing could be negative, at this step
1224 only negative ones are actually applied. Leading spacing is only
1225 applied to leading glyph, trailing - to trailing glyph.
1227 2. Minimum advance width
1229 Advances could only be reduced at this point or unchanged. In any
1230 case it's checked if cluster advance width is less than minimum width.
1231 If it's the case advance width is incremented up to minimum value.
1233 Important part is the direction in which this increment is applied;
1234 it depends on from which directions total cluster advance was trimmed
1235 at step 1. So it could be incremented from leading, trailing, or both
1236 sides. When applied to both sides, each side gets half of difference
1237 that bring advance to minimum width.
1239 3. Positive adjustments
1241 After minimum width rule was applied, positive spacing is applied in same
1242 way as negative ones on step 1.
1244 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1245 keeps its position in coordinate system where initial advance width is counted
1250 It's known that isZeroWidthSpace property keeps initial advance from changing.
1252 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1253 with more than one glyph.
1256 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1257 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1258 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1259 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1263 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1264 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1266 if (min_advance_width
< 0.0) {
1267 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1268 return E_INVALIDARG
;
1271 /* minimum advance is not applied if no adjustments were made */
1272 if (leading_spacing
== 0.0 && trailing_spacing
== 0.0) {
1273 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1274 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1279 for (start
= 0; start
< len
;) {
1280 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1283 UINT32 g
= clustermap
[start
];
1285 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1286 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1289 UINT32 g_start
, g_end
;
1291 g_start
= clustermap
[start
];
1292 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1294 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1295 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1304 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1305 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1306 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1308 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1309 baseline_coord
, exists
);
1313 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1314 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1316 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1320 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1321 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1323 FIXME("(%d %d %p): stub\n", angle
, is_sideways
, transform
);
1327 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1328 DWRITE_SCRIPT_PROPERTIES
*props
)
1330 TRACE("(%u %p)\n", sa
.script
, props
);
1332 if (sa
.script
> Script_LastId
)
1333 return E_INVALIDARG
;
1335 *props
= dwritescripts_properties
[sa
.script
].props
;
1339 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1341 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1344 UINT16 script
= get_char_script(c
);
1345 return !dwritescripts_properties
[script
].is_complex
;
1349 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1350 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1355 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1361 return E_INVALIDARG
;
1368 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1369 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1370 if (is_char_from_simple_script(text
[i
])) {
1381 if (*is_simple
&& indices
) {
1382 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1384 return E_OUTOFMEMORY
;
1386 for (i
= 0; i
< *len_read
; i
++)
1387 codepoints
[i
] = text
[i
];
1389 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1390 heap_free(codepoints
);
1396 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1397 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1398 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1400 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1401 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1405 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1406 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1407 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1409 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1414 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1415 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1416 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1417 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1418 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1419 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1421 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
,
1422 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1423 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1427 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1428 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*transform
)
1430 FIXME("(%d %d %.2f %.2f %p): stub\n", angle
, is_sideways
, originX
, originY
, transform
);
1434 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1435 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1436 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1438 const struct dwritescript_properties
*props
;
1442 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1445 if (sa
.script
> Script_LastId
)
1446 return E_INVALIDARG
;
1448 language
= get_opentype_language(locale
);
1449 props
= &dwritescripts_properties
[sa
.script
];
1450 *actual_tagcount
= 0;
1452 if (props
->scriptalttag
)
1453 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1455 if (*actual_tagcount
== 0)
1456 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1461 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1462 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1463 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1465 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1470 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1471 dwritetextanalyzer_QueryInterface
,
1472 dwritetextanalyzer_AddRef
,
1473 dwritetextanalyzer_Release
,
1474 dwritetextanalyzer_AnalyzeScript
,
1475 dwritetextanalyzer_AnalyzeBidi
,
1476 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1477 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1478 dwritetextanalyzer_GetGlyphs
,
1479 dwritetextanalyzer_GetGlyphPlacements
,
1480 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1481 dwritetextanalyzer1_ApplyCharacterSpacing
,
1482 dwritetextanalyzer1_GetBaseline
,
1483 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1484 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1485 dwritetextanalyzer1_GetScriptProperties
,
1486 dwritetextanalyzer1_GetTextComplexity
,
1487 dwritetextanalyzer1_GetJustificationOpportunities
,
1488 dwritetextanalyzer1_JustifyGlyphAdvances
,
1489 dwritetextanalyzer1_GetJustifiedGlyphs
,
1490 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1491 dwritetextanalyzer2_GetTypographicFeatures
,
1492 dwritetextanalyzer2_CheckTypographicFeature
1495 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1497 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1499 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1503 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1505 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1507 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1509 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1510 IsEqualIID(riid
, &IID_IUnknown
))
1513 IDWriteNumberSubstitution_AddRef(iface
);
1519 return E_NOINTERFACE
;
1522 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1524 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1525 ULONG ref
= InterlockedIncrement(&This
->ref
);
1526 TRACE("(%p)->(%d)\n", This
, ref
);
1530 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1532 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1533 ULONG ref
= InterlockedDecrement(&This
->ref
);
1535 TRACE("(%p)->(%d)\n", This
, ref
);
1538 heap_free(This
->locale
);
1545 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1546 dwritenumbersubstitution_QueryInterface
,
1547 dwritenumbersubstitution_AddRef
,
1548 dwritenumbersubstitution_Release
1551 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1552 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1554 struct dwrite_numbersubstitution
*substitution
;
1558 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1559 return E_INVALIDARG
;
1561 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1562 return E_INVALIDARG
;
1564 substitution
= heap_alloc(sizeof(*substitution
));
1566 return E_OUTOFMEMORY
;
1568 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1569 substitution
->ref
= 1;
1570 substitution
->ignore_user_override
= ignore_user_override
;
1571 substitution
->method
= method
;
1572 substitution
->locale
= heap_strdupW(locale
);
1573 if (locale
&& !substitution
->locale
) {
1574 heap_free(substitution
);
1575 return E_OUTOFMEMORY
;
1578 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;