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 */
1062 IDWriteFontFace1_Release(fontface1
);
1066 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1067 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1068 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1069 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT pixels_per_dip
,
1070 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1071 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1072 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1074 DWRITE_FONT_METRICS metrics
;
1075 IDWriteFontFace1
*fontface1
;
1079 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
),
1080 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, pixels_per_dip
,
1081 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
1082 feature_ranges
, advances
, offsets
);
1084 if (glyph_count
== 0)
1087 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1089 WARN("failed to get IDWriteFontFace1.\n");
1093 IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, pixels_per_dip
, transform
, &metrics
);
1094 for (i
= 0; i
< glyph_count
; i
++) {
1097 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, pixels_per_dip
,
1098 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1102 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1103 offsets
[i
].advanceOffset
= 0.0;
1104 offsets
[i
].ascenderOffset
= 0.0;
1107 /* FIXME: actually apply features */
1109 IDWriteFontFace1_Release(fontface1
);
1113 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1115 FLOAT advance
= 0.0;
1116 for (; start
< end
; start
++)
1117 advance
+= advances
[start
];
1121 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1122 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1123 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1125 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1126 FLOAT advance
= advances
[g
];
1129 if (props
[g
].isZeroWidthSpace
) {
1130 modified_advances
[g
] = advances
[g
];
1131 modified_offsets
[g
] = offsets
[g
];
1135 /* first apply negative spacing and check if we hit minimum width */
1136 if (leading_spacing
< 0.0) {
1137 advance
+= leading_spacing
;
1138 origin
-= leading_spacing
;
1140 if (trailing_spacing
< 0.0)
1141 advance
+= trailing_spacing
;
1143 if (advance
< min_advance_width
) {
1144 FLOAT half
= (min_advance_width
- advance
) / 2.0;
1148 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0)
1150 else if (leading_spacing
< 0.0)
1151 origin
-= min_advance_width
- advance
;
1153 advance
= min_advance_width
;
1156 /* now apply positive spacing adjustments */
1157 if (leading_spacing
> 0.0) {
1158 advance
+= leading_spacing
;
1159 origin
-= leading_spacing
;
1161 if (trailing_spacing
> 0.0)
1162 advance
+= trailing_spacing
;
1164 modified_advances
[g
] = advance
;
1165 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1166 /* ascender is never touched, it's orthogonal to reading direction and is not
1167 affected by advance adjustments */
1168 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1171 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1172 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1173 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1175 BOOL reduced
= leading_spacing
< 0.0 || trailing_spacing
< 0.0;
1176 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1180 modified_advances
[start
] = advances
[start
];
1181 modified_advances
[end
-1] = advances
[end
-1];
1183 /* first apply negative spacing and check if we hit minimum width */
1184 if (leading_spacing
< 0.0) {
1185 advance
+= leading_spacing
;
1186 modified_advances
[start
] += leading_spacing
;
1187 origin
-= leading_spacing
;
1189 if (trailing_spacing
< 0.0) {
1190 advance
+= trailing_spacing
;
1191 modified_advances
[end
-1] += trailing_spacing
;
1193 if (advance
< min_advance_width
) {
1194 /* additional spacing is only applied to leading and trailing glyph */
1195 FLOAT half
= (min_advance_width
- advance
) / 2.0;
1199 modified_advances
[start
] += half
;
1200 modified_advances
[end
-1] += half
;
1202 else if (leading_spacing
< 0.0 && trailing_spacing
< 0.0) {
1204 modified_advances
[start
] += half
;
1205 modified_advances
[end
-1] += half
;
1207 else if (leading_spacing
< 0.0) {
1208 origin
-= min_advance_width
- advance
;
1209 modified_advances
[start
] += min_advance_width
- advance
;
1212 modified_advances
[end
-1] += min_advance_width
- advance
;
1214 advance
= min_advance_width
;
1217 /* now apply positive spacing adjustments */
1218 if (leading_spacing
> 0.0) {
1219 modified_advances
[start
] += leading_spacing
;
1220 origin
-= leading_spacing
;
1222 if (trailing_spacing
> 0.0)
1223 modified_advances
[end
-1] += trailing_spacing
;
1225 for (g
= start
; g
< end
; g
++) {
1227 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1228 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1230 else if (g
== end
- 1)
1231 /* trailing glyph offset is not adjusted */
1232 modified_offsets
[g
] = offsets
[g
];
1234 /* for all glyphs within a cluster use original advances and offsets */
1235 modified_advances
[g
] = advances
[g
];
1236 modified_offsets
[g
] = offsets
[g
];
1241 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1243 UINT16 g
= clustermap
[start
];
1246 while (start
< text_len
&& clustermap
[++start
] == g
)
1251 /* Applies spacing adjustments to clusters.
1253 Adjustments are applied in the following order:
1255 1. Negative adjustments
1257 Leading and trailing spacing could be negative, at this step
1258 only negative ones are actually applied. Leading spacing is only
1259 applied to leading glyph, trailing - to trailing glyph.
1261 2. Minimum advance width
1263 Advances could only be reduced at this point or unchanged. In any
1264 case it's checked if cluster advance width is less than minimum width.
1265 If it's the case advance width is incremented up to minimum value.
1267 Important part is the direction in which this increment is applied;
1268 it depends on from which directions total cluster advance was trimmed
1269 at step 1. So it could be incremented from leading, trailing, or both
1270 sides. When applied to both sides, each side gets half of difference
1271 that bring advance to minimum width.
1273 3. Positive adjustments
1275 After minimum width rule was applied, positive spacing is applied in same
1276 way as negative ones on step 1.
1278 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1279 keeps its position in coordinate system where initial advance width is counted
1284 It's known that isZeroWidthSpace property keeps initial advance from changing.
1286 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1287 with more than one glyph.
1290 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1291 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1292 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1293 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1297 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1298 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1300 if (min_advance_width
< 0.0) {
1301 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1302 return E_INVALIDARG
;
1305 /* minimum advance is not applied if no adjustments were made */
1306 if (leading_spacing
== 0.0 && trailing_spacing
== 0.0) {
1307 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1308 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1313 for (start
= 0; start
< len
;) {
1314 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1317 UINT32 g
= clustermap
[start
];
1319 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1320 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1323 UINT32 g_start
, g_end
;
1325 g_start
= clustermap
[start
];
1326 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1328 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1329 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1338 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1339 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1340 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1342 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1343 baseline_coord
, exists
);
1347 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1348 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1350 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1354 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1355 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1357 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1358 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1361 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1362 DWRITE_SCRIPT_PROPERTIES
*props
)
1364 TRACE("(%u %p)\n", sa
.script
, props
);
1366 if (sa
.script
> Script_LastId
)
1367 return E_INVALIDARG
;
1369 *props
= dwritescripts_properties
[sa
.script
].props
;
1373 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1375 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1378 UINT16 script
= get_char_script(c
);
1379 return !dwritescripts_properties
[script
].is_complex
;
1383 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1384 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1389 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1395 return E_INVALIDARG
;
1402 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1403 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1404 if (is_char_from_simple_script(text
[i
])) {
1415 if (*is_simple
&& indices
) {
1416 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1418 return E_OUTOFMEMORY
;
1420 for (i
= 0; i
< *len_read
; i
++)
1421 codepoints
[i
] = text
[i
];
1423 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1424 heap_free(codepoints
);
1430 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1431 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1432 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1434 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1435 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1439 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1440 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1441 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1443 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1448 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1449 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1450 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1451 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1452 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1453 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1455 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
,
1456 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1457 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1461 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1462 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1464 static const DWRITE_MATRIX transforms
[] = {
1465 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
1466 { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 },
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 }
1471 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1473 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1474 memset(m
, 0, sizeof(*m
));
1475 return E_INVALIDARG
;
1478 /* for sideways case simply rotate 90 degrees more */
1481 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1482 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1484 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1485 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1487 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1488 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1490 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1491 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1498 *m
= transforms
[angle
];
1500 /* shift components represent transform necessary to get from original point to
1501 rotated one in new coordinate system */
1502 if ((originX
!= 0.0 || originY
!= 0.0) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1503 const DWRITE_MATRIX
*p
;
1506 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1507 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1509 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1510 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1516 p
= &transforms
[angle
];
1517 m
->dx
= originX
- (p
->m11
* originX
+ p
->m12
* originY
);
1518 m
->dy
= originY
- (p
->m21
* originX
+ p
->m22
* originY
);
1524 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1525 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1526 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1528 const struct dwritescript_properties
*props
;
1532 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1535 if (sa
.script
> Script_LastId
)
1536 return E_INVALIDARG
;
1538 language
= get_opentype_language(locale
);
1539 props
= &dwritescripts_properties
[sa
.script
];
1540 *actual_tagcount
= 0;
1542 if (props
->scriptalttag
)
1543 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1545 if (*actual_tagcount
== 0)
1546 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1551 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1552 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1553 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1555 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1560 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1561 dwritetextanalyzer_QueryInterface
,
1562 dwritetextanalyzer_AddRef
,
1563 dwritetextanalyzer_Release
,
1564 dwritetextanalyzer_AnalyzeScript
,
1565 dwritetextanalyzer_AnalyzeBidi
,
1566 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1567 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1568 dwritetextanalyzer_GetGlyphs
,
1569 dwritetextanalyzer_GetGlyphPlacements
,
1570 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1571 dwritetextanalyzer1_ApplyCharacterSpacing
,
1572 dwritetextanalyzer1_GetBaseline
,
1573 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1574 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1575 dwritetextanalyzer1_GetScriptProperties
,
1576 dwritetextanalyzer1_GetTextComplexity
,
1577 dwritetextanalyzer1_GetJustificationOpportunities
,
1578 dwritetextanalyzer1_JustifyGlyphAdvances
,
1579 dwritetextanalyzer1_GetJustifiedGlyphs
,
1580 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1581 dwritetextanalyzer2_GetTypographicFeatures
,
1582 dwritetextanalyzer2_CheckTypographicFeature
1585 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1587 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1589 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1593 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1595 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1597 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1599 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1600 IsEqualIID(riid
, &IID_IUnknown
))
1603 IDWriteNumberSubstitution_AddRef(iface
);
1609 return E_NOINTERFACE
;
1612 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1614 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1615 ULONG ref
= InterlockedIncrement(&This
->ref
);
1616 TRACE("(%p)->(%d)\n", This
, ref
);
1620 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1622 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1623 ULONG ref
= InterlockedDecrement(&This
->ref
);
1625 TRACE("(%p)->(%d)\n", This
, ref
);
1628 heap_free(This
->locale
);
1635 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1636 dwritenumbersubstitution_QueryInterface
,
1637 dwritenumbersubstitution_AddRef
,
1638 dwritenumbersubstitution_Release
1641 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1642 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1644 struct dwrite_numbersubstitution
*substitution
;
1648 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1649 return E_INVALIDARG
;
1651 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1652 return E_INVALIDARG
;
1654 substitution
= heap_alloc(sizeof(*substitution
));
1656 return E_OUTOFMEMORY
;
1658 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1659 substitution
->ref
= 1;
1660 substitution
->ignore_user_override
= ignore_user_override
;
1661 substitution
->method
= method
;
1662 substitution
->locale
= heap_strdupW(locale
);
1663 if (locale
&& !substitution
->locale
) {
1664 heap_free(substitution
);
1665 return E_OUTOFMEMORY
;
1668 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;