riched20: Initial support for changing font properties.
[wine/multimedia.git] / dlls / dwrite / analyzer.c
blobb4be9f7cc1a3c704491943811630169808f261f1
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 */
1061 return S_OK;
1064 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1065 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1066 UINT32 text_len, UINT16 const* glyph_indices, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1067 UINT32 glyph_count, IDWriteFontFace * font_face, FLOAT fontEmSize, FLOAT pixels_per_dip,
1068 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1069 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1070 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT* glyph_advances, DWRITE_GLYPH_OFFSET* glyph_offsets)
1072 FIXME("(%s %p %p %u %p %p %u %p %f %f %p %d %d %d %p %s %p %p %u %p %p): stub\n", debugstr_wn(text, text_len),
1073 clustermap, props, text_len, glyph_indices, glyph_props, glyph_count, font_face, fontEmSize, pixels_per_dip,
1074 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
1075 feature_ranges, glyph_advances, glyph_offsets);
1076 return E_NOTIMPL;
1079 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1081 FLOAT advance = 0.0;
1082 for (; start < end; start++)
1083 advance += advances[start];
1084 return advance;
1087 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1088 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1089 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1091 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1092 FLOAT advance = advances[g];
1093 FLOAT origin = 0.0;
1095 if (props[g].isZeroWidthSpace) {
1096 modified_advances[g] = advances[g];
1097 modified_offsets[g] = offsets[g];
1098 return;
1101 /* first apply negative spacing and check if we hit minimum width */
1102 if (leading_spacing < 0.0) {
1103 advance += leading_spacing;
1104 origin -= leading_spacing;
1106 if (trailing_spacing < 0.0)
1107 advance += trailing_spacing;
1109 if (advance < min_advance_width) {
1110 FLOAT half = (min_advance_width - advance) / 2.0;
1112 if (!reduced)
1113 origin -= half;
1114 else if (leading_spacing < 0.0 && trailing_spacing < 0.0)
1115 origin -= half;
1116 else if (leading_spacing < 0.0)
1117 origin -= min_advance_width - advance;
1119 advance = min_advance_width;
1122 /* now apply positive spacing adjustments */
1123 if (leading_spacing > 0.0) {
1124 advance += leading_spacing;
1125 origin -= leading_spacing;
1127 if (trailing_spacing > 0.0)
1128 advance += trailing_spacing;
1130 modified_advances[g] = advance;
1131 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1132 /* ascender is never touched, it's orthogonal to reading direction and is not
1133 affected by advance adjustments */
1134 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1137 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1138 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1139 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1141 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1142 FLOAT advance = get_cluster_advance(advances, start, end);
1143 FLOAT origin = 0.0;
1144 UINT16 g;
1146 modified_advances[start] = advances[start];
1147 modified_advances[end-1] = advances[end-1];
1149 /* first apply negative spacing and check if we hit minimum width */
1150 if (leading_spacing < 0.0) {
1151 advance += leading_spacing;
1152 modified_advances[start] += leading_spacing;
1153 origin -= leading_spacing;
1155 if (trailing_spacing < 0.0) {
1156 advance += trailing_spacing;
1157 modified_advances[end-1] += trailing_spacing;
1159 if (advance < min_advance_width) {
1160 /* additional spacing is only applied to leading and trailing glyph */
1161 FLOAT half = (min_advance_width - advance) / 2.0;
1163 if (!reduced) {
1164 origin -= half;
1165 modified_advances[start] += half;
1166 modified_advances[end-1] += half;
1168 else if (leading_spacing < 0.0 && trailing_spacing < 0.0) {
1169 origin -= half;
1170 modified_advances[start] += half;
1171 modified_advances[end-1] += half;
1173 else if (leading_spacing < 0.0) {
1174 origin -= min_advance_width - advance;
1175 modified_advances[start] += min_advance_width - advance;
1177 else
1178 modified_advances[end-1] += min_advance_width - advance;
1180 advance = min_advance_width;
1183 /* now apply positive spacing adjustments */
1184 if (leading_spacing > 0.0) {
1185 modified_advances[start] += leading_spacing;
1186 origin -= leading_spacing;
1188 if (trailing_spacing > 0.0)
1189 modified_advances[end-1] += trailing_spacing;
1191 for (g = start; g < end; g++) {
1192 if (g == start) {
1193 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1194 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1196 else if (g == end - 1)
1197 /* trailing glyph offset is not adjusted */
1198 modified_offsets[g] = offsets[g];
1199 else {
1200 /* for all glyphs within a cluster use original advances and offsets */
1201 modified_advances[g] = advances[g];
1202 modified_offsets[g] = offsets[g];
1207 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1209 UINT16 g = clustermap[start];
1210 UINT32 length = 1;
1212 while (start < text_len && clustermap[++start] == g)
1213 length++;
1214 return length;
1217 /* Applies spacing adjustments to clusters.
1219 Adjustments are applied in the following order:
1221 1. Negative adjustments
1223 Leading and trailing spacing could be negative, at this step
1224 only negative ones are actually applied. Leading spacing is only
1225 applied to leading glyph, trailing - to trailing glyph.
1227 2. Minimum advance width
1229 Advances could only be reduced at this point or unchanged. In any
1230 case it's checked if cluster advance width is less than minimum width.
1231 If it's the case advance width is incremented up to minimum value.
1233 Important part is the direction in which this increment is applied;
1234 it depends on from which directions total cluster advance was trimmed
1235 at step 1. So it could be incremented from leading, trailing, or both
1236 sides. When applied to both sides, each side gets half of difference
1237 that bring advance to minimum width.
1239 3. Positive adjustments
1241 After minimum width rule was applied, positive spacing is applied in same
1242 way as negative ones on step 1.
1244 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1245 keeps its position in coordinate system where initial advance width is counted
1246 from 0.
1248 Glyph properties
1250 It's known that isZeroWidthSpace property keeps initial advance from changing.
1252 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1253 with more than one glyph.
1256 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1257 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1258 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1259 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1261 UINT16 start;
1263 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1264 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1266 if (min_advance_width < 0.0) {
1267 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1268 return E_INVALIDARG;
1271 /* minimum advance is not applied if no adjustments were made */
1272 if (leading_spacing == 0.0 && trailing_spacing == 0.0) {
1273 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1274 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1275 return S_OK;
1278 start = 0;
1279 for (start = 0; start < len;) {
1280 UINT32 length = get_cluster_length(clustermap, start, len);
1282 if (length == 1) {
1283 UINT32 g = clustermap[start];
1285 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1286 g, advances, offsets, props, modified_advances, modified_offsets);
1288 else {
1289 UINT32 g_start, g_end;
1291 g_start = clustermap[start];
1292 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1294 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1295 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1298 start += length;
1301 return S_OK;
1304 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1305 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1306 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1308 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1309 baseline_coord, exists);
1310 return E_NOTIMPL;
1313 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1314 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1316 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1317 return E_NOTIMPL;
1320 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1321 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1323 FIXME("(%d %d %p): stub\n", angle, is_sideways, transform);
1324 return E_NOTIMPL;
1327 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1328 DWRITE_SCRIPT_PROPERTIES *props)
1330 TRACE("(%u %p)\n", sa.script, props);
1332 if (sa.script > Script_LastId)
1333 return E_INVALIDARG;
1335 *props = dwritescripts_properties[sa.script].props;
1336 return S_OK;
1339 static inline BOOL is_char_from_simple_script(WCHAR c)
1341 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1342 return FALSE;
1343 else {
1344 UINT16 script = get_char_script(c);
1345 return !dwritescripts_properties[script].is_complex;
1349 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1350 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1352 HRESULT hr = S_OK;
1353 int i;
1355 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1357 *is_simple = FALSE;
1358 *len_read = 0;
1360 if (!face)
1361 return E_INVALIDARG;
1363 if (len == 0) {
1364 *is_simple = TRUE;
1365 return S_OK;
1368 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1369 for (i = 1; i < len && text[i]; i++) {
1370 if (is_char_from_simple_script(text[i])) {
1371 if (!*is_simple)
1372 break;
1374 else
1375 *is_simple = FALSE;
1378 *len_read = i;
1380 /* fetch indices */
1381 if (*is_simple && indices) {
1382 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1383 if (!codepoints)
1384 return E_OUTOFMEMORY;
1386 for (i = 0; i < *len_read; i++)
1387 codepoints[i] = text[i];
1389 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1390 heap_free(codepoints);
1393 return hr;
1396 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1397 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1398 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1400 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1401 debugstr_wn(text, length), clustermap, prop, jo);
1402 return E_NOTIMPL;
1405 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1406 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1407 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1409 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1410 justifiedoffsets);
1411 return E_NOTIMPL;
1414 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1415 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1416 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1417 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1418 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1419 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1421 FIXME("(%p %.2f %u %u %u %u %p %p %p %p %p %p %p %p %p %p %p): stub\n", face, font_em_size, sa.script,
1422 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1423 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1424 return E_NOTIMPL;
1427 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1428 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *transform)
1430 FIXME("(%d %d %.2f %.2f %p): stub\n", angle, is_sideways, originX, originY, transform);
1431 return E_NOTIMPL;
1434 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1435 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1436 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1438 const struct dwritescript_properties *props;
1439 HRESULT hr = S_OK;
1440 UINT32 language;
1442 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1443 tags);
1445 if (sa.script > Script_LastId)
1446 return E_INVALIDARG;
1448 language = get_opentype_language(locale);
1449 props = &dwritescripts_properties[sa.script];
1450 *actual_tagcount = 0;
1452 if (props->scriptalttag)
1453 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1455 if (*actual_tagcount == 0)
1456 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1458 return hr;
1461 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1462 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1463 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1465 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1466 feature_applies);
1467 return E_NOTIMPL;
1470 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1471 dwritetextanalyzer_QueryInterface,
1472 dwritetextanalyzer_AddRef,
1473 dwritetextanalyzer_Release,
1474 dwritetextanalyzer_AnalyzeScript,
1475 dwritetextanalyzer_AnalyzeBidi,
1476 dwritetextanalyzer_AnalyzeNumberSubstitution,
1477 dwritetextanalyzer_AnalyzeLineBreakpoints,
1478 dwritetextanalyzer_GetGlyphs,
1479 dwritetextanalyzer_GetGlyphPlacements,
1480 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1481 dwritetextanalyzer1_ApplyCharacterSpacing,
1482 dwritetextanalyzer1_GetBaseline,
1483 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1484 dwritetextanalyzer1_GetGlyphOrientationTransform,
1485 dwritetextanalyzer1_GetScriptProperties,
1486 dwritetextanalyzer1_GetTextComplexity,
1487 dwritetextanalyzer1_GetJustificationOpportunities,
1488 dwritetextanalyzer1_JustifyGlyphAdvances,
1489 dwritetextanalyzer1_GetJustifiedGlyphs,
1490 dwritetextanalyzer2_GetGlyphOrientationTransform,
1491 dwritetextanalyzer2_GetTypographicFeatures,
1492 dwritetextanalyzer2_CheckTypographicFeature
1495 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1497 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1499 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1500 return S_OK;
1503 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1505 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1507 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1509 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1510 IsEqualIID(riid, &IID_IUnknown))
1512 *obj = iface;
1513 IDWriteNumberSubstitution_AddRef(iface);
1514 return S_OK;
1517 *obj = NULL;
1519 return E_NOINTERFACE;
1522 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1524 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1525 ULONG ref = InterlockedIncrement(&This->ref);
1526 TRACE("(%p)->(%d)\n", This, ref);
1527 return ref;
1530 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1532 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1533 ULONG ref = InterlockedDecrement(&This->ref);
1535 TRACE("(%p)->(%d)\n", This, ref);
1537 if (!ref) {
1538 heap_free(This->locale);
1539 heap_free(This);
1542 return ref;
1545 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1546 dwritenumbersubstitution_QueryInterface,
1547 dwritenumbersubstitution_AddRef,
1548 dwritenumbersubstitution_Release
1551 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1552 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1554 struct dwrite_numbersubstitution *substitution;
1556 *ret = NULL;
1558 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1559 return E_INVALIDARG;
1561 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1562 return E_INVALIDARG;
1564 substitution = heap_alloc(sizeof(*substitution));
1565 if (!substitution)
1566 return E_OUTOFMEMORY;
1568 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1569 substitution->ref = 1;
1570 substitution->ignore_user_override = ignore_user_override;
1571 substitution->method = method;
1572 substitution->locale = heap_strdupW(locale);
1573 if (locale && !substitution->locale) {
1574 heap_free(substitution);
1575 return E_OUTOFMEMORY;
1578 *ret = &substitution->IDWriteNumberSubstitution_iface;
1579 return S_OK;