tools: Allow running installed builtin apps even when the 32-bit loader is missing.
[wine.git] / dlls / dwrite / analyzer.c
blob723a762b81c566004337fdb1517f5ed6bf1c5b26
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 <math.h>
26 #include "dwrite_private.h"
27 #include "scripts.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
31 extern const unsigned short wine_linebreak_table[];
32 extern const unsigned short wine_scripts_table[];
34 struct dwritescript_properties {
35 DWRITE_SCRIPT_PROPERTIES props;
36 UINT32 scripttag; /* OpenType script tag */
37 UINT32 scriptalttag; /* Version 2 tag, 0 is not defined */
38 BOOL is_complex;
39 const struct scriptshaping_ops *ops;
42 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
44 /* NOTE: keep this array synced with script ids from scripts.h */
45 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
46 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
47 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
48 { /* Ahom */ { 0x6d6f6841, 338, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
49 { /* Hluw */ { 0x77756c48, 80, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE },
51 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
52 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
53 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
54 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
55 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
56 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
57 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE },
58 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
59 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
60 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE },
61 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
62 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
63 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE },
64 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
65 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
66 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('c','a','k','m') },
67 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
68 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE },
69 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
70 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
71 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
72 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
73 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE },
74 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE },
75 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
76 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
77 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
78 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE },
79 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
80 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
81 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
82 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
83 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
84 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE },
85 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE },
86 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
87 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE },
88 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
89 { /* Hatr */ { 0x72746148, 127, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
90 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE },
91 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
92 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
93 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
94 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
95 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
96 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
97 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE },
98 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
99 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
100 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
101 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE },
102 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
103 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
104 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE },
105 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE, &latn_shaping_ops },
106 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
107 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
108 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
109 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
110 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
111 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
112 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
113 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE },
115 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
116 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
117 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
118 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
119 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','c') },
120 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','o') },
121 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
122 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
123 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE },
124 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
125 { /* Mult */ { 0x746c754d, 323, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
126 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE },
127 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
128 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE },
129 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE },
130 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE },
131 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
132 { /* Hung */ { 0x676e7548, 176, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
133 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
134 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
135 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
136 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE },
137 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
138 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
139 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE },
140 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE },
141 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
142 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
143 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
144 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE },
145 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
146 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
147 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
148 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE },
149 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
150 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
151 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','h','r','d') },
152 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
153 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
154 { /* Sgnw */ { 0x776e6753, 95, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
155 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE },
156 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','o','r','a') },
157 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
158 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
159 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE },
160 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
161 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
162 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE },
163 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
164 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
165 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('t','a','k','r') },
166 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE },
167 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE },
168 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE },
169 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE },
170 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE },
171 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE },
172 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
173 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
174 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE },
175 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
176 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE }
178 #undef _OT
180 struct dwrite_numbersubstitution {
181 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
182 LONG ref;
184 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
185 WCHAR *locale;
186 BOOL ignore_user_override;
189 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
191 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
194 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
196 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
197 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
198 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
199 return ch;
201 return 0;
204 static inline UINT16 get_char_script(WCHAR c)
206 UINT16 script = get_table_entry(wine_scripts_table, c);
207 if (script == Script_Unknown) {
208 WORD type;
209 if (GetStringTypeW(CT_CTYPE1, &c, 1, &type) && (type & C1_CNTRL))
210 script = Script_Common;
212 return script;
215 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 len, IDWriteTextAnalysisSink *sink)
217 DWRITE_SCRIPT_ANALYSIS sa;
218 UINT32 pos, i, length;
220 if (!len) return S_OK;
222 sa.script = get_char_script(*text);
224 pos = position;
225 length = 1;
227 for (i = 1; i < len; i++)
229 UINT16 script = get_char_script(text[i]);
231 /* Unknown type is ignored when preceded or followed by another script */
232 if (sa.script == Script_Unknown) sa.script = script;
233 if (script == Script_Unknown && sa.script != Script_Common) script = sa.script;
234 /* this is a length of a sequence to be reported next */
235 if (sa.script == script) length++;
237 if (sa.script != script)
239 HRESULT hr;
241 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
242 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
243 if (FAILED(hr)) return hr;
244 pos = position + i;
245 length = 1;
246 sa.script = script;
250 /* 1 length case or normal completion call */
251 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
252 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
255 struct linebreaking_state {
256 DWRITE_LINE_BREAKPOINT *breakpoints;
257 UINT32 count;
260 enum BreakConditionLocation {
261 BreakConditionBefore,
262 BreakConditionAfter
265 enum linebreaking_classes {
266 b_BK = 1,
267 b_CR,
268 b_LF,
269 b_CM,
270 b_SG,
271 b_GL,
272 b_CB,
273 b_SP,
274 b_ZW,
275 b_NL,
276 b_WJ,
277 b_JL,
278 b_JV,
279 b_JT,
280 b_H2,
281 b_H3,
282 b_XX,
283 b_OP,
284 b_CL,
285 b_CP,
286 b_QU,
287 b_NS,
288 b_EX,
289 b_SY,
290 b_IS,
291 b_PR,
292 b_PO,
293 b_NU,
294 b_AL,
295 b_ID,
296 b_IN,
297 b_HY,
298 b_BB,
299 b_BA,
300 b_SA,
301 b_AI,
302 b_B2,
303 b_HL,
304 b_CJ,
305 b_RI
308 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
309 set to "can break" and could only be changed once. */
310 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
311 struct linebreaking_state *state)
313 if (location == BreakConditionBefore) {
314 if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK)
315 return;
316 state->breakpoints[pos].breakConditionBefore = condition;
317 if (pos > 0)
318 state->breakpoints[pos-1].breakConditionAfter = condition;
320 else {
321 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
322 return;
323 state->breakpoints[pos].breakConditionAfter = condition;
324 if (pos + 1 < state->count)
325 state->breakpoints[pos+1].breakConditionBefore = condition;
329 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
331 struct linebreaking_state state;
332 short *break_class;
333 int i, j;
335 break_class = heap_alloc(count*sizeof(short));
336 if (!break_class)
337 return E_OUTOFMEMORY;
339 state.breakpoints = breakpoints;
340 state.count = count;
342 /* LB31 - allow breaks everywhere. It will be overridden if needed as
343 other rules dictate. */
344 for (i = 0; i < count; i++)
346 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
348 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK;
349 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK;
350 breakpoints[i].isWhitespace = break_class[i] == b_BK || break_class[i] == b_ZW || break_class[i] == b_SP || isspaceW(text[i]);
351 breakpoints[i].isSoftHyphen = FALSE;
352 breakpoints[i].padding = 0;
354 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
355 switch (break_class[i])
357 case b_AI:
358 case b_SA:
359 case b_SG:
360 case b_XX:
361 break_class[i] = b_AL;
362 break;
363 case b_CJ:
364 break_class[i] = b_NS;
365 break;
369 /* LB2 - never break at the start */
370 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
371 /* LB3 - always break at the end. This one is ignored. */
373 for (i = 0; i < count; i++)
375 switch (break_class[i])
377 /* LB4 - LB6 */
378 case b_CR:
379 /* LB5 - don't break CR x LF */
380 if (i < count-1 && break_class[i+1] == b_LF)
382 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
383 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
384 break;
386 case b_LF:
387 case b_NL:
388 case b_BK:
389 /* LB4 - LB5 - always break after hard breaks */
390 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
391 /* LB6 - do not break before hard breaks */
392 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
393 break;
394 /* LB7 - do not break before spaces */
395 case b_SP:
396 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
397 break;
398 case b_ZW:
399 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
400 /* LB8 - break before character after zero-width space, skip spaces in-between */
401 while (i < count-1 && break_class[i+1] == b_SP)
402 i++;
403 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
404 break;
408 /* LB9 - LB10 */
409 for (i = 0; i < count; i++)
411 if (break_class[i] == b_CM)
413 if (i > 0)
415 switch (break_class[i-1])
417 case b_SP:
418 case b_BK:
419 case b_CR:
420 case b_LF:
421 case b_NL:
422 case b_ZW:
423 break_class[i] = b_AL;
424 break;
425 default:
426 break_class[i] = break_class[i-1];
429 else break_class[i] = b_AL;
433 for (i = 0; i < count; i++)
435 switch (break_class[i])
437 /* LB11 - don't break before and after word joiner */
438 case b_WJ:
439 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
440 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
441 break;
442 /* LB12 - don't break after glue */
443 case b_GL:
444 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
445 /* LB12a */
446 if (i > 0)
448 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
449 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
451 break;
452 /* LB13 */
453 case b_CL:
454 case b_CP:
455 case b_EX:
456 case b_IS:
457 case b_SY:
458 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
459 break;
460 /* LB14 */
461 case b_OP:
462 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
463 while (i < count-1 && break_class[i+1] == b_SP) {
464 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
465 i++;
467 break;
468 /* LB15 */
469 case b_QU:
470 j = i+1;
471 while (j < count-1 && break_class[j] == b_SP)
472 j++;
473 if (break_class[j] == b_OP)
474 for (; j > i; j--)
475 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
476 break;
477 /* LB16 */
478 case b_NS:
479 j = i-1;
480 while(j > 0 && break_class[j] == b_SP)
481 j--;
482 if (break_class[j] == b_CL || break_class[j] == b_CP)
483 for (j++; j <= i; j++)
484 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
485 break;
486 /* LB17 */
487 case b_B2:
488 j = i+1;
489 while (j < count && break_class[j] == b_SP)
490 j++;
491 if (break_class[j] == b_B2)
492 for (; j > i; j--)
493 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
494 break;
498 for (i = 0; i < count; i++)
500 switch(break_class[i])
502 /* LB18 - break is allowed after space */
503 case b_SP:
504 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
505 break;
506 /* LB19 - don't break before or after quotation mark */
507 case b_QU:
508 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
509 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
510 break;
511 /* LB20 */
512 case b_CB:
513 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
514 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
515 break;
516 /* LB21 */
517 case b_BA:
518 case b_HY:
519 case b_NS:
520 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
521 break;
522 case b_BB:
523 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
524 break;
525 /* LB21a */
526 case b_HL:
527 if (i < count-2)
528 switch (break_class[i+1])
530 case b_HY:
531 case b_BA:
532 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
534 break;
535 /* LB22 */
536 case b_IN:
537 if (i > 0)
539 switch (break_class[i-1])
541 case b_AL:
542 case b_HL:
543 case b_ID:
544 case b_IN:
545 case b_NU:
546 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
549 break;
552 if (i < count-1)
554 /* LB23 */
555 if ((break_class[i] == b_ID && break_class[i+1] == b_PO) ||
556 (break_class[i] == b_AL && break_class[i+1] == b_NU) ||
557 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
558 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
559 (break_class[i] == b_NU && break_class[i+1] == b_HL))
560 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
561 /* LB24 */
562 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
563 (break_class[i] == b_PR && break_class[i+1] == b_AL) ||
564 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
565 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
566 (break_class[i] == b_PO && break_class[i+1] == b_HL))
567 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
569 /* LB25 */
570 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
571 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
572 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
573 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
574 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
575 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
576 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
577 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
578 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
579 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
580 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
581 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
582 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
583 (break_class[i] == b_SY && break_class[i+1] == b_NU))
584 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
586 /* LB26 */
587 if (break_class[i] == b_JL)
589 switch (break_class[i+1])
591 case b_JL:
592 case b_JV:
593 case b_H2:
594 case b_H3:
595 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
598 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
599 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
600 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
601 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
602 break_class[i+1] == b_JT)
603 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
605 /* LB27 */
606 switch (break_class[i])
608 case b_JL:
609 case b_JV:
610 case b_JT:
611 case b_H2:
612 case b_H3:
613 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
614 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
616 if (break_class[i] == b_PO)
618 switch (break_class[i+1])
620 case b_JL:
621 case b_JV:
622 case b_JT:
623 case b_H2:
624 case b_H3:
625 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
629 /* LB28 */
630 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
631 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
632 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
633 (break_class[i] == b_HL && break_class[i+1] == b_HL))
634 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
636 /* LB29 */
637 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
638 (break_class[i] == b_IS && break_class[i+1] == b_HL))
639 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
641 /* LB30 */
642 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
643 break_class[i+1] == b_OP)
644 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
645 if (break_class[i] == b_CP &&
646 (break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU))
647 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
649 /* LB30a */
650 if (break_class[i] == b_RI && break_class[i+1] == b_RI)
651 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
655 heap_free(break_class);
656 return S_OK;
659 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
661 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
663 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
664 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
665 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
666 IsEqualIID(riid, &IID_IUnknown))
668 *obj = iface;
669 return S_OK;
672 *obj = NULL;
673 return E_NOINTERFACE;
676 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
678 return 2;
681 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
683 return 1;
686 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
687 data after a first request. */
688 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
690 HRESULT hr;
691 UINT32 len;
693 *buff = NULL;
694 *text = NULL;
695 len = 0;
696 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
697 if (FAILED(hr)) return hr;
699 if (len < length) {
700 UINT32 read;
702 *buff = heap_alloc(length*sizeof(WCHAR));
703 if (!*buff)
704 return E_OUTOFMEMORY;
705 memcpy(*buff, *text, len*sizeof(WCHAR));
706 read = len;
708 while (read < length && *text) {
709 *text = NULL;
710 len = 0;
711 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
712 if (FAILED(hr)) {
713 heap_free(*buff);
714 return hr;
716 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
717 read += len;
720 *text = *buff;
723 return hr;
726 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
727 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
729 WCHAR *buff = NULL;
730 const WCHAR *text;
731 HRESULT hr;
733 TRACE("(%p %u %u %p)\n", source, position, length, sink);
735 if (length == 0)
736 return S_OK;
738 hr = get_text_source_ptr(source, position, length, &text, &buff);
739 if (FAILED(hr))
740 return hr;
742 hr = analyze_script(text, position, length, sink);
743 heap_free(buff);
745 return hr;
748 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
749 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
751 UINT8 *levels = NULL, *explicit = NULL;
752 UINT8 baselevel, level, explicit_level;
753 WCHAR *buff = NULL;
754 const WCHAR *text;
755 UINT32 pos, i;
756 HRESULT hr;
758 TRACE("(%p %u %u %p)\n", source, position, length, sink);
760 if (length == 0)
761 return S_OK;
763 hr = get_text_source_ptr(source, position, length, &text, &buff);
764 if (FAILED(hr))
765 return hr;
767 levels = heap_alloc(length*sizeof(*levels));
768 explicit = heap_alloc(length*sizeof(*explicit));
770 if (!levels || !explicit) {
771 hr = E_OUTOFMEMORY;
772 goto done;
775 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
776 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
777 if (FAILED(hr))
778 goto done;
780 level = levels[0];
781 explicit_level = explicit[0];
782 pos = 0;
783 for (i = 1; i < length; i++) {
784 if (levels[i] != level || explicit[i] != explicit_level) {
785 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, i - pos, explicit_level, level);
786 if (FAILED(hr))
787 break;
788 level = levels[i];
789 explicit_level = explicit[i];
790 pos = i;
793 if (i == length - 1)
794 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, length - pos, explicit_level, level);
797 done:
798 heap_free(explicit);
799 heap_free(levels);
800 heap_free(buff);
802 return hr;
805 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
806 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
808 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
809 return S_OK;
812 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
813 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
815 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
816 WCHAR *buff = NULL;
817 const WCHAR *text;
818 HRESULT hr;
819 UINT32 len;
821 TRACE("(%p %u %u %p)\n", source, position, length, sink);
823 if (length == 0)
824 return S_OK;
826 /* get some, check for length */
827 text = NULL;
828 len = 0;
829 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
830 if (FAILED(hr)) return hr;
832 if (len < length) {
833 UINT32 read;
835 buff = heap_alloc(length*sizeof(WCHAR));
836 if (!buff)
837 return E_OUTOFMEMORY;
838 memcpy(buff, text, len*sizeof(WCHAR));
839 read = len;
841 while (read < length && text) {
842 text = NULL;
843 len = 0;
844 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
845 if (FAILED(hr))
846 goto done;
847 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
848 read += len;
851 text = buff;
854 breakpoints = heap_alloc(length*sizeof(*breakpoints));
855 if (!breakpoints) {
856 hr = E_OUTOFMEMORY;
857 goto done;
860 hr = analyze_linebreaks(text, length, breakpoints);
861 if (FAILED(hr))
862 goto done;
864 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
866 done:
867 heap_free(breakpoints);
868 heap_free(buff);
870 return hr;
873 static UINT32 get_opentype_language(const WCHAR *locale)
875 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
877 if (locale) {
878 WCHAR tag[5];
879 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
880 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
883 return language;
886 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
887 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
888 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
889 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
890 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
891 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
892 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
894 const struct dwritescript_properties *scriptprops;
895 struct scriptshaping_context context;
896 struct scriptshaping_cache *cache = NULL;
897 BOOL update_cluster, need_vertical;
898 IDWriteFontFace1 *fontface1;
899 WCHAR *string;
900 UINT32 i, g;
901 HRESULT hr = S_OK;
902 UINT16 script;
904 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
905 length, fontface, is_sideways, is_rtl, analysis, debugstr_w(locale), substitution, features, feature_range_len,
906 feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices, glyph_props, actual_glyph_count);
908 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
910 if (max_glyph_count < length)
911 return E_NOT_SUFFICIENT_BUFFER;
913 if (substitution)
914 FIXME("number substitution is not supported.\n");
916 for (i = 0; i < length; i++) {
917 /* FIXME: set to better values */
918 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
919 glyph_props[i].isClusterStart = 1;
920 glyph_props[i].isDiacritic = 0;
921 glyph_props[i].isZeroWidthSpace = 0;
922 glyph_props[i].reserved = 0;
924 /* FIXME: have the shaping engine set this */
925 text_props[i].isShapedAlone = 0;
926 text_props[i].reserved = 0;
928 clustermap[i] = i;
931 for (; i < max_glyph_count; i++) {
932 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
933 glyph_props[i].isClusterStart = 0;
934 glyph_props[i].isDiacritic = 0;
935 glyph_props[i].isZeroWidthSpace = 0;
936 glyph_props[i].reserved = 0;
939 string = heap_alloc(sizeof(WCHAR)*length);
940 if (!string)
941 return E_OUTOFMEMORY;
943 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
944 if (FAILED(hr))
945 WARN("failed to get IDWriteFontFace1\n");
947 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
949 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
950 UINT32 codepoint;
952 if (!update_cluster) {
953 codepoint = decode_surrogate_pair(text, i, length);
954 if (!codepoint) {
955 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
956 string[i] = codepoint;
958 else {
959 string[i] = text[i];
960 string[i+1] = text[i+1];
961 update_cluster = TRUE;
964 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
965 if (FAILED(hr))
966 goto done;
968 if (need_vertical) {
969 UINT16 vertical;
971 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
972 if (hr == S_OK)
973 glyph_indices[g] = vertical;
976 g++;
978 else {
979 INT32 k;
981 update_cluster = FALSE;
982 /* mark surrogate halves with same cluster */
983 clustermap[i] = clustermap[i-1];
984 /* update following clusters */
985 for (k = i + 1; k >= 0 && k < length; k++)
986 clustermap[k]--;
989 *actual_glyph_count = g;
991 hr = create_scriptshaping_cache(fontface, &cache);
992 if (FAILED(hr))
993 goto done;
995 context.cache = cache;
996 context.text = text;
997 context.length = length;
998 context.is_rtl = is_rtl;
999 context.max_glyph_count = max_glyph_count;
1000 context.language_tag = get_opentype_language(locale);
1002 scriptprops = &dwritescripts_properties[script];
1003 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
1004 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
1005 if (FAILED(hr))
1006 goto done;
1009 /* FIXME: apply default features */
1011 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1012 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1013 else
1014 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1016 done:
1017 if (fontface1)
1018 IDWriteFontFace1_Release(fontface1);
1019 release_scriptshaping_cache(cache);
1020 heap_free(string);
1022 return hr;
1025 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1026 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1027 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1028 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1029 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1030 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1032 DWRITE_FONT_METRICS metrics;
1033 IDWriteFontFace1 *fontface1;
1034 HRESULT hr;
1035 UINT32 i;
1037 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),
1038 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1039 is_rtl, analysis, debugstr_w(locale), features, feature_range_len, feature_ranges, advances, offsets);
1041 if (glyph_count == 0)
1042 return S_OK;
1044 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1045 if (FAILED(hr)) {
1046 WARN("failed to get IDWriteFontFace1.\n");
1047 return hr;
1050 IDWriteFontFace_GetMetrics(fontface, &metrics);
1051 for (i = 0; i < glyph_count; i++) {
1052 INT32 a;
1054 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1055 if (FAILED(hr))
1056 a = 0;
1058 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1059 offsets[i].advanceOffset = 0.0;
1060 offsets[i].ascenderOffset = 0.0;
1063 /* FIXME: actually apply features */
1065 IDWriteFontFace1_Release(fontface1);
1066 return S_OK;
1069 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1070 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1071 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1072 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1073 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1074 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1075 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1077 DWRITE_FONT_METRICS metrics;
1078 IDWriteFontFace1 *fontface1;
1079 HRESULT hr;
1080 UINT32 i;
1082 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),
1083 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1084 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
1085 feature_ranges, advances, offsets);
1087 if (glyph_count == 0)
1088 return S_OK;
1090 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1091 if (FAILED(hr)) {
1092 WARN("failed to get IDWriteFontFace1.\n");
1093 return hr;
1096 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1097 if (FAILED(hr)) {
1098 IDWriteFontFace1_Release(fontface1);
1099 WARN("failed to get compat metrics, 0x%08x\n", hr);
1100 return hr;
1102 for (i = 0; i < glyph_count; i++) {
1103 INT32 a;
1105 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1106 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1107 if (FAILED(hr))
1108 advances[i] = 0.0;
1109 else
1110 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1111 offsets[i].advanceOffset = 0.0;
1112 offsets[i].ascenderOffset = 0.0;
1115 /* FIXME: actually apply features */
1117 IDWriteFontFace1_Release(fontface1);
1118 return S_OK;
1121 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1123 FLOAT advance = 0.0;
1124 for (; start < end; start++)
1125 advance += advances[start];
1126 return advance;
1129 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1130 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1131 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1133 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1134 FLOAT advance = advances[g];
1135 FLOAT origin = 0.0;
1137 if (props[g].isZeroWidthSpace) {
1138 modified_advances[g] = advances[g];
1139 modified_offsets[g] = offsets[g];
1140 return;
1143 /* first apply negative spacing and check if we hit minimum width */
1144 if (leading_spacing < 0.0) {
1145 advance += leading_spacing;
1146 origin -= leading_spacing;
1148 if (trailing_spacing < 0.0)
1149 advance += trailing_spacing;
1151 if (advance < min_advance_width) {
1152 FLOAT half = (min_advance_width - advance) / 2.0;
1154 if (!reduced)
1155 origin -= half;
1156 else if (leading_spacing < 0.0 && trailing_spacing < 0.0)
1157 origin -= half;
1158 else if (leading_spacing < 0.0)
1159 origin -= min_advance_width - advance;
1161 advance = min_advance_width;
1164 /* now apply positive spacing adjustments */
1165 if (leading_spacing > 0.0) {
1166 advance += leading_spacing;
1167 origin -= leading_spacing;
1169 if (trailing_spacing > 0.0)
1170 advance += trailing_spacing;
1172 modified_advances[g] = advance;
1173 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1174 /* ascender is never touched, it's orthogonal to reading direction and is not
1175 affected by advance adjustments */
1176 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1179 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1180 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1181 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1183 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1184 FLOAT advance = get_cluster_advance(advances, start, end);
1185 FLOAT origin = 0.0;
1186 UINT16 g;
1188 modified_advances[start] = advances[start];
1189 modified_advances[end-1] = advances[end-1];
1191 /* first apply negative spacing and check if we hit minimum width */
1192 if (leading_spacing < 0.0) {
1193 advance += leading_spacing;
1194 modified_advances[start] += leading_spacing;
1195 origin -= leading_spacing;
1197 if (trailing_spacing < 0.0) {
1198 advance += trailing_spacing;
1199 modified_advances[end-1] += trailing_spacing;
1202 advance = min_advance_width - advance;
1203 if (advance > 0.0f) {
1204 /* additional spacing is only applied to leading and trailing glyph */
1205 FLOAT half = advance / 2.0f;
1207 if (!reduced) {
1208 origin -= half;
1209 modified_advances[start] += half;
1210 modified_advances[end-1] += half;
1212 else if (leading_spacing < 0.0 && trailing_spacing < 0.0) {
1213 origin -= half;
1214 modified_advances[start] += half;
1215 modified_advances[end-1] += half;
1217 else if (leading_spacing < 0.0) {
1218 origin -= advance;
1219 modified_advances[start] += advance;
1221 else
1222 modified_advances[end-1] += advance;
1225 /* now apply positive spacing adjustments */
1226 if (leading_spacing > 0.0) {
1227 modified_advances[start] += leading_spacing;
1228 origin -= leading_spacing;
1230 if (trailing_spacing > 0.0)
1231 modified_advances[end-1] += trailing_spacing;
1233 for (g = start; g < end; g++) {
1234 if (g == start) {
1235 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1236 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1238 else if (g == end - 1)
1239 /* trailing glyph offset is not adjusted */
1240 modified_offsets[g] = offsets[g];
1241 else {
1242 /* for all glyphs within a cluster use original advances and offsets */
1243 modified_advances[g] = advances[g];
1244 modified_offsets[g] = offsets[g];
1249 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1251 UINT16 g = clustermap[start];
1252 UINT32 length = 1;
1254 while (start < text_len && clustermap[++start] == g)
1255 length++;
1256 return length;
1259 /* Applies spacing adjustments to clusters.
1261 Adjustments are applied in the following order:
1263 1. Negative adjustments
1265 Leading and trailing spacing could be negative, at this step
1266 only negative ones are actually applied. Leading spacing is only
1267 applied to leading glyph, trailing - to trailing glyph.
1269 2. Minimum advance width
1271 Advances could only be reduced at this point or unchanged. In any
1272 case it's checked if cluster advance width is less than minimum width.
1273 If it's the case advance width is incremented up to minimum value.
1275 Important part is the direction in which this increment is applied;
1276 it depends on from which directions total cluster advance was trimmed
1277 at step 1. So it could be incremented from leading, trailing, or both
1278 sides. When applied to both sides, each side gets half of difference
1279 that bring advance to minimum width.
1281 3. Positive adjustments
1283 After minimum width rule was applied, positive spacing is applied in same
1284 way as negative ones on step 1.
1286 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1287 keeps its position in coordinate system where initial advance width is counted
1288 from 0.
1290 Glyph properties
1292 It's known that isZeroWidthSpace property keeps initial advance from changing.
1294 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1295 with more than one glyph.
1298 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1299 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1300 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1301 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1303 UINT16 start;
1305 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1306 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1308 if (min_advance_width < 0.0) {
1309 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1310 return E_INVALIDARG;
1313 /* minimum advance is not applied if no adjustments were made */
1314 if (leading_spacing == 0.0 && trailing_spacing == 0.0) {
1315 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1316 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1317 return S_OK;
1320 for (start = 0; start < len;) {
1321 UINT32 length = get_cluster_length(clustermap, start, len);
1323 if (length == 1) {
1324 UINT32 g = clustermap[start];
1326 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1327 g, advances, offsets, props, modified_advances, modified_offsets);
1329 else {
1330 UINT32 g_start, g_end;
1332 g_start = clustermap[start];
1333 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1335 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1336 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1339 start += length;
1342 return S_OK;
1345 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1346 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1347 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1349 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1350 baseline_coord, exists);
1351 return E_NOTIMPL;
1354 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1355 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1357 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1358 return E_NOTIMPL;
1361 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1362 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1364 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1365 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1368 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1369 DWRITE_SCRIPT_PROPERTIES *props)
1371 TRACE("(%u %p)\n", sa.script, props);
1373 if (sa.script > Script_LastId)
1374 return E_INVALIDARG;
1376 *props = dwritescripts_properties[sa.script].props;
1377 return S_OK;
1380 static inline BOOL is_char_from_simple_script(WCHAR c)
1382 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1383 return FALSE;
1384 else {
1385 UINT16 script = get_char_script(c);
1386 return !dwritescripts_properties[script].is_complex;
1390 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1391 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1393 HRESULT hr = S_OK;
1394 int i;
1396 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1398 *is_simple = FALSE;
1399 *len_read = 0;
1401 if (!face)
1402 return E_INVALIDARG;
1404 if (len == 0) {
1405 *is_simple = TRUE;
1406 return S_OK;
1409 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1410 for (i = 1; i < len && text[i]; i++) {
1411 if (is_char_from_simple_script(text[i])) {
1412 if (!*is_simple)
1413 break;
1415 else
1416 *is_simple = FALSE;
1419 *len_read = i;
1421 /* fetch indices */
1422 if (*is_simple && indices) {
1423 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1424 if (!codepoints)
1425 return E_OUTOFMEMORY;
1427 for (i = 0; i < *len_read; i++)
1428 codepoints[i] = text[i];
1430 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1431 heap_free(codepoints);
1434 return hr;
1437 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1438 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1439 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1441 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1442 debugstr_wn(text, length), clustermap, prop, jo);
1443 return E_NOTIMPL;
1446 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1447 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1448 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1450 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1451 justifiedoffsets);
1452 return E_NOTIMPL;
1455 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1456 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1457 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1458 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1459 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1460 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1462 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,
1463 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1464 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1465 return E_NOTIMPL;
1468 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1469 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1471 static const DWRITE_MATRIX transforms[] = {
1472 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
1473 { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 },
1474 { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 },
1475 { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 }
1478 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1480 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1481 memset(m, 0, sizeof(*m));
1482 return E_INVALIDARG;
1485 /* for sideways case simply rotate 90 degrees more */
1486 if (is_sideways) {
1487 switch (angle) {
1488 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1489 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1490 break;
1491 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1492 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1493 break;
1494 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1495 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1496 break;
1497 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1498 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1499 break;
1500 default:
1505 *m = transforms[angle];
1507 /* shift components represent transform necessary to get from original point to
1508 rotated one in new coordinate system */
1509 if ((originX != 0.0 || originY != 0.0) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1510 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1511 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1514 return S_OK;
1517 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1518 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1519 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1521 const struct dwritescript_properties *props;
1522 HRESULT hr = S_OK;
1523 UINT32 language;
1525 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1526 tags);
1528 if (sa.script > Script_LastId)
1529 return E_INVALIDARG;
1531 language = get_opentype_language(locale);
1532 props = &dwritescripts_properties[sa.script];
1533 *actual_tagcount = 0;
1535 if (props->scriptalttag)
1536 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1538 if (*actual_tagcount == 0)
1539 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1541 return hr;
1544 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1545 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1546 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1548 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1549 feature_applies);
1550 return E_NOTIMPL;
1553 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1554 dwritetextanalyzer_QueryInterface,
1555 dwritetextanalyzer_AddRef,
1556 dwritetextanalyzer_Release,
1557 dwritetextanalyzer_AnalyzeScript,
1558 dwritetextanalyzer_AnalyzeBidi,
1559 dwritetextanalyzer_AnalyzeNumberSubstitution,
1560 dwritetextanalyzer_AnalyzeLineBreakpoints,
1561 dwritetextanalyzer_GetGlyphs,
1562 dwritetextanalyzer_GetGlyphPlacements,
1563 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1564 dwritetextanalyzer1_ApplyCharacterSpacing,
1565 dwritetextanalyzer1_GetBaseline,
1566 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1567 dwritetextanalyzer1_GetGlyphOrientationTransform,
1568 dwritetextanalyzer1_GetScriptProperties,
1569 dwritetextanalyzer1_GetTextComplexity,
1570 dwritetextanalyzer1_GetJustificationOpportunities,
1571 dwritetextanalyzer1_JustifyGlyphAdvances,
1572 dwritetextanalyzer1_GetJustifiedGlyphs,
1573 dwritetextanalyzer2_GetGlyphOrientationTransform,
1574 dwritetextanalyzer2_GetTypographicFeatures,
1575 dwritetextanalyzer2_CheckTypographicFeature
1578 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1580 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1582 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1583 return S_OK;
1586 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1588 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1590 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1592 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1593 IsEqualIID(riid, &IID_IUnknown))
1595 *obj = iface;
1596 IDWriteNumberSubstitution_AddRef(iface);
1597 return S_OK;
1600 *obj = NULL;
1602 return E_NOINTERFACE;
1605 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1607 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1608 ULONG ref = InterlockedIncrement(&This->ref);
1609 TRACE("(%p)->(%d)\n", This, ref);
1610 return ref;
1613 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1615 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1616 ULONG ref = InterlockedDecrement(&This->ref);
1618 TRACE("(%p)->(%d)\n", This, ref);
1620 if (!ref) {
1621 heap_free(This->locale);
1622 heap_free(This);
1625 return ref;
1628 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1629 dwritenumbersubstitution_QueryInterface,
1630 dwritenumbersubstitution_AddRef,
1631 dwritenumbersubstitution_Release
1634 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1635 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1637 struct dwrite_numbersubstitution *substitution;
1639 *ret = NULL;
1641 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1642 return E_INVALIDARG;
1644 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1645 return E_INVALIDARG;
1647 substitution = heap_alloc(sizeof(*substitution));
1648 if (!substitution)
1649 return E_OUTOFMEMORY;
1651 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1652 substitution->ref = 1;
1653 substitution->ignore_user_override = ignore_user_override;
1654 substitution->method = method;
1655 substitution->locale = heap_strdupW(locale);
1656 if (locale && !substitution->locale) {
1657 heap_free(substitution);
1658 return E_OUTOFMEMORY;
1661 *ret = &substitution->IDWriteNumberSubstitution_iface;
1662 return S_OK;