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
{
38 DWRITE_SCRIPT_PROPERTIES props
;
39 UINT32 scripttag
; /* OpenType script tag */
40 UINT32 scriptalttag
; /* Version 2 tag, 0 if not defined */
42 const struct scriptshaping_ops
*ops
;
45 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
47 /* NOTE: keep this array synced with script ids from scripts.h */
48 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
49 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
51 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
52 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE
},
53 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
54 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
55 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
56 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
57 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
58 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE
},
59 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
60 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
61 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE
},
62 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
63 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
64 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE
},
65 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
66 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
67 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE
},
68 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
69 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
70 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
71 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
72 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE
},
73 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE
},
74 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
75 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE
},
76 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
77 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
78 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
79 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
80 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE
},
81 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE
},
82 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
83 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE
},
84 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
85 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE
},
86 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
87 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
88 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
89 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
90 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
91 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
92 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE
},
93 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
94 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
95 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
96 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE
},
97 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE
},
98 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE
, &latn_shaping_ops
},
99 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
100 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
101 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
102 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
103 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
104 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
105 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE
},
106 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
107 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
108 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE
},
109 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE
},
110 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE
},
111 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE
},
112 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE
},
113 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
114 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
115 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE
},
116 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
117 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
118 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE
},
119 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE
},
120 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE
},
121 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
122 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
123 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE
},
124 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
125 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
126 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
127 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE
},
128 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
129 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
130 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE
},
131 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
132 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
133 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE
},
134 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
135 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
136 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE
},
137 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE
},
138 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE
},
139 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE
},
140 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE
},
141 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE
},
142 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
143 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE
},
144 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE
},
145 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','a','k','m') },
146 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','r','c') },
147 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('m','e','r','o') },
148 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('p','l','r','d') },
149 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','h','r','d') },
150 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','o','r','a') },
151 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','k','r') },
152 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','s','s') },
153 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','g','h','b') },
154 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('d','u','p','l') },
155 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','l','b','a') },
156 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','r','a','n') },
157 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','h','o','j') },
158 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','d') },
159 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','a') },
160 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','h','j') },
161 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','i') },
162 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','n','d') },
163 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('m','o','d','i') },
164 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','r','o','o') },
165 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('n','b','a','t') },
166 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','a','r','b') },
167 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','e','r','m') },
168 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','m','n','g') },
169 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('p','a','l','m') },
170 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','a','u','c') },
171 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','l','p') },
172 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, _OT('s','i','d','d') },
173 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('t','i','r','h') },
174 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('w','a','r','a') },
175 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','d','l','m') },
176 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','h','o','m') },
177 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','l','u','w') },
178 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','h','k','s') },
179 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','a','t','r') },
180 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','r','c') },
181 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','u','l','t') },
182 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','e','w','a') },
183 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','u','n','g') },
184 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','g','e') },
185 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','g','n','w') },
186 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','n','g') },
190 const char *debugstr_sa_script(UINT16 script
)
192 return script
< Script_LastId
? debugstr_an((char*)&dwritescripts_properties
[script
].props
.isoScriptCode
, 4):
196 /* system font falback configuration */
197 static const WCHAR meiryoW
[] = {'M','e','i','r','y','o',0};
199 static const WCHAR
*cjk_families
[] = { meiryoW
};
201 static const DWRITE_UNICODE_RANGE cjk_ranges
[] =
203 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
204 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
205 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
208 struct fallback_mapping
{
209 DWRITE_UNICODE_RANGE
*ranges
;
212 UINT32 families_count
;
213 IDWriteFontCollection
*collection
;
218 static const struct fallback_mapping fontfallback_neutral_data
[] = {
219 #define MAPPING_RANGE(ranges, families) \
220 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
221 (WCHAR **)families, ARRAY_SIZE(families) }
223 MAPPING_RANGE(cjk_ranges
, cjk_families
),
228 struct dwrite_fontfallback
{
229 IDWriteFontFallback IDWriteFontFallback_iface
;
231 IDWriteFactory5
*factory
;
232 IDWriteFontCollection1
*systemcollection
;
233 struct fallback_mapping
*mappings
;
234 UINT32 mappings_count
;
237 struct dwrite_fontfallback_builder
{
238 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface
;
240 IDWriteFactory5
*factory
;
241 struct fallback_mapping
*mappings
;
242 UINT32 mappings_count
;
243 UINT32 mappings_capacity
;
246 struct dwrite_numbersubstitution
{
247 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
250 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
252 BOOL ignore_user_override
;
255 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
257 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
260 static struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
);
262 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback(IDWriteFontFallback
*iface
)
264 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback_iface
);
267 static inline struct dwrite_fontfallback_builder
*impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder
*iface
)
269 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback_builder
, IDWriteFontFallbackBuilder_iface
);
272 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
274 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
275 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
276 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
282 static inline UINT16
get_char_script(WCHAR c
)
284 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
285 return script
== Script_Inherited
? Script_Unknown
: script
;
288 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
290 DWRITE_SCRIPT_ANALYSIS sa
;
292 sa
.script
= get_char_script(c
);
293 sa
.shapes
= iscntrlW(c
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
294 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
298 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
300 DWRITE_SCRIPT_ANALYSIS sa
;
301 UINT32 pos
, i
, seq_length
;
306 sa
= get_char_sa(*text
);
311 for (i
= 1; i
< length
; i
++)
313 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
315 /* Unknown type is ignored when preceded or followed by another script */
318 sa
.script
= cur_sa
.script
;
321 if (cur_sa
.script
== Script_Unknown
)
322 cur_sa
.script
= sa
.script
;
323 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
324 sa
.script
= cur_sa
.script
;
327 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
328 cur_sa
.script
= sa
.script
;
331 /* this is a length of a sequence to be reported next */
332 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
337 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
338 if (FAILED(hr
)) return hr
;
345 /* one char length case or normal completion call */
346 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
349 struct linebreaking_state
{
350 DWRITE_LINE_BREAKPOINT
*breakpoints
;
354 enum BreakConditionLocation
{
355 BreakConditionBefore
,
359 enum linebreaking_classes
{
405 static BOOL
has_strong_condition(DWRITE_BREAK_CONDITION old_condition
, DWRITE_BREAK_CONDITION new_condition
)
407 if (old_condition
== DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
|| old_condition
== DWRITE_BREAK_CONDITION_MUST_BREAK
)
410 if (old_condition
== DWRITE_BREAK_CONDITION_CAN_BREAK
&& new_condition
!= DWRITE_BREAK_CONDITION_MUST_BREAK
)
416 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
417 set to "can break" and could only be changed once. */
418 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
419 struct linebreaking_state
*state
)
421 if (location
== BreakConditionBefore
) {
422 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionBefore
, condition
))
424 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
426 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
429 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionAfter
, condition
))
431 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
432 if (pos
+ 1 < state
->count
)
433 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
437 BOOL
lb_is_newline_char(WCHAR ch
)
439 short c
= get_table_entry(wine_linebreak_table
, ch
);
440 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
443 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
445 struct linebreaking_state state
;
449 break_class
= heap_alloc(count
*sizeof(short));
451 return E_OUTOFMEMORY
;
453 state
.breakpoints
= breakpoints
;
456 for (i
= 0; i
< count
; i
++)
458 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
460 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_NEUTRAL
;
461 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_NEUTRAL
;
462 breakpoints
[i
].isWhitespace
= !!isspaceW(text
[i
]);
463 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
464 breakpoints
[i
].padding
= 0;
466 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
467 switch (break_class
[i
])
473 break_class
[i
] = b_AL
;
476 break_class
[i
] = b_NS
;
481 /* LB2 - never break at the start */
482 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
483 /* LB3 - always break at the end. */
484 set_break_condition(count
- 1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
486 /* LB4 - LB6 - mandatory breaks. */
487 for (i
= 0; i
< count
; i
++)
489 switch (break_class
[i
])
493 /* LB5 - don't break CR x LF */
494 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
496 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
497 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
503 /* LB4 - LB5 - always break after hard breaks */
504 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
505 /* LB6 - do not break before hard breaks */
506 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
511 /* LB7 - LB8 - explicit breaks and non-breaks */
512 for (i
= 0; i
< count
; i
++)
514 switch (break_class
[i
])
516 /* LB7 - do not break before spaces */
518 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
521 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
523 /* LB8 - break before character after zero-width space, skip spaces in-between */
525 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
527 if (j
< count
-1 && break_class
[j
+1] != b_ZW
)
528 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
530 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
532 if (i
< count
-1 && (break_class
[i
+1] == b_ID
|| break_class
[i
+1] == b_EB
|| break_class
[i
+1] == b_EM
))
533 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
538 /* LB9 - LB10 - combining marks */
539 for (i
= 0; i
< count
; i
++)
541 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
545 switch (break_class
[i
-1])
553 break_class
[i
] = b_AL
;
557 break_class
[i
] = break_class
[i
-1];
558 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
562 else break_class
[i
] = b_AL
;
566 for (i
= 0; i
< count
; i
++)
568 switch (break_class
[i
])
570 /* LB11 - don't break before and after word joiner */
572 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
573 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
575 /* LB12 - don't break after glue */
577 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
581 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
582 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
591 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
593 /* LB14 - do not break after OP, even after spaces */
596 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
598 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
600 /* LB15 - do not break within QU-OP, even with intervening spaces */
603 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
605 if (j
< count
- 1 && break_class
[j
+1] == b_OP
)
606 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
611 while(j
> 0 && break_class
[j
] == b_SP
)
613 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
614 for (j
++; j
<= i
; j
++)
615 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
617 /* LB17 - do not break within B2, even with intervening spaces */
620 while (j
< count
&& break_class
[j
+1] == b_SP
)
622 if (j
< count
- 1 && break_class
[j
+1] == b_B2
)
623 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
628 for (i
= 0; i
< count
; i
++)
630 switch(break_class
[i
])
632 /* LB18 - break is allowed after space */
634 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
636 /* LB19 - don't break before or after quotation mark */
638 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
639 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
643 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
644 if (i
< count
- 1 && break_class
[i
+1] != b_QU
)
645 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
651 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
654 if (i
< count
- 1 && break_class
[i
+1] != b_CB
)
655 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
661 switch (break_class
[i
+1])
665 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
668 if (i
> 0 && break_class
[i
-1] == b_SY
)
669 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
675 switch (break_class
[i
-1])
685 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
693 /* LB23 - do not break between digits and letters */
694 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
695 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
696 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
697 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
698 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
700 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
701 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
702 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
703 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
704 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
705 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
706 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
707 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
709 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
710 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
711 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
712 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
713 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
714 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
715 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
716 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
717 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
718 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
721 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
722 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
723 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
724 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
725 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
726 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
727 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
728 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
729 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
730 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
731 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
732 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
733 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
734 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
735 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
738 if (break_class
[i
] == b_JL
)
740 switch (break_class
[i
+1])
746 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
749 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
750 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
751 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
752 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
753 break_class
[i
+1] == b_JT
)
754 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
757 switch (break_class
[i
])
764 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
765 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
767 if (break_class
[i
] == b_PR
)
769 switch (break_class
[i
+1])
776 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
781 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
782 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
783 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
784 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
785 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
788 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
789 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
790 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
793 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
794 break_class
[i
+1] == b_OP
)
795 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
796 if (break_class
[i
] == b_CP
&&
797 (break_class
[i
+1] == b_AL
|| break_class
[i
+1] == b_HL
|| break_class
[i
+1] == b_NU
))
798 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
800 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
801 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
805 while (j
> 0 && break_class
[--j
] == b_RI
)
809 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
812 /* LB30b - do not break between an emoji base and an emoji modifier */
813 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
814 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
818 /* LB31 - allow breaks everywhere else. */
819 for (i
= 0; i
< count
; i
++)
821 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
822 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
825 heap_free(break_class
);
829 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
831 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
833 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
834 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
835 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
836 IsEqualIID(riid
, &IID_IUnknown
))
842 WARN("%s not implemented.\n", debugstr_guid(riid
));
845 return E_NOINTERFACE
;
848 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
853 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
858 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
859 data after a first request. */
860 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
868 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
869 if (FAILED(hr
)) return hr
;
874 *buff
= heap_alloc(length
*sizeof(WCHAR
));
876 return E_OUTOFMEMORY
;
877 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
880 while (read
< length
&& *text
) {
883 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
888 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
898 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
899 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
905 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
910 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
914 hr
= analyze_script(text
, position
, length
, sink
);
920 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
921 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
923 UINT8
*levels
= NULL
, *explicit = NULL
;
924 UINT8 baselevel
, level
, explicit_level
;
925 UINT32 pos
, i
, seq_length
;
930 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
935 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
939 levels
= heap_alloc(length
*sizeof(*levels
));
940 explicit = heap_alloc(length
*sizeof(*explicit));
942 if (!levels
|| !explicit) {
947 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
948 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
953 explicit_level
= explicit[0];
957 for (i
= 1; i
< length
; i
++) {
958 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
961 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
968 explicit_level
= explicit[i
];
971 /* one char length case or normal completion call */
972 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
982 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
983 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
988 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
992 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
993 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
995 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
1001 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
1006 /* get some, check for length */
1009 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
1010 if (FAILED(hr
)) return hr
;
1015 buff
= heap_alloc(length
*sizeof(WCHAR
));
1017 return E_OUTOFMEMORY
;
1018 memcpy(buff
, text
, len
*sizeof(WCHAR
));
1021 while (read
< length
&& text
) {
1024 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
1027 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
1034 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
1040 hr
= analyze_linebreaks(text
, length
, breakpoints
);
1044 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
1047 heap_free(breakpoints
);
1053 static UINT32
get_opentype_language(const WCHAR
*locale
)
1055 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
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 DWRITE_NUMBER_SUBSTITUTION_METHOD
get_number_substitutes(IDWriteNumberSubstitution
*substitution
, WCHAR
*digits
)
1068 struct dwrite_numbersubstitution
*numbersubst
= unsafe_impl_from_IDWriteNumberSubstitution(substitution
);
1069 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1074 return DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1076 lctype
= numbersubst
->ignore_user_override
? LOCALE_NOUSEROVERRIDE
: 0;
1078 if (numbersubst
->method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE
) {
1081 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1082 if (GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_IDIGITSUBSTITUTION
| LOCALE_RETURN_NUMBER
, (WCHAR
*)&value
, 2)) {
1086 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
;
1089 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
;
1094 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value
, debugstr_w(numbersubst
->locale
));
1098 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst
->locale
));
1101 method
= numbersubst
->method
;
1106 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
:
1107 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1109 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1110 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
:
1111 if (GetLocaleInfoEx(numbersubst
->locale
, LOCALE_SISO639LANGNAME
, isolang
, ARRAY_SIZE(isolang
))) {
1112 static const WCHAR arW
[] = {'a','r',0};
1113 static const WCHAR arabicW
[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1115 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1116 if (!strcmpW(arW
, isolang
)) {
1117 strcpyW(digits
, arabicW
);
1121 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1127 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !*digits
) {
1128 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst
->locale
), method
);
1129 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1135 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES
const **features
,
1136 UINT32
const *feature_range_lengths
, UINT32 feature_ranges
)
1140 if (!TRACE_ON(dwrite
) || !features
)
1143 for (i
= 0, start
= 0; i
< feature_ranges
; i
++, start
+= feature_range_lengths
[i
]) {
1144 TRACE("feature range [%u,%u)\n", start
, start
+ feature_range_lengths
[i
]);
1145 for (j
= 0; j
< features
[i
]->featureCount
; j
++)
1146 TRACE("feature %s, parameter %u\n", debugstr_an((char *)&features
[i
]->features
[j
].nameTag
, 4),
1147 features
[i
]->features
[j
].parameter
);
1151 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
1152 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
1153 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
1154 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1155 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
1156 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
1157 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
1159 const struct dwritescript_properties
*scriptprops
;
1160 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1161 struct scriptshaping_context context
;
1162 struct scriptshaping_cache
*cache
= NULL
;
1163 BOOL update_cluster
, need_vertical
;
1164 WCHAR digits
[NATIVE_DIGITS_LEN
];
1165 IDWriteFontFace1
*fontface1
;
1171 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
1172 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1173 features
, feature_range_lengths
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
,
1174 glyph_props
, actual_glyph_count
);
1176 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1178 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1180 if (max_glyph_count
< length
)
1181 return E_NOT_SUFFICIENT_BUFFER
;
1183 string
= heap_alloc(sizeof(WCHAR
)*length
);
1185 return E_OUTOFMEMORY
;
1187 method
= get_number_substitutes(substitution
, digits
);
1189 for (i
= 0; i
< length
; i
++) {
1190 /* FIXME: set to better values */
1191 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
1192 glyph_props
[i
].isClusterStart
= 1;
1193 glyph_props
[i
].isDiacritic
= 0;
1194 glyph_props
[i
].isZeroWidthSpace
= 0;
1195 glyph_props
[i
].reserved
= 0;
1197 /* FIXME: have the shaping engine set this */
1198 text_props
[i
].isShapedAlone
= 0;
1199 text_props
[i
].reserved
= 0;
1203 string
[i
] = text
[i
];
1206 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1211 if (string
[i
] >= '0' && string
[i
] <= '9')
1212 string
[i
] = digits
[string
[i
] - '0'];
1214 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
:
1219 for (; i
< max_glyph_count
; i
++) {
1220 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
1221 glyph_props
[i
].isClusterStart
= 0;
1222 glyph_props
[i
].isDiacritic
= 0;
1223 glyph_props
[i
].isZeroWidthSpace
= 0;
1224 glyph_props
[i
].reserved
= 0;
1227 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1229 WARN("failed to get IDWriteFontFace1\n");
1231 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
1233 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
1236 if (!update_cluster
) {
1237 codepoint
= decode_surrogate_pair(string
, i
, length
);
1239 codepoint
= is_rtl
? bidi_get_mirrored_char(string
[i
]) : string
[i
];
1241 update_cluster
= TRUE
;
1243 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
1247 if (need_vertical
) {
1250 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
1252 glyph_indices
[g
] = vertical
;
1260 update_cluster
= FALSE
;
1261 /* mark surrogate halves with same cluster */
1262 clustermap
[i
] = clustermap
[i
-1];
1263 /* update following clusters */
1264 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
1268 *actual_glyph_count
= g
;
1270 hr
= create_scriptshaping_cache(fontface
, &cache
);
1274 context
.cache
= cache
;
1275 context
.text
= text
;
1276 context
.length
= length
;
1277 context
.is_rtl
= is_rtl
;
1278 context
.max_glyph_count
= max_glyph_count
;
1279 context
.language_tag
= get_opentype_language(locale
);
1281 scriptprops
= &dwritescripts_properties
[script
];
1282 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
1283 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1288 /* FIXME: apply default features */
1290 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1291 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1293 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1297 IDWriteFontFace1_Release(fontface1
);
1298 release_scriptshaping_cache(cache
);
1304 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1305 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1306 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1307 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1308 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1309 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1311 DWRITE_FONT_METRICS metrics
;
1312 IDWriteFontFace1
*fontface1
;
1316 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
),
1317 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1318 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_lengths
,
1319 feature_ranges
, advances
, offsets
);
1321 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1323 if (glyph_count
== 0)
1326 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1328 WARN("failed to get IDWriteFontFace1.\n");
1332 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1333 for (i
= 0; i
< glyph_count
; i
++) {
1334 if (glyph_props
[i
].isZeroWidthSpace
)
1339 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1342 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1344 offsets
[i
].advanceOffset
= 0.0f
;
1345 offsets
[i
].ascenderOffset
= 0.0f
;
1348 /* FIXME: actually apply features */
1350 IDWriteFontFace1_Release(fontface1
);
1354 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1355 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1356 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1357 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT ppdip
,
1358 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1359 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1360 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1362 DWRITE_FONT_METRICS metrics
;
1363 IDWriteFontFace1
*fontface1
;
1367 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text
, text_len
),
1368 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1369 transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
),
1370 features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1372 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1374 if (glyph_count
== 0)
1377 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1379 WARN("failed to get IDWriteFontFace1.\n");
1383 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1385 IDWriteFontFace1_Release(fontface1
);
1386 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1389 for (i
= 0; i
< glyph_count
; i
++) {
1392 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1393 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1397 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1398 offsets
[i
].advanceOffset
= 0.0f
;
1399 offsets
[i
].ascenderOffset
= 0.0f
;
1402 /* FIXME: actually apply features */
1404 IDWriteFontFace1_Release(fontface1
);
1408 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1410 FLOAT advance
= 0.0f
;
1411 for (; start
< end
; start
++)
1412 advance
+= advances
[start
];
1416 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1417 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1418 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1420 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1421 FLOAT advance
= advances
[g
];
1422 FLOAT origin
= 0.0f
;
1424 if (props
[g
].isZeroWidthSpace
) {
1425 modified_advances
[g
] = advances
[g
];
1426 modified_offsets
[g
] = offsets
[g
];
1430 /* first apply negative spacing and check if we hit minimum width */
1431 if (leading_spacing
< 0.0f
) {
1432 advance
+= leading_spacing
;
1433 origin
-= leading_spacing
;
1435 if (trailing_spacing
< 0.0f
)
1436 advance
+= trailing_spacing
;
1438 if (advance
< min_advance_width
) {
1439 FLOAT half
= (min_advance_width
- advance
) / 2.0f
;
1443 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1445 else if (leading_spacing
< 0.0f
)
1446 origin
-= min_advance_width
- advance
;
1448 advance
= min_advance_width
;
1451 /* now apply positive spacing adjustments */
1452 if (leading_spacing
> 0.0f
) {
1453 advance
+= leading_spacing
;
1454 origin
-= leading_spacing
;
1456 if (trailing_spacing
> 0.0f
)
1457 advance
+= trailing_spacing
;
1459 modified_advances
[g
] = advance
;
1460 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1461 /* ascender is never touched, it's orthogonal to reading direction and is not
1462 affected by advance adjustments */
1463 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1466 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1467 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1468 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1470 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1471 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1472 FLOAT origin
= 0.0f
;
1475 modified_advances
[start
] = advances
[start
];
1476 modified_advances
[end
-1] = advances
[end
-1];
1478 /* first apply negative spacing and check if we hit minimum width */
1479 if (leading_spacing
< 0.0f
) {
1480 advance
+= leading_spacing
;
1481 modified_advances
[start
] += leading_spacing
;
1482 origin
-= leading_spacing
;
1484 if (trailing_spacing
< 0.0f
) {
1485 advance
+= trailing_spacing
;
1486 modified_advances
[end
-1] += trailing_spacing
;
1489 advance
= min_advance_width
- advance
;
1490 if (advance
> 0.0f
) {
1491 /* additional spacing is only applied to leading and trailing glyph */
1492 FLOAT half
= advance
/ 2.0f
;
1496 modified_advances
[start
] += half
;
1497 modified_advances
[end
-1] += half
;
1499 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
) {
1501 modified_advances
[start
] += half
;
1502 modified_advances
[end
-1] += half
;
1504 else if (leading_spacing
< 0.0f
) {
1506 modified_advances
[start
] += advance
;
1509 modified_advances
[end
-1] += advance
;
1512 /* now apply positive spacing adjustments */
1513 if (leading_spacing
> 0.0f
) {
1514 modified_advances
[start
] += leading_spacing
;
1515 origin
-= leading_spacing
;
1517 if (trailing_spacing
> 0.0f
)
1518 modified_advances
[end
-1] += trailing_spacing
;
1520 for (g
= start
; g
< end
; g
++) {
1522 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1523 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1525 else if (g
== end
- 1)
1526 /* trailing glyph offset is not adjusted */
1527 modified_offsets
[g
] = offsets
[g
];
1529 /* for all glyphs within a cluster use original advances and offsets */
1530 modified_advances
[g
] = advances
[g
];
1531 modified_offsets
[g
] = offsets
[g
];
1536 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1538 UINT16 g
= clustermap
[start
];
1541 while (start
< text_len
&& clustermap
[++start
] == g
)
1546 /* Applies spacing adjustments to clusters.
1548 Adjustments are applied in the following order:
1550 1. Negative adjustments
1552 Leading and trailing spacing could be negative, at this step
1553 only negative ones are actually applied. Leading spacing is only
1554 applied to leading glyph, trailing - to trailing glyph.
1556 2. Minimum advance width
1558 Advances could only be reduced at this point or unchanged. In any
1559 case it's checked if cluster advance width is less than minimum width.
1560 If it's the case advance width is incremented up to minimum value.
1562 Important part is the direction in which this increment is applied;
1563 it depends on direction from which total cluster advance was trimmed
1564 at step 1. So it could be incremented from leading, trailing, or both
1565 sides. When applied to both sides, each side gets half of difference
1566 that brings advance to minimum width.
1568 3. Positive adjustments
1570 After minimum width rule was applied, positive spacing is applied in the same
1571 way as negative one on step 1.
1573 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1574 keeps its position in coordinate system where initial advance width is counted
1579 It's known that isZeroWidthSpace property keeps initial advance from changing.
1581 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1582 with more than one glyph.
1585 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1586 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1587 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1588 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1592 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1593 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1595 if (min_advance_width
< 0.0f
) {
1596 if (modified_advances
!= advances
)
1597 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1598 return E_INVALIDARG
;
1601 /* minimum advance is not applied if no adjustments were made */
1602 if (leading_spacing
== 0.0f
&& trailing_spacing
== 0.0f
) {
1603 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1604 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1608 for (start
= 0; start
< len
;) {
1609 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1612 UINT32 g
= clustermap
[start
];
1614 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1615 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1618 UINT32 g_start
, g_end
;
1620 g_start
= clustermap
[start
];
1621 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1623 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1624 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1633 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1634 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1635 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1637 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1638 baseline_coord
, exists
);
1642 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1643 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1645 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1649 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1650 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1652 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1653 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1656 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1657 DWRITE_SCRIPT_PROPERTIES
*props
)
1659 TRACE("(%u %p)\n", sa
.script
, props
);
1661 if (sa
.script
> Script_LastId
)
1662 return E_INVALIDARG
;
1664 *props
= dwritescripts_properties
[sa
.script
].props
;
1668 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1670 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
) ||
1671 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1672 c
== 0x200e || c
== 0x200f || (c
>= 0x202a && c
<= 0x202e))
1675 UINT16 script
= get_char_script(c
);
1676 return !dwritescripts_properties
[script
].is_complex
;
1680 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1681 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1686 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1692 return E_INVALIDARG
;
1699 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1700 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1701 if (is_char_from_simple_script(text
[i
])) {
1712 if (*is_simple
&& indices
) {
1713 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1715 return E_OUTOFMEMORY
;
1717 for (i
= 0; i
< *len_read
; i
++)
1718 codepoints
[i
] = text
[i
];
1720 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1721 heap_free(codepoints
);
1727 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1728 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1729 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1731 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1732 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1736 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1737 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1738 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1740 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1745 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1746 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1747 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1748 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1749 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1750 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1752 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
,
1753 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1754 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1758 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1759 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1761 static const DWRITE_MATRIX transforms
[] = {
1762 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1763 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1764 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1765 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1768 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1770 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1771 memset(m
, 0, sizeof(*m
));
1772 return E_INVALIDARG
;
1775 /* for sideways case simply rotate 90 degrees more */
1778 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1779 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1781 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1782 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1784 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1785 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1787 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1788 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1795 *m
= transforms
[angle
];
1797 /* shift components represent transform necessary to get from original point to
1798 rotated one in new coordinate system */
1799 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1800 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1801 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1807 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1808 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1809 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1811 const struct dwritescript_properties
*props
;
1815 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1818 if (sa
.script
> Script_LastId
)
1819 return E_INVALIDARG
;
1821 language
= get_opentype_language(locale
);
1822 props
= &dwritescripts_properties
[sa
.script
];
1823 *actual_tagcount
= 0;
1825 if (props
->scriptalttag
)
1826 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1828 if (*actual_tagcount
== 0)
1829 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1834 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1835 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1836 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1838 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1843 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1844 dwritetextanalyzer_QueryInterface
,
1845 dwritetextanalyzer_AddRef
,
1846 dwritetextanalyzer_Release
,
1847 dwritetextanalyzer_AnalyzeScript
,
1848 dwritetextanalyzer_AnalyzeBidi
,
1849 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1850 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1851 dwritetextanalyzer_GetGlyphs
,
1852 dwritetextanalyzer_GetGlyphPlacements
,
1853 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1854 dwritetextanalyzer1_ApplyCharacterSpacing
,
1855 dwritetextanalyzer1_GetBaseline
,
1856 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1857 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1858 dwritetextanalyzer1_GetScriptProperties
,
1859 dwritetextanalyzer1_GetTextComplexity
,
1860 dwritetextanalyzer1_GetJustificationOpportunities
,
1861 dwritetextanalyzer1_JustifyGlyphAdvances
,
1862 dwritetextanalyzer1_GetJustifiedGlyphs
,
1863 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1864 dwritetextanalyzer2_GetTypographicFeatures
,
1865 dwritetextanalyzer2_CheckTypographicFeature
1868 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1870 IDWriteTextAnalyzer
*get_text_analyzer(void)
1872 return (IDWriteTextAnalyzer
*)&textanalyzer
;
1875 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1877 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1879 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1881 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1882 IsEqualIID(riid
, &IID_IUnknown
))
1885 IDWriteNumberSubstitution_AddRef(iface
);
1889 WARN("%s not implemented.\n", debugstr_guid(riid
));
1893 return E_NOINTERFACE
;
1896 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1898 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1899 ULONG ref
= InterlockedIncrement(&This
->ref
);
1900 TRACE("(%p)->(%d)\n", This
, ref
);
1904 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1906 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1907 ULONG ref
= InterlockedDecrement(&This
->ref
);
1909 TRACE("(%p)->(%d)\n", This
, ref
);
1912 heap_free(This
->locale
);
1919 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1920 dwritenumbersubstitution_QueryInterface
,
1921 dwritenumbersubstitution_AddRef
,
1922 dwritenumbersubstitution_Release
1925 struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
1927 if (!iface
|| iface
->lpVtbl
!= &numbersubstitutionvtbl
)
1929 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
1932 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1933 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1935 struct dwrite_numbersubstitution
*substitution
;
1939 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1940 return E_INVALIDARG
;
1942 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1943 return E_INVALIDARG
;
1945 substitution
= heap_alloc(sizeof(*substitution
));
1947 return E_OUTOFMEMORY
;
1949 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1950 substitution
->ref
= 1;
1951 substitution
->ignore_user_override
= ignore_user_override
;
1952 substitution
->method
= method
;
1953 substitution
->locale
= heap_strdupW(locale
);
1954 if (locale
&& !substitution
->locale
) {
1955 heap_free(substitution
);
1956 return E_OUTOFMEMORY
;
1959 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1963 /* IDWriteFontFallback */
1964 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback
*iface
, REFIID riid
, void **obj
)
1966 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1968 TRACE("(%p)->(%s %p)\n", fallback
, debugstr_guid(riid
), obj
);
1970 if (IsEqualIID(riid
, &IID_IDWriteFontFallback
) || IsEqualIID(riid
, &IID_IUnknown
)) {
1972 IDWriteFontFallback_AddRef(iface
);
1976 WARN("%s not implemented.\n", debugstr_guid(riid
));
1979 return E_NOINTERFACE
;
1982 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback
*iface
)
1984 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1985 TRACE("(%p)\n", fallback
);
1986 return IDWriteFactory5_AddRef(fallback
->factory
);
1989 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback
*iface
)
1991 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1992 TRACE("(%p)\n", fallback
);
1993 return IDWriteFactory5_Release(fallback
->factory
);
1996 static int compare_mapping_range(const void *a
, const void *b
)
1998 UINT32 ch
= *(UINT32
*)a
;
1999 DWRITE_UNICODE_RANGE
*range
= (DWRITE_UNICODE_RANGE
*)b
;
2001 if (ch
> range
->last
)
2003 else if (ch
< range
->first
)
2009 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
2013 for (i
= 0; i
< fallback
->mappings_count
; i
++) {
2014 struct fallback_mapping
*mapping
= &fallback
->mappings
[i
];
2016 if (bsearch(&ch
, mapping
->ranges
, mapping
->ranges_count
, sizeof(*mapping
->ranges
),
2017 compare_mapping_range
) != NULL
)
2024 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
2025 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
2027 IDWriteFontFamily
*family
;
2028 BOOL exists
= FALSE
;
2034 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
2041 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
2045 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
2046 IDWriteFontFamily_Release(family
);
2050 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
2055 for (i
= 0; i
< length
; i
++) {
2056 UINT16 script
= get_char_script(text
[i
]);
2059 if (script
== Script_Unknown
|| script
== Script_Common
) {
2064 /* stop on first unsupported character */
2066 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
2067 if (hr
== S_OK
&& exists
)
2076 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
2077 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2078 IDWriteFont
**mapped_font
)
2080 const struct fallback_mapping
*mapping
;
2084 *mapped_font
= NULL
;
2086 mapping
= find_fallback_mapping(fallback
, text
[0]);
2088 WARN("No mapping range for %#x.\n", text
[0]);
2092 /* Now let's see what fallback can handle. Pick first font that could be created. */
2093 for (i
= 0; i
< mapping
->families_count
; i
++) {
2094 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->families
[i
],
2095 weight
, style
, stretch
, mapped_font
);
2097 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping
->families
[i
]));
2102 if (!*mapped_font
) {
2103 WARN("Failed to create fallback font.\n");
2107 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
2109 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping
->families
[i
]), hr
);
2111 if (!*mapped_length
) {
2112 IDWriteFont_Release(*mapped_font
);
2113 *mapped_font
= NULL
;
2116 return *mapped_length
? S_OK
: E_FAIL
;
2119 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
2120 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2121 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2122 IDWriteFont
**ret_font
, FLOAT
*scale
)
2124 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2129 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
2130 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2137 return E_INVALIDARG
;
2142 if (!basecollection
)
2143 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
2145 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
2149 if (basefamily
&& *basefamily
) {
2150 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
2154 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
2159 if (!*mapped_length
) {
2160 IDWriteFont
*mapped_font
;
2162 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
2164 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2166 *mapped_length
= length
;
2172 IDWriteFont_Release(*ret_font
);
2173 *ret_font
= mapped_font
;
2182 static const IDWriteFontFallbackVtbl fontfallbackvtbl
= {
2183 fontfallback_QueryInterface
,
2184 fontfallback_AddRef
,
2185 fontfallback_Release
,
2186 fontfallback_MapCharacters
2189 HRESULT
create_system_fontfallback(IDWriteFactory5
*factory
, IDWriteFontFallback
**ret
)
2191 struct dwrite_fontfallback
*fallback
;
2195 fallback
= heap_alloc(sizeof(*fallback
));
2197 return E_OUTOFMEMORY
;
2199 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &fontfallbackvtbl
;
2200 fallback
->factory
= factory
;
2201 fallback
->mappings
= (struct fallback_mapping
*)fontfallback_neutral_data
;
2202 fallback
->mappings_count
= ARRAY_SIZE(fontfallback_neutral_data
);
2203 IDWriteFactory5_GetSystemFontCollection(fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
2205 *ret
= &fallback
->IDWriteFontFallback_iface
;
2209 void release_system_fontfallback(IDWriteFontFallback
*iface
)
2211 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2212 IDWriteFontCollection1_Release(fallback
->systemcollection
);
2213 heap_free(fallback
);
2216 static ULONG WINAPI
customfontfallback_AddRef(IDWriteFontFallback
*iface
)
2218 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2219 ULONG ref
= InterlockedIncrement(&fallback
->ref
);
2220 TRACE("(%p)->(%d)\n", fallback
, ref
);
2224 static ULONG WINAPI
customfontfallback_Release(IDWriteFontFallback
*iface
)
2226 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2227 ULONG ref
= InterlockedDecrement(&fallback
->ref
);
2229 TRACE("(%p)->(%d)\n", fallback
, ref
);
2232 IDWriteFactory5_Release(fallback
->factory
);
2233 heap_free(fallback
);
2239 static HRESULT WINAPI
customfontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
2240 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2241 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2242 IDWriteFont
**ret_font
, FLOAT
*scale
)
2244 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2246 FIXME("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
2247 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2252 static const IDWriteFontFallbackVtbl customfontfallbackvtbl
=
2254 fontfallback_QueryInterface
,
2255 customfontfallback_AddRef
,
2256 customfontfallback_Release
,
2257 customfontfallback_MapCharacters
,
2260 static HRESULT WINAPI
fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder
*iface
, REFIID riid
, void **obj
)
2262 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2264 TRACE("(%p)->(%s %p)\n", fallbackbuilder
, debugstr_guid(riid
), obj
);
2266 if (IsEqualIID(riid
, &IID_IDWriteFontFallbackBuilder
) || IsEqualIID(riid
, &IID_IUnknown
)) {
2268 IDWriteFontFallbackBuilder_AddRef(iface
);
2272 WARN("%s not implemented.\n", debugstr_guid(riid
));
2275 return E_NOINTERFACE
;
2278 static ULONG WINAPI
fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder
*iface
)
2280 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2281 ULONG ref
= InterlockedIncrement(&fallbackbuilder
->ref
);
2282 TRACE("(%p)->(%d)\n", fallbackbuilder
, ref
);
2286 static ULONG WINAPI
fontfallbackbuilder_Release(IDWriteFontFallbackBuilder
*iface
)
2288 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2289 ULONG ref
= InterlockedDecrement(&fallbackbuilder
->ref
);
2291 TRACE("(%p)->(%d)\n", fallbackbuilder
, ref
);
2296 for (i
= 0; i
< fallbackbuilder
->mappings_count
; i
++) {
2297 struct fallback_mapping
*mapping
= &fallbackbuilder
->mappings
[i
];
2300 for (j
= 0; j
< mapping
->families_count
; j
++)
2301 heap_free(mapping
->families
[j
]);
2302 heap_free(mapping
->families
);
2304 if (mapping
->collection
)
2305 IDWriteFontCollection_Release(mapping
->collection
);
2306 heap_free(mapping
->ranges
);
2307 heap_free(mapping
->locale
);
2310 IDWriteFactory5_Release(fallbackbuilder
->factory
);
2311 heap_free(fallbackbuilder
->mappings
);
2312 heap_free(fallbackbuilder
);
2318 static HRESULT WINAPI
fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder
*iface
,
2319 const DWRITE_UNICODE_RANGE
*ranges
, UINT32 ranges_count
, WCHAR
const **target_families
, UINT32 families_count
,
2320 IDWriteFontCollection
*collection
, WCHAR
const *locale
, WCHAR
const *base_family
, FLOAT scale
)
2322 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2323 struct fallback_mapping
*mapping
;
2326 TRACE("(%p)->(%p, %u, %p, %u, %p, %s, %s, %f)\n", fallbackbuilder
, ranges
, ranges_count
, target_families
,
2327 families_count
, collection
, debugstr_w(locale
), debugstr_w(base_family
), scale
);
2329 if (!ranges
|| ranges_count
== 0 || !target_families
|| families_count
== 0 || scale
< 0.0f
)
2330 return E_INVALIDARG
;
2333 FIXME("base family ignored.\n");
2335 if (fallbackbuilder
->mappings_count
== fallbackbuilder
->mappings_capacity
) {
2336 struct fallback_mapping
*mappings
;
2338 if (fallbackbuilder
->mappings_capacity
== 0) {
2339 if ((mappings
= heap_alloc(sizeof(*fallbackbuilder
->mappings
) * 16)))
2340 fallbackbuilder
->mappings_capacity
= 16;
2343 if ((mappings
= heap_realloc(fallbackbuilder
->mappings
, sizeof(*fallbackbuilder
->mappings
) *
2344 fallbackbuilder
->mappings_capacity
* 2)))
2345 fallbackbuilder
->mappings_capacity
*= 2;
2348 return E_OUTOFMEMORY
;
2350 fallbackbuilder
->mappings
= mappings
;
2353 mapping
= &fallbackbuilder
->mappings
[fallbackbuilder
->mappings_count
++];
2355 mapping
->ranges
= heap_alloc(sizeof(*mapping
->ranges
) * ranges_count
);
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 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2375 FIXME("(%p)->(%p): stub\n", fallbackbuilder
, fallback
);
2380 static HRESULT WINAPI
fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder
*iface
,
2381 IDWriteFontFallback
**ret
)
2383 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2384 struct dwrite_fontfallback
*fallback
;
2386 FIXME("(%p)->(%p): stub\n", fallbackbuilder
, ret
);
2390 fallback
= heap_alloc(sizeof(*fallback
));
2392 return E_OUTOFMEMORY
;
2394 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &customfontfallbackvtbl
;
2396 fallback
->factory
= fallbackbuilder
->factory
;
2397 IDWriteFactory5_AddRef(fallback
->factory
);
2399 *ret
= &fallback
->IDWriteFontFallback_iface
;
2403 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl
=
2405 fontfallbackbuilder_QueryInterface
,
2406 fontfallbackbuilder_AddRef
,
2407 fontfallbackbuilder_Release
,
2408 fontfallbackbuilder_AddMapping
,
2409 fontfallbackbuilder_AddMappings
,
2410 fontfallbackbuilder_CreateFontFallback
,
2413 HRESULT
create_fontfallback_builder(IDWriteFactory5
*factory
, IDWriteFontFallbackBuilder
**ret
)
2415 struct dwrite_fontfallback_builder
*builder
;
2419 builder
= heap_alloc_zero(sizeof(*builder
));
2421 return E_OUTOFMEMORY
;
2423 builder
->IDWriteFontFallbackBuilder_iface
.lpVtbl
= &fontfallbackbuildervtbl
;
2425 builder
->factory
= factory
;
2426 IDWriteFactory5_AddRef(builder
->factory
);
2428 *ret
= &builder
->IDWriteFontFallbackBuilder_iface
;