wineandroid: Load ntdll from the architecture-specific directory.
[wine.git] / dlls / dwrite / analyzer.c
blob93d507cf3dcbdb904c68051f6ec941eb0a5e9b8e
1 /*
2 * Text analyzer
4 * Copyright 2011 Aric Stewart for CodeWeavers
5 * Copyright 2012, 2014 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include <math.h>
26 #include "dwrite_private.h"
27 #include "scripts.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
31 extern const unsigned short wine_linebreak_table[] DECLSPEC_HIDDEN;
32 extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN;
34 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
35 #define NATIVE_DIGITS_LEN 11
37 struct dwritescript_properties
39 DWRITE_SCRIPT_PROPERTIES props;
40 UINT32 scripttags[3]; /* Maximum 2 script tags, 0-terminated. */
41 BOOL is_complex;
44 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
46 /* NOTE: keep this array synced with script ids from scripts.h */
47 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
48 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
49 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
50 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
51 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('a','r','a','b') }, TRUE },
52 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','n') } },
53 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','v','s','t') } },
54 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('b','a','l','i') } },
55 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','m','u') } },
56 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','t','k') } },
57 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('b','n','g','2'), _OT('b','e','n','g') }, TRUE },
58 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('b','o','p','o') } },
59 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','h') } },
60 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','i') }, TRUE },
61 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','u','g','i') } },
62 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('b','u','h','d') } },
63 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','n','s') }, TRUE },
64 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('c','a','r','i') } },
65 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('c','h','a','m') } },
66 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','h','e','r') }, TRUE },
67 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','o','p','t') } },
68 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('x','s','u','x') } },
69 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('c','p','r','t') } },
70 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','y','r','l') } },
71 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('d','s','r','t') }, TRUE },
72 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('d','e','v','2'), _OT('d','e','v','a') }, TRUE },
73 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('e','g','y','p') } },
74 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','t','h','i') }, TRUE },
75 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','e','o','r') } },
76 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','l','a','g') } },
77 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','t','h') } },
78 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','e','k') } },
79 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','j','r','2'), _OT('g','u','j','r') }, TRUE },
80 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','u','r','2'), _OT('g','u','r','u') }, TRUE },
81 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('h','a','n','i') } },
82 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, { _OT('h','a','n','g') }, TRUE },
83 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('h','a','n','o') } },
84 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('h','e','b','r') }, TRUE },
85 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
86 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','i') } },
87 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','h','l','i') } },
88 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','r','t','i') } },
89 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('j','a','v','a') } },
90 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('k','t','h','i') } },
91 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','n','d','2'), _OT('k','n','d','a') }, TRUE },
92 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
93 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('k','a','l','i') } },
94 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('k','h','a','r') } },
95 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('k','h','m','r') }, TRUE },
96 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('l','a','o',' ') }, TRUE },
97 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','a','t','n') } },
98 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','e','p','c') } },
99 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','m','b') } },
100 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','b') } },
101 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','s','u') } },
102 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','c','i') } },
103 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','d','i') } },
104 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','l','m','2'), _OT('m','l','y','m') }, TRUE },
105 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','d') } },
106 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','t','e','i') } },
107 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','o','n','g') }, TRUE },
108 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','y','m','r') }, TRUE },
109 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','u') }, TRUE },
110 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('n','k','o',' ') }, TRUE },
111 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, { _OT('o','g','a','m') }, TRUE },
112 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','l','c','k') } },
113 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('i','t','a','l') } },
114 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('x','p','e','o') }, TRUE },
115 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','r','b') } },
116 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','k','h') } },
117 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','y','2'), _OT('o','r','y','a') }, TRUE },
118 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','m','a') }, TRUE },
119 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','a','g') }, TRUE },
120 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('p','h','n','x') } },
121 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('r','j','n','g') } },
122 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('r','u','n','r') }, TRUE },
123 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','m','r') } },
124 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','u','r') } },
125 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','a','w') } },
126 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','h') }, TRUE },
127 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','u','n','d') } },
128 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('s','y','l','o') } },
129 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('s','y','r','c') }, TRUE },
130 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','g','l','g') } },
131 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','g','b') } },
132 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','e') }, TRUE },
133 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('l','a','n','a') } },
134 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','a','v','t') } },
135 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','m','l','2'), _OT('t','a','m','l') }, TRUE },
136 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','e','l','2'), _OT('t','e','l','u') }, TRUE },
137 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','h','a','a') }, TRUE },
138 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','h','a','i') }, TRUE },
139 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','i','b','t') }, TRUE },
140 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','f','n','g') }, TRUE },
141 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('u','g','a','r') } },
142 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('v','a','i',' ') }, TRUE },
143 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('y','i',' ',' ') }, TRUE },
144 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','k','m') } },
145 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','r','c') } },
146 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('m','e','r','o') } },
147 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('p','l','r','d') } },
148 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','r','d') } },
149 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','r','a') } },
150 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','k','r') } },
151 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','s','s') } },
152 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','g','h','b') } },
153 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('d','u','p','l') } },
154 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','l','b','a') } },
155 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','a','n') } },
156 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','h','o','j') } },
157 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','d') } },
158 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','a') } },
159 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','h','j') } },
160 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','i') } },
161 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','n','d') } },
162 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('m','o','d','i') } },
163 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','r','o','o') } },
164 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('n','b','a','t') } },
165 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','a','r','b') } },
166 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','e','r','m') } },
167 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','m','n','g') } },
168 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','l','m') } },
169 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','u','c') } },
170 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','l','p') } },
171 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('s','i','d','d') } },
172 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('t','i','r','h') } },
173 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('w','a','r','a') } },
174 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','d','l','m') } },
175 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','h','o','m') } },
176 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','l','u','w') } },
177 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','h','k','s') } },
178 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','a','t','r') } },
179 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','r','c') } },
180 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','u','l','t') } },
181 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','e','w','a') } },
182 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','u','n','g') } },
183 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','g','e') } },
184 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','g','n','w') } },
185 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','n','g') } },
186 { /* Gonm */ { 0x6d6e6f47, 313, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','n','m') } },
187 { /* Nshu */ { 0x7568734e, 499, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('n','s','h','u') } },
188 { /* Soyo */ { 0x6f796f53, 329, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','y','o') } },
189 { /* Zanb */ { 0x626e615a, 339, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('z','a','n','b') } },
190 { /* Dogr */ { 0x72676f44, 328, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('d','o','g','r') } },
191 { /* Gong */ { 0x676e6f47, 312, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','o','n','g') } },
192 { /* Rohg */ { 0x67686f52, 167, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('r','o','h','g') } },
193 { /* Maka */ { 0x616b614d, 366, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','k','a') } },
194 { /* Medf */ { 0x6664654d, 265, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','d','f') } },
195 { /* Sogo */ { 0x6f676f53, 142, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','g','o') } },
196 { /* Sogd */ { 0x64676f53, 141, 8, 0x0020, 1, 1, 0, 0, 0, 1, 1 }, { _OT('s','o','g','d') } },
197 { /* Elym */ { 0x6d796c45, 128, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('e','l','y','m') } },
198 { /* Hmnp */ { 0x706e6d48, 451, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('h','m','n','p') } },
199 { /* Nand */ { 0x646e614e, 311, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('n','a','n','d') } },
200 { /* Wcho */ { 0x6f686357, 283, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('w','c','h','o') } },
202 #undef _OT
204 const char *debugstr_sa_script(UINT16 script)
206 return script < Script_LastId ? debugstr_tag(dwritescripts_properties[script].props.isoScriptCode) : "undefined";
209 /* system font falback configuration */
210 static const WCHAR *cjk_families[] = { L"Meiryo" };
212 static const DWRITE_UNICODE_RANGE cjk_ranges[] =
214 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
215 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
216 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
219 struct fallback_mapping {
220 DWRITE_UNICODE_RANGE *ranges;
221 UINT32 ranges_count;
222 WCHAR **families;
223 UINT32 families_count;
224 IDWriteFontCollection *collection;
225 WCHAR *locale;
226 FLOAT scale;
229 static const struct fallback_mapping fontfallback_neutral_data[] = {
230 #define MAPPING_RANGE(ranges, families) \
231 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
232 (WCHAR **)families, ARRAY_SIZE(families) }
234 MAPPING_RANGE(cjk_ranges, cjk_families),
236 #undef MAPPING_RANGE
239 struct dwrite_fontfallback
241 IDWriteFontFallback1 IDWriteFontFallback1_iface;
242 LONG refcount;
243 IDWriteFactory7 *factory;
244 IDWriteFontCollection1 *systemcollection;
245 struct fallback_mapping *mappings;
246 UINT32 mappings_count;
249 struct dwrite_fontfallback_builder
251 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface;
252 LONG refcount;
253 IDWriteFactory7 *factory;
254 struct fallback_mapping *mappings;
255 size_t size;
256 size_t count;
259 struct dwrite_numbersubstitution
261 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
262 LONG refcount;
264 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
265 WCHAR *locale;
266 BOOL ignore_user_override;
269 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
271 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
274 static struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface);
276 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback1(IDWriteFontFallback1 *iface)
278 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback1_iface);
281 static inline struct dwrite_fontfallback_builder *impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder *iface)
283 return CONTAINING_RECORD(iface, struct dwrite_fontfallback_builder, IDWriteFontFallbackBuilder_iface);
286 static inline UINT16 get_char_script(WCHAR c)
288 UINT16 script = get_table_entry(wine_scripts_table, c);
289 return script == Script_Inherited ? Script_Unknown : script;
292 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
294 DWRITE_SCRIPT_ANALYSIS sa;
295 WORD type;
297 GetStringTypeW(CT_CTYPE1, &c, 1, &type);
298 sa.script = get_char_script(c);
299 sa.shapes = (type & C1_CNTRL) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
300 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
301 return sa;
304 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
306 DWRITE_SCRIPT_ANALYSIS sa;
307 UINT32 pos, i, seq_length;
309 if (!length)
310 return S_OK;
312 sa = get_char_sa(*text);
314 pos = position;
315 seq_length = 1;
317 for (i = 1; i < length; i++)
319 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
321 /* Unknown type is ignored when preceded or followed by another script */
322 switch (sa.script) {
323 case Script_Unknown:
324 sa.script = cur_sa.script;
325 break;
326 case Script_Common:
327 if (cur_sa.script == Script_Unknown)
328 cur_sa.script = sa.script;
329 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
330 sa.script = cur_sa.script;
331 break;
332 default:
333 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
334 cur_sa.script = sa.script;
337 /* this is a length of a sequence to be reported next */
338 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
339 seq_length++;
340 else {
341 HRESULT hr;
343 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
344 if (FAILED(hr)) return hr;
345 pos = position + i;
346 seq_length = 1;
347 sa = cur_sa;
351 /* one char length case or normal completion call */
352 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
355 struct linebreaking_state {
356 DWRITE_LINE_BREAKPOINT *breakpoints;
357 UINT32 count;
360 enum BreakConditionLocation {
361 BreakConditionBefore,
362 BreakConditionAfter
365 enum linebreaking_classes {
366 b_BK = 1,
367 b_CR,
368 b_LF,
369 b_CM,
370 b_SG,
371 b_GL,
372 b_CB,
373 b_SP,
374 b_ZW,
375 b_NL,
376 b_WJ,
377 b_JL,
378 b_JV,
379 b_JT,
380 b_H2,
381 b_H3,
382 b_XX,
383 b_OP,
384 b_CL,
385 b_CP,
386 b_QU,
387 b_NS,
388 b_EX,
389 b_SY,
390 b_IS,
391 b_PR,
392 b_PO,
393 b_NU,
394 b_AL,
395 b_ID,
396 b_IN,
397 b_HY,
398 b_BB,
399 b_BA,
400 b_SA,
401 b_AI,
402 b_B2,
403 b_HL,
404 b_CJ,
405 b_RI,
406 b_EB,
407 b_EM,
408 b_ZWJ,
411 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
413 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
414 return TRUE;
416 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
417 return TRUE;
419 return FALSE;
422 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
423 set to "can break" and could only be changed once. */
424 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
425 struct linebreaking_state *state)
427 if (location == BreakConditionBefore) {
428 if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
429 return;
430 state->breakpoints[pos].breakConditionBefore = condition;
431 if (pos > 0)
432 state->breakpoints[pos-1].breakConditionAfter = condition;
434 else {
435 if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
436 return;
437 state->breakpoints[pos].breakConditionAfter = condition;
438 if (pos + 1 < state->count)
439 state->breakpoints[pos+1].breakConditionBefore = condition;
443 BOOL lb_is_newline_char(WCHAR ch)
445 short c = get_table_entry(wine_linebreak_table, ch);
446 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
449 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
451 struct linebreaking_state state;
452 short *break_class;
453 int i, j;
455 break_class = heap_calloc(count, sizeof(*break_class));
456 if (!break_class)
457 return E_OUTOFMEMORY;
459 state.breakpoints = breakpoints;
460 state.count = count;
462 for (i = 0; i < count; i++)
464 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
466 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
467 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
468 breakpoints[i].isWhitespace = !!iswspace(text[i]);
469 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
470 breakpoints[i].padding = 0;
472 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
473 switch (break_class[i])
475 case b_AI:
476 case b_SA:
477 case b_SG:
478 case b_XX:
479 break_class[i] = b_AL;
480 break;
481 case b_CJ:
482 break_class[i] = b_NS;
483 break;
487 /* LB2 - never break at the start */
488 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
489 /* LB3 - always break at the end. */
490 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
492 /* LB4 - LB6 - mandatory breaks. */
493 for (i = 0; i < count; i++)
495 switch (break_class[i])
497 /* LB4 - LB6 */
498 case b_CR:
499 /* LB5 - don't break CR x LF */
500 if (i < count-1 && break_class[i+1] == b_LF)
502 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
503 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
504 break;
506 case b_LF:
507 case b_NL:
508 case b_BK:
509 /* LB4 - LB5 - always break after hard breaks */
510 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
511 /* LB6 - do not break before hard breaks */
512 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
513 break;
517 /* LB7 - LB8 - explicit breaks and non-breaks */
518 for (i = 0; i < count; i++)
520 switch (break_class[i])
522 /* LB7 - do not break before spaces or zero-width space */
523 case b_SP:
524 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
525 break;
526 case b_ZW:
527 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
529 /* LB8 - break before character after zero-width space, skip spaces in-between */
530 j = i;
531 while (j < count-1 && break_class[j+1] == b_SP)
532 j++;
533 if (j < count-1 && break_class[j+1] != b_ZW)
534 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
535 break;
536 /* LB8a - do not break after ZWJ */
537 case b_ZWJ:
538 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
539 break;
543 /* LB9 - LB10 - combining marks */
544 for (i = 0; i < count; i++)
546 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
548 if (i > 0)
550 switch (break_class[i-1])
552 case b_SP:
553 case b_BK:
554 case b_CR:
555 case b_LF:
556 case b_NL:
557 case b_ZW:
558 break_class[i] = b_AL;
559 break;
560 default:
562 break_class[i] = break_class[i-1];
563 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
567 else break_class[i] = b_AL;
571 for (i = 0; i < count; i++)
573 switch (break_class[i])
575 /* LB11 - don't break before and after word joiner */
576 case b_WJ:
577 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
578 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
579 break;
580 /* LB12 - don't break after glue */
581 case b_GL:
582 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
583 /* LB12a */
584 if (i > 0)
586 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
587 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
589 break;
590 /* LB13 */
591 case b_CL:
592 case b_CP:
593 case b_EX:
594 case b_IS:
595 case b_SY:
596 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
597 break;
598 /* LB14 - do not break after OP, even after spaces */
599 case b_OP:
600 j = i;
601 while (j < count-1 && break_class[j+1] == b_SP)
602 j++;
603 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
604 break;
605 /* LB15 - do not break within QU-OP, even with intervening spaces */
606 case b_QU:
607 j = i;
608 while (j < count-1 && break_class[j+1] == b_SP)
609 j++;
610 if (j < count - 1 && break_class[j+1] == b_OP)
611 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
612 break;
613 /* LB16 */
614 case b_NS:
615 j = i-1;
616 while(j > 0 && break_class[j] == b_SP)
617 j--;
618 if (break_class[j] == b_CL || break_class[j] == b_CP)
619 for (j++; j <= i; j++)
620 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
621 break;
622 /* LB17 - do not break within B2, even with intervening spaces */
623 case b_B2:
624 j = i;
625 while (j < count && break_class[j+1] == b_SP)
626 j++;
627 if (j < count - 1 && break_class[j+1] == b_B2)
628 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
629 break;
633 for (i = 0; i < count; i++)
635 switch(break_class[i])
637 /* LB18 - break is allowed after space */
638 case b_SP:
639 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
640 break;
641 /* LB19 - don't break before or after quotation mark */
642 case b_QU:
643 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
644 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
645 break;
646 /* LB20 */
647 case b_CB:
648 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
649 if (i < count - 1 && break_class[i+1] != b_QU)
650 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
651 break;
652 /* LB21 */
653 case b_BA:
654 case b_HY:
655 case b_NS:
656 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
657 break;
658 case b_BB:
659 if (i < count - 1 && break_class[i+1] != b_CB)
660 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
661 break;
662 /* LB21a, LB21b */
663 case b_HL:
664 /* LB21a */
665 if (i < count-1)
666 switch (break_class[i+1])
668 case b_HY:
669 case b_BA:
670 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
672 /* LB21b */
673 if (i > 0 && break_class[i-1] == b_SY)
674 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
675 break;
676 /* LB22 */
677 case b_IN:
678 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
679 break;
682 if (i < count-1)
684 /* LB23 - do not break between digits and letters */
685 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
686 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
687 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
688 (break_class[i] == b_NU && break_class[i+1] == b_HL))
689 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
691 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
692 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
693 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
694 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
695 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
696 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
697 (break_class[i] == b_EB && break_class[i+1] == b_PO))
698 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
700 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
701 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
702 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
703 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
704 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
705 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
706 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
707 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
708 (break_class[i] == b_HL && break_class[i+1] == b_PO))
709 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
711 /* LB25 */
712 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
713 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
714 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
715 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
716 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
717 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
718 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
719 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
720 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
721 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
722 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
723 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
724 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
725 (break_class[i] == b_SY && break_class[i+1] == b_NU))
726 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
728 /* LB26 */
729 if (break_class[i] == b_JL)
731 switch (break_class[i+1])
733 case b_JL:
734 case b_JV:
735 case b_H2:
736 case b_H3:
737 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
740 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
741 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
742 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
743 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
744 break_class[i+1] == b_JT)
745 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
747 /* LB27 */
748 switch (break_class[i])
750 case b_JL:
751 case b_JV:
752 case b_JT:
753 case b_H2:
754 case b_H3:
755 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
756 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
758 if (break_class[i] == b_PR)
760 switch (break_class[i+1])
762 case b_JL:
763 case b_JV:
764 case b_JT:
765 case b_H2:
766 case b_H3:
767 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
771 /* LB28 */
772 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
773 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
774 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
775 (break_class[i] == b_HL && break_class[i+1] == b_HL))
776 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
778 /* LB29 */
779 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
780 (break_class[i] == b_IS && break_class[i+1] == b_HL))
781 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
783 /* LB30 */
784 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
785 break_class[i+1] == b_OP)
786 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
787 if (break_class[i] == b_CP &&
788 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
789 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
791 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
792 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
793 unsigned int c = 0;
795 j = i + 1;
796 while (j > 0 && break_class[--j] == b_RI)
797 c++;
799 if ((c & 1) == 0)
800 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
803 /* LB30b - do not break between an emoji base and an emoji modifier */
804 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
805 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
809 /* LB31 - allow breaks everywhere else. */
810 for (i = 0; i < count; i++)
812 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
813 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
816 heap_free(break_class);
817 return S_OK;
820 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
822 TRACE("%s, %p.\n", debugstr_guid(riid), obj);
824 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
825 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
826 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
827 IsEqualIID(riid, &IID_IUnknown))
829 *obj = iface;
830 return S_OK;
833 WARN("%s not implemented.\n", debugstr_guid(riid));
835 *obj = NULL;
836 return E_NOINTERFACE;
839 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
841 return 2;
844 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
846 return 1;
849 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
850 data after a first request. */
851 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
853 HRESULT hr;
854 UINT32 len;
856 *buff = NULL;
857 *text = NULL;
858 len = 0;
859 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
860 if (FAILED(hr)) return hr;
862 if (len < length) {
863 UINT32 read;
865 *buff = heap_alloc(length*sizeof(WCHAR));
866 if (!*buff)
867 return E_OUTOFMEMORY;
868 memcpy(*buff, *text, len*sizeof(WCHAR));
869 read = len;
871 while (read < length && *text) {
872 *text = NULL;
873 len = 0;
874 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
875 if (FAILED(hr)) {
876 heap_free(*buff);
877 return hr;
879 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
880 read += len;
883 *text = *buff;
886 return hr;
889 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
890 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
892 WCHAR *buff = NULL;
893 const WCHAR *text;
894 HRESULT hr;
896 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
898 if (length == 0)
899 return S_OK;
901 hr = get_text_source_ptr(source, position, length, &text, &buff);
902 if (FAILED(hr))
903 return hr;
905 hr = analyze_script(text, position, length, sink);
906 heap_free(buff);
908 return hr;
911 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
912 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
914 UINT8 *levels = NULL, *explicit = NULL;
915 UINT8 baselevel, level, explicit_level;
916 UINT32 pos, i, seq_length;
917 WCHAR *buff = NULL;
918 const WCHAR *text;
919 HRESULT hr;
921 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
923 if (!length)
924 return S_OK;
926 hr = get_text_source_ptr(source, position, length, &text, &buff);
927 if (FAILED(hr))
928 return hr;
930 levels = heap_calloc(length, sizeof(*levels));
931 explicit = heap_calloc(length, sizeof(*explicit));
933 if (!levels || !explicit) {
934 hr = E_OUTOFMEMORY;
935 goto done;
938 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
939 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
940 if (FAILED(hr))
941 goto done;
943 level = levels[0];
944 explicit_level = explicit[0];
945 pos = position;
946 seq_length = 1;
948 for (i = 1; i < length; i++) {
949 if (levels[i] == level && explicit[i] == explicit_level)
950 seq_length++;
951 else {
952 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
953 if (FAILED(hr))
954 goto done;
956 pos += seq_length;
957 seq_length = 1;
958 level = levels[i];
959 explicit_level = explicit[i];
962 /* one char length case or normal completion call */
963 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
965 done:
966 heap_free(explicit);
967 heap_free(levels);
968 heap_free(buff);
970 return hr;
973 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
974 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
976 static int once;
978 if (!once++)
979 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
980 return S_OK;
983 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
984 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
986 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
987 WCHAR *buff = NULL;
988 const WCHAR *text;
989 HRESULT hr;
990 UINT32 len;
992 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
994 if (length == 0)
995 return S_OK;
997 /* get some, check for length */
998 text = NULL;
999 len = 0;
1000 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
1001 if (FAILED(hr)) return hr;
1003 if (len < length) {
1004 UINT32 read;
1006 buff = heap_calloc(length, sizeof(*buff));
1007 if (!buff)
1008 return E_OUTOFMEMORY;
1009 memcpy(buff, text, len*sizeof(WCHAR));
1010 read = len;
1012 while (read < length && text) {
1013 text = NULL;
1014 len = 0;
1015 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
1016 if (FAILED(hr))
1017 goto done;
1018 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
1019 read += len;
1022 text = buff;
1025 breakpoints = heap_calloc(length, sizeof(*breakpoints));
1026 if (!breakpoints) {
1027 hr = E_OUTOFMEMORY;
1028 goto done;
1031 hr = analyze_linebreaks(text, length, breakpoints);
1032 if (FAILED(hr))
1033 goto done;
1035 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1037 done:
1038 heap_free(breakpoints);
1039 heap_free(buff);
1041 return hr;
1044 static UINT32 get_opentype_language(const WCHAR *locale)
1046 UINT32 language = DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1048 if (locale) {
1049 WCHAR tag[5];
1050 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, ARRAY_SIZE(tag)))
1051 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1054 return language;
1057 static void get_number_substitutes(IDWriteNumberSubstitution *substitution, BOOL is_rtl, WCHAR *digits)
1059 struct dwrite_numbersubstitution *numbersubst = unsafe_impl_from_IDWriteNumberSubstitution(substitution);
1060 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1061 WCHAR isolang[9];
1062 DWORD lctype;
1064 digits[0] = 0;
1066 if (!numbersubst)
1067 return;
1069 lctype = numbersubst->ignore_user_override ? LOCALE_NOUSEROVERRIDE : 0;
1071 if (numbersubst->method == DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE) {
1072 DWORD value;
1074 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1075 if (GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, (WCHAR *)&value, 2)) {
1076 switch (value)
1078 case 0:
1079 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL;
1080 break;
1081 case 2:
1082 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL;
1083 break;
1084 case 1:
1085 default:
1086 if (value != 1)
1087 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value, debugstr_w(numbersubst->locale));
1090 else
1091 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst->locale));
1093 else
1094 method = numbersubst->method;
1096 switch (method)
1098 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL:
1099 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1100 break;
1101 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1102 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL:
1103 if (GetLocaleInfoEx(numbersubst->locale, LOCALE_SISO639LANGNAME, isolang, ARRAY_SIZE(isolang)))
1105 static const WCHAR arabicW[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1107 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1108 if (!wcscmp(L"ar", isolang))
1110 wcscpy(digits, arabicW);
1111 break;
1114 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1115 break;
1116 default:
1120 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !*digits) {
1121 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst->locale), method);
1122 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1125 if ((method == DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL && !is_rtl) || method == DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE)
1126 digits[0] = 0;
1129 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES const **features,
1130 UINT32 const *feature_range_lengths, UINT32 feature_ranges)
1132 UINT32 i, j, start;
1134 if (!TRACE_ON(dwrite) || !features)
1135 return;
1137 for (i = 0, start = 0; i < feature_ranges; start += feature_range_lengths[i++]) {
1138 TRACE("feature range [%u,%u)\n", start, start + feature_range_lengths[i]);
1139 for (j = 0; j < features[i]->featureCount; j++)
1140 TRACE("feature %s, parameter %u\n", debugstr_tag(features[i]->features[j].nameTag),
1141 features[i]->features[j].parameter);
1145 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1146 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1147 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1148 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1149 UINT32 const* feature_range_lengths, UINT32 feature_ranges, UINT32 max_glyph_count,
1150 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16 *glyphs,
1151 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1153 const struct dwritescript_properties *scriptprops;
1154 struct scriptshaping_context context = { 0 };
1155 struct dwrite_fontface *font_obj;
1156 WCHAR digits[NATIVE_DIGITS_LEN];
1157 unsigned int glyph_count;
1158 HRESULT hr;
1160 TRACE("%s:%u, %p, %d, %d, %s, %s, %p, %p, %p, %u, %u, %p, %p, %p, %p, %p.\n", debugstr_wn(text, length),
1161 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1162 features, feature_range_lengths, feature_ranges, max_glyph_count, clustermap, text_props, glyphs,
1163 glyph_props, actual_glyph_count);
1165 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1167 get_number_substitutes(substitution, is_rtl, digits);
1168 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1169 glyph_count = max(max_glyph_count, length);
1171 context.cache = fontface_get_shaping_cache(font_obj);
1172 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1173 context.text = text;
1174 context.length = length;
1175 context.is_rtl = is_rtl;
1176 context.is_sideways = is_sideways;
1177 context.u.subst.glyphs = heap_calloc(glyph_count, sizeof(*glyphs));
1178 context.u.subst.glyph_props = heap_calloc(glyph_count, sizeof(*glyph_props));
1179 context.u.subst.text_props = text_props;
1180 context.u.subst.clustermap = clustermap;
1181 context.u.subst.max_glyph_count = max_glyph_count;
1182 context.u.subst.capacity = glyph_count;
1183 context.u.subst.digits = digits;
1184 context.language_tag = get_opentype_language(locale);
1185 context.user_features.features = features;
1186 context.user_features.range_lengths = feature_range_lengths;
1187 context.user_features.range_count = feature_ranges;
1188 context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos));
1189 context.table = &context.cache->gsub;
1191 *actual_glyph_count = 0;
1193 scriptprops = &dwritescripts_properties[context.script];
1194 hr = shape_get_glyphs(&context, scriptprops->scripttags);
1195 if (SUCCEEDED(hr))
1197 *actual_glyph_count = context.glyph_count;
1198 memcpy(glyphs, context.u.subst.glyphs, context.glyph_count * sizeof(*glyphs));
1199 memcpy(glyph_props, context.u.subst.glyph_props, context.glyph_count * sizeof(*glyph_props));
1202 heap_free(context.u.subst.glyph_props);
1203 heap_free(context.u.subst.glyphs);
1204 heap_free(context.glyph_infos);
1206 return hr;
1209 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1210 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1211 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1212 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, BOOL is_sideways, BOOL is_rtl,
1213 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1214 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1216 const struct dwritescript_properties *scriptprops;
1217 struct scriptshaping_context context;
1218 struct dwrite_fontface *font_obj;
1219 unsigned int i;
1220 HRESULT hr;
1222 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),
1223 clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1224 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_lengths,
1225 feature_ranges, advances, offsets);
1227 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1229 if (glyph_count == 0)
1230 return S_OK;
1232 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1234 for (i = 0; i < glyph_count; ++i)
1236 if (glyph_props[i].isZeroWidthSpace)
1237 advances[i] = 0.0f;
1238 else
1239 advances[i] = fontface_get_scaled_design_advance(font_obj, DWRITE_MEASURING_MODE_NATURAL, emSize, 1.0f,
1240 NULL, glyphs[i], is_sideways);
1241 offsets[i].advanceOffset = 0.0f;
1242 offsets[i].ascenderOffset = 0.0f;
1245 context.cache = fontface_get_shaping_cache(font_obj);
1246 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1247 context.text = text;
1248 context.length = text_len;
1249 context.is_rtl = is_rtl;
1250 context.is_sideways = is_sideways;
1251 context.u.pos.glyphs = glyphs;
1252 context.u.pos.glyph_props = glyph_props;
1253 context.u.pos.text_props = text_props;
1254 context.u.pos.clustermap = clustermap;
1255 context.glyph_count = glyph_count;
1256 context.emsize = emSize;
1257 context.measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
1258 context.advances = advances;
1259 context.offsets = offsets;
1260 context.language_tag = get_opentype_language(locale);
1261 context.user_features.features = features;
1262 context.user_features.range_lengths = feature_range_lengths;
1263 context.user_features.range_count = feature_ranges;
1264 context.glyph_infos = heap_alloc_zero(sizeof(*context.glyph_infos) * glyph_count);
1265 context.table = &context.cache->gpos;
1267 scriptprops = &dwritescripts_properties[context.script];
1268 hr = shape_get_positions(&context, scriptprops->scripttags);
1270 heap_free(context.glyph_infos);
1272 return hr;
1275 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1276 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1277 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1278 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, float ppdip,
1279 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1280 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1281 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1283 const struct dwritescript_properties *scriptprops;
1284 struct scriptshaping_context context;
1285 DWRITE_MEASURING_MODE measuring_mode;
1286 struct dwrite_fontface *font_obj;
1287 unsigned int i;
1288 HRESULT hr;
1290 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1291 debugstr_wn(text, text_len), clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface,
1292 emSize, ppdip, transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script),
1293 debugstr_w(locale), features, feature_range_lengths, feature_ranges, advances, offsets);
1295 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1297 if (glyph_count == 0)
1298 return S_OK;
1300 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1302 measuring_mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
1304 for (i = 0; i < glyph_count; ++i)
1306 if (glyph_props[i].isZeroWidthSpace)
1307 advances[i] = 0.0f;
1308 else
1309 advances[i] = fontface_get_scaled_design_advance(font_obj, measuring_mode, emSize, ppdip,
1310 transform, glyphs[i], is_sideways);
1311 offsets[i].advanceOffset = 0.0f;
1312 offsets[i].ascenderOffset = 0.0f;
1315 context.cache = fontface_get_shaping_cache(font_obj);
1316 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1317 context.text = text;
1318 context.length = text_len;
1319 context.is_rtl = is_rtl;
1320 context.is_sideways = is_sideways;
1321 context.u.pos.glyphs = glyphs;
1322 context.u.pos.glyph_props = glyph_props;
1323 context.u.pos.text_props = text_props;
1324 context.u.pos.clustermap = clustermap;
1325 context.glyph_count = glyph_count;
1326 context.emsize = emSize * ppdip;
1327 context.measuring_mode = measuring_mode;
1328 context.advances = advances;
1329 context.offsets = offsets;
1330 context.language_tag = get_opentype_language(locale);
1331 context.user_features.features = features;
1332 context.user_features.range_lengths = feature_range_lengths;
1333 context.user_features.range_count = feature_ranges;
1334 context.glyph_infos = heap_alloc_zero(sizeof(*context.glyph_infos) * glyph_count);
1335 context.table = &context.cache->gpos;
1337 scriptprops = &dwritescripts_properties[context.script];
1338 hr = shape_get_positions(&context, scriptprops->scripttags);
1340 heap_free(context.glyph_infos);
1342 return hr;
1345 static HRESULT apply_cluster_spacing(float leading_spacing, float trailing_spacing, float min_advance_width,
1346 unsigned int start, unsigned int end, float const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1347 DWRITE_SHAPING_GLYPH_PROPERTIES const *glyph_props, float *modified_advances,
1348 DWRITE_GLYPH_OFFSET *modified_offsets)
1350 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1351 unsigned int first_spacing, last_spacing, i;
1352 float advance, origin = 0.0f, *deltas;
1353 BOOL is_spacing_cluster = FALSE;
1355 if (modified_advances != advances)
1356 memcpy(&modified_advances[start], &advances[start], (end - start + 1) * sizeof(*advances));
1357 if (modified_offsets != offsets)
1358 memcpy(&modified_offsets[start], &offsets[start], (end - start + 1) * sizeof(*offsets));
1360 for (first_spacing = start; first_spacing <= end; ++first_spacing)
1362 if ((is_spacing_cluster = !glyph_props[first_spacing].isZeroWidthSpace))
1363 break;
1366 /* Nothing to adjust if there is no spacing glyphs. */
1367 if (!is_spacing_cluster)
1368 return S_OK;
1370 for (last_spacing = end; last_spacing >= start; --last_spacing)
1372 if (!glyph_props[last_spacing].isZeroWidthSpace)
1373 break;
1376 deltas = heap_calloc(end - start + 1, sizeof(*deltas));
1377 if (!deltas)
1378 return E_OUTOFMEMORY;
1380 /* Cluster advance, note that properties are ignored. */
1381 origin = offsets[start].advanceOffset;
1382 for (i = start, advance = 0.0f; i <= end; ++i)
1384 float cur = advance + offsets[i].advanceOffset;
1386 deltas[i - start] = cur - origin;
1388 advance += advances[i];
1389 origin = cur;
1392 /* Negative spacing. */
1393 if (leading_spacing < 0.0f)
1395 advance += leading_spacing;
1396 modified_advances[first_spacing] += leading_spacing;
1397 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1400 if (trailing_spacing < 0.0f)
1402 advance += trailing_spacing;
1403 modified_advances[last_spacing] += trailing_spacing;
1406 /* Minimal advance. */
1407 advance = min_advance_width - advance;
1408 if (advance > 0.0f) {
1409 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1410 float half = advance / 2.0f;
1412 if (!reduced)
1414 modified_advances[first_spacing] += half;
1415 modified_advances[last_spacing] += half;
1416 modified_offsets[first_spacing].advanceOffset += half;
1418 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1420 modified_advances[first_spacing] += half;
1421 modified_advances[last_spacing] += half;
1422 modified_offsets[first_spacing].advanceOffset += half;
1424 else if (leading_spacing < 0.0f)
1426 modified_advances[first_spacing] += advance;
1427 modified_offsets[first_spacing].advanceOffset += advance;
1429 else
1430 modified_advances[last_spacing] += advance;
1433 /* Positive spacing. */
1434 if (leading_spacing > 0.0f)
1436 modified_advances[first_spacing] += leading_spacing;
1437 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1440 if (trailing_spacing > 0.0f)
1441 modified_advances[last_spacing] += trailing_spacing;
1443 /* Update offsets to preserve original relative positions within cluster. */
1444 for (i = first_spacing; i > start; --i)
1446 unsigned int cur = i - 1;
1447 modified_offsets[cur].advanceOffset = modified_advances[cur] + modified_offsets[i].advanceOffset -
1448 deltas[i - start];
1451 for (i = first_spacing + 1; i <= end; ++i)
1453 modified_offsets[i].advanceOffset = deltas[i - start] + modified_offsets[i - 1].advanceOffset -
1454 modified_advances[i - 1];
1457 heap_free(deltas);
1459 return S_OK;
1462 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1464 UINT16 g = clustermap[start];
1465 UINT32 length = 1;
1467 while (start < text_len && clustermap[++start] == g)
1468 length++;
1469 return length;
1472 /* Applies spacing adjustments to clusters.
1474 Adjustments are applied in the following order:
1476 1. Negative adjustments
1478 Leading and trailing spacing could be negative, at this step
1479 only negative ones are actually applied. Leading spacing is only
1480 applied to leading glyph, trailing - to trailing glyph.
1482 2. Minimum advance width
1484 Advances could only be reduced at this point or unchanged. In any
1485 case it's checked if cluster advance width is less than minimum width.
1486 If it's the case advance width is incremented up to minimum value.
1488 Important part is the direction in which this increment is applied;
1489 it depends on direction from which total cluster advance was trimmed
1490 at step 1. So it could be incremented from leading, trailing, or both
1491 sides. When applied to both sides, each side gets half of difference
1492 that brings advance to minimum width.
1494 3. Positive adjustments
1496 After minimum width rule was applied, positive spacing is applied in the same
1497 way as negative one on step 1.
1499 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1500 keeps its position in coordinate system where initial advance width is counted
1501 from 0.
1503 Glyph properties
1505 It's known that isZeroWidthSpace property keeps initial advance from changing.
1508 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1509 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1510 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1511 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1513 unsigned int i;
1515 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing, trailing_spacing, min_advance_width,
1516 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1518 if (min_advance_width < 0.0f) {
1519 if (modified_advances != advances)
1520 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1521 return E_INVALIDARG;
1524 for (i = 0; i < len;)
1526 unsigned int length = get_cluster_length(clustermap, i, len);
1527 unsigned int start, end;
1529 start = clustermap[i];
1530 end = i + length < len ? clustermap[i + length] : glyph_count;
1532 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width, start, end - 1, advances,
1533 offsets, props, modified_advances, modified_offsets);
1535 i += length;
1538 return S_OK;
1541 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *fontface,
1542 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1543 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1545 struct dwrite_fontface *font_obj;
1546 const DWRITE_FONT_METRICS1 *metrics;
1548 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1549 baseline_coord, exists);
1551 *exists = FALSE;
1552 *baseline_coord = 0;
1554 if (baseline == DWRITE_BASELINE_DEFAULT)
1555 baseline = vertical ? DWRITE_BASELINE_CENTRAL : DWRITE_BASELINE_ROMAN;
1557 if ((unsigned int)baseline > DWRITE_BASELINE_MAXIMUM)
1558 return E_INVALIDARG;
1560 /* TODO: fetch BASE table data if available. */
1562 if (!*exists && is_simulation_allowed)
1564 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1565 metrics = &font_obj->metrics;
1567 switch (baseline)
1569 case DWRITE_BASELINE_ROMAN:
1570 *baseline_coord = vertical ? metrics->descent : 0;
1571 break;
1572 case DWRITE_BASELINE_CENTRAL:
1573 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1574 -(metrics->ascent - metrics->descent) / 2;
1575 break;
1576 case DWRITE_BASELINE_MATH:
1577 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1578 -(metrics->ascent + metrics->descent) / 2;
1579 break;
1580 case DWRITE_BASELINE_HANGING:
1581 /* FIXME: this one isn't accurate, but close. */
1582 *baseline_coord = vertical ? metrics->capHeight * 6 / 7 + metrics->descent : metrics->capHeight * 6 / 7;
1583 break;
1584 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM:
1585 case DWRITE_BASELINE_MINIMUM:
1586 *baseline_coord = vertical ? 0 : metrics->descent;
1587 break;
1588 case DWRITE_BASELINE_IDEOGRAPHIC_TOP:
1589 case DWRITE_BASELINE_MAXIMUM:
1590 *baseline_coord = vertical ? metrics->ascent + metrics->descent : -metrics->ascent;
1591 break;
1592 default:
1597 return S_OK;
1600 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1601 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1603 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1604 return E_NOTIMPL;
1607 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1608 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1610 TRACE("%d, %d, %p.\n", angle, is_sideways, transform);
1612 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1615 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1616 DWRITE_SCRIPT_PROPERTIES *props)
1618 TRACE("%u, %p.\n", sa.script, props);
1620 if (sa.script > Script_LastId)
1621 return E_INVALIDARG;
1623 *props = dwritescripts_properties[sa.script].props;
1624 return S_OK;
1627 static inline BOOL is_char_from_simple_script(WCHAR c)
1629 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c) ||
1630 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1631 c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e))
1632 return FALSE;
1633 else {
1634 UINT16 script = get_char_script(c);
1635 return !dwritescripts_properties[script].is_complex;
1639 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1640 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1642 HRESULT hr = S_OK;
1643 int i;
1645 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1647 *is_simple = FALSE;
1648 *len_read = 0;
1650 if (!face)
1651 return E_INVALIDARG;
1653 if (len == 0) {
1654 *is_simple = TRUE;
1655 return S_OK;
1658 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1659 for (i = 1; i < len && text[i]; i++) {
1660 if (is_char_from_simple_script(text[i])) {
1661 if (!*is_simple)
1662 break;
1664 else
1665 *is_simple = FALSE;
1668 *len_read = i;
1670 /* fetch indices */
1671 if (*is_simple && indices) {
1672 UINT32 *codepoints = heap_calloc(*len_read, sizeof(*codepoints));
1673 if (!codepoints)
1674 return E_OUTOFMEMORY;
1676 for (i = 0; i < *len_read; i++)
1677 codepoints[i] = text[i];
1679 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1680 heap_free(codepoints);
1683 return hr;
1686 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1687 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1688 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1690 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1691 debugstr_wn(text, length), clustermap, prop, jo);
1692 return E_NOTIMPL;
1695 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1696 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1697 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1699 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1700 justifiedoffsets);
1701 return E_NOTIMPL;
1704 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1705 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1706 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1707 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1708 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1709 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1711 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,
1712 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1713 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1714 return E_NOTIMPL;
1717 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1718 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1720 static const DWRITE_MATRIX transforms[] = {
1721 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1722 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1723 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1724 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1727 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle, is_sideways, originX, originY, m);
1729 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1730 memset(m, 0, sizeof(*m));
1731 return E_INVALIDARG;
1734 /* for sideways case simply rotate 90 degrees more */
1735 if (is_sideways) {
1736 switch (angle) {
1737 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1738 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1739 break;
1740 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1741 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1742 break;
1743 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1744 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1745 break;
1746 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1747 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1748 break;
1749 default:
1754 *m = transforms[angle];
1756 /* shift components represent transform necessary to get from original point to
1757 rotated one in new coordinate system */
1758 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1759 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1760 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1763 return S_OK;
1766 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1767 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1768 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1770 struct scriptshaping_context context = { 0 };
1771 const struct dwritescript_properties *props;
1772 struct dwrite_fontface *font_obj;
1774 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), max_tagcount,
1775 actual_tagcount, tags);
1777 if (sa.script > Script_LastId)
1778 return E_INVALIDARG;
1780 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1782 context.cache = fontface_get_shaping_cache(font_obj);
1783 context.language_tag = get_opentype_language(locale);
1784 props = &dwritescripts_properties[sa.script];
1786 return shape_get_typographic_features(&context, props->scripttags, max_tagcount, actual_tagcount, tags);
1789 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1790 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature,
1791 UINT32 glyph_count, const UINT16 *glyphs, UINT8 *feature_applies)
1793 struct scriptshaping_context context = { 0 };
1794 const struct dwritescript_properties *props;
1795 struct dwrite_fontface *font_obj;
1796 HRESULT hr;
1798 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), debugstr_tag(feature),
1799 glyph_count, glyphs, feature_applies);
1801 if (sa.script > Script_LastId)
1802 return E_INVALIDARG;
1804 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1806 context.cache = fontface_get_shaping_cache(font_obj);
1807 context.language_tag = get_opentype_language(locale);
1808 context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos));
1810 props = &dwritescripts_properties[sa.script];
1812 hr = shape_check_typographic_feature(&context, props->scripttags, feature, glyph_count, glyphs, feature_applies);
1814 heap_free(context.glyph_infos);
1816 return hr;
1819 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl =
1821 dwritetextanalyzer_QueryInterface,
1822 dwritetextanalyzer_AddRef,
1823 dwritetextanalyzer_Release,
1824 dwritetextanalyzer_AnalyzeScript,
1825 dwritetextanalyzer_AnalyzeBidi,
1826 dwritetextanalyzer_AnalyzeNumberSubstitution,
1827 dwritetextanalyzer_AnalyzeLineBreakpoints,
1828 dwritetextanalyzer_GetGlyphs,
1829 dwritetextanalyzer_GetGlyphPlacements,
1830 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1831 dwritetextanalyzer1_ApplyCharacterSpacing,
1832 dwritetextanalyzer1_GetBaseline,
1833 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1834 dwritetextanalyzer1_GetGlyphOrientationTransform,
1835 dwritetextanalyzer1_GetScriptProperties,
1836 dwritetextanalyzer1_GetTextComplexity,
1837 dwritetextanalyzer1_GetJustificationOpportunities,
1838 dwritetextanalyzer1_JustifyGlyphAdvances,
1839 dwritetextanalyzer1_GetJustifiedGlyphs,
1840 dwritetextanalyzer2_GetGlyphOrientationTransform,
1841 dwritetextanalyzer2_GetTypographicFeatures,
1842 dwritetextanalyzer2_CheckTypographicFeature
1845 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1847 IDWriteTextAnalyzer2 *get_text_analyzer(void)
1849 return &textanalyzer;
1852 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1854 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
1856 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1857 IsEqualIID(riid, &IID_IUnknown))
1859 *obj = iface;
1860 IDWriteNumberSubstitution_AddRef(iface);
1861 return S_OK;
1864 WARN("%s not implemented.\n", debugstr_guid(riid));
1866 *obj = NULL;
1868 return E_NOINTERFACE;
1871 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1873 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
1874 ULONG refcount = InterlockedIncrement(&object->refcount);
1876 TRACE("%p, refcount %d.\n", iface, refcount);
1878 return refcount;
1881 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1883 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
1884 ULONG refcount = InterlockedDecrement(&object->refcount);
1886 TRACE("%p, refcount %d.\n", iface, refcount);
1888 if (!refcount)
1890 heap_free(object->locale);
1891 heap_free(object);
1894 return refcount;
1897 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl =
1899 dwritenumbersubstitution_QueryInterface,
1900 dwritenumbersubstitution_AddRef,
1901 dwritenumbersubstitution_Release
1904 struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
1906 if (!iface || iface->lpVtbl != &numbersubstitutionvtbl)
1907 return NULL;
1908 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
1911 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1912 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1914 struct dwrite_numbersubstitution *substitution;
1916 *ret = NULL;
1918 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1919 return E_INVALIDARG;
1921 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1922 return E_INVALIDARG;
1924 substitution = heap_alloc(sizeof(*substitution));
1925 if (!substitution)
1926 return E_OUTOFMEMORY;
1928 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1929 substitution->refcount = 1;
1930 substitution->ignore_user_override = ignore_user_override;
1931 substitution->method = method;
1932 substitution->locale = heap_strdupW(locale);
1933 if (locale && !substitution->locale) {
1934 heap_free(substitution);
1935 return E_OUTOFMEMORY;
1938 *ret = &substitution->IDWriteNumberSubstitution_iface;
1939 return S_OK;
1942 /* IDWriteFontFallback */
1943 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback1 *iface, REFIID riid, void **obj)
1945 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
1947 if (IsEqualIID(riid, &IID_IDWriteFontFallback1) ||
1948 IsEqualIID(riid, &IID_IDWriteFontFallback) ||
1949 IsEqualIID(riid, &IID_IUnknown))
1951 *obj = iface;
1952 IDWriteFontFallback1_AddRef(iface);
1953 return S_OK;
1956 WARN("%s not implemented.\n", debugstr_guid(riid));
1958 *obj = NULL;
1959 return E_NOINTERFACE;
1962 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback1 *iface)
1964 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
1966 TRACE("%p.\n", iface);
1968 return IDWriteFactory7_AddRef(fallback->factory);
1971 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback1 *iface)
1973 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
1975 TRACE("%p.\n", fallback);
1977 return IDWriteFactory7_Release(fallback->factory);
1980 static int __cdecl compare_mapping_range(const void *a, const void *b)
1982 UINT32 ch = *(UINT32 *)a;
1983 DWRITE_UNICODE_RANGE *range = (DWRITE_UNICODE_RANGE *)b;
1985 if (ch > range->last)
1986 return 1;
1987 else if (ch < range->first)
1988 return -1;
1989 else
1990 return 0;
1993 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
1995 UINT32 i;
1997 for (i = 0; i < fallback->mappings_count; i++) {
1998 struct fallback_mapping *mapping = &fallback->mappings[i];
2000 if (bsearch(&ch, mapping->ranges, mapping->ranges_count, sizeof(*mapping->ranges),
2001 compare_mapping_range) != NULL)
2002 return mapping;
2005 return NULL;
2008 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
2009 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
2011 IDWriteFontFamily *family;
2012 BOOL exists = FALSE;
2013 HRESULT hr;
2014 UINT32 i;
2016 *font = NULL;
2018 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
2019 if (FAILED(hr))
2020 return hr;
2022 if (!exists)
2023 return E_FAIL;
2025 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
2026 if (FAILED(hr))
2027 return hr;
2029 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
2030 IDWriteFontFamily_Release(family);
2031 return hr;
2034 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
2036 HRESULT hr = S_OK;
2037 UINT32 i;
2039 for (i = 0; i < length; i++) {
2040 UINT16 script = get_char_script(text[i]);
2041 BOOL exists;
2043 if (script == Script_Unknown || script == Script_Common) {
2044 ++*mapped_length;
2045 continue;
2048 /* stop on first unsupported character */
2049 exists = FALSE;
2050 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
2051 if (hr == S_OK && exists)
2052 ++*mapped_length;
2053 else
2054 break;
2057 return hr;
2060 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
2061 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2062 IDWriteFont **mapped_font)
2064 const struct fallback_mapping *mapping;
2065 HRESULT hr;
2066 UINT32 i;
2068 *mapped_font = NULL;
2070 mapping = find_fallback_mapping(fallback, text[0]);
2071 if (!mapping) {
2072 WARN("No mapping range for %#x.\n", text[0]);
2073 return E_FAIL;
2076 /* Now let's see what fallback can handle. Pick first font that could be created. */
2077 for (i = 0; i < mapping->families_count; i++) {
2078 hr = create_matching_font((IDWriteFontCollection *)fallback->systemcollection, mapping->families[i],
2079 weight, style, stretch, mapped_font);
2080 if (hr == S_OK) {
2081 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping->families[i]));
2082 break;
2086 if (!*mapped_font) {
2087 WARN("Failed to create fallback font.\n");
2088 return E_FAIL;
2091 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
2092 if (FAILED(hr))
2093 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping->families[i]), hr);
2095 if (!*mapped_length) {
2096 IDWriteFont_Release(*mapped_font);
2097 *mapped_font = NULL;
2100 return *mapped_length ? S_OK : E_FAIL;
2103 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2104 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2105 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2106 IDWriteFont **ret_font, FLOAT *scale)
2108 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2109 WCHAR *buff = NULL;
2110 const WCHAR *text;
2111 HRESULT hr;
2113 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2114 debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2116 *mapped_length = 0;
2117 *scale = 1.0f;
2118 *ret_font = NULL;
2120 if (!source)
2121 return E_INVALIDARG;
2123 if (length == 0)
2124 return S_OK;
2126 if (!basecollection)
2127 basecollection = (IDWriteFontCollection*)fallback->systemcollection;
2129 hr = get_text_source_ptr(source, position, length, &text, &buff);
2130 if (FAILED(hr))
2131 goto done;
2133 if (basefamily && *basefamily) {
2134 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
2135 if (FAILED(hr))
2136 goto done;
2138 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
2139 if (FAILED(hr))
2140 goto done;
2143 if (!*mapped_length) {
2144 IDWriteFont *mapped_font;
2146 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
2147 if (FAILED(hr)) {
2148 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2149 if (*ret_font) {
2150 *mapped_length = length;
2151 hr = S_OK;
2154 else {
2155 if (*ret_font)
2156 IDWriteFont_Release(*ret_font);
2157 *ret_font = mapped_font;
2161 done:
2162 heap_free(buff);
2163 return hr;
2166 static HRESULT WINAPI fontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2167 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2168 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 values_count, UINT32 *mapped_length, FLOAT *scale,
2169 IDWriteFontFace5 **ret_fontface)
2171 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2172 debugstr_w(basefamily), axis_values, values_count, mapped_length, scale, ret_fontface);
2174 return E_NOTIMPL;
2177 static const IDWriteFontFallback1Vtbl fontfallbackvtbl =
2179 fontfallback_QueryInterface,
2180 fontfallback_AddRef,
2181 fontfallback_Release,
2182 fontfallback_MapCharacters,
2183 fontfallback1_MapCharacters,
2186 HRESULT create_system_fontfallback(IDWriteFactory7 *factory, IDWriteFontFallback1 **ret)
2188 struct dwrite_fontfallback *fallback;
2190 *ret = NULL;
2192 fallback = heap_alloc(sizeof(*fallback));
2193 if (!fallback)
2194 return E_OUTOFMEMORY;
2196 fallback->IDWriteFontFallback1_iface.lpVtbl = &fontfallbackvtbl;
2197 fallback->factory = factory;
2198 fallback->mappings = (struct fallback_mapping *)fontfallback_neutral_data;
2199 fallback->mappings_count = ARRAY_SIZE(fontfallback_neutral_data);
2200 IDWriteFactory5_GetSystemFontCollection((IDWriteFactory5 *)fallback->factory, FALSE, &fallback->systemcollection, FALSE);
2202 *ret = &fallback->IDWriteFontFallback1_iface;
2204 return S_OK;
2207 void release_system_fontfallback(IDWriteFontFallback1 *iface)
2209 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2210 IDWriteFontCollection1_Release(fallback->systemcollection);
2211 heap_free(fallback);
2214 static ULONG WINAPI customfontfallback_AddRef(IDWriteFontFallback1 *iface)
2216 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2217 ULONG refcount = InterlockedIncrement(&fallback->refcount);
2219 TRACE("%p, refcount %u.\n", iface, refcount);
2221 return refcount;
2224 static ULONG WINAPI customfontfallback_Release(IDWriteFontFallback1 *iface)
2226 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2227 ULONG refcount = InterlockedDecrement(&fallback->refcount);
2229 TRACE("%p, refcount %u.\n", iface, refcount);
2231 if (!refcount)
2233 IDWriteFactory7_Release(fallback->factory);
2234 heap_free(fallback);
2237 return refcount;
2240 static HRESULT WINAPI customfontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2241 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2242 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2243 IDWriteFont **ret_font, FLOAT *scale)
2245 FIXME("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length,
2246 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2248 return E_NOTIMPL;
2251 static HRESULT WINAPI customfontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2252 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2253 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 values_count, UINT32 *mapped_length, FLOAT *scale,
2254 IDWriteFontFace5 **ret_fontface)
2256 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2257 debugstr_w(basefamily), axis_values, values_count, mapped_length, scale, ret_fontface);
2259 return E_NOTIMPL;
2262 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl =
2264 fontfallback_QueryInterface,
2265 customfontfallback_AddRef,
2266 customfontfallback_Release,
2267 customfontfallback_MapCharacters,
2268 customfontfallback1_MapCharacters,
2271 static HRESULT WINAPI fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder *iface, REFIID riid, void **obj)
2273 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2275 if (IsEqualIID(riid, &IID_IDWriteFontFallbackBuilder) || IsEqualIID(riid, &IID_IUnknown)) {
2276 *obj = iface;
2277 IDWriteFontFallbackBuilder_AddRef(iface);
2278 return S_OK;
2281 WARN("%s not implemented.\n", debugstr_guid(riid));
2283 *obj = NULL;
2284 return E_NOINTERFACE;
2287 static ULONG WINAPI fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder *iface)
2289 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2290 ULONG refcount = InterlockedIncrement(&fallbackbuilder->refcount);
2292 TRACE("%p, refcount %d.\n", iface, refcount);
2294 return refcount;
2297 static ULONG WINAPI fontfallbackbuilder_Release(IDWriteFontFallbackBuilder *iface)
2299 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2300 ULONG refcount = InterlockedDecrement(&fallbackbuilder->refcount);
2301 size_t i;
2303 TRACE("%p, refcount %d.\n", iface, refcount);
2305 if (!refcount)
2307 for (i = 0; i < fallbackbuilder->count; ++i)
2309 struct fallback_mapping *mapping = &fallbackbuilder->mappings[i];
2310 UINT32 j;
2312 for (j = 0; j < mapping->families_count; j++)
2313 heap_free(mapping->families[j]);
2314 heap_free(mapping->families);
2316 if (mapping->collection)
2317 IDWriteFontCollection_Release(mapping->collection);
2318 heap_free(mapping->ranges);
2319 heap_free(mapping->locale);
2322 IDWriteFactory7_Release(fallbackbuilder->factory);
2323 heap_free(fallbackbuilder->mappings);
2324 heap_free(fallbackbuilder);
2327 return refcount;
2330 static HRESULT WINAPI fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder *iface,
2331 const DWRITE_UNICODE_RANGE *ranges, UINT32 ranges_count, WCHAR const **target_families, UINT32 families_count,
2332 IDWriteFontCollection *collection, WCHAR const *locale, WCHAR const *base_family, FLOAT scale)
2334 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2335 struct fallback_mapping *mapping;
2336 UINT32 i;
2338 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface, ranges, ranges_count, target_families, families_count,
2339 collection, debugstr_w(locale), debugstr_w(base_family), scale);
2341 if (!ranges || ranges_count == 0 || !target_families || families_count == 0 || scale < 0.0f)
2342 return E_INVALIDARG;
2344 if (base_family)
2345 FIXME("base family ignored.\n");
2347 if (!dwrite_array_reserve((void **)&fallbackbuilder->mappings, &fallbackbuilder->size, fallbackbuilder->count + 1,
2348 sizeof(*fallbackbuilder->mappings)))
2350 return E_OUTOFMEMORY;
2353 mapping = &fallbackbuilder->mappings[fallbackbuilder->count++];
2355 mapping->ranges = heap_calloc(ranges_count, sizeof(*mapping->ranges));
2356 memcpy(mapping->ranges, ranges, sizeof(*mapping->ranges) * ranges_count);
2357 mapping->ranges_count = ranges_count;
2358 mapping->families = heap_alloc_zero(sizeof(*mapping->families) * families_count);
2359 mapping->families_count = families_count;
2360 for (i = 0; i < families_count; i++)
2361 mapping->families[i] = heap_strdupW(target_families[i]);
2362 mapping->collection = collection;
2363 if (mapping->collection)
2364 IDWriteFontCollection_AddRef(mapping->collection);
2365 mapping->locale = heap_strdupW(locale);
2366 mapping->scale = scale;
2368 return S_OK;
2371 static HRESULT WINAPI fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder *iface, IDWriteFontFallback *fallback)
2373 FIXME("%p, %p stub.\n", iface, fallback);
2375 return E_NOTIMPL;
2378 static HRESULT WINAPI fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder *iface,
2379 IDWriteFontFallback **ret)
2381 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2382 struct dwrite_fontfallback *fallback;
2384 TRACE("%p, %p.\n", iface, ret);
2386 *ret = NULL;
2388 fallback = heap_alloc(sizeof(*fallback));
2389 if (!fallback)
2390 return E_OUTOFMEMORY;
2392 fallback->IDWriteFontFallback1_iface.lpVtbl = &customfontfallbackvtbl;
2393 fallback->refcount = 1;
2394 fallback->factory = fallbackbuilder->factory;
2395 IDWriteFactory7_AddRef(fallback->factory);
2397 *ret = (IDWriteFontFallback *)&fallback->IDWriteFontFallback1_iface;
2398 return S_OK;
2401 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl =
2403 fontfallbackbuilder_QueryInterface,
2404 fontfallbackbuilder_AddRef,
2405 fontfallbackbuilder_Release,
2406 fontfallbackbuilder_AddMapping,
2407 fontfallbackbuilder_AddMappings,
2408 fontfallbackbuilder_CreateFontFallback,
2411 HRESULT create_fontfallback_builder(IDWriteFactory7 *factory, IDWriteFontFallbackBuilder **ret)
2413 struct dwrite_fontfallback_builder *builder;
2415 *ret = NULL;
2417 builder = heap_alloc_zero(sizeof(*builder));
2418 if (!builder)
2419 return E_OUTOFMEMORY;
2421 builder->IDWriteFontFallbackBuilder_iface.lpVtbl = &fontfallbackbuildervtbl;
2422 builder->refcount = 1;
2423 builder->factory = factory;
2424 IDWriteFactory7_AddRef(builder->factory);
2426 *ret = &builder->IDWriteFontFallbackBuilder_iface;
2427 return S_OK;