maintainers: Update the Direct3D section.
[wine.git] / dlls / dwrite / analyzer.c
blob7ffcfa8070c5e9617896aed1bbf5406e02de2139
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') } },
201 { /* Chrs */ { 0x73726843, 109, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('c','h','r','s') } },
202 { /* Diak */ { 0x6b616944, 342, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('d','i','a','k') } },
203 { /* Kits */ { 0x7374694b, 288, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('k','i','t','s') } },
204 { /* Yezi */ { 0x697a6559, 192, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('y','e','z','i') } },
206 #undef _OT
208 const char *debugstr_sa_script(UINT16 script)
210 return script < Script_LastId ? debugstr_tag(dwritescripts_properties[script].props.isoScriptCode) : "undefined";
213 /* system font falback configuration */
214 static const WCHAR *cjk_families[] = { L"Meiryo" };
216 static const DWRITE_UNICODE_RANGE cjk_ranges[] =
218 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
219 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
220 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
223 struct fallback_mapping {
224 DWRITE_UNICODE_RANGE *ranges;
225 UINT32 ranges_count;
226 WCHAR **families;
227 UINT32 families_count;
228 IDWriteFontCollection *collection;
229 WCHAR *locale;
230 FLOAT scale;
233 static const struct fallback_mapping fontfallback_neutral_data[] = {
234 #define MAPPING_RANGE(ranges, families) \
235 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
236 (WCHAR **)families, ARRAY_SIZE(families) }
238 MAPPING_RANGE(cjk_ranges, cjk_families),
240 #undef MAPPING_RANGE
243 struct dwrite_fontfallback
245 IDWriteFontFallback1 IDWriteFontFallback1_iface;
246 LONG refcount;
247 IDWriteFactory7 *factory;
248 IDWriteFontCollection1 *systemcollection;
249 struct fallback_mapping *mappings;
250 UINT32 mappings_count;
253 struct dwrite_fontfallback_builder
255 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface;
256 LONG refcount;
257 IDWriteFactory7 *factory;
258 struct fallback_mapping *mappings;
259 size_t size;
260 size_t count;
263 struct dwrite_numbersubstitution
265 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
266 LONG refcount;
268 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
269 WCHAR *locale;
270 BOOL ignore_user_override;
273 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
275 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
278 static struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface);
280 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback1(IDWriteFontFallback1 *iface)
282 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback1_iface);
285 static inline struct dwrite_fontfallback_builder *impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder *iface)
287 return CONTAINING_RECORD(iface, struct dwrite_fontfallback_builder, IDWriteFontFallbackBuilder_iface);
290 static inline UINT16 get_char_script(WCHAR c)
292 UINT16 script = get_table_entry(wine_scripts_table, c);
293 return script == Script_Inherited ? Script_Unknown : script;
296 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
298 DWRITE_SCRIPT_ANALYSIS sa;
299 WORD type;
301 GetStringTypeW(CT_CTYPE1, &c, 1, &type);
302 sa.script = get_char_script(c);
303 sa.shapes = (type & C1_CNTRL) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
304 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
305 return sa;
308 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
310 DWRITE_SCRIPT_ANALYSIS sa;
311 UINT32 pos, i, seq_length;
313 if (!length)
314 return S_OK;
316 sa = get_char_sa(*text);
318 pos = position;
319 seq_length = 1;
321 for (i = 1; i < length; i++)
323 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
325 /* Unknown type is ignored when preceded or followed by another script */
326 switch (sa.script) {
327 case Script_Unknown:
328 sa.script = cur_sa.script;
329 break;
330 case Script_Common:
331 if (cur_sa.script == Script_Unknown)
332 cur_sa.script = sa.script;
333 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
334 sa.script = cur_sa.script;
335 break;
336 default:
337 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
338 cur_sa.script = sa.script;
341 /* this is a length of a sequence to be reported next */
342 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
343 seq_length++;
344 else {
345 HRESULT hr;
347 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
348 if (FAILED(hr)) return hr;
349 pos = position + i;
350 seq_length = 1;
351 sa = cur_sa;
355 /* one char length case or normal completion call */
356 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
359 struct linebreaking_state {
360 DWRITE_LINE_BREAKPOINT *breakpoints;
361 UINT32 count;
364 enum BreakConditionLocation {
365 BreakConditionBefore,
366 BreakConditionAfter
369 enum linebreaking_classes {
370 b_BK = 1,
371 b_CR,
372 b_LF,
373 b_CM,
374 b_SG,
375 b_GL,
376 b_CB,
377 b_SP,
378 b_ZW,
379 b_NL,
380 b_WJ,
381 b_JL,
382 b_JV,
383 b_JT,
384 b_H2,
385 b_H3,
386 b_XX,
387 b_OP,
388 b_CL,
389 b_CP,
390 b_QU,
391 b_NS,
392 b_EX,
393 b_SY,
394 b_IS,
395 b_PR,
396 b_PO,
397 b_NU,
398 b_AL,
399 b_ID,
400 b_IN,
401 b_HY,
402 b_BB,
403 b_BA,
404 b_SA,
405 b_AI,
406 b_B2,
407 b_HL,
408 b_CJ,
409 b_RI,
410 b_EB,
411 b_EM,
412 b_ZWJ,
415 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
417 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
418 return TRUE;
420 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
421 return TRUE;
423 return FALSE;
426 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
427 set to "can break" and could only be changed once. */
428 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
429 struct linebreaking_state *state)
431 if (location == BreakConditionBefore) {
432 if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
433 return;
434 state->breakpoints[pos].breakConditionBefore = condition;
435 if (pos > 0)
436 state->breakpoints[pos-1].breakConditionAfter = condition;
438 else {
439 if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
440 return;
441 state->breakpoints[pos].breakConditionAfter = condition;
442 if (pos + 1 < state->count)
443 state->breakpoints[pos+1].breakConditionBefore = condition;
447 BOOL lb_is_newline_char(WCHAR ch)
449 short c = get_table_entry(wine_linebreak_table, ch);
450 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
453 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
455 struct linebreaking_state state;
456 short *break_class;
457 int i, j;
459 if (!(break_class = calloc(count, sizeof(*break_class))))
460 return E_OUTOFMEMORY;
462 state.breakpoints = breakpoints;
463 state.count = count;
465 for (i = 0; i < count; i++)
467 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
469 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
470 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
471 breakpoints[i].isWhitespace = !!iswspace(text[i]);
472 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
473 breakpoints[i].padding = 0;
475 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
476 switch (break_class[i])
478 case b_AI:
479 case b_SA:
480 case b_SG:
481 case b_XX:
482 break_class[i] = b_AL;
483 break;
484 case b_CJ:
485 break_class[i] = b_NS;
486 break;
490 /* LB2 - never break at the start */
491 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
492 /* LB3 - always break at the end. */
493 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
495 /* LB4 - LB6 - mandatory breaks. */
496 for (i = 0; i < count; i++)
498 switch (break_class[i])
500 /* LB4 - LB6 */
501 case b_CR:
502 /* LB5 - don't break CR x LF */
503 if (i < count-1 && break_class[i+1] == b_LF)
505 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
506 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
507 break;
509 case b_LF:
510 case b_NL:
511 case b_BK:
512 /* LB4 - LB5 - always break after hard breaks */
513 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
514 /* LB6 - do not break before hard breaks */
515 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
516 break;
520 /* LB7 - LB8 - explicit breaks and non-breaks */
521 for (i = 0; i < count; i++)
523 switch (break_class[i])
525 /* LB7 - do not break before spaces or zero-width space */
526 case b_SP:
527 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
528 break;
529 case b_ZW:
530 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
532 /* LB8 - break before character after zero-width space, skip spaces in-between */
533 j = i;
534 while (j < count-1 && break_class[j+1] == b_SP)
535 j++;
536 if (j < count-1 && break_class[j+1] != b_ZW)
537 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
538 break;
539 /* LB8a - do not break after ZWJ */
540 case b_ZWJ:
541 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
542 break;
546 /* LB9 - LB10 - combining marks */
547 for (i = 0; i < count; i++)
549 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
551 if (i > 0)
553 switch (break_class[i-1])
555 case b_SP:
556 case b_BK:
557 case b_CR:
558 case b_LF:
559 case b_NL:
560 case b_ZW:
561 break_class[i] = b_AL;
562 break;
563 default:
565 break_class[i] = break_class[i-1];
566 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
570 else break_class[i] = b_AL;
574 for (i = 0; i < count; i++)
576 switch (break_class[i])
578 /* LB11 - don't break before and after word joiner */
579 case b_WJ:
580 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
581 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
582 break;
583 /* LB12 - don't break after glue */
584 case b_GL:
585 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
586 /* LB12a */
587 if (i > 0)
589 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
590 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
592 break;
593 /* LB13 */
594 case b_CL:
595 case b_CP:
596 case b_EX:
597 case b_IS:
598 case b_SY:
599 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
600 break;
601 /* LB14 - do not break after OP, even after spaces */
602 case b_OP:
603 j = i;
604 while (j < count-1 && break_class[j+1] == b_SP)
605 j++;
606 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
607 break;
608 /* LB15 - do not break within QU-OP, even with intervening spaces */
609 case b_QU:
610 j = i;
611 while (j < count-1 && break_class[j+1] == b_SP)
612 j++;
613 if (j < count - 1 && break_class[j+1] == b_OP)
614 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
615 break;
616 /* LB16 */
617 case b_NS:
618 j = i-1;
619 while(j > 0 && break_class[j] == b_SP)
620 j--;
621 if (break_class[j] == b_CL || break_class[j] == b_CP)
622 for (j++; j <= i; j++)
623 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
624 break;
625 /* LB17 - do not break within B2, even with intervening spaces */
626 case b_B2:
627 j = i;
628 while (j < count && break_class[j+1] == b_SP)
629 j++;
630 if (j < count - 1 && break_class[j+1] == b_B2)
631 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
632 break;
636 for (i = 0; i < count; i++)
638 switch(break_class[i])
640 /* LB18 - break is allowed after space */
641 case b_SP:
642 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
643 break;
644 /* LB19 - don't break before or after quotation mark */
645 case b_QU:
646 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
647 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
648 break;
649 /* LB20 */
650 case b_CB:
651 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
652 if (i < count - 1 && break_class[i+1] != b_QU)
653 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
654 break;
655 /* LB21 */
656 case b_BA:
657 case b_HY:
658 case b_NS:
659 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
660 break;
661 case b_BB:
662 if (i < count - 1 && break_class[i+1] != b_CB)
663 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
664 break;
665 /* LB21a, LB21b */
666 case b_HL:
667 /* LB21a */
668 if (i < count-1)
669 switch (break_class[i+1])
671 case b_HY:
672 case b_BA:
673 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
675 /* LB21b */
676 if (i > 0 && break_class[i-1] == b_SY)
677 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
678 break;
679 /* LB22 */
680 case b_IN:
681 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
682 break;
685 if (i < count-1)
687 /* LB23 - do not break between digits and letters */
688 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
689 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
690 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
691 (break_class[i] == b_NU && break_class[i+1] == b_HL))
692 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
694 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
695 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
696 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
697 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
698 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
699 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
700 (break_class[i] == b_EB && break_class[i+1] == b_PO))
701 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
703 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
704 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
705 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
706 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
707 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
708 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
709 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
710 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
711 (break_class[i] == b_HL && break_class[i+1] == b_PO))
712 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
714 /* LB25 */
715 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
716 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
717 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
718 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
719 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
720 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
721 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
722 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
723 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
724 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
725 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
726 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
727 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
728 (break_class[i] == b_SY && break_class[i+1] == b_NU))
729 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
731 /* LB26 */
732 if (break_class[i] == b_JL)
734 switch (break_class[i+1])
736 case b_JL:
737 case b_JV:
738 case b_H2:
739 case b_H3:
740 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
743 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
744 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
745 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
746 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
747 break_class[i+1] == b_JT)
748 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
750 /* LB27 */
751 switch (break_class[i])
753 case b_JL:
754 case b_JV:
755 case b_JT:
756 case b_H2:
757 case b_H3:
758 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
759 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
761 if (break_class[i] == b_PR)
763 switch (break_class[i+1])
765 case b_JL:
766 case b_JV:
767 case b_JT:
768 case b_H2:
769 case b_H3:
770 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
774 /* LB28 */
775 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
776 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
777 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
778 (break_class[i] == b_HL && break_class[i+1] == b_HL))
779 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
781 /* LB29 */
782 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
783 (break_class[i] == b_IS && break_class[i+1] == b_HL))
784 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
786 /* LB30 */
787 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
788 break_class[i+1] == b_OP)
789 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
790 if (break_class[i] == b_CP &&
791 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
792 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
794 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
795 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
796 unsigned int c = 0;
798 j = i + 1;
799 while (j > 0 && break_class[--j] == b_RI)
800 c++;
802 if ((c & 1) == 0)
803 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
806 /* LB30b - do not break between an emoji base and an emoji modifier */
807 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
808 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
812 /* LB31 - allow breaks everywhere else. */
813 for (i = 0; i < count; i++)
815 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
816 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
819 free(break_class);
820 return S_OK;
823 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
825 TRACE("%s, %p.\n", debugstr_guid(riid), obj);
827 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
828 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
829 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
830 IsEqualIID(riid, &IID_IUnknown))
832 *obj = iface;
833 return S_OK;
836 WARN("%s not implemented.\n", debugstr_guid(riid));
838 *obj = NULL;
839 return E_NOINTERFACE;
842 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
844 return 2;
847 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
849 return 1;
852 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
853 data after a first request. */
854 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
856 HRESULT hr;
857 UINT32 len;
859 *buff = NULL;
860 *text = NULL;
861 len = 0;
862 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
863 if (FAILED(hr)) return hr;
865 if (len < length) {
866 UINT32 read;
868 *buff = calloc(length, sizeof(WCHAR));
869 if (!*buff)
870 return E_OUTOFMEMORY;
871 if (*text)
872 memcpy(*buff, *text, len*sizeof(WCHAR));
873 read = len;
875 while (read < length && *text) {
876 *text = NULL;
877 len = 0;
878 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position+read, text, &len);
879 if (FAILED(hr))
881 free(*buff);
882 return hr;
884 if (!*text)
885 break;
886 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
887 read += len;
890 *text = *buff;
893 return hr;
896 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
897 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
899 WCHAR *buff = NULL;
900 const WCHAR *text;
901 HRESULT hr;
903 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
905 if (length == 0)
906 return S_OK;
908 hr = get_text_source_ptr(source, position, length, &text, &buff);
909 if (FAILED(hr))
910 return hr;
912 hr = analyze_script(text, position, length, sink);
913 free(buff);
915 return hr;
918 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
919 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
921 UINT8 *levels = NULL, *explicit = NULL;
922 UINT8 baselevel, level, explicit_level;
923 UINT32 pos, i, seq_length;
924 WCHAR *buff = NULL;
925 const WCHAR *text;
926 HRESULT hr;
928 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
930 if (!length)
931 return S_OK;
933 hr = get_text_source_ptr(source, position, length, &text, &buff);
934 if (FAILED(hr))
935 return hr;
937 levels = calloc(length, sizeof(*levels));
938 explicit = calloc(length, sizeof(*explicit));
940 if (!levels || !explicit) {
941 hr = E_OUTOFMEMORY;
942 goto done;
945 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
946 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
947 if (FAILED(hr))
948 goto done;
950 level = levels[0];
951 explicit_level = explicit[0];
952 pos = position;
953 seq_length = 1;
955 for (i = 1; i < length; i++) {
956 if (levels[i] == level && explicit[i] == explicit_level)
957 seq_length++;
958 else {
959 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
960 if (FAILED(hr))
961 goto done;
963 pos += seq_length;
964 seq_length = 1;
965 level = levels[i];
966 explicit_level = explicit[i];
969 /* one char length case or normal completion call */
970 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
972 done:
973 free(explicit);
974 free(levels);
975 free(buff);
977 return hr;
980 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
981 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
983 static int once;
985 if (!once++)
986 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
987 return S_OK;
990 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
991 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
993 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
994 WCHAR *buff = NULL;
995 const WCHAR *text;
996 HRESULT hr;
997 UINT32 len;
999 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
1001 if (length == 0)
1002 return S_OK;
1004 /* get some, check for length */
1005 text = NULL;
1006 len = 0;
1007 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
1008 if (FAILED(hr)) return hr;
1010 if (len < length) {
1011 UINT32 read;
1013 if (!(buff = calloc(length, sizeof(*buff))))
1014 return E_OUTOFMEMORY;
1015 if (text)
1016 memcpy(buff, text, len*sizeof(WCHAR));
1017 read = len;
1019 while (read < length && text) {
1020 text = NULL;
1021 len = 0;
1022 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position+read, &text, &len);
1023 if (FAILED(hr))
1024 goto done;
1025 if (!text)
1026 break;
1027 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
1028 read += len;
1031 text = buff;
1034 if (!(breakpoints = calloc(length, sizeof(*breakpoints))))
1036 hr = E_OUTOFMEMORY;
1037 goto done;
1040 hr = analyze_linebreaks(text, length, breakpoints);
1041 if (FAILED(hr))
1042 goto done;
1044 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1046 done:
1047 free(breakpoints);
1048 free(buff);
1050 return hr;
1053 static UINT32 get_opentype_language(const WCHAR *locale)
1055 UINT32 language = DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1057 if (locale) {
1058 WCHAR tag[5];
1059 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, ARRAY_SIZE(tag)))
1060 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1063 return language;
1066 static void get_number_substitutes(IDWriteNumberSubstitution *substitution, BOOL is_rtl, WCHAR *digits)
1068 struct dwrite_numbersubstitution *numbersubst = unsafe_impl_from_IDWriteNumberSubstitution(substitution);
1069 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1070 WCHAR isolang[9];
1071 DWORD lctype;
1073 digits[0] = 0;
1075 if (!numbersubst)
1076 return;
1078 lctype = numbersubst->ignore_user_override ? LOCALE_NOUSEROVERRIDE : 0;
1080 if (numbersubst->method == DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE) {
1081 DWORD value;
1083 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1084 if (GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, (WCHAR *)&value, 2)) {
1085 switch (value)
1087 case 0:
1088 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL;
1089 break;
1090 case 2:
1091 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL;
1092 break;
1093 case 1:
1094 default:
1095 if (value != 1)
1096 WARN("Unknown IDIGITSUBSTITUTION value %lu, locale %s.\n", value, debugstr_w(numbersubst->locale));
1099 else
1100 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst->locale));
1102 else
1103 method = numbersubst->method;
1105 switch (method)
1107 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL:
1108 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1109 break;
1110 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1111 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL:
1112 if (GetLocaleInfoEx(numbersubst->locale, LOCALE_SISO639LANGNAME, isolang, ARRAY_SIZE(isolang)))
1114 static const WCHAR arabicW[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1116 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1117 if (!wcscmp(L"ar", isolang))
1119 wcscpy(digits, arabicW);
1120 break;
1123 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1124 break;
1125 default:
1129 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !*digits) {
1130 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst->locale), method);
1131 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1134 if ((method == DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL && !is_rtl) || method == DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE)
1135 digits[0] = 0;
1138 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES const **features,
1139 UINT32 const *feature_range_lengths, UINT32 feature_ranges)
1141 UINT32 i, j, start;
1143 if (!TRACE_ON(dwrite) || !features)
1144 return;
1146 for (i = 0, start = 0; i < feature_ranges; start += feature_range_lengths[i++]) {
1147 TRACE("feature range [%u,%u)\n", start, start + feature_range_lengths[i]);
1148 for (j = 0; j < features[i]->featureCount; j++)
1149 TRACE("feature %s, parameter %u\n", debugstr_tag(features[i]->features[j].nameTag),
1150 features[i]->features[j].parameter);
1154 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1155 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1156 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1157 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1158 UINT32 const* feature_range_lengths, UINT32 feature_ranges, UINT32 max_glyph_count,
1159 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16 *glyphs,
1160 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1162 const struct dwritescript_properties *scriptprops;
1163 struct scriptshaping_context context = { 0 };
1164 struct dwrite_fontface *font_obj;
1165 WCHAR digits[NATIVE_DIGITS_LEN];
1166 unsigned int glyph_count;
1167 HRESULT hr;
1169 TRACE("%s:%u, %p, %d, %d, %s, %s, %p, %p, %p, %u, %u, %p, %p, %p, %p, %p.\n", debugstr_wn(text, length),
1170 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1171 features, feature_range_lengths, feature_ranges, max_glyph_count, clustermap, text_props, glyphs,
1172 glyph_props, actual_glyph_count);
1174 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1176 get_number_substitutes(substitution, is_rtl, digits);
1177 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1178 glyph_count = max(max_glyph_count, length);
1180 context.cache = fontface_get_shaping_cache(font_obj);
1181 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1182 context.text = text;
1183 context.length = length;
1184 context.is_rtl = is_rtl;
1185 context.is_sideways = is_sideways;
1186 context.u.subst.glyphs = calloc(glyph_count, sizeof(*glyphs));
1187 context.u.subst.glyph_props = calloc(glyph_count, sizeof(*glyph_props));
1188 context.u.subst.text_props = text_props;
1189 context.u.subst.clustermap = clustermap;
1190 context.u.subst.max_glyph_count = max_glyph_count;
1191 context.u.subst.capacity = glyph_count;
1192 context.u.subst.digits = digits;
1193 context.language_tag = get_opentype_language(locale);
1194 context.user_features.features = features;
1195 context.user_features.range_lengths = feature_range_lengths;
1196 context.user_features.range_count = feature_ranges;
1197 context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos));
1198 context.table = &context.cache->gsub;
1200 *actual_glyph_count = 0;
1202 if (!context.u.subst.glyphs || !context.u.subst.glyph_props || !context.glyph_infos)
1204 hr = E_OUTOFMEMORY;
1205 goto failed;
1208 scriptprops = &dwritescripts_properties[context.script];
1209 hr = shape_get_glyphs(&context, scriptprops->scripttags);
1210 if (SUCCEEDED(hr))
1212 *actual_glyph_count = context.glyph_count;
1213 memcpy(glyphs, context.u.subst.glyphs, context.glyph_count * sizeof(*glyphs));
1214 memcpy(glyph_props, context.u.subst.glyph_props, context.glyph_count * sizeof(*glyph_props));
1217 failed:
1218 free(context.u.subst.glyph_props);
1219 free(context.u.subst.glyphs);
1220 free(context.glyph_infos);
1222 return hr;
1225 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1226 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1227 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1228 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, BOOL is_sideways, BOOL is_rtl,
1229 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1230 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1232 const struct dwritescript_properties *scriptprops;
1233 struct scriptshaping_context context;
1234 struct dwrite_fontface *font_obj;
1235 unsigned int i;
1236 HRESULT hr;
1238 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),
1239 clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1240 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_lengths,
1241 feature_ranges, advances, offsets);
1243 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1245 if (glyph_count == 0)
1246 return S_OK;
1248 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1250 for (i = 0; i < glyph_count; ++i)
1252 if (glyph_props[i].isZeroWidthSpace)
1253 advances[i] = 0.0f;
1254 else
1255 advances[i] = fontface_get_scaled_design_advance(font_obj, DWRITE_MEASURING_MODE_NATURAL, emSize, 1.0f,
1256 NULL, glyphs[i], is_sideways);
1257 offsets[i].advanceOffset = 0.0f;
1258 offsets[i].ascenderOffset = 0.0f;
1261 context.cache = fontface_get_shaping_cache(font_obj);
1262 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1263 context.text = text;
1264 context.length = text_len;
1265 context.is_rtl = is_rtl;
1266 context.is_sideways = is_sideways;
1267 context.u.pos.glyphs = glyphs;
1268 context.u.pos.glyph_props = glyph_props;
1269 context.u.pos.text_props = text_props;
1270 context.u.pos.clustermap = clustermap;
1271 context.glyph_count = glyph_count;
1272 context.emsize = emSize;
1273 context.measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
1274 context.advances = advances;
1275 context.offsets = offsets;
1276 context.language_tag = get_opentype_language(locale);
1277 context.user_features.features = features;
1278 context.user_features.range_lengths = feature_range_lengths;
1279 context.user_features.range_count = feature_ranges;
1280 context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos));
1281 context.table = &context.cache->gpos;
1283 if (!context.glyph_infos)
1285 hr = E_OUTOFMEMORY;
1286 goto failed;
1289 scriptprops = &dwritescripts_properties[context.script];
1290 hr = shape_get_positions(&context, scriptprops->scripttags);
1292 failed:
1293 free(context.glyph_infos);
1295 return hr;
1298 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1299 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1300 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1301 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, float ppdip,
1302 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1303 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1304 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1306 const struct dwritescript_properties *scriptprops;
1307 struct scriptshaping_context context;
1308 DWRITE_MEASURING_MODE measuring_mode;
1309 struct dwrite_fontface *font_obj;
1310 unsigned int i;
1311 HRESULT hr;
1313 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1314 debugstr_wn(text, text_len), clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface,
1315 emSize, ppdip, transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script),
1316 debugstr_w(locale), features, feature_range_lengths, feature_ranges, advances, offsets);
1318 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1320 if (glyph_count == 0)
1321 return S_OK;
1323 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1325 measuring_mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
1327 for (i = 0; i < glyph_count; ++i)
1329 if (glyph_props[i].isZeroWidthSpace)
1330 advances[i] = 0.0f;
1331 else
1332 advances[i] = fontface_get_scaled_design_advance(font_obj, measuring_mode, emSize, ppdip,
1333 transform, glyphs[i], is_sideways);
1334 offsets[i].advanceOffset = 0.0f;
1335 offsets[i].ascenderOffset = 0.0f;
1338 context.cache = fontface_get_shaping_cache(font_obj);
1339 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1340 context.text = text;
1341 context.length = text_len;
1342 context.is_rtl = is_rtl;
1343 context.is_sideways = is_sideways;
1344 context.u.pos.glyphs = glyphs;
1345 context.u.pos.glyph_props = glyph_props;
1346 context.u.pos.text_props = text_props;
1347 context.u.pos.clustermap = clustermap;
1348 context.glyph_count = glyph_count;
1349 context.emsize = emSize * ppdip;
1350 context.measuring_mode = measuring_mode;
1351 context.advances = advances;
1352 context.offsets = offsets;
1353 context.language_tag = get_opentype_language(locale);
1354 context.user_features.features = features;
1355 context.user_features.range_lengths = feature_range_lengths;
1356 context.user_features.range_count = feature_ranges;
1357 context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos));
1358 context.table = &context.cache->gpos;
1360 if (!context.glyph_infos)
1362 hr = E_OUTOFMEMORY;
1363 goto failed;
1366 scriptprops = &dwritescripts_properties[context.script];
1367 hr = shape_get_positions(&context, scriptprops->scripttags);
1369 failed:
1370 free(context.glyph_infos);
1372 return hr;
1375 static HRESULT apply_cluster_spacing(float leading_spacing, float trailing_spacing, float min_advance_width,
1376 unsigned int start, unsigned int end, float const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1377 DWRITE_SHAPING_GLYPH_PROPERTIES const *glyph_props, float *modified_advances,
1378 DWRITE_GLYPH_OFFSET *modified_offsets)
1380 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1381 unsigned int first_spacing, last_spacing, i;
1382 float advance, origin = 0.0f, *deltas;
1383 BOOL is_spacing_cluster = FALSE;
1385 if (modified_advances != advances)
1386 memcpy(&modified_advances[start], &advances[start], (end - start + 1) * sizeof(*advances));
1387 if (modified_offsets != offsets)
1388 memcpy(&modified_offsets[start], &offsets[start], (end - start + 1) * sizeof(*offsets));
1390 for (first_spacing = start; first_spacing <= end; ++first_spacing)
1392 if ((is_spacing_cluster = !glyph_props[first_spacing].isZeroWidthSpace))
1393 break;
1396 /* Nothing to adjust if there is no spacing glyphs. */
1397 if (!is_spacing_cluster)
1398 return S_OK;
1400 for (last_spacing = end; last_spacing >= start; --last_spacing)
1402 if (!glyph_props[last_spacing].isZeroWidthSpace)
1403 break;
1406 if (!(deltas = calloc(end - start + 1, sizeof(*deltas))))
1407 return E_OUTOFMEMORY;
1409 /* Cluster advance, note that properties are ignored. */
1410 origin = offsets[start].advanceOffset;
1411 for (i = start, advance = 0.0f; i <= end; ++i)
1413 float cur = advance + offsets[i].advanceOffset;
1415 deltas[i - start] = cur - origin;
1417 advance += advances[i];
1418 origin = cur;
1421 /* Negative spacing. */
1422 if (leading_spacing < 0.0f)
1424 advance += leading_spacing;
1425 modified_advances[first_spacing] += leading_spacing;
1426 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1429 if (trailing_spacing < 0.0f)
1431 advance += trailing_spacing;
1432 modified_advances[last_spacing] += trailing_spacing;
1435 /* Minimal advance. */
1436 advance = min_advance_width - advance;
1437 if (advance > 0.0f) {
1438 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1439 float half = advance / 2.0f;
1441 if (!reduced)
1443 modified_advances[first_spacing] += half;
1444 modified_advances[last_spacing] += half;
1445 modified_offsets[first_spacing].advanceOffset += half;
1447 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1449 modified_advances[first_spacing] += half;
1450 modified_advances[last_spacing] += half;
1451 modified_offsets[first_spacing].advanceOffset += half;
1453 else if (leading_spacing < 0.0f)
1455 modified_advances[first_spacing] += advance;
1456 modified_offsets[first_spacing].advanceOffset += advance;
1458 else
1459 modified_advances[last_spacing] += advance;
1462 /* Positive spacing. */
1463 if (leading_spacing > 0.0f)
1465 modified_advances[first_spacing] += leading_spacing;
1466 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1469 if (trailing_spacing > 0.0f)
1470 modified_advances[last_spacing] += trailing_spacing;
1472 /* Update offsets to preserve original relative positions within cluster. */
1473 for (i = first_spacing; i > start; --i)
1475 unsigned int cur = i - 1;
1476 modified_offsets[cur].advanceOffset = modified_advances[cur] + modified_offsets[i].advanceOffset -
1477 deltas[i - start];
1480 for (i = first_spacing + 1; i <= end; ++i)
1482 modified_offsets[i].advanceOffset = deltas[i - start] + modified_offsets[i - 1].advanceOffset -
1483 modified_advances[i - 1];
1486 free(deltas);
1488 return S_OK;
1491 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1493 UINT16 g = clustermap[start];
1494 UINT32 length = 1;
1496 while (start < text_len && clustermap[++start] == g)
1497 length++;
1498 return length;
1501 /* Applies spacing adjustments to clusters.
1503 Adjustments are applied in the following order:
1505 1. Negative adjustments
1507 Leading and trailing spacing could be negative, at this step
1508 only negative ones are actually applied. Leading spacing is only
1509 applied to leading glyph, trailing - to trailing glyph.
1511 2. Minimum advance width
1513 Advances could only be reduced at this point or unchanged. In any
1514 case it's checked if cluster advance width is less than minimum width.
1515 If it's the case advance width is incremented up to minimum value.
1517 Important part is the direction in which this increment is applied;
1518 it depends on direction from which total cluster advance was trimmed
1519 at step 1. So it could be incremented from leading, trailing, or both
1520 sides. When applied to both sides, each side gets half of difference
1521 that brings advance to minimum width.
1523 3. Positive adjustments
1525 After minimum width rule was applied, positive spacing is applied in the same
1526 way as negative one on step 1.
1528 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1529 keeps its position in coordinate system where initial advance width is counted
1530 from 0.
1532 Glyph properties
1534 It's known that isZeroWidthSpace property keeps initial advance from changing.
1537 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1538 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1539 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1540 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1542 unsigned int i;
1544 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing, trailing_spacing, min_advance_width,
1545 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1547 if (min_advance_width < 0.0f) {
1548 if (modified_advances != advances)
1549 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1550 return E_INVALIDARG;
1553 for (i = 0; i < len;)
1555 unsigned int length = get_cluster_length(clustermap, i, len);
1556 unsigned int start, end;
1558 start = clustermap[i];
1559 end = i + length < len ? clustermap[i + length] : glyph_count;
1561 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width, start, end - 1, advances,
1562 offsets, props, modified_advances, modified_offsets);
1564 i += length;
1567 return S_OK;
1570 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *fontface,
1571 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1572 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1574 struct dwrite_fontface *font_obj;
1575 const DWRITE_FONT_METRICS1 *metrics;
1577 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1578 baseline_coord, exists);
1580 *exists = FALSE;
1581 *baseline_coord = 0;
1583 if (baseline == DWRITE_BASELINE_DEFAULT)
1584 baseline = vertical ? DWRITE_BASELINE_CENTRAL : DWRITE_BASELINE_ROMAN;
1586 if ((unsigned int)baseline > DWRITE_BASELINE_MAXIMUM)
1587 return E_INVALIDARG;
1589 /* TODO: fetch BASE table data if available. */
1591 if (!*exists && is_simulation_allowed)
1593 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1594 metrics = &font_obj->metrics;
1596 switch (baseline)
1598 case DWRITE_BASELINE_ROMAN:
1599 *baseline_coord = vertical ? metrics->descent : 0;
1600 break;
1601 case DWRITE_BASELINE_CENTRAL:
1602 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1603 -(metrics->ascent - metrics->descent) / 2;
1604 break;
1605 case DWRITE_BASELINE_MATH:
1606 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1607 -(metrics->ascent + metrics->descent) / 2;
1608 break;
1609 case DWRITE_BASELINE_HANGING:
1610 /* FIXME: this one isn't accurate, but close. */
1611 *baseline_coord = vertical ? metrics->capHeight * 6 / 7 + metrics->descent : metrics->capHeight * 6 / 7;
1612 break;
1613 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM:
1614 case DWRITE_BASELINE_MINIMUM:
1615 *baseline_coord = vertical ? 0 : metrics->descent;
1616 break;
1617 case DWRITE_BASELINE_IDEOGRAPHIC_TOP:
1618 case DWRITE_BASELINE_MAXIMUM:
1619 *baseline_coord = vertical ? metrics->ascent + metrics->descent : -metrics->ascent;
1620 break;
1621 default:
1626 return S_OK;
1629 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1630 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1632 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1633 return E_NOTIMPL;
1636 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1637 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1639 TRACE("%d, %d, %p.\n", angle, is_sideways, transform);
1641 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1644 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1645 DWRITE_SCRIPT_PROPERTIES *props)
1647 TRACE("%u, %p.\n", sa.script, props);
1649 if (sa.script > Script_LastId)
1650 return E_INVALIDARG;
1652 *props = dwritescripts_properties[sa.script].props;
1653 return S_OK;
1656 static inline BOOL is_char_from_simple_script(WCHAR c)
1658 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c) ||
1659 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1660 c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e))
1661 return FALSE;
1662 else {
1663 UINT16 script = get_char_script(c);
1664 return !dwritescripts_properties[script].is_complex;
1668 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1669 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1671 HRESULT hr = S_OK;
1672 int i;
1674 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1676 *is_simple = FALSE;
1677 *len_read = 0;
1679 if (!face)
1680 return E_INVALIDARG;
1682 if (len == 0) {
1683 *is_simple = TRUE;
1684 return S_OK;
1687 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1688 for (i = 1; i < len && text[i]; i++) {
1689 if (is_char_from_simple_script(text[i])) {
1690 if (!*is_simple)
1691 break;
1693 else
1694 *is_simple = FALSE;
1697 *len_read = i;
1699 /* fetch indices */
1700 if (*is_simple && indices)
1702 UINT32 *codepoints;
1704 if (!(codepoints = calloc(*len_read, sizeof(*codepoints))))
1705 return E_OUTOFMEMORY;
1707 for (i = 0; i < *len_read; i++)
1708 codepoints[i] = text[i];
1710 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1711 free(codepoints);
1714 return hr;
1717 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1718 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1719 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1721 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1722 debugstr_wn(text, length), clustermap, prop, jo);
1723 return E_NOTIMPL;
1726 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1727 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1728 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1730 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1731 justifiedoffsets);
1732 return E_NOTIMPL;
1735 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1736 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1737 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1738 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1739 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1740 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1742 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,
1743 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1744 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1745 return E_NOTIMPL;
1748 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1749 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1751 static const DWRITE_MATRIX transforms[] = {
1752 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1753 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1754 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1755 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1758 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle, is_sideways, originX, originY, m);
1760 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1761 memset(m, 0, sizeof(*m));
1762 return E_INVALIDARG;
1765 /* for sideways case simply rotate 90 degrees more */
1766 if (is_sideways) {
1767 switch (angle) {
1768 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1769 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1770 break;
1771 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1772 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1773 break;
1774 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1775 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1776 break;
1777 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1778 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1779 break;
1780 default:
1785 *m = transforms[angle];
1787 /* shift components represent transform necessary to get from original point to
1788 rotated one in new coordinate system */
1789 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1790 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1791 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1794 return S_OK;
1797 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1798 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1799 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1801 struct scriptshaping_context context = { 0 };
1802 const struct dwritescript_properties *props;
1803 struct dwrite_fontface *font_obj;
1805 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), max_tagcount,
1806 actual_tagcount, tags);
1808 if (sa.script > Script_LastId)
1809 return E_INVALIDARG;
1811 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1813 context.cache = fontface_get_shaping_cache(font_obj);
1814 context.language_tag = get_opentype_language(locale);
1815 props = &dwritescripts_properties[sa.script];
1817 return shape_get_typographic_features(&context, props->scripttags, max_tagcount, actual_tagcount, tags);
1820 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1821 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature,
1822 UINT32 glyph_count, const UINT16 *glyphs, UINT8 *feature_applies)
1824 struct scriptshaping_context context = { 0 };
1825 const struct dwritescript_properties *props;
1826 struct dwrite_fontface *font_obj;
1827 HRESULT hr;
1829 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), debugstr_tag(feature),
1830 glyph_count, glyphs, feature_applies);
1832 if (sa.script > Script_LastId)
1833 return E_INVALIDARG;
1835 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1837 context.cache = fontface_get_shaping_cache(font_obj);
1838 context.language_tag = get_opentype_language(locale);
1839 if (!(context.glyph_infos = calloc(glyph_count, sizeof(*context.glyph_infos))))
1840 return E_OUTOFMEMORY;
1842 props = &dwritescripts_properties[sa.script];
1844 hr = shape_check_typographic_feature(&context, props->scripttags, feature, glyph_count, glyphs, feature_applies);
1846 free(context.glyph_infos);
1848 return hr;
1851 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl =
1853 dwritetextanalyzer_QueryInterface,
1854 dwritetextanalyzer_AddRef,
1855 dwritetextanalyzer_Release,
1856 dwritetextanalyzer_AnalyzeScript,
1857 dwritetextanalyzer_AnalyzeBidi,
1858 dwritetextanalyzer_AnalyzeNumberSubstitution,
1859 dwritetextanalyzer_AnalyzeLineBreakpoints,
1860 dwritetextanalyzer_GetGlyphs,
1861 dwritetextanalyzer_GetGlyphPlacements,
1862 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1863 dwritetextanalyzer1_ApplyCharacterSpacing,
1864 dwritetextanalyzer1_GetBaseline,
1865 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1866 dwritetextanalyzer1_GetGlyphOrientationTransform,
1867 dwritetextanalyzer1_GetScriptProperties,
1868 dwritetextanalyzer1_GetTextComplexity,
1869 dwritetextanalyzer1_GetJustificationOpportunities,
1870 dwritetextanalyzer1_JustifyGlyphAdvances,
1871 dwritetextanalyzer1_GetJustifiedGlyphs,
1872 dwritetextanalyzer2_GetGlyphOrientationTransform,
1873 dwritetextanalyzer2_GetTypographicFeatures,
1874 dwritetextanalyzer2_CheckTypographicFeature
1877 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1879 IDWriteTextAnalyzer2 *get_text_analyzer(void)
1881 return &textanalyzer;
1884 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1886 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
1888 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1889 IsEqualIID(riid, &IID_IUnknown))
1891 *obj = iface;
1892 IDWriteNumberSubstitution_AddRef(iface);
1893 return S_OK;
1896 WARN("%s not implemented.\n", debugstr_guid(riid));
1898 *obj = NULL;
1900 return E_NOINTERFACE;
1903 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1905 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
1906 ULONG refcount = InterlockedIncrement(&object->refcount);
1908 TRACE("%p, refcount %ld.\n", iface, refcount);
1910 return refcount;
1913 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1915 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
1916 ULONG refcount = InterlockedDecrement(&object->refcount);
1918 TRACE("%p, refcount %ld.\n", iface, refcount);
1920 if (!refcount)
1922 free(object->locale);
1923 free(object);
1926 return refcount;
1929 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl =
1931 dwritenumbersubstitution_QueryInterface,
1932 dwritenumbersubstitution_AddRef,
1933 dwritenumbersubstitution_Release
1936 struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
1938 if (!iface || iface->lpVtbl != &numbersubstitutionvtbl)
1939 return NULL;
1940 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
1943 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1944 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1946 struct dwrite_numbersubstitution *substitution;
1948 *ret = NULL;
1950 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1951 return E_INVALIDARG;
1953 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1954 return E_INVALIDARG;
1956 if (!(substitution = calloc(1, sizeof(*substitution))))
1957 return E_OUTOFMEMORY;
1959 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1960 substitution->refcount = 1;
1961 substitution->ignore_user_override = ignore_user_override;
1962 substitution->method = method;
1963 substitution->locale = wcsdup(locale);
1964 if (locale && !substitution->locale)
1966 free(substitution);
1967 return E_OUTOFMEMORY;
1970 *ret = &substitution->IDWriteNumberSubstitution_iface;
1971 return S_OK;
1974 /* IDWriteFontFallback */
1975 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback1 *iface, REFIID riid, void **obj)
1977 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
1979 if (IsEqualIID(riid, &IID_IDWriteFontFallback1) ||
1980 IsEqualIID(riid, &IID_IDWriteFontFallback) ||
1981 IsEqualIID(riid, &IID_IUnknown))
1983 *obj = iface;
1984 IDWriteFontFallback1_AddRef(iface);
1985 return S_OK;
1988 WARN("%s not implemented.\n", debugstr_guid(riid));
1990 *obj = NULL;
1991 return E_NOINTERFACE;
1994 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback1 *iface)
1996 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
1998 TRACE("%p.\n", iface);
2000 return IDWriteFactory7_AddRef(fallback->factory);
2003 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback1 *iface)
2005 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2007 TRACE("%p.\n", fallback);
2009 return IDWriteFactory7_Release(fallback->factory);
2012 static int __cdecl compare_mapping_range(const void *a, const void *b)
2014 UINT32 ch = *(UINT32 *)a;
2015 DWRITE_UNICODE_RANGE *range = (DWRITE_UNICODE_RANGE *)b;
2017 if (ch > range->last)
2018 return 1;
2019 else if (ch < range->first)
2020 return -1;
2021 else
2022 return 0;
2025 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
2027 UINT32 i;
2029 for (i = 0; i < fallback->mappings_count; i++) {
2030 struct fallback_mapping *mapping = &fallback->mappings[i];
2032 if (bsearch(&ch, mapping->ranges, mapping->ranges_count, sizeof(*mapping->ranges),
2033 compare_mapping_range) != NULL)
2034 return mapping;
2037 return NULL;
2040 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
2041 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
2043 IDWriteFontFamily *family;
2044 BOOL exists = FALSE;
2045 HRESULT hr;
2046 UINT32 i;
2048 *font = NULL;
2050 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
2051 if (FAILED(hr))
2052 return hr;
2054 if (!exists)
2055 return E_FAIL;
2057 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
2058 if (FAILED(hr))
2059 return hr;
2061 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
2062 IDWriteFontFamily_Release(family);
2063 return hr;
2066 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
2068 HRESULT hr = S_OK;
2069 UINT32 i;
2071 for (i = 0; i < length; i++) {
2072 UINT16 script = get_char_script(text[i]);
2073 BOOL exists;
2075 if (script == Script_Unknown || script == Script_Common) {
2076 ++*mapped_length;
2077 continue;
2080 /* stop on first unsupported character */
2081 exists = FALSE;
2082 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
2083 if (hr == S_OK && exists)
2084 ++*mapped_length;
2085 else
2086 break;
2089 return hr;
2092 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
2093 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2094 IDWriteFont **mapped_font)
2096 const struct fallback_mapping *mapping;
2097 HRESULT hr;
2098 UINT32 i;
2100 *mapped_font = NULL;
2102 mapping = find_fallback_mapping(fallback, text[0]);
2103 if (!mapping) {
2104 WARN("No mapping range for %#x.\n", text[0]);
2105 return E_FAIL;
2108 /* Now let's see what fallback can handle. Pick first font that could be created. */
2109 for (i = 0; i < mapping->families_count; i++) {
2110 hr = create_matching_font((IDWriteFontCollection *)fallback->systemcollection, mapping->families[i],
2111 weight, style, stretch, mapped_font);
2112 if (hr == S_OK) {
2113 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping->families[i]));
2114 break;
2118 if (!*mapped_font) {
2119 WARN("Failed to create fallback font.\n");
2120 return E_FAIL;
2123 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
2124 if (FAILED(hr))
2125 WARN("Mapping with fallback family %s failed, hr %#lx.\n", debugstr_w(mapping->families[i]), hr);
2127 if (!*mapped_length) {
2128 IDWriteFont_Release(*mapped_font);
2129 *mapped_font = NULL;
2132 return *mapped_length ? S_OK : E_FAIL;
2135 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2136 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2137 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2138 IDWriteFont **ret_font, FLOAT *scale)
2140 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2141 WCHAR *buff = NULL;
2142 const WCHAR *text;
2143 HRESULT hr;
2145 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2146 debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2148 *mapped_length = 0;
2149 *scale = 1.0f;
2150 *ret_font = NULL;
2152 if (!source)
2153 return E_INVALIDARG;
2155 if (length == 0)
2156 return S_OK;
2158 if (!basecollection)
2159 basecollection = (IDWriteFontCollection*)fallback->systemcollection;
2161 hr = get_text_source_ptr(source, position, length, &text, &buff);
2162 if (FAILED(hr))
2163 goto done;
2165 if (basefamily && *basefamily) {
2166 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
2167 if (FAILED(hr))
2168 goto done;
2170 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
2171 if (FAILED(hr))
2172 goto done;
2175 if (!*mapped_length) {
2176 IDWriteFont *mapped_font;
2178 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
2179 if (FAILED(hr)) {
2180 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2181 if (*ret_font) {
2182 *mapped_length = length;
2183 hr = S_OK;
2186 else {
2187 if (*ret_font)
2188 IDWriteFont_Release(*ret_font);
2189 *ret_font = mapped_font;
2193 done:
2194 free(buff);
2195 return hr;
2198 static HRESULT WINAPI fontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2199 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2200 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 values_count, UINT32 *mapped_length, FLOAT *scale,
2201 IDWriteFontFace5 **ret_fontface)
2203 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2204 debugstr_w(basefamily), axis_values, values_count, mapped_length, scale, ret_fontface);
2206 return E_NOTIMPL;
2209 static const IDWriteFontFallback1Vtbl fontfallbackvtbl =
2211 fontfallback_QueryInterface,
2212 fontfallback_AddRef,
2213 fontfallback_Release,
2214 fontfallback_MapCharacters,
2215 fontfallback1_MapCharacters,
2218 HRESULT create_system_fontfallback(IDWriteFactory7 *factory, IDWriteFontFallback1 **ret)
2220 struct dwrite_fontfallback *fallback;
2222 *ret = NULL;
2224 if (!(fallback = calloc(1, sizeof(*fallback))))
2225 return E_OUTOFMEMORY;
2227 fallback->IDWriteFontFallback1_iface.lpVtbl = &fontfallbackvtbl;
2228 fallback->factory = factory;
2229 fallback->mappings = (struct fallback_mapping *)fontfallback_neutral_data;
2230 fallback->mappings_count = ARRAY_SIZE(fontfallback_neutral_data);
2231 IDWriteFactory5_GetSystemFontCollection((IDWriteFactory5 *)fallback->factory, FALSE, &fallback->systemcollection, FALSE);
2233 *ret = &fallback->IDWriteFontFallback1_iface;
2235 return S_OK;
2238 void release_system_fontfallback(IDWriteFontFallback1 *iface)
2240 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2241 IDWriteFontCollection1_Release(fallback->systemcollection);
2242 free(fallback);
2245 static ULONG WINAPI customfontfallback_AddRef(IDWriteFontFallback1 *iface)
2247 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2248 ULONG refcount = InterlockedIncrement(&fallback->refcount);
2250 TRACE("%p, refcount %lu.\n", iface, refcount);
2252 return refcount;
2255 static ULONG WINAPI customfontfallback_Release(IDWriteFontFallback1 *iface)
2257 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2258 ULONG refcount = InterlockedDecrement(&fallback->refcount);
2260 TRACE("%p, refcount %lu.\n", iface, refcount);
2262 if (!refcount)
2264 IDWriteFactory7_Release(fallback->factory);
2265 free(fallback);
2268 return refcount;
2271 static HRESULT WINAPI customfontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2272 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2273 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2274 IDWriteFont **ret_font, FLOAT *scale)
2276 FIXME("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length,
2277 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2279 return E_NOTIMPL;
2282 static HRESULT WINAPI customfontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2283 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2284 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 values_count, UINT32 *mapped_length, FLOAT *scale,
2285 IDWriteFontFace5 **ret_fontface)
2287 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2288 debugstr_w(basefamily), axis_values, values_count, mapped_length, scale, ret_fontface);
2290 return E_NOTIMPL;
2293 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl =
2295 fontfallback_QueryInterface,
2296 customfontfallback_AddRef,
2297 customfontfallback_Release,
2298 customfontfallback_MapCharacters,
2299 customfontfallback1_MapCharacters,
2302 static HRESULT WINAPI fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder *iface, REFIID riid, void **obj)
2304 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2306 if (IsEqualIID(riid, &IID_IDWriteFontFallbackBuilder) || IsEqualIID(riid, &IID_IUnknown)) {
2307 *obj = iface;
2308 IDWriteFontFallbackBuilder_AddRef(iface);
2309 return S_OK;
2312 WARN("%s not implemented.\n", debugstr_guid(riid));
2314 *obj = NULL;
2315 return E_NOINTERFACE;
2318 static ULONG WINAPI fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder *iface)
2320 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2321 ULONG refcount = InterlockedIncrement(&fallbackbuilder->refcount);
2323 TRACE("%p, refcount %ld.\n", iface, refcount);
2325 return refcount;
2328 static ULONG WINAPI fontfallbackbuilder_Release(IDWriteFontFallbackBuilder *iface)
2330 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2331 ULONG refcount = InterlockedDecrement(&fallbackbuilder->refcount);
2332 size_t i;
2334 TRACE("%p, refcount %ld.\n", iface, refcount);
2336 if (!refcount)
2338 for (i = 0; i < fallbackbuilder->count; ++i)
2340 struct fallback_mapping *mapping = &fallbackbuilder->mappings[i];
2341 UINT32 j;
2343 for (j = 0; j < mapping->families_count; j++)
2344 free(mapping->families[j]);
2345 free(mapping->families);
2347 if (mapping->collection)
2348 IDWriteFontCollection_Release(mapping->collection);
2349 free(mapping->ranges);
2350 free(mapping->locale);
2353 IDWriteFactory7_Release(fallbackbuilder->factory);
2354 free(fallbackbuilder->mappings);
2355 free(fallbackbuilder);
2358 return refcount;
2361 static HRESULT WINAPI fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder *iface,
2362 const DWRITE_UNICODE_RANGE *ranges, UINT32 ranges_count, WCHAR const **target_families, UINT32 families_count,
2363 IDWriteFontCollection *collection, WCHAR const *locale, WCHAR const *base_family, FLOAT scale)
2365 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2366 struct fallback_mapping *mapping;
2367 UINT32 i;
2369 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface, ranges, ranges_count, target_families, families_count,
2370 collection, debugstr_w(locale), debugstr_w(base_family), scale);
2372 if (!ranges || ranges_count == 0 || !target_families || families_count == 0 || scale < 0.0f)
2373 return E_INVALIDARG;
2375 if (base_family)
2376 FIXME("base family ignored.\n");
2378 if (!dwrite_array_reserve((void **)&fallbackbuilder->mappings, &fallbackbuilder->size, fallbackbuilder->count + 1,
2379 sizeof(*fallbackbuilder->mappings)))
2381 return E_OUTOFMEMORY;
2384 mapping = &fallbackbuilder->mappings[fallbackbuilder->count++];
2386 mapping->ranges = calloc(ranges_count, sizeof(*mapping->ranges));
2387 memcpy(mapping->ranges, ranges, sizeof(*mapping->ranges) * ranges_count);
2388 mapping->ranges_count = ranges_count;
2389 mapping->families = calloc(families_count, sizeof(*mapping->families));
2390 mapping->families_count = families_count;
2391 for (i = 0; i < families_count; i++)
2392 mapping->families[i] = wcsdup(target_families[i]);
2393 mapping->collection = collection;
2394 if (mapping->collection)
2395 IDWriteFontCollection_AddRef(mapping->collection);
2396 mapping->locale = wcsdup(locale);
2397 mapping->scale = scale;
2399 return S_OK;
2402 static HRESULT WINAPI fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder *iface, IDWriteFontFallback *fallback)
2404 FIXME("%p, %p stub.\n", iface, fallback);
2406 return E_NOTIMPL;
2409 static HRESULT WINAPI fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder *iface,
2410 IDWriteFontFallback **ret)
2412 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2413 struct dwrite_fontfallback *fallback;
2415 TRACE("%p, %p.\n", iface, ret);
2417 *ret = NULL;
2419 if (!(fallback = calloc(1, sizeof(*fallback))))
2420 return E_OUTOFMEMORY;
2422 fallback->IDWriteFontFallback1_iface.lpVtbl = &customfontfallbackvtbl;
2423 fallback->refcount = 1;
2424 fallback->factory = fallbackbuilder->factory;
2425 IDWriteFactory7_AddRef(fallback->factory);
2427 *ret = (IDWriteFontFallback *)&fallback->IDWriteFontFallback1_iface;
2428 return S_OK;
2431 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl =
2433 fontfallbackbuilder_QueryInterface,
2434 fontfallbackbuilder_AddRef,
2435 fontfallbackbuilder_Release,
2436 fontfallbackbuilder_AddMapping,
2437 fontfallbackbuilder_AddMappings,
2438 fontfallbackbuilder_CreateFontFallback,
2441 HRESULT create_fontfallback_builder(IDWriteFactory7 *factory, IDWriteFontFallbackBuilder **ret)
2443 struct dwrite_fontfallback_builder *builder;
2445 *ret = NULL;
2447 if (!(builder = calloc(1, sizeof(*builder))))
2448 return E_OUTOFMEMORY;
2450 builder->IDWriteFontFallbackBuilder_iface.lpVtbl = &fontfallbackbuildervtbl;
2451 builder->refcount = 1;
2452 builder->factory = factory;
2453 IDWriteFactory7_AddRef(builder->factory);
2455 *ret = &builder->IDWriteFontFallbackBuilder_iface;
2456 return S_OK;