user32: Implement IDI_SHIELD.
[wine.git] / dlls / dwrite / analyzer.c
blobbb08017611ff35a802065b8adc57e8175c1c4808
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[] DECLSPEC_HIDDEN;
32 extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN;
34 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
35 #define NATIVE_DIGITS_LEN 11
37 struct dwritescript_properties {
38 DWRITE_SCRIPT_PROPERTIES props;
39 UINT32 scripttag; /* OpenType script tag */
40 UINT32 scriptalttag; /* Version 2 tag, 0 if not defined */
41 BOOL is_complex;
42 const struct scriptshaping_ops *ops;
45 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
47 /* NOTE: keep this array synced with script ids from scripts.h */
48 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
49 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
51 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
52 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE },
53 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
54 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
55 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
56 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
57 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
58 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE },
59 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
60 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
61 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE },
62 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
63 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
64 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE },
65 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
66 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
67 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE },
68 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
69 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
70 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
71 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
72 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE },
73 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE },
74 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
75 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE },
76 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
77 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
78 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
79 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
80 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE },
81 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE },
82 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
83 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE },
84 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
85 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE },
86 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
87 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
88 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
89 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
90 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
91 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
92 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE },
93 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
94 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
95 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
96 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE },
97 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE },
98 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE, &latn_shaping_ops },
99 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
100 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
101 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
102 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
103 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
104 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
105 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE },
106 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
107 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
108 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE },
109 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE },
110 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE },
111 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE },
112 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE },
113 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
114 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
115 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE },
116 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
117 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
118 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE },
119 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE },
120 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE },
121 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
122 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
123 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE },
124 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
125 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
126 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
127 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE },
128 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
129 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
130 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE },
131 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
132 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
133 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE },
134 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
135 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
136 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE },
137 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE },
138 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE },
139 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE },
140 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE },
141 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE },
142 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
143 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE },
144 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE },
145 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','a','k','m') },
146 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','r','c') },
147 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('m','e','r','o') },
148 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('p','l','r','d') },
149 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','h','r','d') },
150 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','o','r','a') },
151 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','k','r') },
152 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','s','s') },
153 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','g','h','b') },
154 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('d','u','p','l') },
155 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','l','b','a') },
156 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','r','a','n') },
157 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','h','o','j') },
158 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','d') },
159 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','a') },
160 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','h','j') },
161 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','i') },
162 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','n','d') },
163 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('m','o','d','i') },
164 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','r','o','o') },
165 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('n','b','a','t') },
166 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','a','r','b') },
167 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','e','r','m') },
168 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','m','n','g') },
169 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('p','a','l','m') },
170 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','a','u','c') },
171 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','l','p') },
172 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, _OT('s','i','d','d') },
173 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('t','i','r','h') },
174 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('w','a','r','a') },
175 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','d','l','m') },
176 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','h','o','m') },
177 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','l','u','w') },
178 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','h','k','s') },
179 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','a','t','r') },
180 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','r','c') },
181 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','u','l','t') },
182 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','e','w','a') },
183 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','u','n','g') },
184 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','g','e') },
185 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','g','n','w') },
186 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','n','g') },
188 #undef _OT
190 const char *debugstr_sa_script(UINT16 script)
192 return script < Script_LastId ? debugstr_an((char*)&dwritescripts_properties[script].props.isoScriptCode, 4):
193 "undefined";
196 /* system font falback configuration */
197 static const WCHAR meiryoW[] = {'M','e','i','r','y','o',0};
199 static const WCHAR *cjk_families[] = { meiryoW };
201 static const DWRITE_UNICODE_RANGE cjk_ranges[] =
203 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
204 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
205 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
208 struct fallback_mapping {
209 DWRITE_UNICODE_RANGE *ranges;
210 UINT32 ranges_count;
211 WCHAR **families;
212 UINT32 families_count;
213 IDWriteFontCollection *collection;
214 WCHAR *locale;
215 FLOAT scale;
218 static const struct fallback_mapping fontfallback_neutral_data[] = {
219 #define MAPPING_RANGE(ranges, families) \
220 { (DWRITE_UNICODE_RANGE *)ranges, sizeof(ranges)/sizeof(ranges[0]), \
221 (WCHAR **)families, sizeof(families)/sizeof(families[0]) }
223 MAPPING_RANGE(cjk_ranges, cjk_families),
225 #undef MAPPING_RANGE
228 struct dwrite_fontfallback {
229 IDWriteFontFallback IDWriteFontFallback_iface;
230 LONG ref;
231 IDWriteFactory5 *factory;
232 IDWriteFontCollection1 *systemcollection;
233 struct fallback_mapping *mappings;
234 UINT32 mappings_count;
237 struct dwrite_fontfallback_builder {
238 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface;
239 LONG ref;
240 IDWriteFactory5 *factory;
241 struct fallback_mapping *mappings;
242 UINT32 mappings_count;
243 UINT32 mappings_capacity;
246 struct dwrite_numbersubstitution {
247 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
248 LONG ref;
250 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
251 WCHAR *locale;
252 BOOL ignore_user_override;
255 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
257 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
260 static struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface);
262 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback(IDWriteFontFallback *iface)
264 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback_iface);
267 static inline struct dwrite_fontfallback_builder *impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder *iface)
269 return CONTAINING_RECORD(iface, struct dwrite_fontfallback_builder, IDWriteFontFallbackBuilder_iface);
272 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
274 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
275 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
276 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
277 return ch;
279 return 0;
282 static inline UINT16 get_char_script(WCHAR c)
284 UINT16 script = get_table_entry(wine_scripts_table, c);
285 return script == Script_Inherited ? Script_Unknown : script;
288 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
290 DWRITE_SCRIPT_ANALYSIS sa;
292 sa.script = get_char_script(c);
293 sa.shapes = iscntrlW(c) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
294 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
295 return sa;
298 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
300 DWRITE_SCRIPT_ANALYSIS sa;
301 UINT32 pos, i, seq_length;
303 if (!length)
304 return S_OK;
306 sa = get_char_sa(*text);
308 pos = position;
309 seq_length = 1;
311 for (i = 1; i < length; i++)
313 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
315 /* Unknown type is ignored when preceded or followed by another script */
316 switch (sa.script) {
317 case Script_Unknown:
318 sa.script = cur_sa.script;
319 break;
320 case Script_Common:
321 if (cur_sa.script == Script_Unknown)
322 cur_sa.script = sa.script;
323 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
324 sa.script = cur_sa.script;
325 break;
326 default:
327 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
328 cur_sa.script = sa.script;
331 /* this is a length of a sequence to be reported next */
332 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
333 seq_length++;
334 else {
335 HRESULT hr;
337 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
338 if (FAILED(hr)) return hr;
339 pos = position + i;
340 seq_length = 1;
341 sa = cur_sa;
345 /* one char length case or normal completion call */
346 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
349 struct linebreaking_state {
350 DWRITE_LINE_BREAKPOINT *breakpoints;
351 UINT32 count;
354 enum BreakConditionLocation {
355 BreakConditionBefore,
356 BreakConditionAfter
359 enum linebreaking_classes {
360 b_BK = 1,
361 b_CR,
362 b_LF,
363 b_CM,
364 b_SG,
365 b_GL,
366 b_CB,
367 b_SP,
368 b_ZW,
369 b_NL,
370 b_WJ,
371 b_JL,
372 b_JV,
373 b_JT,
374 b_H2,
375 b_H3,
376 b_XX,
377 b_OP,
378 b_CL,
379 b_CP,
380 b_QU,
381 b_NS,
382 b_EX,
383 b_SY,
384 b_IS,
385 b_PR,
386 b_PO,
387 b_NU,
388 b_AL,
389 b_ID,
390 b_IN,
391 b_HY,
392 b_BB,
393 b_BA,
394 b_SA,
395 b_AI,
396 b_B2,
397 b_HL,
398 b_CJ,
399 b_RI,
400 b_EB,
401 b_EM,
402 b_ZWJ,
405 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
407 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
408 return TRUE;
410 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
411 return TRUE;
413 return FALSE;
416 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
417 set to "can break" and could only be changed once. */
418 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
419 struct linebreaking_state *state)
421 if (location == BreakConditionBefore) {
422 if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
423 return;
424 state->breakpoints[pos].breakConditionBefore = condition;
425 if (pos > 0)
426 state->breakpoints[pos-1].breakConditionAfter = condition;
428 else {
429 if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
430 return;
431 state->breakpoints[pos].breakConditionAfter = condition;
432 if (pos + 1 < state->count)
433 state->breakpoints[pos+1].breakConditionBefore = condition;
437 BOOL lb_is_newline_char(WCHAR ch)
439 short c = get_table_entry(wine_linebreak_table, ch);
440 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
443 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
445 struct linebreaking_state state;
446 short *break_class;
447 int i, j;
449 break_class = heap_alloc(count*sizeof(short));
450 if (!break_class)
451 return E_OUTOFMEMORY;
453 state.breakpoints = breakpoints;
454 state.count = count;
456 for (i = 0; i < count; i++)
458 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
460 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
461 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
462 breakpoints[i].isWhitespace = !!isspaceW(text[i]);
463 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
464 breakpoints[i].padding = 0;
466 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
467 switch (break_class[i])
469 case b_AI:
470 case b_SA:
471 case b_SG:
472 case b_XX:
473 break_class[i] = b_AL;
474 break;
475 case b_CJ:
476 break_class[i] = b_NS;
477 break;
481 /* LB2 - never break at the start */
482 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
483 /* LB3 - always break at the end. */
484 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
486 /* LB4 - LB6 - mandatory breaks. */
487 for (i = 0; i < count; i++)
489 switch (break_class[i])
491 /* LB4 - LB6 */
492 case b_CR:
493 /* LB5 - don't break CR x LF */
494 if (i < count-1 && break_class[i+1] == b_LF)
496 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
497 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
498 break;
500 case b_LF:
501 case b_NL:
502 case b_BK:
503 /* LB4 - LB5 - always break after hard breaks */
504 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
505 /* LB6 - do not break before hard breaks */
506 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
507 break;
511 /* LB7 - LB8 - explicit breaks and non-breaks */
512 for (i = 0; i < count; i++)
514 switch (break_class[i])
516 /* LB7 - do not break before spaces */
517 case b_SP:
518 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
519 break;
520 case b_ZW:
521 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
523 /* LB8 - break before character after zero-width space, skip spaces in-between */
524 j = i;
525 while (j < count-1 && break_class[j+1] == b_SP)
526 j++;
527 if (j < count-1 && break_class[j+1] != b_ZW)
528 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
529 break;
530 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
531 case b_ZWJ:
532 if (i < count-1 && (break_class[i+1] == b_ID || break_class[i+1] == b_EB || break_class[i+1] == b_EM))
533 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
534 break;
538 /* LB9 - LB10 - combining marks */
539 for (i = 0; i < count; i++)
541 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
543 if (i > 0)
545 switch (break_class[i-1])
547 case b_SP:
548 case b_BK:
549 case b_CR:
550 case b_LF:
551 case b_NL:
552 case b_ZW:
553 break_class[i] = b_AL;
554 break;
555 default:
557 break_class[i] = break_class[i-1];
558 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
562 else break_class[i] = b_AL;
566 for (i = 0; i < count; i++)
568 switch (break_class[i])
570 /* LB11 - don't break before and after word joiner */
571 case b_WJ:
572 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
573 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
574 break;
575 /* LB12 - don't break after glue */
576 case b_GL:
577 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
578 /* LB12a */
579 if (i > 0)
581 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
582 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
584 break;
585 /* LB13 */
586 case b_CL:
587 case b_CP:
588 case b_EX:
589 case b_IS:
590 case b_SY:
591 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
592 break;
593 /* LB14 - do not break after OP, even after spaces */
594 case b_OP:
595 j = i;
596 while (j < count-1 && break_class[j+1] == b_SP)
597 j++;
598 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
599 break;
600 /* LB15 - do not break within QU-OP, even with intervening spaces */
601 case b_QU:
602 j = i;
603 while (j < count-1 && break_class[j+1] == b_SP)
604 j++;
605 if (j < count - 1 && break_class[j+1] == b_OP)
606 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
607 break;
608 /* LB16 */
609 case b_NS:
610 j = i-1;
611 while(j > 0 && break_class[j] == b_SP)
612 j--;
613 if (break_class[j] == b_CL || break_class[j] == b_CP)
614 for (j++; j <= i; j++)
615 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
616 break;
617 /* LB17 - do not break within B2, even with intervening spaces */
618 case b_B2:
619 j = i;
620 while (j < count && break_class[j+1] == b_SP)
621 j++;
622 if (j < count - 1 && break_class[j+1] == b_B2)
623 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
624 break;
628 for (i = 0; i < count; i++)
630 switch(break_class[i])
632 /* LB18 - break is allowed after space */
633 case b_SP:
634 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
635 break;
636 /* LB19 - don't break before or after quotation mark */
637 case b_QU:
638 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
639 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
640 break;
641 /* LB20 */
642 case b_CB:
643 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
644 if (i < count - 1 && break_class[i+1] != b_QU)
645 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
646 break;
647 /* LB21 */
648 case b_BA:
649 case b_HY:
650 case b_NS:
651 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
652 break;
653 case b_BB:
654 if (i < count - 1 && break_class[i+1] != b_CB)
655 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
656 break;
657 /* LB21a, LB21b */
658 case b_HL:
659 /* LB21a */
660 if (i < count-1)
661 switch (break_class[i+1])
663 case b_HY:
664 case b_BA:
665 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
667 /* LB21b */
668 if (i > 0 && break_class[i-1] == b_SY)
669 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
670 break;
671 /* LB22 */
672 case b_IN:
673 if (i > 0)
675 switch (break_class[i-1])
677 case b_AL:
678 case b_HL:
679 case b_EX:
680 case b_ID:
681 case b_EB:
682 case b_EM:
683 case b_IN:
684 case b_NU:
685 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
688 break;
691 if (i < count-1)
693 /* LB23 - do not break between digits and letters */
694 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
695 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
696 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
697 (break_class[i] == b_NU && break_class[i+1] == b_HL))
698 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
700 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
701 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
702 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
703 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
704 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
705 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
706 (break_class[i] == b_EB && break_class[i+1] == b_PO))
707 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
709 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
710 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
711 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
712 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
713 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
714 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
715 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
716 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
717 (break_class[i] == b_HL && break_class[i+1] == b_PO))
718 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
720 /* LB25 */
721 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
722 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
723 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
724 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
725 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
726 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
727 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
728 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
729 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
730 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
731 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
732 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
733 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
734 (break_class[i] == b_SY && break_class[i+1] == b_NU))
735 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
737 /* LB26 */
738 if (break_class[i] == b_JL)
740 switch (break_class[i+1])
742 case b_JL:
743 case b_JV:
744 case b_H2:
745 case b_H3:
746 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
749 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
750 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
751 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
752 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
753 break_class[i+1] == b_JT)
754 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
756 /* LB27 */
757 switch (break_class[i])
759 case b_JL:
760 case b_JV:
761 case b_JT:
762 case b_H2:
763 case b_H3:
764 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
765 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
767 if (break_class[i] == b_PR)
769 switch (break_class[i+1])
771 case b_JL:
772 case b_JV:
773 case b_JT:
774 case b_H2:
775 case b_H3:
776 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
780 /* LB28 */
781 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
782 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
783 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
784 (break_class[i] == b_HL && break_class[i+1] == b_HL))
785 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
787 /* LB29 */
788 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
789 (break_class[i] == b_IS && break_class[i+1] == b_HL))
790 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
792 /* LB30 */
793 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
794 break_class[i+1] == b_OP)
795 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
796 if (break_class[i] == b_CP &&
797 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
798 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
800 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
801 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
802 unsigned int c = 0;
804 j = i + 1;
805 while (j > 0 && break_class[--j] == b_RI)
806 c++;
808 if ((c & 1) == 0)
809 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
812 /* LB30b - do not break between an emoji base and an emoji modifier */
813 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
814 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
818 /* LB31 - allow breaks everywhere else. */
819 for (i = 0; i < count; i++)
821 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
822 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
825 heap_free(break_class);
826 return S_OK;
829 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
831 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
833 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
834 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
835 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
836 IsEqualIID(riid, &IID_IUnknown))
838 *obj = iface;
839 return S_OK;
842 WARN("%s not implemented.\n", debugstr_guid(riid));
844 *obj = NULL;
845 return E_NOINTERFACE;
848 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
850 return 2;
853 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
855 return 1;
858 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
859 data after a first request. */
860 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
862 HRESULT hr;
863 UINT32 len;
865 *buff = NULL;
866 *text = NULL;
867 len = 0;
868 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
869 if (FAILED(hr)) return hr;
871 if (len < length) {
872 UINT32 read;
874 *buff = heap_alloc(length*sizeof(WCHAR));
875 if (!*buff)
876 return E_OUTOFMEMORY;
877 memcpy(*buff, *text, len*sizeof(WCHAR));
878 read = len;
880 while (read < length && *text) {
881 *text = NULL;
882 len = 0;
883 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
884 if (FAILED(hr)) {
885 heap_free(*buff);
886 return hr;
888 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
889 read += len;
892 *text = *buff;
895 return hr;
898 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
899 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
901 WCHAR *buff = NULL;
902 const WCHAR *text;
903 HRESULT hr;
905 TRACE("(%p %u %u %p)\n", source, position, length, sink);
907 if (length == 0)
908 return S_OK;
910 hr = get_text_source_ptr(source, position, length, &text, &buff);
911 if (FAILED(hr))
912 return hr;
914 hr = analyze_script(text, position, length, sink);
915 heap_free(buff);
917 return hr;
920 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
921 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
923 UINT8 *levels = NULL, *explicit = NULL;
924 UINT8 baselevel, level, explicit_level;
925 UINT32 pos, i, seq_length;
926 WCHAR *buff = NULL;
927 const WCHAR *text;
928 HRESULT hr;
930 TRACE("(%p %u %u %p)\n", source, position, length, sink);
932 if (!length)
933 return S_OK;
935 hr = get_text_source_ptr(source, position, length, &text, &buff);
936 if (FAILED(hr))
937 return hr;
939 levels = heap_alloc(length*sizeof(*levels));
940 explicit = heap_alloc(length*sizeof(*explicit));
942 if (!levels || !explicit) {
943 hr = E_OUTOFMEMORY;
944 goto done;
947 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
948 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
949 if (FAILED(hr))
950 goto done;
952 level = levels[0];
953 explicit_level = explicit[0];
954 pos = position;
955 seq_length = 1;
957 for (i = 1; i < length; i++) {
958 if (levels[i] == level && explicit[i] == explicit_level)
959 seq_length++;
960 else {
961 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
962 if (FAILED(hr))
963 goto done;
965 pos += seq_length;
966 seq_length = 1;
967 level = levels[i];
968 explicit_level = explicit[i];
971 /* one char length case or normal completion call */
972 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
974 done:
975 heap_free(explicit);
976 heap_free(levels);
977 heap_free(buff);
979 return hr;
982 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
983 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
985 static int once;
987 if (!once++)
988 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
989 return S_OK;
992 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
993 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
995 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
996 WCHAR *buff = NULL;
997 const WCHAR *text;
998 HRESULT hr;
999 UINT32 len;
1001 TRACE("(%p %u %u %p)\n", source, position, length, sink);
1003 if (length == 0)
1004 return S_OK;
1006 /* get some, check for length */
1007 text = NULL;
1008 len = 0;
1009 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
1010 if (FAILED(hr)) return hr;
1012 if (len < length) {
1013 UINT32 read;
1015 buff = heap_alloc(length*sizeof(WCHAR));
1016 if (!buff)
1017 return E_OUTOFMEMORY;
1018 memcpy(buff, text, len*sizeof(WCHAR));
1019 read = len;
1021 while (read < length && text) {
1022 text = NULL;
1023 len = 0;
1024 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
1025 if (FAILED(hr))
1026 goto done;
1027 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
1028 read += len;
1031 text = buff;
1034 breakpoints = heap_alloc(length*sizeof(*breakpoints));
1035 if (!breakpoints) {
1036 hr = E_OUTOFMEMORY;
1037 goto done;
1040 hr = analyze_linebreaks(text, length, breakpoints);
1041 if (FAILED(hr))
1042 goto done;
1044 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1046 done:
1047 heap_free(breakpoints);
1048 heap_free(buff);
1050 return hr;
1053 static UINT32 get_opentype_language(const WCHAR *locale)
1055 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
1057 if (locale) {
1058 WCHAR tag[5];
1059 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
1060 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1063 return language;
1066 static DWRITE_NUMBER_SUBSTITUTION_METHOD get_number_substitutes(IDWriteNumberSubstitution *substitution, WCHAR *digits)
1068 struct dwrite_numbersubstitution *numbersubst = unsafe_impl_from_IDWriteNumberSubstitution(substitution);
1069 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1070 WCHAR isolang[9];
1071 DWORD lctype;
1073 if (!numbersubst)
1074 return DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1076 lctype = numbersubst->ignore_user_override ? LOCALE_NOUSEROVERRIDE : 0;
1078 if (numbersubst->method == DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE) {
1079 DWORD value;
1081 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1082 if (GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, (WCHAR *)&value, 2)) {
1083 switch (value)
1085 case 0:
1086 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL;
1087 break;
1088 case 2:
1089 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL;
1090 break;
1091 case 1:
1092 default:
1093 if (value != 1)
1094 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value, debugstr_w(numbersubst->locale));
1097 else
1098 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst->locale));
1100 else
1101 method = numbersubst->method;
1103 digits[0] = 0;
1104 switch (method)
1106 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL:
1107 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1108 break;
1109 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1110 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL:
1111 if (GetLocaleInfoEx(numbersubst->locale, LOCALE_SISO639LANGNAME, isolang, sizeof(isolang)/sizeof(isolang[0]))) {
1112 static const WCHAR arW[] = {'a','r',0};
1113 static const WCHAR arabicW[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1115 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1116 if (!strcmpW(arW, isolang)) {
1117 strcpyW(digits, arabicW);
1118 break;
1121 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1122 break;
1123 default:
1127 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !*digits) {
1128 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst->locale), method);
1129 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1132 return method;
1135 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES const **features,
1136 UINT32 const *feature_range_lengths, UINT32 feature_ranges)
1138 UINT32 i, j, start;
1140 if (!TRACE_ON(dwrite) || !features)
1141 return;
1143 for (i = 0, start = 0; i < feature_ranges; i++, start += feature_range_lengths[i]) {
1144 TRACE("feature range [%u,%u)\n", start, start + feature_range_lengths[i]);
1145 for (j = 0; j < features[i]->featureCount; j++)
1146 TRACE("feature %s, parameter %u\n", debugstr_an((char *)&features[i]->features[j].nameTag, 4),
1147 features[i]->features[j].parameter);
1151 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1152 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1153 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1154 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1155 UINT32 const* feature_range_lengths, UINT32 feature_ranges, UINT32 max_glyph_count,
1156 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
1157 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1159 const struct dwritescript_properties *scriptprops;
1160 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1161 struct scriptshaping_context context;
1162 struct scriptshaping_cache *cache = NULL;
1163 BOOL update_cluster, need_vertical;
1164 WCHAR digits[NATIVE_DIGITS_LEN];
1165 IDWriteFontFace1 *fontface1;
1166 WCHAR *string;
1167 UINT32 i, g;
1168 HRESULT hr = S_OK;
1169 UINT16 script;
1171 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
1172 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1173 features, feature_range_lengths, feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices,
1174 glyph_props, actual_glyph_count);
1176 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1178 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1180 if (max_glyph_count < length)
1181 return E_NOT_SUFFICIENT_BUFFER;
1183 string = heap_alloc(sizeof(WCHAR)*length);
1184 if (!string)
1185 return E_OUTOFMEMORY;
1187 method = get_number_substitutes(substitution, digits);
1189 for (i = 0; i < length; i++) {
1190 /* FIXME: set to better values */
1191 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
1192 glyph_props[i].isClusterStart = 1;
1193 glyph_props[i].isDiacritic = 0;
1194 glyph_props[i].isZeroWidthSpace = 0;
1195 glyph_props[i].reserved = 0;
1197 /* FIXME: have the shaping engine set this */
1198 text_props[i].isShapedAlone = 0;
1199 text_props[i].reserved = 0;
1201 clustermap[i] = i;
1203 string[i] = text[i];
1204 switch (method)
1206 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1207 if (!is_rtl)
1208 break;
1209 /* fallthrough */
1210 default:
1211 if (string[i] >= '0' && string[i] <= '9')
1212 string[i] = digits[string[i] - '0'];
1213 break;
1214 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE:
1219 for (; i < max_glyph_count; i++) {
1220 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
1221 glyph_props[i].isClusterStart = 0;
1222 glyph_props[i].isDiacritic = 0;
1223 glyph_props[i].isZeroWidthSpace = 0;
1224 glyph_props[i].reserved = 0;
1227 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1228 if (FAILED(hr))
1229 WARN("failed to get IDWriteFontFace1\n");
1231 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
1233 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
1234 UINT32 codepoint;
1236 if (!update_cluster) {
1237 codepoint = decode_surrogate_pair(string, i, length);
1238 if (!codepoint)
1239 codepoint = is_rtl ? bidi_get_mirrored_char(string[i]) : string[i];
1240 else
1241 update_cluster = TRUE;
1243 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
1244 if (FAILED(hr))
1245 goto done;
1247 if (need_vertical) {
1248 UINT16 vertical;
1250 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
1251 if (hr == S_OK)
1252 glyph_indices[g] = vertical;
1255 g++;
1257 else {
1258 INT32 k;
1260 update_cluster = FALSE;
1261 /* mark surrogate halves with same cluster */
1262 clustermap[i] = clustermap[i-1];
1263 /* update following clusters */
1264 for (k = i + 1; k >= 0 && k < length; k++)
1265 clustermap[k]--;
1268 *actual_glyph_count = g;
1270 hr = create_scriptshaping_cache(fontface, &cache);
1271 if (FAILED(hr))
1272 goto done;
1274 context.cache = cache;
1275 context.text = text;
1276 context.length = length;
1277 context.is_rtl = is_rtl;
1278 context.max_glyph_count = max_glyph_count;
1279 context.language_tag = get_opentype_language(locale);
1281 scriptprops = &dwritescripts_properties[script];
1282 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
1283 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
1284 if (FAILED(hr))
1285 goto done;
1288 /* FIXME: apply default features */
1290 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1291 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1292 else
1293 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1295 done:
1296 if (fontface1)
1297 IDWriteFontFace1_Release(fontface1);
1298 release_scriptshaping_cache(cache);
1299 heap_free(string);
1301 return hr;
1304 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1305 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1306 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1307 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1308 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1309 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1311 DWRITE_FONT_METRICS metrics;
1312 IDWriteFontFace1 *fontface1;
1313 HRESULT hr;
1314 UINT32 i;
1316 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1317 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1318 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_lengths,
1319 feature_ranges, advances, offsets);
1321 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1323 if (glyph_count == 0)
1324 return S_OK;
1326 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1327 if (FAILED(hr)) {
1328 WARN("failed to get IDWriteFontFace1.\n");
1329 return hr;
1332 IDWriteFontFace_GetMetrics(fontface, &metrics);
1333 for (i = 0; i < glyph_count; i++) {
1334 if (glyph_props[i].isZeroWidthSpace)
1335 advances[i] = 0.0f;
1336 else {
1337 INT32 a;
1339 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1340 if (FAILED(hr))
1341 a = 0;
1342 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1344 offsets[i].advanceOffset = 0.0f;
1345 offsets[i].ascenderOffset = 0.0f;
1348 /* FIXME: actually apply features */
1350 IDWriteFontFace1_Release(fontface1);
1351 return S_OK;
1354 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1355 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1356 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1357 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1358 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1359 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1360 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1362 DWRITE_FONT_METRICS metrics;
1363 IDWriteFontFace1 *fontface1;
1364 HRESULT hr;
1365 UINT32 i;
1367 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1368 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1369 transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale),
1370 features, feature_range_lengths, feature_ranges, advances, offsets);
1372 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1374 if (glyph_count == 0)
1375 return S_OK;
1377 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1378 if (FAILED(hr)) {
1379 WARN("failed to get IDWriteFontFace1.\n");
1380 return hr;
1383 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1384 if (FAILED(hr)) {
1385 IDWriteFontFace1_Release(fontface1);
1386 WARN("failed to get compat metrics, 0x%08x\n", hr);
1387 return hr;
1389 for (i = 0; i < glyph_count; i++) {
1390 INT32 a;
1392 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1393 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1394 if (FAILED(hr))
1395 advances[i] = 0.0f;
1396 else
1397 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1398 offsets[i].advanceOffset = 0.0f;
1399 offsets[i].ascenderOffset = 0.0f;
1402 /* FIXME: actually apply features */
1404 IDWriteFontFace1_Release(fontface1);
1405 return S_OK;
1408 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1410 FLOAT advance = 0.0f;
1411 for (; start < end; start++)
1412 advance += advances[start];
1413 return advance;
1416 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1417 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1418 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1420 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1421 FLOAT advance = advances[g];
1422 FLOAT origin = 0.0f;
1424 if (props[g].isZeroWidthSpace) {
1425 modified_advances[g] = advances[g];
1426 modified_offsets[g] = offsets[g];
1427 return;
1430 /* first apply negative spacing and check if we hit minimum width */
1431 if (leading_spacing < 0.0f) {
1432 advance += leading_spacing;
1433 origin -= leading_spacing;
1435 if (trailing_spacing < 0.0f)
1436 advance += trailing_spacing;
1438 if (advance < min_advance_width) {
1439 FLOAT half = (min_advance_width - advance) / 2.0f;
1441 if (!reduced)
1442 origin -= half;
1443 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1444 origin -= half;
1445 else if (leading_spacing < 0.0f)
1446 origin -= min_advance_width - advance;
1448 advance = min_advance_width;
1451 /* now apply positive spacing adjustments */
1452 if (leading_spacing > 0.0f) {
1453 advance += leading_spacing;
1454 origin -= leading_spacing;
1456 if (trailing_spacing > 0.0f)
1457 advance += trailing_spacing;
1459 modified_advances[g] = advance;
1460 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1461 /* ascender is never touched, it's orthogonal to reading direction and is not
1462 affected by advance adjustments */
1463 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1466 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1467 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1468 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1470 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1471 FLOAT advance = get_cluster_advance(advances, start, end);
1472 FLOAT origin = 0.0f;
1473 UINT16 g;
1475 modified_advances[start] = advances[start];
1476 modified_advances[end-1] = advances[end-1];
1478 /* first apply negative spacing and check if we hit minimum width */
1479 if (leading_spacing < 0.0f) {
1480 advance += leading_spacing;
1481 modified_advances[start] += leading_spacing;
1482 origin -= leading_spacing;
1484 if (trailing_spacing < 0.0f) {
1485 advance += trailing_spacing;
1486 modified_advances[end-1] += trailing_spacing;
1489 advance = min_advance_width - advance;
1490 if (advance > 0.0f) {
1491 /* additional spacing is only applied to leading and trailing glyph */
1492 FLOAT half = advance / 2.0f;
1494 if (!reduced) {
1495 origin -= half;
1496 modified_advances[start] += half;
1497 modified_advances[end-1] += half;
1499 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f) {
1500 origin -= half;
1501 modified_advances[start] += half;
1502 modified_advances[end-1] += half;
1504 else if (leading_spacing < 0.0f) {
1505 origin -= advance;
1506 modified_advances[start] += advance;
1508 else
1509 modified_advances[end-1] += advance;
1512 /* now apply positive spacing adjustments */
1513 if (leading_spacing > 0.0f) {
1514 modified_advances[start] += leading_spacing;
1515 origin -= leading_spacing;
1517 if (trailing_spacing > 0.0f)
1518 modified_advances[end-1] += trailing_spacing;
1520 for (g = start; g < end; g++) {
1521 if (g == start) {
1522 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1523 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1525 else if (g == end - 1)
1526 /* trailing glyph offset is not adjusted */
1527 modified_offsets[g] = offsets[g];
1528 else {
1529 /* for all glyphs within a cluster use original advances and offsets */
1530 modified_advances[g] = advances[g];
1531 modified_offsets[g] = offsets[g];
1536 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1538 UINT16 g = clustermap[start];
1539 UINT32 length = 1;
1541 while (start < text_len && clustermap[++start] == g)
1542 length++;
1543 return length;
1546 /* Applies spacing adjustments to clusters.
1548 Adjustments are applied in the following order:
1550 1. Negative adjustments
1552 Leading and trailing spacing could be negative, at this step
1553 only negative ones are actually applied. Leading spacing is only
1554 applied to leading glyph, trailing - to trailing glyph.
1556 2. Minimum advance width
1558 Advances could only be reduced at this point or unchanged. In any
1559 case it's checked if cluster advance width is less than minimum width.
1560 If it's the case advance width is incremented up to minimum value.
1562 Important part is the direction in which this increment is applied;
1563 it depends on direction from which total cluster advance was trimmed
1564 at step 1. So it could be incremented from leading, trailing, or both
1565 sides. When applied to both sides, each side gets half of difference
1566 that brings advance to minimum width.
1568 3. Positive adjustments
1570 After minimum width rule was applied, positive spacing is applied in the same
1571 way as negative one on step 1.
1573 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1574 keeps its position in coordinate system where initial advance width is counted
1575 from 0.
1577 Glyph properties
1579 It's known that isZeroWidthSpace property keeps initial advance from changing.
1581 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1582 with more than one glyph.
1585 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1586 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1587 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1588 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1590 UINT16 start;
1592 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1593 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1595 if (min_advance_width < 0.0f) {
1596 if (modified_advances != advances)
1597 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1598 return E_INVALIDARG;
1601 /* minimum advance is not applied if no adjustments were made */
1602 if (leading_spacing == 0.0f && trailing_spacing == 0.0f) {
1603 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1604 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1605 return S_OK;
1608 for (start = 0; start < len;) {
1609 UINT32 length = get_cluster_length(clustermap, start, len);
1611 if (length == 1) {
1612 UINT32 g = clustermap[start];
1614 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1615 g, advances, offsets, props, modified_advances, modified_offsets);
1617 else {
1618 UINT32 g_start, g_end;
1620 g_start = clustermap[start];
1621 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1623 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1624 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1627 start += length;
1630 return S_OK;
1633 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1634 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1635 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1637 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1638 baseline_coord, exists);
1639 return E_NOTIMPL;
1642 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1643 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1645 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1646 return E_NOTIMPL;
1649 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1650 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1652 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1653 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1656 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1657 DWRITE_SCRIPT_PROPERTIES *props)
1659 TRACE("(%u %p)\n", sa.script, props);
1661 if (sa.script > Script_LastId)
1662 return E_INVALIDARG;
1664 *props = dwritescripts_properties[sa.script].props;
1665 return S_OK;
1668 static inline BOOL is_char_from_simple_script(WCHAR c)
1670 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c) ||
1671 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1672 c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e))
1673 return FALSE;
1674 else {
1675 UINT16 script = get_char_script(c);
1676 return !dwritescripts_properties[script].is_complex;
1680 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1681 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1683 HRESULT hr = S_OK;
1684 int i;
1686 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1688 *is_simple = FALSE;
1689 *len_read = 0;
1691 if (!face)
1692 return E_INVALIDARG;
1694 if (len == 0) {
1695 *is_simple = TRUE;
1696 return S_OK;
1699 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1700 for (i = 1; i < len && text[i]; i++) {
1701 if (is_char_from_simple_script(text[i])) {
1702 if (!*is_simple)
1703 break;
1705 else
1706 *is_simple = FALSE;
1709 *len_read = i;
1711 /* fetch indices */
1712 if (*is_simple && indices) {
1713 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1714 if (!codepoints)
1715 return E_OUTOFMEMORY;
1717 for (i = 0; i < *len_read; i++)
1718 codepoints[i] = text[i];
1720 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1721 heap_free(codepoints);
1724 return hr;
1727 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1728 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1729 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1731 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1732 debugstr_wn(text, length), clustermap, prop, jo);
1733 return E_NOTIMPL;
1736 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1737 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1738 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1740 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1741 justifiedoffsets);
1742 return E_NOTIMPL;
1745 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1746 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1747 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1748 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1749 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1750 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1752 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,
1753 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1754 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1755 return E_NOTIMPL;
1758 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1759 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1761 static const DWRITE_MATRIX transforms[] = {
1762 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1763 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1764 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1765 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1768 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1770 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1771 memset(m, 0, sizeof(*m));
1772 return E_INVALIDARG;
1775 /* for sideways case simply rotate 90 degrees more */
1776 if (is_sideways) {
1777 switch (angle) {
1778 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1779 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1780 break;
1781 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1782 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1783 break;
1784 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1785 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1786 break;
1787 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1788 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1789 break;
1790 default:
1795 *m = transforms[angle];
1797 /* shift components represent transform necessary to get from original point to
1798 rotated one in new coordinate system */
1799 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1800 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1801 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1804 return S_OK;
1807 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1808 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1809 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1811 const struct dwritescript_properties *props;
1812 HRESULT hr = S_OK;
1813 UINT32 language;
1815 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1816 tags);
1818 if (sa.script > Script_LastId)
1819 return E_INVALIDARG;
1821 language = get_opentype_language(locale);
1822 props = &dwritescripts_properties[sa.script];
1823 *actual_tagcount = 0;
1825 if (props->scriptalttag)
1826 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1828 if (*actual_tagcount == 0)
1829 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1831 return hr;
1834 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1835 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1836 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1838 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1839 feature_applies);
1840 return E_NOTIMPL;
1843 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1844 dwritetextanalyzer_QueryInterface,
1845 dwritetextanalyzer_AddRef,
1846 dwritetextanalyzer_Release,
1847 dwritetextanalyzer_AnalyzeScript,
1848 dwritetextanalyzer_AnalyzeBidi,
1849 dwritetextanalyzer_AnalyzeNumberSubstitution,
1850 dwritetextanalyzer_AnalyzeLineBreakpoints,
1851 dwritetextanalyzer_GetGlyphs,
1852 dwritetextanalyzer_GetGlyphPlacements,
1853 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1854 dwritetextanalyzer1_ApplyCharacterSpacing,
1855 dwritetextanalyzer1_GetBaseline,
1856 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1857 dwritetextanalyzer1_GetGlyphOrientationTransform,
1858 dwritetextanalyzer1_GetScriptProperties,
1859 dwritetextanalyzer1_GetTextComplexity,
1860 dwritetextanalyzer1_GetJustificationOpportunities,
1861 dwritetextanalyzer1_JustifyGlyphAdvances,
1862 dwritetextanalyzer1_GetJustifiedGlyphs,
1863 dwritetextanalyzer2_GetGlyphOrientationTransform,
1864 dwritetextanalyzer2_GetTypographicFeatures,
1865 dwritetextanalyzer2_CheckTypographicFeature
1868 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1870 IDWriteTextAnalyzer *get_text_analyzer(void)
1872 return (IDWriteTextAnalyzer *)&textanalyzer;
1875 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1877 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1879 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1881 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1882 IsEqualIID(riid, &IID_IUnknown))
1884 *obj = iface;
1885 IDWriteNumberSubstitution_AddRef(iface);
1886 return S_OK;
1889 WARN("%s not implemented.\n", debugstr_guid(riid));
1891 *obj = NULL;
1893 return E_NOINTERFACE;
1896 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1898 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1899 ULONG ref = InterlockedIncrement(&This->ref);
1900 TRACE("(%p)->(%d)\n", This, ref);
1901 return ref;
1904 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1906 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1907 ULONG ref = InterlockedDecrement(&This->ref);
1909 TRACE("(%p)->(%d)\n", This, ref);
1911 if (!ref) {
1912 heap_free(This->locale);
1913 heap_free(This);
1916 return ref;
1919 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1920 dwritenumbersubstitution_QueryInterface,
1921 dwritenumbersubstitution_AddRef,
1922 dwritenumbersubstitution_Release
1925 struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
1927 if (!iface || iface->lpVtbl != &numbersubstitutionvtbl)
1928 return NULL;
1929 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
1932 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1933 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1935 struct dwrite_numbersubstitution *substitution;
1937 *ret = NULL;
1939 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1940 return E_INVALIDARG;
1942 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1943 return E_INVALIDARG;
1945 substitution = heap_alloc(sizeof(*substitution));
1946 if (!substitution)
1947 return E_OUTOFMEMORY;
1949 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1950 substitution->ref = 1;
1951 substitution->ignore_user_override = ignore_user_override;
1952 substitution->method = method;
1953 substitution->locale = heap_strdupW(locale);
1954 if (locale && !substitution->locale) {
1955 heap_free(substitution);
1956 return E_OUTOFMEMORY;
1959 *ret = &substitution->IDWriteNumberSubstitution_iface;
1960 return S_OK;
1963 /* IDWriteFontFallback */
1964 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback *iface, REFIID riid, void **obj)
1966 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1968 TRACE("(%p)->(%s %p)\n", fallback, debugstr_guid(riid), obj);
1970 if (IsEqualIID(riid, &IID_IDWriteFontFallback) || IsEqualIID(riid, &IID_IUnknown)) {
1971 *obj = iface;
1972 IDWriteFontFallback_AddRef(iface);
1973 return S_OK;
1976 WARN("%s not implemented.\n", debugstr_guid(riid));
1978 *obj = NULL;
1979 return E_NOINTERFACE;
1982 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback *iface)
1984 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1985 TRACE("(%p)\n", fallback);
1986 return IDWriteFactory5_AddRef(fallback->factory);
1989 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback *iface)
1991 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1992 TRACE("(%p)\n", fallback);
1993 return IDWriteFactory5_Release(fallback->factory);
1996 static int compare_mapping_range(const void *a, const void *b)
1998 UINT32 ch = *(UINT32 *)a;
1999 DWRITE_UNICODE_RANGE *range = (DWRITE_UNICODE_RANGE *)b;
2001 if (ch > range->last)
2002 return 1;
2003 else if (ch < range->first)
2004 return -1;
2005 else
2006 return 0;
2009 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
2011 UINT32 i;
2013 for (i = 0; i < fallback->mappings_count; i++) {
2014 struct fallback_mapping *mapping = &fallback->mappings[i];
2016 if (bsearch(&ch, mapping->ranges, mapping->ranges_count, sizeof(*mapping->ranges),
2017 compare_mapping_range) != NULL)
2018 return mapping;
2021 return NULL;
2024 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
2025 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
2027 IDWriteFontFamily *family;
2028 BOOL exists = FALSE;
2029 HRESULT hr;
2030 UINT32 i;
2032 *font = NULL;
2034 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
2035 if (FAILED(hr))
2036 return hr;
2038 if (!exists)
2039 return E_FAIL;
2041 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
2042 if (FAILED(hr))
2043 return hr;
2045 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
2046 IDWriteFontFamily_Release(family);
2047 return hr;
2050 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
2052 HRESULT hr = S_OK;
2053 UINT32 i;
2055 for (i = 0; i < length; i++) {
2056 UINT16 script = get_char_script(text[i]);
2057 BOOL exists;
2059 if (script == Script_Unknown || script == Script_Common) {
2060 ++*mapped_length;
2061 continue;
2064 /* stop on first unsupported character */
2065 exists = FALSE;
2066 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
2067 if (hr == S_OK && exists)
2068 ++*mapped_length;
2069 else
2070 break;
2073 return hr;
2076 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
2077 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2078 IDWriteFont **mapped_font)
2080 const struct fallback_mapping *mapping;
2081 HRESULT hr;
2082 UINT32 i;
2084 *mapped_font = NULL;
2086 mapping = find_fallback_mapping(fallback, text[0]);
2087 if (!mapping) {
2088 WARN("No mapping range for %#x.\n", text[0]);
2089 return E_FAIL;
2092 /* Now let's see what fallback can handle. Pick first font that could be created. */
2093 for (i = 0; i < mapping->families_count; i++) {
2094 hr = create_matching_font((IDWriteFontCollection *)fallback->systemcollection, mapping->families[i],
2095 weight, style, stretch, mapped_font);
2096 if (hr == S_OK) {
2097 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping->families[i]));
2098 break;
2102 if (!*mapped_font) {
2103 WARN("Failed to create fallback font.\n");
2104 return E_FAIL;
2107 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
2108 if (FAILED(hr))
2109 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping->families[i]), hr);
2111 if (!*mapped_length) {
2112 IDWriteFont_Release(*mapped_font);
2113 *mapped_font = NULL;
2116 return *mapped_length ? S_OK : E_FAIL;
2119 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
2120 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2121 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2122 IDWriteFont **ret_font, FLOAT *scale)
2124 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2125 WCHAR *buff = NULL;
2126 const WCHAR *text;
2127 HRESULT hr;
2129 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
2130 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2132 *mapped_length = 0;
2133 *scale = 1.0f;
2134 *ret_font = NULL;
2136 if (!source)
2137 return E_INVALIDARG;
2139 if (length == 0)
2140 return S_OK;
2142 if (!basecollection)
2143 basecollection = (IDWriteFontCollection*)fallback->systemcollection;
2145 hr = get_text_source_ptr(source, position, length, &text, &buff);
2146 if (FAILED(hr))
2147 goto done;
2149 if (basefamily && *basefamily) {
2150 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
2151 if (FAILED(hr))
2152 goto done;
2154 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
2155 if (FAILED(hr))
2156 goto done;
2159 if (!*mapped_length) {
2160 IDWriteFont *mapped_font;
2162 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
2163 if (FAILED(hr)) {
2164 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2165 if (*ret_font) {
2166 *mapped_length = length;
2167 hr = S_OK;
2170 else {
2171 if (*ret_font)
2172 IDWriteFont_Release(*ret_font);
2173 *ret_font = mapped_font;
2177 done:
2178 heap_free(buff);
2179 return hr;
2182 static const IDWriteFontFallbackVtbl fontfallbackvtbl = {
2183 fontfallback_QueryInterface,
2184 fontfallback_AddRef,
2185 fontfallback_Release,
2186 fontfallback_MapCharacters
2189 HRESULT create_system_fontfallback(IDWriteFactory5 *factory, IDWriteFontFallback **ret)
2191 struct dwrite_fontfallback *fallback;
2193 *ret = NULL;
2195 fallback = heap_alloc(sizeof(*fallback));
2196 if (!fallback)
2197 return E_OUTOFMEMORY;
2199 fallback->IDWriteFontFallback_iface.lpVtbl = &fontfallbackvtbl;
2200 fallback->factory = factory;
2201 fallback->mappings = (struct fallback_mapping *)fontfallback_neutral_data;
2202 fallback->mappings_count = sizeof(fontfallback_neutral_data) / sizeof(*fontfallback_neutral_data);
2203 IDWriteFactory5_GetSystemFontCollection(fallback->factory, FALSE, &fallback->systemcollection, FALSE);
2205 *ret = &fallback->IDWriteFontFallback_iface;
2206 return S_OK;
2209 void release_system_fontfallback(IDWriteFontFallback *iface)
2211 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2212 IDWriteFontCollection1_Release(fallback->systemcollection);
2213 heap_free(fallback);
2216 static ULONG WINAPI customfontfallback_AddRef(IDWriteFontFallback *iface)
2218 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2219 ULONG ref = InterlockedIncrement(&fallback->ref);
2220 TRACE("(%p)->(%d)\n", fallback, ref);
2221 return ref;
2224 static ULONG WINAPI customfontfallback_Release(IDWriteFontFallback *iface)
2226 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2227 ULONG ref = InterlockedDecrement(&fallback->ref);
2229 TRACE("(%p)->(%d)\n", fallback, ref);
2231 if (!ref) {
2232 IDWriteFactory5_Release(fallback->factory);
2233 heap_free(fallback);
2236 return ref;
2239 static HRESULT WINAPI customfontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
2240 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2241 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2242 IDWriteFont **ret_font, FLOAT *scale)
2244 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2246 FIXME("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
2247 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2249 return E_NOTIMPL;
2252 static const IDWriteFontFallbackVtbl customfontfallbackvtbl =
2254 fontfallback_QueryInterface,
2255 customfontfallback_AddRef,
2256 customfontfallback_Release,
2257 customfontfallback_MapCharacters,
2260 static HRESULT WINAPI fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder *iface, REFIID riid, void **obj)
2262 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2264 TRACE("(%p)->(%s %p)\n", fallbackbuilder, debugstr_guid(riid), obj);
2266 if (IsEqualIID(riid, &IID_IDWriteFontFallbackBuilder) || IsEqualIID(riid, &IID_IUnknown)) {
2267 *obj = iface;
2268 IDWriteFontFallbackBuilder_AddRef(iface);
2269 return S_OK;
2272 WARN("%s not implemented.\n", debugstr_guid(riid));
2274 *obj = NULL;
2275 return E_NOINTERFACE;
2278 static ULONG WINAPI fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder *iface)
2280 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2281 ULONG ref = InterlockedIncrement(&fallbackbuilder->ref);
2282 TRACE("(%p)->(%d)\n", fallbackbuilder, ref);
2283 return ref;
2286 static ULONG WINAPI fontfallbackbuilder_Release(IDWriteFontFallbackBuilder *iface)
2288 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2289 ULONG ref = InterlockedDecrement(&fallbackbuilder->ref);
2291 TRACE("(%p)->(%d)\n", fallbackbuilder, ref);
2293 if (!ref) {
2294 UINT32 i;
2296 for (i = 0; i < fallbackbuilder->mappings_count; i++) {
2297 struct fallback_mapping *mapping = &fallbackbuilder->mappings[i];
2298 UINT32 j;
2300 for (j = 0; j < mapping->families_count; j++)
2301 heap_free(mapping->families[j]);
2302 heap_free(mapping->families);
2304 if (mapping->collection)
2305 IDWriteFontCollection_Release(mapping->collection);
2306 heap_free(mapping->ranges);
2307 heap_free(mapping->locale);
2310 IDWriteFactory5_Release(fallbackbuilder->factory);
2311 heap_free(fallbackbuilder->mappings);
2312 heap_free(fallbackbuilder);
2315 return ref;
2318 static HRESULT WINAPI fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder *iface,
2319 const DWRITE_UNICODE_RANGE *ranges, UINT32 ranges_count, WCHAR const **target_families, UINT32 families_count,
2320 IDWriteFontCollection *collection, WCHAR const *locale, WCHAR const *base_family, FLOAT scale)
2322 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2323 struct fallback_mapping *mapping;
2324 UINT32 i;
2326 TRACE("(%p)->(%p, %u, %p, %u, %p, %s, %s, %f)\n", fallbackbuilder, ranges, ranges_count, target_families,
2327 families_count, collection, debugstr_w(locale), debugstr_w(base_family), scale);
2329 if (!ranges || ranges_count == 0 || !target_families || families_count == 0 || scale < 0.0f)
2330 return E_INVALIDARG;
2332 if (base_family)
2333 FIXME("base family ignored.\n");
2335 if (fallbackbuilder->mappings_count == fallbackbuilder->mappings_capacity) {
2336 struct fallback_mapping *mappings;
2338 if (fallbackbuilder->mappings_capacity == 0) {
2339 if ((mappings = heap_alloc(sizeof(*fallbackbuilder->mappings) * 16)))
2340 fallbackbuilder->mappings_capacity = 16;
2342 else {
2343 if ((mappings = heap_realloc(fallbackbuilder->mappings, sizeof(*fallbackbuilder->mappings) *
2344 fallbackbuilder->mappings_capacity * 2)))
2345 fallbackbuilder->mappings_capacity *= 2;
2347 if (!mappings)
2348 return E_OUTOFMEMORY;
2350 fallbackbuilder->mappings = mappings;
2353 mapping = &fallbackbuilder->mappings[fallbackbuilder->mappings_count++];
2355 mapping->ranges = heap_alloc(sizeof(*mapping->ranges) * ranges_count);
2356 memcpy(mapping->ranges, ranges, sizeof(*mapping->ranges) * ranges_count);
2357 mapping->ranges_count = ranges_count;
2358 mapping->families = heap_alloc_zero(sizeof(*mapping->families) * families_count);
2359 mapping->families_count = families_count;
2360 for (i = 0; i < families_count; i++)
2361 mapping->families[i] = heap_strdupW(target_families[i]);
2362 mapping->collection = collection;
2363 if (mapping->collection)
2364 IDWriteFontCollection_AddRef(mapping->collection);
2365 mapping->locale = heap_strdupW(locale);
2366 mapping->scale = scale;
2368 return S_OK;
2371 static HRESULT WINAPI fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder *iface, IDWriteFontFallback *fallback)
2373 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2375 FIXME("(%p)->(%p): stub\n", fallbackbuilder, fallback);
2377 return E_NOTIMPL;
2380 static HRESULT WINAPI fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder *iface,
2381 IDWriteFontFallback **ret)
2383 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2384 struct dwrite_fontfallback *fallback;
2386 FIXME("(%p)->(%p): stub\n", fallbackbuilder, ret);
2388 *ret = NULL;
2390 fallback = heap_alloc(sizeof(*fallback));
2391 if (!fallback)
2392 return E_OUTOFMEMORY;
2394 fallback->IDWriteFontFallback_iface.lpVtbl = &customfontfallbackvtbl;
2395 fallback->ref = 1;
2396 fallback->factory = fallbackbuilder->factory;
2397 IDWriteFactory5_AddRef(fallback->factory);
2399 *ret = &fallback->IDWriteFontFallback_iface;
2400 return S_OK;
2403 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl =
2405 fontfallbackbuilder_QueryInterface,
2406 fontfallbackbuilder_AddRef,
2407 fontfallbackbuilder_Release,
2408 fontfallbackbuilder_AddMapping,
2409 fontfallbackbuilder_AddMappings,
2410 fontfallbackbuilder_CreateFontFallback,
2413 HRESULT create_fontfallback_builder(IDWriteFactory5 *factory, IDWriteFontFallbackBuilder **ret)
2415 struct dwrite_fontfallback_builder *builder;
2417 *ret = NULL;
2419 builder = heap_alloc_zero(sizeof(*builder));
2420 if (!builder)
2421 return E_OUTOFMEMORY;
2423 builder->IDWriteFontFallbackBuilder_iface.lpVtbl = &fontfallbackbuildervtbl;
2424 builder->ref = 1;
2425 builder->factory = factory;
2426 IDWriteFactory5_AddRef(builder->factory);
2428 *ret = &builder->IDWriteFontFallbackBuilder_iface;
2429 return S_OK;