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
26 #include "dwrite_private.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
31 extern const unsigned short wine_linebreak_table
[] DECLSPEC_HIDDEN
;
32 extern const unsigned short wine_scripts_table
[] DECLSPEC_HIDDEN
;
33 extern const unsigned short bidi_direction_table
[] DECLSPEC_HIDDEN
;
35 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
36 #define NATIVE_DIGITS_LEN 11
38 struct dwritescript_properties
40 DWRITE_SCRIPT_PROPERTIES props
;
41 UINT32 scripttags
[3]; /* Maximum 2 script tags, 0-terminated. */
45 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
47 /* NOTE: keep this array synced with script ids from scripts.h */
48 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
49 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
51 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
52 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('a','r','a','b') }, TRUE
},
53 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','n') } },
54 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','v','s','t') } },
55 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('b','a','l','i') } },
56 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','m','u') } },
57 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','t','k') } },
58 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('b','n','g','2'), _OT('b','e','n','g') }, TRUE
},
59 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('b','o','p','o') } },
60 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','h') } },
61 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','i') }, TRUE
},
62 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','u','g','i') } },
63 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('b','u','h','d') } },
64 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','n','s') }, TRUE
},
65 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('c','a','r','i') } },
66 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('c','h','a','m') } },
67 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','h','e','r') }, TRUE
},
68 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','o','p','t') } },
69 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('x','s','u','x') } },
70 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('c','p','r','t') } },
71 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','y','r','l') } },
72 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('d','s','r','t') }, TRUE
},
73 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('d','e','v','2'), _OT('d','e','v','a') }, TRUE
},
74 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('e','g','y','p') } },
75 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','t','h','i') }, TRUE
},
76 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','e','o','r') } },
77 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','l','a','g') } },
78 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','t','h') } },
79 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','e','k') } },
80 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','j','r','2'), _OT('g','u','j','r') }, TRUE
},
81 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','u','r','2'), _OT('g','u','r','u') }, TRUE
},
82 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('h','a','n','i') } },
83 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, { _OT('h','a','n','g') }, TRUE
},
84 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('h','a','n','o') } },
85 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('h','e','b','r') }, TRUE
},
86 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
87 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','i') } },
88 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','h','l','i') } },
89 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','r','t','i') } },
90 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('j','a','v','a') } },
91 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('k','t','h','i') } },
92 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','n','d','2'), _OT('k','n','d','a') }, TRUE
},
93 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
94 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('k','a','l','i') } },
95 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('k','h','a','r') } },
96 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('k','h','m','r') }, TRUE
},
97 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('l','a','o',' ') }, TRUE
},
98 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','a','t','n') } },
99 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','e','p','c') } },
100 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','m','b') } },
101 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','b') } },
102 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','s','u') } },
103 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','c','i') } },
104 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','d','i') } },
105 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','l','m','2'), _OT('m','l','y','m') }, TRUE
},
106 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','d') } },
107 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','t','e','i') } },
108 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','o','n','g') }, TRUE
},
109 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','y','m','r') }, TRUE
},
110 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','u') }, TRUE
},
111 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('n','k','o',' ') }, TRUE
},
112 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, { _OT('o','g','a','m') }, TRUE
},
113 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','l','c','k') } },
114 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('i','t','a','l') } },
115 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('x','p','e','o') }, TRUE
},
116 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','r','b') } },
117 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','k','h') } },
118 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','y','2'), _OT('o','r','y','a') }, TRUE
},
119 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','m','a') }, TRUE
},
120 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','a','g') }, TRUE
},
121 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('p','h','n','x') } },
122 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('r','j','n','g') } },
123 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('r','u','n','r') }, TRUE
},
124 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','m','r') } },
125 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','u','r') } },
126 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','a','w') } },
127 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','h') }, TRUE
},
128 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','u','n','d') } },
129 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('s','y','l','o') } },
130 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('s','y','r','c') }, TRUE
},
131 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','g','l','g') } },
132 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','g','b') } },
133 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','e') }, TRUE
},
134 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('l','a','n','a') } },
135 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','a','v','t') } },
136 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','m','l','2'), _OT('t','a','m','l') }, TRUE
},
137 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','e','l','2'), _OT('t','e','l','u') }, TRUE
},
138 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','h','a','a') }, TRUE
},
139 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','h','a','i') }, TRUE
},
140 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','i','b','t') }, TRUE
},
141 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','f','n','g') }, TRUE
},
142 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('u','g','a','r') } },
143 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('v','a','i',' ') }, TRUE
},
144 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('y','i',' ',' ') }, TRUE
},
145 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','k','m') } },
146 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','r','c') } },
147 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('m','e','r','o') } },
148 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('p','l','r','d') } },
149 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','r','d') } },
150 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','r','a') } },
151 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','k','r') } },
152 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','s','s') } },
153 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','g','h','b') } },
154 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('d','u','p','l') } },
155 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','l','b','a') } },
156 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','a','n') } },
157 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','h','o','j') } },
158 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','d') } },
159 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','a') } },
160 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','h','j') } },
161 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','i') } },
162 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','n','d') } },
163 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('m','o','d','i') } },
164 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','r','o','o') } },
165 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('n','b','a','t') } },
166 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','a','r','b') } },
167 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','e','r','m') } },
168 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','m','n','g') } },
169 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','l','m') } },
170 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','u','c') } },
171 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','l','p') } },
172 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('s','i','d','d') } },
173 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('t','i','r','h') } },
174 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('w','a','r','a') } },
175 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','d','l','m') } },
176 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','h','o','m') } },
177 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','l','u','w') } },
178 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','h','k','s') } },
179 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','a','t','r') } },
180 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','r','c') } },
181 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','u','l','t') } },
182 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','e','w','a') } },
183 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','u','n','g') } },
184 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','g','e') } },
185 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','g','n','w') } },
186 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','n','g') } },
187 { /* Gonm */ { 0x6d6e6f47, 313, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','n','m') } },
188 { /* Nshu */ { 0x7568734e, 499, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('n','s','h','u') } },
189 { /* Soyo */ { 0x6f796f53, 329, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','y','o') } },
190 { /* Zanb */ { 0x626e615a, 339, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('z','a','n','b') } },
191 { /* Dogr */ { 0x72676f44, 328, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('d','o','g','r') } },
192 { /* Gong */ { 0x676e6f47, 312, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','o','n','g') } },
193 { /* Rohg */ { 0x67686f52, 167, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('r','o','h','g') } },
194 { /* Maka */ { 0x616b614d, 366, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','k','a') } },
195 { /* Medf */ { 0x6664654d, 265, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','d','f') } },
196 { /* Sogo */ { 0x6f676f53, 142, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','g','o') } },
197 { /* Sogd */ { 0x64676f53, 141, 8, 0x0020, 1, 1, 0, 0, 0, 1, 1 }, { _OT('s','o','g','d') } },
198 { /* Elym */ { 0x6d796c45, 128, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('e','l','y','m') } },
199 { /* Hmnp */ { 0x706e6d48, 451, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('h','m','n','p') } },
200 { /* Nand */ { 0x646e614e, 311, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('n','a','n','d') } },
201 { /* Wcho */ { 0x6f686357, 283, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('w','c','h','o') } },
202 { /* Chrs */ { 0x73726843, 109, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('c','h','r','s') } },
203 { /* Diak */ { 0x6b616944, 342, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('d','i','a','k') } },
204 { /* Kits */ { 0x7374694b, 288, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('k','i','t','s') } },
205 { /* Yezi */ { 0x697a6559, 192, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('y','e','z','i') } },
209 const char *debugstr_sa_script(UINT16 script
)
211 return script
< Script_LastId
? debugstr_tag(dwritescripts_properties
[script
].props
.isoScriptCode
) : "undefined";
214 static const struct fallback_description
217 const WCHAR
*families
;
220 system_fallback_config
[] =
222 /* Latin, Combining Diacritical Marks */
223 { "0000-007F, 0080-00FF, 0100-017F, 0180-024F, "
224 "0250-02AF, 02B0-02FF, 0300-036F", L
"Tahoma" },
226 { "0530-058F, FB10-FB1C", L
"Noto Sans Armenian" },
228 { "0590-05FF, FB1D-FB4F", L
"Noto Sans Hebrew" },
230 { "0600-06FF, 0750-077F, "
231 "08A0-08FF, FB50-FDCF, "
232 "FDF0-FDFF, FE70-FEFE", L
"Noto Sans Arabic" },
234 { "0700-074F", L
"Noto Sans Syriac" },
235 { "0780-07BF", L
"Noto Sans Thaana" },
236 { "07C0-07FF", L
"Noto Sans NKo" },
237 { "0800-083F", L
"Noto Sans Samaritan" },
238 { "0840-085F", L
"Noto Sans Mandaic" },
240 { "0900-097F", L
"Noto Sans Devanagari" },
241 { "0980-09FF", L
"Noto Sans Bengali" },
242 { "0A00-0A7F", L
"Noto Sans Gurmukhi" },
243 { "0A80-0AFF", L
"Noto Sans Gujarati" },
244 { "0B00-0B7F", L
"Noto Sans Oriya" },
245 { "0B80-0BFF", L
"Noto Sans Tamil" },
246 { "0C00-0C7F", L
"Noto Sans Telugu" },
247 { "0C80-0CFF", L
"Noto Sans Kannada" },
248 { "0D00-0D7F", L
"Noto Sans Malayalam" },
249 { "0D80-0DFF", L
"Noto Sans Sinhala" },
251 { "0E00-0E7F", L
"Noto Sans Thai" },
252 { "0E80-0EFF", L
"Noto Sans Lao" },
254 { "0F00-0FFF", L
"Noto Serif Tibetan" },
256 { "1000-109F, A9E0-A9FF, AA60-AA7F", L
"Noto Sans Myanmar" },
258 { "10A0-10FF, 2D00-2D2F", L
"Noto Sans Georgian" },
260 /* Hangul Jamo - 1100-11FF
261 Hangul Compatibility Jamo - 3130-318F
262 Enc. CJK (Paren Hangul) - 3200-321F
263 Enc. CJK (Circled Hangul) - 3260-327F
264 Hangul Jamo Extended-A - A960-A97F
265 Hangul Syllables - AC00-D7AF
266 Hangul Jamo Extended-B - D7B0-D7FF */
268 { "1100-11FF, 3130-318F, "
269 "3200-321F, 3260-327F, "
270 "A960-A97F, AC00-D7FF, "
271 "D7B0-D7FF", L
"Noto Sans CJK KR" },
273 { "1680-169F", L
"Noto Sans Ogham" },
275 { "16A0-16FF", L
"Noto Sans Runic" },
277 { "1700-171F", L
"Noto Sans Tagalog" },
278 { "1720-173F", L
"Noto Sans Hanunoo" },
279 { "1740-175F", L
"Noto Sans Buhid" },
280 { "1760-177F", L
"Noto Sans Tagbanwa" },
282 { "1800-18AF, 202F, 11660-1167F", L
"Noto Sans Mongolian" },
284 { "1900-194F", L
"Noto Sans Limbu" },
285 { "1950-197F", L
"Noto Sans Tai Le" },
286 { "1980-19DF", L
"Noto Sans New Tai Lue" },
287 { "1A00-1A1F", L
"Noto Sans Buginese" },
288 { "1A20-1AAF", L
"Noto Sans Tai Tham" },
289 { "1B00-1B7F", L
"Noto Sans Balinese" },
290 { "1B80-1BBF, 1CC0-1CCF", L
"Noto Sans Sundanes" },
291 { "1BC0-1BFF", L
"Noto Sans Batak" },
292 { "1C00-1C4F", L
"Noto Sans Lepcha" },
293 { "1C50-1C7F", L
"Noto Sans Ol Chiki" },
295 { "2C80-2CFF", L
"Noto Sans Coptic" },
296 { "2D30-2D7F", L
"Noto Sans Tifinagh" },
298 /* CJK Radicals Supplement - 2E80-2EFF */
300 { "2E80-2EFF", L
"Noto Sans CJK SC", L
"zh-Hans" },
301 { "2E80-2EFF", L
"Noto Sans CJK TC", L
"zh-Hant" },
302 { "2E80-2EFF", L
"Noto Sans CJK KR", L
"ko" },
304 /* CJK Symbols and Punctuation - 3000-303F
307 Katakana Phonetic Ext. - 31F0-31FF */
309 { "3000-30FF, 31F0-31FF", L
"Noto Sans CJK SC", L
"zh-Hans" },
310 { "3000-30FF, 31F0-31FF", L
"Noto Sans CJK TC", L
"zh-Hant" },
311 { "3000-30FF, 31F0-31FF", L
"Noto Sans CJK KR", L
"ko" },
312 { "3000-30FF, 31F0-31FF", L
"Noto Sans CJK JP" },
314 /* CJK Unified Ext A - 3400-4DBF
315 CJK Unified - 4E00-9FFF */
317 { "3400-4DBF, 4E00-9FFF", L
"Noto Sans CJK SC", L
"zh-Hans" },
318 { "3400-4DBF, 4E00-9FFF", L
"Noto Sans CJK TC", L
"zh-Hant" },
319 { "3400-4DBF, 4E00-9FFF", L
"Noto Sans CJK KR", L
"ko" },
320 { "3400-4DBF, 4E00-9FFF", L
"Noto Sans CJK JP" },
322 { "A000-A4CF", L
"Noto Sans Yi" },
323 { "A4D0-A4FF", L
"Noto Sans Lisu" },
324 { "A500-A63F", L
"Noto Sans Vai" },
325 { "A6A0-A6FF", L
"Noto Sans Bamum" },
326 { "A800-A82F", L
"Noto Sans Syloti Nagri" },
327 { "A840-A87F", L
"Noto Sans PhagsPa" },
328 { "A880-A8DF", L
"Noto Sans Saurashtra" },
329 { "A900-A92F", L
"Noto Sans Kayah Li" },
330 { "A930-A95F", L
"Noto Sans Rejang" },
331 { "A980-A9DF", L
"Noto Sans Javanese" },
332 { "AA00-AA5F", L
"Noto Sans Cham" },
334 /* CJK Compatibility Ideographs - F900-FAFF */
336 { "F900-FAFF", L
"Noto Sans CJK SC", L
"zh-Hans" },
337 { "F900-FAFF", L
"Noto Sans CJK TC", L
"zh-Hant" },
338 { "F900-FAFF", L
"Noto Sans CJK KR", L
"ko" },
339 { "F900-FAFF", L
"Noto Sans CJK JP" },
341 /* Vertical Forms - FE10-FE1F */
343 { "FE10-FE1F", L
"Noto Sans CJK SC", L
"zh-Hans" },
344 { "FE10-FE1F", L
"Noto Sans CJK KR", L
"ko" },
345 { "FE10-FE1F", L
"Noto Sans CJK TC" },
347 /* CJK Compatibility Forms - FE30-FE4F
348 Small Form Variants - FE50-FE6F */
350 { "FE30-FE6F", L
"Noto Sans CJK SC", L
"zh-Hans" },
351 { "FE30-FE6F", L
"Noto Sans CJK KR", L
"ko" },
352 { "FE30-FE6F", L
"Noto Sans CJK JP", L
"ja" },
353 { "FE30-FE6F", L
"Noto Sans CJK TC" },
355 /* Halfwidth and Fullwidth Forms */
356 { "FF00-FFEF", L
"Noto Sans CJK SC", L
"zh-Hans" },
357 { "FF00-FFEF", L
"Noto Sans CJK TC", L
"zh-Hant" },
358 { "FF00-FFEF", L
"Noto Sans CJK KR", L
"ko" },
359 { "FF00-FFEF", L
"Noto Sans CJK JP" },
362 struct text_source_context
364 IDWriteTextAnalysisSource
*source
;
379 static inline unsigned int text_source_get_char_length(const struct text_source_context
*context
)
381 return context
->ch
> 0xffff ? 2 : 1;
384 static void text_source_read_more(struct text_source_context
*context
)
386 if ((context
->chunk_length
- context
->cursor
) > 1) return;
388 context
->position
+= context
->cursor
;
390 if (FAILED(context
->status
= IDWriteTextAnalysisSource_GetTextAtPosition(context
->source
, context
->position
,
391 &context
->text
, &context
->chunk_length
)))
397 static void text_source_get_u32_char(struct text_source_context
*context
)
402 /* Make sure to have full pair of surrogates */
403 text_source_read_more(context
);
405 available
= context
->chunk_length
- context
->cursor
;
406 text
= context
->text
+ context
->cursor
;
408 if (available
> 1 && IS_HIGH_SURROGATE(*text
) && IS_LOW_SURROGATE(*(text
+ 1)))
410 context
->cursor
+= 2;
411 context
->consumed
+= 2;
412 context
->ch
= 0x10000 + ((*text
- 0xd800) << 10) + *(text
+ 1) - 0xdc00;
421 static HRESULT
text_source_context_init(struct text_source_context
*context
, IDWriteTextAnalysisSource
*source
,
422 UINT32 position
, UINT32 length
)
424 memset(context
, 0, sizeof(*context
));
425 context
->source
= source
;
426 context
->position
= position
;
427 context
->length
= length
;
429 return IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &context
->text
, &context
->chunk_length
);
432 static BOOL
text_source_get_next_u32_char(struct text_source_context
*context
)
434 if (context
->consumed
== context
->length
)
439 else if (context
->cursor
< context
->chunk_length
)
441 text_source_get_u32_char(context
);
445 text_source_read_more(context
);
446 /* Normal end-of-text condition. */
447 if (!context
->text
|| !context
->chunk_length
)
450 text_source_get_u32_char(context
);
456 struct fallback_mapping
458 DWRITE_UNICODE_RANGE
*ranges
;
461 UINT32 families_count
;
462 IDWriteFontCollection
*collection
;
466 struct fallback_locale
469 WCHAR name
[LOCALE_NAME_MAX_LENGTH
];
478 static void fallback_locale_list_destroy(struct list
*locales
)
480 struct fallback_locale
*cur
, *cur2
;
482 LIST_FOR_EACH_ENTRY_SAFE(cur
, cur2
, locales
, struct fallback_locale
, entry
)
484 list_remove(&cur
->entry
);
485 free(cur
->ranges
.data
);
490 static HRESULT
fallback_locale_add_mapping(struct fallback_locale
*locale
, size_t index
)
492 size_t count
= locale
->ranges
.count
;
494 /* Append to last range, or start a new one. */
495 if (count
&& locale
->ranges
.data
[count
- 1] == (index
- 1))
497 locale
->ranges
.data
[count
- 1] = index
;
501 if (!dwrite_array_reserve((void **)&locale
->ranges
.data
, &locale
->ranges
.size
, count
+ 2,
502 sizeof(*locale
->ranges
.data
)))
504 return E_OUTOFMEMORY
;
507 locale
->ranges
.data
[count
] = locale
->ranges
.data
[count
+ 1] = index
;
508 locale
->ranges
.count
+= 2;
513 /* TODO: potentially needs improvement to consider partially matching locale names. */
514 static struct fallback_locale
* font_fallback_get_locale(const struct list
*locales
,
515 const WCHAR
*locale_name
)
517 struct fallback_locale
*locale
, *neutral
= NULL
;
519 LIST_FOR_EACH_ENTRY(locale
, locales
, struct fallback_locale
, entry
)
521 if (!wcsicmp(locale
->name
, locale_name
)) return locale
;
522 if (!*locale
->name
) neutral
= locale
;
530 struct fallback_mapping
*mappings
;
535 struct dwrite_fontfallback
537 IDWriteFontFallback1 IDWriteFontFallback1_iface
;
539 IDWriteFactory7
*factory
;
540 IDWriteFontCollection
*systemcollection
;
541 struct fallback_data data
;
542 size_t mappings_size
;
545 struct dwrite_fontfallback_builder
547 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface
;
549 IDWriteFactory7
*factory
;
550 struct fallback_data data
;
551 size_t mappings_size
;
554 static struct fallback_data system_fallback
=
556 .locales
= LIST_INIT(system_fallback
.locales
),
559 static void release_fallback_mapping(struct fallback_mapping
*mapping
)
563 free(mapping
->ranges
);
564 for (i
= 0; i
< mapping
->families_count
; ++i
)
565 free(mapping
->families
[i
]);
566 free(mapping
->families
);
567 if (mapping
->collection
)
568 IDWriteFontCollection_Release(mapping
->collection
);
571 static void release_fallback_data(struct fallback_data
*data
)
575 for (i
= 0; i
< data
->count
; ++i
)
576 release_fallback_mapping(&data
->mappings
[i
]);
577 free(data
->mappings
);
578 fallback_locale_list_destroy(&data
->locales
);
581 static BOOL
fallback_mapping_contains_character(const struct fallback_mapping
*mapping
, UINT32 ch
)
585 for (i
= 0; i
< mapping
->ranges_count
; ++i
)
587 const DWRITE_UNICODE_RANGE
*range
= &mapping
->ranges
[i
];
588 if (range
->first
<= ch
&& range
->last
>= ch
) return TRUE
;
594 static const struct fallback_mapping
* find_fallback_mapping(const struct fallback_data
*fallback
,
595 const struct fallback_locale
*locale
, UINT32 ch
)
597 const struct fallback_mapping
*mapping
;
600 for (i
= 0; i
< locale
->ranges
.count
; i
+= 2)
602 size_t start
= locale
->ranges
.data
[i
], end
= locale
->ranges
.data
[i
+ 1];
603 for (j
= start
; j
<= end
; ++j
)
605 mapping
= &fallback
->mappings
[j
];
606 if (fallback_mapping_contains_character(mapping
, ch
)) return mapping
;
612 /* Mapping wasn't found for specific locale, try with neutral one. This will only recurse once. */
615 locale
= font_fallback_get_locale(&fallback
->locales
, L
"");
616 mapping
= find_fallback_mapping(fallback
, locale
, ch
);
622 struct dwrite_numbersubstitution
624 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
627 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
629 BOOL ignore_user_override
;
632 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
634 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
637 static struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
);
639 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback1(IDWriteFontFallback1
*iface
)
641 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback1_iface
);
644 static inline struct dwrite_fontfallback_builder
*impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder
*iface
)
646 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback_builder
, IDWriteFontFallbackBuilder_iface
);
649 static inline UINT16
get_char_script(UINT32 c
)
651 UINT16 script
= get_table_entry_32(wine_scripts_table
, c
);
652 return script
== Script_Inherited
? Script_Unknown
: script
;
655 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(UINT32 c
)
657 DWRITE_SCRIPT_ANALYSIS sa
;
659 sa
.script
= get_char_script(c
);
660 sa
.shapes
= DWRITE_SCRIPT_SHAPES_DEFAULT
;
661 if ((c
>= 0x0001 && c
<= 0x001f) /* C0 controls */
662 || (c
>= 0x007f && c
<= 0x009f) /* DELETE, C1 controls */
663 || (c
== 0x00ad) /* SOFT HYPHEN */
664 || (c
>= 0x200b && c
<= 0x200f) /* ZWSP, ZWNJ, ZWJ, LRM, RLM */
665 || (c
>= 0x2028 && c
<= 0x202e) /* Line/paragraph separators, LRE, RLE, PDF, LRO, RLO */
666 || (c
>= 0x2060 && c
<= 0x2064) /* WJ, invisible operators */
667 || (c
>= 0x2066 && c
<= 0x2069) /* LRI, RLI, FSI, PDI */
668 || (c
>= 0x206a && c
<= 0x206f) /* Deprecated control characters */
669 || (c
== 0xfeff) /* ZWBNSP */
670 || (c
== 0xfff9) /* Interlinear annotation */
673 || (c
>= 0x1bca0 && c
<= 0x1bca3) /* Shorthand format controls */
674 || (c
>= 0x1d173 && c
<= 0x1d17a) /* Musical symbols: beams and slurs */
675 || (c
== 0xe0001) /* Language tag, deprecated */
676 || (c
>= 0xe0020 && c
<= 0xe007f)) /* Tag components */
678 sa
.shapes
= DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
682 sa
.shapes
= DWRITE_SCRIPT_SHAPES_DEFAULT
;
688 static HRESULT
analyze_script(struct text_source_context
*context
, IDWriteTextAnalysisSink
*sink
)
690 DWRITE_SCRIPT_ANALYSIS sa
;
694 text_source_get_next_u32_char(context
);
696 sa
= get_char_sa(context
->ch
);
698 pos
= context
->position
;
699 length
= text_source_get_char_length(context
);
701 while (!text_source_get_next_u32_char(context
))
703 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(context
->ch
);
705 /* Unknown type is ignored when preceded or followed by another script */
708 sa
.script
= cur_sa
.script
;
711 if (cur_sa
.script
== Script_Unknown
)
712 cur_sa
.script
= sa
.script
;
713 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
714 sa
.script
= cur_sa
.script
;
717 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
718 cur_sa
.script
= sa
.script
;
721 /* this is a length of a sequence to be reported next */
722 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
723 length
+= text_source_get_char_length(context
);
726 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
727 if (FAILED(hr
)) return hr
;
729 length
= text_source_get_char_length(context
);
734 /* one char length case or normal completion call */
735 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
744 struct linebreaking_state
746 DWRITE_LINE_BREAKPOINT
*breakpoints
;
747 struct break_index
*breaks
;
751 enum BreakConditionLocation
{
752 BreakConditionBefore
,
756 enum linebreaking_classes
{
802 static BOOL
has_strong_condition(DWRITE_BREAK_CONDITION old_condition
, DWRITE_BREAK_CONDITION new_condition
)
804 if (old_condition
== DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
|| old_condition
== DWRITE_BREAK_CONDITION_MUST_BREAK
)
807 if (old_condition
== DWRITE_BREAK_CONDITION_CAN_BREAK
&& new_condition
!= DWRITE_BREAK_CONDITION_MUST_BREAK
)
813 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
814 set to "can break" and could only be changed once. */
815 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
816 struct linebreaking_state
*state
)
818 unsigned int index
= state
->breaks
[pos
].index
;
820 if (location
== BreakConditionBefore
)
822 if (has_strong_condition(state
->breakpoints
[index
].breakConditionBefore
, condition
))
824 state
->breakpoints
[index
].breakConditionBefore
= condition
;
829 index
= state
->breaks
[pos
].index
;
830 if (state
->breaks
[pos
].length
> 1) index
++;
832 state
->breakpoints
[index
].breakConditionAfter
= condition
;
837 if (state
->breaks
[pos
].length
> 1) index
++;
839 if (has_strong_condition(state
->breakpoints
[index
].breakConditionAfter
, condition
))
841 state
->breakpoints
[index
].breakConditionAfter
= condition
;
843 if (pos
+ 1 < state
->count
)
845 index
= state
->breaks
[pos
+ 1].index
;
846 state
->breakpoints
[index
].breakConditionBefore
= condition
;
851 BOOL
lb_is_newline_char(WCHAR ch
)
853 short c
= get_table_entry_32(wine_linebreak_table
, ch
);
854 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
857 static HRESULT
analyze_linebreaks(IDWriteTextAnalysisSource
*source
, UINT32 position
,
858 UINT32 length
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
860 struct text_source_context context
;
861 struct linebreaking_state state
;
862 struct break_index
*breaks
;
863 unsigned int count
, index
;
868 if (FAILED(hr
= text_source_context_init(&context
, source
, position
, length
))) return hr
;
870 if (!(breaks
= calloc(length
, sizeof(*breaks
))))
871 return E_OUTOFMEMORY
;
873 if (!(break_class
= calloc(length
, sizeof(*break_class
))))
876 return E_OUTOFMEMORY
;
880 while (!text_source_get_next_u32_char(&context
))
882 break_class
[count
] = get_table_entry_32(wine_linebreak_table
, context
.ch
);
883 breaks
[count
].length
= text_source_get_char_length(&context
);
884 breaks
[count
].index
= index
;
886 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
887 switch (break_class
[count
])
893 break_class
[count
] = b_AL
;
896 break_class
[count
] = b_NS
;
900 breakpoints
[index
].breakConditionBefore
= DWRITE_BREAK_CONDITION_NEUTRAL
;
901 breakpoints
[index
].breakConditionAfter
= DWRITE_BREAK_CONDITION_NEUTRAL
;
902 breakpoints
[index
].isWhitespace
= context
.ch
< 0xffff ? !!iswspace(context
.ch
) : 0;
903 breakpoints
[index
].isSoftHyphen
= context
.ch
== 0x00ad /* Unicode Soft Hyphen */;
904 breakpoints
[index
].padding
= 0;
907 if (breaks
[count
].length
> 1)
909 breakpoints
[index
] = breakpoints
[index
- 1];
910 /* Never break in surrogate pairs. */
911 breakpoints
[index
- 1].breakConditionAfter
= DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
;
912 breakpoints
[index
].breakConditionBefore
= DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
;
919 state
.breakpoints
= breakpoints
;
920 state
.breaks
= breaks
;
923 /* LB2 - never break at the start */
924 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
925 /* LB3 - always break at the end. */
926 set_break_condition(count
- 1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
928 /* LB4 - LB6 - mandatory breaks. */
929 for (i
= 0; i
< count
; i
++)
931 switch (break_class
[i
])
935 /* LB5 - don't break CR x LF */
936 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
938 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
939 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
945 /* LB4 - LB5 - always break after hard breaks */
946 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
947 /* LB6 - do not break before hard breaks */
948 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
953 /* LB7 - LB8 - explicit breaks and non-breaks */
954 for (i
= 0; i
< count
; i
++)
956 switch (break_class
[i
])
958 /* LB7 - do not break before spaces or zero-width space */
960 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
963 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
965 /* LB8 - break before character after zero-width space, skip spaces in-between */
967 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
969 if (j
< count
-1 && break_class
[j
+1] != b_ZW
)
970 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
972 /* LB8a - do not break after ZWJ */
974 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
979 /* LB9 - LB10 - combining marks */
980 for (i
= 0; i
< count
; i
++)
982 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
986 switch (break_class
[i
-1])
994 break_class
[i
] = b_AL
;
998 break_class
[i
] = break_class
[i
-1];
999 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1003 else break_class
[i
] = b_AL
;
1007 for (i
= 0; i
< count
; i
++)
1009 switch (break_class
[i
])
1011 /* LB11 - don't break before and after word joiner */
1013 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1014 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1016 /* LB12 - don't break after glue */
1018 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1022 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
1023 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1032 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1034 /* LB14 - do not break after OP, even after spaces */
1037 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
1039 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1041 /* LB15 - do not break within QU-OP, even with intervening spaces */
1044 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
1046 if (j
< count
- 1 && break_class
[j
+1] == b_OP
)
1047 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1052 while(j
> 0 && break_class
[j
] == b_SP
)
1054 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
1055 for (j
++; j
<= i
; j
++)
1056 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1058 /* LB17 - do not break within B2, even with intervening spaces */
1061 while (j
< count
&& break_class
[j
+1] == b_SP
)
1063 if (j
< count
- 1 && break_class
[j
+1] == b_B2
)
1064 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1069 for (i
= 0; i
< count
; i
++)
1071 switch(break_class
[i
])
1073 /* LB18 - break is allowed after space */
1075 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
1077 /* LB19 - don't break before or after quotation mark */
1079 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1080 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1084 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
1085 if (i
< count
- 1 && break_class
[i
+1] != b_QU
)
1086 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
1092 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1095 if (i
< count
- 1 && break_class
[i
+1] != b_CB
)
1096 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1102 switch (break_class
[i
+1])
1106 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1109 if (i
> 0 && break_class
[i
-1] == b_SY
)
1110 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1114 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1120 /* LB23 - do not break between digits and letters */
1121 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
1122 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
1123 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
1124 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
1125 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1127 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
1128 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
1129 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
1130 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
1131 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
1132 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
1133 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
1134 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1136 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
1137 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
1138 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
1139 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
1140 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
1141 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
1142 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
1143 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
1144 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
1145 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1148 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
1149 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
1150 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
1151 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
1152 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
1153 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
1154 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
1155 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
1156 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
1157 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
1158 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
1159 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
1160 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
1161 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
1162 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1165 if (break_class
[i
] == b_JL
)
1167 switch (break_class
[i
+1])
1173 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1176 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
1177 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
1178 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1179 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
1180 break_class
[i
+1] == b_JT
)
1181 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1184 switch (break_class
[i
])
1191 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
1192 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1194 if (break_class
[i
] == b_PR
)
1196 switch (break_class
[i
+1])
1203 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1208 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
1209 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
1210 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
1211 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
1212 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1215 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
1216 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
1217 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1220 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
1221 break_class
[i
+1] == b_OP
)
1222 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1223 if (break_class
[i
] == b_CP
&&
1224 (break_class
[i
+1] == b_AL
|| break_class
[i
+1] == b_HL
|| break_class
[i
+1] == b_NU
))
1225 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1227 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
1228 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
1232 while (j
> 0 && break_class
[--j
] == b_RI
)
1236 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1239 /* LB30b - do not break between an emoji base and an emoji modifier */
1240 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
1241 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
1245 /* LB31 - allow breaks everywhere else. */
1246 for (i
= 0; i
< count
; i
++)
1248 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
1249 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
1258 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
1260 TRACE("%s, %p.\n", debugstr_guid(riid
), obj
);
1262 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
1263 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
1264 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
1265 IsEqualIID(riid
, &IID_IUnknown
))
1271 WARN("%s not implemented.\n", debugstr_guid(riid
));
1274 return E_NOINTERFACE
;
1277 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
1282 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
1287 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
1288 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
1290 struct text_source_context context
;
1293 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
1298 if (FAILED(hr
= text_source_context_init(&context
, source
, position
, length
))) return hr
;
1300 return analyze_script(&context
, sink
);
1303 static inline unsigned int get_bidi_char_length(const struct bidi_char
*c
)
1305 return c
->ch
> 0xffff ? 2 : 1;
1308 static inline UINT8
get_char_bidi_class(UINT32 ch
)
1310 return get_table_entry_32(bidi_direction_table
, ch
);
1313 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
1314 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
1316 struct text_source_context context
;
1317 UINT8 baselevel
, resolved
, explicit;
1318 unsigned int i
, chars_count
= 0;
1319 struct bidi_char
*chars
, *ptr
;
1320 UINT32 pos
, seq_length
;
1323 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
1328 if (!(chars
= calloc(length
, sizeof(*chars
))))
1329 return E_OUTOFMEMORY
;
1332 text_source_context_init(&context
, source
, position
, length
);
1333 while (!text_source_get_next_u32_char(&context
))
1335 ptr
->ch
= context
.ch
;
1336 ptr
->nominal_bidi_class
= ptr
->bidi_class
= get_char_bidi_class(context
.ch
);
1342 /* Resolve levels using utf-32 codepoints, size differences are accounted for
1343 when levels are reported with SetBidiLevel(). */
1345 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
1346 hr
= bidi_computelevels(chars
, chars_count
, baselevel
);
1351 resolved
= chars
->resolved
;
1352 explicit = chars
->explicit;
1353 seq_length
= get_bidi_char_length(chars
);
1355 for (i
= 1, ptr
= chars
+ 1; i
< chars_count
; ++i
, ++ptr
)
1357 if (ptr
->resolved
== resolved
&& ptr
->explicit == explicit)
1359 seq_length
+= get_bidi_char_length(ptr
);
1363 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit, resolved
);
1368 seq_length
= get_bidi_char_length(ptr
);
1369 resolved
= ptr
->resolved
;
1370 explicit = ptr
->explicit;
1374 /* one char length case or normal completion call */
1375 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit, resolved
);
1383 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
1384 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
1389 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
1393 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
1394 IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
1396 DWRITE_LINE_BREAKPOINT
*breakpoints
;
1399 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
1404 if (!(breakpoints
= calloc(length
, sizeof(*breakpoints
))))
1405 return E_OUTOFMEMORY
;
1407 if (SUCCEEDED(hr
= analyze_linebreaks(source
, position
, length
, breakpoints
)))
1408 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
1415 static UINT32
get_opentype_language(const WCHAR
*locale
)
1417 UINT32 language
= DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1421 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, ARRAY_SIZE(tag
)))
1422 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
1428 static void get_number_substitutes(IDWriteNumberSubstitution
*substitution
, BOOL is_rtl
, WCHAR
*digits
)
1430 struct dwrite_numbersubstitution
*numbersubst
= unsafe_impl_from_IDWriteNumberSubstitution(substitution
);
1431 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1440 lctype
= numbersubst
->ignore_user_override
? LOCALE_NOUSEROVERRIDE
: 0;
1442 if (numbersubst
->method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE
) {
1445 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1446 if (GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_IDIGITSUBSTITUTION
| LOCALE_RETURN_NUMBER
, (WCHAR
*)&value
, 2)) {
1450 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
;
1453 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
;
1458 WARN("Unknown IDIGITSUBSTITUTION value %lu, locale %s.\n", value
, debugstr_w(numbersubst
->locale
));
1462 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst
->locale
));
1465 method
= numbersubst
->method
;
1469 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
:
1470 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1472 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1473 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
:
1474 if (GetLocaleInfoEx(numbersubst
->locale
, LOCALE_SISO639LANGNAME
, isolang
, ARRAY_SIZE(isolang
)))
1476 static const WCHAR arabicW
[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1478 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1479 if (!wcscmp(L
"ar", isolang
))
1481 wcscpy(digits
, arabicW
);
1485 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1491 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !*digits
) {
1492 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst
->locale
), method
);
1493 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1496 if ((method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
&& !is_rtl
) || method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
)
1500 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES
const **features
,
1501 UINT32
const *feature_range_lengths
, UINT32 feature_ranges
)
1505 if (!TRACE_ON(dwrite
) || !features
)
1508 for (i
= 0, start
= 0; i
< feature_ranges
; start
+= feature_range_lengths
[i
++]) {
1509 TRACE("feature range [%u,%u)\n", start
, start
+ feature_range_lengths
[i
]);
1510 for (j
= 0; j
< features
[i
]->featureCount
; j
++)
1511 TRACE("feature %s, parameter %u\n", debugstr_tag(features
[i
]->features
[j
].nameTag
),
1512 features
[i
]->features
[j
].parameter
);
1516 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
1517 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
1518 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
1519 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1520 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
1521 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
*glyphs
,
1522 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
1524 const struct dwritescript_properties
*scriptprops
;
1525 struct scriptshaping_context context
= { 0 };
1526 struct dwrite_fontface
*font_obj
;
1527 WCHAR digits
[NATIVE_DIGITS_LEN
];
1528 unsigned int glyph_count
;
1531 TRACE("%s:%u, %p, %d, %d, %s, %s, %p, %p, %p, %u, %u, %p, %p, %p, %p, %p.\n", debugstr_wn(text
, length
),
1532 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1533 features
, feature_range_lengths
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyphs
,
1534 glyph_props
, actual_glyph_count
);
1536 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1538 get_number_substitutes(substitution
, is_rtl
, digits
);
1539 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1540 glyph_count
= max(max_glyph_count
, length
);
1542 context
.cache
= fontface_get_shaping_cache(font_obj
);
1543 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1544 context
.text
= text
;
1545 context
.length
= length
;
1546 context
.is_rtl
= is_rtl
;
1547 context
.is_sideways
= is_sideways
;
1548 context
.u
.subst
.glyphs
= calloc(glyph_count
, sizeof(*glyphs
));
1549 context
.u
.subst
.glyph_props
= calloc(glyph_count
, sizeof(*glyph_props
));
1550 context
.u
.subst
.text_props
= text_props
;
1551 context
.u
.subst
.clustermap
= clustermap
;
1552 context
.u
.subst
.max_glyph_count
= max_glyph_count
;
1553 context
.u
.subst
.capacity
= glyph_count
;
1554 context
.u
.subst
.digits
= digits
;
1555 context
.language_tag
= get_opentype_language(locale
);
1556 context
.user_features
.features
= features
;
1557 context
.user_features
.range_lengths
= feature_range_lengths
;
1558 context
.user_features
.range_count
= feature_ranges
;
1559 context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1560 context
.table
= &context
.cache
->gsub
;
1562 *actual_glyph_count
= 0;
1564 if (!context
.u
.subst
.glyphs
|| !context
.u
.subst
.glyph_props
|| !context
.glyph_infos
)
1570 scriptprops
= &dwritescripts_properties
[context
.script
];
1571 hr
= shape_get_glyphs(&context
, scriptprops
->scripttags
);
1574 *actual_glyph_count
= context
.glyph_count
;
1575 memcpy(glyphs
, context
.u
.subst
.glyphs
, context
.glyph_count
* sizeof(*glyphs
));
1576 memcpy(glyph_props
, context
.u
.subst
.glyph_props
, context
.glyph_count
* sizeof(*glyph_props
));
1580 free(context
.u
.subst
.glyph_props
);
1581 free(context
.u
.subst
.glyphs
);
1582 free(context
.glyph_infos
);
1587 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1588 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1589 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1590 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, BOOL is_sideways
, BOOL is_rtl
,
1591 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1592 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1594 const struct dwritescript_properties
*scriptprops
;
1595 struct scriptshaping_context context
;
1596 struct dwrite_fontface
*font_obj
;
1600 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n", debugstr_wn(text
, text_len
),
1601 clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1602 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_lengths
,
1603 feature_ranges
, advances
, offsets
);
1605 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1607 if (glyph_count
== 0)
1610 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1612 for (i
= 0; i
< glyph_count
; ++i
)
1614 if (glyph_props
[i
].isZeroWidthSpace
)
1617 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, DWRITE_MEASURING_MODE_NATURAL
, emSize
, 1.0f
,
1618 NULL
, glyphs
[i
], is_sideways
);
1619 offsets
[i
].advanceOffset
= 0.0f
;
1620 offsets
[i
].ascenderOffset
= 0.0f
;
1623 context
.cache
= fontface_get_shaping_cache(font_obj
);
1624 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1625 context
.text
= text
;
1626 context
.length
= text_len
;
1627 context
.is_rtl
= is_rtl
;
1628 context
.is_sideways
= is_sideways
;
1629 context
.u
.pos
.glyphs
= glyphs
;
1630 context
.u
.pos
.glyph_props
= glyph_props
;
1631 context
.u
.pos
.text_props
= text_props
;
1632 context
.u
.pos
.clustermap
= clustermap
;
1633 context
.glyph_count
= glyph_count
;
1634 context
.emsize
= emSize
;
1635 context
.measuring_mode
= DWRITE_MEASURING_MODE_NATURAL
;
1636 context
.advances
= advances
;
1637 context
.offsets
= offsets
;
1638 context
.language_tag
= get_opentype_language(locale
);
1639 context
.user_features
.features
= features
;
1640 context
.user_features
.range_lengths
= feature_range_lengths
;
1641 context
.user_features
.range_count
= feature_ranges
;
1642 context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1643 context
.table
= &context
.cache
->gpos
;
1645 if (!context
.glyph_infos
)
1651 scriptprops
= &dwritescripts_properties
[context
.script
];
1652 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1655 free(context
.glyph_infos
);
1660 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1661 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1662 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1663 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, float ppdip
,
1664 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1665 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1666 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1668 const struct dwritescript_properties
*scriptprops
;
1669 struct scriptshaping_context context
;
1670 DWRITE_MEASURING_MODE measuring_mode
;
1671 struct dwrite_fontface
*font_obj
;
1675 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1676 debugstr_wn(text
, text_len
), clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
,
1677 emSize
, ppdip
, transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
),
1678 debugstr_w(locale
), features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1680 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1682 if (glyph_count
== 0)
1685 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1687 measuring_mode
= use_gdi_natural
? DWRITE_MEASURING_MODE_GDI_NATURAL
: DWRITE_MEASURING_MODE_GDI_CLASSIC
;
1689 for (i
= 0; i
< glyph_count
; ++i
)
1691 if (glyph_props
[i
].isZeroWidthSpace
)
1694 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, measuring_mode
, emSize
, ppdip
,
1695 transform
, glyphs
[i
], is_sideways
);
1696 offsets
[i
].advanceOffset
= 0.0f
;
1697 offsets
[i
].ascenderOffset
= 0.0f
;
1700 context
.cache
= fontface_get_shaping_cache(font_obj
);
1701 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1702 context
.text
= text
;
1703 context
.length
= text_len
;
1704 context
.is_rtl
= is_rtl
;
1705 context
.is_sideways
= is_sideways
;
1706 context
.u
.pos
.glyphs
= glyphs
;
1707 context
.u
.pos
.glyph_props
= glyph_props
;
1708 context
.u
.pos
.text_props
= text_props
;
1709 context
.u
.pos
.clustermap
= clustermap
;
1710 context
.glyph_count
= glyph_count
;
1711 context
.emsize
= emSize
* ppdip
;
1712 context
.measuring_mode
= measuring_mode
;
1713 context
.advances
= advances
;
1714 context
.offsets
= offsets
;
1715 context
.language_tag
= get_opentype_language(locale
);
1716 context
.user_features
.features
= features
;
1717 context
.user_features
.range_lengths
= feature_range_lengths
;
1718 context
.user_features
.range_count
= feature_ranges
;
1719 context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1720 context
.table
= &context
.cache
->gpos
;
1722 if (!context
.glyph_infos
)
1728 scriptprops
= &dwritescripts_properties
[context
.script
];
1729 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1732 free(context
.glyph_infos
);
1737 static HRESULT
apply_cluster_spacing(float leading_spacing
, float trailing_spacing
, float min_advance_width
,
1738 unsigned int start
, unsigned int end
, float const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1739 DWRITE_SHAPING_GLYPH_PROPERTIES
const *glyph_props
, float *modified_advances
,
1740 DWRITE_GLYPH_OFFSET
*modified_offsets
)
1742 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1743 unsigned int first_spacing
, last_spacing
, i
;
1744 float advance
, origin
= 0.0f
, *deltas
;
1745 BOOL is_spacing_cluster
= FALSE
;
1747 if (modified_advances
!= advances
)
1748 memcpy(&modified_advances
[start
], &advances
[start
], (end
- start
+ 1) * sizeof(*advances
));
1749 if (modified_offsets
!= offsets
)
1750 memcpy(&modified_offsets
[start
], &offsets
[start
], (end
- start
+ 1) * sizeof(*offsets
));
1752 for (first_spacing
= start
; first_spacing
<= end
; ++first_spacing
)
1754 if ((is_spacing_cluster
= !glyph_props
[first_spacing
].isZeroWidthSpace
))
1758 /* Nothing to adjust if there is no spacing glyphs. */
1759 if (!is_spacing_cluster
)
1762 for (last_spacing
= end
; last_spacing
>= start
; --last_spacing
)
1764 if (!glyph_props
[last_spacing
].isZeroWidthSpace
)
1768 if (!(deltas
= calloc(end
- start
+ 1, sizeof(*deltas
))))
1769 return E_OUTOFMEMORY
;
1771 /* Cluster advance, note that properties are ignored. */
1772 origin
= offsets
[start
].advanceOffset
;
1773 for (i
= start
, advance
= 0.0f
; i
<= end
; ++i
)
1775 float cur
= advance
+ offsets
[i
].advanceOffset
;
1777 deltas
[i
- start
] = cur
- origin
;
1779 advance
+= advances
[i
];
1783 /* Negative spacing. */
1784 if (leading_spacing
< 0.0f
)
1786 advance
+= leading_spacing
;
1787 modified_advances
[first_spacing
] += leading_spacing
;
1788 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1791 if (trailing_spacing
< 0.0f
)
1793 advance
+= trailing_spacing
;
1794 modified_advances
[last_spacing
] += trailing_spacing
;
1797 /* Minimal advance. */
1798 advance
= min_advance_width
- advance
;
1799 if (advance
> 0.0f
) {
1800 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1801 float half
= advance
/ 2.0f
;
1805 modified_advances
[first_spacing
] += half
;
1806 modified_advances
[last_spacing
] += half
;
1807 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1809 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1811 modified_advances
[first_spacing
] += half
;
1812 modified_advances
[last_spacing
] += half
;
1813 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1815 else if (leading_spacing
< 0.0f
)
1817 modified_advances
[first_spacing
] += advance
;
1818 modified_offsets
[first_spacing
].advanceOffset
+= advance
;
1821 modified_advances
[last_spacing
] += advance
;
1824 /* Positive spacing. */
1825 if (leading_spacing
> 0.0f
)
1827 modified_advances
[first_spacing
] += leading_spacing
;
1828 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1831 if (trailing_spacing
> 0.0f
)
1832 modified_advances
[last_spacing
] += trailing_spacing
;
1834 /* Update offsets to preserve original relative positions within cluster. */
1835 for (i
= first_spacing
; i
> start
; --i
)
1837 unsigned int cur
= i
- 1;
1838 modified_offsets
[cur
].advanceOffset
= modified_advances
[cur
] + modified_offsets
[i
].advanceOffset
-
1842 for (i
= first_spacing
+ 1; i
<= end
; ++i
)
1844 modified_offsets
[i
].advanceOffset
= deltas
[i
- start
] + modified_offsets
[i
- 1].advanceOffset
-
1845 modified_advances
[i
- 1];
1853 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1855 UINT16 g
= clustermap
[start
];
1858 while (start
< text_len
&& clustermap
[++start
] == g
)
1863 /* Applies spacing adjustments to clusters.
1865 Adjustments are applied in the following order:
1867 1. Negative adjustments
1869 Leading and trailing spacing could be negative, at this step
1870 only negative ones are actually applied. Leading spacing is only
1871 applied to leading glyph, trailing - to trailing glyph.
1873 2. Minimum advance width
1875 Advances could only be reduced at this point or unchanged. In any
1876 case it's checked if cluster advance width is less than minimum width.
1877 If it's the case advance width is incremented up to minimum value.
1879 Important part is the direction in which this increment is applied;
1880 it depends on direction from which total cluster advance was trimmed
1881 at step 1. So it could be incremented from leading, trailing, or both
1882 sides. When applied to both sides, each side gets half of difference
1883 that brings advance to minimum width.
1885 3. Positive adjustments
1887 After minimum width rule was applied, positive spacing is applied in the same
1888 way as negative one on step 1.
1890 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1891 keeps its position in coordinate system where initial advance width is counted
1896 It's known that isZeroWidthSpace property keeps initial advance from changing.
1899 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1900 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1901 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1902 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1906 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1907 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1909 if (min_advance_width
< 0.0f
) {
1910 if (modified_advances
!= advances
)
1911 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1912 return E_INVALIDARG
;
1915 for (i
= 0; i
< len
;)
1917 unsigned int length
= get_cluster_length(clustermap
, i
, len
);
1918 unsigned int start
, end
;
1920 start
= clustermap
[i
];
1921 end
= i
+ length
< len
? clustermap
[i
+ length
] : glyph_count
;
1923 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
, start
, end
- 1, advances
,
1924 offsets
, props
, modified_advances
, modified_offsets
);
1932 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*fontface
,
1933 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1934 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1936 struct dwrite_fontface
*font_obj
;
1937 const DWRITE_FONT_METRICS1
*metrics
;
1939 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1940 baseline_coord
, exists
);
1943 *baseline_coord
= 0;
1945 if (baseline
== DWRITE_BASELINE_DEFAULT
)
1946 baseline
= vertical
? DWRITE_BASELINE_CENTRAL
: DWRITE_BASELINE_ROMAN
;
1948 if ((unsigned int)baseline
> DWRITE_BASELINE_MAXIMUM
)
1949 return E_INVALIDARG
;
1951 /* TODO: fetch BASE table data if available. */
1953 if (!*exists
&& is_simulation_allowed
)
1955 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1956 metrics
= &font_obj
->metrics
;
1960 case DWRITE_BASELINE_ROMAN
:
1961 *baseline_coord
= vertical
? metrics
->descent
: 0;
1963 case DWRITE_BASELINE_CENTRAL
:
1964 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1965 -(metrics
->ascent
- metrics
->descent
) / 2;
1967 case DWRITE_BASELINE_MATH
:
1968 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1969 -(metrics
->ascent
+ metrics
->descent
) / 2;
1971 case DWRITE_BASELINE_HANGING
:
1972 /* FIXME: this one isn't accurate, but close. */
1973 *baseline_coord
= vertical
? metrics
->capHeight
* 6 / 7 + metrics
->descent
: metrics
->capHeight
* 6 / 7;
1975 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM
:
1976 case DWRITE_BASELINE_MINIMUM
:
1977 *baseline_coord
= vertical
? 0 : metrics
->descent
;
1979 case DWRITE_BASELINE_IDEOGRAPHIC_TOP
:
1980 case DWRITE_BASELINE_MAXIMUM
:
1981 *baseline_coord
= vertical
? metrics
->ascent
+ metrics
->descent
: -metrics
->ascent
;
1991 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1992 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1994 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1998 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1999 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
2001 TRACE("%d, %d, %p.\n", angle
, is_sideways
, transform
);
2003 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
2006 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
2007 DWRITE_SCRIPT_PROPERTIES
*props
)
2009 TRACE("%u, %p.\n", sa
.script
, props
);
2011 if (sa
.script
> Script_LastId
)
2012 return E_INVALIDARG
;
2014 *props
= dwritescripts_properties
[sa
.script
].props
;
2018 static inline BOOL
is_char_from_simple_script(WCHAR c
)
2020 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
) ||
2021 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
2022 c
== 0x200e || c
== 0x200f || (c
>= 0x202a && c
<= 0x202e))
2025 UINT16 script
= get_char_script(c
);
2026 return !dwritescripts_properties
[script
].is_complex
;
2030 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
2031 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
2036 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
2042 return E_INVALIDARG
;
2049 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
2050 for (i
= 1; i
< len
&& text
[i
]; i
++) {
2051 if (is_char_from_simple_script(text
[i
])) {
2062 if (*is_simple
&& indices
)
2066 if (!(codepoints
= calloc(*len_read
, sizeof(*codepoints
))))
2067 return E_OUTOFMEMORY
;
2069 for (i
= 0; i
< *len_read
; i
++)
2070 codepoints
[i
] = text
[i
];
2072 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
2079 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
2080 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
2081 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
2083 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
2084 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
2088 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
2089 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
2090 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
2092 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
2097 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
2098 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
2099 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
2100 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
2101 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
2102 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
2104 FIXME("(%p %.2f %u %u %u %u %p %p %p %p %p %p %p %p %p %p %p): stub\n", face
, font_em_size
, sa
.script
,
2105 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
2106 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
2110 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
2111 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
2113 static const DWRITE_MATRIX transforms
[] = {
2114 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
2115 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
2116 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
2117 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
2120 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle
, is_sideways
, originX
, originY
, m
);
2122 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
2123 memset(m
, 0, sizeof(*m
));
2124 return E_INVALIDARG
;
2127 /* for sideways case simply rotate 90 degrees more */
2130 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
2131 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
2133 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
2134 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
2136 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
2137 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
2139 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
2140 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
2147 *m
= transforms
[angle
];
2149 /* shift components represent transform necessary to get from original point to
2150 rotated one in new coordinate system */
2151 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
2152 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
2153 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
2159 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
2160 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
2161 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
2163 struct scriptshaping_context context
= { 0 };
2164 const struct dwritescript_properties
*props
;
2165 struct dwrite_fontface
*font_obj
;
2167 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
,
2168 actual_tagcount
, tags
);
2170 if (sa
.script
> Script_LastId
)
2171 return E_INVALIDARG
;
2173 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
2175 context
.cache
= fontface_get_shaping_cache(font_obj
);
2176 context
.language_tag
= get_opentype_language(locale
);
2177 props
= &dwritescripts_properties
[sa
.script
];
2179 return shape_get_typographic_features(&context
, props
->scripttags
, max_tagcount
, actual_tagcount
, tags
);
2182 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
2183 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
, DWRITE_FONT_FEATURE_TAG feature
,
2184 UINT32 glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
2186 struct scriptshaping_context context
= { 0 };
2187 const struct dwritescript_properties
*props
;
2188 struct dwrite_fontface
*font_obj
;
2191 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), debugstr_tag(feature
),
2192 glyph_count
, glyphs
, feature_applies
);
2194 if (sa
.script
> Script_LastId
)
2195 return E_INVALIDARG
;
2197 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
2199 context
.cache
= fontface_get_shaping_cache(font_obj
);
2200 context
.language_tag
= get_opentype_language(locale
);
2201 if (!(context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
))))
2202 return E_OUTOFMEMORY
;
2204 props
= &dwritescripts_properties
[sa
.script
];
2206 hr
= shape_check_typographic_feature(&context
, props
->scripttags
, feature
, glyph_count
, glyphs
, feature_applies
);
2208 free(context
.glyph_infos
);
2213 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl
=
2215 dwritetextanalyzer_QueryInterface
,
2216 dwritetextanalyzer_AddRef
,
2217 dwritetextanalyzer_Release
,
2218 dwritetextanalyzer_AnalyzeScript
,
2219 dwritetextanalyzer_AnalyzeBidi
,
2220 dwritetextanalyzer_AnalyzeNumberSubstitution
,
2221 dwritetextanalyzer_AnalyzeLineBreakpoints
,
2222 dwritetextanalyzer_GetGlyphs
,
2223 dwritetextanalyzer_GetGlyphPlacements
,
2224 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
2225 dwritetextanalyzer1_ApplyCharacterSpacing
,
2226 dwritetextanalyzer1_GetBaseline
,
2227 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
2228 dwritetextanalyzer1_GetGlyphOrientationTransform
,
2229 dwritetextanalyzer1_GetScriptProperties
,
2230 dwritetextanalyzer1_GetTextComplexity
,
2231 dwritetextanalyzer1_GetJustificationOpportunities
,
2232 dwritetextanalyzer1_JustifyGlyphAdvances
,
2233 dwritetextanalyzer1_GetJustifiedGlyphs
,
2234 dwritetextanalyzer2_GetGlyphOrientationTransform
,
2235 dwritetextanalyzer2_GetTypographicFeatures
,
2236 dwritetextanalyzer2_CheckTypographicFeature
2239 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
2241 IDWriteTextAnalyzer2
*get_text_analyzer(void)
2243 return &textanalyzer
;
2246 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
2248 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
2250 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
2251 IsEqualIID(riid
, &IID_IUnknown
))
2254 IDWriteNumberSubstitution_AddRef(iface
);
2258 WARN("%s not implemented.\n", debugstr_guid(riid
));
2262 return E_NOINTERFACE
;
2265 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
2267 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
2268 ULONG refcount
= InterlockedIncrement(&object
->refcount
);
2270 TRACE("%p, refcount %ld.\n", iface
, refcount
);
2275 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
2277 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
2278 ULONG refcount
= InterlockedDecrement(&object
->refcount
);
2280 TRACE("%p, refcount %ld.\n", iface
, refcount
);
2284 free(object
->locale
);
2291 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
=
2293 dwritenumbersubstitution_QueryInterface
,
2294 dwritenumbersubstitution_AddRef
,
2295 dwritenumbersubstitution_Release
2298 struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
2300 if (!iface
|| iface
->lpVtbl
!= &numbersubstitutionvtbl
)
2302 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
2305 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
2306 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
2308 struct dwrite_numbersubstitution
*substitution
;
2312 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
2313 return E_INVALIDARG
;
2315 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
2316 return E_INVALIDARG
;
2318 if (!(substitution
= calloc(1, sizeof(*substitution
))))
2319 return E_OUTOFMEMORY
;
2321 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
2322 substitution
->refcount
= 1;
2323 substitution
->ignore_user_override
= ignore_user_override
;
2324 substitution
->method
= method
;
2325 substitution
->locale
= wcsdup(locale
);
2326 if (locale
&& !substitution
->locale
)
2329 return E_OUTOFMEMORY
;
2332 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
2336 /* IDWriteFontFallback */
2337 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback1
*iface
, REFIID riid
, void **obj
)
2339 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
2341 if (IsEqualIID(riid
, &IID_IDWriteFontFallback1
) ||
2342 IsEqualIID(riid
, &IID_IDWriteFontFallback
) ||
2343 IsEqualIID(riid
, &IID_IUnknown
))
2346 IDWriteFontFallback1_AddRef(iface
);
2350 WARN("%s not implemented.\n", debugstr_guid(riid
));
2353 return E_NOINTERFACE
;
2356 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback1
*iface
)
2358 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2360 TRACE("%p.\n", iface
);
2362 return IDWriteFactory7_AddRef(fallback
->factory
);
2365 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback1
*iface
)
2367 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2369 TRACE("%p.\n", fallback
);
2371 return IDWriteFactory7_Release(fallback
->factory
);
2374 static inline BOOL
fallback_is_uvs(const struct text_source_context
*context
)
2376 /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */
2377 if (context
->ch
>= 0x180b && context
->ch
<= 0x180d) return TRUE
;
2378 /* VARIATION SELECTOR-1..16 */
2379 if (context
->ch
>= 0xfe00 && context
->ch
<= 0xfe0f) return TRUE
;
2380 /* VARIATION SELECTOR-17..256 */
2381 if (context
->ch
>= 0xe0100 && context
->ch
<= 0xe01ef) return TRUE
;
2385 static UINT32
fallback_font_get_supported_length(IDWriteFont3
*font
, IDWriteTextAnalysisSource
*source
,
2386 UINT32 position
, UINT32 length
)
2388 struct text_source_context context
;
2391 text_source_context_init(&context
, source
, position
, length
);
2392 while (!text_source_get_next_u32_char(&context
))
2394 /* Ignore selectors that are not leading. */
2395 if (!mapped
|| !fallback_is_uvs(&context
))
2397 if (!IDWriteFont3_HasCharacter(font
, context
.ch
)) break;
2399 mapped
+= text_source_get_char_length(&context
);
2405 static HRESULT
fallback_map_characters(const struct dwrite_fontfallback
*fallback
, IDWriteTextAnalysisSource
*source
,
2406 UINT32 position
, UINT32 text_length
, DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
,
2407 DWRITE_FONT_STRETCH stretch
, IDWriteFont
**ret_font
, UINT32
*ret_length
, float *scale
)
2409 const struct fallback_mapping
*mapping
= NULL
;
2410 struct text_source_context context
;
2411 const struct fallback_data
*data
;
2412 const WCHAR
*locale_name
= NULL
;
2413 struct fallback_locale
*locale
;
2414 UINT32 i
, length
= 0, mapped
;
2418 /* ~0u is a marker for system fallback data */
2419 data
= fallback
->data
.count
== ~0u ? &system_fallback
: &fallback
->data
;
2421 /* We will try to map as much of given input as GetLocaleName() says. It's assumed that returned length covers
2422 whole span of characters set with that locale, so callback is only used once. */
2423 if (FAILED(hr
= IDWriteTextAnalysisSource_GetLocaleName(source
, position
, &length
, &locale_name
)))
2426 length
= length
? min(length
, text_length
) : text_length
;
2427 if (!locale_name
) locale_name
= L
"";
2429 /* Lookup locale entry once, if specific locale is missing neutral one will be returned. */
2430 locale
= font_fallback_get_locale(&data
->locales
, locale_name
);
2432 if (FAILED(hr
= text_source_context_init(&context
, source
, position
, length
))) return hr
;
2434 /* Find a mapping for given locale. */
2435 text_source_get_next_u32_char(&context
);
2436 mapping
= find_fallback_mapping(data
, locale
, context
.ch
);
2437 mapped
= text_source_get_char_length(&context
);
2438 while (!text_source_get_next_u32_char(&context
))
2440 if (find_fallback_mapping(data
, locale
, context
.ch
) != mapping
) break;
2441 mapped
+= text_source_get_char_length(&context
);
2447 *ret_length
= mapped
;
2452 /* Go through families in the mapping, use first family that supports some of the input. */
2453 for (i
= 0; i
< mapping
->families_count
; ++i
)
2455 if (SUCCEEDED(create_matching_font(mapping
->collection
? mapping
->collection
: fallback
->systemcollection
,
2456 mapping
->families
[i
], weight
, style
, stretch
, &IID_IDWriteFont3
, (void **)&font
)))
2458 if (!(mapped
= fallback_font_get_supported_length(font
, source
, position
, mapped
)))
2460 IDWriteFont3_Release(font
);
2464 *ret_font
= (IDWriteFont
*)font
;
2465 *ret_length
= mapped
;
2466 *scale
= mapping
->scale
;
2472 /* Mapping was found, but either font couldn't be created or there's no font that supports given input. */
2474 *ret_length
= length
;
2479 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
, DWRITE_FONT_WEIGHT weight
,
2480 DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, REFIID riid
, void **obj
)
2482 IDWriteFontFamily
*family
;
2483 BOOL exists
= FALSE
;
2490 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
2497 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
2501 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, &font
);
2502 IDWriteFontFamily_Release(family
);
2506 hr
= IDWriteFont_QueryInterface(font
, riid
, obj
);
2507 IDWriteFont_Release(font
);
2513 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2514 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2515 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2516 IDWriteFont
**ret_font
, float *scale
)
2518 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2521 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface
, source
, position
, length
,
2522 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2529 return E_INVALIDARG
;
2534 if (!basecollection
)
2535 basecollection
= fallback
->systemcollection
;
2537 if (basefamily
&& *basefamily
)
2539 if (SUCCEEDED(create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
,
2540 &IID_IDWriteFont
, (void **)&font
)))
2542 if ((*mapped_length
= fallback_font_get_supported_length(font
, source
, position
, length
)))
2544 *ret_font
= (IDWriteFont
*)font
;
2548 IDWriteFont3_Release(font
);
2552 return fallback_map_characters(fallback
, source
, position
, length
, weight
, style
, stretch
, ret_font
, mapped_length
, scale
);
2555 static HRESULT WINAPI
fontfallback1_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2556 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2557 DWRITE_FONT_AXIS_VALUE
const *axis_values
, UINT32 values_count
, UINT32
*mapped_length
, FLOAT
*scale
,
2558 IDWriteFontFace5
**ret_fontface
)
2560 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2561 debugstr_w(basefamily
), axis_values
, values_count
, mapped_length
, scale
, ret_fontface
);
2566 static const IDWriteFontFallback1Vtbl fontfallbackvtbl
=
2568 fontfallback_QueryInterface
,
2569 fontfallback_AddRef
,
2570 fontfallback_Release
,
2571 fontfallback_MapCharacters
,
2572 fontfallback1_MapCharacters
,
2575 void release_system_fontfallback(IDWriteFontFallback1
*iface
)
2577 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2578 IDWriteFontCollection_Release(fallback
->systemcollection
);
2582 static ULONG WINAPI
customfontfallback_AddRef(IDWriteFontFallback1
*iface
)
2584 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2585 ULONG refcount
= InterlockedIncrement(&fallback
->refcount
);
2587 TRACE("%p, refcount %lu.\n", iface
, refcount
);
2592 static ULONG WINAPI
customfontfallback_Release(IDWriteFontFallback1
*iface
)
2594 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2595 ULONG refcount
= InterlockedDecrement(&fallback
->refcount
);
2597 TRACE("%p, refcount %lu.\n", iface
, refcount
);
2601 IDWriteFactory7_Release(fallback
->factory
);
2602 if (fallback
->systemcollection
)
2603 IDWriteFontCollection_Release(fallback
->systemcollection
);
2604 release_fallback_data(&fallback
->data
);
2611 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl
=
2613 fontfallback_QueryInterface
,
2614 customfontfallback_AddRef
,
2615 customfontfallback_Release
,
2616 fontfallback_MapCharacters
,
2617 fontfallback1_MapCharacters
,
2620 static HRESULT WINAPI
fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder
*iface
, REFIID riid
, void **obj
)
2622 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
2624 if (IsEqualIID(riid
, &IID_IDWriteFontFallbackBuilder
) || IsEqualIID(riid
, &IID_IUnknown
)) {
2626 IDWriteFontFallbackBuilder_AddRef(iface
);
2630 WARN("%s not implemented.\n", debugstr_guid(riid
));
2633 return E_NOINTERFACE
;
2636 static ULONG WINAPI
fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder
*iface
)
2638 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2639 ULONG refcount
= InterlockedIncrement(&fallbackbuilder
->refcount
);
2641 TRACE("%p, refcount %ld.\n", iface
, refcount
);
2646 static ULONG WINAPI
fontfallbackbuilder_Release(IDWriteFontFallbackBuilder
*iface
)
2648 struct dwrite_fontfallback_builder
*builder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2649 ULONG refcount
= InterlockedDecrement(&builder
->refcount
);
2651 TRACE("%p, refcount %ld.\n", iface
, refcount
);
2655 IDWriteFactory7_Release(builder
->factory
);
2656 release_fallback_data(&builder
->data
);
2663 static struct fallback_locale
* fallback_builder_add_locale(struct dwrite_fontfallback_builder
*builder
,
2664 const WCHAR
*locale_name
)
2666 struct fallback_locale
*locale
;
2668 if (!locale_name
) locale_name
= L
"";
2669 if ((locale
= font_fallback_get_locale(&builder
->data
.locales
, locale_name
))) return locale
;
2670 if (!(locale
= calloc(1, sizeof(*locale
)))) return NULL
;
2671 lstrcpynW(locale
->name
, locale_name
, ARRAY_SIZE(locale
->name
));
2672 list_add_tail(&builder
->data
.locales
, &locale
->entry
);
2676 static HRESULT WINAPI
fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder
*iface
,
2677 const DWRITE_UNICODE_RANGE
*ranges
, UINT32 ranges_count
, WCHAR
const **families
, UINT32 families_count
,
2678 IDWriteFontCollection
*collection
, WCHAR
const *locale_name
, WCHAR
const *base_family
, float scale
)
2680 struct dwrite_fontfallback_builder
*builder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2681 struct fallback_mapping
*mapping
;
2682 struct fallback_locale
*locale
;
2683 unsigned int i
, j
, m
, count
;
2685 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface
, ranges
, ranges_count
, families
, families_count
,
2686 collection
, debugstr_w(locale_name
), debugstr_w(base_family
), scale
);
2688 if (!ranges
|| !ranges_count
|| !families
|| !families_count
|| scale
< 0.0f
)
2689 return E_INVALIDARG
;
2692 FIXME("base family ignored.\n");
2694 if (!dwrite_array_reserve((void **)&builder
->data
.mappings
, &builder
->mappings_size
,
2695 builder
->data
.count
+ 1, sizeof(*builder
->data
.mappings
)))
2697 return E_OUTOFMEMORY
;
2700 mapping
= &builder
->data
.mappings
[builder
->data
.count
];
2701 memset(mapping
, 0, sizeof(*mapping
));
2703 /* Append new mapping, link to its locale node. */
2705 if (!(locale
= fallback_builder_add_locale(builder
, locale_name
)))
2708 if (!(mapping
->ranges
= calloc(ranges_count
, sizeof(*mapping
->ranges
))))
2711 /* Filter ranges that won't be usable. */
2712 for (i
= 0, count
= 0; i
< ranges_count
; ++i
)
2714 if (ranges
[i
].first
> ranges
[i
].last
) continue;
2715 if (ranges
[i
].first
> 0x10ffff) continue;
2716 mapping
->ranges
[count
].first
= ranges
[i
].first
;
2717 mapping
->ranges
[count
].last
= min(ranges
[i
].last
, 0x10ffff);
2722 release_fallback_mapping(mapping
);
2726 mapping
->ranges_count
= count
;
2728 if (!(mapping
->families
= calloc(families_count
, sizeof(*mapping
->families
))))
2730 mapping
->families_count
= families_count
;
2731 for (i
= 0; i
< families_count
; i
++)
2732 if (!(mapping
->families
[i
] = wcsdup(families
[i
]))) goto failed
;
2733 mapping
->scale
= scale
;
2735 if (FAILED(fallback_locale_add_mapping(locale
, builder
->data
.count
))) goto failed
;
2737 /* Mappings with explicit collections take priority, for that reduce existing mappings ranges
2738 by newly added ranges. */
2740 mapping
->collection
= collection
;
2741 if (mapping
->collection
)
2743 IDWriteFontCollection_AddRef(mapping
->collection
);
2745 for (m
= 0; m
< builder
->data
.count
; ++m
)
2747 struct fallback_mapping
*c
= &builder
->data
.mappings
[m
];
2748 if (c
->collection
) continue;
2749 for (i
= 0; i
< count
; ++i
)
2751 const DWRITE_UNICODE_RANGE
*new_range
= &mapping
->ranges
[i
];
2753 for (j
= 0; j
< c
->ranges_count
; ++j
)
2755 DWRITE_UNICODE_RANGE
*range
= &c
->ranges
[j
];
2757 /* In case existing ranges intersect, disable or reduce them */
2758 if (range
->first
>= new_range
->first
&& range
->last
<= new_range
->last
)
2760 range
->first
= range
->last
= ~0u;
2762 else if (range
->first
>= new_range
->first
&& range
->first
<= new_range
->last
)
2764 range
->first
= new_range
->last
;
2766 else if (range
->last
>= new_range
->first
&& range
->last
<= new_range
->last
)
2768 range
->last
= new_range
->first
;
2775 builder
->data
.count
++;
2779 release_fallback_mapping(mapping
);
2780 return E_OUTOFMEMORY
;
2783 static HRESULT WINAPI
fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder
*iface
, IDWriteFontFallback
*fallback
)
2785 FIXME("%p, %p stub.\n", iface
, fallback
);
2790 static HRESULT
fallbackbuilder_init_fallback_data(const struct dwrite_fontfallback_builder
*builder
,
2791 struct fallback_data
*data
)
2793 struct fallback_locale
*iter
, *locale
;
2796 /* Duplicate locales list. */
2797 list_init(&data
->locales
);
2798 LIST_FOR_EACH_ENTRY(iter
, &builder
->data
.locales
, struct fallback_locale
, entry
)
2800 if (!(locale
= calloc(1, sizeof(*locale
)))) goto failed
;
2801 wcscpy(locale
->name
, iter
->name
);
2802 locale
->ranges
.count
= iter
->ranges
.count
;
2803 locale
->ranges
.size
= iter
->ranges
.count
;
2804 if (!(locale
->ranges
.data
= malloc(iter
->ranges
.count
* sizeof(*iter
->ranges
.data
))))
2809 memcpy(locale
->ranges
.data
, iter
->ranges
.data
, iter
->ranges
.count
* sizeof(*iter
->ranges
.data
));
2810 list_add_tail(&data
->locales
, &locale
->entry
);
2813 /* Duplicate mappings. */
2814 if (!(data
->mappings
= calloc(builder
->data
.count
, sizeof(*data
->mappings
))))
2817 data
->count
= builder
->data
.count
;
2818 for (i
= 0; i
< data
->count
; ++i
)
2820 struct fallback_mapping
*src
= &builder
->data
.mappings
[i
];
2821 struct fallback_mapping
*dst
= &data
->mappings
[i
];
2823 if (!(dst
->ranges
= calloc(src
->ranges_count
, sizeof(*src
->ranges
)))) goto failed
;
2824 memcpy(dst
->ranges
, src
->ranges
, src
->ranges_count
* sizeof(*src
->ranges
));
2825 dst
->ranges_count
= src
->ranges_count
;
2827 if (!(dst
->families
= calloc(src
->families_count
, sizeof(*src
->families
)))) goto failed
;
2828 dst
->families_count
= src
->families_count
;
2829 for (j
= 0; j
< src
->families_count
; ++j
)
2831 if (!(dst
->families
[j
] = wcsdup(src
->families
[j
]))) goto failed
;
2834 dst
->collection
= src
->collection
;
2835 if (dst
->collection
)
2836 IDWriteFontCollection_AddRef(dst
->collection
);
2837 dst
->scale
= src
->scale
;
2844 return E_OUTOFMEMORY
;
2847 static HRESULT
fallbackbuilder_create_fallback(struct dwrite_fontfallback_builder
*builder
, struct dwrite_fontfallback
**ret
)
2849 struct dwrite_fontfallback
*fallback
;
2852 if (!(fallback
= calloc(1, sizeof(*fallback
))))
2853 return E_OUTOFMEMORY
;
2855 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &customfontfallbackvtbl
;
2856 fallback
->refcount
= 1;
2857 fallback
->factory
= builder
->factory
;
2858 IDWriteFactory7_AddRef(fallback
->factory
);
2859 if (FAILED(hr
= IDWriteFactory_GetSystemFontCollection((IDWriteFactory
*)fallback
->factory
,
2860 &fallback
->systemcollection
, FALSE
)))
2865 if (FAILED(hr
= fallbackbuilder_init_fallback_data(builder
, &fallback
->data
)))
2876 IDWriteFontFallback1_Release(&fallback
->IDWriteFontFallback1_iface
);
2880 static HRESULT WINAPI
fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder
*iface
,
2881 IDWriteFontFallback
**ret
)
2883 struct dwrite_fontfallback_builder
*builder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2884 struct dwrite_fontfallback
*fallback
;
2887 TRACE("%p, %p.\n", iface
, ret
);
2891 if (SUCCEEDED(hr
= fallbackbuilder_create_fallback(builder
, &fallback
)))
2893 *ret
= (IDWriteFontFallback
*)&fallback
->IDWriteFontFallback1_iface
;
2899 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl
=
2901 fontfallbackbuilder_QueryInterface
,
2902 fontfallbackbuilder_AddRef
,
2903 fontfallbackbuilder_Release
,
2904 fontfallbackbuilder_AddMapping
,
2905 fontfallbackbuilder_AddMappings
,
2906 fontfallbackbuilder_CreateFontFallback
,
2909 static HRESULT
create_fontfallback_builder_internal(IDWriteFactory7
*factory
, struct dwrite_fontfallback_builder
**ret
)
2911 struct dwrite_fontfallback_builder
*builder
;
2915 if (!(builder
= calloc(1, sizeof(*builder
))))
2916 return E_OUTOFMEMORY
;
2918 builder
->IDWriteFontFallbackBuilder_iface
.lpVtbl
= &fontfallbackbuildervtbl
;
2919 builder
->refcount
= 1;
2920 builder
->factory
= factory
;
2921 IDWriteFactory7_AddRef(builder
->factory
);
2922 list_init(&builder
->data
.locales
);
2929 HRESULT
create_fontfallback_builder(IDWriteFactory7
*factory
, IDWriteFontFallbackBuilder
**ret
)
2931 struct dwrite_fontfallback_builder
*builder
;
2936 if (SUCCEEDED(hr
= create_fontfallback_builder_internal(factory
, &builder
)))
2937 *ret
= &builder
->IDWriteFontFallbackBuilder_iface
;
2942 static void system_fallback_parse_ranges(const char *str
, DWRITE_UNICODE_RANGE
*ranges
,
2943 unsigned int max_count
, unsigned int *ret
)
2945 unsigned int count
= 0;
2948 while (*str
&& count
< max_count
)
2950 ranges
[count
].first
= ranges
[count
].last
= strtoul(str
, &end
, 16);
2954 ranges
[count
].last
= strtoul(str
, &end
, 16);
2957 if (*str
== ',') str
++;
2964 static void system_fallback_parse_families(WCHAR
*str
, WCHAR
**families
, unsigned int max_count
,
2967 unsigned int count
= 0;
2968 WCHAR
*family
, *ctx
;
2970 family
= wcstok_s(str
, L
",", &ctx
);
2971 while (family
&& count
< max_count
)
2973 while (*family
== ' ') family
++;
2974 families
[count
++] = family
;
2975 family
= wcstok_s(NULL
, L
",", &ctx
);
2981 static INIT_ONCE init_system_fallback_once
= INIT_ONCE_STATIC_INIT
;
2983 /* Particular factory instance used for initialization is not important, it won't be referenced by
2984 created fallback data. */
2985 static BOOL WINAPI
dwrite_system_fallback_initonce(INIT_ONCE
*once
, void *param
, void **context
)
2987 struct dwrite_fontfallback_builder
*builder
;
2988 IDWriteFontFallbackBuilder
*builder_iface
;
2989 unsigned int range_count
, families_count
;
2990 IDWriteFactory7
*factory
= param
;
2991 DWRITE_UNICODE_RANGE ranges
[16];
2992 WCHAR
*families
[4], *str
;
2996 if (FAILED(create_fontfallback_builder_internal(factory
, &builder
))) return FALSE
;
2997 builder_iface
= &builder
->IDWriteFontFallbackBuilder_iface
;
2999 for (i
= 0; i
< ARRAY_SIZE(system_fallback_config
); ++i
)
3001 const struct fallback_description
*entry
= &system_fallback_config
[i
];
3003 system_fallback_parse_ranges(entry
->ranges
, ranges
, ARRAY_SIZE(ranges
), &range_count
);
3005 /* TODO: reuse the buffer */
3006 str
= wcsdup(entry
->families
);
3007 system_fallback_parse_families(str
, families
, ARRAY_SIZE(families
), &families_count
);
3009 if (FAILED(hr
= IDWriteFontFallbackBuilder_AddMapping(builder_iface
, ranges
, range_count
,
3010 (const WCHAR
**)families
, families_count
, NULL
, entry
->locale
, NULL
, 1.0f
)))
3012 WARN("Failed to add mapping, hr %#lx.\n", hr
);
3018 hr
= fallbackbuilder_init_fallback_data(builder
, &system_fallback
);
3019 IDWriteFontFallbackBuilder_Release(builder_iface
);
3024 void release_system_fallback_data(void)
3026 release_fallback_data(&system_fallback
);
3029 HRESULT
create_system_fontfallback(IDWriteFactory7
*factory
, IDWriteFontFallback1
**ret
)
3031 struct dwrite_fontfallback
*fallback
;
3035 if (!InitOnceExecuteOnce(&init_system_fallback_once
, dwrite_system_fallback_initonce
, factory
, NULL
))
3037 WARN("Failed to initialize system fallback data.\n");
3041 if (!(fallback
= calloc(1, sizeof(*fallback
))))
3042 return E_OUTOFMEMORY
;
3044 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &fontfallbackvtbl
;
3045 fallback
->factory
= factory
;
3046 fallback
->data
.count
= ~0u;
3047 IDWriteFactory_GetSystemFontCollection((IDWriteFactory
*)fallback
->factory
, &fallback
->systemcollection
, FALSE
);
3049 *ret
= &fallback
->IDWriteFontFallback1_iface
;