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 struct dwritescript_properties
{
35 DWRITE_SCRIPT_PROPERTIES props
;
36 UINT32 scripttag
; /* OpenType script tag */
37 UINT32 scriptalttag
; /* Version 2 tag, 0 if not defined */
39 const struct scriptshaping_ops
*ops
;
42 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
44 /* NOTE: keep this array synced with script ids from scripts.h */
45 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
46 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
47 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
48 { /* Ahom */ { 0x6d6f6841, 338, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
49 { /* Hluw */ { 0x77756c48, 80, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE
},
51 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
52 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
53 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
54 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
55 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
56 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
57 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE
},
58 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
59 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
60 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE
},
61 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
62 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
63 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE
},
64 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
65 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
66 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('c','a','k','m') },
67 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
68 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE
},
69 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
70 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
71 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
72 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
73 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE
},
74 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE
},
75 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
76 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
77 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
78 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE
},
79 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
80 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
81 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
82 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
83 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
84 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE
},
85 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE
},
86 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
87 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE
},
88 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
89 { /* Hatr */ { 0x72746148, 127, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
90 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE
},
91 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
92 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
93 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
94 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
95 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
96 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
97 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE
},
98 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
99 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
100 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
101 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE
},
102 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
103 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
104 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE
},
105 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE
, &latn_shaping_ops
},
106 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
107 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
108 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
109 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
110 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
111 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
112 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
113 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE
},
115 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
116 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
117 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
118 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
119 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','c') },
120 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','o') },
121 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
122 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
123 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE
},
124 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
125 { /* Mult */ { 0x746c754d, 323, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
126 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE
},
127 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
128 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE
},
129 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE
},
130 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE
},
131 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
132 { /* Hung */ { 0x676e7548, 176, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
133 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
134 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
135 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
136 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE
},
137 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
138 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
139 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE
},
140 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE
},
141 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
142 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
143 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
144 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE
},
145 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
146 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
147 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
148 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE
},
149 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
150 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
151 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','h','r','d') },
152 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
153 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
154 { /* Sgnw */ { 0x776e6753, 95, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
155 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE
},
156 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','o','r','a') },
157 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
158 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
159 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE
},
160 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
161 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
162 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE
},
163 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
164 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
165 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('t','a','k','r') },
166 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE
},
167 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE
},
168 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE
},
169 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE
},
170 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE
},
171 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE
},
172 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
173 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
174 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE
},
175 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
176 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE
}
180 const char *debugstr_sa_script(UINT16 script
)
182 return script
< Script_LastId
? debugstr_an((char*)&dwritescripts_properties
[script
].props
.isoScriptCode
, 4): "not defined";
185 /* system font falback configuration */
186 static const WCHAR meiryoW
[] = {'M','e','i','r','y','o',0};
188 struct fallback_mapping
{
189 DWRITE_UNICODE_RANGE range
;
193 static const struct fallback_mapping fontfallback_neutral_data
[] = {
194 { { 0x3000, 0x30ff }, meiryoW
}, /* CJK Symbols and Punctuation, Hiragana, Katakana */
195 { { 0x31f0, 0x31ff }, meiryoW
}, /* Katakana Phonetic Extensions */
196 { { 0x4e00, 0x9fff }, meiryoW
}, /* CJK Unified Ideographs */
199 struct dwrite_fontfallback
{
200 IDWriteFontFallback IDWriteFontFallback_iface
;
201 IDWriteFactory2
*factory
;
202 IDWriteFontCollection
*systemcollection
;
203 const struct fallback_mapping
*mappings
;
207 struct dwrite_numbersubstitution
{
208 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
211 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
213 BOOL ignore_user_override
;
216 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
218 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
221 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback(IDWriteFontFallback
*iface
)
223 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback_iface
);
226 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
228 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
229 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
230 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
236 static inline UINT16
get_char_script(WCHAR c
)
238 return get_table_entry(wine_scripts_table
, c
);
241 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
243 DWRITE_SCRIPT_ANALYSIS sa
;
245 sa
.script
= get_char_script(c
);
246 sa
.shapes
= iscntrlW(c
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
247 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
251 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
253 DWRITE_SCRIPT_ANALYSIS sa
;
254 UINT32 pos
, i
, seq_length
;
256 if (!length
) return S_OK
;
258 sa
= get_char_sa(*text
);
263 for (i
= 1; i
< length
; i
++)
265 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
267 /* Unknown type is ignored when preceded or followed by another script */
270 sa
.script
= cur_sa
.script
;
273 if (cur_sa
.script
== Script_Unknown
)
274 cur_sa
.script
= sa
.script
;
275 else if (cur_sa
.script
!= Script_Common
)
276 sa
.script
= cur_sa
.script
;
279 if (cur_sa
.script
== Script_Unknown
|| cur_sa
.script
== Script_Common
)
280 cur_sa
.script
= sa
.script
;
283 /* this is a length of a sequence to be reported next */
284 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
289 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
290 if (FAILED(hr
)) return hr
;
297 /* one char length case or normal completion call */
298 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
301 struct linebreaking_state
{
302 DWRITE_LINE_BREAKPOINT
*breakpoints
;
306 enum BreakConditionLocation
{
307 BreakConditionBefore
,
311 enum linebreaking_classes
{
354 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
355 set to "can break" and could only be changed once. */
356 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
357 struct linebreaking_state
*state
)
359 if (location
== BreakConditionBefore
) {
360 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
362 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
364 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
367 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
369 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
370 if (pos
+ 1 < state
->count
)
371 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
375 BOOL
lb_is_newline_char(WCHAR ch
)
377 short c
= get_table_entry(wine_linebreak_table
, ch
);
378 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
381 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
383 struct linebreaking_state state
;
387 break_class
= heap_alloc(count
*sizeof(short));
389 return E_OUTOFMEMORY
;
391 state
.breakpoints
= breakpoints
;
394 /* LB31 - allow breaks everywhere. It will be overridden if needed as
395 other rules dictate. */
396 for (i
= 0; i
< count
; i
++)
398 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
400 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
401 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
402 breakpoints
[i
].isWhitespace
= !!isspaceW(text
[i
]);
403 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
404 breakpoints
[i
].padding
= 0;
406 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
407 switch (break_class
[i
])
413 break_class
[i
] = b_AL
;
416 break_class
[i
] = b_NS
;
421 /* LB2 - never break at the start */
422 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
423 /* LB3 - always break at the end. This one is ignored. */
425 for (i
= 0; i
< count
; i
++)
427 switch (break_class
[i
])
431 /* LB5 - don't break CR x LF */
432 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
434 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
435 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
441 /* LB4 - LB5 - always break after hard breaks */
442 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
443 /* LB6 - do not break before hard breaks */
444 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
446 /* LB7 - do not break before spaces */
448 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
451 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
452 /* LB8 - break before character after zero-width space, skip spaces in-between */
453 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
455 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
461 for (i
= 0; i
< count
; i
++)
463 if (break_class
[i
] == b_CM
)
467 switch (break_class
[i
-1])
475 break_class
[i
] = b_AL
;
478 break_class
[i
] = break_class
[i
-1];
481 else break_class
[i
] = b_AL
;
485 for (i
= 0; i
< count
; i
++)
487 switch (break_class
[i
])
489 /* LB11 - don't break before and after word joiner */
491 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
492 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
494 /* LB12 - don't break after glue */
496 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
500 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
501 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
510 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
514 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
515 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
516 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
523 while (j
< count
-1 && break_class
[j
] == b_SP
)
525 if (break_class
[j
] == b_OP
)
527 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
532 while(j
> 0 && break_class
[j
] == b_SP
)
534 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
535 for (j
++; j
<= i
; j
++)
536 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
541 while (j
< count
&& break_class
[j
] == b_SP
)
543 if (break_class
[j
] == b_B2
)
545 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
550 for (i
= 0; i
< count
; i
++)
552 switch(break_class
[i
])
554 /* LB18 - break is allowed after space */
556 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
558 /* LB19 - don't break before or after quotation mark */
560 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
561 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
565 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
566 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
572 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
575 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
581 switch (break_class
[i
+1])
585 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
588 if (i
> 0 && break_class
[i
-1] == b_SY
)
589 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
595 switch (break_class
[i
-1])
603 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
612 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
613 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
614 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
615 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
616 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
617 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
619 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
620 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
621 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
622 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
623 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
624 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
627 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
628 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
629 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
630 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
631 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
632 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
633 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
634 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
635 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
636 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
637 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
638 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
639 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
640 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
641 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
644 if (break_class
[i
] == b_JL
)
646 switch (break_class
[i
+1])
652 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
655 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
656 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
657 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
658 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
659 break_class
[i
+1] == b_JT
)
660 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
663 switch (break_class
[i
])
670 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
671 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
673 if (break_class
[i
] == b_PO
)
675 switch (break_class
[i
+1])
682 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
687 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
688 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
689 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
690 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
691 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
694 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
695 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
696 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
699 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
700 break_class
[i
+1] == b_OP
)
701 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
702 if (break_class
[i
] == b_CP
&&
703 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
704 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
707 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
708 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
712 heap_free(break_class
);
716 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
718 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
720 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
721 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
722 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
723 IsEqualIID(riid
, &IID_IUnknown
))
730 return E_NOINTERFACE
;
733 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
738 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
743 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
744 data after a first request. */
745 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
753 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
754 if (FAILED(hr
)) return hr
;
759 *buff
= heap_alloc(length
*sizeof(WCHAR
));
761 return E_OUTOFMEMORY
;
762 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
765 while (read
< length
&& *text
) {
768 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
773 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
783 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
784 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
790 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
795 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
799 hr
= analyze_script(text
, position
, length
, sink
);
805 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
806 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
808 UINT8
*levels
= NULL
, *explicit = NULL
;
809 UINT8 baselevel
, level
, explicit_level
;
810 UINT32 pos
, i
, seq_length
;
815 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
820 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
824 levels
= heap_alloc(length
*sizeof(*levels
));
825 explicit = heap_alloc(length
*sizeof(*explicit));
827 if (!levels
|| !explicit) {
832 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
833 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
838 explicit_level
= explicit[0];
842 for (i
= 1; i
< length
; i
++) {
843 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
846 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
853 explicit_level
= explicit[i
];
856 /* one char length case or normal completion call */
857 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
867 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
868 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
870 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
874 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
875 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
877 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
883 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
888 /* get some, check for length */
891 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
892 if (FAILED(hr
)) return hr
;
897 buff
= heap_alloc(length
*sizeof(WCHAR
));
899 return E_OUTOFMEMORY
;
900 memcpy(buff
, text
, len
*sizeof(WCHAR
));
903 while (read
< length
&& text
) {
906 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
909 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
916 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
922 hr
= analyze_linebreaks(text
, length
, breakpoints
);
926 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
929 heap_free(breakpoints
);
935 static UINT32
get_opentype_language(const WCHAR
*locale
)
937 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
941 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
942 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
948 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
949 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
950 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
951 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
952 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
953 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
954 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
956 const struct dwritescript_properties
*scriptprops
;
957 struct scriptshaping_context context
;
958 struct scriptshaping_cache
*cache
= NULL
;
959 BOOL update_cluster
, need_vertical
;
960 IDWriteFontFace1
*fontface1
;
966 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
967 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
968 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
970 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
972 if (max_glyph_count
< length
)
973 return E_NOT_SUFFICIENT_BUFFER
;
976 FIXME("number substitution is not supported.\n");
978 for (i
= 0; i
< length
; i
++) {
979 /* FIXME: set to better values */
980 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
981 glyph_props
[i
].isClusterStart
= 1;
982 glyph_props
[i
].isDiacritic
= 0;
983 glyph_props
[i
].isZeroWidthSpace
= 0;
984 glyph_props
[i
].reserved
= 0;
986 /* FIXME: have the shaping engine set this */
987 text_props
[i
].isShapedAlone
= 0;
988 text_props
[i
].reserved
= 0;
993 for (; i
< max_glyph_count
; i
++) {
994 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
995 glyph_props
[i
].isClusterStart
= 0;
996 glyph_props
[i
].isDiacritic
= 0;
997 glyph_props
[i
].isZeroWidthSpace
= 0;
998 glyph_props
[i
].reserved
= 0;
1001 string
= heap_alloc(sizeof(WCHAR
)*length
);
1003 return E_OUTOFMEMORY
;
1005 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1007 WARN("failed to get IDWriteFontFace1\n");
1009 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
1011 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
1014 if (!update_cluster
) {
1015 codepoint
= decode_surrogate_pair(text
, i
, length
);
1017 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
1018 string
[i
] = codepoint
;
1021 string
[i
] = text
[i
];
1022 string
[i
+1] = text
[i
+1];
1023 update_cluster
= TRUE
;
1026 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
1030 if (need_vertical
) {
1033 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
1035 glyph_indices
[g
] = vertical
;
1043 update_cluster
= FALSE
;
1044 /* mark surrogate halves with same cluster */
1045 clustermap
[i
] = clustermap
[i
-1];
1046 /* update following clusters */
1047 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
1051 *actual_glyph_count
= g
;
1053 hr
= create_scriptshaping_cache(fontface
, &cache
);
1057 context
.cache
= cache
;
1058 context
.text
= text
;
1059 context
.length
= length
;
1060 context
.is_rtl
= is_rtl
;
1061 context
.max_glyph_count
= max_glyph_count
;
1062 context
.language_tag
= get_opentype_language(locale
);
1064 scriptprops
= &dwritescripts_properties
[script
];
1065 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
1066 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1071 /* FIXME: apply default features */
1073 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1074 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1076 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1080 IDWriteFontFace1_Release(fontface1
);
1081 release_scriptshaping_cache(cache
);
1087 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1088 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1089 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1090 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1091 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1092 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1094 DWRITE_FONT_METRICS metrics
;
1095 IDWriteFontFace1
*fontface1
;
1099 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %p %s %p %p %u %p %p)\n", debugstr_wn(text
, text_len
),
1100 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1101 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, advances
, offsets
);
1103 if (glyph_count
== 0)
1106 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1108 WARN("failed to get IDWriteFontFace1.\n");
1112 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1113 for (i
= 0; i
< glyph_count
; i
++) {
1114 if (glyph_props
[i
].isZeroWidthSpace
)
1119 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1122 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1124 offsets
[i
].advanceOffset
= 0.0f
;
1125 offsets
[i
].ascenderOffset
= 0.0f
;
1128 /* FIXME: actually apply features */
1130 IDWriteFontFace1_Release(fontface1
);
1134 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1135 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1136 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1137 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT ppdip
,
1138 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1139 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1140 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1142 DWRITE_FONT_METRICS metrics
;
1143 IDWriteFontFace1
*fontface1
;
1147 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %p %s %p %p %u %p %p)\n", debugstr_wn(text
, text_len
),
1148 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1149 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
1150 feature_ranges
, advances
, offsets
);
1152 if (glyph_count
== 0)
1155 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1157 WARN("failed to get IDWriteFontFace1.\n");
1161 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1163 IDWriteFontFace1_Release(fontface1
);
1164 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1167 for (i
= 0; i
< glyph_count
; i
++) {
1170 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1171 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1175 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1176 offsets
[i
].advanceOffset
= 0.0f
;
1177 offsets
[i
].ascenderOffset
= 0.0f
;
1180 /* FIXME: actually apply features */
1182 IDWriteFontFace1_Release(fontface1
);
1186 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1188 FLOAT advance
= 0.0f
;
1189 for (; start
< end
; start
++)
1190 advance
+= advances
[start
];
1194 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1195 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1196 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1198 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1199 FLOAT advance
= advances
[g
];
1200 FLOAT origin
= 0.0f
;
1202 if (props
[g
].isZeroWidthSpace
) {
1203 modified_advances
[g
] = advances
[g
];
1204 modified_offsets
[g
] = offsets
[g
];
1208 /* first apply negative spacing and check if we hit minimum width */
1209 if (leading_spacing
< 0.0f
) {
1210 advance
+= leading_spacing
;
1211 origin
-= leading_spacing
;
1213 if (trailing_spacing
< 0.0f
)
1214 advance
+= trailing_spacing
;
1216 if (advance
< min_advance_width
) {
1217 FLOAT half
= (min_advance_width
- advance
) / 2.0f
;
1221 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1223 else if (leading_spacing
< 0.0f
)
1224 origin
-= min_advance_width
- advance
;
1226 advance
= min_advance_width
;
1229 /* now apply positive spacing adjustments */
1230 if (leading_spacing
> 0.0f
) {
1231 advance
+= leading_spacing
;
1232 origin
-= leading_spacing
;
1234 if (trailing_spacing
> 0.0f
)
1235 advance
+= trailing_spacing
;
1237 modified_advances
[g
] = advance
;
1238 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1239 /* ascender is never touched, it's orthogonal to reading direction and is not
1240 affected by advance adjustments */
1241 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1244 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1245 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1246 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1248 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1249 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1250 FLOAT origin
= 0.0f
;
1253 modified_advances
[start
] = advances
[start
];
1254 modified_advances
[end
-1] = advances
[end
-1];
1256 /* first apply negative spacing and check if we hit minimum width */
1257 if (leading_spacing
< 0.0f
) {
1258 advance
+= leading_spacing
;
1259 modified_advances
[start
] += leading_spacing
;
1260 origin
-= leading_spacing
;
1262 if (trailing_spacing
< 0.0f
) {
1263 advance
+= trailing_spacing
;
1264 modified_advances
[end
-1] += trailing_spacing
;
1267 advance
= min_advance_width
- advance
;
1268 if (advance
> 0.0f
) {
1269 /* additional spacing is only applied to leading and trailing glyph */
1270 FLOAT half
= advance
/ 2.0f
;
1274 modified_advances
[start
] += half
;
1275 modified_advances
[end
-1] += half
;
1277 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
) {
1279 modified_advances
[start
] += half
;
1280 modified_advances
[end
-1] += half
;
1282 else if (leading_spacing
< 0.0f
) {
1284 modified_advances
[start
] += advance
;
1287 modified_advances
[end
-1] += advance
;
1290 /* now apply positive spacing adjustments */
1291 if (leading_spacing
> 0.0f
) {
1292 modified_advances
[start
] += leading_spacing
;
1293 origin
-= leading_spacing
;
1295 if (trailing_spacing
> 0.0f
)
1296 modified_advances
[end
-1] += trailing_spacing
;
1298 for (g
= start
; g
< end
; g
++) {
1300 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1301 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1303 else if (g
== end
- 1)
1304 /* trailing glyph offset is not adjusted */
1305 modified_offsets
[g
] = offsets
[g
];
1307 /* for all glyphs within a cluster use original advances and offsets */
1308 modified_advances
[g
] = advances
[g
];
1309 modified_offsets
[g
] = offsets
[g
];
1314 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1316 UINT16 g
= clustermap
[start
];
1319 while (start
< text_len
&& clustermap
[++start
] == g
)
1324 /* Applies spacing adjustments to clusters.
1326 Adjustments are applied in the following order:
1328 1. Negative adjustments
1330 Leading and trailing spacing could be negative, at this step
1331 only negative ones are actually applied. Leading spacing is only
1332 applied to leading glyph, trailing - to trailing glyph.
1334 2. Minimum advance width
1336 Advances could only be reduced at this point or unchanged. In any
1337 case it's checked if cluster advance width is less than minimum width.
1338 If it's the case advance width is incremented up to minimum value.
1340 Important part is the direction in which this increment is applied;
1341 it depends on direction from which total cluster advance was trimmed
1342 at step 1. So it could be incremented from leading, trailing, or both
1343 sides. When applied to both sides, each side gets half of difference
1344 that brings advance to minimum width.
1346 3. Positive adjustments
1348 After minimum width rule was applied, positive spacing is applied in the same
1349 way as negative one on step 1.
1351 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1352 keeps its position in coordinate system where initial advance width is counted
1357 It's known that isZeroWidthSpace property keeps initial advance from changing.
1359 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1360 with more than one glyph.
1363 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1364 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1365 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1366 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1370 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1371 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1373 if (min_advance_width
< 0.0f
) {
1374 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1375 return E_INVALIDARG
;
1378 /* minimum advance is not applied if no adjustments were made */
1379 if (leading_spacing
== 0.0f
&& trailing_spacing
== 0.0f
) {
1380 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1381 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1385 for (start
= 0; start
< len
;) {
1386 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1389 UINT32 g
= clustermap
[start
];
1391 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1392 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1395 UINT32 g_start
, g_end
;
1397 g_start
= clustermap
[start
];
1398 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1400 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1401 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1410 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1411 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1412 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1414 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1415 baseline_coord
, exists
);
1419 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1420 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1422 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1426 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1427 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1429 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1430 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1433 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1434 DWRITE_SCRIPT_PROPERTIES
*props
)
1436 TRACE("(%u %p)\n", sa
.script
, props
);
1438 if (sa
.script
> Script_LastId
)
1439 return E_INVALIDARG
;
1441 *props
= dwritescripts_properties
[sa
.script
].props
;
1445 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1447 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1450 UINT16 script
= get_char_script(c
);
1451 return !dwritescripts_properties
[script
].is_complex
;
1455 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1456 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1461 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1467 return E_INVALIDARG
;
1474 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1475 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1476 if (is_char_from_simple_script(text
[i
])) {
1487 if (*is_simple
&& indices
) {
1488 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1490 return E_OUTOFMEMORY
;
1492 for (i
= 0; i
< *len_read
; i
++)
1493 codepoints
[i
] = text
[i
];
1495 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1496 heap_free(codepoints
);
1502 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1503 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1504 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1506 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1507 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1511 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1512 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1513 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1515 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1520 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1521 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1522 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1523 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1524 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1525 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1527 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
,
1528 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1529 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1533 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1534 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1536 static const DWRITE_MATRIX transforms
[] = {
1537 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1538 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1539 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1540 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1543 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1545 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1546 memset(m
, 0, sizeof(*m
));
1547 return E_INVALIDARG
;
1550 /* for sideways case simply rotate 90 degrees more */
1553 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1554 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1556 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1557 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1559 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1560 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1562 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1563 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1570 *m
= transforms
[angle
];
1572 /* shift components represent transform necessary to get from original point to
1573 rotated one in new coordinate system */
1574 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1575 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1576 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1582 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1583 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1584 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1586 const struct dwritescript_properties
*props
;
1590 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1593 if (sa
.script
> Script_LastId
)
1594 return E_INVALIDARG
;
1596 language
= get_opentype_language(locale
);
1597 props
= &dwritescripts_properties
[sa
.script
];
1598 *actual_tagcount
= 0;
1600 if (props
->scriptalttag
)
1601 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1603 if (*actual_tagcount
== 0)
1604 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1609 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1610 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1611 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1613 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1618 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1619 dwritetextanalyzer_QueryInterface
,
1620 dwritetextanalyzer_AddRef
,
1621 dwritetextanalyzer_Release
,
1622 dwritetextanalyzer_AnalyzeScript
,
1623 dwritetextanalyzer_AnalyzeBidi
,
1624 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1625 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1626 dwritetextanalyzer_GetGlyphs
,
1627 dwritetextanalyzer_GetGlyphPlacements
,
1628 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1629 dwritetextanalyzer1_ApplyCharacterSpacing
,
1630 dwritetextanalyzer1_GetBaseline
,
1631 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1632 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1633 dwritetextanalyzer1_GetScriptProperties
,
1634 dwritetextanalyzer1_GetTextComplexity
,
1635 dwritetextanalyzer1_GetJustificationOpportunities
,
1636 dwritetextanalyzer1_JustifyGlyphAdvances
,
1637 dwritetextanalyzer1_GetJustifiedGlyphs
,
1638 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1639 dwritetextanalyzer2_GetTypographicFeatures
,
1640 dwritetextanalyzer2_CheckTypographicFeature
1643 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1645 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1647 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1651 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1653 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1655 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1657 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1658 IsEqualIID(riid
, &IID_IUnknown
))
1661 IDWriteNumberSubstitution_AddRef(iface
);
1667 return E_NOINTERFACE
;
1670 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1672 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1673 ULONG ref
= InterlockedIncrement(&This
->ref
);
1674 TRACE("(%p)->(%d)\n", This
, ref
);
1678 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1680 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1681 ULONG ref
= InterlockedDecrement(&This
->ref
);
1683 TRACE("(%p)->(%d)\n", This
, ref
);
1686 heap_free(This
->locale
);
1693 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1694 dwritenumbersubstitution_QueryInterface
,
1695 dwritenumbersubstitution_AddRef
,
1696 dwritenumbersubstitution_Release
1699 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1700 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1702 struct dwrite_numbersubstitution
*substitution
;
1706 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1707 return E_INVALIDARG
;
1709 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1710 return E_INVALIDARG
;
1712 substitution
= heap_alloc(sizeof(*substitution
));
1714 return E_OUTOFMEMORY
;
1716 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1717 substitution
->ref
= 1;
1718 substitution
->ignore_user_override
= ignore_user_override
;
1719 substitution
->method
= method
;
1720 substitution
->locale
= heap_strdupW(locale
);
1721 if (locale
&& !substitution
->locale
) {
1722 heap_free(substitution
);
1723 return E_OUTOFMEMORY
;
1726 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1730 /* IDWriteFontFallback */
1731 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback
*iface
, REFIID riid
, void **obj
)
1733 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1735 TRACE("(%p)->(%s %p)\n", fallback
, debugstr_guid(riid
), obj
);
1737 if (IsEqualIID(riid
, &IID_IDWriteFontFallback
) || IsEqualIID(riid
, &IID_IUnknown
)) {
1739 IDWriteFontFallback_AddRef(iface
);
1744 return E_NOINTERFACE
;
1747 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback
*iface
)
1749 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1750 TRACE("(%p)\n", fallback
);
1751 return IDWriteFactory2_AddRef(fallback
->factory
);
1754 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback
*iface
)
1756 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1757 TRACE("(%p)\n", fallback
);
1758 return IDWriteFactory2_Release(fallback
->factory
);
1761 static int compare_fallback_mapping(const void *a
, const void *b
)
1763 UINT32 ch
= *(UINT32
*)a
;
1764 struct fallback_mapping
*mapping
= (struct fallback_mapping
*)b
;
1766 if (ch
> mapping
->range
.last
)
1768 else if (ch
< mapping
->range
.first
)
1774 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
1776 return bsearch(&ch
, fallback
->mappings
, fallback
->count
, sizeof(*fallback
->mappings
),
1777 compare_fallback_mapping
);
1780 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
1781 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
1783 IDWriteFontFamily
*family
;
1784 BOOL exists
= FALSE
;
1790 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
1797 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
1801 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
1802 IDWriteFontFamily_Release(family
);
1806 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
1811 for (i
= 0; i
< length
; i
++) {
1812 UINT16 script
= get_char_script(text
[i
]);
1815 if (script
== Script_Unknown
|| script
== Script_Common
) {
1820 /* stop on first unsupported character */
1822 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
1823 if (hr
== S_OK
&& exists
)
1832 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
1833 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
, IDWriteFont
**mapped_font
)
1835 const struct fallback_mapping
*mapping
;
1838 mapping
= find_fallback_mapping(fallback
, text
[0]);
1840 WARN("no mapping for 0x%x\n", text
[0]);
1844 /* now let's see what fallback can handle */
1845 hr
= create_matching_font(fallback
->systemcollection
, mapping
->family
, weight
, style
, stretch
, mapped_font
);
1847 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping
->family
),
1848 mapping
->range
.first
, mapping
->range
.last
, hr
);
1852 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
1854 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping
->family
), hr
);
1856 if (!*mapped_length
) {
1857 IDWriteFont_Release(*mapped_font
);
1858 *mapped_font
= NULL
;
1861 return *mapped_length
? S_OK
: E_FAIL
;
1864 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
1865 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
1866 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
1867 IDWriteFont
**ret_font
, FLOAT
*scale
)
1869 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1874 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
1875 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
1882 return E_INVALIDARG
;
1887 if (!basecollection
)
1888 basecollection
= fallback
->systemcollection
;
1890 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
1894 if (basefamily
&& *basefamily
) {
1895 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
1899 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
1904 if (!*mapped_length
) {
1905 IDWriteFont
*mapped_font
;
1907 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
1909 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
1911 *mapped_length
= length
;
1917 IDWriteFont_Release(*ret_font
);
1918 *ret_font
= mapped_font
;
1927 static const IDWriteFontFallbackVtbl fontfallbackvtbl
= {
1928 fontfallback_QueryInterface
,
1929 fontfallback_AddRef
,
1930 fontfallback_Release
,
1931 fontfallback_MapCharacters
1934 HRESULT
create_system_fontfallback(IDWriteFactory2
*factory
, IDWriteFontFallback
**ret
)
1936 struct dwrite_fontfallback
*fallback
;
1940 fallback
= heap_alloc(sizeof(*fallback
));
1942 return E_OUTOFMEMORY
;
1944 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &fontfallbackvtbl
;
1945 fallback
->factory
= factory
;
1946 fallback
->mappings
= fontfallback_neutral_data
;
1947 fallback
->count
= sizeof(fontfallback_neutral_data
)/sizeof(fontfallback_neutral_data
[0]);
1948 IDWriteFactory2_GetSystemFontCollection(fallback
->factory
, &fallback
->systemcollection
, FALSE
);
1950 *ret
= &fallback
->IDWriteFontFallback_iface
;
1954 void release_system_fontfallback(IDWriteFontFallback
*iface
)
1956 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1957 IDWriteFontCollection_Release(fallback
->systemcollection
);
1958 heap_free(fallback
);