Revert "ntdll: Avoid one of the calls to getattrlist() when read_directory_getattrlis...
[wine/multimedia.git] / dlls / dwrite / analyzer.c
blob8c2a8012403670ab9e5ee50140cd199c3f7e4280
1 /*
2 * Text analyzer
4 * Copyright 2011 Aric Stewart for CodeWeavers
5 * Copyright 2012, 2014 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include "dwrite_private.h"
25 #include "scripts.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 */
36 BOOL is_complex;
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 }
170 #undef _OT
172 struct dwrite_numbersubstitution {
173 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
174 LONG ref;
176 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
177 WCHAR *locale;
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);
191 return ch;
193 return 0;
196 static inline UINT16 get_char_script(WCHAR c)
198 UINT16 script = get_table_entry(wine_scripts_table, c);
199 if (script == Script_Unknown) {
200 WORD type;
201 if (GetStringTypeW(CT_CTYPE1, &c, 1, &type) && (type & C1_CNTRL))
202 script = Script_Common;
204 return script;
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);
216 pos = position;
217 length = 1;
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)
231 HRESULT hr;
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;
236 pos = position + i;
237 length = 1;
238 sa.script = script;
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;
249 UINT32 count;
252 enum BreakConditionLocation {
253 BreakConditionBefore,
254 BreakConditionAfter
257 enum linebreaking_classes {
258 b_BK = 1,
259 b_CR,
260 b_LF,
261 b_CM,
262 b_SG,
263 b_GL,
264 b_CB,
265 b_SP,
266 b_ZW,
267 b_NL,
268 b_WJ,
269 b_JL,
270 b_JV,
271 b_JT,
272 b_H2,
273 b_H3,
274 b_XX,
275 b_OP,
276 b_CL,
277 b_CP,
278 b_QU,
279 b_NS,
280 b_EX,
281 b_SY,
282 b_IS,
283 b_PR,
284 b_PO,
285 b_NU,
286 b_AL,
287 b_ID,
288 b_IN,
289 b_HY,
290 b_BB,
291 b_BA,
292 b_SA,
293 b_AI,
294 b_B2,
295 b_HL,
296 b_CJ,
297 b_RI
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)
307 return;
308 state->breakpoints[pos].breakConditionBefore = condition;
309 if (pos > 0)
310 state->breakpoints[pos-1].breakConditionAfter = condition;
312 else {
313 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
314 return;
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;
324 short *break_class;
325 int i, j;
327 break_class = heap_alloc(count*sizeof(short));
328 if (!break_class)
329 return E_OUTOFMEMORY;
331 state.breakpoints = breakpoints;
332 state.count = count;
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])
349 case b_AI:
350 case b_SA:
351 case b_SG:
352 case b_XX:
353 break_class[i] = b_AL;
354 break;
355 case b_CJ:
356 break_class[i] = b_NS;
357 break;
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])
369 /* LB4 - LB6 */
370 case b_CR:
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);
376 break;
378 case b_LF:
379 case b_NL:
380 case b_BK:
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);
385 break;
386 /* LB7 - do not break before spaces */
387 case b_SP:
388 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
389 break;
390 case b_ZW:
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)
394 i++;
395 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
396 break;
400 /* LB9 - LB10 */
401 for (i = 0; i < count; i++)
403 if (break_class[i] == b_CM)
405 if (i > 0)
407 switch (break_class[i-1])
409 case b_SP:
410 case b_BK:
411 case b_CR:
412 case b_LF:
413 case b_NL:
414 case b_ZW:
415 break_class[i] = b_AL;
416 break;
417 default:
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 */
430 case b_WJ:
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);
433 break;
434 /* LB12 - don't break after glue */
435 case b_GL:
436 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
437 /* LB12a */
438 if (i > 0)
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);
443 break;
444 /* LB13 */
445 case b_CL:
446 case b_CP:
447 case b_EX:
448 case b_IS:
449 case b_SY:
450 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
451 break;
452 /* LB14 */
453 case b_OP:
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);
457 i++;
459 break;
460 /* LB15 */
461 case b_QU:
462 j = i+1;
463 while (j < count-1 && break_class[j] == b_SP)
464 j++;
465 if (break_class[j] == b_OP)
466 for (; j > i; j--)
467 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
468 break;
469 /* LB16 */
470 case b_NS:
471 j = i-1;
472 while(j > 0 && break_class[j] == b_SP)
473 j--;
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);
477 break;
478 /* LB17 */
479 case b_B2:
480 j = i+1;
481 while (j < count && break_class[j] == b_SP)
482 j++;
483 if (break_class[j] == b_B2)
484 for (; j > i; j--)
485 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
486 break;
490 for (i = 0; i < count; i++)
492 switch(break_class[i])
494 /* LB18 - break is allowed after space */
495 case b_SP:
496 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
497 break;
498 /* LB19 - don't break before or after quotation mark */
499 case b_QU:
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);
502 break;
503 /* LB20 */
504 case b_CB:
505 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
506 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
507 break;
508 /* LB21 */
509 case b_BA:
510 case b_HY:
511 case b_NS:
512 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
513 break;
514 case b_BB:
515 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
516 break;
517 /* LB21a */
518 case b_HL:
519 if (i < count-2)
520 switch (break_class[i+1])
522 case b_HY:
523 case b_BA:
524 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
526 break;
527 /* LB22 */
528 case b_IN:
529 if (i > 0)
531 switch (break_class[i-1])
533 case b_AL:
534 case b_HL:
535 case b_ID:
536 case b_IN:
537 case b_NU:
538 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
541 break;
544 if (i < count-1)
546 /* LB23 */
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);
553 /* LB24 */
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);
561 /* LB25 */
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);
578 /* LB26 */
579 if (break_class[i] == b_JL)
581 switch (break_class[i+1])
583 case b_JL:
584 case b_JV:
585 case b_H2:
586 case b_H3:
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);
597 /* LB27 */
598 switch (break_class[i])
600 case b_JL:
601 case b_JV:
602 case b_JT:
603 case b_H2:
604 case b_H3:
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])
612 case b_JL:
613 case b_JV:
614 case b_JT:
615 case b_H2:
616 case b_H3:
617 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
621 /* LB28 */
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);
628 /* LB29 */
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);
633 /* LB30 */
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);
641 /* LB30a */
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);
648 return S_OK;
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))
660 *obj = iface;
661 return S_OK;
664 *obj = NULL;
665 return E_NOINTERFACE;
668 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
670 return 2;
673 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
675 return 1;
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)
682 HRESULT hr;
683 UINT32 len;
685 *buff = NULL;
686 *text = NULL;
687 len = 0;
688 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
689 if (FAILED(hr)) return hr;
691 if (len < length) {
692 UINT32 read;
694 *buff = heap_alloc(length*sizeof(WCHAR));
695 if (!*buff)
696 return E_OUTOFMEMORY;
697 memcpy(*buff, *text, len*sizeof(WCHAR));
698 read = len;
700 while (read < length && *text) {
701 *text = NULL;
702 len = 0;
703 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
704 if (FAILED(hr)) {
705 heap_free(*buff);
706 return hr;
708 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
709 read += len;
712 *text = *buff;
715 return hr;
718 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
719 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
721 WCHAR *buff = NULL;
722 const WCHAR *text;
723 HRESULT hr;
725 TRACE("(%p %u %u %p)\n", source, position, length, sink);
727 if (length == 0)
728 return S_OK;
730 hr = get_text_source_ptr(source, position, length, &text, &buff);
731 if (FAILED(hr))
732 return hr;
734 hr = analyze_script(text, position, length, sink);
735 heap_free(buff);
737 return hr;
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;
745 WCHAR *buff = NULL;
746 const WCHAR *text;
747 UINT32 pos, i;
748 HRESULT hr;
750 TRACE("(%p %u %u %p)\n", source, position, length, sink);
752 if (length == 0)
753 return S_OK;
755 hr = get_text_source_ptr(source, position, length, &text, &buff);
756 if (FAILED(hr))
757 return hr;
759 levels = heap_alloc(length*sizeof(*levels));
760 explicit = heap_alloc(length*sizeof(*explicit));
762 if (!levels || !explicit) {
763 hr = E_OUTOFMEMORY;
764 goto done;
767 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
768 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
769 if (FAILED(hr))
770 goto done;
772 level = levels[0];
773 explicit_level = explicit[0];
774 pos = 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);
778 if (FAILED(hr))
779 break;
780 level = levels[i];
781 explicit_level = explicit[i];
782 pos = i;
785 if (i == length - 1)
786 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, length - pos, explicit_level, level);
789 done:
790 heap_free(explicit);
791 heap_free(levels);
792 heap_free(buff);
794 return hr;
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);
801 return S_OK;
804 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
805 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
807 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
808 WCHAR *buff = NULL;
809 const WCHAR *text;
810 HRESULT hr;
811 UINT32 len;
813 TRACE("(%p %u %u %p)\n", source, position, length, sink);
815 if (length == 0)
816 return S_OK;
818 /* get some, check for length */
819 text = NULL;
820 len = 0;
821 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
822 if (FAILED(hr)) return hr;
824 if (len < length) {
825 UINT32 read;
827 buff = heap_alloc(length*sizeof(WCHAR));
828 if (!buff)
829 return E_OUTOFMEMORY;
830 memcpy(buff, text, len*sizeof(WCHAR));
831 read = len;
833 while (read < length && text) {
834 text = NULL;
835 len = 0;
836 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
837 if (FAILED(hr))
838 goto done;
839 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
840 read += len;
843 text = buff;
846 breakpoints = heap_alloc(length*sizeof(*breakpoints));
847 if (!breakpoints) {
848 hr = E_OUTOFMEMORY;
849 goto done;
852 hr = analyze_linebreaks(text, length, breakpoints);
853 if (FAILED(hr))
854 goto done;
856 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
858 done:
859 heap_free(breakpoints);
860 heap_free(buff);
862 return hr;
865 static UINT32 get_opentype_language(const WCHAR *locale)
867 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
869 if (locale) {
870 WCHAR tag[5];
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]);
875 return language;
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;
891 WCHAR *string;
892 UINT32 i, g;
893 HRESULT hr = S_OK;
894 UINT16 script;
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;
905 if (substitution)
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;
920 clustermap[i] = i;
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);
932 if (!string)
933 return E_OUTOFMEMORY;
935 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
936 if (FAILED(hr))
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++) {
942 UINT32 codepoint;
944 if (!update_cluster) {
945 codepoint = decode_surrogate_pair(text, i, length);
946 if (!codepoint) {
947 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
948 string[i] = codepoint;
950 else {
951 string[i] = text[i];
952 string[i+1] = text[i+1];
953 update_cluster = TRUE;
956 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
957 if (FAILED(hr))
958 goto done;
960 if (need_vertical) {
961 UINT16 vertical;
963 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
964 if (hr == S_OK)
965 glyph_indices[g] = vertical;
968 g++;
970 else {
971 INT32 k;
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++)
978 clustermap[k]--;
981 *actual_glyph_count = g;
983 hr = create_scriptshaping_cache(fontface, &cache);
984 if (FAILED(hr))
985 goto done;
987 context.cache = cache;
988 context.text = text;
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);
997 if (FAILED(hr))
998 goto done;
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);
1005 else
1006 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1008 done:
1009 if (fontface1)
1010 IDWriteFontFace1_Release(fontface1);
1011 release_scriptshaping_cache(cache);
1012 heap_free(string);
1014 return hr;
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;
1031 HRESULT hr;
1032 UINT32 i;
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)
1039 return S_OK;
1041 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1042 if (FAILED(hr)) {
1043 WARN("failed to get IDWriteFontFace1.\n");
1044 return hr;
1047 IDWriteFontFace_GetMetrics(fontface, &metrics);
1048 for (i = 0; i < glyph_count; i++) {
1049 INT32 a;
1051 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1052 if (FAILED(hr))
1053 a = 0;
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);
1063 return S_OK;
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;
1076 HRESULT hr;
1077 UINT32 i;
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)
1085 return S_OK;
1087 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1088 if (FAILED(hr)) {
1089 WARN("failed to get IDWriteFontFace1.\n");
1090 return hr;
1093 IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, pixels_per_dip, transform, &metrics);
1094 for (i = 0; i < glyph_count; i++) {
1095 INT32 a;
1097 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, pixels_per_dip,
1098 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1099 if (FAILED(hr))
1100 a = 0;
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);
1110 return S_OK;
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];
1118 return advance;
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];
1127 FLOAT origin = 0.0;
1129 if (props[g].isZeroWidthSpace) {
1130 modified_advances[g] = advances[g];
1131 modified_offsets[g] = offsets[g];
1132 return;
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;
1146 if (!reduced)
1147 origin -= half;
1148 else if (leading_spacing < 0.0 && trailing_spacing < 0.0)
1149 origin -= half;
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);
1177 FLOAT origin = 0.0;
1178 UINT16 g;
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;
1197 if (!reduced) {
1198 origin -= half;
1199 modified_advances[start] += half;
1200 modified_advances[end-1] += half;
1202 else if (leading_spacing < 0.0 && trailing_spacing < 0.0) {
1203 origin -= half;
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;
1211 else
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++) {
1226 if (g == start) {
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];
1233 else {
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];
1244 UINT32 length = 1;
1246 while (start < text_len && clustermap[++start] == g)
1247 length++;
1248 return length;
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
1280 from 0.
1282 Glyph properties
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)
1295 UINT16 start;
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));
1309 return S_OK;
1312 start = 0;
1313 for (start = 0; start < len;) {
1314 UINT32 length = get_cluster_length(clustermap, start, len);
1316 if (length == 1) {
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);
1322 else {
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);
1332 start += length;
1335 return S_OK;
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);
1344 return E_NOTIMPL;
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);
1351 return E_NOTIMPL;
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;
1370 return S_OK;
1373 static inline BOOL is_char_from_simple_script(WCHAR c)
1375 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1376 return FALSE;
1377 else {
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)
1386 HRESULT hr = S_OK;
1387 int i;
1389 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1391 *is_simple = FALSE;
1392 *len_read = 0;
1394 if (!face)
1395 return E_INVALIDARG;
1397 if (len == 0) {
1398 *is_simple = TRUE;
1399 return S_OK;
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])) {
1405 if (!*is_simple)
1406 break;
1408 else
1409 *is_simple = FALSE;
1412 *len_read = i;
1414 /* fetch indices */
1415 if (*is_simple && indices) {
1416 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1417 if (!codepoints)
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);
1427 return hr;
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);
1436 return E_NOTIMPL;
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,
1444 justifiedoffsets);
1445 return E_NOTIMPL;
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);
1458 return E_NOTIMPL;
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 */
1479 if (is_sideways) {
1480 switch (angle) {
1481 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1482 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1483 break;
1484 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1485 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1486 break;
1487 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1488 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1489 break;
1490 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1491 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1492 break;
1493 default:
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;
1505 switch (angle) {
1506 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1507 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1508 break;
1509 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1510 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1511 break;
1512 default:
1516 p = &transforms[angle];
1517 m->dx = originX - (p->m11 * originX + p->m12 * originY);
1518 m->dy = originY - (p->m21 * originX + p->m22 * originY);
1521 return S_OK;
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;
1529 HRESULT hr = S_OK;
1530 UINT32 language;
1532 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1533 tags);
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);
1548 return hr;
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,
1556 feature_applies);
1557 return E_NOTIMPL;
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;
1590 return S_OK;
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))
1602 *obj = iface;
1603 IDWriteNumberSubstitution_AddRef(iface);
1604 return S_OK;
1607 *obj = NULL;
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);
1617 return 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);
1627 if (!ref) {
1628 heap_free(This->locale);
1629 heap_free(This);
1632 return ref;
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;
1646 *ret = NULL;
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));
1655 if (!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;
1669 return S_OK;