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') },
172 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','d','l','m') },
173 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','h','o','m') },
174 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','l','u','w') },
175 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','h','k','s') },
176 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','a','t','r') },
177 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','r','c') },
178 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','u','l','t') },
179 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','e','w','a') },
180 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','u','n','g') },
181 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','g','e') },
182 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','g','n','w') },
183 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','n','g') },
187 const char *debugstr_sa_script(UINT16 script
)
189 return script
< Script_LastId
? debugstr_an((char*)&dwritescripts_properties
[script
].props
.isoScriptCode
, 4): "not defined";
192 /* system font falback configuration */
193 static const WCHAR meiryoW
[] = {'M','e','i','r','y','o',0};
195 struct fallback_mapping
{
196 DWRITE_UNICODE_RANGE range
;
200 static const struct fallback_mapping fontfallback_neutral_data
[] = {
201 { { 0x3000, 0x30ff }, meiryoW
}, /* CJK Symbols and Punctuation, Hiragana, Katakana */
202 { { 0x31f0, 0x31ff }, meiryoW
}, /* Katakana Phonetic Extensions */
203 { { 0x4e00, 0x9fff }, meiryoW
}, /* CJK Unified Ideographs */
206 struct dwrite_fontfallback
{
207 IDWriteFontFallback IDWriteFontFallback_iface
;
208 IDWriteFactory4
*factory
;
209 IDWriteFontCollection1
*systemcollection
;
210 const struct fallback_mapping
*mappings
;
214 struct dwrite_numbersubstitution
{
215 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
218 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
220 BOOL ignore_user_override
;
223 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
225 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
228 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback(IDWriteFontFallback
*iface
)
230 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback_iface
);
233 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
235 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
236 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
237 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
243 static inline UINT16
get_char_script(WCHAR c
)
245 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
246 return script
== Script_Inherited
? Script_Unknown
: script
;
249 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
251 DWRITE_SCRIPT_ANALYSIS sa
;
253 sa
.script
= get_char_script(c
);
254 sa
.shapes
= iscntrlW(c
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
255 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
259 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
261 DWRITE_SCRIPT_ANALYSIS sa
;
262 UINT32 pos
, i
, seq_length
;
267 sa
= get_char_sa(*text
);
272 for (i
= 1; i
< length
; i
++)
274 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
276 /* Unknown type is ignored when preceded or followed by another script */
279 sa
.script
= cur_sa
.script
;
282 if (cur_sa
.script
== Script_Unknown
)
283 cur_sa
.script
= sa
.script
;
284 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
285 sa
.script
= cur_sa
.script
;
288 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
289 cur_sa
.script
= sa
.script
;
292 /* this is a length of a sequence to be reported next */
293 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
298 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
299 if (FAILED(hr
)) return hr
;
306 /* one char length case or normal completion call */
307 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
310 struct linebreaking_state
{
311 DWRITE_LINE_BREAKPOINT
*breakpoints
;
315 enum BreakConditionLocation
{
316 BreakConditionBefore
,
320 enum linebreaking_classes
{
366 static BOOL
has_strong_condition(DWRITE_BREAK_CONDITION old_condition
, DWRITE_BREAK_CONDITION new_condition
)
368 if (old_condition
== DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
|| old_condition
== DWRITE_BREAK_CONDITION_MUST_BREAK
)
371 if (old_condition
== DWRITE_BREAK_CONDITION_CAN_BREAK
&& new_condition
!= DWRITE_BREAK_CONDITION_MUST_BREAK
)
377 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
378 set to "can break" and could only be changed once. */
379 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
380 struct linebreaking_state
*state
)
382 if (location
== BreakConditionBefore
) {
383 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionBefore
, condition
))
385 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
387 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
390 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionAfter
, condition
))
392 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
393 if (pos
+ 1 < state
->count
)
394 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
398 BOOL
lb_is_newline_char(WCHAR ch
)
400 short c
= get_table_entry(wine_linebreak_table
, ch
);
401 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
404 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
406 struct linebreaking_state state
;
410 break_class
= heap_alloc(count
*sizeof(short));
412 return E_OUTOFMEMORY
;
414 state
.breakpoints
= breakpoints
;
417 for (i
= 0; i
< count
; i
++)
419 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
421 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_NEUTRAL
;
422 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_NEUTRAL
;
423 breakpoints
[i
].isWhitespace
= !!isspaceW(text
[i
]);
424 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
425 breakpoints
[i
].padding
= 0;
427 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
428 switch (break_class
[i
])
434 break_class
[i
] = b_AL
;
437 break_class
[i
] = b_NS
;
442 /* LB2 - never break at the start */
443 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
444 /* LB3 - always break at the end. */
445 set_break_condition(count
- 1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
447 /* LB4 - LB6 - mandatory breaks. */
448 for (i
= 0; i
< count
; i
++)
450 switch (break_class
[i
])
454 /* LB5 - don't break CR x LF */
455 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
457 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
458 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
464 /* LB4 - LB5 - always break after hard breaks */
465 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
466 /* LB6 - do not break before hard breaks */
467 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
472 /* LB7 - LB8 - explicit breaks and non-breaks */
473 for (i
= 0; i
< count
; i
++)
475 switch (break_class
[i
])
477 /* LB7 - do not break before spaces */
479 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
482 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
484 /* LB8 - break before character after zero-width space, skip spaces in-between */
486 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
488 if (j
< count
-1 && break_class
[j
+1] != b_ZW
)
489 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
491 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
493 if (i
< count
-1 && (break_class
[i
+1] == b_ID
|| break_class
[i
+1] == b_EB
|| break_class
[i
+1] == b_EM
))
494 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
499 /* LB9 - LB10 - combining marks */
500 for (i
= 0; i
< count
; i
++)
502 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
506 switch (break_class
[i
-1])
514 break_class
[i
] = b_AL
;
518 break_class
[i
] = break_class
[i
-1];
519 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
523 else break_class
[i
] = b_AL
;
527 for (i
= 0; i
< count
; i
++)
529 switch (break_class
[i
])
531 /* LB11 - don't break before and after word joiner */
533 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
534 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
536 /* LB12 - don't break after glue */
538 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
542 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
543 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
552 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
554 /* LB14 - do not break after OP, even after spaces */
557 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
559 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
561 /* LB15 - do not break within QU-OP, even with intervening spaces */
564 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
566 if (j
< count
- 1 && break_class
[j
+1] == b_OP
)
567 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
572 while(j
> 0 && break_class
[j
] == b_SP
)
574 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
575 for (j
++; j
<= i
; j
++)
576 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
578 /* LB17 - do not break within B2, even with intervening spaces */
581 while (j
< count
&& break_class
[j
+1] == b_SP
)
583 if (j
< count
- 1 && break_class
[j
+1] == b_B2
)
584 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
589 for (i
= 0; i
< count
; i
++)
591 switch(break_class
[i
])
593 /* LB18 - break is allowed after space */
595 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
597 /* LB19 - don't break before or after quotation mark */
599 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
600 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
604 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
605 if (i
< count
- 1 && break_class
[i
+1] != b_QU
)
606 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
612 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
615 if (i
< count
- 1 && break_class
[i
+1] != b_CB
)
616 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
622 switch (break_class
[i
+1])
626 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
629 if (i
> 0 && break_class
[i
-1] == b_SY
)
630 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
636 switch (break_class
[i
-1])
646 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
654 /* LB23 - do not break between digits and letters */
655 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
656 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
657 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
658 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
659 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
661 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
662 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
663 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
664 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
665 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
666 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
667 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
668 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
670 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
671 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
672 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
673 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
674 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
675 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
676 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
677 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
678 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
679 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
682 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
683 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
684 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
685 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
686 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
687 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
688 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
689 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
690 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
691 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
692 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
693 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
694 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
695 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
696 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
699 if (break_class
[i
] == b_JL
)
701 switch (break_class
[i
+1])
707 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
710 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
711 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
712 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
713 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
714 break_class
[i
+1] == b_JT
)
715 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
718 switch (break_class
[i
])
725 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
726 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
728 if (break_class
[i
] == b_PR
)
730 switch (break_class
[i
+1])
737 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
742 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
743 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
744 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
745 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
746 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
749 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
750 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
751 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
754 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
755 break_class
[i
+1] == b_OP
)
756 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
757 if (break_class
[i
] == b_CP
&&
758 (break_class
[i
+1] == b_AL
|| break_class
[i
+1] == b_HL
|| break_class
[i
+1] == b_NU
))
759 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
761 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
762 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
766 while (j
> 0 && break_class
[--j
] == b_RI
)
770 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
773 /* LB30b - do not break between an emoji base and an emoji modifier */
774 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
775 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
779 /* LB31 - allow breaks everywhere else. */
780 for (i
= 0; i
< count
; i
++)
782 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
783 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
786 heap_free(break_class
);
790 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
792 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
794 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
795 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
796 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
797 IsEqualIID(riid
, &IID_IUnknown
))
804 return E_NOINTERFACE
;
807 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
812 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
817 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
818 data after a first request. */
819 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
827 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
828 if (FAILED(hr
)) return hr
;
833 *buff
= heap_alloc(length
*sizeof(WCHAR
));
835 return E_OUTOFMEMORY
;
836 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
839 while (read
< length
&& *text
) {
842 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
847 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
857 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
858 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
864 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
869 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
873 hr
= analyze_script(text
, position
, length
, sink
);
879 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
880 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
882 UINT8
*levels
= NULL
, *explicit = NULL
;
883 UINT8 baselevel
, level
, explicit_level
;
884 UINT32 pos
, i
, seq_length
;
889 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
894 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
898 levels
= heap_alloc(length
*sizeof(*levels
));
899 explicit = heap_alloc(length
*sizeof(*explicit));
901 if (!levels
|| !explicit) {
906 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
907 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
912 explicit_level
= explicit[0];
916 for (i
= 1; i
< length
; i
++) {
917 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
920 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
927 explicit_level
= explicit[i
];
930 /* one char length case or normal completion call */
931 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
941 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
942 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
947 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
951 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
952 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
954 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
960 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
965 /* get some, check for length */
968 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
969 if (FAILED(hr
)) return hr
;
974 buff
= heap_alloc(length
*sizeof(WCHAR
));
976 return E_OUTOFMEMORY
;
977 memcpy(buff
, text
, len
*sizeof(WCHAR
));
980 while (read
< length
&& text
) {
983 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
986 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
993 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
999 hr
= analyze_linebreaks(text
, length
, breakpoints
);
1003 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
1006 heap_free(breakpoints
);
1012 static UINT32
get_opentype_language(const WCHAR
*locale
)
1014 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
1018 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
1019 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
1025 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
1026 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
1027 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
1028 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1029 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
1030 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
1031 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
1033 const struct dwritescript_properties
*scriptprops
;
1034 struct scriptshaping_context context
;
1035 struct scriptshaping_cache
*cache
= NULL
;
1036 BOOL update_cluster
, need_vertical
;
1037 IDWriteFontFace1
*fontface1
;
1043 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
1044 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1045 features
, feature_range_len
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
,
1046 glyph_props
, actual_glyph_count
);
1048 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1050 if (max_glyph_count
< length
)
1051 return E_NOT_SUFFICIENT_BUFFER
;
1054 FIXME("number substitution is not supported.\n");
1056 for (i
= 0; i
< length
; i
++) {
1057 /* FIXME: set to better values */
1058 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
1059 glyph_props
[i
].isClusterStart
= 1;
1060 glyph_props
[i
].isDiacritic
= 0;
1061 glyph_props
[i
].isZeroWidthSpace
= 0;
1062 glyph_props
[i
].reserved
= 0;
1064 /* FIXME: have the shaping engine set this */
1065 text_props
[i
].isShapedAlone
= 0;
1066 text_props
[i
].reserved
= 0;
1071 for (; i
< max_glyph_count
; i
++) {
1072 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
1073 glyph_props
[i
].isClusterStart
= 0;
1074 glyph_props
[i
].isDiacritic
= 0;
1075 glyph_props
[i
].isZeroWidthSpace
= 0;
1076 glyph_props
[i
].reserved
= 0;
1079 string
= heap_alloc(sizeof(WCHAR
)*length
);
1081 return E_OUTOFMEMORY
;
1083 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1085 WARN("failed to get IDWriteFontFace1\n");
1087 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
1089 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
1092 if (!update_cluster
) {
1093 codepoint
= decode_surrogate_pair(text
, i
, length
);
1095 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
1096 string
[i
] = codepoint
;
1099 string
[i
] = text
[i
];
1100 string
[i
+1] = text
[i
+1];
1101 update_cluster
= TRUE
;
1104 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
1108 if (need_vertical
) {
1111 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
1113 glyph_indices
[g
] = vertical
;
1121 update_cluster
= FALSE
;
1122 /* mark surrogate halves with same cluster */
1123 clustermap
[i
] = clustermap
[i
-1];
1124 /* update following clusters */
1125 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
1129 *actual_glyph_count
= g
;
1131 hr
= create_scriptshaping_cache(fontface
, &cache
);
1135 context
.cache
= cache
;
1136 context
.text
= text
;
1137 context
.length
= length
;
1138 context
.is_rtl
= is_rtl
;
1139 context
.max_glyph_count
= max_glyph_count
;
1140 context
.language_tag
= get_opentype_language(locale
);
1142 scriptprops
= &dwritescripts_properties
[script
];
1143 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
1144 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
1149 /* FIXME: apply default features */
1151 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
1152 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1154 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1158 IDWriteFontFace1_Release(fontface1
);
1159 release_scriptshaping_cache(cache
);
1165 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1166 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1167 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1168 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1169 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1170 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1172 DWRITE_FONT_METRICS metrics
;
1173 IDWriteFontFace1
*fontface1
;
1177 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
),
1178 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1179 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_len
,
1180 feature_ranges
, advances
, offsets
);
1182 if (glyph_count
== 0)
1185 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1187 WARN("failed to get IDWriteFontFace1.\n");
1191 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1192 for (i
= 0; i
< glyph_count
; i
++) {
1193 if (glyph_props
[i
].isZeroWidthSpace
)
1198 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1201 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1203 offsets
[i
].advanceOffset
= 0.0f
;
1204 offsets
[i
].ascenderOffset
= 0.0f
;
1207 /* FIXME: actually apply features */
1209 IDWriteFontFace1_Release(fontface1
);
1213 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1214 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1215 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1216 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, FLOAT ppdip
,
1217 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1218 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1219 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1221 DWRITE_FONT_METRICS metrics
;
1222 IDWriteFontFace1
*fontface1
;
1226 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
),
1227 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1228 transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
),
1229 features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1231 if (glyph_count
== 0)
1234 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1236 WARN("failed to get IDWriteFontFace1.\n");
1240 hr
= IDWriteFontFace_GetGdiCompatibleMetrics(fontface
, emSize
, ppdip
, transform
, &metrics
);
1242 IDWriteFontFace1_Release(fontface1
);
1243 WARN("failed to get compat metrics, 0x%08x\n", hr
);
1246 for (i
= 0; i
< glyph_count
; i
++) {
1249 hr
= IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1
, emSize
, ppdip
,
1250 transform
, use_gdi_natural
, is_sideways
, 1, &glyphs
[i
], &a
);
1254 advances
[i
] = floorf(a
* emSize
* ppdip
/ metrics
.designUnitsPerEm
+ 0.5f
) / ppdip
;
1255 offsets
[i
].advanceOffset
= 0.0f
;
1256 offsets
[i
].ascenderOffset
= 0.0f
;
1259 /* FIXME: actually apply features */
1261 IDWriteFontFace1_Release(fontface1
);
1265 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1267 FLOAT advance
= 0.0f
;
1268 for (; start
< end
; start
++)
1269 advance
+= advances
[start
];
1273 static void apply_single_glyph_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
,
1274 FLOAT min_advance_width
, UINT32 g
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1275 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1277 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1278 FLOAT advance
= advances
[g
];
1279 FLOAT origin
= 0.0f
;
1281 if (props
[g
].isZeroWidthSpace
) {
1282 modified_advances
[g
] = advances
[g
];
1283 modified_offsets
[g
] = offsets
[g
];
1287 /* first apply negative spacing and check if we hit minimum width */
1288 if (leading_spacing
< 0.0f
) {
1289 advance
+= leading_spacing
;
1290 origin
-= leading_spacing
;
1292 if (trailing_spacing
< 0.0f
)
1293 advance
+= trailing_spacing
;
1295 if (advance
< min_advance_width
) {
1296 FLOAT half
= (min_advance_width
- advance
) / 2.0f
;
1300 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1302 else if (leading_spacing
< 0.0f
)
1303 origin
-= min_advance_width
- advance
;
1305 advance
= min_advance_width
;
1308 /* now apply positive spacing adjustments */
1309 if (leading_spacing
> 0.0f
) {
1310 advance
+= leading_spacing
;
1311 origin
-= leading_spacing
;
1313 if (trailing_spacing
> 0.0f
)
1314 advance
+= trailing_spacing
;
1316 modified_advances
[g
] = advance
;
1317 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1318 /* ascender is never touched, it's orthogonal to reading direction and is not
1319 affected by advance adjustments */
1320 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1323 static void apply_cluster_spacing(FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
,
1324 UINT32 start
, UINT32 end
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1325 FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1327 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1328 FLOAT advance
= get_cluster_advance(advances
, start
, end
);
1329 FLOAT origin
= 0.0f
;
1332 modified_advances
[start
] = advances
[start
];
1333 modified_advances
[end
-1] = advances
[end
-1];
1335 /* first apply negative spacing and check if we hit minimum width */
1336 if (leading_spacing
< 0.0f
) {
1337 advance
+= leading_spacing
;
1338 modified_advances
[start
] += leading_spacing
;
1339 origin
-= leading_spacing
;
1341 if (trailing_spacing
< 0.0f
) {
1342 advance
+= trailing_spacing
;
1343 modified_advances
[end
-1] += trailing_spacing
;
1346 advance
= min_advance_width
- advance
;
1347 if (advance
> 0.0f
) {
1348 /* additional spacing is only applied to leading and trailing glyph */
1349 FLOAT half
= advance
/ 2.0f
;
1353 modified_advances
[start
] += half
;
1354 modified_advances
[end
-1] += half
;
1356 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
) {
1358 modified_advances
[start
] += half
;
1359 modified_advances
[end
-1] += half
;
1361 else if (leading_spacing
< 0.0f
) {
1363 modified_advances
[start
] += advance
;
1366 modified_advances
[end
-1] += advance
;
1369 /* now apply positive spacing adjustments */
1370 if (leading_spacing
> 0.0f
) {
1371 modified_advances
[start
] += leading_spacing
;
1372 origin
-= leading_spacing
;
1374 if (trailing_spacing
> 0.0f
)
1375 modified_advances
[end
-1] += trailing_spacing
;
1377 for (g
= start
; g
< end
; g
++) {
1379 modified_offsets
[g
].advanceOffset
= offsets
[g
].advanceOffset
- origin
;
1380 modified_offsets
[g
].ascenderOffset
= offsets
[g
].ascenderOffset
;
1382 else if (g
== end
- 1)
1383 /* trailing glyph offset is not adjusted */
1384 modified_offsets
[g
] = offsets
[g
];
1386 /* for all glyphs within a cluster use original advances and offsets */
1387 modified_advances
[g
] = advances
[g
];
1388 modified_offsets
[g
] = offsets
[g
];
1393 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1395 UINT16 g
= clustermap
[start
];
1398 while (start
< text_len
&& clustermap
[++start
] == g
)
1403 /* Applies spacing adjustments to clusters.
1405 Adjustments are applied in the following order:
1407 1. Negative adjustments
1409 Leading and trailing spacing could be negative, at this step
1410 only negative ones are actually applied. Leading spacing is only
1411 applied to leading glyph, trailing - to trailing glyph.
1413 2. Minimum advance width
1415 Advances could only be reduced at this point or unchanged. In any
1416 case it's checked if cluster advance width is less than minimum width.
1417 If it's the case advance width is incremented up to minimum value.
1419 Important part is the direction in which this increment is applied;
1420 it depends on direction from which total cluster advance was trimmed
1421 at step 1. So it could be incremented from leading, trailing, or both
1422 sides. When applied to both sides, each side gets half of difference
1423 that brings advance to minimum width.
1425 3. Positive adjustments
1427 After minimum width rule was applied, positive spacing is applied in the same
1428 way as negative one on step 1.
1430 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1431 keeps its position in coordinate system where initial advance width is counted
1436 It's known that isZeroWidthSpace property keeps initial advance from changing.
1438 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1439 with more than one glyph.
1442 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1443 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1444 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1445 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1449 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1450 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1452 if (min_advance_width
< 0.0f
) {
1453 if (modified_advances
!= advances
)
1454 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1455 return E_INVALIDARG
;
1458 /* minimum advance is not applied if no adjustments were made */
1459 if (leading_spacing
== 0.0f
&& trailing_spacing
== 0.0f
) {
1460 memmove(modified_advances
, advances
, glyph_count
*sizeof(*advances
));
1461 memmove(modified_offsets
, offsets
, glyph_count
*sizeof(*offsets
));
1465 for (start
= 0; start
< len
;) {
1466 UINT32 length
= get_cluster_length(clustermap
, start
, len
);
1469 UINT32 g
= clustermap
[start
];
1471 apply_single_glyph_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1472 g
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1475 UINT32 g_start
, g_end
;
1477 g_start
= clustermap
[start
];
1478 g_end
= (start
+ length
< len
) ? clustermap
[start
+ length
] : glyph_count
;
1480 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
,
1481 g_start
, g_end
, advances
, offsets
, modified_advances
, modified_offsets
);
1490 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1491 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1492 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1494 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1495 baseline_coord
, exists
);
1499 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1500 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1502 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1506 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1507 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1509 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1510 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1513 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1514 DWRITE_SCRIPT_PROPERTIES
*props
)
1516 TRACE("(%u %p)\n", sa
.script
, props
);
1518 if (sa
.script
> Script_LastId
)
1519 return E_INVALIDARG
;
1521 *props
= dwritescripts_properties
[sa
.script
].props
;
1525 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1527 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1530 UINT16 script
= get_char_script(c
);
1531 return !dwritescripts_properties
[script
].is_complex
;
1535 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1536 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1541 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1547 return E_INVALIDARG
;
1554 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1555 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1556 if (is_char_from_simple_script(text
[i
])) {
1567 if (*is_simple
&& indices
) {
1568 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1570 return E_OUTOFMEMORY
;
1572 for (i
= 0; i
< *len_read
; i
++)
1573 codepoints
[i
] = text
[i
];
1575 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1576 heap_free(codepoints
);
1582 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1583 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1584 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1586 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1587 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1591 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1592 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1593 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1595 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1600 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1601 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1602 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1603 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1604 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1605 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1607 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
,
1608 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1609 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1613 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1614 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1616 static const DWRITE_MATRIX transforms
[] = {
1617 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1618 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1619 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1620 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1623 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1625 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1626 memset(m
, 0, sizeof(*m
));
1627 return E_INVALIDARG
;
1630 /* for sideways case simply rotate 90 degrees more */
1633 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1634 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1636 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1637 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1639 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1640 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1642 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1643 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1650 *m
= transforms
[angle
];
1652 /* shift components represent transform necessary to get from original point to
1653 rotated one in new coordinate system */
1654 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1655 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1656 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1662 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1663 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1664 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1666 const struct dwritescript_properties
*props
;
1670 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1673 if (sa
.script
> Script_LastId
)
1674 return E_INVALIDARG
;
1676 language
= get_opentype_language(locale
);
1677 props
= &dwritescripts_properties
[sa
.script
];
1678 *actual_tagcount
= 0;
1680 if (props
->scriptalttag
)
1681 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1683 if (*actual_tagcount
== 0)
1684 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1689 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1690 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1691 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1693 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1698 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1699 dwritetextanalyzer_QueryInterface
,
1700 dwritetextanalyzer_AddRef
,
1701 dwritetextanalyzer_Release
,
1702 dwritetextanalyzer_AnalyzeScript
,
1703 dwritetextanalyzer_AnalyzeBidi
,
1704 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1705 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1706 dwritetextanalyzer_GetGlyphs
,
1707 dwritetextanalyzer_GetGlyphPlacements
,
1708 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1709 dwritetextanalyzer1_ApplyCharacterSpacing
,
1710 dwritetextanalyzer1_GetBaseline
,
1711 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1712 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1713 dwritetextanalyzer1_GetScriptProperties
,
1714 dwritetextanalyzer1_GetTextComplexity
,
1715 dwritetextanalyzer1_GetJustificationOpportunities
,
1716 dwritetextanalyzer1_JustifyGlyphAdvances
,
1717 dwritetextanalyzer1_GetJustifiedGlyphs
,
1718 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1719 dwritetextanalyzer2_GetTypographicFeatures
,
1720 dwritetextanalyzer2_CheckTypographicFeature
1723 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1725 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1727 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1731 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1733 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1735 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1737 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1738 IsEqualIID(riid
, &IID_IUnknown
))
1741 IDWriteNumberSubstitution_AddRef(iface
);
1747 return E_NOINTERFACE
;
1750 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1752 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1753 ULONG ref
= InterlockedIncrement(&This
->ref
);
1754 TRACE("(%p)->(%d)\n", This
, ref
);
1758 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1760 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1761 ULONG ref
= InterlockedDecrement(&This
->ref
);
1763 TRACE("(%p)->(%d)\n", This
, ref
);
1766 heap_free(This
->locale
);
1773 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1774 dwritenumbersubstitution_QueryInterface
,
1775 dwritenumbersubstitution_AddRef
,
1776 dwritenumbersubstitution_Release
1779 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1780 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1782 struct dwrite_numbersubstitution
*substitution
;
1786 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1787 return E_INVALIDARG
;
1789 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1790 return E_INVALIDARG
;
1792 substitution
= heap_alloc(sizeof(*substitution
));
1794 return E_OUTOFMEMORY
;
1796 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1797 substitution
->ref
= 1;
1798 substitution
->ignore_user_override
= ignore_user_override
;
1799 substitution
->method
= method
;
1800 substitution
->locale
= heap_strdupW(locale
);
1801 if (locale
&& !substitution
->locale
) {
1802 heap_free(substitution
);
1803 return E_OUTOFMEMORY
;
1806 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1810 /* IDWriteFontFallback */
1811 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback
*iface
, REFIID riid
, void **obj
)
1813 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1815 TRACE("(%p)->(%s %p)\n", fallback
, debugstr_guid(riid
), obj
);
1817 if (IsEqualIID(riid
, &IID_IDWriteFontFallback
) || IsEqualIID(riid
, &IID_IUnknown
)) {
1819 IDWriteFontFallback_AddRef(iface
);
1824 return E_NOINTERFACE
;
1827 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback
*iface
)
1829 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1830 TRACE("(%p)\n", fallback
);
1831 return IDWriteFactory4_AddRef(fallback
->factory
);
1834 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback
*iface
)
1836 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1837 TRACE("(%p)\n", fallback
);
1838 return IDWriteFactory4_Release(fallback
->factory
);
1841 static int compare_fallback_mapping(const void *a
, const void *b
)
1843 UINT32 ch
= *(UINT32
*)a
;
1844 struct fallback_mapping
*mapping
= (struct fallback_mapping
*)b
;
1846 if (ch
> mapping
->range
.last
)
1848 else if (ch
< mapping
->range
.first
)
1854 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
1856 return bsearch(&ch
, fallback
->mappings
, fallback
->count
, sizeof(*fallback
->mappings
),
1857 compare_fallback_mapping
);
1860 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
1861 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
1863 IDWriteFontFamily
*family
;
1864 BOOL exists
= FALSE
;
1870 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
1877 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
1881 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
1882 IDWriteFontFamily_Release(family
);
1886 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
1891 for (i
= 0; i
< length
; i
++) {
1892 UINT16 script
= get_char_script(text
[i
]);
1895 if (script
== Script_Unknown
|| script
== Script_Common
) {
1900 /* stop on first unsupported character */
1902 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
1903 if (hr
== S_OK
&& exists
)
1912 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
1913 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
, IDWriteFont
**mapped_font
)
1915 const struct fallback_mapping
*mapping
;
1918 mapping
= find_fallback_mapping(fallback
, text
[0]);
1920 WARN("no mapping for 0x%x\n", text
[0]);
1924 /* now let's see what fallback can handle */
1925 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->family
, weight
, style
, stretch
, mapped_font
);
1927 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping
->family
),
1928 mapping
->range
.first
, mapping
->range
.last
, hr
);
1932 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
1934 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping
->family
), hr
);
1936 if (!*mapped_length
) {
1937 IDWriteFont_Release(*mapped_font
);
1938 *mapped_font
= NULL
;
1941 return *mapped_length
? S_OK
: E_FAIL
;
1944 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
1945 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
1946 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
1947 IDWriteFont
**ret_font
, FLOAT
*scale
)
1949 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1954 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
1955 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
1962 return E_INVALIDARG
;
1967 if (!basecollection
)
1968 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
1970 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
1974 if (basefamily
&& *basefamily
) {
1975 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
1979 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
1984 if (!*mapped_length
) {
1985 IDWriteFont
*mapped_font
;
1987 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
1989 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
1991 *mapped_length
= length
;
1997 IDWriteFont_Release(*ret_font
);
1998 *ret_font
= mapped_font
;
2007 static const IDWriteFontFallbackVtbl fontfallbackvtbl
= {
2008 fontfallback_QueryInterface
,
2009 fontfallback_AddRef
,
2010 fontfallback_Release
,
2011 fontfallback_MapCharacters
2014 HRESULT
create_system_fontfallback(IDWriteFactory4
*factory
, IDWriteFontFallback
**ret
)
2016 struct dwrite_fontfallback
*fallback
;
2020 fallback
= heap_alloc(sizeof(*fallback
));
2022 return E_OUTOFMEMORY
;
2024 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &fontfallbackvtbl
;
2025 fallback
->factory
= factory
;
2026 fallback
->mappings
= fontfallback_neutral_data
;
2027 fallback
->count
= sizeof(fontfallback_neutral_data
)/sizeof(fontfallback_neutral_data
[0]);
2028 IDWriteFactory4_GetSystemFontCollection(fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
2030 *ret
= &fallback
->IDWriteFontFallback_iface
;
2034 void release_system_fontfallback(IDWriteFontFallback
*iface
)
2036 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2037 IDWriteFontCollection1_Release(fallback
->systemcollection
);
2038 heap_free(fallback
);