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): "not defined";
195 /* system font falback configuration */
196 static const WCHAR meiryoW
[] = {'M','e','i','r','y','o',0};
198 struct fallback_mapping
{
199 DWRITE_UNICODE_RANGE range
;
203 static const struct fallback_mapping fontfallback_neutral_data
[] = {
204 { { 0x3000, 0x30ff }, meiryoW
}, /* CJK Symbols and Punctuation, Hiragana, Katakana */
205 { { 0x31f0, 0x31ff }, meiryoW
}, /* Katakana Phonetic Extensions */
206 { { 0x4e00, 0x9fff }, meiryoW
}, /* CJK Unified Ideographs */
209 struct dwrite_fontfallback
{
210 IDWriteFontFallback IDWriteFontFallback_iface
;
211 IDWriteFactory5
*factory
;
212 IDWriteFontCollection1
*systemcollection
;
213 const struct fallback_mapping
*mappings
;
217 struct dwrite_numbersubstitution
{
218 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
221 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
223 BOOL ignore_user_override
;
226 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
228 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
231 static struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
);
233 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback(IDWriteFontFallback
*iface
)
235 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback_iface
);
238 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
240 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
241 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
242 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
248 static inline UINT16
get_char_script(WCHAR c
)
250 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
251 return script
== Script_Inherited
? Script_Unknown
: script
;
254 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
256 DWRITE_SCRIPT_ANALYSIS sa
;
258 sa
.script
= get_char_script(c
);
259 sa
.shapes
= iscntrlW(c
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
260 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
264 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
266 DWRITE_SCRIPT_ANALYSIS sa
;
267 UINT32 pos
, i
, seq_length
;
272 sa
= get_char_sa(*text
);
277 for (i
= 1; i
< length
; i
++)
279 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
281 /* Unknown type is ignored when preceded or followed by another script */
284 sa
.script
= cur_sa
.script
;
287 if (cur_sa
.script
== Script_Unknown
)
288 cur_sa
.script
= sa
.script
;
289 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
290 sa
.script
= cur_sa
.script
;
293 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
294 cur_sa
.script
= sa
.script
;
297 /* this is a length of a sequence to be reported next */
298 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
303 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
304 if (FAILED(hr
)) return hr
;
311 /* one char length case or normal completion call */
312 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
315 struct linebreaking_state
{
316 DWRITE_LINE_BREAKPOINT
*breakpoints
;
320 enum BreakConditionLocation
{
321 BreakConditionBefore
,
325 enum linebreaking_classes
{
371 static BOOL
has_strong_condition(DWRITE_BREAK_CONDITION old_condition
, DWRITE_BREAK_CONDITION new_condition
)
373 if (old_condition
== DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
|| old_condition
== DWRITE_BREAK_CONDITION_MUST_BREAK
)
376 if (old_condition
== DWRITE_BREAK_CONDITION_CAN_BREAK
&& new_condition
!= DWRITE_BREAK_CONDITION_MUST_BREAK
)
382 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
383 set to "can break" and could only be changed once. */
384 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
385 struct linebreaking_state
*state
)
387 if (location
== BreakConditionBefore
) {
388 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionBefore
, condition
))
390 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
392 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
395 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionAfter
, condition
))
397 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
398 if (pos
+ 1 < state
->count
)
399 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
403 BOOL
lb_is_newline_char(WCHAR ch
)
405 short c
= get_table_entry(wine_linebreak_table
, ch
);
406 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
409 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
411 struct linebreaking_state state
;
415 break_class
= heap_alloc(count
*sizeof(short));
417 return E_OUTOFMEMORY
;
419 state
.breakpoints
= breakpoints
;
422 for (i
= 0; i
< count
; i
++)
424 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
426 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_NEUTRAL
;
427 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_NEUTRAL
;
428 breakpoints
[i
].isWhitespace
= !!isspaceW(text
[i
]);
429 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
430 breakpoints
[i
].padding
= 0;
432 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
433 switch (break_class
[i
])
439 break_class
[i
] = b_AL
;
442 break_class
[i
] = b_NS
;
447 /* LB2 - never break at the start */
448 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
449 /* LB3 - always break at the end. */
450 set_break_condition(count
- 1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
452 /* LB4 - LB6 - mandatory breaks. */
453 for (i
= 0; i
< count
; i
++)
455 switch (break_class
[i
])
459 /* LB5 - don't break CR x LF */
460 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
462 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
463 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
469 /* LB4 - LB5 - always break after hard breaks */
470 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
471 /* LB6 - do not break before hard breaks */
472 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
477 /* LB7 - LB8 - explicit breaks and non-breaks */
478 for (i
= 0; i
< count
; i
++)
480 switch (break_class
[i
])
482 /* LB7 - do not break before spaces */
484 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
487 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
489 /* LB8 - break before character after zero-width space, skip spaces in-between */
491 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
493 if (j
< count
-1 && break_class
[j
+1] != b_ZW
)
494 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
496 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
498 if (i
< count
-1 && (break_class
[i
+1] == b_ID
|| break_class
[i
+1] == b_EB
|| break_class
[i
+1] == b_EM
))
499 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
504 /* LB9 - LB10 - combining marks */
505 for (i
= 0; i
< count
; i
++)
507 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
511 switch (break_class
[i
-1])
519 break_class
[i
] = b_AL
;
523 break_class
[i
] = break_class
[i
-1];
524 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
528 else break_class
[i
] = b_AL
;
532 for (i
= 0; i
< count
; i
++)
534 switch (break_class
[i
])
536 /* LB11 - don't break before and after word joiner */
538 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
539 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
541 /* LB12 - don't break after glue */
543 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
547 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
548 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
557 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
559 /* LB14 - do not break after OP, even after spaces */
562 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
564 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
566 /* LB15 - do not break within QU-OP, even with intervening spaces */
569 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
571 if (j
< count
- 1 && break_class
[j
+1] == b_OP
)
572 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
577 while(j
> 0 && break_class
[j
] == b_SP
)
579 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
580 for (j
++; j
<= i
; j
++)
581 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
583 /* LB17 - do not break within B2, even with intervening spaces */
586 while (j
< count
&& break_class
[j
+1] == b_SP
)
588 if (j
< count
- 1 && break_class
[j
+1] == b_B2
)
589 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
594 for (i
= 0; i
< count
; i
++)
596 switch(break_class
[i
])
598 /* LB18 - break is allowed after space */
600 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
602 /* LB19 - don't break before or after quotation mark */
604 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
605 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
609 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
610 if (i
< count
- 1 && break_class
[i
+1] != b_QU
)
611 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
617 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
620 if (i
< count
- 1 && break_class
[i
+1] != b_CB
)
621 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
627 switch (break_class
[i
+1])
631 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
634 if (i
> 0 && break_class
[i
-1] == b_SY
)
635 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
641 switch (break_class
[i
-1])
651 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
659 /* LB23 - do not break between digits and letters */
660 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
661 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
662 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
663 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
664 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
666 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
667 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
668 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
669 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
670 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
671 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
672 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
673 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
675 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
676 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
677 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
678 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
679 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
680 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
681 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
682 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
683 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
684 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
687 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
688 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
689 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
690 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
691 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
692 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
693 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
694 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
695 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
696 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
697 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
698 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
699 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
700 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
701 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
704 if (break_class
[i
] == b_JL
)
706 switch (break_class
[i
+1])
712 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
715 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
716 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
717 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
718 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
719 break_class
[i
+1] == b_JT
)
720 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
723 switch (break_class
[i
])
730 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
731 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
733 if (break_class
[i
] == b_PR
)
735 switch (break_class
[i
+1])
742 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
747 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
748 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
749 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
750 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
751 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
754 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
755 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
756 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
759 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
760 break_class
[i
+1] == b_OP
)
761 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
762 if (break_class
[i
] == b_CP
&&
763 (break_class
[i
+1] == b_AL
|| break_class
[i
+1] == b_HL
|| break_class
[i
+1] == b_NU
))
764 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
766 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
767 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
771 while (j
> 0 && break_class
[--j
] == b_RI
)
775 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
778 /* LB30b - do not break between an emoji base and an emoji modifier */
779 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
780 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
784 /* LB31 - allow breaks everywhere else. */
785 for (i
= 0; i
< count
; i
++)
787 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
788 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
791 heap_free(break_class
);
795 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
797 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
799 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
800 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
801 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
802 IsEqualIID(riid
, &IID_IUnknown
))
809 return E_NOINTERFACE
;
812 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
817 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
822 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
823 data after a first request. */
824 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
832 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
833 if (FAILED(hr
)) return hr
;
838 *buff
= heap_alloc(length
*sizeof(WCHAR
));
840 return E_OUTOFMEMORY
;
841 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
844 while (read
< length
&& *text
) {
847 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
852 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
862 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
863 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
869 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
874 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
878 hr
= analyze_script(text
, position
, length
, sink
);
884 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
885 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
887 UINT8
*levels
= NULL
, *explicit = NULL
;
888 UINT8 baselevel
, level
, explicit_level
;
889 UINT32 pos
, i
, seq_length
;
894 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
899 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
903 levels
= heap_alloc(length
*sizeof(*levels
));
904 explicit = heap_alloc(length
*sizeof(*explicit));
906 if (!levels
|| !explicit) {
911 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
912 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
917 explicit_level
= explicit[0];
921 for (i
= 1; i
< length
; i
++) {
922 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
925 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
932 explicit_level
= explicit[i
];
935 /* one char length case or normal completion call */
936 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
946 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
947 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
952 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
956 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
957 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
959 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
965 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
970 /* get some, check for length */
973 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
974 if (FAILED(hr
)) return hr
;
979 buff
= heap_alloc(length
*sizeof(WCHAR
));
981 return E_OUTOFMEMORY
;
982 memcpy(buff
, text
, len
*sizeof(WCHAR
));
985 while (read
< length
&& text
) {
988 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
991 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
998 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
1004 hr
= analyze_linebreaks(text
, length
, breakpoints
);
1008 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
1011 heap_free(breakpoints
);
1017 static UINT32
get_opentype_language(const WCHAR
*locale
)
1019 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
1023 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
1024 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
1030 static DWRITE_NUMBER_SUBSTITUTION_METHOD
get_number_substitutes(IDWriteNumberSubstitution
*substitution
, WCHAR
*digits
)
1032 struct dwrite_numbersubstitution
*numbersubst
= unsafe_impl_from_IDWriteNumberSubstitution(substitution
);
1033 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1038 return DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1040 lctype
= numbersubst
->ignore_user_override
? LOCALE_NOUSEROVERRIDE
: 0;
1042 if (numbersubst
->method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE
) {
1045 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1046 if (GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_IDIGITSUBSTITUTION
| LOCALE_RETURN_NUMBER
, (WCHAR
*)&value
, 2)) {
1050 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
;
1053 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
;
1058 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value
, debugstr_w(numbersubst
->locale
));
1062 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst
->locale
));
1065 method
= numbersubst
->method
;
1070 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
:
1071 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1073 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1074 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
:
1075 if (GetLocaleInfoEx(numbersubst
->locale
, LOCALE_SISO639LANGNAME
, isolang
, sizeof(isolang
)/sizeof(isolang
[0]))) {
1076 static const WCHAR arW
[] = {'a','r',0};
1077 static const WCHAR arabicW
[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1079 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1080 if (!strcmpW(arW
, isolang
)) {
1081 strcpyW(digits
, arabicW
);
1085 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1091 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !*digits
) {
1092 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst
->locale
), method
);
1093 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1099 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
1100 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
1101 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
1102 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1103 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
1104 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
1105 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
1107 const struct dwritescript_properties
*scriptprops
;
1108 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1109 struct scriptshaping_context context
;
1110 struct scriptshaping_cache
*cache
= NULL
;
1111 BOOL update_cluster
, need_vertical
;
1112 WCHAR digits
[NATIVE_DIGITS_LEN
];
1113 IDWriteFontFace1
*fontface1
;
1119 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
1120 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1121 features
, feature_range_len
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
,
1122 glyph_props
, actual_glyph_count
);
1124 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1126 if (max_glyph_count
< length
)
1127 return E_NOT_SUFFICIENT_BUFFER
;
1129 string
= heap_alloc(sizeof(WCHAR
)*length
);
1131 return E_OUTOFMEMORY
;
1133 method
= get_number_substitutes(substitution
, digits
);
1135 for (i
= 0; i
< length
; i
++) {
1136 /* FIXME: set to better values */
1137 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
1138 glyph_props
[i
].isClusterStart
= 1;
1139 glyph_props
[i
].isDiacritic
= 0;
1140 glyph_props
[i
].isZeroWidthSpace
= 0;
1141 glyph_props
[i
].reserved
= 0;
1143 /* FIXME: have the shaping engine set this */
1144 text_props
[i
].isShapedAlone
= 0;
1145 text_props
[i
].reserved
= 0;
1149 string
[i
] = text
[i
];
1152 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1157 if (string
[i
] >= '0' && string
[i
] <= '9')
1158 string
[i
] = digits
[string
[i
] - '0'];
1160 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
:
1165 for (; i
< max_glyph_count
; i
++) {
1166 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
1167 glyph_props
[i
].isClusterStart
= 0;
1168 glyph_props
[i
].isDiacritic
= 0;
1169 glyph_props
[i
].isZeroWidthSpace
= 0;
1170 glyph_props
[i
].reserved
= 0;
1173 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1175 WARN("failed to get IDWriteFontFace1\n");
1177 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
1179 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
1182 if (!update_cluster
) {
1183 codepoint
= decode_surrogate_pair(string
, i
, length
);
1185 codepoint
= is_rtl
? bidi_get_mirrored_char(string
[i
]) : string
[i
];
1187 update_cluster
= TRUE
;
1189 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
1193 if (need_vertical
) {
1196 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
1198 glyph_indices
[g
] = vertical
;
1206 update_cluster
= FALSE
;
1207 /* mark surrogate halves with same cluster */
1208 clustermap
[i
] = clustermap
[i
-1];
1209 /* update following clusters */
1210 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
1214 *actual_glyph_count
= g
;
1216 hr
= create_scriptshaping_cache(fontface
, &cache
);
1220 context
.cache
= cache
;
1221 context
.text
= text
;
1222 context
.length
= length
;
1223 context
.is_rtl
= is_rtl
;
1224 context
.max_glyph_count
= max_glyph_count
;
1225 context
.language_tag
= get_opentype_language(locale
);
1227 scriptprops
= &dwritescripts_properties
[script
];
1228 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
1229 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1234 /* FIXME: apply default features */
1236 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1237 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1239 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1243 IDWriteFontFace1_Release(fontface1
);
1244 release_scriptshaping_cache(cache
);
1250 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1251 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1252 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1253 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1254 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1255 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1257 DWRITE_FONT_METRICS metrics
;
1258 IDWriteFontFace1
*fontface1
;
1262 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
),
1263 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1264 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_len
,
1265 feature_ranges
, advances
, offsets
);
1267 if (glyph_count
== 0)
1270 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1272 WARN("failed to get IDWriteFontFace1.\n");
1276 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1277 for (i
= 0; i
< glyph_count
; i
++) {
1278 if (glyph_props
[i
].isZeroWidthSpace
)
1283 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1286 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1288 offsets
[i
].advanceOffset
= 0.0f
;
1289 offsets
[i
].ascenderOffset
= 0.0f
;
1292 /* FIXME: actually apply features */
1294 IDWriteFontFace1_Release(fontface1
);
1298 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1299 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* 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 DWRITE_FONT_METRICS metrics
;
1307 IDWriteFontFace1
*fontface1
;
1311 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
),
1312 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1313 transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
),
1314 features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1316 if (glyph_count
== 0)
1319 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1321 WARN("failed to get IDWriteFontFace1.\n");
1325 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1327 IDWriteFontFace1_Release(fontface1
);
1328 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1331 for (i
= 0; i
< glyph_count
; i
++) {
1334 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1335 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1339 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1340 offsets
[i
].advanceOffset
= 0.0f
;
1341 offsets
[i
].ascenderOffset
= 0.0f
;
1344 /* FIXME: actually apply features */
1346 IDWriteFontFace1_Release(fontface1
);
1350 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1352 FLOAT advance
= 0.0f
;
1353 for (; start
< end
; start
++)
1354 advance
+= advances
[start
];
1358 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1359 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1360 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1362 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1363 FLOAT advance
= advances
[g
];
1364 FLOAT origin
= 0.0f
;
1366 if (props
[g
].isZeroWidthSpace
) {
1367 modified_advances
[g
] = advances
[g
];
1368 modified_offsets
[g
] = offsets
[g
];
1372 /* first apply negative spacing and check if we hit minimum width */
1373 if (leading_spacing
< 0.0f
) {
1374 advance
+= leading_spacing
;
1375 origin
-= leading_spacing
;
1377 if (trailing_spacing
< 0.0f
)
1378 advance
+= trailing_spacing
;
1380 if (advance
< min_advance_width
) {
1381 FLOAT half
= (min_advance_width
- advance
) / 2.0f
;
1385 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1387 else if (leading_spacing
< 0.0f
)
1388 origin
-= min_advance_width
- advance
;
1390 advance
= min_advance_width
;
1393 /* now apply positive spacing adjustments */
1394 if (leading_spacing
> 0.0f
) {
1395 advance
+= leading_spacing
;
1396 origin
-= leading_spacing
;
1398 if (trailing_spacing
> 0.0f
)
1399 advance
+= trailing_spacing
;
1401 modified_advances
[g
] = advance
;
1402 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1403 /* ascender is never touched, it's orthogonal to reading direction and is not
1404 affected by advance adjustments */
1405 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1408 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1409 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1410 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1412 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1413 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1414 FLOAT origin
= 0.0f
;
1417 modified_advances
[start
] = advances
[start
];
1418 modified_advances
[end
-1] = advances
[end
-1];
1420 /* first apply negative spacing and check if we hit minimum width */
1421 if (leading_spacing
< 0.0f
) {
1422 advance
+= leading_spacing
;
1423 modified_advances
[start
] += leading_spacing
;
1424 origin
-= leading_spacing
;
1426 if (trailing_spacing
< 0.0f
) {
1427 advance
+= trailing_spacing
;
1428 modified_advances
[end
-1] += trailing_spacing
;
1431 advance
= min_advance_width
- advance
;
1432 if (advance
> 0.0f
) {
1433 /* additional spacing is only applied to leading and trailing glyph */
1434 FLOAT half
= advance
/ 2.0f
;
1438 modified_advances
[start
] += half
;
1439 modified_advances
[end
-1] += half
;
1441 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
) {
1443 modified_advances
[start
] += half
;
1444 modified_advances
[end
-1] += half
;
1446 else if (leading_spacing
< 0.0f
) {
1448 modified_advances
[start
] += advance
;
1451 modified_advances
[end
-1] += advance
;
1454 /* now apply positive spacing adjustments */
1455 if (leading_spacing
> 0.0f
) {
1456 modified_advances
[start
] += leading_spacing
;
1457 origin
-= leading_spacing
;
1459 if (trailing_spacing
> 0.0f
)
1460 modified_advances
[end
-1] += trailing_spacing
;
1462 for (g
= start
; g
< end
; g
++) {
1464 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1465 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1467 else if (g
== end
- 1)
1468 /* trailing glyph offset is not adjusted */
1469 modified_offsets
[g
] = offsets
[g
];
1471 /* for all glyphs within a cluster use original advances and offsets */
1472 modified_advances
[g
] = advances
[g
];
1473 modified_offsets
[g
] = offsets
[g
];
1478 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1480 UINT16 g
= clustermap
[start
];
1483 while (start
< text_len
&& clustermap
[++start
] == g
)
1488 /* Applies spacing adjustments to clusters.
1490 Adjustments are applied in the following order:
1492 1. Negative adjustments
1494 Leading and trailing spacing could be negative, at this step
1495 only negative ones are actually applied. Leading spacing is only
1496 applied to leading glyph, trailing - to trailing glyph.
1498 2. Minimum advance width
1500 Advances could only be reduced at this point or unchanged. In any
1501 case it's checked if cluster advance width is less than minimum width.
1502 If it's the case advance width is incremented up to minimum value.
1504 Important part is the direction in which this increment is applied;
1505 it depends on direction from which total cluster advance was trimmed
1506 at step 1. So it could be incremented from leading, trailing, or both
1507 sides. When applied to both sides, each side gets half of difference
1508 that brings advance to minimum width.
1510 3. Positive adjustments
1512 After minimum width rule was applied, positive spacing is applied in the same
1513 way as negative one on step 1.
1515 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1516 keeps its position in coordinate system where initial advance width is counted
1521 It's known that isZeroWidthSpace property keeps initial advance from changing.
1523 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1524 with more than one glyph.
1527 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1528 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1529 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1530 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1534 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1535 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1537 if (min_advance_width
< 0.0f
) {
1538 if (modified_advances
!= advances
)
1539 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1540 return E_INVALIDARG
;
1543 /* minimum advance is not applied if no adjustments were made */
1544 if (leading_spacing
== 0.0f
&& trailing_spacing
== 0.0f
) {
1545 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1546 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1550 for (start
= 0; start
< len
;) {
1551 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1554 UINT32 g
= clustermap
[start
];
1556 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1557 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1560 UINT32 g_start
, g_end
;
1562 g_start
= clustermap
[start
];
1563 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1565 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1566 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1575 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1576 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1577 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1579 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1580 baseline_coord
, exists
);
1584 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1585 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1587 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1591 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1592 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1594 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1595 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1598 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1599 DWRITE_SCRIPT_PROPERTIES
*props
)
1601 TRACE("(%u %p)\n", sa
.script
, props
);
1603 if (sa
.script
> Script_LastId
)
1604 return E_INVALIDARG
;
1606 *props
= dwritescripts_properties
[sa
.script
].props
;
1610 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1612 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
) ||
1613 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1614 c
== 0x200e || c
== 0x200f || (c
>= 0x202a && c
<= 0x202e))
1617 UINT16 script
= get_char_script(c
);
1618 return !dwritescripts_properties
[script
].is_complex
;
1622 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1623 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1628 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1634 return E_INVALIDARG
;
1641 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1642 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1643 if (is_char_from_simple_script(text
[i
])) {
1654 if (*is_simple
&& indices
) {
1655 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1657 return E_OUTOFMEMORY
;
1659 for (i
= 0; i
< *len_read
; i
++)
1660 codepoints
[i
] = text
[i
];
1662 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1663 heap_free(codepoints
);
1669 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1670 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1671 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1673 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1674 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1678 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1679 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1680 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1682 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1687 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1688 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1689 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1690 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1691 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1692 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1694 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
,
1695 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1696 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1700 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1701 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1703 static const DWRITE_MATRIX transforms
[] = {
1704 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1705 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1706 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1707 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1710 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1712 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1713 memset(m
, 0, sizeof(*m
));
1714 return E_INVALIDARG
;
1717 /* for sideways case simply rotate 90 degrees more */
1720 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1721 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1723 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1724 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1726 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1727 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1729 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1730 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1737 *m
= transforms
[angle
];
1739 /* shift components represent transform necessary to get from original point to
1740 rotated one in new coordinate system */
1741 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1742 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1743 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1749 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1750 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1751 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1753 const struct dwritescript_properties
*props
;
1757 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1760 if (sa
.script
> Script_LastId
)
1761 return E_INVALIDARG
;
1763 language
= get_opentype_language(locale
);
1764 props
= &dwritescripts_properties
[sa
.script
];
1765 *actual_tagcount
= 0;
1767 if (props
->scriptalttag
)
1768 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1770 if (*actual_tagcount
== 0)
1771 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1776 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1777 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1778 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1780 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1785 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1786 dwritetextanalyzer_QueryInterface
,
1787 dwritetextanalyzer_AddRef
,
1788 dwritetextanalyzer_Release
,
1789 dwritetextanalyzer_AnalyzeScript
,
1790 dwritetextanalyzer_AnalyzeBidi
,
1791 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1792 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1793 dwritetextanalyzer_GetGlyphs
,
1794 dwritetextanalyzer_GetGlyphPlacements
,
1795 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1796 dwritetextanalyzer1_ApplyCharacterSpacing
,
1797 dwritetextanalyzer1_GetBaseline
,
1798 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1799 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1800 dwritetextanalyzer1_GetScriptProperties
,
1801 dwritetextanalyzer1_GetTextComplexity
,
1802 dwritetextanalyzer1_GetJustificationOpportunities
,
1803 dwritetextanalyzer1_JustifyGlyphAdvances
,
1804 dwritetextanalyzer1_GetJustifiedGlyphs
,
1805 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1806 dwritetextanalyzer2_GetTypographicFeatures
,
1807 dwritetextanalyzer2_CheckTypographicFeature
1810 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1812 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1814 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1818 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1820 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1822 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1824 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1825 IsEqualIID(riid
, &IID_IUnknown
))
1828 IDWriteNumberSubstitution_AddRef(iface
);
1834 return E_NOINTERFACE
;
1837 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1839 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1840 ULONG ref
= InterlockedIncrement(&This
->ref
);
1841 TRACE("(%p)->(%d)\n", This
, ref
);
1845 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1847 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1848 ULONG ref
= InterlockedDecrement(&This
->ref
);
1850 TRACE("(%p)->(%d)\n", This
, ref
);
1853 heap_free(This
->locale
);
1860 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1861 dwritenumbersubstitution_QueryInterface
,
1862 dwritenumbersubstitution_AddRef
,
1863 dwritenumbersubstitution_Release
1866 struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
1868 if (!iface
|| iface
->lpVtbl
!= &numbersubstitutionvtbl
)
1870 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
1873 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1874 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1876 struct dwrite_numbersubstitution
*substitution
;
1880 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1881 return E_INVALIDARG
;
1883 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1884 return E_INVALIDARG
;
1886 substitution
= heap_alloc(sizeof(*substitution
));
1888 return E_OUTOFMEMORY
;
1890 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1891 substitution
->ref
= 1;
1892 substitution
->ignore_user_override
= ignore_user_override
;
1893 substitution
->method
= method
;
1894 substitution
->locale
= heap_strdupW(locale
);
1895 if (locale
&& !substitution
->locale
) {
1896 heap_free(substitution
);
1897 return E_OUTOFMEMORY
;
1900 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1904 /* IDWriteFontFallback */
1905 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback
*iface
, REFIID riid
, void **obj
)
1907 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1909 TRACE("(%p)->(%s %p)\n", fallback
, debugstr_guid(riid
), obj
);
1911 if (IsEqualIID(riid
, &IID_IDWriteFontFallback
) || IsEqualIID(riid
, &IID_IUnknown
)) {
1913 IDWriteFontFallback_AddRef(iface
);
1918 return E_NOINTERFACE
;
1921 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback
*iface
)
1923 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1924 TRACE("(%p)\n", fallback
);
1925 return IDWriteFactory5_AddRef(fallback
->factory
);
1928 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback
*iface
)
1930 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1931 TRACE("(%p)\n", fallback
);
1932 return IDWriteFactory5_Release(fallback
->factory
);
1935 static int compare_fallback_mapping(const void *a
, const void *b
)
1937 UINT32 ch
= *(UINT32
*)a
;
1938 struct fallback_mapping
*mapping
= (struct fallback_mapping
*)b
;
1940 if (ch
> mapping
->range
.last
)
1942 else if (ch
< mapping
->range
.first
)
1948 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
1950 return bsearch(&ch
, fallback
->mappings
, fallback
->count
, sizeof(*fallback
->mappings
),
1951 compare_fallback_mapping
);
1954 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
1955 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
1957 IDWriteFontFamily
*family
;
1958 BOOL exists
= FALSE
;
1964 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
1971 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
1975 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
1976 IDWriteFontFamily_Release(family
);
1980 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
1985 for (i
= 0; i
< length
; i
++) {
1986 UINT16 script
= get_char_script(text
[i
]);
1989 if (script
== Script_Unknown
|| script
== Script_Common
) {
1994 /* stop on first unsupported character */
1996 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
1997 if (hr
== S_OK
&& exists
)
2006 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
2007 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
, IDWriteFont
**mapped_font
)
2009 const struct fallback_mapping
*mapping
;
2012 mapping
= find_fallback_mapping(fallback
, text
[0]);
2014 WARN("no mapping for 0x%x\n", text
[0]);
2018 /* now let's see what fallback can handle */
2019 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->family
, weight
, style
, stretch
, mapped_font
);
2021 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping
->family
),
2022 mapping
->range
.first
, mapping
->range
.last
, hr
);
2026 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
2028 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping
->family
), hr
);
2030 if (!*mapped_length
) {
2031 IDWriteFont_Release(*mapped_font
);
2032 *mapped_font
= NULL
;
2035 return *mapped_length
? S_OK
: E_FAIL
;
2038 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
2039 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2040 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2041 IDWriteFont
**ret_font
, FLOAT
*scale
)
2043 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2048 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
2049 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2056 return E_INVALIDARG
;
2061 if (!basecollection
)
2062 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
2064 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
2068 if (basefamily
&& *basefamily
) {
2069 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
2073 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
2078 if (!*mapped_length
) {
2079 IDWriteFont
*mapped_font
;
2081 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
2083 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2085 *mapped_length
= length
;
2091 IDWriteFont_Release(*ret_font
);
2092 *ret_font
= mapped_font
;
2101 static const IDWriteFontFallbackVtbl fontfallbackvtbl
= {
2102 fontfallback_QueryInterface
,
2103 fontfallback_AddRef
,
2104 fontfallback_Release
,
2105 fontfallback_MapCharacters
2108 HRESULT
create_system_fontfallback(IDWriteFactory5
*factory
, IDWriteFontFallback
**ret
)
2110 struct dwrite_fontfallback
*fallback
;
2114 fallback
= heap_alloc(sizeof(*fallback
));
2116 return E_OUTOFMEMORY
;
2118 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &fontfallbackvtbl
;
2119 fallback
->factory
= factory
;
2120 fallback
->mappings
= fontfallback_neutral_data
;
2121 fallback
->count
= sizeof(fontfallback_neutral_data
)/sizeof(fontfallback_neutral_data
[0]);
2122 IDWriteFactory5_GetSystemFontCollection(fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
2124 *ret
= &fallback
->IDWriteFontFallback_iface
;
2128 void release_system_fontfallback(IDWriteFontFallback
*iface
)
2130 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2131 IDWriteFontCollection1_Release(fallback
->systemcollection
);
2132 heap_free(fallback
);