mshtml: Removed no longer used attr_name from event_info_t.
[wine.git] / dlls / dwrite / analyzer.c
blob14a93e89d83956a7eb9ab33b816c5c837bfddc73
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 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 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
49 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE },
50 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
51 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
52 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
53 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
54 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
55 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE },
56 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
57 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
58 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE },
59 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
60 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
61 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE },
62 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
63 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
64 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE },
65 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
66 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
67 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
68 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
69 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE },
70 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE },
71 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
72 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE },
73 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
74 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
75 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
76 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
77 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE },
78 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE },
79 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
80 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE },
81 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
82 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE },
83 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
84 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
85 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
86 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
87 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
88 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
89 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE },
90 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
91 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
92 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
93 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE },
94 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE },
95 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE, &latn_shaping_ops },
96 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
97 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
98 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
99 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
100 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
101 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
102 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE },
103 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
104 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
105 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE },
106 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE },
107 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE },
108 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE },
109 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE },
110 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
111 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
112 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE },
113 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
114 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
115 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE },
116 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE },
117 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE },
118 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
119 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
120 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE },
121 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
122 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
123 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
124 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE },
125 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
126 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
127 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE },
128 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
129 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
130 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE },
131 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
132 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
133 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE },
134 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE },
135 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE },
136 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE },
137 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE },
138 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE },
139 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
140 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE },
141 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE },
142 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','a','k','m') },
143 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','r','c') },
144 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('m','e','r','o') },
145 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('p','l','r','d') },
146 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','h','r','d') },
147 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','o','r','a') },
148 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','k','r') },
149 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','s','s') },
150 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','g','h','b') },
151 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('d','u','p','l') },
152 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','l','b','a') },
153 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','r','a','n') },
154 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','h','o','j') },
155 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','d') },
156 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','a') },
157 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','h','j') },
158 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','i') },
159 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','n','d') },
160 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('m','o','d','i') },
161 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','r','o','o') },
162 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('n','b','a','t') },
163 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','a','r','b') },
164 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','e','r','m') },
165 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','m','n','g') },
166 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('p','a','l','m') },
167 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','a','u','c') },
168 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','l','p') },
169 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, _OT('s','i','d','d') },
170 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('t','i','r','h') },
171 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('w','a','r','a') },
172 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','d','l','m') },
173 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','h','o','m') },
174 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','l','u','w') },
175 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','h','k','s') },
176 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','a','t','r') },
177 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','r','c') },
178 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','u','l','t') },
179 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','e','w','a') },
180 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','u','n','g') },
181 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','g','e') },
182 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','g','n','w') },
183 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','n','g') },
185 #undef _OT
187 const char *debugstr_sa_script(UINT16 script)
189 return script < Script_LastId ? debugstr_an((char*)&dwritescripts_properties[script].props.isoScriptCode, 4): "not defined";
192 /* system font falback configuration */
193 static const WCHAR meiryoW[] = {'M','e','i','r','y','o',0};
195 struct fallback_mapping {
196 DWRITE_UNICODE_RANGE range;
197 const WCHAR *family;
200 static const struct fallback_mapping fontfallback_neutral_data[] = {
201 { { 0x3000, 0x30ff }, meiryoW }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
202 { { 0x31f0, 0x31ff }, meiryoW }, /* Katakana Phonetic Extensions */
203 { { 0x4e00, 0x9fff }, meiryoW }, /* CJK Unified Ideographs */
206 struct dwrite_fontfallback {
207 IDWriteFontFallback IDWriteFontFallback_iface;
208 IDWriteFactory4 *factory;
209 IDWriteFontCollection1 *systemcollection;
210 const struct fallback_mapping *mappings;
211 UINT32 count;
214 struct dwrite_numbersubstitution {
215 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
216 LONG ref;
218 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
219 WCHAR *locale;
220 BOOL ignore_user_override;
223 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
225 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
228 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback(IDWriteFontFallback *iface)
230 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback_iface);
233 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
235 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
236 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
237 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
238 return ch;
240 return 0;
243 static inline UINT16 get_char_script(WCHAR c)
245 UINT16 script = get_table_entry(wine_scripts_table, c);
246 return script == Script_Inherited ? Script_Unknown : script;
249 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
251 DWRITE_SCRIPT_ANALYSIS sa;
253 sa.script = get_char_script(c);
254 sa.shapes = iscntrlW(c) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
255 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
256 return sa;
259 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
261 DWRITE_SCRIPT_ANALYSIS sa;
262 UINT32 pos, i, seq_length;
264 if (!length)
265 return S_OK;
267 sa = get_char_sa(*text);
269 pos = position;
270 seq_length = 1;
272 for (i = 1; i < length; i++)
274 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
276 /* Unknown type is ignored when preceded or followed by another script */
277 switch (sa.script) {
278 case Script_Unknown:
279 sa.script = cur_sa.script;
280 break;
281 case Script_Common:
282 if (cur_sa.script == Script_Unknown)
283 cur_sa.script = sa.script;
284 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
285 sa.script = cur_sa.script;
286 break;
287 default:
288 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
289 cur_sa.script = sa.script;
292 /* this is a length of a sequence to be reported next */
293 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
294 seq_length++;
295 else {
296 HRESULT hr;
298 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
299 if (FAILED(hr)) return hr;
300 pos = position + i;
301 seq_length = 1;
302 sa = cur_sa;
306 /* one char length case or normal completion call */
307 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
310 struct linebreaking_state {
311 DWRITE_LINE_BREAKPOINT *breakpoints;
312 UINT32 count;
315 enum BreakConditionLocation {
316 BreakConditionBefore,
317 BreakConditionAfter
320 enum linebreaking_classes {
321 b_BK = 1,
322 b_CR,
323 b_LF,
324 b_CM,
325 b_SG,
326 b_GL,
327 b_CB,
328 b_SP,
329 b_ZW,
330 b_NL,
331 b_WJ,
332 b_JL,
333 b_JV,
334 b_JT,
335 b_H2,
336 b_H3,
337 b_XX,
338 b_OP,
339 b_CL,
340 b_CP,
341 b_QU,
342 b_NS,
343 b_EX,
344 b_SY,
345 b_IS,
346 b_PR,
347 b_PO,
348 b_NU,
349 b_AL,
350 b_ID,
351 b_IN,
352 b_HY,
353 b_BB,
354 b_BA,
355 b_SA,
356 b_AI,
357 b_B2,
358 b_HL,
359 b_CJ,
360 b_RI,
361 b_EB,
362 b_EM,
363 b_ZWJ,
366 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
368 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
369 return TRUE;
371 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
372 return TRUE;
374 return FALSE;
377 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
378 set to "can break" and could only be changed once. */
379 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
380 struct linebreaking_state *state)
382 if (location == BreakConditionBefore) {
383 if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
384 return;
385 state->breakpoints[pos].breakConditionBefore = condition;
386 if (pos > 0)
387 state->breakpoints[pos-1].breakConditionAfter = condition;
389 else {
390 if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
391 return;
392 state->breakpoints[pos].breakConditionAfter = condition;
393 if (pos + 1 < state->count)
394 state->breakpoints[pos+1].breakConditionBefore = condition;
398 BOOL lb_is_newline_char(WCHAR ch)
400 short c = get_table_entry(wine_linebreak_table, ch);
401 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
404 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
406 struct linebreaking_state state;
407 short *break_class;
408 int i, j;
410 break_class = heap_alloc(count*sizeof(short));
411 if (!break_class)
412 return E_OUTOFMEMORY;
414 state.breakpoints = breakpoints;
415 state.count = count;
417 for (i = 0; i < count; i++)
419 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
421 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
422 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
423 breakpoints[i].isWhitespace = !!isspaceW(text[i]);
424 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
425 breakpoints[i].padding = 0;
427 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
428 switch (break_class[i])
430 case b_AI:
431 case b_SA:
432 case b_SG:
433 case b_XX:
434 break_class[i] = b_AL;
435 break;
436 case b_CJ:
437 break_class[i] = b_NS;
438 break;
442 /* LB2 - never break at the start */
443 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
444 /* LB3 - always break at the end. */
445 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
447 /* LB4 - LB6 - mandatory breaks. */
448 for (i = 0; i < count; i++)
450 switch (break_class[i])
452 /* LB4 - LB6 */
453 case b_CR:
454 /* LB5 - don't break CR x LF */
455 if (i < count-1 && break_class[i+1] == b_LF)
457 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
458 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
459 break;
461 case b_LF:
462 case b_NL:
463 case b_BK:
464 /* LB4 - LB5 - always break after hard breaks */
465 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
466 /* LB6 - do not break before hard breaks */
467 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
468 break;
472 /* LB7 - LB8 - explicit breaks and non-breaks */
473 for (i = 0; i < count; i++)
475 switch (break_class[i])
477 /* LB7 - do not break before spaces */
478 case b_SP:
479 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
480 break;
481 case b_ZW:
482 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
484 /* LB8 - break before character after zero-width space, skip spaces in-between */
485 j = i;
486 while (j < count-1 && break_class[j+1] == b_SP)
487 j++;
488 if (j < count-1 && break_class[j+1] != b_ZW)
489 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
490 break;
491 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
492 case b_ZWJ:
493 if (i < count-1 && (break_class[i+1] == b_ID || break_class[i+1] == b_EB || break_class[i+1] == b_EM))
494 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
495 break;
499 /* LB9 - LB10 - combining marks */
500 for (i = 0; i < count; i++)
502 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
504 if (i > 0)
506 switch (break_class[i-1])
508 case b_SP:
509 case b_BK:
510 case b_CR:
511 case b_LF:
512 case b_NL:
513 case b_ZW:
514 break_class[i] = b_AL;
515 break;
516 default:
518 break_class[i] = break_class[i-1];
519 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
523 else break_class[i] = b_AL;
527 for (i = 0; i < count; i++)
529 switch (break_class[i])
531 /* LB11 - don't break before and after word joiner */
532 case b_WJ:
533 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
534 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
535 break;
536 /* LB12 - don't break after glue */
537 case b_GL:
538 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
539 /* LB12a */
540 if (i > 0)
542 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
543 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
545 break;
546 /* LB13 */
547 case b_CL:
548 case b_CP:
549 case b_EX:
550 case b_IS:
551 case b_SY:
552 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
553 break;
554 /* LB14 - do not break after OP, even after spaces */
555 case b_OP:
556 j = i;
557 while (j < count-1 && break_class[j+1] == b_SP)
558 j++;
559 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
560 break;
561 /* LB15 - do not break within QU-OP, even with intervening spaces */
562 case b_QU:
563 j = i;
564 while (j < count-1 && break_class[j+1] == b_SP)
565 j++;
566 if (j < count - 1 && break_class[j+1] == b_OP)
567 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
568 break;
569 /* LB16 */
570 case b_NS:
571 j = i-1;
572 while(j > 0 && break_class[j] == b_SP)
573 j--;
574 if (break_class[j] == b_CL || break_class[j] == b_CP)
575 for (j++; j <= i; j++)
576 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
577 break;
578 /* LB17 - do not break within B2, even with intervening spaces */
579 case b_B2:
580 j = i;
581 while (j < count && break_class[j+1] == b_SP)
582 j++;
583 if (j < count - 1 && break_class[j+1] == b_B2)
584 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
585 break;
589 for (i = 0; i < count; i++)
591 switch(break_class[i])
593 /* LB18 - break is allowed after space */
594 case b_SP:
595 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
596 break;
597 /* LB19 - don't break before or after quotation mark */
598 case b_QU:
599 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
600 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
601 break;
602 /* LB20 */
603 case b_CB:
604 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
605 if (i < count - 1 && break_class[i+1] != b_QU)
606 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
607 break;
608 /* LB21 */
609 case b_BA:
610 case b_HY:
611 case b_NS:
612 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
613 break;
614 case b_BB:
615 if (i < count - 1 && break_class[i+1] != b_CB)
616 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
617 break;
618 /* LB21a, LB21b */
619 case b_HL:
620 /* LB21a */
621 if (i < count-1)
622 switch (break_class[i+1])
624 case b_HY:
625 case b_BA:
626 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
628 /* LB21b */
629 if (i > 0 && break_class[i-1] == b_SY)
630 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
631 break;
632 /* LB22 */
633 case b_IN:
634 if (i > 0)
636 switch (break_class[i-1])
638 case b_AL:
639 case b_HL:
640 case b_EX:
641 case b_ID:
642 case b_EB:
643 case b_EM:
644 case b_IN:
645 case b_NU:
646 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
649 break;
652 if (i < count-1)
654 /* LB23 - do not break between digits and letters */
655 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
656 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
657 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
658 (break_class[i] == b_NU && break_class[i+1] == b_HL))
659 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
661 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
662 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
663 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
664 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
665 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
666 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
667 (break_class[i] == b_EB && break_class[i+1] == b_PO))
668 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
670 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
671 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
672 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
673 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
674 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
675 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
676 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
677 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
678 (break_class[i] == b_HL && break_class[i+1] == b_PO))
679 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
681 /* LB25 */
682 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
683 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
684 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
685 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
686 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
687 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
688 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
689 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
690 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
691 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
692 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
693 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
694 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
695 (break_class[i] == b_SY && break_class[i+1] == b_NU))
696 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
698 /* LB26 */
699 if (break_class[i] == b_JL)
701 switch (break_class[i+1])
703 case b_JL:
704 case b_JV:
705 case b_H2:
706 case b_H3:
707 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
710 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
711 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
712 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
713 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
714 break_class[i+1] == b_JT)
715 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
717 /* LB27 */
718 switch (break_class[i])
720 case b_JL:
721 case b_JV:
722 case b_JT:
723 case b_H2:
724 case b_H3:
725 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
726 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
728 if (break_class[i] == b_PR)
730 switch (break_class[i+1])
732 case b_JL:
733 case b_JV:
734 case b_JT:
735 case b_H2:
736 case b_H3:
737 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
741 /* LB28 */
742 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
743 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
744 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
745 (break_class[i] == b_HL && break_class[i+1] == b_HL))
746 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
748 /* LB29 */
749 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
750 (break_class[i] == b_IS && break_class[i+1] == b_HL))
751 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
753 /* LB30 */
754 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
755 break_class[i+1] == b_OP)
756 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
757 if (break_class[i] == b_CP &&
758 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
759 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
761 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
762 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
763 unsigned int c = 0;
765 j = i + 1;
766 while (j > 0 && break_class[--j] == b_RI)
767 c++;
769 if ((c & 1) == 0)
770 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
773 /* LB30b - do not break between an emoji base and an emoji modifier */
774 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
775 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
779 /* LB31 - allow breaks everywhere else. */
780 for (i = 0; i < count; i++)
782 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
783 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
786 heap_free(break_class);
787 return S_OK;
790 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
792 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
794 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
795 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
796 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
797 IsEqualIID(riid, &IID_IUnknown))
799 *obj = iface;
800 return S_OK;
803 *obj = NULL;
804 return E_NOINTERFACE;
807 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
809 return 2;
812 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
814 return 1;
817 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
818 data after a first request. */
819 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
821 HRESULT hr;
822 UINT32 len;
824 *buff = NULL;
825 *text = NULL;
826 len = 0;
827 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
828 if (FAILED(hr)) return hr;
830 if (len < length) {
831 UINT32 read;
833 *buff = heap_alloc(length*sizeof(WCHAR));
834 if (!*buff)
835 return E_OUTOFMEMORY;
836 memcpy(*buff, *text, len*sizeof(WCHAR));
837 read = len;
839 while (read < length && *text) {
840 *text = NULL;
841 len = 0;
842 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
843 if (FAILED(hr)) {
844 heap_free(*buff);
845 return hr;
847 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
848 read += len;
851 *text = *buff;
854 return hr;
857 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
858 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
860 WCHAR *buff = NULL;
861 const WCHAR *text;
862 HRESULT hr;
864 TRACE("(%p %u %u %p)\n", source, position, length, sink);
866 if (length == 0)
867 return S_OK;
869 hr = get_text_source_ptr(source, position, length, &text, &buff);
870 if (FAILED(hr))
871 return hr;
873 hr = analyze_script(text, position, length, sink);
874 heap_free(buff);
876 return hr;
879 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
880 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
882 UINT8 *levels = NULL, *explicit = NULL;
883 UINT8 baselevel, level, explicit_level;
884 UINT32 pos, i, seq_length;
885 WCHAR *buff = NULL;
886 const WCHAR *text;
887 HRESULT hr;
889 TRACE("(%p %u %u %p)\n", source, position, length, sink);
891 if (!length)
892 return S_OK;
894 hr = get_text_source_ptr(source, position, length, &text, &buff);
895 if (FAILED(hr))
896 return hr;
898 levels = heap_alloc(length*sizeof(*levels));
899 explicit = heap_alloc(length*sizeof(*explicit));
901 if (!levels || !explicit) {
902 hr = E_OUTOFMEMORY;
903 goto done;
906 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
907 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
908 if (FAILED(hr))
909 goto done;
911 level = levels[0];
912 explicit_level = explicit[0];
913 pos = position;
914 seq_length = 1;
916 for (i = 1; i < length; i++) {
917 if (levels[i] == level && explicit[i] == explicit_level)
918 seq_length++;
919 else {
920 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
921 if (FAILED(hr))
922 goto done;
924 pos += seq_length;
925 seq_length = 1;
926 level = levels[i];
927 explicit_level = explicit[i];
930 /* one char length case or normal completion call */
931 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
933 done:
934 heap_free(explicit);
935 heap_free(levels);
936 heap_free(buff);
938 return hr;
941 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
942 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
944 static int once;
946 if (!once++)
947 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
948 return S_OK;
951 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
952 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
954 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
955 WCHAR *buff = NULL;
956 const WCHAR *text;
957 HRESULT hr;
958 UINT32 len;
960 TRACE("(%p %u %u %p)\n", source, position, length, sink);
962 if (length == 0)
963 return S_OK;
965 /* get some, check for length */
966 text = NULL;
967 len = 0;
968 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
969 if (FAILED(hr)) return hr;
971 if (len < length) {
972 UINT32 read;
974 buff = heap_alloc(length*sizeof(WCHAR));
975 if (!buff)
976 return E_OUTOFMEMORY;
977 memcpy(buff, text, len*sizeof(WCHAR));
978 read = len;
980 while (read < length && text) {
981 text = NULL;
982 len = 0;
983 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
984 if (FAILED(hr))
985 goto done;
986 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
987 read += len;
990 text = buff;
993 breakpoints = heap_alloc(length*sizeof(*breakpoints));
994 if (!breakpoints) {
995 hr = E_OUTOFMEMORY;
996 goto done;
999 hr = analyze_linebreaks(text, length, breakpoints);
1000 if (FAILED(hr))
1001 goto done;
1003 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1005 done:
1006 heap_free(breakpoints);
1007 heap_free(buff);
1009 return hr;
1012 static UINT32 get_opentype_language(const WCHAR *locale)
1014 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
1016 if (locale) {
1017 WCHAR tag[5];
1018 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
1019 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1022 return language;
1025 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1026 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1027 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1028 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1029 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
1030 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
1031 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1033 const struct dwritescript_properties *scriptprops;
1034 struct scriptshaping_context context;
1035 struct scriptshaping_cache *cache = NULL;
1036 BOOL update_cluster, need_vertical;
1037 IDWriteFontFace1 *fontface1;
1038 WCHAR *string;
1039 UINT32 i, g;
1040 HRESULT hr = S_OK;
1041 UINT16 script;
1043 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
1044 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1045 features, feature_range_len, feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices,
1046 glyph_props, actual_glyph_count);
1048 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1050 if (max_glyph_count < length)
1051 return E_NOT_SUFFICIENT_BUFFER;
1053 if (substitution)
1054 FIXME("number substitution is not supported.\n");
1056 for (i = 0; i < length; i++) {
1057 /* FIXME: set to better values */
1058 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
1059 glyph_props[i].isClusterStart = 1;
1060 glyph_props[i].isDiacritic = 0;
1061 glyph_props[i].isZeroWidthSpace = 0;
1062 glyph_props[i].reserved = 0;
1064 /* FIXME: have the shaping engine set this */
1065 text_props[i].isShapedAlone = 0;
1066 text_props[i].reserved = 0;
1068 clustermap[i] = i;
1071 for (; i < max_glyph_count; i++) {
1072 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
1073 glyph_props[i].isClusterStart = 0;
1074 glyph_props[i].isDiacritic = 0;
1075 glyph_props[i].isZeroWidthSpace = 0;
1076 glyph_props[i].reserved = 0;
1079 string = heap_alloc(sizeof(WCHAR)*length);
1080 if (!string)
1081 return E_OUTOFMEMORY;
1083 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1084 if (FAILED(hr))
1085 WARN("failed to get IDWriteFontFace1\n");
1087 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
1089 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
1090 UINT32 codepoint;
1092 if (!update_cluster) {
1093 codepoint = decode_surrogate_pair(text, i, length);
1094 if (!codepoint) {
1095 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
1096 string[i] = codepoint;
1098 else {
1099 string[i] = text[i];
1100 string[i+1] = text[i+1];
1101 update_cluster = TRUE;
1104 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
1105 if (FAILED(hr))
1106 goto done;
1108 if (need_vertical) {
1109 UINT16 vertical;
1111 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
1112 if (hr == S_OK)
1113 glyph_indices[g] = vertical;
1116 g++;
1118 else {
1119 INT32 k;
1121 update_cluster = FALSE;
1122 /* mark surrogate halves with same cluster */
1123 clustermap[i] = clustermap[i-1];
1124 /* update following clusters */
1125 for (k = i + 1; k >= 0 && k < length; k++)
1126 clustermap[k]--;
1129 *actual_glyph_count = g;
1131 hr = create_scriptshaping_cache(fontface, &cache);
1132 if (FAILED(hr))
1133 goto done;
1135 context.cache = cache;
1136 context.text = text;
1137 context.length = length;
1138 context.is_rtl = is_rtl;
1139 context.max_glyph_count = max_glyph_count;
1140 context.language_tag = get_opentype_language(locale);
1142 scriptprops = &dwritescripts_properties[script];
1143 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
1144 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
1145 if (FAILED(hr))
1146 goto done;
1149 /* FIXME: apply default features */
1151 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1152 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1153 else
1154 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1156 done:
1157 if (fontface1)
1158 IDWriteFontFace1_Release(fontface1);
1159 release_scriptshaping_cache(cache);
1160 heap_free(string);
1162 return hr;
1165 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1166 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1167 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1168 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1169 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1170 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1172 DWRITE_FONT_METRICS metrics;
1173 IDWriteFontFace1 *fontface1;
1174 HRESULT hr;
1175 UINT32 i;
1177 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),
1178 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1179 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_len,
1180 feature_ranges, advances, offsets);
1182 if (glyph_count == 0)
1183 return S_OK;
1185 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1186 if (FAILED(hr)) {
1187 WARN("failed to get IDWriteFontFace1.\n");
1188 return hr;
1191 IDWriteFontFace_GetMetrics(fontface, &metrics);
1192 for (i = 0; i < glyph_count; i++) {
1193 if (glyph_props[i].isZeroWidthSpace)
1194 advances[i] = 0.0f;
1195 else {
1196 INT32 a;
1198 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1199 if (FAILED(hr))
1200 a = 0;
1201 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1203 offsets[i].advanceOffset = 0.0f;
1204 offsets[i].ascenderOffset = 0.0f;
1207 /* FIXME: actually apply features */
1209 IDWriteFontFace1_Release(fontface1);
1210 return S_OK;
1213 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1214 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1215 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1216 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1217 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1218 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1219 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1221 DWRITE_FONT_METRICS metrics;
1222 IDWriteFontFace1 *fontface1;
1223 HRESULT hr;
1224 UINT32 i;
1226 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),
1227 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1228 transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale),
1229 features, feature_range_lengths, feature_ranges, advances, offsets);
1231 if (glyph_count == 0)
1232 return S_OK;
1234 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1235 if (FAILED(hr)) {
1236 WARN("failed to get IDWriteFontFace1.\n");
1237 return hr;
1240 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1241 if (FAILED(hr)) {
1242 IDWriteFontFace1_Release(fontface1);
1243 WARN("failed to get compat metrics, 0x%08x\n", hr);
1244 return hr;
1246 for (i = 0; i < glyph_count; i++) {
1247 INT32 a;
1249 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1250 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1251 if (FAILED(hr))
1252 advances[i] = 0.0f;
1253 else
1254 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1255 offsets[i].advanceOffset = 0.0f;
1256 offsets[i].ascenderOffset = 0.0f;
1259 /* FIXME: actually apply features */
1261 IDWriteFontFace1_Release(fontface1);
1262 return S_OK;
1265 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1267 FLOAT advance = 0.0f;
1268 for (; start < end; start++)
1269 advance += advances[start];
1270 return advance;
1273 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1274 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1275 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1277 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1278 FLOAT advance = advances[g];
1279 FLOAT origin = 0.0f;
1281 if (props[g].isZeroWidthSpace) {
1282 modified_advances[g] = advances[g];
1283 modified_offsets[g] = offsets[g];
1284 return;
1287 /* first apply negative spacing and check if we hit minimum width */
1288 if (leading_spacing < 0.0f) {
1289 advance += leading_spacing;
1290 origin -= leading_spacing;
1292 if (trailing_spacing < 0.0f)
1293 advance += trailing_spacing;
1295 if (advance < min_advance_width) {
1296 FLOAT half = (min_advance_width - advance) / 2.0f;
1298 if (!reduced)
1299 origin -= half;
1300 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1301 origin -= half;
1302 else if (leading_spacing < 0.0f)
1303 origin -= min_advance_width - advance;
1305 advance = min_advance_width;
1308 /* now apply positive spacing adjustments */
1309 if (leading_spacing > 0.0f) {
1310 advance += leading_spacing;
1311 origin -= leading_spacing;
1313 if (trailing_spacing > 0.0f)
1314 advance += trailing_spacing;
1316 modified_advances[g] = advance;
1317 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1318 /* ascender is never touched, it's orthogonal to reading direction and is not
1319 affected by advance adjustments */
1320 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1323 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1324 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1325 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1327 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1328 FLOAT advance = get_cluster_advance(advances, start, end);
1329 FLOAT origin = 0.0f;
1330 UINT16 g;
1332 modified_advances[start] = advances[start];
1333 modified_advances[end-1] = advances[end-1];
1335 /* first apply negative spacing and check if we hit minimum width */
1336 if (leading_spacing < 0.0f) {
1337 advance += leading_spacing;
1338 modified_advances[start] += leading_spacing;
1339 origin -= leading_spacing;
1341 if (trailing_spacing < 0.0f) {
1342 advance += trailing_spacing;
1343 modified_advances[end-1] += trailing_spacing;
1346 advance = min_advance_width - advance;
1347 if (advance > 0.0f) {
1348 /* additional spacing is only applied to leading and trailing glyph */
1349 FLOAT half = advance / 2.0f;
1351 if (!reduced) {
1352 origin -= half;
1353 modified_advances[start] += half;
1354 modified_advances[end-1] += half;
1356 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f) {
1357 origin -= half;
1358 modified_advances[start] += half;
1359 modified_advances[end-1] += half;
1361 else if (leading_spacing < 0.0f) {
1362 origin -= advance;
1363 modified_advances[start] += advance;
1365 else
1366 modified_advances[end-1] += advance;
1369 /* now apply positive spacing adjustments */
1370 if (leading_spacing > 0.0f) {
1371 modified_advances[start] += leading_spacing;
1372 origin -= leading_spacing;
1374 if (trailing_spacing > 0.0f)
1375 modified_advances[end-1] += trailing_spacing;
1377 for (g = start; g < end; g++) {
1378 if (g == start) {
1379 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1380 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1382 else if (g == end - 1)
1383 /* trailing glyph offset is not adjusted */
1384 modified_offsets[g] = offsets[g];
1385 else {
1386 /* for all glyphs within a cluster use original advances and offsets */
1387 modified_advances[g] = advances[g];
1388 modified_offsets[g] = offsets[g];
1393 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1395 UINT16 g = clustermap[start];
1396 UINT32 length = 1;
1398 while (start < text_len && clustermap[++start] == g)
1399 length++;
1400 return length;
1403 /* Applies spacing adjustments to clusters.
1405 Adjustments are applied in the following order:
1407 1. Negative adjustments
1409 Leading and trailing spacing could be negative, at this step
1410 only negative ones are actually applied. Leading spacing is only
1411 applied to leading glyph, trailing - to trailing glyph.
1413 2. Minimum advance width
1415 Advances could only be reduced at this point or unchanged. In any
1416 case it's checked if cluster advance width is less than minimum width.
1417 If it's the case advance width is incremented up to minimum value.
1419 Important part is the direction in which this increment is applied;
1420 it depends on direction from which total cluster advance was trimmed
1421 at step 1. So it could be incremented from leading, trailing, or both
1422 sides. When applied to both sides, each side gets half of difference
1423 that brings advance to minimum width.
1425 3. Positive adjustments
1427 After minimum width rule was applied, positive spacing is applied in the same
1428 way as negative one on step 1.
1430 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1431 keeps its position in coordinate system where initial advance width is counted
1432 from 0.
1434 Glyph properties
1436 It's known that isZeroWidthSpace property keeps initial advance from changing.
1438 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1439 with more than one glyph.
1442 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1443 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1444 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1445 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1447 UINT16 start;
1449 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1450 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1452 if (min_advance_width < 0.0f) {
1453 if (modified_advances != advances)
1454 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1455 return E_INVALIDARG;
1458 /* minimum advance is not applied if no adjustments were made */
1459 if (leading_spacing == 0.0f && trailing_spacing == 0.0f) {
1460 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1461 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1462 return S_OK;
1465 for (start = 0; start < len;) {
1466 UINT32 length = get_cluster_length(clustermap, start, len);
1468 if (length == 1) {
1469 UINT32 g = clustermap[start];
1471 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1472 g, advances, offsets, props, modified_advances, modified_offsets);
1474 else {
1475 UINT32 g_start, g_end;
1477 g_start = clustermap[start];
1478 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1480 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1481 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1484 start += length;
1487 return S_OK;
1490 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1491 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1492 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1494 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1495 baseline_coord, exists);
1496 return E_NOTIMPL;
1499 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1500 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1502 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1503 return E_NOTIMPL;
1506 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1507 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1509 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1510 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1513 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1514 DWRITE_SCRIPT_PROPERTIES *props)
1516 TRACE("(%u %p)\n", sa.script, props);
1518 if (sa.script > Script_LastId)
1519 return E_INVALIDARG;
1521 *props = dwritescripts_properties[sa.script].props;
1522 return S_OK;
1525 static inline BOOL is_char_from_simple_script(WCHAR c)
1527 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1528 return FALSE;
1529 else {
1530 UINT16 script = get_char_script(c);
1531 return !dwritescripts_properties[script].is_complex;
1535 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1536 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1538 HRESULT hr = S_OK;
1539 int i;
1541 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1543 *is_simple = FALSE;
1544 *len_read = 0;
1546 if (!face)
1547 return E_INVALIDARG;
1549 if (len == 0) {
1550 *is_simple = TRUE;
1551 return S_OK;
1554 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1555 for (i = 1; i < len && text[i]; i++) {
1556 if (is_char_from_simple_script(text[i])) {
1557 if (!*is_simple)
1558 break;
1560 else
1561 *is_simple = FALSE;
1564 *len_read = i;
1566 /* fetch indices */
1567 if (*is_simple && indices) {
1568 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1569 if (!codepoints)
1570 return E_OUTOFMEMORY;
1572 for (i = 0; i < *len_read; i++)
1573 codepoints[i] = text[i];
1575 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1576 heap_free(codepoints);
1579 return hr;
1582 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1583 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1584 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1586 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1587 debugstr_wn(text, length), clustermap, prop, jo);
1588 return E_NOTIMPL;
1591 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1592 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1593 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1595 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1596 justifiedoffsets);
1597 return E_NOTIMPL;
1600 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1601 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1602 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1603 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1604 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1605 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1607 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,
1608 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1609 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1610 return E_NOTIMPL;
1613 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1614 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1616 static const DWRITE_MATRIX transforms[] = {
1617 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1618 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1619 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1620 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1623 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1625 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1626 memset(m, 0, sizeof(*m));
1627 return E_INVALIDARG;
1630 /* for sideways case simply rotate 90 degrees more */
1631 if (is_sideways) {
1632 switch (angle) {
1633 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1634 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1635 break;
1636 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1637 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1638 break;
1639 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1640 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1641 break;
1642 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1643 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1644 break;
1645 default:
1650 *m = transforms[angle];
1652 /* shift components represent transform necessary to get from original point to
1653 rotated one in new coordinate system */
1654 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1655 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1656 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1659 return S_OK;
1662 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1663 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1664 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1666 const struct dwritescript_properties *props;
1667 HRESULT hr = S_OK;
1668 UINT32 language;
1670 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1671 tags);
1673 if (sa.script > Script_LastId)
1674 return E_INVALIDARG;
1676 language = get_opentype_language(locale);
1677 props = &dwritescripts_properties[sa.script];
1678 *actual_tagcount = 0;
1680 if (props->scriptalttag)
1681 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1683 if (*actual_tagcount == 0)
1684 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1686 return hr;
1689 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1690 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1691 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1693 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1694 feature_applies);
1695 return E_NOTIMPL;
1698 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1699 dwritetextanalyzer_QueryInterface,
1700 dwritetextanalyzer_AddRef,
1701 dwritetextanalyzer_Release,
1702 dwritetextanalyzer_AnalyzeScript,
1703 dwritetextanalyzer_AnalyzeBidi,
1704 dwritetextanalyzer_AnalyzeNumberSubstitution,
1705 dwritetextanalyzer_AnalyzeLineBreakpoints,
1706 dwritetextanalyzer_GetGlyphs,
1707 dwritetextanalyzer_GetGlyphPlacements,
1708 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1709 dwritetextanalyzer1_ApplyCharacterSpacing,
1710 dwritetextanalyzer1_GetBaseline,
1711 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1712 dwritetextanalyzer1_GetGlyphOrientationTransform,
1713 dwritetextanalyzer1_GetScriptProperties,
1714 dwritetextanalyzer1_GetTextComplexity,
1715 dwritetextanalyzer1_GetJustificationOpportunities,
1716 dwritetextanalyzer1_JustifyGlyphAdvances,
1717 dwritetextanalyzer1_GetJustifiedGlyphs,
1718 dwritetextanalyzer2_GetGlyphOrientationTransform,
1719 dwritetextanalyzer2_GetTypographicFeatures,
1720 dwritetextanalyzer2_CheckTypographicFeature
1723 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1725 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1727 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1728 return S_OK;
1731 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1733 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1735 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1737 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1738 IsEqualIID(riid, &IID_IUnknown))
1740 *obj = iface;
1741 IDWriteNumberSubstitution_AddRef(iface);
1742 return S_OK;
1745 *obj = NULL;
1747 return E_NOINTERFACE;
1750 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1752 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1753 ULONG ref = InterlockedIncrement(&This->ref);
1754 TRACE("(%p)->(%d)\n", This, ref);
1755 return ref;
1758 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1760 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1761 ULONG ref = InterlockedDecrement(&This->ref);
1763 TRACE("(%p)->(%d)\n", This, ref);
1765 if (!ref) {
1766 heap_free(This->locale);
1767 heap_free(This);
1770 return ref;
1773 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1774 dwritenumbersubstitution_QueryInterface,
1775 dwritenumbersubstitution_AddRef,
1776 dwritenumbersubstitution_Release
1779 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1780 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1782 struct dwrite_numbersubstitution *substitution;
1784 *ret = NULL;
1786 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1787 return E_INVALIDARG;
1789 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1790 return E_INVALIDARG;
1792 substitution = heap_alloc(sizeof(*substitution));
1793 if (!substitution)
1794 return E_OUTOFMEMORY;
1796 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1797 substitution->ref = 1;
1798 substitution->ignore_user_override = ignore_user_override;
1799 substitution->method = method;
1800 substitution->locale = heap_strdupW(locale);
1801 if (locale && !substitution->locale) {
1802 heap_free(substitution);
1803 return E_OUTOFMEMORY;
1806 *ret = &substitution->IDWriteNumberSubstitution_iface;
1807 return S_OK;
1810 /* IDWriteFontFallback */
1811 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback *iface, REFIID riid, void **obj)
1813 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1815 TRACE("(%p)->(%s %p)\n", fallback, debugstr_guid(riid), obj);
1817 if (IsEqualIID(riid, &IID_IDWriteFontFallback) || IsEqualIID(riid, &IID_IUnknown)) {
1818 *obj = iface;
1819 IDWriteFontFallback_AddRef(iface);
1820 return S_OK;
1823 *obj = NULL;
1824 return E_NOINTERFACE;
1827 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback *iface)
1829 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1830 TRACE("(%p)\n", fallback);
1831 return IDWriteFactory4_AddRef(fallback->factory);
1834 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback *iface)
1836 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1837 TRACE("(%p)\n", fallback);
1838 return IDWriteFactory4_Release(fallback->factory);
1841 static int compare_fallback_mapping(const void *a, const void *b)
1843 UINT32 ch = *(UINT32*)a;
1844 struct fallback_mapping *mapping = (struct fallback_mapping*)b;
1846 if (ch > mapping->range.last)
1847 return 1;
1848 else if (ch < mapping->range.first)
1849 return -1;
1850 else
1851 return 0;
1854 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
1856 return bsearch(&ch, fallback->mappings, fallback->count, sizeof(*fallback->mappings),
1857 compare_fallback_mapping);
1860 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
1861 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
1863 IDWriteFontFamily *family;
1864 BOOL exists = FALSE;
1865 HRESULT hr;
1866 UINT32 i;
1868 *font = NULL;
1870 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
1871 if (FAILED(hr))
1872 return hr;
1874 if (!exists)
1875 return E_FAIL;
1877 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
1878 if (FAILED(hr))
1879 return hr;
1881 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
1882 IDWriteFontFamily_Release(family);
1883 return hr;
1886 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
1888 HRESULT hr = S_OK;
1889 UINT32 i;
1891 for (i = 0; i < length; i++) {
1892 UINT16 script = get_char_script(text[i]);
1893 BOOL exists;
1895 if (script == Script_Unknown || script == Script_Common) {
1896 ++*mapped_length;
1897 continue;
1900 /* stop on first unsupported character */
1901 exists = FALSE;
1902 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
1903 if (hr == S_OK && exists)
1904 ++*mapped_length;
1905 else
1906 break;
1909 return hr;
1912 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
1913 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length, IDWriteFont **mapped_font)
1915 const struct fallback_mapping *mapping;
1916 HRESULT hr;
1918 mapping = find_fallback_mapping(fallback, text[0]);
1919 if (!mapping) {
1920 WARN("no mapping for 0x%x\n", text[0]);
1921 return E_FAIL;
1924 /* now let's see what fallback can handle */
1925 hr = create_matching_font((IDWriteFontCollection*)fallback->systemcollection, mapping->family, weight, style, stretch, mapped_font);
1926 if (FAILED(hr)) {
1927 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping->family),
1928 mapping->range.first, mapping->range.last, hr);
1929 return hr;
1932 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
1933 if (FAILED(hr))
1934 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping->family), hr);
1936 if (!*mapped_length) {
1937 IDWriteFont_Release(*mapped_font);
1938 *mapped_font = NULL;
1941 return *mapped_length ? S_OK : E_FAIL;
1944 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
1945 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
1946 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
1947 IDWriteFont **ret_font, FLOAT *scale)
1949 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1950 WCHAR *buff = NULL;
1951 const WCHAR *text;
1952 HRESULT hr;
1954 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
1955 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
1957 *mapped_length = 0;
1958 *scale = 1.0f;
1959 *ret_font = NULL;
1961 if (!source)
1962 return E_INVALIDARG;
1964 if (length == 0)
1965 return S_OK;
1967 if (!basecollection)
1968 basecollection = (IDWriteFontCollection*)fallback->systemcollection;
1970 hr = get_text_source_ptr(source, position, length, &text, &buff);
1971 if (FAILED(hr))
1972 goto done;
1974 if (basefamily && *basefamily) {
1975 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
1976 if (FAILED(hr))
1977 goto done;
1979 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
1980 if (FAILED(hr))
1981 goto done;
1984 if (!*mapped_length) {
1985 IDWriteFont *mapped_font;
1987 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
1988 if (FAILED(hr)) {
1989 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
1990 if (*ret_font) {
1991 *mapped_length = length;
1992 hr = S_OK;
1995 else {
1996 if (*ret_font)
1997 IDWriteFont_Release(*ret_font);
1998 *ret_font = mapped_font;
2002 done:
2003 heap_free(buff);
2004 return hr;
2007 static const IDWriteFontFallbackVtbl fontfallbackvtbl = {
2008 fontfallback_QueryInterface,
2009 fontfallback_AddRef,
2010 fontfallback_Release,
2011 fontfallback_MapCharacters
2014 HRESULT create_system_fontfallback(IDWriteFactory4 *factory, IDWriteFontFallback **ret)
2016 struct dwrite_fontfallback *fallback;
2018 *ret = NULL;
2020 fallback = heap_alloc(sizeof(*fallback));
2021 if (!fallback)
2022 return E_OUTOFMEMORY;
2024 fallback->IDWriteFontFallback_iface.lpVtbl = &fontfallbackvtbl;
2025 fallback->factory = factory;
2026 fallback->mappings = fontfallback_neutral_data;
2027 fallback->count = sizeof(fontfallback_neutral_data)/sizeof(fontfallback_neutral_data[0]);
2028 IDWriteFactory4_GetSystemFontCollection(fallback->factory, FALSE, &fallback->systemcollection, FALSE);
2030 *ret = &fallback->IDWriteFontFallback_iface;
2031 return S_OK;
2034 void release_system_fontfallback(IDWriteFontFallback *iface)
2036 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2037 IDWriteFontCollection1_Release(fallback->systemcollection);
2038 heap_free(fallback);