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') } },
201 { /* Chrs */ { 0x73726843, 109, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('c','h','r','s') } },
202 { /* Diak */ { 0x6b616944, 342, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('d','i','a','k') } },
203 { /* Kits */ { 0x7374694b, 288, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('k','i','t','s') } },
204 { /* Yezi */ { 0x697a6559, 192, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('y','e','z','i') } },
208 const char *debugstr_sa_script(UINT16 script
)
210 return script
< Script_LastId
? debugstr_tag(dwritescripts_properties
[script
].props
.isoScriptCode
) : "undefined";
213 /* system font falback configuration */
214 static const WCHAR
*cjk_families
[] = { L
"Meiryo" };
216 static const DWRITE_UNICODE_RANGE cjk_ranges
[] =
218 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
219 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
220 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
223 struct fallback_mapping
{
224 DWRITE_UNICODE_RANGE
*ranges
;
227 UINT32 families_count
;
228 IDWriteFontCollection
*collection
;
233 static const struct fallback_mapping fontfallback_neutral_data
[] = {
234 #define MAPPING_RANGE(ranges, families) \
235 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
236 (WCHAR **)families, ARRAY_SIZE(families) }
238 MAPPING_RANGE(cjk_ranges
, cjk_families
),
243 struct dwrite_fontfallback
245 IDWriteFontFallback1 IDWriteFontFallback1_iface
;
247 IDWriteFactory7
*factory
;
248 IDWriteFontCollection1
*systemcollection
;
249 struct fallback_mapping
*mappings
;
250 UINT32 mappings_count
;
253 struct dwrite_fontfallback_builder
255 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface
;
257 IDWriteFactory7
*factory
;
258 struct fallback_mapping
*mappings
;
263 struct dwrite_numbersubstitution
265 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
268 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
270 BOOL ignore_user_override
;
273 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
275 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
278 static struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
);
280 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback1(IDWriteFontFallback1
*iface
)
282 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback1_iface
);
285 static inline struct dwrite_fontfallback_builder
*impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder
*iface
)
287 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback_builder
, IDWriteFontFallbackBuilder_iface
);
290 static inline UINT16
get_char_script(WCHAR c
)
292 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
293 return script
== Script_Inherited
? Script_Unknown
: script
;
296 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
298 DWRITE_SCRIPT_ANALYSIS sa
;
301 GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
);
302 sa
.script
= get_char_script(c
);
303 sa
.shapes
= (type
& C1_CNTRL
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
304 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
308 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
310 DWRITE_SCRIPT_ANALYSIS sa
;
311 UINT32 pos
, i
, seq_length
;
316 sa
= get_char_sa(*text
);
321 for (i
= 1; i
< length
; i
++)
323 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
325 /* Unknown type is ignored when preceded or followed by another script */
328 sa
.script
= cur_sa
.script
;
331 if (cur_sa
.script
== Script_Unknown
)
332 cur_sa
.script
= sa
.script
;
333 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
334 sa
.script
= cur_sa
.script
;
337 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
338 cur_sa
.script
= sa
.script
;
341 /* this is a length of a sequence to be reported next */
342 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
347 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
348 if (FAILED(hr
)) return hr
;
355 /* one char length case or normal completion call */
356 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
359 struct linebreaking_state
{
360 DWRITE_LINE_BREAKPOINT
*breakpoints
;
364 enum BreakConditionLocation
{
365 BreakConditionBefore
,
369 enum linebreaking_classes
{
415 static BOOL
has_strong_condition(DWRITE_BREAK_CONDITION old_condition
, DWRITE_BREAK_CONDITION new_condition
)
417 if (old_condition
== DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
|| old_condition
== DWRITE_BREAK_CONDITION_MUST_BREAK
)
420 if (old_condition
== DWRITE_BREAK_CONDITION_CAN_BREAK
&& new_condition
!= DWRITE_BREAK_CONDITION_MUST_BREAK
)
426 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
427 set to "can break" and could only be changed once. */
428 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
429 struct linebreaking_state
*state
)
431 if (location
== BreakConditionBefore
) {
432 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionBefore
, condition
))
434 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
436 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
439 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionAfter
, condition
))
441 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
442 if (pos
+ 1 < state
->count
)
443 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
447 BOOL
lb_is_newline_char(WCHAR ch
)
449 short c
= get_table_entry(wine_linebreak_table
, ch
);
450 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
453 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
455 struct linebreaking_state state
;
459 if (!(break_class
= calloc(count
, sizeof(*break_class
))))
460 return E_OUTOFMEMORY
;
462 state
.breakpoints
= breakpoints
;
465 for (i
= 0; i
< count
; i
++)
467 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
469 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_NEUTRAL
;
470 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_NEUTRAL
;
471 breakpoints
[i
].isWhitespace
= !!iswspace(text
[i
]);
472 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
473 breakpoints
[i
].padding
= 0;
475 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
476 switch (break_class
[i
])
482 break_class
[i
] = b_AL
;
485 break_class
[i
] = b_NS
;
490 /* LB2 - never break at the start */
491 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
492 /* LB3 - always break at the end. */
493 set_break_condition(count
- 1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
495 /* LB4 - LB6 - mandatory breaks. */
496 for (i
= 0; i
< count
; i
++)
498 switch (break_class
[i
])
502 /* LB5 - don't break CR x LF */
503 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
505 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
506 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
512 /* LB4 - LB5 - always break after hard breaks */
513 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
514 /* LB6 - do not break before hard breaks */
515 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
520 /* LB7 - LB8 - explicit breaks and non-breaks */
521 for (i
= 0; i
< count
; i
++)
523 switch (break_class
[i
])
525 /* LB7 - do not break before spaces or zero-width space */
527 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
530 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
532 /* LB8 - break before character after zero-width space, skip spaces in-between */
534 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
536 if (j
< count
-1 && break_class
[j
+1] != b_ZW
)
537 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
539 /* LB8a - do not break after ZWJ */
541 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
546 /* LB9 - LB10 - combining marks */
547 for (i
= 0; i
< count
; i
++)
549 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
553 switch (break_class
[i
-1])
561 break_class
[i
] = b_AL
;
565 break_class
[i
] = break_class
[i
-1];
566 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
570 else break_class
[i
] = b_AL
;
574 for (i
= 0; i
< count
; i
++)
576 switch (break_class
[i
])
578 /* LB11 - don't break before and after word joiner */
580 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
581 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
583 /* LB12 - don't break after glue */
585 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
589 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
590 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
599 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
601 /* LB14 - do not break after OP, even after spaces */
604 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
606 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
608 /* LB15 - do not break within QU-OP, even with intervening spaces */
611 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
613 if (j
< count
- 1 && break_class
[j
+1] == b_OP
)
614 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
619 while(j
> 0 && break_class
[j
] == b_SP
)
621 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
622 for (j
++; j
<= i
; j
++)
623 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
625 /* LB17 - do not break within B2, even with intervening spaces */
628 while (j
< count
&& break_class
[j
+1] == b_SP
)
630 if (j
< count
- 1 && break_class
[j
+1] == b_B2
)
631 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
636 for (i
= 0; i
< count
; i
++)
638 switch(break_class
[i
])
640 /* LB18 - break is allowed after space */
642 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
644 /* LB19 - don't break before or after quotation mark */
646 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
647 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
651 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
652 if (i
< count
- 1 && break_class
[i
+1] != b_QU
)
653 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
659 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
662 if (i
< count
- 1 && break_class
[i
+1] != b_CB
)
663 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
669 switch (break_class
[i
+1])
673 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
676 if (i
> 0 && break_class
[i
-1] == b_SY
)
677 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
681 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
687 /* LB23 - do not break between digits and letters */
688 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
689 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
690 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
691 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
692 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
694 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
695 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
696 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
697 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
698 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
699 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
700 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
701 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
703 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
704 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
705 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
706 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
707 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
708 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
709 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
710 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
711 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
712 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
715 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
716 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
717 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
718 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
719 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
720 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
721 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
722 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
723 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
724 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
725 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
726 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
727 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
728 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
729 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
732 if (break_class
[i
] == b_JL
)
734 switch (break_class
[i
+1])
740 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
743 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
744 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
745 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
746 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
747 break_class
[i
+1] == b_JT
)
748 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
751 switch (break_class
[i
])
758 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
759 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
761 if (break_class
[i
] == b_PR
)
763 switch (break_class
[i
+1])
770 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
775 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
776 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
777 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
778 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
779 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
782 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
783 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
784 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
787 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
788 break_class
[i
+1] == b_OP
)
789 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
790 if (break_class
[i
] == b_CP
&&
791 (break_class
[i
+1] == b_AL
|| break_class
[i
+1] == b_HL
|| break_class
[i
+1] == b_NU
))
792 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
794 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
795 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
799 while (j
> 0 && break_class
[--j
] == b_RI
)
803 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
806 /* LB30b - do not break between an emoji base and an emoji modifier */
807 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
808 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
812 /* LB31 - allow breaks everywhere else. */
813 for (i
= 0; i
< count
; i
++)
815 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
816 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
823 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
825 TRACE("%s, %p.\n", debugstr_guid(riid
), obj
);
827 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
828 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
829 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
830 IsEqualIID(riid
, &IID_IUnknown
))
836 WARN("%s not implemented.\n", debugstr_guid(riid
));
839 return E_NOINTERFACE
;
842 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
847 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
852 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
853 data after a first request. */
854 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
862 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
863 if (FAILED(hr
)) return hr
;
868 *buff
= calloc(length
, sizeof(WCHAR
));
870 return E_OUTOFMEMORY
;
872 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
875 while (read
< length
&& *text
) {
878 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
+read
, text
, &len
);
886 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
896 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
897 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
903 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
908 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
912 hr
= analyze_script(text
, position
, length
, sink
);
918 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
919 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
921 UINT8
*levels
= NULL
, *explicit = NULL
;
922 UINT8 baselevel
, level
, explicit_level
;
923 UINT32 pos
, i
, seq_length
;
928 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
933 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
937 levels
= calloc(length
, sizeof(*levels
));
938 explicit = calloc(length
, sizeof(*explicit));
940 if (!levels
|| !explicit) {
945 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
946 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
951 explicit_level
= explicit[0];
955 for (i
= 1; i
< length
; i
++) {
956 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
959 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
966 explicit_level
= explicit[i
];
969 /* one char length case or normal completion call */
970 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
980 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
981 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
986 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
990 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
991 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
993 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
999 TRACE("%p, %u, %u, %p.\n", source
, position
, length
, sink
);
1004 /* get some, check for length */
1007 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
1008 if (FAILED(hr
)) return hr
;
1013 if (!(buff
= calloc(length
, sizeof(*buff
))))
1014 return E_OUTOFMEMORY
;
1016 memcpy(buff
, text
, len
*sizeof(WCHAR
));
1019 while (read
< length
&& text
) {
1022 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
+read
, &text
, &len
);
1027 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
1034 if (!(breakpoints
= calloc(length
, sizeof(*breakpoints
))))
1040 hr
= analyze_linebreaks(text
, length
, breakpoints
);
1044 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
1053 static UINT32
get_opentype_language(const WCHAR
*locale
)
1055 UINT32 language
= DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1059 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, ARRAY_SIZE(tag
)))
1060 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
1066 static void get_number_substitutes(IDWriteNumberSubstitution
*substitution
, BOOL is_rtl
, WCHAR
*digits
)
1068 struct dwrite_numbersubstitution
*numbersubst
= unsafe_impl_from_IDWriteNumberSubstitution(substitution
);
1069 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1078 lctype
= numbersubst
->ignore_user_override
? LOCALE_NOUSEROVERRIDE
: 0;
1080 if (numbersubst
->method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE
) {
1083 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1084 if (GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_IDIGITSUBSTITUTION
| LOCALE_RETURN_NUMBER
, (WCHAR
*)&value
, 2)) {
1088 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
;
1091 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
;
1096 WARN("Unknown IDIGITSUBSTITUTION value %lu, locale %s.\n", value
, debugstr_w(numbersubst
->locale
));
1100 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst
->locale
));
1103 method
= numbersubst
->method
;
1107 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
:
1108 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1110 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1111 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
:
1112 if (GetLocaleInfoEx(numbersubst
->locale
, LOCALE_SISO639LANGNAME
, isolang
, ARRAY_SIZE(isolang
)))
1114 static const WCHAR arabicW
[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1116 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1117 if (!wcscmp(L
"ar", isolang
))
1119 wcscpy(digits
, arabicW
);
1123 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1129 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !*digits
) {
1130 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst
->locale
), method
);
1131 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1134 if ((method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
&& !is_rtl
) || method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
)
1138 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES
const **features
,
1139 UINT32
const *feature_range_lengths
, UINT32 feature_ranges
)
1143 if (!TRACE_ON(dwrite
) || !features
)
1146 for (i
= 0, start
= 0; i
< feature_ranges
; start
+= feature_range_lengths
[i
++]) {
1147 TRACE("feature range [%u,%u)\n", start
, start
+ feature_range_lengths
[i
]);
1148 for (j
= 0; j
< features
[i
]->featureCount
; j
++)
1149 TRACE("feature %s, parameter %u\n", debugstr_tag(features
[i
]->features
[j
].nameTag
),
1150 features
[i
]->features
[j
].parameter
);
1154 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
1155 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
1156 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
1157 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1158 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
1159 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
*glyphs
,
1160 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
1162 const struct dwritescript_properties
*scriptprops
;
1163 struct scriptshaping_context context
= { 0 };
1164 struct dwrite_fontface
*font_obj
;
1165 WCHAR digits
[NATIVE_DIGITS_LEN
];
1166 unsigned int glyph_count
;
1169 TRACE("%s:%u, %p, %d, %d, %s, %s, %p, %p, %p, %u, %u, %p, %p, %p, %p, %p.\n", debugstr_wn(text
, length
),
1170 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1171 features
, feature_range_lengths
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyphs
,
1172 glyph_props
, actual_glyph_count
);
1174 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1176 get_number_substitutes(substitution
, is_rtl
, digits
);
1177 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1178 glyph_count
= max(max_glyph_count
, length
);
1180 context
.cache
= fontface_get_shaping_cache(font_obj
);
1181 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1182 context
.text
= text
;
1183 context
.length
= length
;
1184 context
.is_rtl
= is_rtl
;
1185 context
.is_sideways
= is_sideways
;
1186 context
.u
.subst
.glyphs
= calloc(glyph_count
, sizeof(*glyphs
));
1187 context
.u
.subst
.glyph_props
= calloc(glyph_count
, sizeof(*glyph_props
));
1188 context
.u
.subst
.text_props
= text_props
;
1189 context
.u
.subst
.clustermap
= clustermap
;
1190 context
.u
.subst
.max_glyph_count
= max_glyph_count
;
1191 context
.u
.subst
.capacity
= glyph_count
;
1192 context
.u
.subst
.digits
= digits
;
1193 context
.language_tag
= get_opentype_language(locale
);
1194 context
.user_features
.features
= features
;
1195 context
.user_features
.range_lengths
= feature_range_lengths
;
1196 context
.user_features
.range_count
= feature_ranges
;
1197 context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1198 context
.table
= &context
.cache
->gsub
;
1200 *actual_glyph_count
= 0;
1202 if (!context
.u
.subst
.glyphs
|| !context
.u
.subst
.glyph_props
|| !context
.glyph_infos
)
1208 scriptprops
= &dwritescripts_properties
[context
.script
];
1209 hr
= shape_get_glyphs(&context
, scriptprops
->scripttags
);
1212 *actual_glyph_count
= context
.glyph_count
;
1213 memcpy(glyphs
, context
.u
.subst
.glyphs
, context
.glyph_count
* sizeof(*glyphs
));
1214 memcpy(glyph_props
, context
.u
.subst
.glyph_props
, context
.glyph_count
* sizeof(*glyph_props
));
1218 free(context
.u
.subst
.glyph_props
);
1219 free(context
.u
.subst
.glyphs
);
1220 free(context
.glyph_infos
);
1225 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1226 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1227 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1228 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, BOOL is_sideways
, BOOL is_rtl
,
1229 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1230 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1232 const struct dwritescript_properties
*scriptprops
;
1233 struct scriptshaping_context context
;
1234 struct dwrite_fontface
*font_obj
;
1238 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n", debugstr_wn(text
, text_len
),
1239 clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1240 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_lengths
,
1241 feature_ranges
, advances
, offsets
);
1243 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1245 if (glyph_count
== 0)
1248 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1250 for (i
= 0; i
< glyph_count
; ++i
)
1252 if (glyph_props
[i
].isZeroWidthSpace
)
1255 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, DWRITE_MEASURING_MODE_NATURAL
, emSize
, 1.0f
,
1256 NULL
, glyphs
[i
], is_sideways
);
1257 offsets
[i
].advanceOffset
= 0.0f
;
1258 offsets
[i
].ascenderOffset
= 0.0f
;
1261 context
.cache
= fontface_get_shaping_cache(font_obj
);
1262 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1263 context
.text
= text
;
1264 context
.length
= text_len
;
1265 context
.is_rtl
= is_rtl
;
1266 context
.is_sideways
= is_sideways
;
1267 context
.u
.pos
.glyphs
= glyphs
;
1268 context
.u
.pos
.glyph_props
= glyph_props
;
1269 context
.u
.pos
.text_props
= text_props
;
1270 context
.u
.pos
.clustermap
= clustermap
;
1271 context
.glyph_count
= glyph_count
;
1272 context
.emsize
= emSize
;
1273 context
.measuring_mode
= DWRITE_MEASURING_MODE_NATURAL
;
1274 context
.advances
= advances
;
1275 context
.offsets
= offsets
;
1276 context
.language_tag
= get_opentype_language(locale
);
1277 context
.user_features
.features
= features
;
1278 context
.user_features
.range_lengths
= feature_range_lengths
;
1279 context
.user_features
.range_count
= feature_ranges
;
1280 context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1281 context
.table
= &context
.cache
->gpos
;
1283 if (!context
.glyph_infos
)
1289 scriptprops
= &dwritescripts_properties
[context
.script
];
1290 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1293 free(context
.glyph_infos
);
1298 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1299 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
*text_props
,
1300 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1301 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, float ppdip
,
1302 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1303 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1304 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1306 const struct dwritescript_properties
*scriptprops
;
1307 struct scriptshaping_context context
;
1308 DWRITE_MEASURING_MODE measuring_mode
;
1309 struct dwrite_fontface
*font_obj
;
1313 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1314 debugstr_wn(text
, text_len
), clustermap
, text_props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
,
1315 emSize
, ppdip
, transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
),
1316 debugstr_w(locale
), features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1318 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1320 if (glyph_count
== 0)
1323 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1325 measuring_mode
= use_gdi_natural
? DWRITE_MEASURING_MODE_GDI_NATURAL
: DWRITE_MEASURING_MODE_GDI_CLASSIC
;
1327 for (i
= 0; i
< glyph_count
; ++i
)
1329 if (glyph_props
[i
].isZeroWidthSpace
)
1332 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, measuring_mode
, emSize
, ppdip
,
1333 transform
, glyphs
[i
], is_sideways
);
1334 offsets
[i
].advanceOffset
= 0.0f
;
1335 offsets
[i
].ascenderOffset
= 0.0f
;
1338 context
.cache
= fontface_get_shaping_cache(font_obj
);
1339 context
.script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1340 context
.text
= text
;
1341 context
.length
= text_len
;
1342 context
.is_rtl
= is_rtl
;
1343 context
.is_sideways
= is_sideways
;
1344 context
.u
.pos
.glyphs
= glyphs
;
1345 context
.u
.pos
.glyph_props
= glyph_props
;
1346 context
.u
.pos
.text_props
= text_props
;
1347 context
.u
.pos
.clustermap
= clustermap
;
1348 context
.glyph_count
= glyph_count
;
1349 context
.emsize
= emSize
* ppdip
;
1350 context
.measuring_mode
= measuring_mode
;
1351 context
.advances
= advances
;
1352 context
.offsets
= offsets
;
1353 context
.language_tag
= get_opentype_language(locale
);
1354 context
.user_features
.features
= features
;
1355 context
.user_features
.range_lengths
= feature_range_lengths
;
1356 context
.user_features
.range_count
= feature_ranges
;
1357 context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
));
1358 context
.table
= &context
.cache
->gpos
;
1360 if (!context
.glyph_infos
)
1366 scriptprops
= &dwritescripts_properties
[context
.script
];
1367 hr
= shape_get_positions(&context
, scriptprops
->scripttags
);
1370 free(context
.glyph_infos
);
1375 static HRESULT
apply_cluster_spacing(float leading_spacing
, float trailing_spacing
, float min_advance_width
,
1376 unsigned int start
, unsigned int end
, float const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1377 DWRITE_SHAPING_GLYPH_PROPERTIES
const *glyph_props
, float *modified_advances
,
1378 DWRITE_GLYPH_OFFSET
*modified_offsets
)
1380 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1381 unsigned int first_spacing
, last_spacing
, i
;
1382 float advance
, origin
= 0.0f
, *deltas
;
1383 BOOL is_spacing_cluster
= FALSE
;
1385 if (modified_advances
!= advances
)
1386 memcpy(&modified_advances
[start
], &advances
[start
], (end
- start
+ 1) * sizeof(*advances
));
1387 if (modified_offsets
!= offsets
)
1388 memcpy(&modified_offsets
[start
], &offsets
[start
], (end
- start
+ 1) * sizeof(*offsets
));
1390 for (first_spacing
= start
; first_spacing
<= end
; ++first_spacing
)
1392 if ((is_spacing_cluster
= !glyph_props
[first_spacing
].isZeroWidthSpace
))
1396 /* Nothing to adjust if there is no spacing glyphs. */
1397 if (!is_spacing_cluster
)
1400 for (last_spacing
= end
; last_spacing
>= start
; --last_spacing
)
1402 if (!glyph_props
[last_spacing
].isZeroWidthSpace
)
1406 if (!(deltas
= calloc(end
- start
+ 1, sizeof(*deltas
))))
1407 return E_OUTOFMEMORY
;
1409 /* Cluster advance, note that properties are ignored. */
1410 origin
= offsets
[start
].advanceOffset
;
1411 for (i
= start
, advance
= 0.0f
; i
<= end
; ++i
)
1413 float cur
= advance
+ offsets
[i
].advanceOffset
;
1415 deltas
[i
- start
] = cur
- origin
;
1417 advance
+= advances
[i
];
1421 /* Negative spacing. */
1422 if (leading_spacing
< 0.0f
)
1424 advance
+= leading_spacing
;
1425 modified_advances
[first_spacing
] += leading_spacing
;
1426 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1429 if (trailing_spacing
< 0.0f
)
1431 advance
+= trailing_spacing
;
1432 modified_advances
[last_spacing
] += trailing_spacing
;
1435 /* Minimal advance. */
1436 advance
= min_advance_width
- advance
;
1437 if (advance
> 0.0f
) {
1438 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1439 float half
= advance
/ 2.0f
;
1443 modified_advances
[first_spacing
] += half
;
1444 modified_advances
[last_spacing
] += half
;
1445 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1447 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1449 modified_advances
[first_spacing
] += half
;
1450 modified_advances
[last_spacing
] += half
;
1451 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1453 else if (leading_spacing
< 0.0f
)
1455 modified_advances
[first_spacing
] += advance
;
1456 modified_offsets
[first_spacing
].advanceOffset
+= advance
;
1459 modified_advances
[last_spacing
] += advance
;
1462 /* Positive spacing. */
1463 if (leading_spacing
> 0.0f
)
1465 modified_advances
[first_spacing
] += leading_spacing
;
1466 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1469 if (trailing_spacing
> 0.0f
)
1470 modified_advances
[last_spacing
] += trailing_spacing
;
1472 /* Update offsets to preserve original relative positions within cluster. */
1473 for (i
= first_spacing
; i
> start
; --i
)
1475 unsigned int cur
= i
- 1;
1476 modified_offsets
[cur
].advanceOffset
= modified_advances
[cur
] + modified_offsets
[i
].advanceOffset
-
1480 for (i
= first_spacing
+ 1; i
<= end
; ++i
)
1482 modified_offsets
[i
].advanceOffset
= deltas
[i
- start
] + modified_offsets
[i
- 1].advanceOffset
-
1483 modified_advances
[i
- 1];
1491 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1493 UINT16 g
= clustermap
[start
];
1496 while (start
< text_len
&& clustermap
[++start
] == g
)
1501 /* Applies spacing adjustments to clusters.
1503 Adjustments are applied in the following order:
1505 1. Negative adjustments
1507 Leading and trailing spacing could be negative, at this step
1508 only negative ones are actually applied. Leading spacing is only
1509 applied to leading glyph, trailing - to trailing glyph.
1511 2. Minimum advance width
1513 Advances could only be reduced at this point or unchanged. In any
1514 case it's checked if cluster advance width is less than minimum width.
1515 If it's the case advance width is incremented up to minimum value.
1517 Important part is the direction in which this increment is applied;
1518 it depends on direction from which total cluster advance was trimmed
1519 at step 1. So it could be incremented from leading, trailing, or both
1520 sides. When applied to both sides, each side gets half of difference
1521 that brings advance to minimum width.
1523 3. Positive adjustments
1525 After minimum width rule was applied, positive spacing is applied in the same
1526 way as negative one on step 1.
1528 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1529 keeps its position in coordinate system where initial advance width is counted
1534 It's known that isZeroWidthSpace property keeps initial advance from changing.
1537 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1538 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1539 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1540 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1544 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1545 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1547 if (min_advance_width
< 0.0f
) {
1548 if (modified_advances
!= advances
)
1549 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1550 return E_INVALIDARG
;
1553 for (i
= 0; i
< len
;)
1555 unsigned int length
= get_cluster_length(clustermap
, i
, len
);
1556 unsigned int start
, end
;
1558 start
= clustermap
[i
];
1559 end
= i
+ length
< len
? clustermap
[i
+ length
] : glyph_count
;
1561 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
, start
, end
- 1, advances
,
1562 offsets
, props
, modified_advances
, modified_offsets
);
1570 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*fontface
,
1571 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1572 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1574 struct dwrite_fontface
*font_obj
;
1575 const DWRITE_FONT_METRICS1
*metrics
;
1577 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1578 baseline_coord
, exists
);
1581 *baseline_coord
= 0;
1583 if (baseline
== DWRITE_BASELINE_DEFAULT
)
1584 baseline
= vertical
? DWRITE_BASELINE_CENTRAL
: DWRITE_BASELINE_ROMAN
;
1586 if ((unsigned int)baseline
> DWRITE_BASELINE_MAXIMUM
)
1587 return E_INVALIDARG
;
1589 /* TODO: fetch BASE table data if available. */
1591 if (!*exists
&& is_simulation_allowed
)
1593 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1594 metrics
= &font_obj
->metrics
;
1598 case DWRITE_BASELINE_ROMAN
:
1599 *baseline_coord
= vertical
? metrics
->descent
: 0;
1601 case DWRITE_BASELINE_CENTRAL
:
1602 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1603 -(metrics
->ascent
- metrics
->descent
) / 2;
1605 case DWRITE_BASELINE_MATH
:
1606 *baseline_coord
= vertical
? (metrics
->ascent
+ metrics
->descent
) / 2 :
1607 -(metrics
->ascent
+ metrics
->descent
) / 2;
1609 case DWRITE_BASELINE_HANGING
:
1610 /* FIXME: this one isn't accurate, but close. */
1611 *baseline_coord
= vertical
? metrics
->capHeight
* 6 / 7 + metrics
->descent
: metrics
->capHeight
* 6 / 7;
1613 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM
:
1614 case DWRITE_BASELINE_MINIMUM
:
1615 *baseline_coord
= vertical
? 0 : metrics
->descent
;
1617 case DWRITE_BASELINE_IDEOGRAPHIC_TOP
:
1618 case DWRITE_BASELINE_MAXIMUM
:
1619 *baseline_coord
= vertical
? metrics
->ascent
+ metrics
->descent
: -metrics
->ascent
;
1629 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1630 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1632 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1636 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1637 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1639 TRACE("%d, %d, %p.\n", angle
, is_sideways
, transform
);
1641 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1644 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1645 DWRITE_SCRIPT_PROPERTIES
*props
)
1647 TRACE("%u, %p.\n", sa
.script
, props
);
1649 if (sa
.script
> Script_LastId
)
1650 return E_INVALIDARG
;
1652 *props
= dwritescripts_properties
[sa
.script
].props
;
1656 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1658 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
) ||
1659 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1660 c
== 0x200e || c
== 0x200f || (c
>= 0x202a && c
<= 0x202e))
1663 UINT16 script
= get_char_script(c
);
1664 return !dwritescripts_properties
[script
].is_complex
;
1668 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1669 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1674 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1680 return E_INVALIDARG
;
1687 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1688 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1689 if (is_char_from_simple_script(text
[i
])) {
1700 if (*is_simple
&& indices
)
1704 if (!(codepoints
= calloc(*len_read
, sizeof(*codepoints
))))
1705 return E_OUTOFMEMORY
;
1707 for (i
= 0; i
< *len_read
; i
++)
1708 codepoints
[i
] = text
[i
];
1710 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1717 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1718 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1719 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1721 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1722 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1726 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1727 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1728 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1730 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1735 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1736 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1737 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1738 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1739 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1740 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1742 FIXME("(%p %.2f %u %u %u %u %p %p %p %p %p %p %p %p %p %p %p): stub\n", face
, font_em_size
, sa
.script
,
1743 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1744 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1748 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1749 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1751 static const DWRITE_MATRIX transforms
[] = {
1752 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1753 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1754 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1755 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1758 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle
, is_sideways
, originX
, originY
, m
);
1760 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1761 memset(m
, 0, sizeof(*m
));
1762 return E_INVALIDARG
;
1765 /* for sideways case simply rotate 90 degrees more */
1768 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1769 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1771 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1772 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1774 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1775 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1777 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1778 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1785 *m
= transforms
[angle
];
1787 /* shift components represent transform necessary to get from original point to
1788 rotated one in new coordinate system */
1789 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1790 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1791 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1797 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1798 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1799 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1801 struct scriptshaping_context context
= { 0 };
1802 const struct dwritescript_properties
*props
;
1803 struct dwrite_fontface
*font_obj
;
1805 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
,
1806 actual_tagcount
, tags
);
1808 if (sa
.script
> Script_LastId
)
1809 return E_INVALIDARG
;
1811 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1813 context
.cache
= fontface_get_shaping_cache(font_obj
);
1814 context
.language_tag
= get_opentype_language(locale
);
1815 props
= &dwritescripts_properties
[sa
.script
];
1817 return shape_get_typographic_features(&context
, props
->scripttags
, max_tagcount
, actual_tagcount
, tags
);
1820 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1821 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
, DWRITE_FONT_FEATURE_TAG feature
,
1822 UINT32 glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
1824 struct scriptshaping_context context
= { 0 };
1825 const struct dwritescript_properties
*props
;
1826 struct dwrite_fontface
*font_obj
;
1829 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface
, fontface
, sa
.script
, debugstr_w(locale
), debugstr_tag(feature
),
1830 glyph_count
, glyphs
, feature_applies
);
1832 if (sa
.script
> Script_LastId
)
1833 return E_INVALIDARG
;
1835 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1837 context
.cache
= fontface_get_shaping_cache(font_obj
);
1838 context
.language_tag
= get_opentype_language(locale
);
1839 if (!(context
.glyph_infos
= calloc(glyph_count
, sizeof(*context
.glyph_infos
))))
1840 return E_OUTOFMEMORY
;
1842 props
= &dwritescripts_properties
[sa
.script
];
1844 hr
= shape_check_typographic_feature(&context
, props
->scripttags
, feature
, glyph_count
, glyphs
, feature_applies
);
1846 free(context
.glyph_infos
);
1851 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl
=
1853 dwritetextanalyzer_QueryInterface
,
1854 dwritetextanalyzer_AddRef
,
1855 dwritetextanalyzer_Release
,
1856 dwritetextanalyzer_AnalyzeScript
,
1857 dwritetextanalyzer_AnalyzeBidi
,
1858 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1859 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1860 dwritetextanalyzer_GetGlyphs
,
1861 dwritetextanalyzer_GetGlyphPlacements
,
1862 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1863 dwritetextanalyzer1_ApplyCharacterSpacing
,
1864 dwritetextanalyzer1_GetBaseline
,
1865 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1866 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1867 dwritetextanalyzer1_GetScriptProperties
,
1868 dwritetextanalyzer1_GetTextComplexity
,
1869 dwritetextanalyzer1_GetJustificationOpportunities
,
1870 dwritetextanalyzer1_JustifyGlyphAdvances
,
1871 dwritetextanalyzer1_GetJustifiedGlyphs
,
1872 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1873 dwritetextanalyzer2_GetTypographicFeatures
,
1874 dwritetextanalyzer2_CheckTypographicFeature
1877 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1879 IDWriteTextAnalyzer2
*get_text_analyzer(void)
1881 return &textanalyzer
;
1884 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1886 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1888 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1889 IsEqualIID(riid
, &IID_IUnknown
))
1892 IDWriteNumberSubstitution_AddRef(iface
);
1896 WARN("%s not implemented.\n", debugstr_guid(riid
));
1900 return E_NOINTERFACE
;
1903 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1905 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
1906 ULONG refcount
= InterlockedIncrement(&object
->refcount
);
1908 TRACE("%p, refcount %ld.\n", iface
, refcount
);
1913 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1915 struct dwrite_numbersubstitution
*object
= impl_from_IDWriteNumberSubstitution(iface
);
1916 ULONG refcount
= InterlockedDecrement(&object
->refcount
);
1918 TRACE("%p, refcount %ld.\n", iface
, refcount
);
1922 free(object
->locale
);
1929 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
=
1931 dwritenumbersubstitution_QueryInterface
,
1932 dwritenumbersubstitution_AddRef
,
1933 dwritenumbersubstitution_Release
1936 struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
1938 if (!iface
|| iface
->lpVtbl
!= &numbersubstitutionvtbl
)
1940 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
1943 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1944 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1946 struct dwrite_numbersubstitution
*substitution
;
1950 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1951 return E_INVALIDARG
;
1953 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1954 return E_INVALIDARG
;
1956 if (!(substitution
= calloc(1, sizeof(*substitution
))))
1957 return E_OUTOFMEMORY
;
1959 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1960 substitution
->refcount
= 1;
1961 substitution
->ignore_user_override
= ignore_user_override
;
1962 substitution
->method
= method
;
1963 substitution
->locale
= wcsdup(locale
);
1964 if (locale
&& !substitution
->locale
)
1967 return E_OUTOFMEMORY
;
1970 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1974 /* IDWriteFontFallback */
1975 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback1
*iface
, REFIID riid
, void **obj
)
1977 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
1979 if (IsEqualIID(riid
, &IID_IDWriteFontFallback1
) ||
1980 IsEqualIID(riid
, &IID_IDWriteFontFallback
) ||
1981 IsEqualIID(riid
, &IID_IUnknown
))
1984 IDWriteFontFallback1_AddRef(iface
);
1988 WARN("%s not implemented.\n", debugstr_guid(riid
));
1991 return E_NOINTERFACE
;
1994 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback1
*iface
)
1996 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
1998 TRACE("%p.\n", iface
);
2000 return IDWriteFactory7_AddRef(fallback
->factory
);
2003 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback1
*iface
)
2005 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2007 TRACE("%p.\n", fallback
);
2009 return IDWriteFactory7_Release(fallback
->factory
);
2012 static int __cdecl
compare_mapping_range(const void *a
, const void *b
)
2014 UINT32 ch
= *(UINT32
*)a
;
2015 DWRITE_UNICODE_RANGE
*range
= (DWRITE_UNICODE_RANGE
*)b
;
2017 if (ch
> range
->last
)
2019 else if (ch
< range
->first
)
2025 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
2029 for (i
= 0; i
< fallback
->mappings_count
; i
++) {
2030 struct fallback_mapping
*mapping
= &fallback
->mappings
[i
];
2032 if (bsearch(&ch
, mapping
->ranges
, mapping
->ranges_count
, sizeof(*mapping
->ranges
),
2033 compare_mapping_range
) != NULL
)
2040 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
2041 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
2043 IDWriteFontFamily
*family
;
2044 BOOL exists
= FALSE
;
2050 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
2057 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
2061 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
2062 IDWriteFontFamily_Release(family
);
2066 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
2071 for (i
= 0; i
< length
; i
++) {
2072 UINT16 script
= get_char_script(text
[i
]);
2075 if (script
== Script_Unknown
|| script
== Script_Common
) {
2080 /* stop on first unsupported character */
2082 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
2083 if (hr
== S_OK
&& exists
)
2092 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
2093 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2094 IDWriteFont
**mapped_font
)
2096 const struct fallback_mapping
*mapping
;
2100 *mapped_font
= NULL
;
2102 mapping
= find_fallback_mapping(fallback
, text
[0]);
2104 WARN("No mapping range for %#x.\n", text
[0]);
2108 /* Now let's see what fallback can handle. Pick first font that could be created. */
2109 for (i
= 0; i
< mapping
->families_count
; i
++) {
2110 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->families
[i
],
2111 weight
, style
, stretch
, mapped_font
);
2113 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping
->families
[i
]));
2118 if (!*mapped_font
) {
2119 WARN("Failed to create fallback font.\n");
2123 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
2125 WARN("Mapping with fallback family %s failed, hr %#lx.\n", debugstr_w(mapping
->families
[i
]), hr
);
2127 if (!*mapped_length
) {
2128 IDWriteFont_Release(*mapped_font
);
2129 *mapped_font
= NULL
;
2132 return *mapped_length
? S_OK
: E_FAIL
;
2135 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2136 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2137 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2138 IDWriteFont
**ret_font
, FLOAT
*scale
)
2140 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2145 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2146 debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2153 return E_INVALIDARG
;
2158 if (!basecollection
)
2159 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
2161 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
2165 if (basefamily
&& *basefamily
) {
2166 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
2170 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
2175 if (!*mapped_length
) {
2176 IDWriteFont
*mapped_font
;
2178 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
2180 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2182 *mapped_length
= length
;
2188 IDWriteFont_Release(*ret_font
);
2189 *ret_font
= mapped_font
;
2198 static HRESULT WINAPI
fontfallback1_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2199 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2200 DWRITE_FONT_AXIS_VALUE
const *axis_values
, UINT32 values_count
, UINT32
*mapped_length
, FLOAT
*scale
,
2201 IDWriteFontFace5
**ret_fontface
)
2203 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2204 debugstr_w(basefamily
), axis_values
, values_count
, mapped_length
, scale
, ret_fontface
);
2209 static const IDWriteFontFallback1Vtbl fontfallbackvtbl
=
2211 fontfallback_QueryInterface
,
2212 fontfallback_AddRef
,
2213 fontfallback_Release
,
2214 fontfallback_MapCharacters
,
2215 fontfallback1_MapCharacters
,
2218 HRESULT
create_system_fontfallback(IDWriteFactory7
*factory
, IDWriteFontFallback1
**ret
)
2220 struct dwrite_fontfallback
*fallback
;
2224 if (!(fallback
= calloc(1, sizeof(*fallback
))))
2225 return E_OUTOFMEMORY
;
2227 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &fontfallbackvtbl
;
2228 fallback
->factory
= factory
;
2229 fallback
->mappings
= (struct fallback_mapping
*)fontfallback_neutral_data
;
2230 fallback
->mappings_count
= ARRAY_SIZE(fontfallback_neutral_data
);
2231 IDWriteFactory5_GetSystemFontCollection((IDWriteFactory5
*)fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
2233 *ret
= &fallback
->IDWriteFontFallback1_iface
;
2238 void release_system_fontfallback(IDWriteFontFallback1
*iface
)
2240 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2241 IDWriteFontCollection1_Release(fallback
->systemcollection
);
2245 static ULONG WINAPI
customfontfallback_AddRef(IDWriteFontFallback1
*iface
)
2247 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2248 ULONG refcount
= InterlockedIncrement(&fallback
->refcount
);
2250 TRACE("%p, refcount %lu.\n", iface
, refcount
);
2255 static ULONG WINAPI
customfontfallback_Release(IDWriteFontFallback1
*iface
)
2257 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback1(iface
);
2258 ULONG refcount
= InterlockedDecrement(&fallback
->refcount
);
2260 TRACE("%p, refcount %lu.\n", iface
, refcount
);
2264 IDWriteFactory7_Release(fallback
->factory
);
2271 static HRESULT WINAPI
customfontfallback_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2272 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2273 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2274 IDWriteFont
**ret_font
, FLOAT
*scale
)
2276 FIXME("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface
, source
, position
, length
,
2277 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2282 static HRESULT WINAPI
customfontfallback1_MapCharacters(IDWriteFontFallback1
*iface
, IDWriteTextAnalysisSource
*source
,
2283 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2284 DWRITE_FONT_AXIS_VALUE
const *axis_values
, UINT32 values_count
, UINT32
*mapped_length
, FLOAT
*scale
,
2285 IDWriteFontFace5
**ret_fontface
)
2287 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface
, source
, position
, length
, basecollection
,
2288 debugstr_w(basefamily
), axis_values
, values_count
, mapped_length
, scale
, ret_fontface
);
2293 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl
=
2295 fontfallback_QueryInterface
,
2296 customfontfallback_AddRef
,
2297 customfontfallback_Release
,
2298 customfontfallback_MapCharacters
,
2299 customfontfallback1_MapCharacters
,
2302 static HRESULT WINAPI
fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder
*iface
, REFIID riid
, void **obj
)
2304 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
2306 if (IsEqualIID(riid
, &IID_IDWriteFontFallbackBuilder
) || IsEqualIID(riid
, &IID_IUnknown
)) {
2308 IDWriteFontFallbackBuilder_AddRef(iface
);
2312 WARN("%s not implemented.\n", debugstr_guid(riid
));
2315 return E_NOINTERFACE
;
2318 static ULONG WINAPI
fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder
*iface
)
2320 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2321 ULONG refcount
= InterlockedIncrement(&fallbackbuilder
->refcount
);
2323 TRACE("%p, refcount %ld.\n", iface
, refcount
);
2328 static ULONG WINAPI
fontfallbackbuilder_Release(IDWriteFontFallbackBuilder
*iface
)
2330 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2331 ULONG refcount
= InterlockedDecrement(&fallbackbuilder
->refcount
);
2334 TRACE("%p, refcount %ld.\n", iface
, refcount
);
2338 for (i
= 0; i
< fallbackbuilder
->count
; ++i
)
2340 struct fallback_mapping
*mapping
= &fallbackbuilder
->mappings
[i
];
2343 for (j
= 0; j
< mapping
->families_count
; j
++)
2344 free(mapping
->families
[j
]);
2345 free(mapping
->families
);
2347 if (mapping
->collection
)
2348 IDWriteFontCollection_Release(mapping
->collection
);
2349 free(mapping
->ranges
);
2350 free(mapping
->locale
);
2353 IDWriteFactory7_Release(fallbackbuilder
->factory
);
2354 free(fallbackbuilder
->mappings
);
2355 free(fallbackbuilder
);
2361 static HRESULT WINAPI
fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder
*iface
,
2362 const DWRITE_UNICODE_RANGE
*ranges
, UINT32 ranges_count
, WCHAR
const **target_families
, UINT32 families_count
,
2363 IDWriteFontCollection
*collection
, WCHAR
const *locale
, WCHAR
const *base_family
, FLOAT scale
)
2365 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2366 struct fallback_mapping
*mapping
;
2369 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface
, ranges
, ranges_count
, target_families
, families_count
,
2370 collection
, debugstr_w(locale
), debugstr_w(base_family
), scale
);
2372 if (!ranges
|| ranges_count
== 0 || !target_families
|| families_count
== 0 || scale
< 0.0f
)
2373 return E_INVALIDARG
;
2376 FIXME("base family ignored.\n");
2378 if (!dwrite_array_reserve((void **)&fallbackbuilder
->mappings
, &fallbackbuilder
->size
, fallbackbuilder
->count
+ 1,
2379 sizeof(*fallbackbuilder
->mappings
)))
2381 return E_OUTOFMEMORY
;
2384 mapping
= &fallbackbuilder
->mappings
[fallbackbuilder
->count
++];
2386 mapping
->ranges
= calloc(ranges_count
, sizeof(*mapping
->ranges
));
2387 memcpy(mapping
->ranges
, ranges
, sizeof(*mapping
->ranges
) * ranges_count
);
2388 mapping
->ranges_count
= ranges_count
;
2389 mapping
->families
= calloc(families_count
, sizeof(*mapping
->families
));
2390 mapping
->families_count
= families_count
;
2391 for (i
= 0; i
< families_count
; i
++)
2392 mapping
->families
[i
] = wcsdup(target_families
[i
]);
2393 mapping
->collection
= collection
;
2394 if (mapping
->collection
)
2395 IDWriteFontCollection_AddRef(mapping
->collection
);
2396 mapping
->locale
= wcsdup(locale
);
2397 mapping
->scale
= scale
;
2402 static HRESULT WINAPI
fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder
*iface
, IDWriteFontFallback
*fallback
)
2404 FIXME("%p, %p stub.\n", iface
, fallback
);
2409 static HRESULT WINAPI
fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder
*iface
,
2410 IDWriteFontFallback
**ret
)
2412 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2413 struct dwrite_fontfallback
*fallback
;
2415 TRACE("%p, %p.\n", iface
, ret
);
2419 if (!(fallback
= calloc(1, sizeof(*fallback
))))
2420 return E_OUTOFMEMORY
;
2422 fallback
->IDWriteFontFallback1_iface
.lpVtbl
= &customfontfallbackvtbl
;
2423 fallback
->refcount
= 1;
2424 fallback
->factory
= fallbackbuilder
->factory
;
2425 IDWriteFactory7_AddRef(fallback
->factory
);
2427 *ret
= (IDWriteFontFallback
*)&fallback
->IDWriteFontFallback1_iface
;
2431 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl
=
2433 fontfallbackbuilder_QueryInterface
,
2434 fontfallbackbuilder_AddRef
,
2435 fontfallbackbuilder_Release
,
2436 fontfallbackbuilder_AddMapping
,
2437 fontfallbackbuilder_AddMappings
,
2438 fontfallbackbuilder_CreateFontFallback
,
2441 HRESULT
create_fontfallback_builder(IDWriteFactory7
*factory
, IDWriteFontFallbackBuilder
**ret
)
2443 struct dwrite_fontfallback_builder
*builder
;
2447 if (!(builder
= calloc(1, sizeof(*builder
))))
2448 return E_OUTOFMEMORY
;
2450 builder
->IDWriteFontFallbackBuilder_iface
.lpVtbl
= &fontfallbackbuildervtbl
;
2451 builder
->refcount
= 1;
2452 builder
->factory
= factory
;
2453 IDWriteFactory7_AddRef(builder
->factory
);
2455 *ret
= &builder
->IDWriteFontFallbackBuilder_iface
;