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 scriptprops
= &dwritescripts_properties
[context
.script
];
1194 hr
= shape_get_glyphs(&context
, scriptprops
->scripttags
);
1197 *actual_glyph_count
= context
.glyph_count
;
1198 memcpy(glyphs
, context
.u
.subst
.glyphs
, context
.glyph_count
* sizeof(*glyphs
));
1199 memcpy(glyph_props
, context
.u
.subst
.glyph_props
, context
.glyph_count
* sizeof(*glyph_props
));
1202 heap_free(context
.u
.subst
.glyph_props
);
1203 heap_free(context
.u
.subst
.glyphs
);
1204 heap_free(context
.glyph_infos
);
1209 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1210 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1211 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1212 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, BOOL is_sideways
, BOOL is_rtl
,
1213 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1214 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1216 const struct dwritescript_properties
*scriptprops
;
1217 struct scriptshaping_context context
;
1218 struct dwrite_fontface
*font_obj
;
1222 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n", debugstr_wn(text
, text_len
),
1223 clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1224 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_lengths
,
1225 feature_ranges
, advances
, offsets
);
1227 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1229 if (glyph_count
== 0)
1232 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1234 for (i
= 0; i
< glyph_count
; ++i
)
1236 if (glyph_props
[i
].isZeroWidthSpace
)
1239 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, DWRITE_MEASURING_MODE_NATURAL
, emSize
, 1.0f
,
1240 NULL
, glyphs
[i
], is_sideways
);
1241 offsets
[i
].advanceOffset
= 0.0f
;
1242 offsets
[i
].ascenderOffset
= 0.0f
;
1245 context
.cache
= fontface_get_shaping_cache(font_obj
);
1246 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1247 context
.text
= text
;
1248 context
.length
= text_len
;
1249 context
.is_rtl
= is_rtl
;
1250 context
.is_sideways
= is_sideways
;
1251 context
.u
.pos
.glyphs
= glyphs
;
1252 context
.u
.pos
.glyph_props
= glyph_props
;
1253 context
.u
.pos
.text_props
= text_props
;
1254 context
.u
.pos
.clustermap
= clustermap
;
1255 context
.glyph_count
= glyph_count
;
1256 context
.emsize
= emSize
;
1257 context
.measuring_mode
= DWRITE_MEASURING_MODE_NATURAL
;
1258 context
.advances
= advances
;
1259 context
.offsets
= offsets
;
1260 context
.language_tag
= get_opentype_language(locale
);
1261 context
.user_features
.features
= features
;
1262 context
.user_features
.range_lengths
= feature_range_lengths
;
1263 context
.user_features
.range_count
= feature_ranges
;
1264 context
.glyph_infos
= heap_alloc_zero(sizeof(*context
.glyph_infos
) * glyph_count
);
1265 context
.table
= &context
.cache
->gpos
;
1267 scriptprops
= &dwritescripts_properties
[context
.script
];
1268 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1270 heap_free(context
.glyph_infos
);
1275 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1276 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1277 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1278 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, float ppdip
,
1279 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1280 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1281 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1283 const struct dwritescript_properties
*scriptprops
;
1284 struct scriptshaping_context context
;
1285 DWRITE_MEASURING_MODE measuring_mode
;
1286 struct dwrite_fontface
*font_obj
;
1290 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1291 debugstr_wn(text
, text_len
), clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
,
1292 emSize
, ppdip
, transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
),
1293 debugstr_w(locale
), features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1295 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1297 if (glyph_count
== 0)
1300 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1302 measuring_mode
= use_gdi_natural
? DWRITE_MEASURING_MODE_GDI_NATURAL
: DWRITE_MEASURING_MODE_GDI_CLASSIC
;
1304 for (i
= 0; i
< glyph_count
; ++i
)
1306 if (glyph_props
[i
].isZeroWidthSpace
)
1309 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, measuring_mode
, emSize
, ppdip
,
1310 transform
, glyphs
[i
], is_sideways
);
1311 offsets
[i
].advanceOffset
= 0.0f
;
1312 offsets
[i
].ascenderOffset
= 0.0f
;
1315 context
.cache
= fontface_get_shaping_cache(font_obj
);
1316 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1317 context
.text
= text
;
1318 context
.length
= text_len
;
1319 context
.is_rtl
= is_rtl
;
1320 context
.is_sideways
= is_sideways
;
1321 context
.u
.pos
.glyphs
= glyphs
;
1322 context
.u
.pos
.glyph_props
= glyph_props
;
1323 context
.u
.pos
.text_props
= text_props
;
1324 context
.u
.pos
.clustermap
= clustermap
;
1325 context
.glyph_count
= glyph_count
;
1326 context
.emsize
= emSize
* ppdip
;
1327 context
.measuring_mode
= measuring_mode
;
1328 context
.advances
= advances
;
1329 context
.offsets
= offsets
;
1330 context
.language_tag
= get_opentype_language(locale
);
1331 context
.user_features
.features
= features
;
1332 context
.user_features
.range_lengths
= feature_range_lengths
;
1333 context
.user_features
.range_count
= feature_ranges
;
1334 context
.glyph_infos
= heap_alloc_zero(sizeof(*context
.glyph_infos
) * glyph_count
);
1335 context
.table
= &context
.cache
->gpos
;
1337 scriptprops
= &dwritescripts_properties
[context
.script
];
1338 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1340 heap_free(context
.glyph_infos
);
1345 static HRESULT
apply_cluster_spacing(float leading_spacing
, float trailing_spacing
, float min_advance_width
,
1346 unsigned int start
, unsigned int end
, float const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1347 DWRITE_SHAPING_GLYPH_PROPERTIES
const *glyph_props
, float *modified_advances
,
1348 DWRITE_GLYPH_OFFSET
*modified_offsets
)
1350 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1351 unsigned int first_spacing
, last_spacing
, i
;
1352 float advance
, origin
= 0.0f
, *deltas
;
1353 BOOL is_spacing_cluster
= FALSE
;
1355 if (modified_advances
!= advances
)
1356 memcpy(&modified_advances
[start
], &advances
[start
], (end
- start
+ 1) * sizeof(*advances
));
1357 if (modified_offsets
!= offsets
)
1358 memcpy(&modified_offsets
[start
], &offsets
[start
], (end
- start
+ 1) * sizeof(*offsets
));
1360 for (first_spacing
= start
; first_spacing
<= end
; ++first_spacing
)
1362 if ((is_spacing_cluster
= !glyph_props
[first_spacing
].isZeroWidthSpace
))
1366 /* Nothing to adjust if there is no spacing glyphs. */
1367 if (!is_spacing_cluster
)
1370 for (last_spacing
= end
; last_spacing
>= start
; --last_spacing
)
1372 if (!glyph_props
[last_spacing
].isZeroWidthSpace
)
1376 deltas
= heap_calloc(end
- start
+ 1, sizeof(*deltas
));
1378 return E_OUTOFMEMORY
;
1380 /* Cluster advance, note that properties are ignored. */
1381 origin
= offsets
[start
].advanceOffset
;
1382 for (i
= start
, advance
= 0.0f
; i
<= end
; ++i
)
1384 float cur
= advance
+ offsets
[i
].advanceOffset
;
1386 deltas
[i
- start
] = cur
- origin
;
1388 advance
+= advances
[i
];
1392 /* Negative spacing. */
1393 if (leading_spacing
< 0.0f
)
1395 advance
+= leading_spacing
;
1396 modified_advances
[first_spacing
] += leading_spacing
;
1397 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1400 if (trailing_spacing
< 0.0f
)
1402 advance
+= trailing_spacing
;
1403 modified_advances
[last_spacing
] += trailing_spacing
;
1406 /* Minimal advance. */
1407 advance
= min_advance_width
- advance
;
1408 if (advance
> 0.0f
) {
1409 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1410 float half
= advance
/ 2.0f
;
1414 modified_advances
[first_spacing
] += half
;
1415 modified_advances
[last_spacing
] += half
;
1416 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1418 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1420 modified_advances
[first_spacing
] += half
;
1421 modified_advances
[last_spacing
] += half
;
1422 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1424 else if (leading_spacing
< 0.0f
)
1426 modified_advances
[first_spacing
] += advance
;
1427 modified_offsets
[first_spacing
].advanceOffset
+= advance
;
1430 modified_advances
[last_spacing
] += advance
;
1433 /* Positive spacing. */
1434 if (leading_spacing
> 0.0f
)
1436 modified_advances
[first_spacing
] += leading_spacing
;
1437 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1440 if (trailing_spacing
> 0.0f
)
1441 modified_advances
[last_spacing
] += trailing_spacing
;
1443 /* Update offsets to preserve original relative positions within cluster. */
1444 for (i
= first_spacing
; i
> start
; --i
)
1446 unsigned int cur
= i
- 1;
1447 modified_offsets
[cur
].advanceOffset
= modified_advances
[cur
] + modified_offsets
[i
].advanceOffset
-
1451 for (i
= first_spacing
+ 1; i
<= end
; ++i
)
1453 modified_offsets
[i
].advanceOffset
= deltas
[i
- start
] + modified_offsets
[i
- 1].advanceOffset
-
1454 modified_advances
[i
- 1];
1462 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1464 UINT16 g
= clustermap
[start
];
1467 while (start
< text_len
&& clustermap
[++start
] == g
)
1472 /* Applies spacing adjustments to clusters.
1474 Adjustments are applied in the following order:
1476 1. Negative adjustments
1478 Leading and trailing spacing could be negative, at this step
1479 only negative ones are actually applied. Leading spacing is only
1480 applied to leading glyph, trailing - to trailing glyph.
1482 2. Minimum advance width
1484 Advances could only be reduced at this point or unchanged. In any
1485 case it's checked if cluster advance width is less than minimum width.
1486 If it's the case advance width is incremented up to minimum value.
1488 Important part is the direction in which this increment is applied;
1489 it depends on direction from which total cluster advance was trimmed
1490 at step 1. So it could be incremented from leading, trailing, or both
1491 sides. When applied to both sides, each side gets half of difference
1492 that brings advance to minimum width.
1494 3. Positive adjustments
1496 After minimum width rule was applied, positive spacing is applied in the same
1497 way as negative one on step 1.
1499 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1500 keeps its position in coordinate system where initial advance width is counted
1505 It's known that isZeroWidthSpace property keeps initial advance from changing.
1508 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1509 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1510 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1511 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1515 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1516 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1518 if (min_advance_width
< 0.0f
) {
1519 if (modified_advances
!= advances
)
1520 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1521 return E_INVALIDARG
;
1524 for (i
= 0; i
< len
;)
1526 unsigned int length
= get_cluster_length(clustermap
, i
, len
);
1527 unsigned int start
, end
;
1529 start
= clustermap
[i
];
1530 end
= i
+ length
< len
? clustermap
[i
+ length
] : glyph_count
;
1532 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
, start
, end
- 1, advances
,
1533 offsets
, props
, modified_advances
, modified_offsets
);
1541 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*fontface
,
1542 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1543 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1545 struct dwrite_fontface
*font_obj
;
1546 const DWRITE_FONT_METRICS1
*metrics
;
1548 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1549 baseline_coord
, exists
);
1552 *baseline_coord
= 0;
1554 if (baseline
== DWRITE_BASELINE_DEFAULT
)
1555 baseline
= vertical
? DWRITE_BASELINE_CENTRAL
: DWRITE_BASELINE_ROMAN
;
1557 if ((unsigned int)baseline
> DWRITE_BASELINE_MAXIMUM
)
1558 return E_INVALIDARG
;
1560 /* TODO: fetch BASE table data if available. */
1562 if (!*exists
&& is_simulation_allowed
)
1564 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1565 metrics
= &font_obj
->metrics
;
1569 case DWRITE_BASELINE_ROMAN
:
1570 *baseline_coord
= vertical
? metrics
->descent
: 0;
1572 case DWRITE_BASELINE_CENTRAL
:
1573 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1574 -(metrics
->ascent
- metrics
->descent
) / 2;
1576 case DWRITE_BASELINE_MATH
:
1577 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1578 -(metrics
->ascent
+ metrics
->descent
) / 2;
1580 case DWRITE_BASELINE_HANGING
:
1581 /* FIXME: this one isn't accurate, but close. */
1582 *baseline_coord
= vertical
? metrics
->capHeight
* 6 / 7 + metrics
->descent
: metrics
->capHeight
* 6 / 7;
1584 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM
:
1585 case DWRITE_BASELINE_MINIMUM
:
1586 *baseline_coord
= vertical
? 0 : metrics
->descent
;
1588 case DWRITE_BASELINE_IDEOGRAPHIC_TOP
:
1589 case DWRITE_BASELINE_MAXIMUM
:
1590 *baseline_coord
= vertical
? metrics
->ascent
+ metrics
->descent
: -metrics
->ascent
;
1600 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1601 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1603 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1607 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1608 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1610 TRACE("%d, %d, %p.\n", angle
, is_sideways
, transform
);
1612 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1615 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1616 DWRITE_SCRIPT_PROPERTIES
*props
)
1618 TRACE("%u, %p.\n", sa
.script
, props
);
1620 if (sa
.script
> Script_LastId
)
1621 return E_INVALIDARG
;
1623 *props
= dwritescripts_properties
[sa
.script
].props
;
1627 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1629 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
) ||
1630 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1631 c
== 0x200e || c
== 0x200f || (c
>= 0x202a && c
<= 0x202e))
1634 UINT16 script
= get_char_script(c
);
1635 return !dwritescripts_properties
[script
].is_complex
;
1639 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1640 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1645 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1651 return E_INVALIDARG
;
1658 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1659 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1660 if (is_char_from_simple_script(text
[i
])) {
1671 if (*is_simple
&& indices
) {
1672 UINT32
*codepoints
= heap_calloc(*len_read
, sizeof(*codepoints
));
1674 return E_OUTOFMEMORY
;
1676 for (i
= 0; i
< *len_read
; i
++)
1677 codepoints
[i
] = text
[i
];
1679 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1680 heap_free(codepoints
);
1686 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1687 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1688 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1690 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1691 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1695 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1696 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1697 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1699 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1704 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1705 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1706 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1707 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1708 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1709 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1711 FIXME("(%p %.2f %u %u %u %u %p %p %p %p %p %p %p %p %p %p %p): stub\n", face
, font_em_size
, sa
.script
,
1712 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1713 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1717 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1718 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1720 static const DWRITE_MATRIX transforms
[] = {
1721 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1722 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1723 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1724 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1727 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle
, is_sideways
, originX
, originY
, m
);
1729 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1730 memset(m
, 0, sizeof(*m
));
1731 return E_INVALIDARG
;
1734 /* for sideways case simply rotate 90 degrees more */
1737 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1738 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1740 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1741 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1743 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1744 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1746 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1747 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1754 *m
= transforms
[angle
];
1756 /* shift components represent transform necessary to get from original point to
1757 rotated one in new coordinate system */
1758 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1759 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1760 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1766 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1767 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1768 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1770 struct scriptshaping_context context
= { 0 };
1771 const struct dwritescript_properties
*props
;
1772 struct dwrite_fontface
*font_obj
;
1774 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
,
1775 actual_tagcount
, tags
);
1777 if (sa
.script
> Script_LastId
)
1778 return E_INVALIDARG
;
1780 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1782 context
.cache
= fontface_get_shaping_cache(font_obj
);
1783 context
.language_tag
= get_opentype_language(locale
);
1784 props
= &dwritescripts_properties
[sa
.script
];
1786 return shape_get_typographic_features(&context
, props
->scripttags
, max_tagcount
, actual_tagcount
, tags
);
1789 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1790 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
, DWRITE_FONT_FEATURE_TAG feature
,
1791 UINT32 glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
1793 struct scriptshaping_context context
= { 0 };
1794 const struct dwritescript_properties
*props
;
1795 struct dwrite_fontface
*font_obj
;
1798 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), debugstr_tag(feature
),
1799 glyph_count
, glyphs
, feature_applies
);
1801 if (sa
.script
> Script_LastId
)
1802 return E_INVALIDARG
;
1804 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1806 context
.cache
= fontface_get_shaping_cache(font_obj
);
1807 context
.language_tag
= get_opentype_language(locale
);
1808 context
.glyph_infos
= heap_calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1810 props
= &dwritescripts_properties
[sa
.script
];
1812 hr
= shape_check_typographic_feature(&context
, props
->scripttags
, feature
, glyph_count
, glyphs
, feature_applies
);
1814 heap_free(context
.glyph_infos
);
1819 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl
=
1821 dwritetextanalyzer_QueryInterface
,
1822 dwritetextanalyzer_AddRef
,
1823 dwritetextanalyzer_Release
,
1824 dwritetextanalyzer_AnalyzeScript
,
1825 dwritetextanalyzer_AnalyzeBidi
,
1826 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1827 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1828 dwritetextanalyzer_GetGlyphs
,
1829 dwritetextanalyzer_GetGlyphPlacements
,
1830 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1831 dwritetextanalyzer1_ApplyCharacterSpacing
,
1832 dwritetextanalyzer1_GetBaseline
,
1833 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1834 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1835 dwritetextanalyzer1_GetScriptProperties
,
1836 dwritetextanalyzer1_GetTextComplexity
,
1837 dwritetextanalyzer1_GetJustificationOpportunities
,
1838 dwritetextanalyzer1_JustifyGlyphAdvances
,
1839 dwritetextanalyzer1_GetJustifiedGlyphs
,
1840 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1841 dwritetextanalyzer2_GetTypographicFeatures
,
1842 dwritetextanalyzer2_CheckTypographicFeature
1845 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1847 IDWriteTextAnalyzer2
*get_text_analyzer(void)
1849 return &textanalyzer
;
1852 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1854 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1856 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1857 IsEqualIID(riid
, &IID_IUnknown
))
1860 IDWriteNumberSubstitution_AddRef(iface
);
1864 WARN("%s not implemented.\n", debugstr_guid(riid
));
1868 return E_NOINTERFACE
;
1871 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1873 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
1874 ULONG refcount
= InterlockedIncrement(&object
->refcount
);
1876 TRACE("%p, refcount %d.\n", iface
, refcount
);
1881 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1883 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
1884 ULONG refcount
= InterlockedDecrement(&object
->refcount
);
1886 TRACE("%p, refcount %d.\n", iface
, refcount
);
1890 heap_free(object
->locale
);
1897 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
=
1899 dwritenumbersubstitution_QueryInterface
,
1900 dwritenumbersubstitution_AddRef
,
1901 dwritenumbersubstitution_Release
1904 struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
1906 if (!iface
|| iface
->lpVtbl
!= &numbersubstitutionvtbl
)
1908 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
1911 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1912 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1914 struct dwrite_numbersubstitution
*substitution
;
1918 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1919 return E_INVALIDARG
;
1921 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1922 return E_INVALIDARG
;
1924 substitution
= heap_alloc(sizeof(*substitution
));
1926 return E_OUTOFMEMORY
;
1928 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1929 substitution
->refcount
= 1;
1930 substitution
->ignore_user_override
= ignore_user_override
;
1931 substitution
->method
= method
;
1932 substitution
->locale
= heap_strdupW(locale
);
1933 if (locale
&& !substitution
->locale
) {
1934 heap_free(substitution
);
1935 return E_OUTOFMEMORY
;
1938 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1942 /* IDWriteFontFallback */
1943 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback1
*iface
, REFIID riid
, void **obj
)
1945 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1947 if (IsEqualIID(riid
, &IID_IDWriteFontFallback1
) ||
1948 IsEqualIID(riid
, &IID_IDWriteFontFallback
) ||
1949 IsEqualIID(riid
, &IID_IUnknown
))
1952 IDWriteFontFallback1_AddRef(iface
);
1956 WARN("%s not implemented.\n", debugstr_guid(riid
));
1959 return E_NOINTERFACE
;
1962 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback1
*iface
)
1964 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
1966 TRACE("%p.\n", iface
);
1968 return IDWriteFactory7_AddRef(fallback
->factory
);
1971 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback1
*iface
)
1973 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
1975 TRACE("%p.\n", fallback
);
1977 return IDWriteFactory7_Release(fallback
->factory
);
1980 static int __cdecl
compare_mapping_range(const void *a
, const void *b
)
1982 UINT32 ch
= *(UINT32
*)a
;
1983 DWRITE_UNICODE_RANGE
*range
= (DWRITE_UNICODE_RANGE
*)b
;
1985 if (ch
> range
->last
)
1987 else if (ch
< range
->first
)
1993 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
1997 for (i
= 0; i
< fallback
->mappings_count
; i
++) {
1998 struct fallback_mapping
*mapping
= &fallback
->mappings
[i
];
2000 if (bsearch(&ch
, mapping
->ranges
, mapping
->ranges_count
, sizeof(*mapping
->ranges
),
2001 compare_mapping_range
) != NULL
)
2008 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
2009 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
2011 IDWriteFontFamily
*family
;
2012 BOOL exists
= FALSE
;
2018 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
2025 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
2029 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
2030 IDWriteFontFamily_Release(family
);
2034 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
2039 for (i
= 0; i
< length
; i
++) {
2040 UINT16 script
= get_char_script(text
[i
]);
2043 if (script
== Script_Unknown
|| script
== Script_Common
) {
2048 /* stop on first unsupported character */
2050 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
2051 if (hr
== S_OK
&& exists
)
2060 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
2061 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2062 IDWriteFont
**mapped_font
)
2064 const struct fallback_mapping
*mapping
;
2068 *mapped_font
= NULL
;
2070 mapping
= find_fallback_mapping(fallback
, text
[0]);
2072 WARN("No mapping range for %#x.\n", text
[0]);
2076 /* Now let's see what fallback can handle. Pick first font that could be created. */
2077 for (i
= 0; i
< mapping
->families_count
; i
++) {
2078 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->families
[i
],
2079 weight
, style
, stretch
, mapped_font
);
2081 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping
->families
[i
]));
2086 if (!*mapped_font
) {
2087 WARN("Failed to create fallback font.\n");
2091 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
2093 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping
->families
[i
]), hr
);
2095 if (!*mapped_length
) {
2096 IDWriteFont_Release(*mapped_font
);
2097 *mapped_font
= NULL
;
2100 return *mapped_length
? S_OK
: E_FAIL
;
2103 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2104 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2105 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2106 IDWriteFont
**ret_font
, FLOAT
*scale
)
2108 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2113 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2114 debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2121 return E_INVALIDARG
;
2126 if (!basecollection
)
2127 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
2129 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
2133 if (basefamily
&& *basefamily
) {
2134 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
2138 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
2143 if (!*mapped_length
) {
2144 IDWriteFont
*mapped_font
;
2146 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
2148 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2150 *mapped_length
= length
;
2156 IDWriteFont_Release(*ret_font
);
2157 *ret_font
= mapped_font
;
2166 static HRESULT WINAPI
fontfallback1_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2167 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2168 DWRITE_FONT_AXIS_VALUE
const *axis_values
, UINT32 values_count
, UINT32
*mapped_length
, FLOAT
*scale
,
2169 IDWriteFontFace5
**ret_fontface
)
2171 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2172 debugstr_w(basefamily
), axis_values
, values_count
, mapped_length
, scale
, ret_fontface
);
2177 static const IDWriteFontFallback1Vtbl fontfallbackvtbl
=
2179 fontfallback_QueryInterface
,
2180 fontfallback_AddRef
,
2181 fontfallback_Release
,
2182 fontfallback_MapCharacters
,
2183 fontfallback1_MapCharacters
,
2186 HRESULT
create_system_fontfallback(IDWriteFactory7
*factory
, IDWriteFontFallback1
**ret
)
2188 struct dwrite_fontfallback
*fallback
;
2192 fallback
= heap_alloc(sizeof(*fallback
));
2194 return E_OUTOFMEMORY
;
2196 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &fontfallbackvtbl
;
2197 fallback
->factory
= factory
;
2198 fallback
->mappings
= (struct fallback_mapping
*)fontfallback_neutral_data
;
2199 fallback
->mappings_count
= ARRAY_SIZE(fontfallback_neutral_data
);
2200 IDWriteFactory5_GetSystemFontCollection((IDWriteFactory5
*)fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
2202 *ret
= &fallback
->IDWriteFontFallback1_iface
;
2207 void release_system_fontfallback(IDWriteFontFallback1
*iface
)
2209 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2210 IDWriteFontCollection1_Release(fallback
->systemcollection
);
2211 heap_free(fallback
);
2214 static ULONG WINAPI
customfontfallback_AddRef(IDWriteFontFallback1
*iface
)
2216 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2217 ULONG refcount
= InterlockedIncrement(&fallback
->refcount
);
2219 TRACE("%p, refcount %u.\n", iface
, refcount
);
2224 static ULONG WINAPI
customfontfallback_Release(IDWriteFontFallback1
*iface
)
2226 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2227 ULONG refcount
= InterlockedDecrement(&fallback
->refcount
);
2229 TRACE("%p, refcount %u.\n", iface
, refcount
);
2233 IDWriteFactory7_Release(fallback
->factory
);
2234 heap_free(fallback
);
2240 static HRESULT WINAPI
customfontfallback_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2241 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2242 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2243 IDWriteFont
**ret_font
, FLOAT
*scale
)
2245 FIXME("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface
, source
, position
, length
,
2246 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2251 static HRESULT WINAPI
customfontfallback1_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2252 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2253 DWRITE_FONT_AXIS_VALUE
const *axis_values
, UINT32 values_count
, UINT32
*mapped_length
, FLOAT
*scale
,
2254 IDWriteFontFace5
**ret_fontface
)
2256 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2257 debugstr_w(basefamily
), axis_values
, values_count
, mapped_length
, scale
, ret_fontface
);
2262 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl
=
2264 fontfallback_QueryInterface
,
2265 customfontfallback_AddRef
,
2266 customfontfallback_Release
,
2267 customfontfallback_MapCharacters
,
2268 customfontfallback1_MapCharacters
,
2271 static HRESULT WINAPI
fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder
*iface
, REFIID riid
, void **obj
)
2273 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
2275 if (IsEqualIID(riid
, &IID_IDWriteFontFallbackBuilder
) || IsEqualIID(riid
, &IID_IUnknown
)) {
2277 IDWriteFontFallbackBuilder_AddRef(iface
);
2281 WARN("%s not implemented.\n", debugstr_guid(riid
));
2284 return E_NOINTERFACE
;
2287 static ULONG WINAPI
fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder
*iface
)
2289 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2290 ULONG refcount
= InterlockedIncrement(&fallbackbuilder
->refcount
);
2292 TRACE("%p, refcount %d.\n", iface
, refcount
);
2297 static ULONG WINAPI
fontfallbackbuilder_Release(IDWriteFontFallbackBuilder
*iface
)
2299 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2300 ULONG refcount
= InterlockedDecrement(&fallbackbuilder
->refcount
);
2303 TRACE("%p, refcount %d.\n", iface
, refcount
);
2307 for (i
= 0; i
< fallbackbuilder
->count
; ++i
)
2309 struct fallback_mapping
*mapping
= &fallbackbuilder
->mappings
[i
];
2312 for (j
= 0; j
< mapping
->families_count
; j
++)
2313 heap_free(mapping
->families
[j
]);
2314 heap_free(mapping
->families
);
2316 if (mapping
->collection
)
2317 IDWriteFontCollection_Release(mapping
->collection
);
2318 heap_free(mapping
->ranges
);
2319 heap_free(mapping
->locale
);
2322 IDWriteFactory7_Release(fallbackbuilder
->factory
);
2323 heap_free(fallbackbuilder
->mappings
);
2324 heap_free(fallbackbuilder
);
2330 static HRESULT WINAPI
fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder
*iface
,
2331 const DWRITE_UNICODE_RANGE
*ranges
, UINT32 ranges_count
, WCHAR
const **target_families
, UINT32 families_count
,
2332 IDWriteFontCollection
*collection
, WCHAR
const *locale
, WCHAR
const *base_family
, FLOAT scale
)
2334 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2335 struct fallback_mapping
*mapping
;
2338 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface
, ranges
, ranges_count
, target_families
, families_count
,
2339 collection
, debugstr_w(locale
), debugstr_w(base_family
), scale
);
2341 if (!ranges
|| ranges_count
== 0 || !target_families
|| families_count
== 0 || scale
< 0.0f
)
2342 return E_INVALIDARG
;
2345 FIXME("base family ignored.\n");
2347 if (!dwrite_array_reserve((void **)&fallbackbuilder
->mappings
, &fallbackbuilder
->size
, fallbackbuilder
->count
+ 1,
2348 sizeof(*fallbackbuilder
->mappings
)))
2350 return E_OUTOFMEMORY
;
2353 mapping
= &fallbackbuilder
->mappings
[fallbackbuilder
->count
++];
2355 mapping
->ranges
= heap_calloc(ranges_count
, sizeof(*mapping
->ranges
));
2356 memcpy(mapping
->ranges
, ranges
, sizeof(*mapping
->ranges
) * ranges_count
);
2357 mapping
->ranges_count
= ranges_count
;
2358 mapping
->families
= heap_alloc_zero(sizeof(*mapping
->families
) * families_count
);
2359 mapping
->families_count
= families_count
;
2360 for (i
= 0; i
< families_count
; i
++)
2361 mapping
->families
[i
] = heap_strdupW(target_families
[i
]);
2362 mapping
->collection
= collection
;
2363 if (mapping
->collection
)
2364 IDWriteFontCollection_AddRef(mapping
->collection
);
2365 mapping
->locale
= heap_strdupW(locale
);
2366 mapping
->scale
= scale
;
2371 static HRESULT WINAPI
fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder
*iface
, IDWriteFontFallback
*fallback
)
2373 FIXME("%p, %p stub.\n", iface
, fallback
);
2378 static HRESULT WINAPI
fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder
*iface
,
2379 IDWriteFontFallback
**ret
)
2381 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2382 struct dwrite_fontfallback
*fallback
;
2384 TRACE("%p, %p.\n", iface
, ret
);
2388 fallback
= heap_alloc(sizeof(*fallback
));
2390 return E_OUTOFMEMORY
;
2392 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &customfontfallbackvtbl
;
2393 fallback
->refcount
= 1;
2394 fallback
->factory
= fallbackbuilder
->factory
;
2395 IDWriteFactory7_AddRef(fallback
->factory
);
2397 *ret
= (IDWriteFontFallback
*)&fallback
->IDWriteFontFallback1_iface
;
2401 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl
=
2403 fontfallbackbuilder_QueryInterface
,
2404 fontfallbackbuilder_AddRef
,
2405 fontfallbackbuilder_Release
,
2406 fontfallbackbuilder_AddMapping
,
2407 fontfallbackbuilder_AddMappings
,
2408 fontfallbackbuilder_CreateFontFallback
,
2411 HRESULT
create_fontfallback_builder(IDWriteFactory7
*factory
, IDWriteFontFallbackBuilder
**ret
)
2413 struct dwrite_fontfallback_builder
*builder
;
2417 builder
= heap_alloc_zero(sizeof(*builder
));
2419 return E_OUTOFMEMORY
;
2421 builder
->IDWriteFontFallbackBuilder_iface
.lpVtbl
= &fontfallbackbuildervtbl
;
2422 builder
->refcount
= 1;
2423 builder
->factory
= factory
;
2424 IDWriteFactory7_AddRef(builder
->factory
);
2426 *ret
= &builder
->IDWriteFontFallbackBuilder_iface
;