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 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
49 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE
},
50 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
51 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
52 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
53 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
54 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
55 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE
},
56 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
57 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
58 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE
},
59 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
60 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
61 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE
},
62 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
63 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
64 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE
},
65 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
66 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
67 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
68 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
69 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE
},
70 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE
},
71 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
72 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE
},
73 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
74 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
75 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
76 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
77 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE
},
78 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE
},
79 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
80 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE
},
81 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
82 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE
},
83 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
84 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
85 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
86 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
87 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
88 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
89 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE
},
90 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
91 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
92 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
93 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE
},
94 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE
},
95 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE
, &latn_shaping_ops
},
96 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
97 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
98 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
99 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
100 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
101 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
102 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE
},
103 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
104 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
105 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE
},
106 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE
},
107 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE
},
108 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE
},
109 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE
},
110 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
111 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
112 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE
},
113 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
114 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
115 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE
},
116 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE
},
117 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE
},
118 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
119 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
120 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE
},
121 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
122 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
123 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
124 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE
},
125 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
126 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
127 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE
},
128 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
129 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
130 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE
},
131 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
132 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
133 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE
},
134 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE
},
135 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE
},
136 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE
},
137 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE
},
138 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE
},
139 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
140 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE
},
141 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE
},
142 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','a','k','m') },
143 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','r','c') },
144 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('m','e','r','o') },
145 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('p','l','r','d') },
146 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','h','r','d') },
147 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','o','r','a') },
148 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','k','r') },
149 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','s','s') },
150 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','g','h','b') },
151 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('d','u','p','l') },
152 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','l','b','a') },
153 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','r','a','n') },
154 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','h','o','j') },
155 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','d') },
156 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','a') },
157 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','h','j') },
158 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','i') },
159 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','n','d') },
160 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('m','o','d','i') },
161 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','r','o','o') },
162 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('n','b','a','t') },
163 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','a','r','b') },
164 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','e','r','m') },
165 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','m','n','g') },
166 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('p','a','l','m') },
167 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','a','u','c') },
168 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','l','p') },
169 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, _OT('s','i','d','d') },
170 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('t','i','r','h') },
171 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('w','a','r','a') },
175 const char *debugstr_sa_script(UINT16 script
)
177 return script
< Script_LastId
? debugstr_an((char*)&dwritescripts_properties
[script
].props
.isoScriptCode
, 4): "not defined";
180 /* system font falback configuration */
181 static const WCHAR meiryoW
[] = {'M','e','i','r','y','o',0};
183 struct fallback_mapping
{
184 DWRITE_UNICODE_RANGE range
;
188 static const struct fallback_mapping fontfallback_neutral_data
[] = {
189 { { 0x3000, 0x30ff }, meiryoW
}, /* CJK Symbols and Punctuation, Hiragana, Katakana */
190 { { 0x31f0, 0x31ff }, meiryoW
}, /* Katakana Phonetic Extensions */
191 { { 0x4e00, 0x9fff }, meiryoW
}, /* CJK Unified Ideographs */
194 struct dwrite_fontfallback
{
195 IDWriteFontFallback IDWriteFontFallback_iface
;
196 IDWriteFactory3
*factory
;
197 IDWriteFontCollection1
*systemcollection
;
198 const struct fallback_mapping
*mappings
;
202 struct dwrite_numbersubstitution
{
203 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
206 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
208 BOOL ignore_user_override
;
211 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
213 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
216 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback(IDWriteFontFallback
*iface
)
218 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback_iface
);
221 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
223 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
224 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
225 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
231 static inline UINT16
get_char_script(WCHAR c
)
233 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
234 return script
== Script_Inherited
? Script_Unknown
: script
;
237 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
239 DWRITE_SCRIPT_ANALYSIS sa
;
241 sa
.script
= get_char_script(c
);
242 sa
.shapes
= iscntrlW(c
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
243 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
247 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
249 DWRITE_SCRIPT_ANALYSIS sa
;
250 UINT32 pos
, i
, seq_length
;
255 sa
= get_char_sa(*text
);
260 for (i
= 1; i
< length
; i
++)
262 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
264 /* Unknown type is ignored when preceded or followed by another script */
267 sa
.script
= cur_sa
.script
;
270 if (cur_sa
.script
== Script_Unknown
)
271 cur_sa
.script
= sa
.script
;
272 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
273 sa
.script
= cur_sa
.script
;
276 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
277 cur_sa
.script
= sa
.script
;
280 /* this is a length of a sequence to be reported next */
281 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
286 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
287 if (FAILED(hr
)) return hr
;
294 /* one char length case or normal completion call */
295 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
298 struct linebreaking_state
{
299 DWRITE_LINE_BREAKPOINT
*breakpoints
;
303 enum BreakConditionLocation
{
304 BreakConditionBefore
,
308 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
);
457 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
459 if (i
< count
-1 && (break_class
[i
+1] == b_ID
|| break_class
[i
+1] == b_EB
|| break_class
[i
+1] == b_EM
))
460 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
466 for (i
= 0; i
< count
; i
++)
468 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
472 switch (break_class
[i
-1])
480 break_class
[i
] = b_AL
;
483 break_class
[i
] = break_class
[i
-1];
486 else break_class
[i
] = b_AL
;
490 for (i
= 0; i
< count
; i
++)
492 switch (break_class
[i
])
494 /* LB11 - don't break before and after word joiner */
496 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
497 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
499 /* LB12 - don't break after glue */
501 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
505 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
506 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
515 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
519 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
520 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
521 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
528 while (j
< count
-1 && break_class
[j
] == b_SP
)
530 if (break_class
[j
] == b_OP
)
532 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
537 while(j
> 0 && break_class
[j
] == b_SP
)
539 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
540 for (j
++; j
<= i
; j
++)
541 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
546 while (j
< count
&& break_class
[j
] == b_SP
)
548 if (break_class
[j
] == b_B2
)
550 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
555 for (i
= 0; i
< count
; i
++)
557 switch(break_class
[i
])
559 /* LB18 - break is allowed after space */
561 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
563 /* LB19 - don't break before or after quotation mark */
565 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
566 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
570 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
571 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
577 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
580 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
586 switch (break_class
[i
+1])
590 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
593 if (i
> 0 && break_class
[i
-1] == b_SY
)
594 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
600 switch (break_class
[i
-1])
610 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
618 /* LB23 - do not break between digits and letters */
619 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
620 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
621 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
622 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
623 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
625 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
626 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
627 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
628 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
629 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
630 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
631 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
632 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
634 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
635 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
636 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
637 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
638 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
639 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
640 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
641 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
642 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
643 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
646 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
647 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
648 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
649 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
650 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
651 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
652 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
653 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
654 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
655 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
656 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
657 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
658 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
659 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
660 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
663 if (break_class
[i
] == b_JL
)
665 switch (break_class
[i
+1])
671 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
674 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
675 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
676 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
677 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
678 break_class
[i
+1] == b_JT
)
679 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
682 switch (break_class
[i
])
689 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
690 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
692 if (break_class
[i
] == b_PO
)
694 switch (break_class
[i
+1])
701 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
706 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
707 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
708 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
709 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
710 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
713 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
714 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
715 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
718 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
719 break_class
[i
+1] == b_OP
)
720 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
721 if (break_class
[i
] == b_CP
&&
722 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
723 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
725 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
726 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
730 while (j
> 0 && break_class
[--j
] == b_RI
)
734 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
737 /* LB30b - do not break between an emoji base and an emoji modifier */
738 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
739 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
743 heap_free(break_class
);
747 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
749 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
751 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
752 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
753 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
754 IsEqualIID(riid
, &IID_IUnknown
))
761 return E_NOINTERFACE
;
764 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
769 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
774 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
775 data after a first request. */
776 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
784 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
785 if (FAILED(hr
)) return hr
;
790 *buff
= heap_alloc(length
*sizeof(WCHAR
));
792 return E_OUTOFMEMORY
;
793 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
796 while (read
< length
&& *text
) {
799 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
804 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
814 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
815 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
821 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
826 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
830 hr
= analyze_script(text
, position
, length
, sink
);
836 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
837 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
839 UINT8
*levels
= NULL
, *explicit = NULL
;
840 UINT8 baselevel
, level
, explicit_level
;
841 UINT32 pos
, i
, seq_length
;
846 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
851 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
855 levels
= heap_alloc(length
*sizeof(*levels
));
856 explicit = heap_alloc(length
*sizeof(*explicit));
858 if (!levels
|| !explicit) {
863 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
864 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
869 explicit_level
= explicit[0];
873 for (i
= 1; i
< length
; i
++) {
874 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
877 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
884 explicit_level
= explicit[i
];
887 /* one char length case or normal completion call */
888 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
898 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
899 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
904 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
908 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
909 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
911 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
917 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
922 /* get some, check for length */
925 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
926 if (FAILED(hr
)) return hr
;
931 buff
= heap_alloc(length
*sizeof(WCHAR
));
933 return E_OUTOFMEMORY
;
934 memcpy(buff
, text
, len
*sizeof(WCHAR
));
937 while (read
< length
&& text
) {
940 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
943 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
950 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
956 hr
= analyze_linebreaks(text
, length
, breakpoints
);
960 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
963 heap_free(breakpoints
);
969 static UINT32
get_opentype_language(const WCHAR
*locale
)
971 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
975 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
976 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
982 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
983 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
984 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
985 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
986 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
987 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
988 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
990 const struct dwritescript_properties
*scriptprops
;
991 struct scriptshaping_context context
;
992 struct scriptshaping_cache
*cache
= NULL
;
993 BOOL update_cluster
, need_vertical
;
994 IDWriteFontFace1
*fontface1
;
1000 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
1001 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1002 features
, feature_range_len
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
,
1003 glyph_props
, actual_glyph_count
);
1005 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1007 if (max_glyph_count
< length
)
1008 return E_NOT_SUFFICIENT_BUFFER
;
1011 FIXME("number substitution is not supported.\n");
1013 for (i
= 0; i
< length
; i
++) {
1014 /* FIXME: set to better values */
1015 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
1016 glyph_props
[i
].isClusterStart
= 1;
1017 glyph_props
[i
].isDiacritic
= 0;
1018 glyph_props
[i
].isZeroWidthSpace
= 0;
1019 glyph_props
[i
].reserved
= 0;
1021 /* FIXME: have the shaping engine set this */
1022 text_props
[i
].isShapedAlone
= 0;
1023 text_props
[i
].reserved
= 0;
1028 for (; i
< max_glyph_count
; i
++) {
1029 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
1030 glyph_props
[i
].isClusterStart
= 0;
1031 glyph_props
[i
].isDiacritic
= 0;
1032 glyph_props
[i
].isZeroWidthSpace
= 0;
1033 glyph_props
[i
].reserved
= 0;
1036 string
= heap_alloc(sizeof(WCHAR
)*length
);
1038 return E_OUTOFMEMORY
;
1040 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1042 WARN("failed to get IDWriteFontFace1\n");
1044 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
1046 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
1049 if (!update_cluster
) {
1050 codepoint
= decode_surrogate_pair(text
, i
, length
);
1052 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
1053 string
[i
] = codepoint
;
1056 string
[i
] = text
[i
];
1057 string
[i
+1] = text
[i
+1];
1058 update_cluster
= TRUE
;
1061 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
1065 if (need_vertical
) {
1068 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
1070 glyph_indices
[g
] = vertical
;
1078 update_cluster
= FALSE
;
1079 /* mark surrogate halves with same cluster */
1080 clustermap
[i
] = clustermap
[i
-1];
1081 /* update following clusters */
1082 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
1086 *actual_glyph_count
= g
;
1088 hr
= create_scriptshaping_cache(fontface
, &cache
);
1092 context
.cache
= cache
;
1093 context
.text
= text
;
1094 context
.length
= length
;
1095 context
.is_rtl
= is_rtl
;
1096 context
.max_glyph_count
= max_glyph_count
;
1097 context
.language_tag
= get_opentype_language(locale
);
1099 scriptprops
= &dwritescripts_properties
[script
];
1100 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
1101 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1106 /* FIXME: apply default features */
1108 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1109 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1111 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1115 IDWriteFontFace1_Release(fontface1
);
1116 release_scriptshaping_cache(cache
);
1122 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1123 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1124 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1125 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1126 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1127 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1129 DWRITE_FONT_METRICS metrics
;
1130 IDWriteFontFace1
*fontface1
;
1134 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text
, text_len
),
1135 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1136 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_len
,
1137 feature_ranges
, advances
, offsets
);
1139 if (glyph_count
== 0)
1142 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1144 WARN("failed to get IDWriteFontFace1.\n");
1148 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1149 for (i
= 0; i
< glyph_count
; i
++) {
1150 if (glyph_props
[i
].isZeroWidthSpace
)
1155 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1158 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1160 offsets
[i
].advanceOffset
= 0.0f
;
1161 offsets
[i
].ascenderOffset
= 0.0f
;
1164 /* FIXME: actually apply features */
1166 IDWriteFontFace1_Release(fontface1
);
1170 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1171 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1172 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1173 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT ppdip
,
1174 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1175 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1176 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1178 DWRITE_FONT_METRICS metrics
;
1179 IDWriteFontFace1
*fontface1
;
1183 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text
, text_len
),
1184 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1185 transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
),
1186 features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1188 if (glyph_count
== 0)
1191 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1193 WARN("failed to get IDWriteFontFace1.\n");
1197 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1199 IDWriteFontFace1_Release(fontface1
);
1200 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1203 for (i
= 0; i
< glyph_count
; i
++) {
1206 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1207 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1211 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1212 offsets
[i
].advanceOffset
= 0.0f
;
1213 offsets
[i
].ascenderOffset
= 0.0f
;
1216 /* FIXME: actually apply features */
1218 IDWriteFontFace1_Release(fontface1
);
1222 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1224 FLOAT advance
= 0.0f
;
1225 for (; start
< end
; start
++)
1226 advance
+= advances
[start
];
1230 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1231 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1232 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1234 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1235 FLOAT advance
= advances
[g
];
1236 FLOAT origin
= 0.0f
;
1238 if (props
[g
].isZeroWidthSpace
) {
1239 modified_advances
[g
] = advances
[g
];
1240 modified_offsets
[g
] = offsets
[g
];
1244 /* first apply negative spacing and check if we hit minimum width */
1245 if (leading_spacing
< 0.0f
) {
1246 advance
+= leading_spacing
;
1247 origin
-= leading_spacing
;
1249 if (trailing_spacing
< 0.0f
)
1250 advance
+= trailing_spacing
;
1252 if (advance
< min_advance_width
) {
1253 FLOAT half
= (min_advance_width
- advance
) / 2.0f
;
1257 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1259 else if (leading_spacing
< 0.0f
)
1260 origin
-= min_advance_width
- advance
;
1262 advance
= min_advance_width
;
1265 /* now apply positive spacing adjustments */
1266 if (leading_spacing
> 0.0f
) {
1267 advance
+= leading_spacing
;
1268 origin
-= leading_spacing
;
1270 if (trailing_spacing
> 0.0f
)
1271 advance
+= trailing_spacing
;
1273 modified_advances
[g
] = advance
;
1274 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1275 /* ascender is never touched, it's orthogonal to reading direction and is not
1276 affected by advance adjustments */
1277 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1280 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1281 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1282 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1284 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1285 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1286 FLOAT origin
= 0.0f
;
1289 modified_advances
[start
] = advances
[start
];
1290 modified_advances
[end
-1] = advances
[end
-1];
1292 /* first apply negative spacing and check if we hit minimum width */
1293 if (leading_spacing
< 0.0f
) {
1294 advance
+= leading_spacing
;
1295 modified_advances
[start
] += leading_spacing
;
1296 origin
-= leading_spacing
;
1298 if (trailing_spacing
< 0.0f
) {
1299 advance
+= trailing_spacing
;
1300 modified_advances
[end
-1] += trailing_spacing
;
1303 advance
= min_advance_width
- advance
;
1304 if (advance
> 0.0f
) {
1305 /* additional spacing is only applied to leading and trailing glyph */
1306 FLOAT half
= advance
/ 2.0f
;
1310 modified_advances
[start
] += half
;
1311 modified_advances
[end
-1] += half
;
1313 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
) {
1315 modified_advances
[start
] += half
;
1316 modified_advances
[end
-1] += half
;
1318 else if (leading_spacing
< 0.0f
) {
1320 modified_advances
[start
] += advance
;
1323 modified_advances
[end
-1] += advance
;
1326 /* now apply positive spacing adjustments */
1327 if (leading_spacing
> 0.0f
) {
1328 modified_advances
[start
] += leading_spacing
;
1329 origin
-= leading_spacing
;
1331 if (trailing_spacing
> 0.0f
)
1332 modified_advances
[end
-1] += trailing_spacing
;
1334 for (g
= start
; g
< end
; g
++) {
1336 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1337 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1339 else if (g
== end
- 1)
1340 /* trailing glyph offset is not adjusted */
1341 modified_offsets
[g
] = offsets
[g
];
1343 /* for all glyphs within a cluster use original advances and offsets */
1344 modified_advances
[g
] = advances
[g
];
1345 modified_offsets
[g
] = offsets
[g
];
1350 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1352 UINT16 g
= clustermap
[start
];
1355 while (start
< text_len
&& clustermap
[++start
] == g
)
1360 /* Applies spacing adjustments to clusters.
1362 Adjustments are applied in the following order:
1364 1. Negative adjustments
1366 Leading and trailing spacing could be negative, at this step
1367 only negative ones are actually applied. Leading spacing is only
1368 applied to leading glyph, trailing - to trailing glyph.
1370 2. Minimum advance width
1372 Advances could only be reduced at this point or unchanged. In any
1373 case it's checked if cluster advance width is less than minimum width.
1374 If it's the case advance width is incremented up to minimum value.
1376 Important part is the direction in which this increment is applied;
1377 it depends on direction from which total cluster advance was trimmed
1378 at step 1. So it could be incremented from leading, trailing, or both
1379 sides. When applied to both sides, each side gets half of difference
1380 that brings advance to minimum width.
1382 3. Positive adjustments
1384 After minimum width rule was applied, positive spacing is applied in the same
1385 way as negative one on step 1.
1387 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1388 keeps its position in coordinate system where initial advance width is counted
1393 It's known that isZeroWidthSpace property keeps initial advance from changing.
1395 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1396 with more than one glyph.
1399 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1400 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1401 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1402 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1406 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1407 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1409 if (min_advance_width
< 0.0f
) {
1410 if (modified_advances
!= advances
)
1411 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1412 return E_INVALIDARG
;
1415 /* minimum advance is not applied if no adjustments were made */
1416 if (leading_spacing
== 0.0f
&& trailing_spacing
== 0.0f
) {
1417 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1418 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1422 for (start
= 0; start
< len
;) {
1423 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1426 UINT32 g
= clustermap
[start
];
1428 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1429 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1432 UINT32 g_start
, g_end
;
1434 g_start
= clustermap
[start
];
1435 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1437 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1438 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1447 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1448 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1449 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1451 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1452 baseline_coord
, exists
);
1456 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1457 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1459 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1463 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1464 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1466 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1467 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1470 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1471 DWRITE_SCRIPT_PROPERTIES
*props
)
1473 TRACE("(%u %p)\n", sa
.script
, props
);
1475 if (sa
.script
> Script_LastId
)
1476 return E_INVALIDARG
;
1478 *props
= dwritescripts_properties
[sa
.script
].props
;
1482 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1484 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1487 UINT16 script
= get_char_script(c
);
1488 return !dwritescripts_properties
[script
].is_complex
;
1492 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1493 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1498 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1504 return E_INVALIDARG
;
1511 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1512 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1513 if (is_char_from_simple_script(text
[i
])) {
1524 if (*is_simple
&& indices
) {
1525 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1527 return E_OUTOFMEMORY
;
1529 for (i
= 0; i
< *len_read
; i
++)
1530 codepoints
[i
] = text
[i
];
1532 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1533 heap_free(codepoints
);
1539 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1540 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1541 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1543 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1544 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1548 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1549 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1550 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1552 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1557 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1558 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1559 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1560 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1561 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1562 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1564 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
,
1565 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1566 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1570 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1571 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1573 static const DWRITE_MATRIX transforms
[] = {
1574 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1575 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1576 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1577 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1580 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1582 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1583 memset(m
, 0, sizeof(*m
));
1584 return E_INVALIDARG
;
1587 /* for sideways case simply rotate 90 degrees more */
1590 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1591 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1593 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1594 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1596 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1597 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1599 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1600 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1607 *m
= transforms
[angle
];
1609 /* shift components represent transform necessary to get from original point to
1610 rotated one in new coordinate system */
1611 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1612 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1613 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1619 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1620 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1621 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1623 const struct dwritescript_properties
*props
;
1627 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1630 if (sa
.script
> Script_LastId
)
1631 return E_INVALIDARG
;
1633 language
= get_opentype_language(locale
);
1634 props
= &dwritescripts_properties
[sa
.script
];
1635 *actual_tagcount
= 0;
1637 if (props
->scriptalttag
)
1638 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1640 if (*actual_tagcount
== 0)
1641 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1646 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1647 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1648 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1650 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1655 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1656 dwritetextanalyzer_QueryInterface
,
1657 dwritetextanalyzer_AddRef
,
1658 dwritetextanalyzer_Release
,
1659 dwritetextanalyzer_AnalyzeScript
,
1660 dwritetextanalyzer_AnalyzeBidi
,
1661 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1662 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1663 dwritetextanalyzer_GetGlyphs
,
1664 dwritetextanalyzer_GetGlyphPlacements
,
1665 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1666 dwritetextanalyzer1_ApplyCharacterSpacing
,
1667 dwritetextanalyzer1_GetBaseline
,
1668 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1669 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1670 dwritetextanalyzer1_GetScriptProperties
,
1671 dwritetextanalyzer1_GetTextComplexity
,
1672 dwritetextanalyzer1_GetJustificationOpportunities
,
1673 dwritetextanalyzer1_JustifyGlyphAdvances
,
1674 dwritetextanalyzer1_GetJustifiedGlyphs
,
1675 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1676 dwritetextanalyzer2_GetTypographicFeatures
,
1677 dwritetextanalyzer2_CheckTypographicFeature
1680 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1682 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1684 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1688 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1690 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1692 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1694 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1695 IsEqualIID(riid
, &IID_IUnknown
))
1698 IDWriteNumberSubstitution_AddRef(iface
);
1704 return E_NOINTERFACE
;
1707 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1709 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1710 ULONG ref
= InterlockedIncrement(&This
->ref
);
1711 TRACE("(%p)->(%d)\n", This
, ref
);
1715 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1717 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1718 ULONG ref
= InterlockedDecrement(&This
->ref
);
1720 TRACE("(%p)->(%d)\n", This
, ref
);
1723 heap_free(This
->locale
);
1730 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1731 dwritenumbersubstitution_QueryInterface
,
1732 dwritenumbersubstitution_AddRef
,
1733 dwritenumbersubstitution_Release
1736 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1737 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1739 struct dwrite_numbersubstitution
*substitution
;
1743 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1744 return E_INVALIDARG
;
1746 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1747 return E_INVALIDARG
;
1749 substitution
= heap_alloc(sizeof(*substitution
));
1751 return E_OUTOFMEMORY
;
1753 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1754 substitution
->ref
= 1;
1755 substitution
->ignore_user_override
= ignore_user_override
;
1756 substitution
->method
= method
;
1757 substitution
->locale
= heap_strdupW(locale
);
1758 if (locale
&& !substitution
->locale
) {
1759 heap_free(substitution
);
1760 return E_OUTOFMEMORY
;
1763 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1767 /* IDWriteFontFallback */
1768 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback
*iface
, REFIID riid
, void **obj
)
1770 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1772 TRACE("(%p)->(%s %p)\n", fallback
, debugstr_guid(riid
), obj
);
1774 if (IsEqualIID(riid
, &IID_IDWriteFontFallback
) || IsEqualIID(riid
, &IID_IUnknown
)) {
1776 IDWriteFontFallback_AddRef(iface
);
1781 return E_NOINTERFACE
;
1784 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback
*iface
)
1786 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1787 TRACE("(%p)\n", fallback
);
1788 return IDWriteFactory3_AddRef(fallback
->factory
);
1791 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback
*iface
)
1793 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1794 TRACE("(%p)\n", fallback
);
1795 return IDWriteFactory3_Release(fallback
->factory
);
1798 static int compare_fallback_mapping(const void *a
, const void *b
)
1800 UINT32 ch
= *(UINT32
*)a
;
1801 struct fallback_mapping
*mapping
= (struct fallback_mapping
*)b
;
1803 if (ch
> mapping
->range
.last
)
1805 else if (ch
< mapping
->range
.first
)
1811 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
1813 return bsearch(&ch
, fallback
->mappings
, fallback
->count
, sizeof(*fallback
->mappings
),
1814 compare_fallback_mapping
);
1817 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
1818 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
1820 IDWriteFontFamily
*family
;
1821 BOOL exists
= FALSE
;
1827 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
1834 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
1838 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
1839 IDWriteFontFamily_Release(family
);
1843 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
1848 for (i
= 0; i
< length
; i
++) {
1849 UINT16 script
= get_char_script(text
[i
]);
1852 if (script
== Script_Unknown
|| script
== Script_Common
) {
1857 /* stop on first unsupported character */
1859 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
1860 if (hr
== S_OK
&& exists
)
1869 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
1870 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
, IDWriteFont
**mapped_font
)
1872 const struct fallback_mapping
*mapping
;
1875 mapping
= find_fallback_mapping(fallback
, text
[0]);
1877 WARN("no mapping for 0x%x\n", text
[0]);
1881 /* now let's see what fallback can handle */
1882 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->family
, weight
, style
, stretch
, mapped_font
);
1884 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping
->family
),
1885 mapping
->range
.first
, mapping
->range
.last
, hr
);
1889 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
1891 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping
->family
), hr
);
1893 if (!*mapped_length
) {
1894 IDWriteFont_Release(*mapped_font
);
1895 *mapped_font
= NULL
;
1898 return *mapped_length
? S_OK
: E_FAIL
;
1901 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
1902 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
1903 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
1904 IDWriteFont
**ret_font
, FLOAT
*scale
)
1906 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1911 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
1912 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
1919 return E_INVALIDARG
;
1924 if (!basecollection
)
1925 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
1927 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
1931 if (basefamily
&& *basefamily
) {
1932 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
1936 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
1941 if (!*mapped_length
) {
1942 IDWriteFont
*mapped_font
;
1944 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
1946 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
1948 *mapped_length
= length
;
1954 IDWriteFont_Release(*ret_font
);
1955 *ret_font
= mapped_font
;
1964 static const IDWriteFontFallbackVtbl fontfallbackvtbl
= {
1965 fontfallback_QueryInterface
,
1966 fontfallback_AddRef
,
1967 fontfallback_Release
,
1968 fontfallback_MapCharacters
1971 HRESULT
create_system_fontfallback(IDWriteFactory3
*factory
, IDWriteFontFallback
**ret
)
1973 struct dwrite_fontfallback
*fallback
;
1977 fallback
= heap_alloc(sizeof(*fallback
));
1979 return E_OUTOFMEMORY
;
1981 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &fontfallbackvtbl
;
1982 fallback
->factory
= factory
;
1983 fallback
->mappings
= fontfallback_neutral_data
;
1984 fallback
->count
= sizeof(fontfallback_neutral_data
)/sizeof(fontfallback_neutral_data
[0]);
1985 IDWriteFactory3_GetSystemFontCollection(fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
1987 *ret
= &fallback
->IDWriteFontFallback_iface
;
1991 void release_system_fontfallback(IDWriteFontFallback
*iface
)
1993 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1994 IDWriteFontCollection1_Release(fallback
->systemcollection
);
1995 heap_free(fallback
);