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
;
34 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
35 #define NATIVE_DIGITS_LEN 11
37 struct dwritescript_properties
39 DWRITE_SCRIPT_PROPERTIES props
;
40 UINT32 scripttags
[3]; /* Maximum 2 script tags, 0-terminated. */
44 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
46 /* NOTE: keep this array synced with script ids from scripts.h */
47 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
48 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
49 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
50 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
51 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('a','r','a','b') }, TRUE
},
52 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','n') } },
53 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','v','s','t') } },
54 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('b','a','l','i') } },
55 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','m','u') } },
56 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','t','k') } },
57 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('b','n','g','2'), _OT('b','e','n','g') }, TRUE
},
58 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('b','o','p','o') } },
59 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','h') } },
60 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','i') }, TRUE
},
61 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','u','g','i') } },
62 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('b','u','h','d') } },
63 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','n','s') }, TRUE
},
64 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('c','a','r','i') } },
65 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('c','h','a','m') } },
66 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','h','e','r') }, TRUE
},
67 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','o','p','t') } },
68 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('x','s','u','x') } },
69 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('c','p','r','t') } },
70 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','y','r','l') } },
71 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('d','s','r','t') }, TRUE
},
72 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('d','e','v','2'), _OT('d','e','v','a') }, TRUE
},
73 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('e','g','y','p') } },
74 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','t','h','i') }, TRUE
},
75 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','e','o','r') } },
76 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','l','a','g') } },
77 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','t','h') } },
78 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','e','k') } },
79 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','j','r','2'), _OT('g','u','j','r') }, TRUE
},
80 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','u','r','2'), _OT('g','u','r','u') }, TRUE
},
81 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('h','a','n','i') } },
82 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, { _OT('h','a','n','g') }, TRUE
},
83 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('h','a','n','o') } },
84 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('h','e','b','r') }, TRUE
},
85 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
86 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','i') } },
87 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','h','l','i') } },
88 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','r','t','i') } },
89 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('j','a','v','a') } },
90 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('k','t','h','i') } },
91 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','n','d','2'), _OT('k','n','d','a') }, TRUE
},
92 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
93 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('k','a','l','i') } },
94 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('k','h','a','r') } },
95 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('k','h','m','r') }, TRUE
},
96 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('l','a','o',' ') }, TRUE
},
97 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','a','t','n') } },
98 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','e','p','c') } },
99 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','m','b') } },
100 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','b') } },
101 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','s','u') } },
102 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','c','i') } },
103 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','d','i') } },
104 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','l','m','2'), _OT('m','l','y','m') }, TRUE
},
105 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','d') } },
106 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','t','e','i') } },
107 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','o','n','g') }, TRUE
},
108 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','y','m','r') }, TRUE
},
109 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','u') }, TRUE
},
110 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('n','k','o',' ') }, TRUE
},
111 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, { _OT('o','g','a','m') }, TRUE
},
112 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','l','c','k') } },
113 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('i','t','a','l') } },
114 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('x','p','e','o') }, TRUE
},
115 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','r','b') } },
116 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','k','h') } },
117 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','y','2'), _OT('o','r','y','a') }, TRUE
},
118 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','m','a') }, TRUE
},
119 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','a','g') }, TRUE
},
120 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('p','h','n','x') } },
121 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('r','j','n','g') } },
122 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('r','u','n','r') }, TRUE
},
123 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','m','r') } },
124 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','u','r') } },
125 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','a','w') } },
126 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','h') }, TRUE
},
127 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','u','n','d') } },
128 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('s','y','l','o') } },
129 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('s','y','r','c') }, TRUE
},
130 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','g','l','g') } },
131 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','g','b') } },
132 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','e') }, TRUE
},
133 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('l','a','n','a') } },
134 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','a','v','t') } },
135 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','m','l','2'), _OT('t','a','m','l') }, TRUE
},
136 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','e','l','2'), _OT('t','e','l','u') }, TRUE
},
137 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','h','a','a') }, TRUE
},
138 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','h','a','i') }, TRUE
},
139 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','i','b','t') }, TRUE
},
140 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','f','n','g') }, TRUE
},
141 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('u','g','a','r') } },
142 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('v','a','i',' ') }, TRUE
},
143 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('y','i',' ',' ') }, TRUE
},
144 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','k','m') } },
145 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','r','c') } },
146 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('m','e','r','o') } },
147 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('p','l','r','d') } },
148 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','r','d') } },
149 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','r','a') } },
150 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','k','r') } },
151 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','s','s') } },
152 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','g','h','b') } },
153 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('d','u','p','l') } },
154 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','l','b','a') } },
155 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','a','n') } },
156 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','h','o','j') } },
157 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','d') } },
158 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','a') } },
159 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','h','j') } },
160 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','i') } },
161 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','n','d') } },
162 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('m','o','d','i') } },
163 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','r','o','o') } },
164 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('n','b','a','t') } },
165 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','a','r','b') } },
166 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','e','r','m') } },
167 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','m','n','g') } },
168 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','l','m') } },
169 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','u','c') } },
170 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','l','p') } },
171 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('s','i','d','d') } },
172 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('t','i','r','h') } },
173 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('w','a','r','a') } },
174 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','d','l','m') } },
175 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','h','o','m') } },
176 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','l','u','w') } },
177 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','h','k','s') } },
178 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','a','t','r') } },
179 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','r','c') } },
180 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','u','l','t') } },
181 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','e','w','a') } },
182 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','u','n','g') } },
183 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','g','e') } },
184 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','g','n','w') } },
185 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','n','g') } },
186 { /* Gonm */ { 0x6d6e6f47, 313, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','n','m') } },
187 { /* Nshu */ { 0x7568734e, 499, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('n','s','h','u') } },
188 { /* Soyo */ { 0x6f796f53, 329, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','y','o') } },
189 { /* Zanb */ { 0x626e615a, 339, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('z','a','n','b') } },
190 { /* Dogr */ { 0x72676f44, 328, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('d','o','g','r') } },
191 { /* Gong */ { 0x676e6f47, 312, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','o','n','g') } },
192 { /* Rohg */ { 0x67686f52, 167, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('r','o','h','g') } },
193 { /* Maka */ { 0x616b614d, 366, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','k','a') } },
194 { /* Medf */ { 0x6664654d, 265, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','d','f') } },
195 { /* Sogo */ { 0x6f676f53, 142, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','g','o') } },
196 { /* Sogd */ { 0x64676f53, 141, 8, 0x0020, 1, 1, 0, 0, 0, 1, 1 }, { _OT('s','o','g','d') } },
197 { /* Elym */ { 0x6d796c45, 128, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('e','l','y','m') } },
198 { /* Hmnp */ { 0x706e6d48, 451, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('h','m','n','p') } },
199 { /* Nand */ { 0x646e614e, 311, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('n','a','n','d') } },
200 { /* Wcho */ { 0x6f686357, 283, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('w','c','h','o') } },
204 const char *debugstr_sa_script(UINT16 script
)
206 return script
< Script_LastId
? debugstr_tag(dwritescripts_properties
[script
].props
.isoScriptCode
) : "undefined";
209 /* system font falback configuration */
210 static const WCHAR
*cjk_families
[] = { L
"Meiryo" };
212 static const DWRITE_UNICODE_RANGE cjk_ranges
[] =
214 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
215 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
216 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
219 struct fallback_mapping
{
220 DWRITE_UNICODE_RANGE
*ranges
;
223 UINT32 families_count
;
224 IDWriteFontCollection
*collection
;
229 static const struct fallback_mapping fontfallback_neutral_data
[] = {
230 #define MAPPING_RANGE(ranges, families) \
231 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
232 (WCHAR **)families, ARRAY_SIZE(families) }
234 MAPPING_RANGE(cjk_ranges
, cjk_families
),
239 struct dwrite_fontfallback
241 IDWriteFontFallback1 IDWriteFontFallback1_iface
;
243 IDWriteFactory7
*factory
;
244 IDWriteFontCollection1
*systemcollection
;
245 struct fallback_mapping
*mappings
;
246 UINT32 mappings_count
;
249 struct dwrite_fontfallback_builder
251 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface
;
253 IDWriteFactory7
*factory
;
254 struct fallback_mapping
*mappings
;
259 struct dwrite_numbersubstitution
261 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
264 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
266 BOOL ignore_user_override
;
269 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
271 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
274 static struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
);
276 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback1(IDWriteFontFallback1
*iface
)
278 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback1_iface
);
281 static inline struct dwrite_fontfallback_builder
*impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder
*iface
)
283 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback_builder
, IDWriteFontFallbackBuilder_iface
);
286 static inline UINT16
get_char_script(WCHAR c
)
288 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
289 return script
== Script_Inherited
? Script_Unknown
: script
;
292 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
294 DWRITE_SCRIPT_ANALYSIS sa
;
297 GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
);
298 sa
.script
= get_char_script(c
);
299 sa
.shapes
= (type
& C1_CNTRL
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
300 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
304 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
306 DWRITE_SCRIPT_ANALYSIS sa
;
307 UINT32 pos
, i
, seq_length
;
312 sa
= get_char_sa(*text
);
317 for (i
= 1; i
< length
; i
++)
319 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
321 /* Unknown type is ignored when preceded or followed by another script */
324 sa
.script
= cur_sa
.script
;
327 if (cur_sa
.script
== Script_Unknown
)
328 cur_sa
.script
= sa
.script
;
329 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
330 sa
.script
= cur_sa
.script
;
333 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
334 cur_sa
.script
= sa
.script
;
337 /* this is a length of a sequence to be reported next */
338 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
343 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
344 if (FAILED(hr
)) return hr
;
351 /* one char length case or normal completion call */
352 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
355 struct linebreaking_state
{
356 DWRITE_LINE_BREAKPOINT
*breakpoints
;
360 enum BreakConditionLocation
{
361 BreakConditionBefore
,
365 enum linebreaking_classes
{
411 static BOOL
has_strong_condition(DWRITE_BREAK_CONDITION old_condition
, DWRITE_BREAK_CONDITION new_condition
)
413 if (old_condition
== DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
|| old_condition
== DWRITE_BREAK_CONDITION_MUST_BREAK
)
416 if (old_condition
== DWRITE_BREAK_CONDITION_CAN_BREAK
&& new_condition
!= DWRITE_BREAK_CONDITION_MUST_BREAK
)
422 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
423 set to "can break" and could only be changed once. */
424 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
425 struct linebreaking_state
*state
)
427 if (location
== BreakConditionBefore
) {
428 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionBefore
, condition
))
430 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
432 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
435 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionAfter
, condition
))
437 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
438 if (pos
+ 1 < state
->count
)
439 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
443 BOOL
lb_is_newline_char(WCHAR ch
)
445 short c
= get_table_entry(wine_linebreak_table
, ch
);
446 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
449 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
451 struct linebreaking_state state
;
455 break_class
= heap_calloc(count
, sizeof(*break_class
));
457 return E_OUTOFMEMORY
;
459 state
.breakpoints
= breakpoints
;
462 for (i
= 0; i
< count
; i
++)
464 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
466 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_NEUTRAL
;
467 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_NEUTRAL
;
468 breakpoints
[i
].isWhitespace
= !!iswspace(text
[i
]);
469 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
470 breakpoints
[i
].padding
= 0;
472 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
473 switch (break_class
[i
])
479 break_class
[i
] = b_AL
;
482 break_class
[i
] = b_NS
;
487 /* LB2 - never break at the start */
488 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
489 /* LB3 - always break at the end. */
490 set_break_condition(count
- 1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
492 /* LB4 - LB6 - mandatory breaks. */
493 for (i
= 0; i
< count
; i
++)
495 switch (break_class
[i
])
499 /* LB5 - don't break CR x LF */
500 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
502 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
503 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
509 /* LB4 - LB5 - always break after hard breaks */
510 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
511 /* LB6 - do not break before hard breaks */
512 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
517 /* LB7 - LB8 - explicit breaks and non-breaks */
518 for (i
= 0; i
< count
; i
++)
520 switch (break_class
[i
])
522 /* LB7 - do not break before spaces or zero-width space */
524 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
527 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
529 /* LB8 - break before character after zero-width space, skip spaces in-between */
531 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
533 if (j
< count
-1 && break_class
[j
+1] != b_ZW
)
534 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
536 /* LB8a - do not break after ZWJ */
538 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
543 /* LB9 - LB10 - combining marks */
544 for (i
= 0; i
< count
; i
++)
546 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
550 switch (break_class
[i
-1])
558 break_class
[i
] = b_AL
;
562 break_class
[i
] = break_class
[i
-1];
563 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
567 else break_class
[i
] = b_AL
;
571 for (i
= 0; i
< count
; i
++)
573 switch (break_class
[i
])
575 /* LB11 - don't break before and after word joiner */
577 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
578 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
580 /* LB12 - don't break after glue */
582 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
586 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
587 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
596 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
598 /* LB14 - do not break after OP, even after spaces */
601 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
603 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
605 /* LB15 - do not break within QU-OP, even with intervening spaces */
608 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
610 if (j
< count
- 1 && break_class
[j
+1] == b_OP
)
611 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
616 while(j
> 0 && break_class
[j
] == b_SP
)
618 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
619 for (j
++; j
<= i
; j
++)
620 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
622 /* LB17 - do not break within B2, even with intervening spaces */
625 while (j
< count
&& break_class
[j
+1] == b_SP
)
627 if (j
< count
- 1 && break_class
[j
+1] == b_B2
)
628 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
633 for (i
= 0; i
< count
; i
++)
635 switch(break_class
[i
])
637 /* LB18 - break is allowed after space */
639 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
641 /* LB19 - don't break before or after quotation mark */
643 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
644 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
648 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
649 if (i
< count
- 1 && break_class
[i
+1] != b_QU
)
650 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
656 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
659 if (i
< count
- 1 && break_class
[i
+1] != b_CB
)
660 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
666 switch (break_class
[i
+1])
670 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
673 if (i
> 0 && break_class
[i
-1] == b_SY
)
674 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
678 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
684 /* LB23 - do not break between digits and letters */
685 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
686 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
687 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
688 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
689 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
691 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
692 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
693 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
694 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
695 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
696 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
697 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
698 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
700 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
701 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
702 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
703 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
704 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
705 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
706 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
707 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
708 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
709 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
712 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
713 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
714 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
715 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
716 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
717 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
718 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
719 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
720 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
721 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
722 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
723 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
724 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
725 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
726 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
729 if (break_class
[i
] == b_JL
)
731 switch (break_class
[i
+1])
737 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
740 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
741 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
742 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
743 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
744 break_class
[i
+1] == b_JT
)
745 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
748 switch (break_class
[i
])
755 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
756 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
758 if (break_class
[i
] == b_PR
)
760 switch (break_class
[i
+1])
767 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
772 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
773 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
774 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
775 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
776 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
779 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
780 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
781 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
784 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
785 break_class
[i
+1] == b_OP
)
786 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
787 if (break_class
[i
] == b_CP
&&
788 (break_class
[i
+1] == b_AL
|| break_class
[i
+1] == b_HL
|| break_class
[i
+1] == b_NU
))
789 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
791 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
792 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
796 while (j
> 0 && break_class
[--j
] == b_RI
)
800 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
803 /* LB30b - do not break between an emoji base and an emoji modifier */
804 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
805 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
809 /* LB31 - allow breaks everywhere else. */
810 for (i
= 0; i
< count
; i
++)
812 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
813 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
816 heap_free(break_class
);
820 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
822 TRACE("%s, %p.\n", debugstr_guid(riid
), obj
);
824 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
825 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
826 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
827 IsEqualIID(riid
, &IID_IUnknown
))
833 WARN("%s not implemented.\n", debugstr_guid(riid
));
836 return E_NOINTERFACE
;
839 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
844 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
849 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
850 data after a first request. */
851 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
859 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
860 if (FAILED(hr
)) return hr
;
865 *buff
= heap_alloc(length
*sizeof(WCHAR
));
867 return E_OUTOFMEMORY
;
868 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
871 while (read
< length
&& *text
) {
874 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
879 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
889 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
890 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
896 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
901 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
905 hr
= analyze_script(text
, position
, length
, sink
);
911 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
912 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
914 UINT8
*levels
= NULL
, *explicit = NULL
;
915 UINT8 baselevel
, level
, explicit_level
;
916 UINT32 pos
, i
, seq_length
;
921 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
926 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
930 levels
= heap_calloc(length
, sizeof(*levels
));
931 explicit = heap_calloc(length
, sizeof(*explicit));
933 if (!levels
|| !explicit) {
938 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
939 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
944 explicit_level
= explicit[0];
948 for (i
= 1; i
< length
; i
++) {
949 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
952 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
959 explicit_level
= explicit[i
];
962 /* one char length case or normal completion call */
963 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
973 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
974 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
979 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
983 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
984 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
986 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
992 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
997 /* get some, check for length */
1000 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
1001 if (FAILED(hr
)) return hr
;
1006 buff
= heap_calloc(length
, sizeof(*buff
));
1008 return E_OUTOFMEMORY
;
1009 memcpy(buff
, text
, len
*sizeof(WCHAR
));
1012 while (read
< length
&& text
) {
1015 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
1018 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
1025 breakpoints
= heap_calloc(length
, sizeof(*breakpoints
));
1031 hr
= analyze_linebreaks(text
, length
, breakpoints
);
1035 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
1038 heap_free(breakpoints
);
1044 static UINT32
get_opentype_language(const WCHAR
*locale
)
1046 UINT32 language
= DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1050 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, ARRAY_SIZE(tag
)))
1051 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
1057 static void get_number_substitutes(IDWriteNumberSubstitution
*substitution
, BOOL is_rtl
, WCHAR
*digits
)
1059 struct dwrite_numbersubstitution
*numbersubst
= unsafe_impl_from_IDWriteNumberSubstitution(substitution
);
1060 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1069 lctype
= numbersubst
->ignore_user_override
? LOCALE_NOUSEROVERRIDE
: 0;
1071 if (numbersubst
->method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE
) {
1074 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1075 if (GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_IDIGITSUBSTITUTION
| LOCALE_RETURN_NUMBER
, (WCHAR
*)&value
, 2)) {
1079 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
;
1082 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
;
1087 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value
, debugstr_w(numbersubst
->locale
));
1091 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst
->locale
));
1094 method
= numbersubst
->method
;
1098 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
:
1099 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1101 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1102 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
:
1103 if (GetLocaleInfoEx(numbersubst
->locale
, LOCALE_SISO639LANGNAME
, isolang
, ARRAY_SIZE(isolang
)))
1105 static const WCHAR arabicW
[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1107 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1108 if (!wcscmp(L
"ar", isolang
))
1110 wcscpy(digits
, arabicW
);
1114 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1120 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !*digits
) {
1121 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst
->locale
), method
);
1122 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1125 if ((method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
&& !is_rtl
) || method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
)
1129 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES
const **features
,
1130 UINT32
const *feature_range_lengths
, UINT32 feature_ranges
)
1134 if (!TRACE_ON(dwrite
) || !features
)
1137 for (i
= 0, start
= 0; i
< feature_ranges
; start
+= feature_range_lengths
[i
++]) {
1138 TRACE("feature range [%u,%u)\n", start
, start
+ feature_range_lengths
[i
]);
1139 for (j
= 0; j
< features
[i
]->featureCount
; j
++)
1140 TRACE("feature %s, parameter %u\n", debugstr_tag(features
[i
]->features
[j
].nameTag
),
1141 features
[i
]->features
[j
].parameter
);
1145 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
1146 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
1147 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
1148 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1149 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
1150 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
*glyphs
,
1151 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
1153 const struct dwritescript_properties
*scriptprops
;
1154 struct scriptshaping_context context
= { 0 };
1155 struct dwrite_fontface
*font_obj
;
1156 WCHAR digits
[NATIVE_DIGITS_LEN
];
1157 unsigned int glyph_count
;
1160 TRACE("%s:%u, %p, %d, %d, %s, %s, %p, %p, %p, %u, %u, %p, %p, %p, %p, %p.\n", debugstr_wn(text
, length
),
1161 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1162 features
, feature_range_lengths
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyphs
,
1163 glyph_props
, actual_glyph_count
);
1165 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1167 get_number_substitutes(substitution
, is_rtl
, digits
);
1168 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1169 glyph_count
= max(max_glyph_count
, length
);
1171 context
.cache
= fontface_get_shaping_cache(font_obj
);
1172 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1173 context
.text
= text
;
1174 context
.length
= length
;
1175 context
.is_rtl
= is_rtl
;
1176 context
.is_sideways
= is_sideways
;
1177 context
.u
.subst
.glyphs
= heap_calloc(glyph_count
, sizeof(*glyphs
));
1178 context
.u
.subst
.glyph_props
= heap_calloc(glyph_count
, sizeof(*glyph_props
));
1179 context
.u
.subst
.text_props
= text_props
;
1180 context
.u
.subst
.clustermap
= clustermap
;
1181 context
.u
.subst
.max_glyph_count
= max_glyph_count
;
1182 context
.u
.subst
.capacity
= glyph_count
;
1183 context
.u
.subst
.digits
= digits
;
1184 context
.language_tag
= get_opentype_language(locale
);
1185 context
.user_features
.features
= features
;
1186 context
.user_features
.range_lengths
= feature_range_lengths
;
1187 context
.user_features
.range_count
= feature_ranges
;
1188 context
.glyph_infos
= heap_calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1189 context
.table
= &context
.cache
->gsub
;
1191 *actual_glyph_count
= 0;
1193 if (!context
.u
.subst
.glyphs
|| !context
.u
.subst
.glyph_props
|| !context
.glyph_infos
)
1199 scriptprops
= &dwritescripts_properties
[context
.script
];
1200 hr
= shape_get_glyphs(&context
, scriptprops
->scripttags
);
1203 *actual_glyph_count
= context
.glyph_count
;
1204 memcpy(glyphs
, context
.u
.subst
.glyphs
, context
.glyph_count
* sizeof(*glyphs
));
1205 memcpy(glyph_props
, context
.u
.subst
.glyph_props
, context
.glyph_count
* sizeof(*glyph_props
));
1209 heap_free(context
.u
.subst
.glyph_props
);
1210 heap_free(context
.u
.subst
.glyphs
);
1211 heap_free(context
.glyph_infos
);
1216 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1217 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1218 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1219 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, BOOL is_sideways
, BOOL is_rtl
,
1220 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1221 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1223 const struct dwritescript_properties
*scriptprops
;
1224 struct scriptshaping_context context
;
1225 struct dwrite_fontface
*font_obj
;
1229 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
),
1230 clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1231 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_lengths
,
1232 feature_ranges
, advances
, offsets
);
1234 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1236 if (glyph_count
== 0)
1239 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1241 for (i
= 0; i
< glyph_count
; ++i
)
1243 if (glyph_props
[i
].isZeroWidthSpace
)
1246 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, DWRITE_MEASURING_MODE_NATURAL
, emSize
, 1.0f
,
1247 NULL
, glyphs
[i
], is_sideways
);
1248 offsets
[i
].advanceOffset
= 0.0f
;
1249 offsets
[i
].ascenderOffset
= 0.0f
;
1252 context
.cache
= fontface_get_shaping_cache(font_obj
);
1253 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1254 context
.text
= text
;
1255 context
.length
= text_len
;
1256 context
.is_rtl
= is_rtl
;
1257 context
.is_sideways
= is_sideways
;
1258 context
.u
.pos
.glyphs
= glyphs
;
1259 context
.u
.pos
.glyph_props
= glyph_props
;
1260 context
.u
.pos
.text_props
= text_props
;
1261 context
.u
.pos
.clustermap
= clustermap
;
1262 context
.glyph_count
= glyph_count
;
1263 context
.emsize
= emSize
;
1264 context
.measuring_mode
= DWRITE_MEASURING_MODE_NATURAL
;
1265 context
.advances
= advances
;
1266 context
.offsets
= offsets
;
1267 context
.language_tag
= get_opentype_language(locale
);
1268 context
.user_features
.features
= features
;
1269 context
.user_features
.range_lengths
= feature_range_lengths
;
1270 context
.user_features
.range_count
= feature_ranges
;
1271 context
.glyph_infos
= heap_calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1272 context
.table
= &context
.cache
->gpos
;
1274 if (!context
.glyph_infos
)
1280 scriptprops
= &dwritescripts_properties
[context
.script
];
1281 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1284 heap_free(context
.glyph_infos
);
1289 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1290 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1291 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1292 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, float ppdip
,
1293 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1294 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1295 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1297 const struct dwritescript_properties
*scriptprops
;
1298 struct scriptshaping_context context
;
1299 DWRITE_MEASURING_MODE measuring_mode
;
1300 struct dwrite_fontface
*font_obj
;
1304 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1305 debugstr_wn(text
, text_len
), clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
,
1306 emSize
, ppdip
, transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
),
1307 debugstr_w(locale
), features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1309 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1311 if (glyph_count
== 0)
1314 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1316 measuring_mode
= use_gdi_natural
? DWRITE_MEASURING_MODE_GDI_NATURAL
: DWRITE_MEASURING_MODE_GDI_CLASSIC
;
1318 for (i
= 0; i
< glyph_count
; ++i
)
1320 if (glyph_props
[i
].isZeroWidthSpace
)
1323 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, measuring_mode
, emSize
, ppdip
,
1324 transform
, glyphs
[i
], is_sideways
);
1325 offsets
[i
].advanceOffset
= 0.0f
;
1326 offsets
[i
].ascenderOffset
= 0.0f
;
1329 context
.cache
= fontface_get_shaping_cache(font_obj
);
1330 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1331 context
.text
= text
;
1332 context
.length
= text_len
;
1333 context
.is_rtl
= is_rtl
;
1334 context
.is_sideways
= is_sideways
;
1335 context
.u
.pos
.glyphs
= glyphs
;
1336 context
.u
.pos
.glyph_props
= glyph_props
;
1337 context
.u
.pos
.text_props
= text_props
;
1338 context
.u
.pos
.clustermap
= clustermap
;
1339 context
.glyph_count
= glyph_count
;
1340 context
.emsize
= emSize
* ppdip
;
1341 context
.measuring_mode
= measuring_mode
;
1342 context
.advances
= advances
;
1343 context
.offsets
= offsets
;
1344 context
.language_tag
= get_opentype_language(locale
);
1345 context
.user_features
.features
= features
;
1346 context
.user_features
.range_lengths
= feature_range_lengths
;
1347 context
.user_features
.range_count
= feature_ranges
;
1348 context
.glyph_infos
= heap_calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1349 context
.table
= &context
.cache
->gpos
;
1351 if (!context
.glyph_infos
)
1357 scriptprops
= &dwritescripts_properties
[context
.script
];
1358 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1361 heap_free(context
.glyph_infos
);
1366 static HRESULT
apply_cluster_spacing(float leading_spacing
, float trailing_spacing
, float min_advance_width
,
1367 unsigned int start
, unsigned int end
, float const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1368 DWRITE_SHAPING_GLYPH_PROPERTIES
const *glyph_props
, float *modified_advances
,
1369 DWRITE_GLYPH_OFFSET
*modified_offsets
)
1371 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1372 unsigned int first_spacing
, last_spacing
, i
;
1373 float advance
, origin
= 0.0f
, *deltas
;
1374 BOOL is_spacing_cluster
= FALSE
;
1376 if (modified_advances
!= advances
)
1377 memcpy(&modified_advances
[start
], &advances
[start
], (end
- start
+ 1) * sizeof(*advances
));
1378 if (modified_offsets
!= offsets
)
1379 memcpy(&modified_offsets
[start
], &offsets
[start
], (end
- start
+ 1) * sizeof(*offsets
));
1381 for (first_spacing
= start
; first_spacing
<= end
; ++first_spacing
)
1383 if ((is_spacing_cluster
= !glyph_props
[first_spacing
].isZeroWidthSpace
))
1387 /* Nothing to adjust if there is no spacing glyphs. */
1388 if (!is_spacing_cluster
)
1391 for (last_spacing
= end
; last_spacing
>= start
; --last_spacing
)
1393 if (!glyph_props
[last_spacing
].isZeroWidthSpace
)
1397 deltas
= heap_calloc(end
- start
+ 1, sizeof(*deltas
));
1399 return E_OUTOFMEMORY
;
1401 /* Cluster advance, note that properties are ignored. */
1402 origin
= offsets
[start
].advanceOffset
;
1403 for (i
= start
, advance
= 0.0f
; i
<= end
; ++i
)
1405 float cur
= advance
+ offsets
[i
].advanceOffset
;
1407 deltas
[i
- start
] = cur
- origin
;
1409 advance
+= advances
[i
];
1413 /* Negative spacing. */
1414 if (leading_spacing
< 0.0f
)
1416 advance
+= leading_spacing
;
1417 modified_advances
[first_spacing
] += leading_spacing
;
1418 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1421 if (trailing_spacing
< 0.0f
)
1423 advance
+= trailing_spacing
;
1424 modified_advances
[last_spacing
] += trailing_spacing
;
1427 /* Minimal advance. */
1428 advance
= min_advance_width
- advance
;
1429 if (advance
> 0.0f
) {
1430 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1431 float half
= advance
/ 2.0f
;
1435 modified_advances
[first_spacing
] += half
;
1436 modified_advances
[last_spacing
] += half
;
1437 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1439 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1441 modified_advances
[first_spacing
] += half
;
1442 modified_advances
[last_spacing
] += half
;
1443 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1445 else if (leading_spacing
< 0.0f
)
1447 modified_advances
[first_spacing
] += advance
;
1448 modified_offsets
[first_spacing
].advanceOffset
+= advance
;
1451 modified_advances
[last_spacing
] += advance
;
1454 /* Positive spacing. */
1455 if (leading_spacing
> 0.0f
)
1457 modified_advances
[first_spacing
] += leading_spacing
;
1458 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1461 if (trailing_spacing
> 0.0f
)
1462 modified_advances
[last_spacing
] += trailing_spacing
;
1464 /* Update offsets to preserve original relative positions within cluster. */
1465 for (i
= first_spacing
; i
> start
; --i
)
1467 unsigned int cur
= i
- 1;
1468 modified_offsets
[cur
].advanceOffset
= modified_advances
[cur
] + modified_offsets
[i
].advanceOffset
-
1472 for (i
= first_spacing
+ 1; i
<= end
; ++i
)
1474 modified_offsets
[i
].advanceOffset
= deltas
[i
- start
] + modified_offsets
[i
- 1].advanceOffset
-
1475 modified_advances
[i
- 1];
1483 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1485 UINT16 g
= clustermap
[start
];
1488 while (start
< text_len
&& clustermap
[++start
] == g
)
1493 /* Applies spacing adjustments to clusters.
1495 Adjustments are applied in the following order:
1497 1. Negative adjustments
1499 Leading and trailing spacing could be negative, at this step
1500 only negative ones are actually applied. Leading spacing is only
1501 applied to leading glyph, trailing - to trailing glyph.
1503 2. Minimum advance width
1505 Advances could only be reduced at this point or unchanged. In any
1506 case it's checked if cluster advance width is less than minimum width.
1507 If it's the case advance width is incremented up to minimum value.
1509 Important part is the direction in which this increment is applied;
1510 it depends on direction from which total cluster advance was trimmed
1511 at step 1. So it could be incremented from leading, trailing, or both
1512 sides. When applied to both sides, each side gets half of difference
1513 that brings advance to minimum width.
1515 3. Positive adjustments
1517 After minimum width rule was applied, positive spacing is applied in the same
1518 way as negative one on step 1.
1520 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1521 keeps its position in coordinate system where initial advance width is counted
1526 It's known that isZeroWidthSpace property keeps initial advance from changing.
1529 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1530 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1531 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1532 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1536 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1537 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1539 if (min_advance_width
< 0.0f
) {
1540 if (modified_advances
!= advances
)
1541 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1542 return E_INVALIDARG
;
1545 for (i
= 0; i
< len
;)
1547 unsigned int length
= get_cluster_length(clustermap
, i
, len
);
1548 unsigned int start
, end
;
1550 start
= clustermap
[i
];
1551 end
= i
+ length
< len
? clustermap
[i
+ length
] : glyph_count
;
1553 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
, start
, end
- 1, advances
,
1554 offsets
, props
, modified_advances
, modified_offsets
);
1562 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*fontface
,
1563 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1564 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1566 struct dwrite_fontface
*font_obj
;
1567 const DWRITE_FONT_METRICS1
*metrics
;
1569 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1570 baseline_coord
, exists
);
1573 *baseline_coord
= 0;
1575 if (baseline
== DWRITE_BASELINE_DEFAULT
)
1576 baseline
= vertical
? DWRITE_BASELINE_CENTRAL
: DWRITE_BASELINE_ROMAN
;
1578 if ((unsigned int)baseline
> DWRITE_BASELINE_MAXIMUM
)
1579 return E_INVALIDARG
;
1581 /* TODO: fetch BASE table data if available. */
1583 if (!*exists
&& is_simulation_allowed
)
1585 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1586 metrics
= &font_obj
->metrics
;
1590 case DWRITE_BASELINE_ROMAN
:
1591 *baseline_coord
= vertical
? metrics
->descent
: 0;
1593 case DWRITE_BASELINE_CENTRAL
:
1594 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1595 -(metrics
->ascent
- metrics
->descent
) / 2;
1597 case DWRITE_BASELINE_MATH
:
1598 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1599 -(metrics
->ascent
+ metrics
->descent
) / 2;
1601 case DWRITE_BASELINE_HANGING
:
1602 /* FIXME: this one isn't accurate, but close. */
1603 *baseline_coord
= vertical
? metrics
->capHeight
* 6 / 7 + metrics
->descent
: metrics
->capHeight
* 6 / 7;
1605 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM
:
1606 case DWRITE_BASELINE_MINIMUM
:
1607 *baseline_coord
= vertical
? 0 : metrics
->descent
;
1609 case DWRITE_BASELINE_IDEOGRAPHIC_TOP
:
1610 case DWRITE_BASELINE_MAXIMUM
:
1611 *baseline_coord
= vertical
? metrics
->ascent
+ metrics
->descent
: -metrics
->ascent
;
1621 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1622 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1624 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1628 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1629 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1631 TRACE("%d, %d, %p.\n", angle
, is_sideways
, transform
);
1633 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1636 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1637 DWRITE_SCRIPT_PROPERTIES
*props
)
1639 TRACE("%u, %p.\n", sa
.script
, props
);
1641 if (sa
.script
> Script_LastId
)
1642 return E_INVALIDARG
;
1644 *props
= dwritescripts_properties
[sa
.script
].props
;
1648 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1650 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
) ||
1651 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1652 c
== 0x200e || c
== 0x200f || (c
>= 0x202a && c
<= 0x202e))
1655 UINT16 script
= get_char_script(c
);
1656 return !dwritescripts_properties
[script
].is_complex
;
1660 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1661 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1666 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1672 return E_INVALIDARG
;
1679 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1680 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1681 if (is_char_from_simple_script(text
[i
])) {
1692 if (*is_simple
&& indices
) {
1693 UINT32
*codepoints
= heap_calloc(*len_read
, sizeof(*codepoints
));
1695 return E_OUTOFMEMORY
;
1697 for (i
= 0; i
< *len_read
; i
++)
1698 codepoints
[i
] = text
[i
];
1700 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1701 heap_free(codepoints
);
1707 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1708 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1709 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1711 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1712 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1716 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1717 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1718 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1720 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1725 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1726 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1727 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1728 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1729 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1730 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1732 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
,
1733 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1734 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1738 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1739 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1741 static const DWRITE_MATRIX transforms
[] = {
1742 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1743 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1744 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1745 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1748 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle
, is_sideways
, originX
, originY
, m
);
1750 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1751 memset(m
, 0, sizeof(*m
));
1752 return E_INVALIDARG
;
1755 /* for sideways case simply rotate 90 degrees more */
1758 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1759 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1761 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1762 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1764 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1765 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1767 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1768 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1775 *m
= transforms
[angle
];
1777 /* shift components represent transform necessary to get from original point to
1778 rotated one in new coordinate system */
1779 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1780 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1781 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1787 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1788 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1789 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1791 struct scriptshaping_context context
= { 0 };
1792 const struct dwritescript_properties
*props
;
1793 struct dwrite_fontface
*font_obj
;
1795 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
,
1796 actual_tagcount
, tags
);
1798 if (sa
.script
> Script_LastId
)
1799 return E_INVALIDARG
;
1801 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1803 context
.cache
= fontface_get_shaping_cache(font_obj
);
1804 context
.language_tag
= get_opentype_language(locale
);
1805 props
= &dwritescripts_properties
[sa
.script
];
1807 return shape_get_typographic_features(&context
, props
->scripttags
, max_tagcount
, actual_tagcount
, tags
);
1810 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1811 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
, DWRITE_FONT_FEATURE_TAG feature
,
1812 UINT32 glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
1814 struct scriptshaping_context context
= { 0 };
1815 const struct dwritescript_properties
*props
;
1816 struct dwrite_fontface
*font_obj
;
1819 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), debugstr_tag(feature
),
1820 glyph_count
, glyphs
, feature_applies
);
1822 if (sa
.script
> Script_LastId
)
1823 return E_INVALIDARG
;
1825 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1827 context
.cache
= fontface_get_shaping_cache(font_obj
);
1828 context
.language_tag
= get_opentype_language(locale
);
1829 if (!(context
.glyph_infos
= heap_calloc(glyph_count
, sizeof(*context
.glyph_infos
))))
1830 return E_OUTOFMEMORY
;
1832 props
= &dwritescripts_properties
[sa
.script
];
1834 hr
= shape_check_typographic_feature(&context
, props
->scripttags
, feature
, glyph_count
, glyphs
, feature_applies
);
1836 heap_free(context
.glyph_infos
);
1841 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl
=
1843 dwritetextanalyzer_QueryInterface
,
1844 dwritetextanalyzer_AddRef
,
1845 dwritetextanalyzer_Release
,
1846 dwritetextanalyzer_AnalyzeScript
,
1847 dwritetextanalyzer_AnalyzeBidi
,
1848 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1849 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1850 dwritetextanalyzer_GetGlyphs
,
1851 dwritetextanalyzer_GetGlyphPlacements
,
1852 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1853 dwritetextanalyzer1_ApplyCharacterSpacing
,
1854 dwritetextanalyzer1_GetBaseline
,
1855 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1856 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1857 dwritetextanalyzer1_GetScriptProperties
,
1858 dwritetextanalyzer1_GetTextComplexity
,
1859 dwritetextanalyzer1_GetJustificationOpportunities
,
1860 dwritetextanalyzer1_JustifyGlyphAdvances
,
1861 dwritetextanalyzer1_GetJustifiedGlyphs
,
1862 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1863 dwritetextanalyzer2_GetTypographicFeatures
,
1864 dwritetextanalyzer2_CheckTypographicFeature
1867 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1869 IDWriteTextAnalyzer2
*get_text_analyzer(void)
1871 return &textanalyzer
;
1874 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1876 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1878 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1879 IsEqualIID(riid
, &IID_IUnknown
))
1882 IDWriteNumberSubstitution_AddRef(iface
);
1886 WARN("%s not implemented.\n", debugstr_guid(riid
));
1890 return E_NOINTERFACE
;
1893 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1895 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
1896 ULONG refcount
= InterlockedIncrement(&object
->refcount
);
1898 TRACE("%p, refcount %d.\n", iface
, refcount
);
1903 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1905 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
1906 ULONG refcount
= InterlockedDecrement(&object
->refcount
);
1908 TRACE("%p, refcount %d.\n", iface
, refcount
);
1912 heap_free(object
->locale
);
1919 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
=
1921 dwritenumbersubstitution_QueryInterface
,
1922 dwritenumbersubstitution_AddRef
,
1923 dwritenumbersubstitution_Release
1926 struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
1928 if (!iface
|| iface
->lpVtbl
!= &numbersubstitutionvtbl
)
1930 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
1933 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1934 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1936 struct dwrite_numbersubstitution
*substitution
;
1940 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1941 return E_INVALIDARG
;
1943 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1944 return E_INVALIDARG
;
1946 substitution
= heap_alloc(sizeof(*substitution
));
1948 return E_OUTOFMEMORY
;
1950 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1951 substitution
->refcount
= 1;
1952 substitution
->ignore_user_override
= ignore_user_override
;
1953 substitution
->method
= method
;
1954 substitution
->locale
= heap_strdupW(locale
);
1955 if (locale
&& !substitution
->locale
) {
1956 heap_free(substitution
);
1957 return E_OUTOFMEMORY
;
1960 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1964 /* IDWriteFontFallback */
1965 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback1
*iface
, REFIID riid
, void **obj
)
1967 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1969 if (IsEqualIID(riid
, &IID_IDWriteFontFallback1
) ||
1970 IsEqualIID(riid
, &IID_IDWriteFontFallback
) ||
1971 IsEqualIID(riid
, &IID_IUnknown
))
1974 IDWriteFontFallback1_AddRef(iface
);
1978 WARN("%s not implemented.\n", debugstr_guid(riid
));
1981 return E_NOINTERFACE
;
1984 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback1
*iface
)
1986 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
1988 TRACE("%p.\n", iface
);
1990 return IDWriteFactory7_AddRef(fallback
->factory
);
1993 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback1
*iface
)
1995 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
1997 TRACE("%p.\n", fallback
);
1999 return IDWriteFactory7_Release(fallback
->factory
);
2002 static int __cdecl
compare_mapping_range(const void *a
, const void *b
)
2004 UINT32 ch
= *(UINT32
*)a
;
2005 DWRITE_UNICODE_RANGE
*range
= (DWRITE_UNICODE_RANGE
*)b
;
2007 if (ch
> range
->last
)
2009 else if (ch
< range
->first
)
2015 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
2019 for (i
= 0; i
< fallback
->mappings_count
; i
++) {
2020 struct fallback_mapping
*mapping
= &fallback
->mappings
[i
];
2022 if (bsearch(&ch
, mapping
->ranges
, mapping
->ranges_count
, sizeof(*mapping
->ranges
),
2023 compare_mapping_range
) != NULL
)
2030 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
2031 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
2033 IDWriteFontFamily
*family
;
2034 BOOL exists
= FALSE
;
2040 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
2047 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
2051 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
2052 IDWriteFontFamily_Release(family
);
2056 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
2061 for (i
= 0; i
< length
; i
++) {
2062 UINT16 script
= get_char_script(text
[i
]);
2065 if (script
== Script_Unknown
|| script
== Script_Common
) {
2070 /* stop on first unsupported character */
2072 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
2073 if (hr
== S_OK
&& exists
)
2082 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
2083 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2084 IDWriteFont
**mapped_font
)
2086 const struct fallback_mapping
*mapping
;
2090 *mapped_font
= NULL
;
2092 mapping
= find_fallback_mapping(fallback
, text
[0]);
2094 WARN("No mapping range for %#x.\n", text
[0]);
2098 /* Now let's see what fallback can handle. Pick first font that could be created. */
2099 for (i
= 0; i
< mapping
->families_count
; i
++) {
2100 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->families
[i
],
2101 weight
, style
, stretch
, mapped_font
);
2103 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping
->families
[i
]));
2108 if (!*mapped_font
) {
2109 WARN("Failed to create fallback font.\n");
2113 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
2115 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping
->families
[i
]), hr
);
2117 if (!*mapped_length
) {
2118 IDWriteFont_Release(*mapped_font
);
2119 *mapped_font
= NULL
;
2122 return *mapped_length
? S_OK
: E_FAIL
;
2125 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2126 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2127 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2128 IDWriteFont
**ret_font
, FLOAT
*scale
)
2130 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2135 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2136 debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2143 return E_INVALIDARG
;
2148 if (!basecollection
)
2149 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
2151 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
2155 if (basefamily
&& *basefamily
) {
2156 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
2160 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
2165 if (!*mapped_length
) {
2166 IDWriteFont
*mapped_font
;
2168 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
2170 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2172 *mapped_length
= length
;
2178 IDWriteFont_Release(*ret_font
);
2179 *ret_font
= mapped_font
;
2188 static HRESULT WINAPI
fontfallback1_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2189 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2190 DWRITE_FONT_AXIS_VALUE
const *axis_values
, UINT32 values_count
, UINT32
*mapped_length
, FLOAT
*scale
,
2191 IDWriteFontFace5
**ret_fontface
)
2193 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2194 debugstr_w(basefamily
), axis_values
, values_count
, mapped_length
, scale
, ret_fontface
);
2199 static const IDWriteFontFallback1Vtbl fontfallbackvtbl
=
2201 fontfallback_QueryInterface
,
2202 fontfallback_AddRef
,
2203 fontfallback_Release
,
2204 fontfallback_MapCharacters
,
2205 fontfallback1_MapCharacters
,
2208 HRESULT
create_system_fontfallback(IDWriteFactory7
*factory
, IDWriteFontFallback1
**ret
)
2210 struct dwrite_fontfallback
*fallback
;
2214 fallback
= heap_alloc(sizeof(*fallback
));
2216 return E_OUTOFMEMORY
;
2218 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &fontfallbackvtbl
;
2219 fallback
->factory
= factory
;
2220 fallback
->mappings
= (struct fallback_mapping
*)fontfallback_neutral_data
;
2221 fallback
->mappings_count
= ARRAY_SIZE(fontfallback_neutral_data
);
2222 IDWriteFactory5_GetSystemFontCollection((IDWriteFactory5
*)fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
2224 *ret
= &fallback
->IDWriteFontFallback1_iface
;
2229 void release_system_fontfallback(IDWriteFontFallback1
*iface
)
2231 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2232 IDWriteFontCollection1_Release(fallback
->systemcollection
);
2233 heap_free(fallback
);
2236 static ULONG WINAPI
customfontfallback_AddRef(IDWriteFontFallback1
*iface
)
2238 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2239 ULONG refcount
= InterlockedIncrement(&fallback
->refcount
);
2241 TRACE("%p, refcount %u.\n", iface
, refcount
);
2246 static ULONG WINAPI
customfontfallback_Release(IDWriteFontFallback1
*iface
)
2248 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2249 ULONG refcount
= InterlockedDecrement(&fallback
->refcount
);
2251 TRACE("%p, refcount %u.\n", iface
, refcount
);
2255 IDWriteFactory7_Release(fallback
->factory
);
2256 heap_free(fallback
);
2262 static HRESULT WINAPI
customfontfallback_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2263 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2264 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2265 IDWriteFont
**ret_font
, FLOAT
*scale
)
2267 FIXME("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface
, source
, position
, length
,
2268 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2273 static HRESULT WINAPI
customfontfallback1_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2274 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2275 DWRITE_FONT_AXIS_VALUE
const *axis_values
, UINT32 values_count
, UINT32
*mapped_length
, FLOAT
*scale
,
2276 IDWriteFontFace5
**ret_fontface
)
2278 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2279 debugstr_w(basefamily
), axis_values
, values_count
, mapped_length
, scale
, ret_fontface
);
2284 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl
=
2286 fontfallback_QueryInterface
,
2287 customfontfallback_AddRef
,
2288 customfontfallback_Release
,
2289 customfontfallback_MapCharacters
,
2290 customfontfallback1_MapCharacters
,
2293 static HRESULT WINAPI
fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder
*iface
, REFIID riid
, void **obj
)
2295 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
2297 if (IsEqualIID(riid
, &IID_IDWriteFontFallbackBuilder
) || IsEqualIID(riid
, &IID_IUnknown
)) {
2299 IDWriteFontFallbackBuilder_AddRef(iface
);
2303 WARN("%s not implemented.\n", debugstr_guid(riid
));
2306 return E_NOINTERFACE
;
2309 static ULONG WINAPI
fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder
*iface
)
2311 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2312 ULONG refcount
= InterlockedIncrement(&fallbackbuilder
->refcount
);
2314 TRACE("%p, refcount %d.\n", iface
, refcount
);
2319 static ULONG WINAPI
fontfallbackbuilder_Release(IDWriteFontFallbackBuilder
*iface
)
2321 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2322 ULONG refcount
= InterlockedDecrement(&fallbackbuilder
->refcount
);
2325 TRACE("%p, refcount %d.\n", iface
, refcount
);
2329 for (i
= 0; i
< fallbackbuilder
->count
; ++i
)
2331 struct fallback_mapping
*mapping
= &fallbackbuilder
->mappings
[i
];
2334 for (j
= 0; j
< mapping
->families_count
; j
++)
2335 heap_free(mapping
->families
[j
]);
2336 heap_free(mapping
->families
);
2338 if (mapping
->collection
)
2339 IDWriteFontCollection_Release(mapping
->collection
);
2340 heap_free(mapping
->ranges
);
2341 heap_free(mapping
->locale
);
2344 IDWriteFactory7_Release(fallbackbuilder
->factory
);
2345 heap_free(fallbackbuilder
->mappings
);
2346 heap_free(fallbackbuilder
);
2352 static HRESULT WINAPI
fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder
*iface
,
2353 const DWRITE_UNICODE_RANGE
*ranges
, UINT32 ranges_count
, WCHAR
const **target_families
, UINT32 families_count
,
2354 IDWriteFontCollection
*collection
, WCHAR
const *locale
, WCHAR
const *base_family
, FLOAT scale
)
2356 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2357 struct fallback_mapping
*mapping
;
2360 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface
, ranges
, ranges_count
, target_families
, families_count
,
2361 collection
, debugstr_w(locale
), debugstr_w(base_family
), scale
);
2363 if (!ranges
|| ranges_count
== 0 || !target_families
|| families_count
== 0 || scale
< 0.0f
)
2364 return E_INVALIDARG
;
2367 FIXME("base family ignored.\n");
2369 if (!dwrite_array_reserve((void **)&fallbackbuilder
->mappings
, &fallbackbuilder
->size
, fallbackbuilder
->count
+ 1,
2370 sizeof(*fallbackbuilder
->mappings
)))
2372 return E_OUTOFMEMORY
;
2375 mapping
= &fallbackbuilder
->mappings
[fallbackbuilder
->count
++];
2377 mapping
->ranges
= heap_calloc(ranges_count
, sizeof(*mapping
->ranges
));
2378 memcpy(mapping
->ranges
, ranges
, sizeof(*mapping
->ranges
) * ranges_count
);
2379 mapping
->ranges_count
= ranges_count
;
2380 mapping
->families
= heap_calloc(families_count
, sizeof(*mapping
->families
));
2381 mapping
->families_count
= families_count
;
2382 for (i
= 0; i
< families_count
; i
++)
2383 mapping
->families
[i
] = heap_strdupW(target_families
[i
]);
2384 mapping
->collection
= collection
;
2385 if (mapping
->collection
)
2386 IDWriteFontCollection_AddRef(mapping
->collection
);
2387 mapping
->locale
= heap_strdupW(locale
);
2388 mapping
->scale
= scale
;
2393 static HRESULT WINAPI
fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder
*iface
, IDWriteFontFallback
*fallback
)
2395 FIXME("%p, %p stub.\n", iface
, fallback
);
2400 static HRESULT WINAPI
fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder
*iface
,
2401 IDWriteFontFallback
**ret
)
2403 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2404 struct dwrite_fontfallback
*fallback
;
2406 TRACE("%p, %p.\n", iface
, ret
);
2410 fallback
= heap_alloc(sizeof(*fallback
));
2412 return E_OUTOFMEMORY
;
2414 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &customfontfallbackvtbl
;
2415 fallback
->refcount
= 1;
2416 fallback
->factory
= fallbackbuilder
->factory
;
2417 IDWriteFactory7_AddRef(fallback
->factory
);
2419 *ret
= (IDWriteFontFallback
*)&fallback
->IDWriteFontFallback1_iface
;
2423 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl
=
2425 fontfallbackbuilder_QueryInterface
,
2426 fontfallbackbuilder_AddRef
,
2427 fontfallbackbuilder_Release
,
2428 fontfallbackbuilder_AddMapping
,
2429 fontfallbackbuilder_AddMappings
,
2430 fontfallbackbuilder_CreateFontFallback
,
2433 HRESULT
create_fontfallback_builder(IDWriteFactory7
*factory
, IDWriteFontFallbackBuilder
**ret
)
2435 struct dwrite_fontfallback_builder
*builder
;
2439 builder
= heap_alloc_zero(sizeof(*builder
));
2441 return E_OUTOFMEMORY
;
2443 builder
->IDWriteFontFallbackBuilder_iface
.lpVtbl
= &fontfallbackbuildervtbl
;
2444 builder
->refcount
= 1;
2445 builder
->factory
= factory
;
2446 IDWriteFactory7_AddRef(builder
->factory
);
2448 *ret
= &builder
->IDWriteFontFallbackBuilder_iface
;