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
[];
32 extern const unsigned short wine_scripts_table
[];
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 { { 0x4e00, 0x9fff }, meiryoW
}, /* CJK Unified Ideographs */
197 struct dwrite_fontfallback
{
198 IDWriteFontFallback IDWriteFontFallback_iface
;
199 IDWriteFactory2
*factory
;
200 const struct fallback_mapping
*mappings
;
204 struct dwrite_numbersubstitution
{
205 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
208 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
210 BOOL ignore_user_override
;
213 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
215 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
218 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback(IDWriteFontFallback
*iface
)
220 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback_iface
);
223 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
225 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
226 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
227 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
233 static inline UINT16
get_char_script(WCHAR c
)
235 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
236 if (script
== Script_Unknown
) {
238 if (GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
) && (type
& C1_CNTRL
))
239 script
= Script_Common
;
244 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 len
, IDWriteTextAnalysisSink
*sink
)
246 DWRITE_SCRIPT_ANALYSIS sa
;
247 UINT32 pos
, i
, length
;
249 if (!len
) return S_OK
;
251 sa
.script
= get_char_script(*text
);
256 for (i
= 1; i
< len
; i
++)
258 UINT16 script
= get_char_script(text
[i
]);
260 /* Unknown type is ignored when preceded or followed by another script */
261 if (sa
.script
== Script_Unknown
) sa
.script
= script
;
262 if (script
== Script_Unknown
&& sa
.script
!= Script_Common
) script
= sa
.script
;
263 /* this is a length of a sequence to be reported next */
264 if (sa
.script
== script
) length
++;
266 if (sa
.script
!= script
)
270 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
271 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
272 if (FAILED(hr
)) return hr
;
279 /* 1 length case or normal completion call */
280 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
281 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
284 struct linebreaking_state
{
285 DWRITE_LINE_BREAKPOINT
*breakpoints
;
289 enum BreakConditionLocation
{
290 BreakConditionBefore
,
294 enum linebreaking_classes
{
337 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
338 set to "can break" and could only be changed once. */
339 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
340 struct linebreaking_state
*state
)
342 if (location
== BreakConditionBefore
) {
343 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
345 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
347 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
350 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
352 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
353 if (pos
+ 1 < state
->count
)
354 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
358 BOOL
lb_is_newline_char(WCHAR ch
)
360 short c
= get_table_entry(wine_linebreak_table
, ch
);
361 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
364 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
366 struct linebreaking_state state
;
370 break_class
= heap_alloc(count
*sizeof(short));
372 return E_OUTOFMEMORY
;
374 state
.breakpoints
= breakpoints
;
377 /* LB31 - allow breaks everywhere. It will be overridden if needed as
378 other rules dictate. */
379 for (i
= 0; i
< count
; i
++)
381 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
383 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
384 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
385 breakpoints
[i
].isWhitespace
= !!isspaceW(text
[i
]);
386 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
387 breakpoints
[i
].padding
= 0;
389 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
390 switch (break_class
[i
])
396 break_class
[i
] = b_AL
;
399 break_class
[i
] = b_NS
;
404 /* LB2 - never break at the start */
405 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
406 /* LB3 - always break at the end. This one is ignored. */
408 for (i
= 0; i
< count
; i
++)
410 switch (break_class
[i
])
414 /* LB5 - don't break CR x LF */
415 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
417 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
418 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
424 /* LB4 - LB5 - always break after hard breaks */
425 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
426 /* LB6 - do not break before hard breaks */
427 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
429 /* LB7 - do not break before spaces */
431 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
434 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
435 /* LB8 - break before character after zero-width space, skip spaces in-between */
436 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
438 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
444 for (i
= 0; i
< count
; i
++)
446 if (break_class
[i
] == b_CM
)
450 switch (break_class
[i
-1])
458 break_class
[i
] = b_AL
;
461 break_class
[i
] = break_class
[i
-1];
464 else break_class
[i
] = b_AL
;
468 for (i
= 0; i
< count
; i
++)
470 switch (break_class
[i
])
472 /* LB11 - don't break before and after word joiner */
474 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
475 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
477 /* LB12 - don't break after glue */
479 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
483 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
484 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
493 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
497 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
498 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
499 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
506 while (j
< count
-1 && break_class
[j
] == b_SP
)
508 if (break_class
[j
] == b_OP
)
510 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
515 while(j
> 0 && break_class
[j
] == b_SP
)
517 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
518 for (j
++; j
<= i
; j
++)
519 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
524 while (j
< count
&& break_class
[j
] == b_SP
)
526 if (break_class
[j
] == b_B2
)
528 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
533 for (i
= 0; i
< count
; i
++)
535 switch(break_class
[i
])
537 /* LB18 - break is allowed after space */
539 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
541 /* LB19 - don't break before or after quotation mark */
543 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
544 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
548 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
549 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
555 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
558 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
564 switch (break_class
[i
+1])
568 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
571 if (i
> 0 && break_class
[i
-1] == b_SY
)
572 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
578 switch (break_class
[i
-1])
586 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
595 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
596 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
597 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
598 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
599 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
600 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
602 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
603 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
604 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
605 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
606 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
607 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
610 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
611 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
612 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
613 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
614 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
615 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
616 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
617 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
618 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
619 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
620 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
621 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
622 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
623 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
624 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
627 if (break_class
[i
] == b_JL
)
629 switch (break_class
[i
+1])
635 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
638 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
639 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
640 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
641 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
642 break_class
[i
+1] == b_JT
)
643 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
646 switch (break_class
[i
])
653 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
654 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
656 if (break_class
[i
] == b_PO
)
658 switch (break_class
[i
+1])
665 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
670 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
671 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
672 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
673 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
674 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
677 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
678 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
679 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
682 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
683 break_class
[i
+1] == b_OP
)
684 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
685 if (break_class
[i
] == b_CP
&&
686 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
687 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
690 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
691 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
695 heap_free(break_class
);
699 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
701 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
703 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
704 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
705 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
706 IsEqualIID(riid
, &IID_IUnknown
))
713 return E_NOINTERFACE
;
716 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
721 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
726 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
727 data after a first request. */
728 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
736 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
737 if (FAILED(hr
)) return hr
;
742 *buff
= heap_alloc(length
*sizeof(WCHAR
));
744 return E_OUTOFMEMORY
;
745 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
748 while (read
< length
&& *text
) {
751 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
756 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
766 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
767 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
773 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
778 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
782 hr
= analyze_script(text
, position
, length
, sink
);
788 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
789 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
791 UINT8
*levels
= NULL
, *explicit = NULL
;
792 UINT8 baselevel
, level
, explicit_level
;
798 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
803 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
807 levels
= heap_alloc(length
*sizeof(*levels
));
808 explicit = heap_alloc(length
*sizeof(*explicit));
810 if (!levels
|| !explicit) {
815 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
816 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
821 explicit_level
= explicit[0];
823 for (i
= 1; i
< length
; i
++) {
824 if (levels
[i
] != level
|| explicit[i
] != explicit_level
) {
825 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, i
- pos
, explicit_level
, level
);
829 explicit_level
= explicit[i
];
834 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, length
- pos
, explicit_level
, level
);
845 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
846 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
848 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
852 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
853 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
855 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
861 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
866 /* get some, check for length */
869 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
870 if (FAILED(hr
)) return hr
;
875 buff
= heap_alloc(length
*sizeof(WCHAR
));
877 return E_OUTOFMEMORY
;
878 memcpy(buff
, text
, len
*sizeof(WCHAR
));
881 while (read
< length
&& text
) {
884 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
887 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
894 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
900 hr
= analyze_linebreaks(text
, length
, breakpoints
);
904 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
907 heap_free(breakpoints
);
913 static UINT32
get_opentype_language(const WCHAR
*locale
)
915 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
919 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
920 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
926 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
927 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
928 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
929 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
930 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
931 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
932 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
934 const struct dwritescript_properties
*scriptprops
;
935 struct scriptshaping_context context
;
936 struct scriptshaping_cache
*cache
= NULL
;
937 BOOL update_cluster
, need_vertical
;
938 IDWriteFontFace1
*fontface1
;
944 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
945 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
946 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
948 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
950 if (max_glyph_count
< length
)
951 return E_NOT_SUFFICIENT_BUFFER
;
954 FIXME("number substitution is not supported.\n");
956 for (i
= 0; i
< length
; i
++) {
957 /* FIXME: set to better values */
958 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
959 glyph_props
[i
].isClusterStart
= 1;
960 glyph_props
[i
].isDiacritic
= 0;
961 glyph_props
[i
].isZeroWidthSpace
= 0;
962 glyph_props
[i
].reserved
= 0;
964 /* FIXME: have the shaping engine set this */
965 text_props
[i
].isShapedAlone
= 0;
966 text_props
[i
].reserved
= 0;
971 for (; i
< max_glyph_count
; i
++) {
972 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
973 glyph_props
[i
].isClusterStart
= 0;
974 glyph_props
[i
].isDiacritic
= 0;
975 glyph_props
[i
].isZeroWidthSpace
= 0;
976 glyph_props
[i
].reserved
= 0;
979 string
= heap_alloc(sizeof(WCHAR
)*length
);
981 return E_OUTOFMEMORY
;
983 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
985 WARN("failed to get IDWriteFontFace1\n");
987 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
989 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
992 if (!update_cluster
) {
993 codepoint
= decode_surrogate_pair(text
, i
, length
);
995 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
996 string
[i
] = codepoint
;
1000 string
[i
+1] = text
[i
+1];
1001 update_cluster
= TRUE
;
1004 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
1008 if (need_vertical
) {
1011 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
1013 glyph_indices
[g
] = vertical
;
1021 update_cluster
= FALSE
;
1022 /* mark surrogate halves with same cluster */
1023 clustermap
[i
] = clustermap
[i
-1];
1024 /* update following clusters */
1025 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
1029 *actual_glyph_count
= g
;
1031 hr
= create_scriptshaping_cache(fontface
, &cache
);
1035 context
.cache
= cache
;
1036 context
.text
= text
;
1037 context
.length
= length
;
1038 context
.is_rtl
= is_rtl
;
1039 context
.max_glyph_count
= max_glyph_count
;
1040 context
.language_tag
= get_opentype_language(locale
);
1042 scriptprops
= &dwritescripts_properties
[script
];
1043 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
1044 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1049 /* FIXME: apply default features */
1051 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1052 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1054 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1058 IDWriteFontFace1_Release(fontface1
);
1059 release_scriptshaping_cache(cache
);
1065 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1066 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1067 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1068 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1069 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1070 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1072 DWRITE_FONT_METRICS metrics
;
1073 IDWriteFontFace1
*fontface1
;
1077 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
),
1078 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1079 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, advances
, offsets
);
1081 if (glyph_count
== 0)
1084 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1086 WARN("failed to get IDWriteFontFace1.\n");
1090 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1091 for (i
= 0; i
< glyph_count
; i
++) {
1092 if (glyph_props
[i
].isZeroWidthSpace
)
1097 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1100 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1102 offsets
[i
].advanceOffset
= 0.0f
;
1103 offsets
[i
].ascenderOffset
= 0.0f
;
1106 /* FIXME: actually apply features */
1108 IDWriteFontFace1_Release(fontface1
);
1112 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1113 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1114 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1115 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT ppdip
,
1116 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1117 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1118 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1120 DWRITE_FONT_METRICS metrics
;
1121 IDWriteFontFace1
*fontface1
;
1125 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
),
1126 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1127 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
1128 feature_ranges
, advances
, offsets
);
1130 if (glyph_count
== 0)
1133 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1135 WARN("failed to get IDWriteFontFace1.\n");
1139 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1141 IDWriteFontFace1_Release(fontface1
);
1142 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1145 for (i
= 0; i
< glyph_count
; i
++) {
1148 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1149 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1153 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1154 offsets
[i
].advanceOffset
= 0.0f
;
1155 offsets
[i
].ascenderOffset
= 0.0f
;
1158 /* FIXME: actually apply features */
1160 IDWriteFontFace1_Release(fontface1
);
1164 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1166 FLOAT advance
= 0.0f
;
1167 for (; start
< end
; start
++)
1168 advance
+= advances
[start
];
1172 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1173 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1174 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1176 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1177 FLOAT advance
= advances
[g
];
1178 FLOAT origin
= 0.0f
;
1180 if (props
[g
].isZeroWidthSpace
) {
1181 modified_advances
[g
] = advances
[g
];
1182 modified_offsets
[g
] = offsets
[g
];
1186 /* first apply negative spacing and check if we hit minimum width */
1187 if (leading_spacing
< 0.0f
) {
1188 advance
+= leading_spacing
;
1189 origin
-= leading_spacing
;
1191 if (trailing_spacing
< 0.0f
)
1192 advance
+= trailing_spacing
;
1194 if (advance
< min_advance_width
) {
1195 FLOAT half
= (min_advance_width
- advance
) / 2.0f
;
1199 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1201 else if (leading_spacing
< 0.0f
)
1202 origin
-= min_advance_width
- advance
;
1204 advance
= min_advance_width
;
1207 /* now apply positive spacing adjustments */
1208 if (leading_spacing
> 0.0f
) {
1209 advance
+= leading_spacing
;
1210 origin
-= leading_spacing
;
1212 if (trailing_spacing
> 0.0f
)
1213 advance
+= trailing_spacing
;
1215 modified_advances
[g
] = advance
;
1216 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1217 /* ascender is never touched, it's orthogonal to reading direction and is not
1218 affected by advance adjustments */
1219 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1222 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1223 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1224 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1226 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1227 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1228 FLOAT origin
= 0.0f
;
1231 modified_advances
[start
] = advances
[start
];
1232 modified_advances
[end
-1] = advances
[end
-1];
1234 /* first apply negative spacing and check if we hit minimum width */
1235 if (leading_spacing
< 0.0f
) {
1236 advance
+= leading_spacing
;
1237 modified_advances
[start
] += leading_spacing
;
1238 origin
-= leading_spacing
;
1240 if (trailing_spacing
< 0.0f
) {
1241 advance
+= trailing_spacing
;
1242 modified_advances
[end
-1] += trailing_spacing
;
1245 advance
= min_advance_width
- advance
;
1246 if (advance
> 0.0f
) {
1247 /* additional spacing is only applied to leading and trailing glyph */
1248 FLOAT half
= advance
/ 2.0f
;
1252 modified_advances
[start
] += half
;
1253 modified_advances
[end
-1] += half
;
1255 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
) {
1257 modified_advances
[start
] += half
;
1258 modified_advances
[end
-1] += half
;
1260 else if (leading_spacing
< 0.0f
) {
1262 modified_advances
[start
] += advance
;
1265 modified_advances
[end
-1] += advance
;
1268 /* now apply positive spacing adjustments */
1269 if (leading_spacing
> 0.0f
) {
1270 modified_advances
[start
] += leading_spacing
;
1271 origin
-= leading_spacing
;
1273 if (trailing_spacing
> 0.0f
)
1274 modified_advances
[end
-1] += trailing_spacing
;
1276 for (g
= start
; g
< end
; g
++) {
1278 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1279 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1281 else if (g
== end
- 1)
1282 /* trailing glyph offset is not adjusted */
1283 modified_offsets
[g
] = offsets
[g
];
1285 /* for all glyphs within a cluster use original advances and offsets */
1286 modified_advances
[g
] = advances
[g
];
1287 modified_offsets
[g
] = offsets
[g
];
1292 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1294 UINT16 g
= clustermap
[start
];
1297 while (start
< text_len
&& clustermap
[++start
] == g
)
1302 /* Applies spacing adjustments to clusters.
1304 Adjustments are applied in the following order:
1306 1. Negative adjustments
1308 Leading and trailing spacing could be negative, at this step
1309 only negative ones are actually applied. Leading spacing is only
1310 applied to leading glyph, trailing - to trailing glyph.
1312 2. Minimum advance width
1314 Advances could only be reduced at this point or unchanged. In any
1315 case it's checked if cluster advance width is less than minimum width.
1316 If it's the case advance width is incremented up to minimum value.
1318 Important part is the direction in which this increment is applied;
1319 it depends on direction from which total cluster advance was trimmed
1320 at step 1. So it could be incremented from leading, trailing, or both
1321 sides. When applied to both sides, each side gets half of difference
1322 that brings advance to minimum width.
1324 3. Positive adjustments
1326 After minimum width rule was applied, positive spacing is applied in the same
1327 way as negative one on step 1.
1329 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1330 keeps its position in coordinate system where initial advance width is counted
1335 It's known that isZeroWidthSpace property keeps initial advance from changing.
1337 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1338 with more than one glyph.
1341 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1342 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1343 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1344 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1348 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1349 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1351 if (min_advance_width
< 0.0f
) {
1352 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1353 return E_INVALIDARG
;
1356 /* minimum advance is not applied if no adjustments were made */
1357 if (leading_spacing
== 0.0f
&& trailing_spacing
== 0.0f
) {
1358 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1359 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1363 for (start
= 0; start
< len
;) {
1364 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1367 UINT32 g
= clustermap
[start
];
1369 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1370 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1373 UINT32 g_start
, g_end
;
1375 g_start
= clustermap
[start
];
1376 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1378 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1379 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1388 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1389 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1390 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1392 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1393 baseline_coord
, exists
);
1397 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1398 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1400 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1404 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1405 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1407 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1408 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1411 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1412 DWRITE_SCRIPT_PROPERTIES
*props
)
1414 TRACE("(%u %p)\n", sa
.script
, props
);
1416 if (sa
.script
> Script_LastId
)
1417 return E_INVALIDARG
;
1419 *props
= dwritescripts_properties
[sa
.script
].props
;
1423 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1425 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1428 UINT16 script
= get_char_script(c
);
1429 return !dwritescripts_properties
[script
].is_complex
;
1433 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1434 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1439 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1445 return E_INVALIDARG
;
1452 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1453 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1454 if (is_char_from_simple_script(text
[i
])) {
1465 if (*is_simple
&& indices
) {
1466 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1468 return E_OUTOFMEMORY
;
1470 for (i
= 0; i
< *len_read
; i
++)
1471 codepoints
[i
] = text
[i
];
1473 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1474 heap_free(codepoints
);
1480 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1481 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1482 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1484 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1485 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1489 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1490 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1491 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1493 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1498 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1499 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1500 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1501 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1502 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1503 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1505 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
,
1506 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1507 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1511 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1512 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1514 static const DWRITE_MATRIX transforms
[] = {
1515 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1516 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1517 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1518 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1521 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1523 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1524 memset(m
, 0, sizeof(*m
));
1525 return E_INVALIDARG
;
1528 /* for sideways case simply rotate 90 degrees more */
1531 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1532 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1534 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1535 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1537 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1538 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1540 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1541 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1548 *m
= transforms
[angle
];
1550 /* shift components represent transform necessary to get from original point to
1551 rotated one in new coordinate system */
1552 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1553 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1554 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1560 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1561 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1562 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1564 const struct dwritescript_properties
*props
;
1568 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1571 if (sa
.script
> Script_LastId
)
1572 return E_INVALIDARG
;
1574 language
= get_opentype_language(locale
);
1575 props
= &dwritescripts_properties
[sa
.script
];
1576 *actual_tagcount
= 0;
1578 if (props
->scriptalttag
)
1579 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1581 if (*actual_tagcount
== 0)
1582 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1587 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1588 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1589 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1591 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1596 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1597 dwritetextanalyzer_QueryInterface
,
1598 dwritetextanalyzer_AddRef
,
1599 dwritetextanalyzer_Release
,
1600 dwritetextanalyzer_AnalyzeScript
,
1601 dwritetextanalyzer_AnalyzeBidi
,
1602 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1603 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1604 dwritetextanalyzer_GetGlyphs
,
1605 dwritetextanalyzer_GetGlyphPlacements
,
1606 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1607 dwritetextanalyzer1_ApplyCharacterSpacing
,
1608 dwritetextanalyzer1_GetBaseline
,
1609 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1610 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1611 dwritetextanalyzer1_GetScriptProperties
,
1612 dwritetextanalyzer1_GetTextComplexity
,
1613 dwritetextanalyzer1_GetJustificationOpportunities
,
1614 dwritetextanalyzer1_JustifyGlyphAdvances
,
1615 dwritetextanalyzer1_GetJustifiedGlyphs
,
1616 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1617 dwritetextanalyzer2_GetTypographicFeatures
,
1618 dwritetextanalyzer2_CheckTypographicFeature
1621 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1623 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1625 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1629 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1631 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1633 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1635 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1636 IsEqualIID(riid
, &IID_IUnknown
))
1639 IDWriteNumberSubstitution_AddRef(iface
);
1645 return E_NOINTERFACE
;
1648 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1650 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1651 ULONG ref
= InterlockedIncrement(&This
->ref
);
1652 TRACE("(%p)->(%d)\n", This
, ref
);
1656 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1658 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1659 ULONG ref
= InterlockedDecrement(&This
->ref
);
1661 TRACE("(%p)->(%d)\n", This
, ref
);
1664 heap_free(This
->locale
);
1671 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1672 dwritenumbersubstitution_QueryInterface
,
1673 dwritenumbersubstitution_AddRef
,
1674 dwritenumbersubstitution_Release
1677 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1678 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1680 struct dwrite_numbersubstitution
*substitution
;
1684 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1685 return E_INVALIDARG
;
1687 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1688 return E_INVALIDARG
;
1690 substitution
= heap_alloc(sizeof(*substitution
));
1692 return E_OUTOFMEMORY
;
1694 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1695 substitution
->ref
= 1;
1696 substitution
->ignore_user_override
= ignore_user_override
;
1697 substitution
->method
= method
;
1698 substitution
->locale
= heap_strdupW(locale
);
1699 if (locale
&& !substitution
->locale
) {
1700 heap_free(substitution
);
1701 return E_OUTOFMEMORY
;
1704 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1708 /* IDWriteFontFallback */
1709 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback
*iface
, REFIID riid
, void **obj
)
1711 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1713 TRACE("(%p)->(%s %p)\n", fallback
, debugstr_guid(riid
), obj
);
1715 if (IsEqualIID(riid
, &IID_IDWriteFontFallback
) || IsEqualIID(riid
, &IID_IUnknown
)) {
1717 IDWriteFontFallback_AddRef(iface
);
1722 return E_NOINTERFACE
;
1725 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback
*iface
)
1727 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1728 TRACE("(%p)\n", fallback
);
1729 return IDWriteFactory2_AddRef(fallback
->factory
);
1732 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback
*iface
)
1734 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1735 TRACE("(%p)\n", fallback
);
1736 return IDWriteFactory2_Release(fallback
->factory
);
1739 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
1743 for (i
= 0; i
< fallback
->count
; i
++) {
1744 if (fallback
->mappings
[i
].range
.first
<= ch
&& fallback
->mappings
[i
].range
.last
>= ch
)
1745 return &fallback
->mappings
[i
];
1751 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
1752 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
1754 IDWriteFontFamily
*family
;
1755 BOOL exists
= FALSE
;
1761 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
1768 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
1772 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
1773 IDWriteFontFamily_Release(family
);
1777 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
1782 for (i
= 0; i
< length
; i
++) {
1783 UINT16 script
= get_char_script(text
[i
]);
1786 if (script
== Script_Unknown
|| script
== Script_Common
) {
1791 /* stop on first unsupported character */
1793 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
1794 if (hr
== S_OK
&& exists
)
1803 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
1804 IDWriteFontCollection
*collection
, DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
,
1805 UINT32
*mapped_length
, IDWriteFont
**mapped_font
)
1807 const struct fallback_mapping
*mapping
;
1810 mapping
= find_fallback_mapping(fallback
, text
[0]);
1812 WARN("no mapping for 0x%x\n", text
[0]);
1816 /* now let's see what fallback can handle */
1817 hr
= create_matching_font(collection
, mapping
->family
, weight
, style
, stretch
, mapped_font
);
1819 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping
->family
),
1820 mapping
->range
.first
, mapping
->range
.last
, hr
);
1824 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
1826 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping
->family
), hr
);
1828 if (!*mapped_length
) {
1829 IDWriteFont_Release(*mapped_font
);
1830 *mapped_font
= NULL
;
1833 return *mapped_length
? S_OK
: E_FAIL
;
1836 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
1837 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
1838 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
1839 IDWriteFont
**ret_font
, FLOAT
*scale
)
1841 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1846 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
1847 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
1854 return E_INVALIDARG
;
1859 if (!basecollection
) {
1860 hr
= IDWriteFactory2_GetSystemFontCollection(fallback
->factory
, &basecollection
, FALSE
);
1865 IDWriteFontCollection_AddRef(basecollection
);
1867 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
1871 if (basefamily
&& *basefamily
) {
1872 IDWriteFont
*mapped_font
;
1874 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
1878 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
1882 if (!*mapped_length
) {
1883 hr
= fallback_get_fallback_font(fallback
, text
, length
, basecollection
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
1885 *mapped_length
= length
;
1890 IDWriteFont_Release(*ret_font
);
1891 *ret_font
= mapped_font
;
1896 hr
= fallback_get_fallback_font(fallback
, text
, length
, basecollection
, weight
, style
, stretch
, mapped_length
, ret_font
);
1899 IDWriteFontCollection_Release(basecollection
);
1904 static const IDWriteFontFallbackVtbl fontfallbackvtbl
= {
1905 fontfallback_QueryInterface
,
1906 fontfallback_AddRef
,
1907 fontfallback_Release
,
1908 fontfallback_MapCharacters
1911 HRESULT
create_system_fontfallback(IDWriteFactory2
*factory
, IDWriteFontFallback
**ret
)
1913 struct dwrite_fontfallback
*fallback
;
1917 fallback
= heap_alloc(sizeof(*fallback
));
1919 return E_OUTOFMEMORY
;
1921 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &fontfallbackvtbl
;
1922 fallback
->factory
= factory
;
1923 fallback
->mappings
= fontfallback_neutral_data
;
1924 fallback
->count
= sizeof(fontfallback_neutral_data
)/sizeof(fontfallback_neutral_data
[0]);
1926 *ret
= &fallback
->IDWriteFontFallback_iface
;
1930 void release_system_fontfallback(IDWriteFontFallback
*iface
)
1932 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1933 heap_free(fallback
);