iphlpapi: Add GetPerTcpConnectionEStats stub.
[wine.git] / dlls / dwrite / analyzer.c
blob4e503eb8b5c40a9a5cd1b542e392e6619d70d824
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;
33 extern const unsigned short bidi_direction_table[] DECLSPEC_HIDDEN;
35 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
36 #define NATIVE_DIGITS_LEN 11
38 struct dwritescript_properties
40 DWRITE_SCRIPT_PROPERTIES props;
41 UINT32 scripttags[3]; /* Maximum 2 script tags, 0-terminated. */
42 BOOL is_complex;
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') }, 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','n','g','2'), _OT('b','e','n','g') }, 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') }, 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') }, 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') }, 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') }, TRUE },
73 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('d','e','v','2'), _OT('d','e','v','a') }, 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') }, 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','j','r','2'), _OT('g','u','j','r') }, TRUE },
81 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','u','r','2'), _OT('g','u','r','u') }, 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') }, 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') }, 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','2'), _OT('k','n','d','a') }, 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') }, TRUE },
97 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('l','a','o',' ') }, TRUE },
98 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','a','t','n') } },
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','m','2'), _OT('m','l','y','m') }, 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') }, TRUE },
109 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','y','m','r') }, TRUE },
110 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','u') }, TRUE },
111 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('n','k','o',' ') }, TRUE },
112 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, { _OT('o','g','a','m') }, 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') }, 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','2'), _OT('o','r','y','a') }, TRUE },
119 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','m','a') }, TRUE },
120 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','a','g') }, 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') }, 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') }, 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') }, 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') }, 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','m','l','2'), _OT('t','a','m','l') }, TRUE },
137 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','e','l','2'), _OT('t','e','l','u') }, TRUE },
138 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','h','a','a') }, TRUE },
139 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','h','a','i') }, TRUE },
140 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','i','b','t') }, TRUE },
141 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','f','n','g') }, 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',' ') }, TRUE },
144 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('y','i',' ',' ') }, 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') } },
187 { /* Gonm */ { 0x6d6e6f47, 313, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','n','m') } },
188 { /* Nshu */ { 0x7568734e, 499, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('n','s','h','u') } },
189 { /* Soyo */ { 0x6f796f53, 329, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','y','o') } },
190 { /* Zanb */ { 0x626e615a, 339, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('z','a','n','b') } },
191 { /* Dogr */ { 0x72676f44, 328, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('d','o','g','r') } },
192 { /* Gong */ { 0x676e6f47, 312, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','o','n','g') } },
193 { /* Rohg */ { 0x67686f52, 167, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('r','o','h','g') } },
194 { /* Maka */ { 0x616b614d, 366, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','k','a') } },
195 { /* Medf */ { 0x6664654d, 265, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','d','f') } },
196 { /* Sogo */ { 0x6f676f53, 142, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','g','o') } },
197 { /* Sogd */ { 0x64676f53, 141, 8, 0x0020, 1, 1, 0, 0, 0, 1, 1 }, { _OT('s','o','g','d') } },
198 { /* Elym */ { 0x6d796c45, 128, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('e','l','y','m') } },
199 { /* Hmnp */ { 0x706e6d48, 451, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('h','m','n','p') } },
200 { /* Nand */ { 0x646e614e, 311, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('n','a','n','d') } },
201 { /* Wcho */ { 0x6f686357, 283, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('w','c','h','o') } },
202 { /* Chrs */ { 0x73726843, 109, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('c','h','r','s') } },
203 { /* Diak */ { 0x6b616944, 342, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('d','i','a','k') } },
204 { /* Kits */ { 0x7374694b, 288, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('k','i','t','s') } },
205 { /* Yezi */ { 0x697a6559, 192, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('y','e','z','i') } },
207 #undef _OT
209 const char *debugstr_sa_script(UINT16 script)
211 return script < Script_LastId ? debugstr_tag(dwritescripts_properties[script].props.isoScriptCode) : "undefined";
214 static const struct fallback_description
216 const char *ranges;
217 const WCHAR *families;
218 const WCHAR *locale;
220 system_fallback_config[] =
222 /* Latin, Combining Diacritical Marks */
223 { "0000-007F, 0080-00FF, 0100-017F, 0180-024F, "
224 "0250-02AF, 02B0-02FF, 0300-036F", L"Tahoma" },
226 { "0530-058F, FB10-FB1C", L"Noto Sans Armenian" },
228 { "0590-05FF, FB1D-FB4F", L"Noto Sans Hebrew" },
230 { "0600-06FF, 0750-077F, "
231 "08A0-08FF, FB50-FDCF, "
232 "FDF0-FDFF, FE70-FEFE", L"Noto Sans Arabic" },
234 { "0700-074F", L"Noto Sans Syriac" },
235 { "0780-07BF", L"Noto Sans Thaana" },
236 { "07C0-07FF", L"Noto Sans NKo" },
237 { "0800-083F", L"Noto Sans Samaritan" },
238 { "0840-085F", L"Noto Sans Mandaic" },
240 { "0900-097F", L"Noto Sans Devanagari" },
241 { "0980-09FF", L"Noto Sans Bengali" },
242 { "0A00-0A7F", L"Noto Sans Gurmukhi" },
243 { "0A80-0AFF", L"Noto Sans Gujarati" },
244 { "0B00-0B7F", L"Noto Sans Oriya" },
245 { "0B80-0BFF", L"Noto Sans Tamil" },
246 { "0C00-0C7F", L"Noto Sans Telugu" },
247 { "0C80-0CFF", L"Noto Sans Kannada" },
248 { "0D00-0D7F", L"Noto Sans Malayalam" },
249 { "0D80-0DFF", L"Noto Sans Sinhala" },
251 { "0E00-0E7F", L"Noto Sans Thai" },
252 { "0E80-0EFF", L"Noto Sans Lao" },
254 { "0F00-0FFF", L"Noto Serif Tibetan" },
256 { "1000-109F, A9E0-A9FF, AA60-AA7F", L"Noto Sans Myanmar" },
258 { "10A0-10FF, 2D00-2D2F", L"Noto Sans Georgian" },
260 /* Hangul Jamo - 1100-11FF
261 Hangul Compatibility Jamo - 3130-318F
262 Enc. CJK (Paren Hangul) - 3200-321F
263 Enc. CJK (Circled Hangul) - 3260-327F
264 Hangul Jamo Extended-A - A960-A97F
265 Hangul Syllables - AC00-D7AF
266 Hangul Jamo Extended-B - D7B0-D7FF */
268 { "1100-11FF, 3130-318F, "
269 "3200-321F, 3260-327F, "
270 "A960-A97F, AC00-D7FF, "
271 "D7B0-D7FF", L"Noto Sans CJK KR" },
273 { "1680-169F", L"Noto Sans Ogham" },
275 { "16A0-16FF", L"Noto Sans Runic" },
277 { "1700-171F", L"Noto Sans Tagalog" },
278 { "1720-173F", L"Noto Sans Hanunoo" },
279 { "1740-175F", L"Noto Sans Buhid" },
280 { "1760-177F", L"Noto Sans Tagbanwa" },
282 { "1800-18AF, 202F, 11660-1167F", L"Noto Sans Mongolian" },
284 { "1900-194F", L"Noto Sans Limbu" },
285 { "1950-197F", L"Noto Sans Tai Le" },
286 { "1980-19DF", L"Noto Sans New Tai Lue" },
287 { "1A00-1A1F", L"Noto Sans Buginese" },
288 { "1A20-1AAF", L"Noto Sans Tai Tham" },
289 { "1B00-1B7F", L"Noto Sans Balinese" },
290 { "1B80-1BBF, 1CC0-1CCF", L"Noto Sans Sundanes" },
291 { "1BC0-1BFF", L"Noto Sans Batak" },
292 { "1C00-1C4F", L"Noto Sans Lepcha" },
293 { "1C50-1C7F", L"Noto Sans Ol Chiki" },
295 { "2C80-2CFF", L"Noto Sans Coptic" },
296 { "2D30-2D7F", L"Noto Sans Tifinagh" },
298 /* CJK Radicals Supplement - 2E80-2EFF */
300 { "2E80-2EFF", L"Noto Sans CJK SC", L"zh-Hans" },
301 { "2E80-2EFF", L"Noto Sans CJK TC", L"zh-Hant" },
302 { "2E80-2EFF", L"Noto Sans CJK KR", L"ko" },
304 /* CJK Symbols and Punctuation - 3000-303F
305 Hiragana - 3040-309F
306 Katakana - 30A0-30FF
307 Katakana Phonetic Ext. - 31F0-31FF */
309 { "3000-30FF, 31F0-31FF", L"Noto Sans CJK SC", L"zh-Hans" },
310 { "3000-30FF, 31F0-31FF", L"Noto Sans CJK TC", L"zh-Hant" },
311 { "3000-30FF, 31F0-31FF", L"Noto Sans CJK KR", L"ko" },
312 { "3000-30FF, 31F0-31FF", L"Noto Sans CJK JP" },
314 /* CJK Unified Ext A - 3400-4DBF
315 CJK Unified - 4E00-9FFF */
317 { "3400-4DBF, 4E00-9FFF", L"Noto Sans CJK SC", L"zh-Hans" },
318 { "3400-4DBF, 4E00-9FFF", L"Noto Sans CJK TC", L"zh-Hant" },
319 { "3400-4DBF, 4E00-9FFF", L"Noto Sans CJK KR", L"ko" },
320 { "3400-4DBF, 4E00-9FFF", L"Noto Sans CJK JP" },
322 { "A000-A4CF", L"Noto Sans Yi" },
323 { "A4D0-A4FF", L"Noto Sans Lisu" },
324 { "A500-A63F", L"Noto Sans Vai" },
325 { "A6A0-A6FF", L"Noto Sans Bamum" },
326 { "A800-A82F", L"Noto Sans Syloti Nagri" },
327 { "A840-A87F", L"Noto Sans PhagsPa" },
328 { "A880-A8DF", L"Noto Sans Saurashtra" },
329 { "A900-A92F", L"Noto Sans Kayah Li" },
330 { "A930-A95F", L"Noto Sans Rejang" },
331 { "A980-A9DF", L"Noto Sans Javanese" },
332 { "AA00-AA5F", L"Noto Sans Cham" },
334 /* CJK Compatibility Ideographs - F900-FAFF */
336 { "F900-FAFF", L"Noto Sans CJK SC", L"zh-Hans" },
337 { "F900-FAFF", L"Noto Sans CJK TC", L"zh-Hant" },
338 { "F900-FAFF", L"Noto Sans CJK KR", L"ko" },
339 { "F900-FAFF", L"Noto Sans CJK JP" },
341 /* Vertical Forms - FE10-FE1F */
343 { "FE10-FE1F", L"Noto Sans CJK SC", L"zh-Hans" },
344 { "FE10-FE1F", L"Noto Sans CJK KR", L"ko" },
345 { "FE10-FE1F", L"Noto Sans CJK TC" },
347 /* CJK Compatibility Forms - FE30-FE4F
348 Small Form Variants - FE50-FE6F */
350 { "FE30-FE6F", L"Noto Sans CJK SC", L"zh-Hans" },
351 { "FE30-FE6F", L"Noto Sans CJK KR", L"ko" },
352 { "FE30-FE6F", L"Noto Sans CJK JP", L"ja" },
353 { "FE30-FE6F", L"Noto Sans CJK TC" },
355 /* Halfwidth and Fullwidth Forms */
356 { "FF00-FFEF", L"Noto Sans CJK SC", L"zh-Hans" },
357 { "FF00-FFEF", L"Noto Sans CJK TC", L"zh-Hant" },
358 { "FF00-FFEF", L"Noto Sans CJK KR", L"ko" },
359 { "FF00-FFEF", L"Noto Sans CJK JP" },
362 struct text_source_context
364 IDWriteTextAnalysisSource *source;
366 UINT32 position;
367 UINT32 length;
368 UINT32 consumed;
370 UINT32 chunk_length;
371 const WCHAR *text;
372 UINT32 cursor;
373 HRESULT status;
374 BOOL end;
376 UINT32 ch;
379 static inline unsigned int text_source_get_char_length(const struct text_source_context *context)
381 return context->ch > 0xffff ? 2 : 1;
384 static void text_source_read_more(struct text_source_context *context)
386 if ((context->chunk_length - context->cursor) > 1) return;
388 context->position += context->cursor;
389 context->cursor = 0;
390 if (FAILED(context->status = IDWriteTextAnalysisSource_GetTextAtPosition(context->source, context->position,
391 &context->text, &context->chunk_length)))
393 context->end = TRUE;
397 static void text_source_get_u32_char(struct text_source_context *context)
399 const WCHAR *text;
400 UINT32 available;
402 /* Make sure to have full pair of surrogates */
403 text_source_read_more(context);
405 available = context->chunk_length - context->cursor;
406 text = context->text + context->cursor;
408 if (available > 1 && IS_HIGH_SURROGATE(*text) && IS_LOW_SURROGATE(*(text + 1)))
410 context->cursor += 2;
411 context->consumed += 2;
412 context->ch = 0x10000 + ((*text - 0xd800) << 10) + *(text + 1) - 0xdc00;
413 return;
416 context->cursor++;
417 context->consumed++;
418 context->ch = *text;
421 static HRESULT text_source_context_init(struct text_source_context *context, IDWriteTextAnalysisSource *source,
422 UINT32 position, UINT32 length)
424 memset(context, 0, sizeof(*context));
425 context->source = source;
426 context->position = position;
427 context->length = length;
429 return IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &context->text, &context->chunk_length);
432 static BOOL text_source_get_next_u32_char(struct text_source_context *context)
434 if (context->consumed == context->length)
436 context->ch = 0;
437 context->end = TRUE;
439 else if (context->cursor < context->chunk_length)
441 text_source_get_u32_char(context);
443 else
445 text_source_read_more(context);
446 /* Normal end-of-text condition. */
447 if (!context->text || !context->chunk_length)
448 context->end = TRUE;
449 else
450 text_source_get_u32_char(context);
453 return context->end;
456 struct fallback_mapping
458 DWRITE_UNICODE_RANGE *ranges;
459 UINT32 ranges_count;
460 WCHAR **families;
461 UINT32 families_count;
462 IDWriteFontCollection *collection;
463 float scale;
466 struct fallback_locale
468 struct list entry;
469 WCHAR name[LOCALE_NAME_MAX_LENGTH];
470 struct
472 size_t *data;
473 size_t count;
474 size_t size;
475 } ranges;
478 static void fallback_locale_list_destroy(struct list *locales)
480 struct fallback_locale *cur, *cur2;
482 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, locales, struct fallback_locale, entry)
484 list_remove(&cur->entry);
485 free(cur->ranges.data);
486 free(cur);
490 static HRESULT fallback_locale_add_mapping(struct fallback_locale *locale, size_t index)
492 size_t count = locale->ranges.count;
494 /* Append to last range, or start a new one. */
495 if (count && locale->ranges.data[count - 1] == (index - 1))
497 locale->ranges.data[count - 1] = index;
498 return S_OK;
501 if (!dwrite_array_reserve((void **)&locale->ranges.data, &locale->ranges.size, count + 2,
502 sizeof(*locale->ranges.data)))
504 return E_OUTOFMEMORY;
507 locale->ranges.data[count] = locale->ranges.data[count + 1] = index;
508 locale->ranges.count += 2;
510 return S_OK;
513 /* TODO: potentially needs improvement to consider partially matching locale names. */
514 static struct fallback_locale * font_fallback_get_locale(const struct list *locales,
515 const WCHAR *locale_name)
517 struct fallback_locale *locale, *neutral = NULL;
519 LIST_FOR_EACH_ENTRY(locale, locales, struct fallback_locale, entry)
521 if (!wcsicmp(locale->name, locale_name)) return locale;
522 if (!*locale->name) neutral = locale;
525 return neutral;
528 struct fallback_data
530 struct fallback_mapping *mappings;
531 size_t count;
532 struct list locales;
535 struct dwrite_fontfallback
537 IDWriteFontFallback1 IDWriteFontFallback1_iface;
538 LONG refcount;
539 IDWriteFactory7 *factory;
540 IDWriteFontCollection *systemcollection;
541 struct fallback_data data;
542 size_t mappings_size;
545 struct dwrite_fontfallback_builder
547 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface;
548 LONG refcount;
549 IDWriteFactory7 *factory;
550 struct fallback_data data;
551 size_t mappings_size;
554 static struct fallback_data system_fallback =
556 .locales = LIST_INIT(system_fallback.locales),
559 static void release_fallback_mapping(struct fallback_mapping *mapping)
561 unsigned int i;
563 free(mapping->ranges);
564 for (i = 0; i < mapping->families_count; ++i)
565 free(mapping->families[i]);
566 free(mapping->families);
567 if (mapping->collection)
568 IDWriteFontCollection_Release(mapping->collection);
571 static void release_fallback_data(struct fallback_data *data)
573 size_t i;
575 for (i = 0; i < data->count; ++i)
576 release_fallback_mapping(&data->mappings[i]);
577 free(data->mappings);
578 fallback_locale_list_destroy(&data->locales);
581 static BOOL fallback_mapping_contains_character(const struct fallback_mapping *mapping, UINT32 ch)
583 size_t i;
585 for (i = 0; i < mapping->ranges_count; ++i)
587 const DWRITE_UNICODE_RANGE *range = &mapping->ranges[i];
588 if (range->first <= ch && range->last >= ch) return TRUE;
591 return FALSE;
594 static const struct fallback_mapping * find_fallback_mapping(const struct fallback_data *fallback,
595 const struct fallback_locale *locale, UINT32 ch)
597 const struct fallback_mapping *mapping;
598 size_t i, j;
600 for (i = 0; i < locale->ranges.count; i += 2)
602 size_t start = locale->ranges.data[i], end = locale->ranges.data[i + 1];
603 for (j = start; j <= end; ++j)
605 mapping = &fallback->mappings[j];
606 if (fallback_mapping_contains_character(mapping, ch)) return mapping;
610 mapping = NULL;
612 /* Mapping wasn't found for specific locale, try with neutral one. This will only recurse once. */
613 if (*locale->name)
615 locale = font_fallback_get_locale(&fallback->locales, L"");
616 mapping = find_fallback_mapping(fallback, locale, ch);
619 return mapping;
622 struct dwrite_numbersubstitution
624 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
625 LONG refcount;
627 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
628 WCHAR *locale;
629 BOOL ignore_user_override;
632 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
634 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
637 static struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface);
639 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback1(IDWriteFontFallback1 *iface)
641 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback1_iface);
644 static inline struct dwrite_fontfallback_builder *impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder *iface)
646 return CONTAINING_RECORD(iface, struct dwrite_fontfallback_builder, IDWriteFontFallbackBuilder_iface);
649 static inline UINT16 get_char_script(UINT32 c)
651 UINT16 script = get_table_entry_32(wine_scripts_table, c);
652 return script == Script_Inherited ? Script_Unknown : script;
655 static DWRITE_SCRIPT_ANALYSIS get_char_sa(UINT32 c)
657 DWRITE_SCRIPT_ANALYSIS sa;
659 sa.script = get_char_script(c);
660 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
661 if ((c >= 0x0001 && c <= 0x001f) /* C0 controls */
662 || (c >= 0x007f && c <= 0x009f) /* DELETE, C1 controls */
663 || (c == 0x00ad) /* SOFT HYPHEN */
664 || (c >= 0x200b && c <= 0x200f) /* ZWSP, ZWNJ, ZWJ, LRM, RLM */
665 || (c >= 0x2028 && c <= 0x202e) /* Line/paragraph separators, LRE, RLE, PDF, LRO, RLO */
666 || (c >= 0x2060 && c <= 0x2064) /* WJ, invisible operators */
667 || (c >= 0x2066 && c <= 0x2069) /* LRI, RLI, FSI, PDI */
668 || (c >= 0x206a && c <= 0x206f) /* Deprecated control characters */
669 || (c == 0xfeff) /* ZWBNSP */
670 || (c == 0xfff9) /* Interlinear annotation */
671 || (c == 0xfffa)
672 || (c == 0xfffb)
673 || (c >= 0x1bca0 && c <= 0x1bca3) /* Shorthand format controls */
674 || (c >= 0x1d173 && c <= 0x1d17a) /* Musical symbols: beams and slurs */
675 || (c == 0xe0001) /* Language tag, deprecated */
676 || (c >= 0xe0020 && c <= 0xe007f)) /* Tag components */
678 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
680 else
682 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
685 return sa;
688 static HRESULT analyze_script(struct text_source_context *context, IDWriteTextAnalysisSink *sink)
690 DWRITE_SCRIPT_ANALYSIS sa;
691 UINT32 pos, length;
692 HRESULT hr;
694 text_source_get_next_u32_char(context);
696 sa = get_char_sa(context->ch);
698 pos = context->position;
699 length = text_source_get_char_length(context);
701 while (!text_source_get_next_u32_char(context))
703 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(context->ch);
705 /* Unknown type is ignored when preceded or followed by another script */
706 switch (sa.script) {
707 case Script_Unknown:
708 sa.script = cur_sa.script;
709 break;
710 case Script_Common:
711 if (cur_sa.script == Script_Unknown)
712 cur_sa.script = sa.script;
713 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
714 sa.script = cur_sa.script;
715 break;
716 default:
717 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
718 cur_sa.script = sa.script;
721 /* this is a length of a sequence to be reported next */
722 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
723 length += text_source_get_char_length(context);
724 else
726 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
727 if (FAILED(hr)) return hr;
728 pos += length;
729 length = text_source_get_char_length(context);
730 sa = cur_sa;
734 /* one char length case or normal completion call */
735 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
738 struct break_index
740 unsigned int index;
741 UINT8 length;
744 struct linebreaking_state
746 DWRITE_LINE_BREAKPOINT *breakpoints;
747 struct break_index *breaks;
748 UINT32 count;
751 enum BreakConditionLocation {
752 BreakConditionBefore,
753 BreakConditionAfter
756 enum linebreaking_classes {
757 b_BK = 1,
758 b_CR,
759 b_LF,
760 b_CM,
761 b_SG,
762 b_GL,
763 b_CB,
764 b_SP,
765 b_ZW,
766 b_NL,
767 b_WJ,
768 b_JL,
769 b_JV,
770 b_JT,
771 b_H2,
772 b_H3,
773 b_XX,
774 b_OP,
775 b_CL,
776 b_CP,
777 b_QU,
778 b_NS,
779 b_EX,
780 b_SY,
781 b_IS,
782 b_PR,
783 b_PO,
784 b_NU,
785 b_AL,
786 b_ID,
787 b_IN,
788 b_HY,
789 b_BB,
790 b_BA,
791 b_SA,
792 b_AI,
793 b_B2,
794 b_HL,
795 b_CJ,
796 b_RI,
797 b_EB,
798 b_EM,
799 b_ZWJ,
802 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
804 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
805 return TRUE;
807 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
808 return TRUE;
810 return FALSE;
813 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
814 set to "can break" and could only be changed once. */
815 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
816 struct linebreaking_state *state)
818 unsigned int index = state->breaks[pos].index;
820 if (location == BreakConditionBefore)
822 if (has_strong_condition(state->breakpoints[index].breakConditionBefore, condition))
823 return;
824 state->breakpoints[index].breakConditionBefore = condition;
825 if (pos)
827 --pos;
829 index = state->breaks[pos].index;
830 if (state->breaks[pos].length > 1) index++;
832 state->breakpoints[index].breakConditionAfter = condition;
835 else
837 if (state->breaks[pos].length > 1) index++;
839 if (has_strong_condition(state->breakpoints[index].breakConditionAfter, condition))
840 return;
841 state->breakpoints[index].breakConditionAfter = condition;
843 if (pos + 1 < state->count)
845 index = state->breaks[pos + 1].index;
846 state->breakpoints[index].breakConditionBefore = condition;
851 BOOL lb_is_newline_char(WCHAR ch)
853 short c = get_table_entry_32(wine_linebreak_table, ch);
854 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
857 static HRESULT analyze_linebreaks(IDWriteTextAnalysisSource *source, UINT32 position,
858 UINT32 length, DWRITE_LINE_BREAKPOINT *breakpoints)
860 struct text_source_context context;
861 struct linebreaking_state state;
862 struct break_index *breaks;
863 unsigned int count, index;
864 short *break_class;
865 int i = 0, j;
866 HRESULT hr;
868 if (FAILED(hr = text_source_context_init(&context, source, position, length))) return hr;
870 if (!(breaks = calloc(length, sizeof(*breaks))))
871 return E_OUTOFMEMORY;
873 if (!(break_class = calloc(length, sizeof(*break_class))))
875 free(breaks);
876 return E_OUTOFMEMORY;
879 count = index = 0;
880 while (!text_source_get_next_u32_char(&context))
882 break_class[count] = get_table_entry_32(wine_linebreak_table, context.ch);
883 breaks[count].length = text_source_get_char_length(&context);
884 breaks[count].index = index;
886 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
887 switch (break_class[count])
889 case b_AI:
890 case b_SA:
891 case b_SG:
892 case b_XX:
893 break_class[count] = b_AL;
894 break;
895 case b_CJ:
896 break_class[count] = b_NS;
897 break;
900 breakpoints[index].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
901 breakpoints[index].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
902 breakpoints[index].isWhitespace = context.ch < 0xffff ? !!iswspace(context.ch) : 0;
903 breakpoints[index].isSoftHyphen = context.ch == 0x00ad /* Unicode Soft Hyphen */;
904 breakpoints[index].padding = 0;
905 ++index;
907 if (breaks[count].length > 1)
909 breakpoints[index] = breakpoints[index - 1];
910 /* Never break in surrogate pairs. */
911 breakpoints[index - 1].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
912 breakpoints[index].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
913 ++index;
916 ++count;
919 state.breakpoints = breakpoints;
920 state.breaks = breaks;
921 state.count = count;
923 /* LB2 - never break at the start */
924 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
925 /* LB3 - always break at the end. */
926 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
928 /* LB4 - LB6 - mandatory breaks. */
929 for (i = 0; i < count; i++)
931 switch (break_class[i])
933 /* LB4 - LB6 */
934 case b_CR:
935 /* LB5 - don't break CR x LF */
936 if (i < count-1 && break_class[i+1] == b_LF)
938 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
939 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
940 break;
942 case b_LF:
943 case b_NL:
944 case b_BK:
945 /* LB4 - LB5 - always break after hard breaks */
946 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
947 /* LB6 - do not break before hard breaks */
948 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
949 break;
953 /* LB7 - LB8 - explicit breaks and non-breaks */
954 for (i = 0; i < count; i++)
956 switch (break_class[i])
958 /* LB7 - do not break before spaces or zero-width space */
959 case b_SP:
960 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
961 break;
962 case b_ZW:
963 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
965 /* LB8 - break before character after zero-width space, skip spaces in-between */
966 j = i;
967 while (j < count-1 && break_class[j+1] == b_SP)
968 j++;
969 if (j < count-1 && break_class[j+1] != b_ZW)
970 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
971 break;
972 /* LB8a - do not break after ZWJ */
973 case b_ZWJ:
974 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
975 break;
979 /* LB9 - LB10 - combining marks */
980 for (i = 0; i < count; i++)
982 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
984 if (i > 0)
986 switch (break_class[i-1])
988 case b_SP:
989 case b_BK:
990 case b_CR:
991 case b_LF:
992 case b_NL:
993 case b_ZW:
994 break_class[i] = b_AL;
995 break;
996 default:
998 break_class[i] = break_class[i-1];
999 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1003 else break_class[i] = b_AL;
1007 for (i = 0; i < count; i++)
1009 switch (break_class[i])
1011 /* LB11 - don't break before and after word joiner */
1012 case b_WJ:
1013 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1014 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1015 break;
1016 /* LB12 - don't break after glue */
1017 case b_GL:
1018 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1019 /* LB12a */
1020 if (i > 0)
1022 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
1023 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1025 break;
1026 /* LB13 */
1027 case b_CL:
1028 case b_CP:
1029 case b_EX:
1030 case b_IS:
1031 case b_SY:
1032 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1033 break;
1034 /* LB14 - do not break after OP, even after spaces */
1035 case b_OP:
1036 j = i;
1037 while (j < count-1 && break_class[j+1] == b_SP)
1038 j++;
1039 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1040 break;
1041 /* LB15 - do not break within QU-OP, even with intervening spaces */
1042 case b_QU:
1043 j = i;
1044 while (j < count-1 && break_class[j+1] == b_SP)
1045 j++;
1046 if (j < count - 1 && break_class[j+1] == b_OP)
1047 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1048 break;
1049 /* LB16 */
1050 case b_NS:
1051 j = i-1;
1052 while(j > 0 && break_class[j] == b_SP)
1053 j--;
1054 if (break_class[j] == b_CL || break_class[j] == b_CP)
1055 for (j++; j <= i; j++)
1056 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1057 break;
1058 /* LB17 - do not break within B2, even with intervening spaces */
1059 case b_B2:
1060 j = i;
1061 while (j < count && break_class[j+1] == b_SP)
1062 j++;
1063 if (j < count - 1 && break_class[j+1] == b_B2)
1064 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1065 break;
1069 for (i = 0; i < count; i++)
1071 switch(break_class[i])
1073 /* LB18 - break is allowed after space */
1074 case b_SP:
1075 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
1076 break;
1077 /* LB19 - don't break before or after quotation mark */
1078 case b_QU:
1079 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1080 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1081 break;
1082 /* LB20 */
1083 case b_CB:
1084 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
1085 if (i < count - 1 && break_class[i+1] != b_QU)
1086 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
1087 break;
1088 /* LB21 */
1089 case b_BA:
1090 case b_HY:
1091 case b_NS:
1092 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1093 break;
1094 case b_BB:
1095 if (i < count - 1 && break_class[i+1] != b_CB)
1096 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1097 break;
1098 /* LB21a, LB21b */
1099 case b_HL:
1100 /* LB21a */
1101 if (i < count-1)
1102 switch (break_class[i+1])
1104 case b_HY:
1105 case b_BA:
1106 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1108 /* LB21b */
1109 if (i > 0 && break_class[i-1] == b_SY)
1110 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1111 break;
1112 /* LB22 */
1113 case b_IN:
1114 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1115 break;
1118 if (i < count-1)
1120 /* LB23 - do not break between digits and letters */
1121 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
1122 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
1123 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
1124 (break_class[i] == b_NU && break_class[i+1] == b_HL))
1125 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1127 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
1128 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
1129 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
1130 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
1131 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
1132 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
1133 (break_class[i] == b_EB && break_class[i+1] == b_PO))
1134 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1136 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
1137 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
1138 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
1139 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
1140 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
1141 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
1142 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
1143 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
1144 (break_class[i] == b_HL && break_class[i+1] == b_PO))
1145 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1147 /* LB25 */
1148 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
1149 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
1150 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
1151 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
1152 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
1153 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
1154 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
1155 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
1156 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
1157 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
1158 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
1159 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
1160 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
1161 (break_class[i] == b_SY && break_class[i+1] == b_NU))
1162 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1164 /* LB26 */
1165 if (break_class[i] == b_JL)
1167 switch (break_class[i+1])
1169 case b_JL:
1170 case b_JV:
1171 case b_H2:
1172 case b_H3:
1173 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1176 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
1177 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
1178 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1179 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
1180 break_class[i+1] == b_JT)
1181 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1183 /* LB27 */
1184 switch (break_class[i])
1186 case b_JL:
1187 case b_JV:
1188 case b_JT:
1189 case b_H2:
1190 case b_H3:
1191 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
1192 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1194 if (break_class[i] == b_PR)
1196 switch (break_class[i+1])
1198 case b_JL:
1199 case b_JV:
1200 case b_JT:
1201 case b_H2:
1202 case b_H3:
1203 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1207 /* LB28 */
1208 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
1209 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
1210 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
1211 (break_class[i] == b_HL && break_class[i+1] == b_HL))
1212 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1214 /* LB29 */
1215 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
1216 (break_class[i] == b_IS && break_class[i+1] == b_HL))
1217 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1219 /* LB30 */
1220 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
1221 break_class[i+1] == b_OP)
1222 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1223 if (break_class[i] == b_CP &&
1224 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
1225 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1227 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
1228 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
1229 unsigned int c = 0;
1231 j = i + 1;
1232 while (j > 0 && break_class[--j] == b_RI)
1233 c++;
1235 if ((c & 1) == 0)
1236 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1239 /* LB30b - do not break between an emoji base and an emoji modifier */
1240 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
1241 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
1245 /* LB31 - allow breaks everywhere else. */
1246 for (i = 0; i < count; i++)
1248 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
1249 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
1252 free(break_class);
1253 free(breaks);
1255 return S_OK;
1258 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
1260 TRACE("%s, %p.\n", debugstr_guid(riid), obj);
1262 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
1263 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
1264 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
1265 IsEqualIID(riid, &IID_IUnknown))
1267 *obj = iface;
1268 return S_OK;
1271 WARN("%s not implemented.\n", debugstr_guid(riid));
1273 *obj = NULL;
1274 return E_NOINTERFACE;
1277 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
1279 return 2;
1282 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
1284 return 1;
1287 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
1288 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
1290 struct text_source_context context;
1291 HRESULT hr;
1293 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
1295 if (!length)
1296 return S_OK;
1298 if (FAILED(hr = text_source_context_init(&context, source, position, length))) return hr;
1300 return analyze_script(&context, sink);
1303 static inline unsigned int get_bidi_char_length(const struct bidi_char *c)
1305 return c->ch > 0xffff ? 2 : 1;
1308 static inline UINT8 get_char_bidi_class(UINT32 ch)
1310 return get_table_entry_32(bidi_direction_table, ch);
1313 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
1314 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
1316 struct text_source_context context;
1317 UINT8 baselevel, resolved, explicit;
1318 unsigned int i, chars_count = 0;
1319 struct bidi_char *chars, *ptr;
1320 UINT32 pos, seq_length;
1321 HRESULT hr;
1323 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
1325 if (!length)
1326 return S_OK;
1328 if (!(chars = calloc(length, sizeof(*chars))))
1329 return E_OUTOFMEMORY;
1331 ptr = chars;
1332 text_source_context_init(&context, source, position, length);
1333 while (!text_source_get_next_u32_char(&context))
1335 ptr->ch = context.ch;
1336 ptr->nominal_bidi_class = ptr->bidi_class = get_char_bidi_class(context.ch);
1337 ptr++;
1339 ++chars_count;
1342 /* Resolve levels using utf-32 codepoints, size differences are accounted for
1343 when levels are reported with SetBidiLevel(). */
1345 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
1346 hr = bidi_computelevels(chars, chars_count, baselevel);
1347 if (FAILED(hr))
1348 goto done;
1350 pos = position;
1351 resolved = chars->resolved;
1352 explicit = chars->explicit;
1353 seq_length = get_bidi_char_length(chars);
1355 for (i = 1, ptr = chars + 1; i < chars_count; ++i, ++ptr)
1357 if (ptr->resolved == resolved && ptr->explicit == explicit)
1359 seq_length += get_bidi_char_length(ptr);
1361 else
1363 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit, resolved);
1364 if (FAILED(hr))
1365 goto done;
1367 pos += seq_length;
1368 seq_length = get_bidi_char_length(ptr);
1369 resolved = ptr->resolved;
1370 explicit = ptr->explicit;
1374 /* one char length case or normal completion call */
1375 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit, resolved);
1377 done:
1378 free(chars);
1380 return hr;
1383 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
1384 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
1386 static int once;
1388 if (!once++)
1389 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
1390 return S_OK;
1393 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
1394 IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
1396 DWRITE_LINE_BREAKPOINT *breakpoints;
1397 HRESULT hr;
1399 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
1401 if (!length)
1402 return S_OK;
1404 if (!(breakpoints = calloc(length, sizeof(*breakpoints))))
1405 return E_OUTOFMEMORY;
1407 if (SUCCEEDED(hr = analyze_linebreaks(source, position, length, breakpoints)))
1408 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1410 free(breakpoints);
1412 return hr;
1415 static UINT32 get_opentype_language(const WCHAR *locale)
1417 UINT32 language = DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1419 if (locale) {
1420 WCHAR tag[5];
1421 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, ARRAY_SIZE(tag)))
1422 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1425 return language;
1428 static void get_number_substitutes(IDWriteNumberSubstitution *substitution, BOOL is_rtl, WCHAR *digits)
1430 struct dwrite_numbersubstitution *numbersubst = unsafe_impl_from_IDWriteNumberSubstitution(substitution);
1431 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1432 WCHAR isolang[9];
1433 DWORD lctype;
1435 digits[0] = 0;
1437 if (!numbersubst)
1438 return;
1440 lctype = numbersubst->ignore_user_override ? LOCALE_NOUSEROVERRIDE : 0;
1442 if (numbersubst->method == DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE) {
1443 DWORD value;
1445 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1446 if (GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, (WCHAR *)&value, 2)) {
1447 switch (value)
1449 case 0:
1450 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL;
1451 break;
1452 case 2:
1453 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL;
1454 break;
1455 case 1:
1456 default:
1457 if (value != 1)
1458 WARN("Unknown IDIGITSUBSTITUTION value %lu, locale %s.\n", value, debugstr_w(numbersubst->locale));
1461 else
1462 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst->locale));
1464 else
1465 method = numbersubst->method;
1467 switch (method)
1469 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL:
1470 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1471 break;
1472 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1473 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL:
1474 if (GetLocaleInfoEx(numbersubst->locale, LOCALE_SISO639LANGNAME, isolang, ARRAY_SIZE(isolang)))
1476 static const WCHAR arabicW[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1478 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1479 if (!wcscmp(L"ar", isolang))
1481 wcscpy(digits, arabicW);
1482 break;
1485 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1486 break;
1487 default:
1491 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !*digits) {
1492 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst->locale), method);
1493 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1496 if ((method == DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL && !is_rtl) || method == DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE)
1497 digits[0] = 0;
1500 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES const **features,
1501 UINT32 const *feature_range_lengths, UINT32 feature_ranges)
1503 UINT32 i, j, start;
1505 if (!TRACE_ON(dwrite) || !features)
1506 return;
1508 for (i = 0, start = 0; i < feature_ranges; start += feature_range_lengths[i++]) {
1509 TRACE("feature range [%u,%u)\n", start, start + feature_range_lengths[i]);
1510 for (j = 0; j < features[i]->featureCount; j++)
1511 TRACE("feature %s, parameter %u\n", debugstr_tag(features[i]->features[j].nameTag),
1512 features[i]->features[j].parameter);
1516 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1517 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1518 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1519 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1520 UINT32 const* feature_range_lengths, UINT32 feature_ranges, UINT32 max_glyph_count,
1521 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16 *glyphs,
1522 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1524 const struct dwritescript_properties *scriptprops;
1525 struct scriptshaping_context context = { 0 };
1526 struct dwrite_fontface *font_obj;
1527 WCHAR digits[NATIVE_DIGITS_LEN];
1528 unsigned int glyph_count;
1529 HRESULT hr;
1531 TRACE("%s:%u, %p, %d, %d, %s, %s, %p, %p, %p, %u, %u, %p, %p, %p, %p, %p.\n", debugstr_wn(text, length),
1532 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1533 features, feature_range_lengths, feature_ranges, max_glyph_count, clustermap, text_props, glyphs,
1534 glyph_props, actual_glyph_count);
1536 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1538 get_number_substitutes(substitution, is_rtl, digits);
1539 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1540 glyph_count = max(max_glyph_count, length);
1542 context.cache = fontface_get_shaping_cache(font_obj);
1543 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1544 context.text = text;
1545 context.length = length;
1546 context.is_rtl = is_rtl;
1547 context.is_sideways = is_sideways;
1548 context.u.subst.glyphs = calloc(glyph_count, sizeof(*glyphs));
1549 context.u.subst.glyph_props = calloc(glyph_count, sizeof(*glyph_props));
1550 context.u.subst.text_props = text_props;
1551 context.u.subst.clustermap = clustermap;
1552 context.u.subst.max_glyph_count = max_glyph_count;
1553 context.u.subst.capacity = glyph_count;
1554 context.u.subst.digits = digits;
1555 context.language_tag = get_opentype_language(locale);
1556 context.user_features.features = features;
1557 context.user_features.range_lengths = feature_range_lengths;
1558 context.user_features.range_count = feature_ranges;
1559 context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos));
1560 context.table = &context.cache->gsub;
1562 *actual_glyph_count = 0;
1564 if (!context.u.subst.glyphs || !context.u.subst.glyph_props || !context.glyph_infos)
1566 hr = E_OUTOFMEMORY;
1567 goto failed;
1570 scriptprops = &dwritescripts_properties[context.script];
1571 hr = shape_get_glyphs(&context, scriptprops->scripttags);
1572 if (SUCCEEDED(hr))
1574 *actual_glyph_count = context.glyph_count;
1575 memcpy(glyphs, context.u.subst.glyphs, context.glyph_count * sizeof(*glyphs));
1576 memcpy(glyph_props, context.u.subst.glyph_props, context.glyph_count * sizeof(*glyph_props));
1579 failed:
1580 free(context.u.subst.glyph_props);
1581 free(context.u.subst.glyphs);
1582 free(context.glyph_infos);
1584 return hr;
1587 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1588 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1589 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1590 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, BOOL is_sideways, BOOL is_rtl,
1591 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1592 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1594 const struct dwritescript_properties *scriptprops;
1595 struct scriptshaping_context context;
1596 struct dwrite_fontface *font_obj;
1597 unsigned int i;
1598 HRESULT hr;
1600 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),
1601 clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1602 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_lengths,
1603 feature_ranges, advances, offsets);
1605 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1607 if (glyph_count == 0)
1608 return S_OK;
1610 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1612 for (i = 0; i < glyph_count; ++i)
1614 if (glyph_props[i].isZeroWidthSpace)
1615 advances[i] = 0.0f;
1616 else
1617 advances[i] = fontface_get_scaled_design_advance(font_obj, DWRITE_MEASURING_MODE_NATURAL, emSize, 1.0f,
1618 NULL, glyphs[i], is_sideways);
1619 offsets[i].advanceOffset = 0.0f;
1620 offsets[i].ascenderOffset = 0.0f;
1623 context.cache = fontface_get_shaping_cache(font_obj);
1624 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1625 context.text = text;
1626 context.length = text_len;
1627 context.is_rtl = is_rtl;
1628 context.is_sideways = is_sideways;
1629 context.u.pos.glyphs = glyphs;
1630 context.u.pos.glyph_props = glyph_props;
1631 context.u.pos.text_props = text_props;
1632 context.u.pos.clustermap = clustermap;
1633 context.glyph_count = glyph_count;
1634 context.emsize = emSize;
1635 context.measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
1636 context.advances = advances;
1637 context.offsets = offsets;
1638 context.language_tag = get_opentype_language(locale);
1639 context.user_features.features = features;
1640 context.user_features.range_lengths = feature_range_lengths;
1641 context.user_features.range_count = feature_ranges;
1642 context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos));
1643 context.table = &context.cache->gpos;
1645 if (!context.glyph_infos)
1647 hr = E_OUTOFMEMORY;
1648 goto failed;
1651 scriptprops = &dwritescripts_properties[context.script];
1652 hr = shape_get_positions(&context, scriptprops->scripttags);
1654 failed:
1655 free(context.glyph_infos);
1657 return hr;
1660 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1661 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1662 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1663 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, float ppdip,
1664 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1665 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1666 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1668 const struct dwritescript_properties *scriptprops;
1669 struct scriptshaping_context context;
1670 DWRITE_MEASURING_MODE measuring_mode;
1671 struct dwrite_fontface *font_obj;
1672 unsigned int i;
1673 HRESULT hr;
1675 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1676 debugstr_wn(text, text_len), clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface,
1677 emSize, ppdip, transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script),
1678 debugstr_w(locale), features, feature_range_lengths, feature_ranges, advances, offsets);
1680 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1682 if (glyph_count == 0)
1683 return S_OK;
1685 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1687 measuring_mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
1689 for (i = 0; i < glyph_count; ++i)
1691 if (glyph_props[i].isZeroWidthSpace)
1692 advances[i] = 0.0f;
1693 else
1694 advances[i] = fontface_get_scaled_design_advance(font_obj, measuring_mode, emSize, ppdip,
1695 transform, glyphs[i], is_sideways);
1696 offsets[i].advanceOffset = 0.0f;
1697 offsets[i].ascenderOffset = 0.0f;
1700 context.cache = fontface_get_shaping_cache(font_obj);
1701 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1702 context.text = text;
1703 context.length = text_len;
1704 context.is_rtl = is_rtl;
1705 context.is_sideways = is_sideways;
1706 context.u.pos.glyphs = glyphs;
1707 context.u.pos.glyph_props = glyph_props;
1708 context.u.pos.text_props = text_props;
1709 context.u.pos.clustermap = clustermap;
1710 context.glyph_count = glyph_count;
1711 context.emsize = emSize * ppdip;
1712 context.measuring_mode = measuring_mode;
1713 context.advances = advances;
1714 context.offsets = offsets;
1715 context.language_tag = get_opentype_language(locale);
1716 context.user_features.features = features;
1717 context.user_features.range_lengths = feature_range_lengths;
1718 context.user_features.range_count = feature_ranges;
1719 context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos));
1720 context.table = &context.cache->gpos;
1722 if (!context.glyph_infos)
1724 hr = E_OUTOFMEMORY;
1725 goto failed;
1728 scriptprops = &dwritescripts_properties[context.script];
1729 hr = shape_get_positions(&context, scriptprops->scripttags);
1731 failed:
1732 free(context.glyph_infos);
1734 return hr;
1737 static HRESULT apply_cluster_spacing(float leading_spacing, float trailing_spacing, float min_advance_width,
1738 unsigned int start, unsigned int end, float const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1739 DWRITE_SHAPING_GLYPH_PROPERTIES const *glyph_props, float *modified_advances,
1740 DWRITE_GLYPH_OFFSET *modified_offsets)
1742 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1743 unsigned int first_spacing, last_spacing, i;
1744 float advance, origin = 0.0f, *deltas;
1745 BOOL is_spacing_cluster = FALSE;
1747 if (modified_advances != advances)
1748 memcpy(&modified_advances[start], &advances[start], (end - start + 1) * sizeof(*advances));
1749 if (modified_offsets != offsets)
1750 memcpy(&modified_offsets[start], &offsets[start], (end - start + 1) * sizeof(*offsets));
1752 for (first_spacing = start; first_spacing <= end; ++first_spacing)
1754 if ((is_spacing_cluster = !glyph_props[first_spacing].isZeroWidthSpace))
1755 break;
1758 /* Nothing to adjust if there is no spacing glyphs. */
1759 if (!is_spacing_cluster)
1760 return S_OK;
1762 for (last_spacing = end; last_spacing >= start; --last_spacing)
1764 if (!glyph_props[last_spacing].isZeroWidthSpace)
1765 break;
1768 if (!(deltas = calloc(end - start + 1, sizeof(*deltas))))
1769 return E_OUTOFMEMORY;
1771 /* Cluster advance, note that properties are ignored. */
1772 origin = offsets[start].advanceOffset;
1773 for (i = start, advance = 0.0f; i <= end; ++i)
1775 float cur = advance + offsets[i].advanceOffset;
1777 deltas[i - start] = cur - origin;
1779 advance += advances[i];
1780 origin = cur;
1783 /* Negative spacing. */
1784 if (leading_spacing < 0.0f)
1786 advance += leading_spacing;
1787 modified_advances[first_spacing] += leading_spacing;
1788 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1791 if (trailing_spacing < 0.0f)
1793 advance += trailing_spacing;
1794 modified_advances[last_spacing] += trailing_spacing;
1797 /* Minimal advance. */
1798 advance = min_advance_width - advance;
1799 if (advance > 0.0f) {
1800 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1801 float half = advance / 2.0f;
1803 if (!reduced)
1805 modified_advances[first_spacing] += half;
1806 modified_advances[last_spacing] += half;
1807 modified_offsets[first_spacing].advanceOffset += half;
1809 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1811 modified_advances[first_spacing] += half;
1812 modified_advances[last_spacing] += half;
1813 modified_offsets[first_spacing].advanceOffset += half;
1815 else if (leading_spacing < 0.0f)
1817 modified_advances[first_spacing] += advance;
1818 modified_offsets[first_spacing].advanceOffset += advance;
1820 else
1821 modified_advances[last_spacing] += advance;
1824 /* Positive spacing. */
1825 if (leading_spacing > 0.0f)
1827 modified_advances[first_spacing] += leading_spacing;
1828 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1831 if (trailing_spacing > 0.0f)
1832 modified_advances[last_spacing] += trailing_spacing;
1834 /* Update offsets to preserve original relative positions within cluster. */
1835 for (i = first_spacing; i > start; --i)
1837 unsigned int cur = i - 1;
1838 modified_offsets[cur].advanceOffset = modified_advances[cur] + modified_offsets[i].advanceOffset -
1839 deltas[i - start];
1842 for (i = first_spacing + 1; i <= end; ++i)
1844 modified_offsets[i].advanceOffset = deltas[i - start] + modified_offsets[i - 1].advanceOffset -
1845 modified_advances[i - 1];
1848 free(deltas);
1850 return S_OK;
1853 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1855 UINT16 g = clustermap[start];
1856 UINT32 length = 1;
1858 while (start < text_len && clustermap[++start] == g)
1859 length++;
1860 return length;
1863 /* Applies spacing adjustments to clusters.
1865 Adjustments are applied in the following order:
1867 1. Negative adjustments
1869 Leading and trailing spacing could be negative, at this step
1870 only negative ones are actually applied. Leading spacing is only
1871 applied to leading glyph, trailing - to trailing glyph.
1873 2. Minimum advance width
1875 Advances could only be reduced at this point or unchanged. In any
1876 case it's checked if cluster advance width is less than minimum width.
1877 If it's the case advance width is incremented up to minimum value.
1879 Important part is the direction in which this increment is applied;
1880 it depends on direction from which total cluster advance was trimmed
1881 at step 1. So it could be incremented from leading, trailing, or both
1882 sides. When applied to both sides, each side gets half of difference
1883 that brings advance to minimum width.
1885 3. Positive adjustments
1887 After minimum width rule was applied, positive spacing is applied in the same
1888 way as negative one on step 1.
1890 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1891 keeps its position in coordinate system where initial advance width is counted
1892 from 0.
1894 Glyph properties
1896 It's known that isZeroWidthSpace property keeps initial advance from changing.
1899 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1900 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1901 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1902 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1904 unsigned int i;
1906 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing, trailing_spacing, min_advance_width,
1907 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1909 if (min_advance_width < 0.0f) {
1910 if (modified_advances != advances)
1911 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1912 return E_INVALIDARG;
1915 for (i = 0; i < len;)
1917 unsigned int length = get_cluster_length(clustermap, i, len);
1918 unsigned int start, end;
1920 start = clustermap[i];
1921 end = i + length < len ? clustermap[i + length] : glyph_count;
1923 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width, start, end - 1, advances,
1924 offsets, props, modified_advances, modified_offsets);
1926 i += length;
1929 return S_OK;
1932 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *fontface,
1933 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1934 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1936 struct dwrite_fontface *font_obj;
1937 const DWRITE_FONT_METRICS1 *metrics;
1939 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1940 baseline_coord, exists);
1942 *exists = FALSE;
1943 *baseline_coord = 0;
1945 if (baseline == DWRITE_BASELINE_DEFAULT)
1946 baseline = vertical ? DWRITE_BASELINE_CENTRAL : DWRITE_BASELINE_ROMAN;
1948 if ((unsigned int)baseline > DWRITE_BASELINE_MAXIMUM)
1949 return E_INVALIDARG;
1951 /* TODO: fetch BASE table data if available. */
1953 if (!*exists && is_simulation_allowed)
1955 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1956 metrics = &font_obj->metrics;
1958 switch (baseline)
1960 case DWRITE_BASELINE_ROMAN:
1961 *baseline_coord = vertical ? metrics->descent : 0;
1962 break;
1963 case DWRITE_BASELINE_CENTRAL:
1964 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1965 -(metrics->ascent - metrics->descent) / 2;
1966 break;
1967 case DWRITE_BASELINE_MATH:
1968 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1969 -(metrics->ascent + metrics->descent) / 2;
1970 break;
1971 case DWRITE_BASELINE_HANGING:
1972 /* FIXME: this one isn't accurate, but close. */
1973 *baseline_coord = vertical ? metrics->capHeight * 6 / 7 + metrics->descent : metrics->capHeight * 6 / 7;
1974 break;
1975 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM:
1976 case DWRITE_BASELINE_MINIMUM:
1977 *baseline_coord = vertical ? 0 : metrics->descent;
1978 break;
1979 case DWRITE_BASELINE_IDEOGRAPHIC_TOP:
1980 case DWRITE_BASELINE_MAXIMUM:
1981 *baseline_coord = vertical ? metrics->ascent + metrics->descent : -metrics->ascent;
1982 break;
1983 default:
1988 return S_OK;
1991 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1992 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1994 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1995 return E_NOTIMPL;
1998 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1999 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
2001 TRACE("%d, %d, %p.\n", angle, is_sideways, transform);
2003 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
2006 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
2007 DWRITE_SCRIPT_PROPERTIES *props)
2009 TRACE("%u, %p.\n", sa.script, props);
2011 if (sa.script > Script_LastId)
2012 return E_INVALIDARG;
2014 *props = dwritescripts_properties[sa.script].props;
2015 return S_OK;
2018 static inline BOOL is_char_from_simple_script(WCHAR c)
2020 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c) ||
2021 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
2022 c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e))
2023 return FALSE;
2024 else {
2025 UINT16 script = get_char_script(c);
2026 return !dwritescripts_properties[script].is_complex;
2030 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
2031 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
2033 HRESULT hr = S_OK;
2034 int i;
2036 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
2038 *is_simple = FALSE;
2039 *len_read = 0;
2041 if (!face)
2042 return E_INVALIDARG;
2044 if (len == 0) {
2045 *is_simple = TRUE;
2046 return S_OK;
2049 *is_simple = text[0] && is_char_from_simple_script(text[0]);
2050 for (i = 1; i < len && text[i]; i++) {
2051 if (is_char_from_simple_script(text[i])) {
2052 if (!*is_simple)
2053 break;
2055 else
2056 *is_simple = FALSE;
2059 *len_read = i;
2061 /* fetch indices */
2062 if (*is_simple && indices)
2064 UINT32 *codepoints;
2066 if (!(codepoints = calloc(*len_read, sizeof(*codepoints))))
2067 return E_OUTOFMEMORY;
2069 for (i = 0; i < *len_read; i++)
2070 codepoints[i] = text[i];
2072 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
2073 free(codepoints);
2076 return hr;
2079 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
2080 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
2081 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
2083 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
2084 debugstr_wn(text, length), clustermap, prop, jo);
2085 return E_NOTIMPL;
2088 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
2089 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
2090 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
2092 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
2093 justifiedoffsets);
2094 return E_NOTIMPL;
2097 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
2098 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
2099 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
2100 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
2101 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
2102 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
2104 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,
2105 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
2106 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
2107 return E_NOTIMPL;
2110 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
2111 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
2113 static const DWRITE_MATRIX transforms[] = {
2114 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
2115 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
2116 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
2117 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
2120 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle, is_sideways, originX, originY, m);
2122 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
2123 memset(m, 0, sizeof(*m));
2124 return E_INVALIDARG;
2127 /* for sideways case simply rotate 90 degrees more */
2128 if (is_sideways) {
2129 switch (angle) {
2130 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2131 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
2132 break;
2133 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2134 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
2135 break;
2136 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2137 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
2138 break;
2139 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2140 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2141 break;
2142 default:
2147 *m = transforms[angle];
2149 /* shift components represent transform necessary to get from original point to
2150 rotated one in new coordinate system */
2151 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
2152 m->dx = originX - (m->m11 * originX + m->m21 * originY);
2153 m->dy = originY - (m->m12 * originX + m->m22 * originY);
2156 return S_OK;
2159 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
2160 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
2161 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
2163 struct scriptshaping_context context = { 0 };
2164 const struct dwritescript_properties *props;
2165 struct dwrite_fontface *font_obj;
2167 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), max_tagcount,
2168 actual_tagcount, tags);
2170 if (sa.script > Script_LastId)
2171 return E_INVALIDARG;
2173 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
2175 context.cache = fontface_get_shaping_cache(font_obj);
2176 context.language_tag = get_opentype_language(locale);
2177 props = &dwritescripts_properties[sa.script];
2179 return shape_get_typographic_features(&context, props->scripttags, max_tagcount, actual_tagcount, tags);
2182 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
2183 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature,
2184 UINT32 glyph_count, const UINT16 *glyphs, UINT8 *feature_applies)
2186 struct scriptshaping_context context = { 0 };
2187 const struct dwritescript_properties *props;
2188 struct dwrite_fontface *font_obj;
2189 HRESULT hr;
2191 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), debugstr_tag(feature),
2192 glyph_count, glyphs, feature_applies);
2194 if (sa.script > Script_LastId)
2195 return E_INVALIDARG;
2197 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
2199 context.cache = fontface_get_shaping_cache(font_obj);
2200 context.language_tag = get_opentype_language(locale);
2201 if (!(context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos))))
2202 return E_OUTOFMEMORY;
2204 props = &dwritescripts_properties[sa.script];
2206 hr = shape_check_typographic_feature(&context, props->scripttags, feature, glyph_count, glyphs, feature_applies);
2208 free(context.glyph_infos);
2210 return hr;
2213 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl =
2215 dwritetextanalyzer_QueryInterface,
2216 dwritetextanalyzer_AddRef,
2217 dwritetextanalyzer_Release,
2218 dwritetextanalyzer_AnalyzeScript,
2219 dwritetextanalyzer_AnalyzeBidi,
2220 dwritetextanalyzer_AnalyzeNumberSubstitution,
2221 dwritetextanalyzer_AnalyzeLineBreakpoints,
2222 dwritetextanalyzer_GetGlyphs,
2223 dwritetextanalyzer_GetGlyphPlacements,
2224 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
2225 dwritetextanalyzer1_ApplyCharacterSpacing,
2226 dwritetextanalyzer1_GetBaseline,
2227 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
2228 dwritetextanalyzer1_GetGlyphOrientationTransform,
2229 dwritetextanalyzer1_GetScriptProperties,
2230 dwritetextanalyzer1_GetTextComplexity,
2231 dwritetextanalyzer1_GetJustificationOpportunities,
2232 dwritetextanalyzer1_JustifyGlyphAdvances,
2233 dwritetextanalyzer1_GetJustifiedGlyphs,
2234 dwritetextanalyzer2_GetGlyphOrientationTransform,
2235 dwritetextanalyzer2_GetTypographicFeatures,
2236 dwritetextanalyzer2_CheckTypographicFeature
2239 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
2241 IDWriteTextAnalyzer2 *get_text_analyzer(void)
2243 return &textanalyzer;
2246 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
2248 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2250 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
2251 IsEqualIID(riid, &IID_IUnknown))
2253 *obj = iface;
2254 IDWriteNumberSubstitution_AddRef(iface);
2255 return S_OK;
2258 WARN("%s not implemented.\n", debugstr_guid(riid));
2260 *obj = NULL;
2262 return E_NOINTERFACE;
2265 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
2267 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
2268 ULONG refcount = InterlockedIncrement(&object->refcount);
2270 TRACE("%p, refcount %ld.\n", iface, refcount);
2272 return refcount;
2275 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
2277 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
2278 ULONG refcount = InterlockedDecrement(&object->refcount);
2280 TRACE("%p, refcount %ld.\n", iface, refcount);
2282 if (!refcount)
2284 free(object->locale);
2285 free(object);
2288 return refcount;
2291 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl =
2293 dwritenumbersubstitution_QueryInterface,
2294 dwritenumbersubstitution_AddRef,
2295 dwritenumbersubstitution_Release
2298 struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
2300 if (!iface || iface->lpVtbl != &numbersubstitutionvtbl)
2301 return NULL;
2302 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
2305 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
2306 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
2308 struct dwrite_numbersubstitution *substitution;
2310 *ret = NULL;
2312 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
2313 return E_INVALIDARG;
2315 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
2316 return E_INVALIDARG;
2318 if (!(substitution = calloc(1, sizeof(*substitution))))
2319 return E_OUTOFMEMORY;
2321 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
2322 substitution->refcount = 1;
2323 substitution->ignore_user_override = ignore_user_override;
2324 substitution->method = method;
2325 substitution->locale = wcsdup(locale);
2326 if (locale && !substitution->locale)
2328 free(substitution);
2329 return E_OUTOFMEMORY;
2332 *ret = &substitution->IDWriteNumberSubstitution_iface;
2333 return S_OK;
2336 /* IDWriteFontFallback */
2337 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback1 *iface, REFIID riid, void **obj)
2339 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2341 if (IsEqualIID(riid, &IID_IDWriteFontFallback1) ||
2342 IsEqualIID(riid, &IID_IDWriteFontFallback) ||
2343 IsEqualIID(riid, &IID_IUnknown))
2345 *obj = iface;
2346 IDWriteFontFallback1_AddRef(iface);
2347 return S_OK;
2350 WARN("%s not implemented.\n", debugstr_guid(riid));
2352 *obj = NULL;
2353 return E_NOINTERFACE;
2356 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback1 *iface)
2358 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2360 TRACE("%p.\n", iface);
2362 return IDWriteFactory7_AddRef(fallback->factory);
2365 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback1 *iface)
2367 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2369 TRACE("%p.\n", fallback);
2371 return IDWriteFactory7_Release(fallback->factory);
2374 static inline BOOL fallback_is_uvs(const struct text_source_context *context)
2376 /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */
2377 if (context->ch >= 0x180b && context->ch <= 0x180d) return TRUE;
2378 /* VARIATION SELECTOR-1..16 */
2379 if (context->ch >= 0xfe00 && context->ch <= 0xfe0f) return TRUE;
2380 /* VARIATION SELECTOR-17..256 */
2381 if (context->ch >= 0xe0100 && context->ch <= 0xe01ef) return TRUE;
2382 return FALSE;
2385 static UINT32 fallback_font_get_supported_length(IDWriteFont3 *font, IDWriteTextAnalysisSource *source,
2386 UINT32 position, UINT32 length)
2388 struct text_source_context context;
2389 UINT32 mapped = 0;
2391 text_source_context_init(&context, source, position, length);
2392 while (!text_source_get_next_u32_char(&context))
2394 /* Ignore selectors that are not leading. */
2395 if (!mapped || !fallback_is_uvs(&context))
2397 if (!IDWriteFont3_HasCharacter(font, context.ch)) break;
2399 mapped += text_source_get_char_length(&context);
2402 return mapped;
2405 static HRESULT fallback_map_characters(const struct dwrite_fontfallback *fallback, IDWriteTextAnalysisSource *source,
2406 UINT32 position, UINT32 text_length, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style,
2407 DWRITE_FONT_STRETCH stretch, IDWriteFont **ret_font, UINT32 *ret_length, float *scale)
2409 const struct fallback_mapping *mapping = NULL;
2410 struct text_source_context context;
2411 const struct fallback_data *data;
2412 const WCHAR *locale_name = NULL;
2413 struct fallback_locale *locale;
2414 UINT32 i, length = 0, mapped;
2415 IDWriteFont3 *font;
2416 HRESULT hr;
2418 /* ~0u is a marker for system fallback data */
2419 data = fallback->data.count == ~0u ? &system_fallback : &fallback->data;
2421 /* We will try to map as much of given input as GetLocaleName() says. It's assumed that returned length covers
2422 whole span of characters set with that locale, so callback is only used once. */
2423 if (FAILED(hr = IDWriteTextAnalysisSource_GetLocaleName(source, position, &length, &locale_name)))
2424 return hr;
2426 length = length ? min(length, text_length) : text_length;
2427 if (!locale_name) locale_name = L"";
2429 /* Lookup locale entry once, if specific locale is missing neutral one will be returned. */
2430 locale = font_fallback_get_locale(&data->locales, locale_name);
2432 if (FAILED(hr = text_source_context_init(&context, source, position, length))) return hr;
2434 /* Find a mapping for given locale. */
2435 text_source_get_next_u32_char(&context);
2436 mapping = find_fallback_mapping(data, locale, context.ch);
2437 mapped = text_source_get_char_length(&context);
2438 while (!text_source_get_next_u32_char(&context))
2440 if (find_fallback_mapping(data, locale, context.ch) != mapping) break;
2441 mapped += text_source_get_char_length(&context);
2444 if (!mapping)
2446 *ret_font = NULL;
2447 *ret_length = mapped;
2449 return S_OK;
2452 /* Go through families in the mapping, use first family that supports some of the input. */
2453 for (i = 0; i < mapping->families_count; ++i)
2455 if (SUCCEEDED(create_matching_font(mapping->collection ? mapping->collection : fallback->systemcollection,
2456 mapping->families[i], weight, style, stretch, &IID_IDWriteFont3, (void **)&font)))
2458 if (!(mapped = fallback_font_get_supported_length(font, source, position, mapped)))
2460 IDWriteFont3_Release(font);
2461 continue;
2464 *ret_font = (IDWriteFont *)font;
2465 *ret_length = mapped;
2466 *scale = mapping->scale;
2468 return S_OK;
2472 /* Mapping was found, but either font couldn't be created or there's no font that supports given input. */
2473 *ret_font = NULL;
2474 *ret_length = length;
2476 return S_OK;
2479 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name, DWRITE_FONT_WEIGHT weight,
2480 DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, REFIID riid, void **obj)
2482 IDWriteFontFamily *family;
2483 BOOL exists = FALSE;
2484 IDWriteFont *font;
2485 HRESULT hr;
2486 UINT32 i;
2488 *obj = NULL;
2490 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
2491 if (FAILED(hr))
2492 return hr;
2494 if (!exists)
2495 return E_FAIL;
2497 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
2498 if (FAILED(hr))
2499 return hr;
2501 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, &font);
2502 IDWriteFontFamily_Release(family);
2504 if (SUCCEEDED(hr))
2506 hr = IDWriteFont_QueryInterface(font, riid, obj);
2507 IDWriteFont_Release(font);
2510 return hr;
2513 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2514 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2515 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2516 IDWriteFont **ret_font, float *scale)
2518 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2519 IDWriteFont3 *font;
2521 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length,
2522 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2524 *mapped_length = 0;
2525 *ret_font = NULL;
2526 *scale = 1.0f;
2528 if (!source)
2529 return E_INVALIDARG;
2531 if (!length)
2532 return S_OK;
2534 if (!basecollection)
2535 basecollection = fallback->systemcollection;
2537 if (basefamily && *basefamily)
2539 if (SUCCEEDED(create_matching_font(basecollection, basefamily, weight, style, stretch,
2540 &IID_IDWriteFont, (void **)&font)))
2542 if ((*mapped_length = fallback_font_get_supported_length(font, source, position, length)))
2544 *ret_font = (IDWriteFont *)font;
2545 *scale = 1.0f;
2546 return S_OK;
2548 IDWriteFont3_Release(font);
2552 return fallback_map_characters(fallback, source, position, length, weight, style, stretch, ret_font, mapped_length, scale);
2555 static HRESULT WINAPI fontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2556 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2557 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 values_count, UINT32 *mapped_length, FLOAT *scale,
2558 IDWriteFontFace5 **ret_fontface)
2560 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2561 debugstr_w(basefamily), axis_values, values_count, mapped_length, scale, ret_fontface);
2563 return E_NOTIMPL;
2566 static const IDWriteFontFallback1Vtbl fontfallbackvtbl =
2568 fontfallback_QueryInterface,
2569 fontfallback_AddRef,
2570 fontfallback_Release,
2571 fontfallback_MapCharacters,
2572 fontfallback1_MapCharacters,
2575 void release_system_fontfallback(IDWriteFontFallback1 *iface)
2577 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2578 IDWriteFontCollection_Release(fallback->systemcollection);
2579 free(fallback);
2582 static ULONG WINAPI customfontfallback_AddRef(IDWriteFontFallback1 *iface)
2584 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2585 ULONG refcount = InterlockedIncrement(&fallback->refcount);
2587 TRACE("%p, refcount %lu.\n", iface, refcount);
2589 return refcount;
2592 static ULONG WINAPI customfontfallback_Release(IDWriteFontFallback1 *iface)
2594 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2595 ULONG refcount = InterlockedDecrement(&fallback->refcount);
2597 TRACE("%p, refcount %lu.\n", iface, refcount);
2599 if (!refcount)
2601 IDWriteFactory7_Release(fallback->factory);
2602 if (fallback->systemcollection)
2603 IDWriteFontCollection_Release(fallback->systemcollection);
2604 release_fallback_data(&fallback->data);
2605 free(fallback);
2608 return refcount;
2611 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl =
2613 fontfallback_QueryInterface,
2614 customfontfallback_AddRef,
2615 customfontfallback_Release,
2616 fontfallback_MapCharacters,
2617 fontfallback1_MapCharacters,
2620 static HRESULT WINAPI fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder *iface, REFIID riid, void **obj)
2622 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2624 if (IsEqualIID(riid, &IID_IDWriteFontFallbackBuilder) || IsEqualIID(riid, &IID_IUnknown)) {
2625 *obj = iface;
2626 IDWriteFontFallbackBuilder_AddRef(iface);
2627 return S_OK;
2630 WARN("%s not implemented.\n", debugstr_guid(riid));
2632 *obj = NULL;
2633 return E_NOINTERFACE;
2636 static ULONG WINAPI fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder *iface)
2638 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2639 ULONG refcount = InterlockedIncrement(&fallbackbuilder->refcount);
2641 TRACE("%p, refcount %ld.\n", iface, refcount);
2643 return refcount;
2646 static ULONG WINAPI fontfallbackbuilder_Release(IDWriteFontFallbackBuilder *iface)
2648 struct dwrite_fontfallback_builder *builder = impl_from_IDWriteFontFallbackBuilder(iface);
2649 ULONG refcount = InterlockedDecrement(&builder->refcount);
2651 TRACE("%p, refcount %ld.\n", iface, refcount);
2653 if (!refcount)
2655 IDWriteFactory7_Release(builder->factory);
2656 release_fallback_data(&builder->data);
2657 free(builder);
2660 return refcount;
2663 static struct fallback_locale * fallback_builder_add_locale(struct dwrite_fontfallback_builder *builder,
2664 const WCHAR *locale_name)
2666 struct fallback_locale *locale;
2668 if (!locale_name) locale_name = L"";
2669 if ((locale = font_fallback_get_locale(&builder->data.locales, locale_name))) return locale;
2670 if (!(locale = calloc(1, sizeof(*locale)))) return NULL;
2671 lstrcpynW(locale->name, locale_name, ARRAY_SIZE(locale->name));
2672 list_add_tail(&builder->data.locales, &locale->entry);
2673 return locale;
2676 static HRESULT WINAPI fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder *iface,
2677 const DWRITE_UNICODE_RANGE *ranges, UINT32 ranges_count, WCHAR const **families, UINT32 families_count,
2678 IDWriteFontCollection *collection, WCHAR const *locale_name, WCHAR const *base_family, float scale)
2680 struct dwrite_fontfallback_builder *builder = impl_from_IDWriteFontFallbackBuilder(iface);
2681 struct fallback_mapping *mapping;
2682 struct fallback_locale *locale;
2683 unsigned int i, j, m, count;
2685 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface, ranges, ranges_count, families, families_count,
2686 collection, debugstr_w(locale_name), debugstr_w(base_family), scale);
2688 if (!ranges || !ranges_count || !families || !families_count || scale < 0.0f)
2689 return E_INVALIDARG;
2691 if (base_family)
2692 FIXME("base family ignored.\n");
2694 if (!dwrite_array_reserve((void **)&builder->data.mappings, &builder->mappings_size,
2695 builder->data.count + 1, sizeof(*builder->data.mappings)))
2697 return E_OUTOFMEMORY;
2700 mapping = &builder->data.mappings[builder->data.count];
2701 memset(mapping, 0, sizeof(*mapping));
2703 /* Append new mapping, link to its locale node. */
2705 if (!(locale = fallback_builder_add_locale(builder, locale_name)))
2706 return E_FAIL;
2708 if (!(mapping->ranges = calloc(ranges_count, sizeof(*mapping->ranges))))
2709 goto failed;
2711 /* Filter ranges that won't be usable. */
2712 for (i = 0, count = 0; i < ranges_count; ++i)
2714 if (ranges[i].first > ranges[i].last) continue;
2715 if (ranges[i].first > 0x10ffff) continue;
2716 mapping->ranges[count].first = ranges[i].first;
2717 mapping->ranges[count].last = min(ranges[i].last, 0x10ffff);
2718 count++;
2720 if (!count)
2722 release_fallback_mapping(mapping);
2723 return S_OK;
2726 mapping->ranges_count = count;
2728 if (!(mapping->families = calloc(families_count, sizeof(*mapping->families))))
2729 goto failed;
2730 mapping->families_count = families_count;
2731 for (i = 0; i < families_count; i++)
2732 if (!(mapping->families[i] = wcsdup(families[i]))) goto failed;
2733 mapping->scale = scale;
2735 if (FAILED(fallback_locale_add_mapping(locale, builder->data.count))) goto failed;
2737 /* Mappings with explicit collections take priority, for that reduce existing mappings ranges
2738 by newly added ranges. */
2740 mapping->collection = collection;
2741 if (mapping->collection)
2743 IDWriteFontCollection_AddRef(mapping->collection);
2745 for (m = 0; m < builder->data.count; ++m)
2747 struct fallback_mapping *c = &builder->data.mappings[m];
2748 if (c->collection) continue;
2749 for (i = 0; i < count; ++i)
2751 const DWRITE_UNICODE_RANGE *new_range = &mapping->ranges[i];
2753 for (j = 0; j < c->ranges_count; ++j)
2755 DWRITE_UNICODE_RANGE *range = &c->ranges[j];
2757 /* In case existing ranges intersect, disable or reduce them */
2758 if (range->first >= new_range->first && range->last <= new_range->last)
2760 range->first = range->last = ~0u;
2762 else if (range->first >= new_range->first && range->first <= new_range->last)
2764 range->first = new_range->last;
2766 else if (range->last >= new_range->first && range->last <= new_range->last)
2768 range->last = new_range->first;
2775 builder->data.count++;
2776 return S_OK;
2778 failed:
2779 release_fallback_mapping(mapping);
2780 return E_OUTOFMEMORY;
2783 static HRESULT WINAPI fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder *iface, IDWriteFontFallback *fallback)
2785 FIXME("%p, %p stub.\n", iface, fallback);
2787 return E_NOTIMPL;
2790 static HRESULT fallbackbuilder_init_fallback_data(const struct dwrite_fontfallback_builder *builder,
2791 struct fallback_data *data)
2793 struct fallback_locale *iter, *locale;
2794 size_t i, j;
2796 /* Duplicate locales list. */
2797 list_init(&data->locales);
2798 LIST_FOR_EACH_ENTRY(iter, &builder->data.locales, struct fallback_locale, entry)
2800 if (!(locale = calloc(1, sizeof(*locale)))) goto failed;
2801 wcscpy(locale->name, iter->name);
2802 locale->ranges.count = iter->ranges.count;
2803 locale->ranges.size = iter->ranges.count;
2804 if (!(locale->ranges.data = malloc(iter->ranges.count * sizeof(*iter->ranges.data))))
2806 free(locale);
2807 goto failed;
2809 memcpy(locale->ranges.data, iter->ranges.data, iter->ranges.count * sizeof(*iter->ranges.data));
2810 list_add_tail(&data->locales, &locale->entry);
2813 /* Duplicate mappings. */
2814 if (!(data->mappings = calloc(builder->data.count, sizeof(*data->mappings))))
2815 goto failed;
2817 data->count = builder->data.count;
2818 for (i = 0; i < data->count; ++i)
2820 struct fallback_mapping *src = &builder->data.mappings[i];
2821 struct fallback_mapping *dst = &data->mappings[i];
2823 if (!(dst->ranges = calloc(src->ranges_count, sizeof(*src->ranges)))) goto failed;
2824 memcpy(dst->ranges, src->ranges, src->ranges_count * sizeof(*src->ranges));
2825 dst->ranges_count = src->ranges_count;
2827 if (!(dst->families = calloc(src->families_count, sizeof(*src->families)))) goto failed;
2828 dst->families_count = src->families_count;
2829 for (j = 0; j < src->families_count; ++j)
2831 if (!(dst->families[j] = wcsdup(src->families[j]))) goto failed;
2834 dst->collection = src->collection;
2835 if (dst->collection)
2836 IDWriteFontCollection_AddRef(dst->collection);
2837 dst->scale = src->scale;
2840 return S_OK;
2842 failed:
2844 return E_OUTOFMEMORY;
2847 static HRESULT fallbackbuilder_create_fallback(struct dwrite_fontfallback_builder *builder, struct dwrite_fontfallback **ret)
2849 struct dwrite_fontfallback *fallback;
2850 HRESULT hr;
2852 if (!(fallback = calloc(1, sizeof(*fallback))))
2853 return E_OUTOFMEMORY;
2855 fallback->IDWriteFontFallback1_iface.lpVtbl = &customfontfallbackvtbl;
2856 fallback->refcount = 1;
2857 fallback->factory = builder->factory;
2858 IDWriteFactory7_AddRef(fallback->factory);
2859 if (FAILED(hr = IDWriteFactory_GetSystemFontCollection((IDWriteFactory *)fallback->factory,
2860 &fallback->systemcollection, FALSE)))
2862 goto done;
2865 if (FAILED(hr = fallbackbuilder_init_fallback_data(builder, &fallback->data)))
2867 goto done;
2870 *ret = fallback;
2872 return S_OK;
2874 done:
2876 IDWriteFontFallback1_Release(&fallback->IDWriteFontFallback1_iface);
2877 return hr;
2880 static HRESULT WINAPI fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder *iface,
2881 IDWriteFontFallback **ret)
2883 struct dwrite_fontfallback_builder *builder = impl_from_IDWriteFontFallbackBuilder(iface);
2884 struct dwrite_fontfallback *fallback;
2885 HRESULT hr;
2887 TRACE("%p, %p.\n", iface, ret);
2889 *ret = NULL;
2891 if (SUCCEEDED(hr = fallbackbuilder_create_fallback(builder, &fallback)))
2893 *ret = (IDWriteFontFallback *)&fallback->IDWriteFontFallback1_iface;
2896 return hr;
2899 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl =
2901 fontfallbackbuilder_QueryInterface,
2902 fontfallbackbuilder_AddRef,
2903 fontfallbackbuilder_Release,
2904 fontfallbackbuilder_AddMapping,
2905 fontfallbackbuilder_AddMappings,
2906 fontfallbackbuilder_CreateFontFallback,
2909 static HRESULT create_fontfallback_builder_internal(IDWriteFactory7 *factory, struct dwrite_fontfallback_builder **ret)
2911 struct dwrite_fontfallback_builder *builder;
2913 *ret = NULL;
2915 if (!(builder = calloc(1, sizeof(*builder))))
2916 return E_OUTOFMEMORY;
2918 builder->IDWriteFontFallbackBuilder_iface.lpVtbl = &fontfallbackbuildervtbl;
2919 builder->refcount = 1;
2920 builder->factory = factory;
2921 IDWriteFactory7_AddRef(builder->factory);
2922 list_init(&builder->data.locales);
2924 *ret = builder;
2926 return S_OK;
2929 HRESULT create_fontfallback_builder(IDWriteFactory7 *factory, IDWriteFontFallbackBuilder **ret)
2931 struct dwrite_fontfallback_builder *builder;
2932 HRESULT hr;
2934 *ret = NULL;
2936 if (SUCCEEDED(hr = create_fontfallback_builder_internal(factory, &builder)))
2937 *ret = &builder->IDWriteFontFallbackBuilder_iface;
2939 return hr;
2942 static void system_fallback_parse_ranges(const char *str, DWRITE_UNICODE_RANGE *ranges,
2943 unsigned int max_count, unsigned int *ret)
2945 unsigned int count = 0;
2946 char *end;
2948 while (*str && count < max_count)
2950 ranges[count].first = ranges[count].last = strtoul(str, &end, 16);
2951 if (*end == '-')
2953 str = end + 1;
2954 ranges[count].last = strtoul(str, &end, 16);
2956 str = end;
2957 if (*str == ',') str++;
2958 count++;
2961 *ret = count;
2964 static void system_fallback_parse_families(WCHAR *str, WCHAR **families, unsigned int max_count,
2965 unsigned int *ret)
2967 unsigned int count = 0;
2968 WCHAR *family, *ctx;
2970 family = wcstok_s(str, L",", &ctx);
2971 while (family && count < max_count)
2973 while (*family == ' ') family++;
2974 families[count++] = family;
2975 family = wcstok_s(NULL, L",", &ctx);
2978 *ret = count;
2981 static INIT_ONCE init_system_fallback_once = INIT_ONCE_STATIC_INIT;
2983 /* Particular factory instance used for initialization is not important, it won't be referenced by
2984 created fallback data. */
2985 static BOOL WINAPI dwrite_system_fallback_initonce(INIT_ONCE *once, void *param, void **context)
2987 struct dwrite_fontfallback_builder *builder;
2988 IDWriteFontFallbackBuilder *builder_iface;
2989 unsigned int range_count, families_count;
2990 IDWriteFactory7 *factory = param;
2991 DWRITE_UNICODE_RANGE ranges[16];
2992 WCHAR *families[4], *str;
2993 HRESULT hr;
2994 size_t i;
2996 if (FAILED(create_fontfallback_builder_internal(factory, &builder))) return FALSE;
2997 builder_iface = &builder->IDWriteFontFallbackBuilder_iface;
2999 for (i = 0; i < ARRAY_SIZE(system_fallback_config); ++i)
3001 const struct fallback_description *entry = &system_fallback_config[i];
3003 system_fallback_parse_ranges(entry->ranges, ranges, ARRAY_SIZE(ranges), &range_count);
3005 /* TODO: reuse the buffer */
3006 str = wcsdup(entry->families);
3007 system_fallback_parse_families(str, families, ARRAY_SIZE(families), &families_count);
3009 if (FAILED(hr = IDWriteFontFallbackBuilder_AddMapping(builder_iface, ranges, range_count,
3010 (const WCHAR **)families, families_count, NULL, entry->locale, NULL, 1.0f)))
3012 WARN("Failed to add mapping, hr %#lx.\n", hr);
3015 free(str);
3018 hr = fallbackbuilder_init_fallback_data(builder, &system_fallback);
3019 IDWriteFontFallbackBuilder_Release(builder_iface);
3021 return hr == S_OK;
3024 void release_system_fallback_data(void)
3026 release_fallback_data(&system_fallback);
3029 HRESULT create_system_fontfallback(IDWriteFactory7 *factory, IDWriteFontFallback1 **ret)
3031 struct dwrite_fontfallback *fallback;
3033 *ret = NULL;
3035 if (!InitOnceExecuteOnce(&init_system_fallback_once, dwrite_system_fallback_initonce, factory, NULL))
3037 WARN("Failed to initialize system fallback data.\n");
3038 return E_FAIL;
3041 if (!(fallback = calloc(1, sizeof(*fallback))))
3042 return E_OUTOFMEMORY;
3044 fallback->IDWriteFontFallback1_iface.lpVtbl = &fontfallbackvtbl;
3045 fallback->factory = factory;
3046 fallback->data.count = ~0u;
3047 IDWriteFactory_GetSystemFontCollection((IDWriteFactory *)fallback->factory, &fallback->systemcollection, FALSE);
3049 *ret = &fallback->IDWriteFontFallback1_iface;
3051 return S_OK;