d3dx9: Use parameter data for the first constant only in d3dx_set_shader_const_state().
[wine.git] / dlls / dwrite / analyzer.c
blobca6dca1e4d7b4f657fb7c8ce4baebc929a8027fb
1 /*
2 * Text analyzer
4 * Copyright 2011 Aric Stewart for CodeWeavers
5 * Copyright 2012, 2014 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include <math.h>
26 #include "dwrite_private.h"
27 #include "scripts.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
31 extern const unsigned short wine_linebreak_table[] DECLSPEC_HIDDEN;
32 extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN;
34 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
35 #define NATIVE_DIGITS_LEN 11
37 struct dwritescript_properties {
38 DWRITE_SCRIPT_PROPERTIES props;
39 UINT32 scripttags[3]; /* Maximum 2 script tags, 0-terminated. */
40 BOOL is_complex;
41 const struct scriptshaping_ops *ops;
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') }, FALSE, &latn_shaping_ops },
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') } },
191 #undef _OT
193 const char *debugstr_sa_script(UINT16 script)
195 return script < Script_LastId ? debugstr_tag(dwritescripts_properties[script].props.isoScriptCode) : "undefined";
198 /* system font falback configuration */
199 static const WCHAR meiryoW[] = {'M','e','i','r','y','o',0};
201 static const WCHAR *cjk_families[] = { meiryoW };
203 static const DWRITE_UNICODE_RANGE cjk_ranges[] =
205 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
206 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
207 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
210 struct fallback_mapping {
211 DWRITE_UNICODE_RANGE *ranges;
212 UINT32 ranges_count;
213 WCHAR **families;
214 UINT32 families_count;
215 IDWriteFontCollection *collection;
216 WCHAR *locale;
217 FLOAT scale;
220 static const struct fallback_mapping fontfallback_neutral_data[] = {
221 #define MAPPING_RANGE(ranges, families) \
222 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
223 (WCHAR **)families, ARRAY_SIZE(families) }
225 MAPPING_RANGE(cjk_ranges, cjk_families),
227 #undef MAPPING_RANGE
230 struct dwrite_fontfallback {
231 IDWriteFontFallback IDWriteFontFallback_iface;
232 LONG ref;
233 IDWriteFactory5 *factory;
234 IDWriteFontCollection1 *systemcollection;
235 struct fallback_mapping *mappings;
236 UINT32 mappings_count;
239 struct dwrite_fontfallback_builder {
240 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface;
241 LONG ref;
242 IDWriteFactory5 *factory;
243 struct fallback_mapping *mappings;
244 UINT32 mappings_count;
245 UINT32 mappings_capacity;
248 struct dwrite_numbersubstitution {
249 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
250 LONG ref;
252 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
253 WCHAR *locale;
254 BOOL ignore_user_override;
257 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
259 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
262 static struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface);
264 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback(IDWriteFontFallback *iface)
266 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback_iface);
269 static inline struct dwrite_fontfallback_builder *impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder *iface)
271 return CONTAINING_RECORD(iface, struct dwrite_fontfallback_builder, IDWriteFontFallbackBuilder_iface);
274 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
276 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
277 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
278 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
279 return ch;
281 return 0;
284 static inline UINT16 get_char_script(WCHAR c)
286 UINT16 script = get_table_entry(wine_scripts_table, c);
287 return script == Script_Inherited ? Script_Unknown : script;
290 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
292 DWRITE_SCRIPT_ANALYSIS sa;
294 sa.script = get_char_script(c);
295 sa.shapes = iscntrlW(c) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
296 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
297 return sa;
300 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
302 DWRITE_SCRIPT_ANALYSIS sa;
303 UINT32 pos, i, seq_length;
305 if (!length)
306 return S_OK;
308 sa = get_char_sa(*text);
310 pos = position;
311 seq_length = 1;
313 for (i = 1; i < length; i++)
315 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
317 /* Unknown type is ignored when preceded or followed by another script */
318 switch (sa.script) {
319 case Script_Unknown:
320 sa.script = cur_sa.script;
321 break;
322 case Script_Common:
323 if (cur_sa.script == Script_Unknown)
324 cur_sa.script = sa.script;
325 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
326 sa.script = cur_sa.script;
327 break;
328 default:
329 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
330 cur_sa.script = sa.script;
333 /* this is a length of a sequence to be reported next */
334 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
335 seq_length++;
336 else {
337 HRESULT hr;
339 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
340 if (FAILED(hr)) return hr;
341 pos = position + i;
342 seq_length = 1;
343 sa = cur_sa;
347 /* one char length case or normal completion call */
348 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
351 struct linebreaking_state {
352 DWRITE_LINE_BREAKPOINT *breakpoints;
353 UINT32 count;
356 enum BreakConditionLocation {
357 BreakConditionBefore,
358 BreakConditionAfter
361 enum linebreaking_classes {
362 b_BK = 1,
363 b_CR,
364 b_LF,
365 b_CM,
366 b_SG,
367 b_GL,
368 b_CB,
369 b_SP,
370 b_ZW,
371 b_NL,
372 b_WJ,
373 b_JL,
374 b_JV,
375 b_JT,
376 b_H2,
377 b_H3,
378 b_XX,
379 b_OP,
380 b_CL,
381 b_CP,
382 b_QU,
383 b_NS,
384 b_EX,
385 b_SY,
386 b_IS,
387 b_PR,
388 b_PO,
389 b_NU,
390 b_AL,
391 b_ID,
392 b_IN,
393 b_HY,
394 b_BB,
395 b_BA,
396 b_SA,
397 b_AI,
398 b_B2,
399 b_HL,
400 b_CJ,
401 b_RI,
402 b_EB,
403 b_EM,
404 b_ZWJ,
407 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
409 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
410 return TRUE;
412 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
413 return TRUE;
415 return FALSE;
418 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
419 set to "can break" and could only be changed once. */
420 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
421 struct linebreaking_state *state)
423 if (location == BreakConditionBefore) {
424 if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
425 return;
426 state->breakpoints[pos].breakConditionBefore = condition;
427 if (pos > 0)
428 state->breakpoints[pos-1].breakConditionAfter = condition;
430 else {
431 if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
432 return;
433 state->breakpoints[pos].breakConditionAfter = condition;
434 if (pos + 1 < state->count)
435 state->breakpoints[pos+1].breakConditionBefore = condition;
439 BOOL lb_is_newline_char(WCHAR ch)
441 short c = get_table_entry(wine_linebreak_table, ch);
442 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
445 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
447 struct linebreaking_state state;
448 short *break_class;
449 int i, j;
451 break_class = heap_calloc(count, sizeof(*break_class));
452 if (!break_class)
453 return E_OUTOFMEMORY;
455 state.breakpoints = breakpoints;
456 state.count = count;
458 for (i = 0; i < count; i++)
460 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
462 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
463 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
464 breakpoints[i].isWhitespace = !!isspaceW(text[i]);
465 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
466 breakpoints[i].padding = 0;
468 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
469 switch (break_class[i])
471 case b_AI:
472 case b_SA:
473 case b_SG:
474 case b_XX:
475 break_class[i] = b_AL;
476 break;
477 case b_CJ:
478 break_class[i] = b_NS;
479 break;
483 /* LB2 - never break at the start */
484 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
485 /* LB3 - always break at the end. */
486 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
488 /* LB4 - LB6 - mandatory breaks. */
489 for (i = 0; i < count; i++)
491 switch (break_class[i])
493 /* LB4 - LB6 */
494 case b_CR:
495 /* LB5 - don't break CR x LF */
496 if (i < count-1 && break_class[i+1] == b_LF)
498 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
499 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
500 break;
502 case b_LF:
503 case b_NL:
504 case b_BK:
505 /* LB4 - LB5 - always break after hard breaks */
506 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
507 /* LB6 - do not break before hard breaks */
508 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
509 break;
513 /* LB7 - LB8 - explicit breaks and non-breaks */
514 for (i = 0; i < count; i++)
516 switch (break_class[i])
518 /* LB7 - do not break before spaces or zero-width space */
519 case b_SP:
520 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
521 break;
522 case b_ZW:
523 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
525 /* LB8 - break before character after zero-width space, skip spaces in-between */
526 j = i;
527 while (j < count-1 && break_class[j+1] == b_SP)
528 j++;
529 if (j < count-1 && break_class[j+1] != b_ZW)
530 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
531 break;
532 /* LB8a - do not break after ZWJ */
533 case b_ZWJ:
534 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
535 break;
539 /* LB9 - LB10 - combining marks */
540 for (i = 0; i < count; i++)
542 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
544 if (i > 0)
546 switch (break_class[i-1])
548 case b_SP:
549 case b_BK:
550 case b_CR:
551 case b_LF:
552 case b_NL:
553 case b_ZW:
554 break_class[i] = b_AL;
555 break;
556 default:
558 break_class[i] = break_class[i-1];
559 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
563 else break_class[i] = b_AL;
567 for (i = 0; i < count; i++)
569 switch (break_class[i])
571 /* LB11 - don't break before and after word joiner */
572 case b_WJ:
573 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
574 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
575 break;
576 /* LB12 - don't break after glue */
577 case b_GL:
578 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
579 /* LB12a */
580 if (i > 0)
582 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
583 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
585 break;
586 /* LB13 */
587 case b_CL:
588 case b_CP:
589 case b_EX:
590 case b_IS:
591 case b_SY:
592 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
593 break;
594 /* LB14 - do not break after OP, even after spaces */
595 case b_OP:
596 j = i;
597 while (j < count-1 && break_class[j+1] == b_SP)
598 j++;
599 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
600 break;
601 /* LB15 - do not break within QU-OP, even with intervening spaces */
602 case b_QU:
603 j = i;
604 while (j < count-1 && break_class[j+1] == b_SP)
605 j++;
606 if (j < count - 1 && break_class[j+1] == b_OP)
607 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
608 break;
609 /* LB16 */
610 case b_NS:
611 j = i-1;
612 while(j > 0 && break_class[j] == b_SP)
613 j--;
614 if (break_class[j] == b_CL || break_class[j] == b_CP)
615 for (j++; j <= i; j++)
616 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
617 break;
618 /* LB17 - do not break within B2, even with intervening spaces */
619 case b_B2:
620 j = i;
621 while (j < count && break_class[j+1] == b_SP)
622 j++;
623 if (j < count - 1 && break_class[j+1] == b_B2)
624 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
625 break;
629 for (i = 0; i < count; i++)
631 switch(break_class[i])
633 /* LB18 - break is allowed after space */
634 case b_SP:
635 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
636 break;
637 /* LB19 - don't break before or after quotation mark */
638 case b_QU:
639 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
640 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
641 break;
642 /* LB20 */
643 case b_CB:
644 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
645 if (i < count - 1 && break_class[i+1] != b_QU)
646 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
647 break;
648 /* LB21 */
649 case b_BA:
650 case b_HY:
651 case b_NS:
652 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
653 break;
654 case b_BB:
655 if (i < count - 1 && break_class[i+1] != b_CB)
656 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
657 break;
658 /* LB21a, LB21b */
659 case b_HL:
660 /* LB21a */
661 if (i < count-1)
662 switch (break_class[i+1])
664 case b_HY:
665 case b_BA:
666 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
668 /* LB21b */
669 if (i > 0 && break_class[i-1] == b_SY)
670 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
671 break;
672 /* LB22 */
673 case b_IN:
674 if (i > 0)
676 switch (break_class[i-1])
678 case b_AL:
679 case b_HL:
680 case b_EX:
681 case b_ID:
682 case b_EB:
683 case b_EM:
684 case b_IN:
685 case b_NU:
686 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
689 break;
692 if (i < count-1)
694 /* LB23 - do not break between digits and letters */
695 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
696 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
697 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
698 (break_class[i] == b_NU && break_class[i+1] == b_HL))
699 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
701 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
702 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
703 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
704 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
705 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
706 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
707 (break_class[i] == b_EB && break_class[i+1] == b_PO))
708 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
710 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
711 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
712 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
713 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
714 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
715 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
716 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
717 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
718 (break_class[i] == b_HL && break_class[i+1] == b_PO))
719 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
721 /* LB25 */
722 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
723 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
724 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
725 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
726 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
727 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
728 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
729 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
730 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
731 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
732 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
733 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
734 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
735 (break_class[i] == b_SY && break_class[i+1] == b_NU))
736 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
738 /* LB26 */
739 if (break_class[i] == b_JL)
741 switch (break_class[i+1])
743 case b_JL:
744 case b_JV:
745 case b_H2:
746 case b_H3:
747 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
750 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
751 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
752 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
753 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
754 break_class[i+1] == b_JT)
755 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
757 /* LB27 */
758 switch (break_class[i])
760 case b_JL:
761 case b_JV:
762 case b_JT:
763 case b_H2:
764 case b_H3:
765 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
766 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
768 if (break_class[i] == b_PR)
770 switch (break_class[i+1])
772 case b_JL:
773 case b_JV:
774 case b_JT:
775 case b_H2:
776 case b_H3:
777 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
781 /* LB28 */
782 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
783 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
784 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
785 (break_class[i] == b_HL && break_class[i+1] == b_HL))
786 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
788 /* LB29 */
789 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
790 (break_class[i] == b_IS && break_class[i+1] == b_HL))
791 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
793 /* LB30 */
794 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
795 break_class[i+1] == b_OP)
796 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
797 if (break_class[i] == b_CP &&
798 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
799 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
801 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
802 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
803 unsigned int c = 0;
805 j = i + 1;
806 while (j > 0 && break_class[--j] == b_RI)
807 c++;
809 if ((c & 1) == 0)
810 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
813 /* LB30b - do not break between an emoji base and an emoji modifier */
814 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
815 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
819 /* LB31 - allow breaks everywhere else. */
820 for (i = 0; i < count; i++)
822 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
823 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
826 heap_free(break_class);
827 return S_OK;
830 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
832 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
834 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
835 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
836 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
837 IsEqualIID(riid, &IID_IUnknown))
839 *obj = iface;
840 return S_OK;
843 WARN("%s not implemented.\n", debugstr_guid(riid));
845 *obj = NULL;
846 return E_NOINTERFACE;
849 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
851 return 2;
854 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
856 return 1;
859 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
860 data after a first request. */
861 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
863 HRESULT hr;
864 UINT32 len;
866 *buff = NULL;
867 *text = NULL;
868 len = 0;
869 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
870 if (FAILED(hr)) return hr;
872 if (len < length) {
873 UINT32 read;
875 *buff = heap_alloc(length*sizeof(WCHAR));
876 if (!*buff)
877 return E_OUTOFMEMORY;
878 memcpy(*buff, *text, len*sizeof(WCHAR));
879 read = len;
881 while (read < length && *text) {
882 *text = NULL;
883 len = 0;
884 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
885 if (FAILED(hr)) {
886 heap_free(*buff);
887 return hr;
889 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
890 read += len;
893 *text = *buff;
896 return hr;
899 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
900 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
902 WCHAR *buff = NULL;
903 const WCHAR *text;
904 HRESULT hr;
906 TRACE("(%p %u %u %p)\n", source, position, length, sink);
908 if (length == 0)
909 return S_OK;
911 hr = get_text_source_ptr(source, position, length, &text, &buff);
912 if (FAILED(hr))
913 return hr;
915 hr = analyze_script(text, position, length, sink);
916 heap_free(buff);
918 return hr;
921 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
922 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
924 UINT8 *levels = NULL, *explicit = NULL;
925 UINT8 baselevel, level, explicit_level;
926 UINT32 pos, i, seq_length;
927 WCHAR *buff = NULL;
928 const WCHAR *text;
929 HRESULT hr;
931 TRACE("(%p %u %u %p)\n", source, position, length, sink);
933 if (!length)
934 return S_OK;
936 hr = get_text_source_ptr(source, position, length, &text, &buff);
937 if (FAILED(hr))
938 return hr;
940 levels = heap_calloc(length, sizeof(*levels));
941 explicit = heap_calloc(length, sizeof(*explicit));
943 if (!levels || !explicit) {
944 hr = E_OUTOFMEMORY;
945 goto done;
948 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
949 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
950 if (FAILED(hr))
951 goto done;
953 level = levels[0];
954 explicit_level = explicit[0];
955 pos = position;
956 seq_length = 1;
958 for (i = 1; i < length; i++) {
959 if (levels[i] == level && explicit[i] == explicit_level)
960 seq_length++;
961 else {
962 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
963 if (FAILED(hr))
964 goto done;
966 pos += seq_length;
967 seq_length = 1;
968 level = levels[i];
969 explicit_level = explicit[i];
972 /* one char length case or normal completion call */
973 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
975 done:
976 heap_free(explicit);
977 heap_free(levels);
978 heap_free(buff);
980 return hr;
983 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
984 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
986 static int once;
988 if (!once++)
989 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
990 return S_OK;
993 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
994 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
996 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
997 WCHAR *buff = NULL;
998 const WCHAR *text;
999 HRESULT hr;
1000 UINT32 len;
1002 TRACE("(%p %u %u %p)\n", source, position, length, sink);
1004 if (length == 0)
1005 return S_OK;
1007 /* get some, check for length */
1008 text = NULL;
1009 len = 0;
1010 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
1011 if (FAILED(hr)) return hr;
1013 if (len < length) {
1014 UINT32 read;
1016 buff = heap_calloc(length, sizeof(*buff));
1017 if (!buff)
1018 return E_OUTOFMEMORY;
1019 memcpy(buff, text, len*sizeof(WCHAR));
1020 read = len;
1022 while (read < length && text) {
1023 text = NULL;
1024 len = 0;
1025 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
1026 if (FAILED(hr))
1027 goto done;
1028 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
1029 read += len;
1032 text = buff;
1035 breakpoints = heap_calloc(length, sizeof(*breakpoints));
1036 if (!breakpoints) {
1037 hr = E_OUTOFMEMORY;
1038 goto done;
1041 hr = analyze_linebreaks(text, length, breakpoints);
1042 if (FAILED(hr))
1043 goto done;
1045 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1047 done:
1048 heap_free(breakpoints);
1049 heap_free(buff);
1051 return hr;
1054 static UINT32 get_opentype_language(const WCHAR *locale)
1056 UINT32 language = DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1058 if (locale) {
1059 WCHAR tag[5];
1060 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, ARRAY_SIZE(tag)))
1061 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1064 return language;
1067 static DWRITE_NUMBER_SUBSTITUTION_METHOD get_number_substitutes(IDWriteNumberSubstitution *substitution, WCHAR *digits)
1069 struct dwrite_numbersubstitution *numbersubst = unsafe_impl_from_IDWriteNumberSubstitution(substitution);
1070 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1071 WCHAR isolang[9];
1072 DWORD lctype;
1074 if (!numbersubst)
1075 return DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1077 lctype = numbersubst->ignore_user_override ? LOCALE_NOUSEROVERRIDE : 0;
1079 if (numbersubst->method == DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE) {
1080 DWORD value;
1082 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1083 if (GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, (WCHAR *)&value, 2)) {
1084 switch (value)
1086 case 0:
1087 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL;
1088 break;
1089 case 2:
1090 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL;
1091 break;
1092 case 1:
1093 default:
1094 if (value != 1)
1095 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value, debugstr_w(numbersubst->locale));
1098 else
1099 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst->locale));
1101 else
1102 method = numbersubst->method;
1104 digits[0] = 0;
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))) {
1113 static const WCHAR arW[] = {'a','r',0};
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 (!strcmpW(arW, isolang)) {
1118 strcpyW(digits, arabicW);
1119 break;
1122 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1123 break;
1124 default:
1128 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !*digits) {
1129 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst->locale), method);
1130 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1133 return method;
1136 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES const **features,
1137 UINT32 const *feature_range_lengths, UINT32 feature_ranges)
1139 UINT32 i, j, start;
1141 if (!TRACE_ON(dwrite) || !features)
1142 return;
1144 for (i = 0, start = 0; i < feature_ranges; start += feature_range_lengths[i++]) {
1145 TRACE("feature range [%u,%u)\n", start, start + feature_range_lengths[i]);
1146 for (j = 0; j < features[i]->featureCount; j++)
1147 TRACE("feature %s, parameter %u\n", debugstr_tag(features[i]->features[j].nameTag),
1148 features[i]->features[j].parameter);
1152 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1153 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1154 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1155 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1156 UINT32 const* feature_range_lengths, UINT32 feature_ranges, UINT32 max_glyph_count,
1157 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
1158 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1160 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1161 struct scriptshaping_context context;
1162 struct dwrite_fontface *font_obj;
1163 WCHAR digits[NATIVE_DIGITS_LEN];
1164 BOOL update_cluster;
1165 WCHAR *string;
1166 UINT32 i, g;
1167 HRESULT hr = S_OK;
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, glyph_indices,
1172 glyph_props, actual_glyph_count);
1174 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1176 if (max_glyph_count < length)
1177 return E_NOT_SUFFICIENT_BUFFER;
1179 string = heap_calloc(length, sizeof(*string));
1180 if (!string)
1181 return E_OUTOFMEMORY;
1183 method = get_number_substitutes(substitution, digits);
1185 for (i = 0; i < length; i++) {
1186 /* FIXME: set to better values */
1187 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
1188 glyph_props[i].isClusterStart = 1;
1189 glyph_props[i].isDiacritic = 0;
1190 glyph_props[i].isZeroWidthSpace = 0;
1191 glyph_props[i].reserved = 0;
1193 /* FIXME: have the shaping engine set this */
1194 text_props[i].isShapedAlone = 0;
1195 text_props[i].reserved = 0;
1197 clustermap[i] = i;
1199 string[i] = text[i];
1200 switch (method)
1202 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1203 if (!is_rtl)
1204 break;
1205 /* fallthrough */
1206 default:
1207 if (string[i] >= '0' && string[i] <= '9')
1208 string[i] = digits[string[i] - '0'];
1209 break;
1210 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE:
1215 for (; i < max_glyph_count; i++) {
1216 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
1217 glyph_props[i].isClusterStart = 0;
1218 glyph_props[i].isDiacritic = 0;
1219 glyph_props[i].isZeroWidthSpace = 0;
1220 glyph_props[i].reserved = 0;
1223 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
1224 UINT32 codepoint;
1226 if (!update_cluster) {
1227 codepoint = decode_surrogate_pair(string, i, length);
1228 if (!codepoint)
1229 codepoint = is_rtl ? bidi_get_mirrored_char(string[i]) : string[i];
1230 else
1231 update_cluster = TRUE;
1233 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
1234 if (FAILED(hr))
1235 goto done;
1237 g++;
1239 else {
1240 INT32 k;
1242 update_cluster = FALSE;
1243 /* mark surrogate halves with same cluster */
1244 clustermap[i] = clustermap[i-1];
1245 /* update following clusters */
1246 for (k = i + 1; k >= 0 && k < length; k++)
1247 clustermap[k]--;
1250 *actual_glyph_count = g;
1252 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1254 context.cache = fontface_get_shaping_cache(font_obj);
1255 context.text = text;
1256 context.length = length;
1257 context.is_rtl = is_rtl;
1258 context.language_tag = get_opentype_language(locale);
1260 /* FIXME: apply default features */
1262 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1264 done:
1265 heap_free(string);
1267 return hr;
1270 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1271 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1272 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1273 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, BOOL is_sideways, BOOL is_rtl,
1274 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1275 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1277 const struct dwritescript_properties *scriptprops;
1278 struct dwrite_fontface *font_obj;
1279 unsigned int i, script;
1280 HRESULT hr = S_OK;
1282 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),
1283 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1284 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_lengths,
1285 feature_ranges, advances, offsets);
1287 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1289 if (glyph_count == 0)
1290 return S_OK;
1292 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1294 for (i = 0; i < glyph_count; ++i)
1296 if (glyph_props[i].isZeroWidthSpace)
1297 advances[i] = 0.0f;
1298 else
1299 advances[i] = fontface_get_scaled_design_advance(font_obj, DWRITE_MEASURING_MODE_NATURAL, emSize, 1.0f,
1300 NULL, glyphs[i], is_sideways);
1301 offsets[i].advanceOffset = 0.0f;
1302 offsets[i].ascenderOffset = 0.0f;
1305 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1307 scriptprops = &dwritescripts_properties[script];
1308 if (scriptprops->ops && scriptprops->ops->gpos_features)
1310 struct scriptshaping_context context;
1312 context.cache = fontface_get_shaping_cache(font_obj);
1313 context.text = text;
1314 context.length = text_len;
1315 context.is_rtl = is_rtl;
1316 context.u.pos.glyphs = glyphs;
1317 context.u.pos.glyph_props = glyph_props;
1318 context.glyph_count = glyph_count;
1319 context.emsize = emSize;
1320 context.measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
1321 context.advances = advances;
1322 context.offsets = offsets;
1323 context.language_tag = get_opentype_language(locale);
1325 hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features);
1328 return hr;
1331 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1332 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1333 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1334 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, float ppdip,
1335 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1336 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1337 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1339 const struct dwritescript_properties *scriptprops;
1340 DWRITE_MEASURING_MODE measuring_mode;
1341 struct dwrite_fontface *font_obj;
1342 unsigned int i, script;
1343 HRESULT hr = S_OK;
1345 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1346 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1347 transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale),
1348 features, feature_range_lengths, feature_ranges, advances, offsets);
1350 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1352 if (glyph_count == 0)
1353 return S_OK;
1355 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1357 measuring_mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
1359 for (i = 0; i < glyph_count; ++i)
1361 if (glyph_props[i].isZeroWidthSpace)
1362 advances[i] = 0.0f;
1363 else
1364 advances[i] = fontface_get_scaled_design_advance(font_obj, measuring_mode, emSize, ppdip,
1365 transform, glyphs[i], is_sideways);
1366 offsets[i].advanceOffset = 0.0f;
1367 offsets[i].ascenderOffset = 0.0f;
1370 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1372 scriptprops = &dwritescripts_properties[script];
1373 if (scriptprops->ops && scriptprops->ops->gpos_features)
1375 struct scriptshaping_context context;
1377 context.cache = fontface_get_shaping_cache(font_obj);
1378 context.text = text;
1379 context.length = text_len;
1380 context.is_rtl = is_rtl;
1381 context.u.pos.glyphs = glyphs;
1382 context.u.pos.glyph_props = glyph_props;
1383 context.glyph_count = glyph_count;
1384 context.emsize = emSize * ppdip;
1385 context.measuring_mode = measuring_mode;
1386 context.advances = advances;
1387 context.offsets = offsets;
1388 context.language_tag = get_opentype_language(locale);
1390 hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features);
1393 return hr;
1396 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1398 FLOAT advance = 0.0f;
1399 for (; start < end; start++)
1400 advance += advances[start];
1401 return advance;
1404 static HRESULT apply_cluster_spacing(float leading_spacing, float trailing_spacing, float min_advance_width,
1405 unsigned int start, unsigned int end, float const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1406 DWRITE_SHAPING_GLYPH_PROPERTIES const *glyph_props, float *modified_advances,
1407 DWRITE_GLYPH_OFFSET *modified_offsets)
1409 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1410 unsigned int first_spacing, last_spacing, i;
1411 float advance, origin = 0.0f, *deltas;
1412 BOOL is_spacing_cluster = FALSE;
1414 if (modified_advances != advances)
1415 memcpy(&modified_advances[start], &advances[start], (end - start + 1) * sizeof(*advances));
1416 if (modified_offsets != offsets)
1417 memcpy(&modified_offsets[start], &offsets[start], (end - start + 1) * sizeof(*offsets));
1419 for (first_spacing = start; first_spacing <= end; ++first_spacing)
1421 if ((is_spacing_cluster = !glyph_props[first_spacing].isZeroWidthSpace))
1422 break;
1425 /* Nothing to adjust if there is no spacing glyphs. */
1426 if (!is_spacing_cluster)
1427 return S_OK;
1429 for (last_spacing = end; last_spacing >= start; --last_spacing)
1431 if (!glyph_props[last_spacing].isZeroWidthSpace)
1432 break;
1435 deltas = heap_calloc(end - start + 1, sizeof(*deltas));
1436 if (!deltas)
1437 return E_OUTOFMEMORY;
1439 /* Cluster advance, note that properties are ignored. */
1440 origin = offsets[start].advanceOffset;
1441 for (i = start, advance = 0.0f; i <= end; ++i)
1443 float cur = advance + offsets[i].advanceOffset;
1445 deltas[i - start] = cur - origin;
1447 advance += advances[i];
1448 origin = cur;
1451 /* Negative spacing. */
1452 if (leading_spacing < 0.0f)
1454 advance += leading_spacing;
1455 modified_advances[first_spacing] += leading_spacing;
1456 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1459 if (trailing_spacing < 0.0f)
1461 advance += trailing_spacing;
1462 modified_advances[last_spacing] += trailing_spacing;
1465 /* Minimal advance. */
1466 advance = min_advance_width - advance;
1467 if (advance > 0.0f) {
1468 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1469 float half = advance / 2.0f;
1471 if (!reduced)
1473 modified_advances[first_spacing] += half;
1474 modified_advances[last_spacing] += half;
1475 modified_offsets[first_spacing].advanceOffset += half;
1477 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1479 modified_advances[first_spacing] += half;
1480 modified_advances[last_spacing] += half;
1481 modified_offsets[first_spacing].advanceOffset += half;
1483 else if (leading_spacing < 0.0f)
1485 modified_advances[first_spacing] += advance;
1486 modified_offsets[first_spacing].advanceOffset += advance;
1488 else
1489 modified_advances[last_spacing] += advance;
1492 /* Positive spacing. */
1493 if (leading_spacing > 0.0f)
1495 modified_advances[first_spacing] += leading_spacing;
1496 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1499 if (trailing_spacing > 0.0f)
1500 modified_advances[last_spacing] += trailing_spacing;
1502 /* Update offsets to preserve original relative positions within cluster. */
1503 for (i = first_spacing; i > start; --i)
1505 unsigned int cur = i - 1;
1506 modified_offsets[cur].advanceOffset = modified_advances[cur] + modified_offsets[i].advanceOffset -
1507 deltas[i - start];
1510 for (i = first_spacing + 1; i <= end; ++i)
1512 modified_offsets[i].advanceOffset = deltas[i - start] + modified_offsets[i - 1].advanceOffset -
1513 modified_advances[i - 1];
1516 heap_free(deltas);
1518 return S_OK;
1521 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1523 UINT16 g = clustermap[start];
1524 UINT32 length = 1;
1526 while (start < text_len && clustermap[++start] == g)
1527 length++;
1528 return length;
1531 /* Applies spacing adjustments to clusters.
1533 Adjustments are applied in the following order:
1535 1. Negative adjustments
1537 Leading and trailing spacing could be negative, at this step
1538 only negative ones are actually applied. Leading spacing is only
1539 applied to leading glyph, trailing - to trailing glyph.
1541 2. Minimum advance width
1543 Advances could only be reduced at this point or unchanged. In any
1544 case it's checked if cluster advance width is less than minimum width.
1545 If it's the case advance width is incremented up to minimum value.
1547 Important part is the direction in which this increment is applied;
1548 it depends on direction from which total cluster advance was trimmed
1549 at step 1. So it could be incremented from leading, trailing, or both
1550 sides. When applied to both sides, each side gets half of difference
1551 that brings advance to minimum width.
1553 3. Positive adjustments
1555 After minimum width rule was applied, positive spacing is applied in the same
1556 way as negative one on step 1.
1558 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1559 keeps its position in coordinate system where initial advance width is counted
1560 from 0.
1562 Glyph properties
1564 It's known that isZeroWidthSpace property keeps initial advance from changing.
1567 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1568 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1569 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1570 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1572 unsigned int i;
1574 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1575 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1577 if (min_advance_width < 0.0f) {
1578 if (modified_advances != advances)
1579 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1580 return E_INVALIDARG;
1583 for (i = 0; i < len;)
1585 unsigned int length = get_cluster_length(clustermap, i, len);
1586 unsigned int start, end;
1588 start = clustermap[i];
1589 end = i + length < len ? clustermap[i + length] : glyph_count;
1591 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width, start, end - 1, advances,
1592 offsets, props, modified_advances, modified_offsets);
1594 i += length;
1597 return S_OK;
1600 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1601 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1602 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1604 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1605 baseline_coord, exists);
1606 return E_NOTIMPL;
1609 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1610 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1612 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1613 return E_NOTIMPL;
1616 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1617 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1619 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1620 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1623 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1624 DWRITE_SCRIPT_PROPERTIES *props)
1626 TRACE("(%u %p)\n", sa.script, props);
1628 if (sa.script > Script_LastId)
1629 return E_INVALIDARG;
1631 *props = dwritescripts_properties[sa.script].props;
1632 return S_OK;
1635 static inline BOOL is_char_from_simple_script(WCHAR c)
1637 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c) ||
1638 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1639 c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e))
1640 return FALSE;
1641 else {
1642 UINT16 script = get_char_script(c);
1643 return !dwritescripts_properties[script].is_complex;
1647 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1648 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1650 HRESULT hr = S_OK;
1651 int i;
1653 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1655 *is_simple = FALSE;
1656 *len_read = 0;
1658 if (!face)
1659 return E_INVALIDARG;
1661 if (len == 0) {
1662 *is_simple = TRUE;
1663 return S_OK;
1666 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1667 for (i = 1; i < len && text[i]; i++) {
1668 if (is_char_from_simple_script(text[i])) {
1669 if (!*is_simple)
1670 break;
1672 else
1673 *is_simple = FALSE;
1676 *len_read = i;
1678 /* fetch indices */
1679 if (*is_simple && indices) {
1680 UINT32 *codepoints = heap_calloc(*len_read, sizeof(*codepoints));
1681 if (!codepoints)
1682 return E_OUTOFMEMORY;
1684 for (i = 0; i < *len_read; i++)
1685 codepoints[i] = text[i];
1687 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1688 heap_free(codepoints);
1691 return hr;
1694 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1695 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1696 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1698 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1699 debugstr_wn(text, length), clustermap, prop, jo);
1700 return E_NOTIMPL;
1703 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1704 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1705 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1707 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1708 justifiedoffsets);
1709 return E_NOTIMPL;
1712 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1713 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1714 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1715 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1716 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1717 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1719 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,
1720 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1721 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1722 return E_NOTIMPL;
1725 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1726 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1728 static const DWRITE_MATRIX transforms[] = {
1729 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1730 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1731 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1732 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1735 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1737 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1738 memset(m, 0, sizeof(*m));
1739 return E_INVALIDARG;
1742 /* for sideways case simply rotate 90 degrees more */
1743 if (is_sideways) {
1744 switch (angle) {
1745 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1746 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1747 break;
1748 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1749 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1750 break;
1751 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1752 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1753 break;
1754 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1755 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1756 break;
1757 default:
1762 *m = transforms[angle];
1764 /* shift components represent transform necessary to get from original point to
1765 rotated one in new coordinate system */
1766 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1767 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1768 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1771 return S_OK;
1774 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1775 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1776 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1778 const struct dwritescript_properties *props;
1779 const DWORD *scripts;
1780 HRESULT hr = S_OK;
1781 UINT32 language;
1783 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1784 tags);
1786 if (sa.script > Script_LastId)
1787 return E_INVALIDARG;
1789 language = get_opentype_language(locale);
1790 props = &dwritescripts_properties[sa.script];
1791 *actual_tagcount = 0;
1793 scripts = props->scripttags;
1794 while (*scripts && !*actual_tagcount)
1796 hr = opentype_get_typographic_features(fontface, *scripts, language, max_tagcount, actual_tagcount, tags);
1797 scripts++;
1800 return hr;
1803 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1804 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature,
1805 UINT32 glyph_count, const UINT16 *glyphs, UINT8 *feature_applies)
1807 FIXME("(%p %u %s %s %u %p %p): stub\n", face, sa.script, debugstr_w(locale), debugstr_tag(feature), glyph_count,
1808 glyphs, feature_applies);
1809 return E_NOTIMPL;
1812 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1813 dwritetextanalyzer_QueryInterface,
1814 dwritetextanalyzer_AddRef,
1815 dwritetextanalyzer_Release,
1816 dwritetextanalyzer_AnalyzeScript,
1817 dwritetextanalyzer_AnalyzeBidi,
1818 dwritetextanalyzer_AnalyzeNumberSubstitution,
1819 dwritetextanalyzer_AnalyzeLineBreakpoints,
1820 dwritetextanalyzer_GetGlyphs,
1821 dwritetextanalyzer_GetGlyphPlacements,
1822 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1823 dwritetextanalyzer1_ApplyCharacterSpacing,
1824 dwritetextanalyzer1_GetBaseline,
1825 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1826 dwritetextanalyzer1_GetGlyphOrientationTransform,
1827 dwritetextanalyzer1_GetScriptProperties,
1828 dwritetextanalyzer1_GetTextComplexity,
1829 dwritetextanalyzer1_GetJustificationOpportunities,
1830 dwritetextanalyzer1_JustifyGlyphAdvances,
1831 dwritetextanalyzer1_GetJustifiedGlyphs,
1832 dwritetextanalyzer2_GetGlyphOrientationTransform,
1833 dwritetextanalyzer2_GetTypographicFeatures,
1834 dwritetextanalyzer2_CheckTypographicFeature
1837 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1839 IDWriteTextAnalyzer *get_text_analyzer(void)
1841 return (IDWriteTextAnalyzer *)&textanalyzer;
1844 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1846 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1848 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1850 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1851 IsEqualIID(riid, &IID_IUnknown))
1853 *obj = iface;
1854 IDWriteNumberSubstitution_AddRef(iface);
1855 return S_OK;
1858 WARN("%s not implemented.\n", debugstr_guid(riid));
1860 *obj = NULL;
1862 return E_NOINTERFACE;
1865 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1867 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1868 ULONG ref = InterlockedIncrement(&This->ref);
1869 TRACE("(%p)->(%d)\n", This, ref);
1870 return ref;
1873 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1875 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1876 ULONG ref = InterlockedDecrement(&This->ref);
1878 TRACE("(%p)->(%d)\n", This, ref);
1880 if (!ref) {
1881 heap_free(This->locale);
1882 heap_free(This);
1885 return ref;
1888 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1889 dwritenumbersubstitution_QueryInterface,
1890 dwritenumbersubstitution_AddRef,
1891 dwritenumbersubstitution_Release
1894 struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
1896 if (!iface || iface->lpVtbl != &numbersubstitutionvtbl)
1897 return NULL;
1898 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
1901 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1902 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1904 struct dwrite_numbersubstitution *substitution;
1906 *ret = NULL;
1908 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1909 return E_INVALIDARG;
1911 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1912 return E_INVALIDARG;
1914 substitution = heap_alloc(sizeof(*substitution));
1915 if (!substitution)
1916 return E_OUTOFMEMORY;
1918 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1919 substitution->ref = 1;
1920 substitution->ignore_user_override = ignore_user_override;
1921 substitution->method = method;
1922 substitution->locale = heap_strdupW(locale);
1923 if (locale && !substitution->locale) {
1924 heap_free(substitution);
1925 return E_OUTOFMEMORY;
1928 *ret = &substitution->IDWriteNumberSubstitution_iface;
1929 return S_OK;
1932 /* IDWriteFontFallback */
1933 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback *iface, REFIID riid, void **obj)
1935 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1937 TRACE("(%p)->(%s %p)\n", fallback, debugstr_guid(riid), obj);
1939 if (IsEqualIID(riid, &IID_IDWriteFontFallback) || IsEqualIID(riid, &IID_IUnknown)) {
1940 *obj = iface;
1941 IDWriteFontFallback_AddRef(iface);
1942 return S_OK;
1945 WARN("%s not implemented.\n", debugstr_guid(riid));
1947 *obj = NULL;
1948 return E_NOINTERFACE;
1951 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback *iface)
1953 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1954 TRACE("(%p)\n", fallback);
1955 return IDWriteFactory5_AddRef(fallback->factory);
1958 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback *iface)
1960 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1961 TRACE("(%p)\n", fallback);
1962 return IDWriteFactory5_Release(fallback->factory);
1965 static int compare_mapping_range(const void *a, const void *b)
1967 UINT32 ch = *(UINT32 *)a;
1968 DWRITE_UNICODE_RANGE *range = (DWRITE_UNICODE_RANGE *)b;
1970 if (ch > range->last)
1971 return 1;
1972 else if (ch < range->first)
1973 return -1;
1974 else
1975 return 0;
1978 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
1980 UINT32 i;
1982 for (i = 0; i < fallback->mappings_count; i++) {
1983 struct fallback_mapping *mapping = &fallback->mappings[i];
1985 if (bsearch(&ch, mapping->ranges, mapping->ranges_count, sizeof(*mapping->ranges),
1986 compare_mapping_range) != NULL)
1987 return mapping;
1990 return NULL;
1993 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
1994 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
1996 IDWriteFontFamily *family;
1997 BOOL exists = FALSE;
1998 HRESULT hr;
1999 UINT32 i;
2001 *font = NULL;
2003 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
2004 if (FAILED(hr))
2005 return hr;
2007 if (!exists)
2008 return E_FAIL;
2010 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
2011 if (FAILED(hr))
2012 return hr;
2014 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
2015 IDWriteFontFamily_Release(family);
2016 return hr;
2019 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
2021 HRESULT hr = S_OK;
2022 UINT32 i;
2024 for (i = 0; i < length; i++) {
2025 UINT16 script = get_char_script(text[i]);
2026 BOOL exists;
2028 if (script == Script_Unknown || script == Script_Common) {
2029 ++*mapped_length;
2030 continue;
2033 /* stop on first unsupported character */
2034 exists = FALSE;
2035 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
2036 if (hr == S_OK && exists)
2037 ++*mapped_length;
2038 else
2039 break;
2042 return hr;
2045 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
2046 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2047 IDWriteFont **mapped_font)
2049 const struct fallback_mapping *mapping;
2050 HRESULT hr;
2051 UINT32 i;
2053 *mapped_font = NULL;
2055 mapping = find_fallback_mapping(fallback, text[0]);
2056 if (!mapping) {
2057 WARN("No mapping range for %#x.\n", text[0]);
2058 return E_FAIL;
2061 /* Now let's see what fallback can handle. Pick first font that could be created. */
2062 for (i = 0; i < mapping->families_count; i++) {
2063 hr = create_matching_font((IDWriteFontCollection *)fallback->systemcollection, mapping->families[i],
2064 weight, style, stretch, mapped_font);
2065 if (hr == S_OK) {
2066 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping->families[i]));
2067 break;
2071 if (!*mapped_font) {
2072 WARN("Failed to create fallback font.\n");
2073 return E_FAIL;
2076 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
2077 if (FAILED(hr))
2078 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping->families[i]), hr);
2080 if (!*mapped_length) {
2081 IDWriteFont_Release(*mapped_font);
2082 *mapped_font = NULL;
2085 return *mapped_length ? S_OK : E_FAIL;
2088 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
2089 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2090 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2091 IDWriteFont **ret_font, FLOAT *scale)
2093 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2094 WCHAR *buff = NULL;
2095 const WCHAR *text;
2096 HRESULT hr;
2098 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
2099 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2101 *mapped_length = 0;
2102 *scale = 1.0f;
2103 *ret_font = NULL;
2105 if (!source)
2106 return E_INVALIDARG;
2108 if (length == 0)
2109 return S_OK;
2111 if (!basecollection)
2112 basecollection = (IDWriteFontCollection*)fallback->systemcollection;
2114 hr = get_text_source_ptr(source, position, length, &text, &buff);
2115 if (FAILED(hr))
2116 goto done;
2118 if (basefamily && *basefamily) {
2119 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
2120 if (FAILED(hr))
2121 goto done;
2123 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
2124 if (FAILED(hr))
2125 goto done;
2128 if (!*mapped_length) {
2129 IDWriteFont *mapped_font;
2131 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
2132 if (FAILED(hr)) {
2133 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2134 if (*ret_font) {
2135 *mapped_length = length;
2136 hr = S_OK;
2139 else {
2140 if (*ret_font)
2141 IDWriteFont_Release(*ret_font);
2142 *ret_font = mapped_font;
2146 done:
2147 heap_free(buff);
2148 return hr;
2151 static const IDWriteFontFallbackVtbl fontfallbackvtbl = {
2152 fontfallback_QueryInterface,
2153 fontfallback_AddRef,
2154 fontfallback_Release,
2155 fontfallback_MapCharacters
2158 HRESULT create_system_fontfallback(IDWriteFactory5 *factory, IDWriteFontFallback **ret)
2160 struct dwrite_fontfallback *fallback;
2162 *ret = NULL;
2164 fallback = heap_alloc(sizeof(*fallback));
2165 if (!fallback)
2166 return E_OUTOFMEMORY;
2168 fallback->IDWriteFontFallback_iface.lpVtbl = &fontfallbackvtbl;
2169 fallback->factory = factory;
2170 fallback->mappings = (struct fallback_mapping *)fontfallback_neutral_data;
2171 fallback->mappings_count = ARRAY_SIZE(fontfallback_neutral_data);
2172 IDWriteFactory5_GetSystemFontCollection(fallback->factory, FALSE, &fallback->systemcollection, FALSE);
2174 *ret = &fallback->IDWriteFontFallback_iface;
2175 return S_OK;
2178 void release_system_fontfallback(IDWriteFontFallback *iface)
2180 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2181 IDWriteFontCollection1_Release(fallback->systemcollection);
2182 heap_free(fallback);
2185 static ULONG WINAPI customfontfallback_AddRef(IDWriteFontFallback *iface)
2187 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2188 ULONG ref = InterlockedIncrement(&fallback->ref);
2189 TRACE("(%p)->(%d)\n", fallback, ref);
2190 return ref;
2193 static ULONG WINAPI customfontfallback_Release(IDWriteFontFallback *iface)
2195 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2196 ULONG ref = InterlockedDecrement(&fallback->ref);
2198 TRACE("(%p)->(%d)\n", fallback, ref);
2200 if (!ref) {
2201 IDWriteFactory5_Release(fallback->factory);
2202 heap_free(fallback);
2205 return ref;
2208 static HRESULT WINAPI customfontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
2209 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2210 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2211 IDWriteFont **ret_font, FLOAT *scale)
2213 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2215 FIXME("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
2216 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2218 return E_NOTIMPL;
2221 static const IDWriteFontFallbackVtbl customfontfallbackvtbl =
2223 fontfallback_QueryInterface,
2224 customfontfallback_AddRef,
2225 customfontfallback_Release,
2226 customfontfallback_MapCharacters,
2229 static HRESULT WINAPI fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder *iface, REFIID riid, void **obj)
2231 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2233 TRACE("(%p)->(%s %p)\n", fallbackbuilder, debugstr_guid(riid), obj);
2235 if (IsEqualIID(riid, &IID_IDWriteFontFallbackBuilder) || IsEqualIID(riid, &IID_IUnknown)) {
2236 *obj = iface;
2237 IDWriteFontFallbackBuilder_AddRef(iface);
2238 return S_OK;
2241 WARN("%s not implemented.\n", debugstr_guid(riid));
2243 *obj = NULL;
2244 return E_NOINTERFACE;
2247 static ULONG WINAPI fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder *iface)
2249 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2250 ULONG ref = InterlockedIncrement(&fallbackbuilder->ref);
2251 TRACE("(%p)->(%d)\n", fallbackbuilder, ref);
2252 return ref;
2255 static ULONG WINAPI fontfallbackbuilder_Release(IDWriteFontFallbackBuilder *iface)
2257 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2258 ULONG ref = InterlockedDecrement(&fallbackbuilder->ref);
2260 TRACE("(%p)->(%d)\n", fallbackbuilder, ref);
2262 if (!ref) {
2263 UINT32 i;
2265 for (i = 0; i < fallbackbuilder->mappings_count; i++) {
2266 struct fallback_mapping *mapping = &fallbackbuilder->mappings[i];
2267 UINT32 j;
2269 for (j = 0; j < mapping->families_count; j++)
2270 heap_free(mapping->families[j]);
2271 heap_free(mapping->families);
2273 if (mapping->collection)
2274 IDWriteFontCollection_Release(mapping->collection);
2275 heap_free(mapping->ranges);
2276 heap_free(mapping->locale);
2279 IDWriteFactory5_Release(fallbackbuilder->factory);
2280 heap_free(fallbackbuilder->mappings);
2281 heap_free(fallbackbuilder);
2284 return ref;
2287 static HRESULT WINAPI fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder *iface,
2288 const DWRITE_UNICODE_RANGE *ranges, UINT32 ranges_count, WCHAR const **target_families, UINT32 families_count,
2289 IDWriteFontCollection *collection, WCHAR const *locale, WCHAR const *base_family, FLOAT scale)
2291 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2292 struct fallback_mapping *mapping;
2293 UINT32 i;
2295 TRACE("(%p)->(%p, %u, %p, %u, %p, %s, %s, %f)\n", fallbackbuilder, ranges, ranges_count, target_families,
2296 families_count, collection, debugstr_w(locale), debugstr_w(base_family), scale);
2298 if (!ranges || ranges_count == 0 || !target_families || families_count == 0 || scale < 0.0f)
2299 return E_INVALIDARG;
2301 if (base_family)
2302 FIXME("base family ignored.\n");
2304 if (fallbackbuilder->mappings_count == fallbackbuilder->mappings_capacity) {
2305 struct fallback_mapping *mappings;
2307 if (fallbackbuilder->mappings_capacity == 0) {
2308 if ((mappings = heap_calloc(16, sizeof(*mappings))))
2309 fallbackbuilder->mappings_capacity = 16;
2311 else {
2312 if ((mappings = heap_realloc(fallbackbuilder->mappings, sizeof(*fallbackbuilder->mappings) *
2313 fallbackbuilder->mappings_capacity * 2)))
2314 fallbackbuilder->mappings_capacity *= 2;
2316 if (!mappings)
2317 return E_OUTOFMEMORY;
2319 fallbackbuilder->mappings = mappings;
2322 mapping = &fallbackbuilder->mappings[fallbackbuilder->mappings_count++];
2324 mapping->ranges = heap_calloc(ranges_count, sizeof(*mapping->ranges));
2325 memcpy(mapping->ranges, ranges, sizeof(*mapping->ranges) * ranges_count);
2326 mapping->ranges_count = ranges_count;
2327 mapping->families = heap_alloc_zero(sizeof(*mapping->families) * families_count);
2328 mapping->families_count = families_count;
2329 for (i = 0; i < families_count; i++)
2330 mapping->families[i] = heap_strdupW(target_families[i]);
2331 mapping->collection = collection;
2332 if (mapping->collection)
2333 IDWriteFontCollection_AddRef(mapping->collection);
2334 mapping->locale = heap_strdupW(locale);
2335 mapping->scale = scale;
2337 return S_OK;
2340 static HRESULT WINAPI fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder *iface, IDWriteFontFallback *fallback)
2342 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2344 FIXME("(%p)->(%p): stub\n", fallbackbuilder, fallback);
2346 return E_NOTIMPL;
2349 static HRESULT WINAPI fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder *iface,
2350 IDWriteFontFallback **ret)
2352 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2353 struct dwrite_fontfallback *fallback;
2355 FIXME("(%p)->(%p): stub\n", fallbackbuilder, ret);
2357 *ret = NULL;
2359 fallback = heap_alloc(sizeof(*fallback));
2360 if (!fallback)
2361 return E_OUTOFMEMORY;
2363 fallback->IDWriteFontFallback_iface.lpVtbl = &customfontfallbackvtbl;
2364 fallback->ref = 1;
2365 fallback->factory = fallbackbuilder->factory;
2366 IDWriteFactory5_AddRef(fallback->factory);
2368 *ret = &fallback->IDWriteFontFallback_iface;
2369 return S_OK;
2372 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl =
2374 fontfallbackbuilder_QueryInterface,
2375 fontfallbackbuilder_AddRef,
2376 fontfallbackbuilder_Release,
2377 fontfallbackbuilder_AddMapping,
2378 fontfallbackbuilder_AddMappings,
2379 fontfallbackbuilder_CreateFontFallback,
2382 HRESULT create_fontfallback_builder(IDWriteFactory5 *factory, IDWriteFontFallbackBuilder **ret)
2384 struct dwrite_fontfallback_builder *builder;
2386 *ret = NULL;
2388 builder = heap_alloc_zero(sizeof(*builder));
2389 if (!builder)
2390 return E_OUTOFMEMORY;
2392 builder->IDWriteFontFallbackBuilder_iface.lpVtbl = &fontfallbackbuildervtbl;
2393 builder->ref = 1;
2394 builder->factory = factory;
2395 IDWriteFactory5_AddRef(builder->factory);
2397 *ret = &builder->IDWriteFontFallbackBuilder_iface;
2398 return S_OK;