dwrite: Force advances to zero for isZeroWidthSpace glyphs.
[wine.git] / dlls / dwrite / analyzer.c
blob1b52eb7b21068d30b7dba4953f57d73749d0f06c
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 if 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 const char *debugstr_sa_script(UINT16 script)
182 return script < Script_LastId ? debugstr_an((char*)&dwritescripts_properties[script].props.isoScriptCode, 4): "not defined";
185 /* system font falback configuration */
186 static const WCHAR meiryoW[] = {'M','e','i','r','y','o',0};
188 struct fallback_mapping {
189 DWRITE_UNICODE_RANGE range;
190 const WCHAR *family;
193 static const struct fallback_mapping fontfallback_neutral_data[] = {
194 { { 0x4e00, 0x9fff }, meiryoW }, /* CJK Unified Ideographs */
197 struct dwrite_fontfallback {
198 IDWriteFontFallback IDWriteFontFallback_iface;
199 IDWriteFactory2 *factory;
200 const struct fallback_mapping *mappings;
201 UINT32 count;
204 struct dwrite_numbersubstitution {
205 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
206 LONG ref;
208 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
209 WCHAR *locale;
210 BOOL ignore_user_override;
213 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
215 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
218 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback(IDWriteFontFallback *iface)
220 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback_iface);
223 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
225 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
226 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
227 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
228 return ch;
230 return 0;
233 static inline UINT16 get_char_script(WCHAR c)
235 UINT16 script = get_table_entry(wine_scripts_table, c);
236 if (script == Script_Unknown) {
237 WORD type;
238 if (GetStringTypeW(CT_CTYPE1, &c, 1, &type) && (type & C1_CNTRL))
239 script = Script_Common;
241 return script;
244 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 len, IDWriteTextAnalysisSink *sink)
246 DWRITE_SCRIPT_ANALYSIS sa;
247 UINT32 pos, i, length;
249 if (!len) return S_OK;
251 sa.script = get_char_script(*text);
253 pos = position;
254 length = 1;
256 for (i = 1; i < len; i++)
258 UINT16 script = get_char_script(text[i]);
260 /* Unknown type is ignored when preceded or followed by another script */
261 if (sa.script == Script_Unknown) sa.script = script;
262 if (script == Script_Unknown && sa.script != Script_Common) script = sa.script;
263 /* this is a length of a sequence to be reported next */
264 if (sa.script == script) length++;
266 if (sa.script != script)
268 HRESULT hr;
270 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
271 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
272 if (FAILED(hr)) return hr;
273 pos = position + i;
274 length = 1;
275 sa.script = script;
279 /* 1 length case or normal completion call */
280 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
281 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
284 struct linebreaking_state {
285 DWRITE_LINE_BREAKPOINT *breakpoints;
286 UINT32 count;
289 enum BreakConditionLocation {
290 BreakConditionBefore,
291 BreakConditionAfter
294 enum linebreaking_classes {
295 b_BK = 1,
296 b_CR,
297 b_LF,
298 b_CM,
299 b_SG,
300 b_GL,
301 b_CB,
302 b_SP,
303 b_ZW,
304 b_NL,
305 b_WJ,
306 b_JL,
307 b_JV,
308 b_JT,
309 b_H2,
310 b_H3,
311 b_XX,
312 b_OP,
313 b_CL,
314 b_CP,
315 b_QU,
316 b_NS,
317 b_EX,
318 b_SY,
319 b_IS,
320 b_PR,
321 b_PO,
322 b_NU,
323 b_AL,
324 b_ID,
325 b_IN,
326 b_HY,
327 b_BB,
328 b_BA,
329 b_SA,
330 b_AI,
331 b_B2,
332 b_HL,
333 b_CJ,
334 b_RI
337 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
338 set to "can break" and could only be changed once. */
339 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
340 struct linebreaking_state *state)
342 if (location == BreakConditionBefore) {
343 if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK)
344 return;
345 state->breakpoints[pos].breakConditionBefore = condition;
346 if (pos > 0)
347 state->breakpoints[pos-1].breakConditionAfter = condition;
349 else {
350 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
351 return;
352 state->breakpoints[pos].breakConditionAfter = condition;
353 if (pos + 1 < state->count)
354 state->breakpoints[pos+1].breakConditionBefore = condition;
358 BOOL lb_is_newline_char(WCHAR ch)
360 short c = get_table_entry(wine_linebreak_table, ch);
361 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
364 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
366 struct linebreaking_state state;
367 short *break_class;
368 int i, j;
370 break_class = heap_alloc(count*sizeof(short));
371 if (!break_class)
372 return E_OUTOFMEMORY;
374 state.breakpoints = breakpoints;
375 state.count = count;
377 /* LB31 - allow breaks everywhere. It will be overridden if needed as
378 other rules dictate. */
379 for (i = 0; i < count; i++)
381 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
383 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK;
384 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK;
385 breakpoints[i].isWhitespace = !!isspaceW(text[i]);
386 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
387 breakpoints[i].padding = 0;
389 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
390 switch (break_class[i])
392 case b_AI:
393 case b_SA:
394 case b_SG:
395 case b_XX:
396 break_class[i] = b_AL;
397 break;
398 case b_CJ:
399 break_class[i] = b_NS;
400 break;
404 /* LB2 - never break at the start */
405 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
406 /* LB3 - always break at the end. This one is ignored. */
408 for (i = 0; i < count; i++)
410 switch (break_class[i])
412 /* LB4 - LB6 */
413 case b_CR:
414 /* LB5 - don't break CR x LF */
415 if (i < count-1 && break_class[i+1] == b_LF)
417 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
418 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
419 break;
421 case b_LF:
422 case b_NL:
423 case b_BK:
424 /* LB4 - LB5 - always break after hard breaks */
425 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
426 /* LB6 - do not break before hard breaks */
427 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
428 break;
429 /* LB7 - do not break before spaces */
430 case b_SP:
431 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
432 break;
433 case b_ZW:
434 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
435 /* LB8 - break before character after zero-width space, skip spaces in-between */
436 while (i < count-1 && break_class[i+1] == b_SP)
437 i++;
438 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
439 break;
443 /* LB9 - LB10 */
444 for (i = 0; i < count; i++)
446 if (break_class[i] == b_CM)
448 if (i > 0)
450 switch (break_class[i-1])
452 case b_SP:
453 case b_BK:
454 case b_CR:
455 case b_LF:
456 case b_NL:
457 case b_ZW:
458 break_class[i] = b_AL;
459 break;
460 default:
461 break_class[i] = break_class[i-1];
464 else break_class[i] = b_AL;
468 for (i = 0; i < count; i++)
470 switch (break_class[i])
472 /* LB11 - don't break before and after word joiner */
473 case b_WJ:
474 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
475 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
476 break;
477 /* LB12 - don't break after glue */
478 case b_GL:
479 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
480 /* LB12a */
481 if (i > 0)
483 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
484 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
486 break;
487 /* LB13 */
488 case b_CL:
489 case b_CP:
490 case b_EX:
491 case b_IS:
492 case b_SY:
493 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
494 break;
495 /* LB14 */
496 case b_OP:
497 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
498 while (i < count-1 && break_class[i+1] == b_SP) {
499 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
500 i++;
502 break;
503 /* LB15 */
504 case b_QU:
505 j = i+1;
506 while (j < count-1 && break_class[j] == b_SP)
507 j++;
508 if (break_class[j] == b_OP)
509 for (; j > i; j--)
510 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
511 break;
512 /* LB16 */
513 case b_NS:
514 j = i-1;
515 while(j > 0 && break_class[j] == b_SP)
516 j--;
517 if (break_class[j] == b_CL || break_class[j] == b_CP)
518 for (j++; j <= i; j++)
519 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
520 break;
521 /* LB17 */
522 case b_B2:
523 j = i+1;
524 while (j < count && break_class[j] == b_SP)
525 j++;
526 if (break_class[j] == b_B2)
527 for (; j > i; j--)
528 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
529 break;
533 for (i = 0; i < count; i++)
535 switch(break_class[i])
537 /* LB18 - break is allowed after space */
538 case b_SP:
539 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
540 break;
541 /* LB19 - don't break before or after quotation mark */
542 case b_QU:
543 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
544 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
545 break;
546 /* LB20 */
547 case b_CB:
548 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
549 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
550 break;
551 /* LB21 */
552 case b_BA:
553 case b_HY:
554 case b_NS:
555 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
556 break;
557 case b_BB:
558 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
559 break;
560 /* LB21a, LB21b */
561 case b_HL:
562 /* LB21a */
563 if (i < count-1)
564 switch (break_class[i+1])
566 case b_HY:
567 case b_BA:
568 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
570 /* LB21b */
571 if (i > 0 && break_class[i-1] == b_SY)
572 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
573 break;
574 /* LB22 */
575 case b_IN:
576 if (i > 0)
578 switch (break_class[i-1])
580 case b_AL:
581 case b_HL:
582 case b_EX:
583 case b_ID:
584 case b_IN:
585 case b_NU:
586 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
589 break;
592 if (i < count-1)
594 /* LB23 */
595 if ((break_class[i] == b_ID && break_class[i+1] == b_PO) ||
596 (break_class[i] == b_AL && break_class[i+1] == b_NU) ||
597 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
598 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
599 (break_class[i] == b_NU && break_class[i+1] == b_HL))
600 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
601 /* LB24 */
602 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
603 (break_class[i] == b_PR && break_class[i+1] == b_AL) ||
604 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
605 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
606 (break_class[i] == b_PO && break_class[i+1] == b_HL))
607 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
609 /* LB25 */
610 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
611 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
612 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
613 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
614 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
615 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
616 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
617 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
618 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
619 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
620 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
621 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
622 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
623 (break_class[i] == b_SY && break_class[i+1] == b_NU))
624 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
626 /* LB26 */
627 if (break_class[i] == b_JL)
629 switch (break_class[i+1])
631 case b_JL:
632 case b_JV:
633 case b_H2:
634 case b_H3:
635 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
638 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
639 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
640 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
641 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
642 break_class[i+1] == b_JT)
643 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
645 /* LB27 */
646 switch (break_class[i])
648 case b_JL:
649 case b_JV:
650 case b_JT:
651 case b_H2:
652 case b_H3:
653 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
654 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
656 if (break_class[i] == b_PO)
658 switch (break_class[i+1])
660 case b_JL:
661 case b_JV:
662 case b_JT:
663 case b_H2:
664 case b_H3:
665 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
669 /* LB28 */
670 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
671 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
672 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
673 (break_class[i] == b_HL && break_class[i+1] == b_HL))
674 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
676 /* LB29 */
677 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
678 (break_class[i] == b_IS && break_class[i+1] == b_HL))
679 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
681 /* LB30 */
682 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
683 break_class[i+1] == b_OP)
684 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
685 if (break_class[i] == b_CP &&
686 (break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU))
687 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
689 /* LB30a */
690 if (break_class[i] == b_RI && break_class[i+1] == b_RI)
691 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
695 heap_free(break_class);
696 return S_OK;
699 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
701 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
703 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
704 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
705 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
706 IsEqualIID(riid, &IID_IUnknown))
708 *obj = iface;
709 return S_OK;
712 *obj = NULL;
713 return E_NOINTERFACE;
716 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
718 return 2;
721 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
723 return 1;
726 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
727 data after a first request. */
728 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
730 HRESULT hr;
731 UINT32 len;
733 *buff = NULL;
734 *text = NULL;
735 len = 0;
736 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
737 if (FAILED(hr)) return hr;
739 if (len < length) {
740 UINT32 read;
742 *buff = heap_alloc(length*sizeof(WCHAR));
743 if (!*buff)
744 return E_OUTOFMEMORY;
745 memcpy(*buff, *text, len*sizeof(WCHAR));
746 read = len;
748 while (read < length && *text) {
749 *text = NULL;
750 len = 0;
751 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
752 if (FAILED(hr)) {
753 heap_free(*buff);
754 return hr;
756 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
757 read += len;
760 *text = *buff;
763 return hr;
766 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
767 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
769 WCHAR *buff = NULL;
770 const WCHAR *text;
771 HRESULT hr;
773 TRACE("(%p %u %u %p)\n", source, position, length, sink);
775 if (length == 0)
776 return S_OK;
778 hr = get_text_source_ptr(source, position, length, &text, &buff);
779 if (FAILED(hr))
780 return hr;
782 hr = analyze_script(text, position, length, sink);
783 heap_free(buff);
785 return hr;
788 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
789 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
791 UINT8 *levels = NULL, *explicit = NULL;
792 UINT8 baselevel, level, explicit_level;
793 WCHAR *buff = NULL;
794 const WCHAR *text;
795 UINT32 pos, i;
796 HRESULT hr;
798 TRACE("(%p %u %u %p)\n", source, position, length, sink);
800 if (length == 0)
801 return S_OK;
803 hr = get_text_source_ptr(source, position, length, &text, &buff);
804 if (FAILED(hr))
805 return hr;
807 levels = heap_alloc(length*sizeof(*levels));
808 explicit = heap_alloc(length*sizeof(*explicit));
810 if (!levels || !explicit) {
811 hr = E_OUTOFMEMORY;
812 goto done;
815 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
816 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
817 if (FAILED(hr))
818 goto done;
820 level = levels[0];
821 explicit_level = explicit[0];
822 pos = 0;
823 for (i = 1; i < length; i++) {
824 if (levels[i] != level || explicit[i] != explicit_level) {
825 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, i - pos, explicit_level, level);
826 if (FAILED(hr))
827 break;
828 level = levels[i];
829 explicit_level = explicit[i];
830 pos = i;
833 if (i == length - 1)
834 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, length - pos, explicit_level, level);
837 done:
838 heap_free(explicit);
839 heap_free(levels);
840 heap_free(buff);
842 return hr;
845 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
846 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
848 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
849 return S_OK;
852 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
853 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
855 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
856 WCHAR *buff = NULL;
857 const WCHAR *text;
858 HRESULT hr;
859 UINT32 len;
861 TRACE("(%p %u %u %p)\n", source, position, length, sink);
863 if (length == 0)
864 return S_OK;
866 /* get some, check for length */
867 text = NULL;
868 len = 0;
869 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
870 if (FAILED(hr)) return hr;
872 if (len < length) {
873 UINT32 read;
875 buff = heap_alloc(length*sizeof(WCHAR));
876 if (!buff)
877 return E_OUTOFMEMORY;
878 memcpy(buff, text, len*sizeof(WCHAR));
879 read = len;
881 while (read < length && text) {
882 text = NULL;
883 len = 0;
884 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
885 if (FAILED(hr))
886 goto done;
887 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
888 read += len;
891 text = buff;
894 breakpoints = heap_alloc(length*sizeof(*breakpoints));
895 if (!breakpoints) {
896 hr = E_OUTOFMEMORY;
897 goto done;
900 hr = analyze_linebreaks(text, length, breakpoints);
901 if (FAILED(hr))
902 goto done;
904 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
906 done:
907 heap_free(breakpoints);
908 heap_free(buff);
910 return hr;
913 static UINT32 get_opentype_language(const WCHAR *locale)
915 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
917 if (locale) {
918 WCHAR tag[5];
919 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
920 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
923 return language;
926 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
927 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
928 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
929 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
930 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
931 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
932 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
934 const struct dwritescript_properties *scriptprops;
935 struct scriptshaping_context context;
936 struct scriptshaping_cache *cache = NULL;
937 BOOL update_cluster, need_vertical;
938 IDWriteFontFace1 *fontface1;
939 WCHAR *string;
940 UINT32 i, g;
941 HRESULT hr = S_OK;
942 UINT16 script;
944 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
945 length, fontface, is_sideways, is_rtl, analysis, debugstr_w(locale), substitution, features, feature_range_len,
946 feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices, glyph_props, actual_glyph_count);
948 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
950 if (max_glyph_count < length)
951 return E_NOT_SUFFICIENT_BUFFER;
953 if (substitution)
954 FIXME("number substitution is not supported.\n");
956 for (i = 0; i < length; i++) {
957 /* FIXME: set to better values */
958 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
959 glyph_props[i].isClusterStart = 1;
960 glyph_props[i].isDiacritic = 0;
961 glyph_props[i].isZeroWidthSpace = 0;
962 glyph_props[i].reserved = 0;
964 /* FIXME: have the shaping engine set this */
965 text_props[i].isShapedAlone = 0;
966 text_props[i].reserved = 0;
968 clustermap[i] = i;
971 for (; i < max_glyph_count; i++) {
972 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
973 glyph_props[i].isClusterStart = 0;
974 glyph_props[i].isDiacritic = 0;
975 glyph_props[i].isZeroWidthSpace = 0;
976 glyph_props[i].reserved = 0;
979 string = heap_alloc(sizeof(WCHAR)*length);
980 if (!string)
981 return E_OUTOFMEMORY;
983 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
984 if (FAILED(hr))
985 WARN("failed to get IDWriteFontFace1\n");
987 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
989 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
990 UINT32 codepoint;
992 if (!update_cluster) {
993 codepoint = decode_surrogate_pair(text, i, length);
994 if (!codepoint) {
995 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
996 string[i] = codepoint;
998 else {
999 string[i] = text[i];
1000 string[i+1] = text[i+1];
1001 update_cluster = TRUE;
1004 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
1005 if (FAILED(hr))
1006 goto done;
1008 if (need_vertical) {
1009 UINT16 vertical;
1011 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
1012 if (hr == S_OK)
1013 glyph_indices[g] = vertical;
1016 g++;
1018 else {
1019 INT32 k;
1021 update_cluster = FALSE;
1022 /* mark surrogate halves with same cluster */
1023 clustermap[i] = clustermap[i-1];
1024 /* update following clusters */
1025 for (k = i + 1; k >= 0 && k < length; k++)
1026 clustermap[k]--;
1029 *actual_glyph_count = g;
1031 hr = create_scriptshaping_cache(fontface, &cache);
1032 if (FAILED(hr))
1033 goto done;
1035 context.cache = cache;
1036 context.text = text;
1037 context.length = length;
1038 context.is_rtl = is_rtl;
1039 context.max_glyph_count = max_glyph_count;
1040 context.language_tag = get_opentype_language(locale);
1042 scriptprops = &dwritescripts_properties[script];
1043 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
1044 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
1045 if (FAILED(hr))
1046 goto done;
1049 /* FIXME: apply default features */
1051 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1052 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1053 else
1054 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1056 done:
1057 if (fontface1)
1058 IDWriteFontFace1_Release(fontface1);
1059 release_scriptshaping_cache(cache);
1060 heap_free(string);
1062 return hr;
1065 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1066 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1067 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1068 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, 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_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1072 DWRITE_FONT_METRICS metrics;
1073 IDWriteFontFace1 *fontface1;
1074 HRESULT hr;
1075 UINT32 i;
1077 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),
1078 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1079 is_rtl, analysis, debugstr_w(locale), features, feature_range_len, feature_ranges, advances, offsets);
1081 if (glyph_count == 0)
1082 return S_OK;
1084 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1085 if (FAILED(hr)) {
1086 WARN("failed to get IDWriteFontFace1.\n");
1087 return hr;
1090 IDWriteFontFace_GetMetrics(fontface, &metrics);
1091 for (i = 0; i < glyph_count; i++) {
1092 if (glyph_props[i].isZeroWidthSpace)
1093 advances[i] = 0.0f;
1094 else {
1095 INT32 a;
1097 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1098 if (FAILED(hr))
1099 a = 0;
1100 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1102 offsets[i].advanceOffset = 0.0f;
1103 offsets[i].ascenderOffset = 0.0f;
1106 /* FIXME: actually apply features */
1108 IDWriteFontFace1_Release(fontface1);
1109 return S_OK;
1112 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1113 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1114 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1115 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1116 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1117 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1118 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1120 DWRITE_FONT_METRICS metrics;
1121 IDWriteFontFace1 *fontface1;
1122 HRESULT hr;
1123 UINT32 i;
1125 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),
1126 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1127 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
1128 feature_ranges, advances, offsets);
1130 if (glyph_count == 0)
1131 return S_OK;
1133 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1134 if (FAILED(hr)) {
1135 WARN("failed to get IDWriteFontFace1.\n");
1136 return hr;
1139 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1140 if (FAILED(hr)) {
1141 IDWriteFontFace1_Release(fontface1);
1142 WARN("failed to get compat metrics, 0x%08x\n", hr);
1143 return hr;
1145 for (i = 0; i < glyph_count; i++) {
1146 INT32 a;
1148 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1149 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1150 if (FAILED(hr))
1151 advances[i] = 0.0f;
1152 else
1153 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1154 offsets[i].advanceOffset = 0.0f;
1155 offsets[i].ascenderOffset = 0.0f;
1158 /* FIXME: actually apply features */
1160 IDWriteFontFace1_Release(fontface1);
1161 return S_OK;
1164 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1166 FLOAT advance = 0.0f;
1167 for (; start < end; start++)
1168 advance += advances[start];
1169 return advance;
1172 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1173 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1174 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1176 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1177 FLOAT advance = advances[g];
1178 FLOAT origin = 0.0f;
1180 if (props[g].isZeroWidthSpace) {
1181 modified_advances[g] = advances[g];
1182 modified_offsets[g] = offsets[g];
1183 return;
1186 /* first apply negative spacing and check if we hit minimum width */
1187 if (leading_spacing < 0.0f) {
1188 advance += leading_spacing;
1189 origin -= leading_spacing;
1191 if (trailing_spacing < 0.0f)
1192 advance += trailing_spacing;
1194 if (advance < min_advance_width) {
1195 FLOAT half = (min_advance_width - advance) / 2.0f;
1197 if (!reduced)
1198 origin -= half;
1199 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1200 origin -= half;
1201 else if (leading_spacing < 0.0f)
1202 origin -= min_advance_width - advance;
1204 advance = min_advance_width;
1207 /* now apply positive spacing adjustments */
1208 if (leading_spacing > 0.0f) {
1209 advance += leading_spacing;
1210 origin -= leading_spacing;
1212 if (trailing_spacing > 0.0f)
1213 advance += trailing_spacing;
1215 modified_advances[g] = advance;
1216 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1217 /* ascender is never touched, it's orthogonal to reading direction and is not
1218 affected by advance adjustments */
1219 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1222 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1223 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1224 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1226 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1227 FLOAT advance = get_cluster_advance(advances, start, end);
1228 FLOAT origin = 0.0f;
1229 UINT16 g;
1231 modified_advances[start] = advances[start];
1232 modified_advances[end-1] = advances[end-1];
1234 /* first apply negative spacing and check if we hit minimum width */
1235 if (leading_spacing < 0.0f) {
1236 advance += leading_spacing;
1237 modified_advances[start] += leading_spacing;
1238 origin -= leading_spacing;
1240 if (trailing_spacing < 0.0f) {
1241 advance += trailing_spacing;
1242 modified_advances[end-1] += trailing_spacing;
1245 advance = min_advance_width - advance;
1246 if (advance > 0.0f) {
1247 /* additional spacing is only applied to leading and trailing glyph */
1248 FLOAT half = advance / 2.0f;
1250 if (!reduced) {
1251 origin -= half;
1252 modified_advances[start] += half;
1253 modified_advances[end-1] += half;
1255 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f) {
1256 origin -= half;
1257 modified_advances[start] += half;
1258 modified_advances[end-1] += half;
1260 else if (leading_spacing < 0.0f) {
1261 origin -= advance;
1262 modified_advances[start] += advance;
1264 else
1265 modified_advances[end-1] += advance;
1268 /* now apply positive spacing adjustments */
1269 if (leading_spacing > 0.0f) {
1270 modified_advances[start] += leading_spacing;
1271 origin -= leading_spacing;
1273 if (trailing_spacing > 0.0f)
1274 modified_advances[end-1] += trailing_spacing;
1276 for (g = start; g < end; g++) {
1277 if (g == start) {
1278 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1279 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1281 else if (g == end - 1)
1282 /* trailing glyph offset is not adjusted */
1283 modified_offsets[g] = offsets[g];
1284 else {
1285 /* for all glyphs within a cluster use original advances and offsets */
1286 modified_advances[g] = advances[g];
1287 modified_offsets[g] = offsets[g];
1292 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1294 UINT16 g = clustermap[start];
1295 UINT32 length = 1;
1297 while (start < text_len && clustermap[++start] == g)
1298 length++;
1299 return length;
1302 /* Applies spacing adjustments to clusters.
1304 Adjustments are applied in the following order:
1306 1. Negative adjustments
1308 Leading and trailing spacing could be negative, at this step
1309 only negative ones are actually applied. Leading spacing is only
1310 applied to leading glyph, trailing - to trailing glyph.
1312 2. Minimum advance width
1314 Advances could only be reduced at this point or unchanged. In any
1315 case it's checked if cluster advance width is less than minimum width.
1316 If it's the case advance width is incremented up to minimum value.
1318 Important part is the direction in which this increment is applied;
1319 it depends on direction from which total cluster advance was trimmed
1320 at step 1. So it could be incremented from leading, trailing, or both
1321 sides. When applied to both sides, each side gets half of difference
1322 that brings advance to minimum width.
1324 3. Positive adjustments
1326 After minimum width rule was applied, positive spacing is applied in the same
1327 way as negative one on step 1.
1329 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1330 keeps its position in coordinate system where initial advance width is counted
1331 from 0.
1333 Glyph properties
1335 It's known that isZeroWidthSpace property keeps initial advance from changing.
1337 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1338 with more than one glyph.
1341 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1342 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1343 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1344 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1346 UINT16 start;
1348 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1349 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1351 if (min_advance_width < 0.0f) {
1352 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1353 return E_INVALIDARG;
1356 /* minimum advance is not applied if no adjustments were made */
1357 if (leading_spacing == 0.0f && trailing_spacing == 0.0f) {
1358 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1359 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1360 return S_OK;
1363 for (start = 0; start < len;) {
1364 UINT32 length = get_cluster_length(clustermap, start, len);
1366 if (length == 1) {
1367 UINT32 g = clustermap[start];
1369 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1370 g, advances, offsets, props, modified_advances, modified_offsets);
1372 else {
1373 UINT32 g_start, g_end;
1375 g_start = clustermap[start];
1376 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1378 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1379 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1382 start += length;
1385 return S_OK;
1388 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1389 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1390 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1392 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1393 baseline_coord, exists);
1394 return E_NOTIMPL;
1397 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1398 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1400 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1401 return E_NOTIMPL;
1404 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1405 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1407 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1408 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1411 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1412 DWRITE_SCRIPT_PROPERTIES *props)
1414 TRACE("(%u %p)\n", sa.script, props);
1416 if (sa.script > Script_LastId)
1417 return E_INVALIDARG;
1419 *props = dwritescripts_properties[sa.script].props;
1420 return S_OK;
1423 static inline BOOL is_char_from_simple_script(WCHAR c)
1425 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1426 return FALSE;
1427 else {
1428 UINT16 script = get_char_script(c);
1429 return !dwritescripts_properties[script].is_complex;
1433 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1434 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1436 HRESULT hr = S_OK;
1437 int i;
1439 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1441 *is_simple = FALSE;
1442 *len_read = 0;
1444 if (!face)
1445 return E_INVALIDARG;
1447 if (len == 0) {
1448 *is_simple = TRUE;
1449 return S_OK;
1452 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1453 for (i = 1; i < len && text[i]; i++) {
1454 if (is_char_from_simple_script(text[i])) {
1455 if (!*is_simple)
1456 break;
1458 else
1459 *is_simple = FALSE;
1462 *len_read = i;
1464 /* fetch indices */
1465 if (*is_simple && indices) {
1466 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1467 if (!codepoints)
1468 return E_OUTOFMEMORY;
1470 for (i = 0; i < *len_read; i++)
1471 codepoints[i] = text[i];
1473 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1474 heap_free(codepoints);
1477 return hr;
1480 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1481 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1482 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1484 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1485 debugstr_wn(text, length), clustermap, prop, jo);
1486 return E_NOTIMPL;
1489 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1490 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1491 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1493 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1494 justifiedoffsets);
1495 return E_NOTIMPL;
1498 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1499 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1500 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1501 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1502 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1503 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1505 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,
1506 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1507 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1508 return E_NOTIMPL;
1511 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1512 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1514 static const DWRITE_MATRIX transforms[] = {
1515 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1516 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1517 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1518 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1521 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1523 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1524 memset(m, 0, sizeof(*m));
1525 return E_INVALIDARG;
1528 /* for sideways case simply rotate 90 degrees more */
1529 if (is_sideways) {
1530 switch (angle) {
1531 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1532 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1533 break;
1534 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1535 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1536 break;
1537 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1538 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1539 break;
1540 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1541 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1542 break;
1543 default:
1548 *m = transforms[angle];
1550 /* shift components represent transform necessary to get from original point to
1551 rotated one in new coordinate system */
1552 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1553 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1554 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1557 return S_OK;
1560 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1561 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1562 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1564 const struct dwritescript_properties *props;
1565 HRESULT hr = S_OK;
1566 UINT32 language;
1568 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1569 tags);
1571 if (sa.script > Script_LastId)
1572 return E_INVALIDARG;
1574 language = get_opentype_language(locale);
1575 props = &dwritescripts_properties[sa.script];
1576 *actual_tagcount = 0;
1578 if (props->scriptalttag)
1579 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1581 if (*actual_tagcount == 0)
1582 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1584 return hr;
1587 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1588 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1589 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1591 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1592 feature_applies);
1593 return E_NOTIMPL;
1596 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1597 dwritetextanalyzer_QueryInterface,
1598 dwritetextanalyzer_AddRef,
1599 dwritetextanalyzer_Release,
1600 dwritetextanalyzer_AnalyzeScript,
1601 dwritetextanalyzer_AnalyzeBidi,
1602 dwritetextanalyzer_AnalyzeNumberSubstitution,
1603 dwritetextanalyzer_AnalyzeLineBreakpoints,
1604 dwritetextanalyzer_GetGlyphs,
1605 dwritetextanalyzer_GetGlyphPlacements,
1606 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1607 dwritetextanalyzer1_ApplyCharacterSpacing,
1608 dwritetextanalyzer1_GetBaseline,
1609 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1610 dwritetextanalyzer1_GetGlyphOrientationTransform,
1611 dwritetextanalyzer1_GetScriptProperties,
1612 dwritetextanalyzer1_GetTextComplexity,
1613 dwritetextanalyzer1_GetJustificationOpportunities,
1614 dwritetextanalyzer1_JustifyGlyphAdvances,
1615 dwritetextanalyzer1_GetJustifiedGlyphs,
1616 dwritetextanalyzer2_GetGlyphOrientationTransform,
1617 dwritetextanalyzer2_GetTypographicFeatures,
1618 dwritetextanalyzer2_CheckTypographicFeature
1621 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1623 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1625 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1626 return S_OK;
1629 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1631 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1633 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1635 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1636 IsEqualIID(riid, &IID_IUnknown))
1638 *obj = iface;
1639 IDWriteNumberSubstitution_AddRef(iface);
1640 return S_OK;
1643 *obj = NULL;
1645 return E_NOINTERFACE;
1648 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1650 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1651 ULONG ref = InterlockedIncrement(&This->ref);
1652 TRACE("(%p)->(%d)\n", This, ref);
1653 return ref;
1656 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1658 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1659 ULONG ref = InterlockedDecrement(&This->ref);
1661 TRACE("(%p)->(%d)\n", This, ref);
1663 if (!ref) {
1664 heap_free(This->locale);
1665 heap_free(This);
1668 return ref;
1671 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1672 dwritenumbersubstitution_QueryInterface,
1673 dwritenumbersubstitution_AddRef,
1674 dwritenumbersubstitution_Release
1677 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1678 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1680 struct dwrite_numbersubstitution *substitution;
1682 *ret = NULL;
1684 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1685 return E_INVALIDARG;
1687 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1688 return E_INVALIDARG;
1690 substitution = heap_alloc(sizeof(*substitution));
1691 if (!substitution)
1692 return E_OUTOFMEMORY;
1694 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1695 substitution->ref = 1;
1696 substitution->ignore_user_override = ignore_user_override;
1697 substitution->method = method;
1698 substitution->locale = heap_strdupW(locale);
1699 if (locale && !substitution->locale) {
1700 heap_free(substitution);
1701 return E_OUTOFMEMORY;
1704 *ret = &substitution->IDWriteNumberSubstitution_iface;
1705 return S_OK;
1708 /* IDWriteFontFallback */
1709 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback *iface, REFIID riid, void **obj)
1711 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1713 TRACE("(%p)->(%s %p)\n", fallback, debugstr_guid(riid), obj);
1715 if (IsEqualIID(riid, &IID_IDWriteFontFallback) || IsEqualIID(riid, &IID_IUnknown)) {
1716 *obj = iface;
1717 IDWriteFontFallback_AddRef(iface);
1718 return S_OK;
1721 *obj = NULL;
1722 return E_NOINTERFACE;
1725 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback *iface)
1727 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1728 TRACE("(%p)\n", fallback);
1729 return IDWriteFactory2_AddRef(fallback->factory);
1732 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback *iface)
1734 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1735 TRACE("(%p)\n", fallback);
1736 return IDWriteFactory2_Release(fallback->factory);
1739 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
1741 UINT32 i;
1743 for (i = 0; i < fallback->count; i++) {
1744 if (fallback->mappings[i].range.first <= ch && fallback->mappings[i].range.last >= ch)
1745 return &fallback->mappings[i];
1748 return NULL;
1751 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
1752 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
1754 IDWriteFontFamily *family;
1755 BOOL exists = FALSE;
1756 HRESULT hr;
1757 UINT32 i;
1759 *font = NULL;
1761 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
1762 if (FAILED(hr))
1763 return hr;
1765 if (!exists)
1766 return E_FAIL;
1768 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
1769 if (FAILED(hr))
1770 return hr;
1772 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
1773 IDWriteFontFamily_Release(family);
1774 return hr;
1777 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
1779 HRESULT hr = S_OK;
1780 UINT32 i;
1782 for (i = 0; i < length; i++) {
1783 UINT16 script = get_char_script(text[i]);
1784 BOOL exists;
1786 if (script == Script_Unknown || script == Script_Common) {
1787 ++*mapped_length;
1788 continue;
1791 /* stop on first unsupported character */
1792 exists = FALSE;
1793 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
1794 if (hr == S_OK && exists)
1795 ++*mapped_length;
1796 else
1797 break;
1800 return hr;
1803 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
1804 IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch,
1805 UINT32 *mapped_length, IDWriteFont **mapped_font)
1807 const struct fallback_mapping *mapping;
1808 HRESULT hr;
1810 mapping = find_fallback_mapping(fallback, text[0]);
1811 if (!mapping) {
1812 WARN("no mapping for 0x%x\n", text[0]);
1813 return E_FAIL;
1816 /* now let's see what fallback can handle */
1817 hr = create_matching_font(collection, mapping->family, weight, style, stretch, mapped_font);
1818 if (FAILED(hr)) {
1819 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping->family),
1820 mapping->range.first, mapping->range.last, hr);
1821 return hr;
1824 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
1825 if (FAILED(hr))
1826 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping->family), hr);
1828 if (!*mapped_length) {
1829 IDWriteFont_Release(*mapped_font);
1830 *mapped_font = NULL;
1833 return *mapped_length ? S_OK : E_FAIL;
1836 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
1837 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
1838 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
1839 IDWriteFont **ret_font, FLOAT *scale)
1841 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1842 WCHAR *buff = NULL;
1843 const WCHAR *text;
1844 HRESULT hr;
1846 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
1847 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
1849 *mapped_length = 0;
1850 *scale = 1.0f;
1851 *ret_font = NULL;
1853 if (!source)
1854 return E_INVALIDARG;
1856 if (length == 0)
1857 return S_OK;
1859 if (!basecollection) {
1860 hr = IDWriteFactory2_GetSystemFontCollection(fallback->factory, &basecollection, FALSE);
1861 if (FAILED(hr))
1862 return hr;
1864 else
1865 IDWriteFontCollection_AddRef(basecollection);
1867 hr = get_text_source_ptr(source, position, length, &text, &buff);
1868 if (FAILED(hr))
1869 goto done;
1871 if (basefamily && *basefamily) {
1872 IDWriteFont *mapped_font;
1874 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
1875 if (FAILED(hr))
1876 goto done;
1878 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
1879 if (FAILED(hr))
1880 goto done;
1882 if (!*mapped_length) {
1883 hr = fallback_get_fallback_font(fallback, text, length, basecollection, weight, style, stretch, mapped_length, &mapped_font);
1884 if (FAILED(hr)) {
1885 *mapped_length = length;
1886 hr = S_OK;
1887 goto done;
1889 else {
1890 IDWriteFont_Release(*ret_font);
1891 *ret_font = mapped_font;
1895 else
1896 hr = fallback_get_fallback_font(fallback, text, length, basecollection, weight, style, stretch, mapped_length, ret_font);
1898 done:
1899 IDWriteFontCollection_Release(basecollection);
1900 heap_free(buff);
1901 return hr;
1904 static const IDWriteFontFallbackVtbl fontfallbackvtbl = {
1905 fontfallback_QueryInterface,
1906 fontfallback_AddRef,
1907 fontfallback_Release,
1908 fontfallback_MapCharacters
1911 HRESULT create_system_fontfallback(IDWriteFactory2 *factory, IDWriteFontFallback **ret)
1913 struct dwrite_fontfallback *fallback;
1915 *ret = NULL;
1917 fallback = heap_alloc(sizeof(*fallback));
1918 if (!fallback)
1919 return E_OUTOFMEMORY;
1921 fallback->IDWriteFontFallback_iface.lpVtbl = &fontfallbackvtbl;
1922 fallback->factory = factory;
1923 fallback->mappings = fontfallback_neutral_data;
1924 fallback->count = sizeof(fontfallback_neutral_data)/sizeof(fontfallback_neutral_data[0]);
1926 *ret = &fallback->IDWriteFontFallback_iface;
1927 return S_OK;
1930 void release_system_fontfallback(IDWriteFontFallback *iface)
1932 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1933 heap_free(fallback);