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
24 #include "dwrite_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
29 extern const unsigned short wine_linebreak_table
[];
30 extern const unsigned short wine_scripts_table
[];
32 struct dwritescript_properties
{
33 DWRITE_SCRIPT_PROPERTIES props
;
34 UINT32 scripttag
; /* OpenType script tag */
35 UINT32 scriptalttag
; /* Version 2 tag, 0 is not defined */
37 const struct scriptshaping_ops
*ops
;
40 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
42 /* NOTE: keep this array synced with script ids from scripts.h */
43 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
44 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
45 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
46 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE
},
47 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
48 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
49 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
50 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
51 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
52 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
53 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE
},
54 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
55 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
56 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE
},
57 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
58 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
59 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE
},
60 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
61 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
62 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('c','a','k','m') },
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 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
72 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
73 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
74 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE
},
75 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
76 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
77 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
78 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
79 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
80 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE
},
81 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE
},
82 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
83 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE
},
84 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
85 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE
},
86 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
87 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
88 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
89 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
90 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
91 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
92 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE
},
93 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
94 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
95 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
96 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE
},
97 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
98 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
99 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE
},
100 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE
, &latn_shaping_ops
},
101 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
102 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
103 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
104 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
105 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
106 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
107 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
108 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
109 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE
},
110 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
111 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
112 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
113 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','c') },
115 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','o') },
116 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
117 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
118 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE
},
119 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
120 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE
},
121 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
122 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE
},
123 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE
},
124 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE
},
125 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
126 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
127 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
128 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
129 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE
},
130 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
131 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
132 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE
},
133 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE
},
134 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
135 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
136 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
137 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE
},
138 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
139 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
140 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
141 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE
},
142 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
143 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
144 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','h','r','d') },
145 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
146 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
147 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE
},
148 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','o','r','a') },
149 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
150 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
151 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE
},
152 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
153 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
154 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE
},
155 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
156 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
157 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('t','a','k','r') },
158 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE
},
159 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE
},
160 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE
},
161 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE
},
162 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE
},
163 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE
},
164 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
165 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
166 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE
},
167 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
168 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE
}
172 struct dwrite_numbersubstitution
{
173 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
176 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
178 BOOL ignore_user_override
;
181 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
183 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
186 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
188 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
189 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
190 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
196 static inline UINT16
get_char_script(WCHAR c
)
198 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
199 if (script
== Script_Unknown
) {
201 if (GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
) && (type
& C1_CNTRL
))
202 script
= Script_Common
;
207 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 len
, IDWriteTextAnalysisSink
*sink
)
209 DWRITE_SCRIPT_ANALYSIS sa
;
210 UINT32 pos
, i
, length
;
212 if (!len
) return S_OK
;
214 sa
.script
= get_char_script(*text
);
219 for (i
= 1; i
< len
; i
++)
221 UINT16 script
= get_char_script(text
[i
]);
223 /* Unknown type is ignored when preceded or followed by another script */
224 if (sa
.script
== Script_Unknown
) sa
.script
= script
;
225 if (script
== Script_Unknown
&& sa
.script
!= Script_Common
) script
= sa
.script
;
226 /* this is a length of a sequence to be reported next */
227 if (sa
.script
== script
) length
++;
229 if (sa
.script
!= script
)
233 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
234 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
235 if (FAILED(hr
)) return hr
;
242 /* 1 length case or normal completion call */
243 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
244 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
247 struct linebreaking_state
{
248 DWRITE_LINE_BREAKPOINT
*breakpoints
;
252 enum BreakConditionLocation
{
253 BreakConditionBefore
,
257 enum linebreaking_classes
{
300 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
301 set to "can break" and could only be changed once. */
302 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
303 struct linebreaking_state
*state
)
305 if (location
== BreakConditionBefore
) {
306 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
308 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
310 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
313 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
315 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
316 if (pos
+ 1 < state
->count
)
317 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
321 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
323 struct linebreaking_state state
;
327 break_class
= heap_alloc(count
*sizeof(short));
329 return E_OUTOFMEMORY
;
331 state
.breakpoints
= breakpoints
;
334 /* LB31 - allow breaks everywhere. It will be overridden if needed as
335 other rules dictate. */
336 for (i
= 0; i
< count
; i
++)
338 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
340 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
341 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
342 breakpoints
[i
].isWhitespace
= break_class
[i
] == b_BK
|| break_class
[i
] == b_ZW
|| break_class
[i
] == b_SP
|| isspaceW(text
[i
]);
343 breakpoints
[i
].isSoftHyphen
= FALSE
;
344 breakpoints
[i
].padding
= 0;
346 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
347 switch (break_class
[i
])
353 break_class
[i
] = b_AL
;
356 break_class
[i
] = b_NS
;
361 /* LB2 - never break at the start */
362 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
363 /* LB3 - always break at the end. This one is ignored. */
365 for (i
= 0; i
< count
; i
++)
367 switch (break_class
[i
])
371 /* LB5 - don't break CR x LF */
372 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
374 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
375 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
381 /* LB4 - LB5 - always break after hard breaks */
382 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
383 /* LB6 - do not break before hard breaks */
384 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
386 /* LB7 - do not break before spaces */
388 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
391 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
392 /* LB8 - break before character after zero-width space, skip spaces in-between */
393 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
395 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
401 for (i
= 0; i
< count
; i
++)
403 if (break_class
[i
] == b_CM
)
407 switch (break_class
[i
-1])
415 break_class
[i
] = b_AL
;
418 break_class
[i
] = break_class
[i
-1];
421 else break_class
[i
] = b_AL
;
425 for (i
= 0; i
< count
; i
++)
427 switch (break_class
[i
])
429 /* LB11 - don't break before and after word joiner */
431 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
432 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
434 /* LB12 - don't break after glue */
436 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
440 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
441 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
450 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
454 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
455 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
456 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
463 while (j
< count
-1 && break_class
[j
] == b_SP
)
465 if (break_class
[j
] == b_OP
)
467 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
472 while(j
> 0 && break_class
[j
] == b_SP
)
474 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
475 for (j
++; j
<= i
; j
++)
476 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
481 while (j
< count
&& break_class
[j
] == b_SP
)
483 if (break_class
[j
] == b_B2
)
485 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
490 for (i
= 0; i
< count
; i
++)
492 switch(break_class
[i
])
494 /* LB18 - break is allowed after space */
496 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
498 /* LB19 - don't break before or after quotation mark */
500 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
501 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
505 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
506 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
512 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
515 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
520 switch (break_class
[i
+1])
524 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
531 switch (break_class
[i
-1])
538 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
547 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
548 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
549 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
550 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
551 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
552 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
554 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
555 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
556 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
557 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
558 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
559 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
562 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
563 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
564 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
565 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
566 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
567 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
568 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
569 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
570 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
571 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
572 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
573 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
574 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
575 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
576 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
579 if (break_class
[i
] == b_JL
)
581 switch (break_class
[i
+1])
587 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
590 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
591 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
592 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
593 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
594 break_class
[i
+1] == b_JT
)
595 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
598 switch (break_class
[i
])
605 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
606 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
608 if (break_class
[i
] == b_PO
)
610 switch (break_class
[i
+1])
617 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
622 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
623 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
624 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
625 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
626 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
629 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
630 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
631 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
634 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
635 break_class
[i
+1] == b_OP
)
636 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
637 if (break_class
[i
] == b_CP
&&
638 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
639 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
642 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
643 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
647 heap_free(break_class
);
651 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
653 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
655 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
656 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
657 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
658 IsEqualIID(riid
, &IID_IUnknown
))
665 return E_NOINTERFACE
;
669 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
674 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
679 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
680 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
686 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
688 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
689 if (FAILED(hr
)) return hr
;
691 return analyze_script(text
, len
, sink
);
694 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
695 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
697 UINT8
*levels
= NULL
, *explicit = NULL
;
698 UINT8 baselevel
, level
, explicit_level
;
704 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
709 /* get some, check for length */
712 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
713 if (FAILED(hr
)) return hr
;
718 buff
= heap_alloc(length
*sizeof(WCHAR
));
720 return E_OUTOFMEMORY
;
721 memcpy(buff
, text
, len
*sizeof(WCHAR
));
724 while (read
< length
&& text
) {
727 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
730 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
737 levels
= heap_alloc(length
*sizeof(*levels
));
738 explicit = heap_alloc(length
*sizeof(*explicit));
740 if (!levels
|| !explicit) {
745 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
746 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
751 explicit_level
= explicit[0];
753 for (i
= 1; i
< length
; i
++) {
754 if (levels
[i
] != level
|| explicit[i
] != explicit_level
) {
755 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, i
- pos
, explicit_level
, level
);
759 explicit_level
= explicit[i
];
764 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, length
- pos
, explicit_level
, level
);
775 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
776 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
778 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
782 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
783 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
785 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
791 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
796 /* get some, check for length */
799 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
800 if (FAILED(hr
)) return hr
;
805 buff
= heap_alloc(length
*sizeof(WCHAR
));
807 return E_OUTOFMEMORY
;
808 memcpy(buff
, text
, len
*sizeof(WCHAR
));
811 while (read
< length
&& text
) {
814 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
817 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
824 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
830 hr
= analyze_linebreaks(text
, length
, breakpoints
);
834 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
837 heap_free(breakpoints
);
843 static UINT32
get_opentype_language(const WCHAR
*locale
)
845 UINT32 language
= DWRITE_FONT_FEATURE_TAG_DEFAULT
;
849 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
850 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
856 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
857 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
858 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
859 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
860 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
861 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
862 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
864 const struct dwritescript_properties
*scriptprops
;
865 struct scriptshaping_context context
;
866 struct scriptshaping_cache
*cache
= NULL
;
867 BOOL update_cluster
, need_vertical
;
868 IDWriteFontFace1
*fontface1
;
874 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
875 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
876 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
878 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
880 if (max_glyph_count
< length
)
881 return E_NOT_SUFFICIENT_BUFFER
;
884 FIXME("number substitution is not supported.\n");
886 for (i
= 0; i
< length
; i
++) {
887 /* FIXME: set to better values */
888 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
889 glyph_props
[i
].isClusterStart
= 1;
890 glyph_props
[i
].isDiacritic
= 0;
891 glyph_props
[i
].isZeroWidthSpace
= 0;
892 glyph_props
[i
].reserved
= 0;
894 /* FIXME: have the shaping engine set this */
895 text_props
[i
].isShapedAlone
= 0;
896 text_props
[i
].reserved
= 0;
901 for (; i
< max_glyph_count
; i
++) {
902 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
903 glyph_props
[i
].isClusterStart
= 0;
904 glyph_props
[i
].isDiacritic
= 0;
905 glyph_props
[i
].isZeroWidthSpace
= 0;
906 glyph_props
[i
].reserved
= 0;
909 string
= heap_alloc(sizeof(WCHAR
)*length
);
911 return E_OUTOFMEMORY
;
913 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
915 WARN("failed to get IDWriteFontFace1\n");
917 need_vertical
= is_sideways
&& fontface1
&& IDWriteFontFace1_HasVerticalGlyphVariants(fontface1
);
919 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
922 if (!update_cluster
) {
923 codepoint
= decode_surrogate_pair(text
, i
, length
);
925 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
926 string
[i
] = codepoint
;
930 string
[i
+1] = text
[i
+1];
931 update_cluster
= TRUE
;
934 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
941 hr
= IDWriteFontFace1_GetVerticalGlyphVariants(fontface1
, 1, &glyph_indices
[g
], &vertical
);
943 glyph_indices
[g
] = vertical
;
951 update_cluster
= FALSE
;
952 /* mark surrogate halves with same cluster */
953 clustermap
[i
] = clustermap
[i
-1];
954 /* update following clusters */
955 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
959 *actual_glyph_count
= g
;
961 hr
= create_scriptshaping_cache(fontface
, &cache
);
965 context
.cache
= cache
;
967 context
.length
= length
;
968 context
.is_rtl
= is_rtl
;
969 context
.max_glyph_count
= max_glyph_count
;
970 context
.language_tag
= get_opentype_language(locale
);
972 scriptprops
= &dwritescripts_properties
[script
];
973 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
974 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
979 /* FIXME: apply default features */
981 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
982 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
984 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
988 IDWriteFontFace1_Release(fontface1
);
989 release_scriptshaping_cache(cache
);
995 static inline FLOAT
get_scaled_advance_width(INT32 advance
, FLOAT emSize
, const DWRITE_FONT_METRICS
*metrics
)
997 return (FLOAT
)advance
* emSize
/ (FLOAT
)metrics
->designUnitsPerEm
;
1000 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1001 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1002 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1003 UINT32 glyph_count
, IDWriteFontFace
*fontface
, FLOAT emSize
, BOOL is_sideways
, BOOL is_rtl
,
1004 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1005 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
*advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1007 DWRITE_FONT_METRICS metrics
;
1008 IDWriteFontFace1
*fontface1
;
1012 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %p %s %p %p %u %p %p)\n", debugstr_w(text
),
1013 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1014 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, advances
, offsets
);
1016 if (glyph_count
== 0)
1019 hr
= IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void**)&fontface1
);
1021 WARN("failed to get IDWriteFontFace1.\n");
1025 IDWriteFontFace_GetMetrics(fontface
, &metrics
);
1026 for (i
= 0; i
< glyph_count
; i
++) {
1029 hr
= IDWriteFontFace1_GetDesignGlyphAdvances(fontface1
, 1, &glyphs
[i
], &a
, is_sideways
);
1033 advances
[i
] = get_scaled_advance_width(a
, emSize
, &metrics
);
1034 offsets
[i
].advanceOffset
= 0.0;
1035 offsets
[i
].ascenderOffset
= 0.0;
1038 /* FIXME: actually apply features */
1042 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1043 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1044 UINT32 text_len
, UINT16
const* glyph_indices
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1045 UINT32 glyph_count
, IDWriteFontFace
* font_face
, FLOAT fontEmSize
, FLOAT pixels_per_dip
,
1046 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1047 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1048 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
* glyph_advances
, DWRITE_GLYPH_OFFSET
* glyph_offsets
)
1050 FIXME("(%s %p %p %u %p %p %u %p %f %f %p %d %d %d %p %s %p %p %u %p %p): stub\n", debugstr_w(text
),
1051 clustermap
, props
, text_len
, glyph_indices
, glyph_props
, glyph_count
, font_face
, fontEmSize
, pixels_per_dip
,
1052 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
1053 feature_ranges
, glyph_advances
, glyph_offsets
);
1057 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1058 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1059 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1060 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1062 FIXME("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p): stub\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1063 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1067 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1068 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1069 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1071 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1072 baseline_coord
, exists
);
1076 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1077 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1079 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1083 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1084 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1086 FIXME("(%d %d %p): stub\n", angle
, is_sideways
, transform
);
1090 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1091 DWRITE_SCRIPT_PROPERTIES
*props
)
1093 TRACE("(%u %p)\n", sa
.script
, props
);
1095 if (sa
.script
> Script_LastId
)
1096 return E_INVALIDARG
;
1098 *props
= dwritescripts_properties
[sa
.script
].props
;
1102 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1104 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1107 UINT16 script
= get_char_script(c
);
1108 return !dwritescripts_properties
[script
].is_complex
;
1112 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1113 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1118 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1124 return E_INVALIDARG
;
1131 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1132 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1133 if (is_char_from_simple_script(text
[i
])) {
1144 if (*is_simple
&& indices
) {
1145 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1147 return E_OUTOFMEMORY
;
1149 for (i
= 0; i
< *len_read
; i
++)
1150 codepoints
[i
] = text
[i
];
1152 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1153 heap_free(codepoints
);
1159 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1160 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1161 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1163 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1164 debugstr_w(text
), clustermap
, prop
, jo
);
1168 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1169 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1170 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1172 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1177 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1178 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1179 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1180 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1181 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1182 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1184 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
,
1185 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1186 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1190 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1191 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*transform
)
1193 FIXME("(%d %d %.2f %.2f %p): stub\n", angle
, is_sideways
, originX
, originY
, transform
);
1197 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1198 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1199 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1201 const struct dwritescript_properties
*props
;
1205 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1208 if (sa
.script
> Script_LastId
)
1209 return E_INVALIDARG
;
1211 language
= get_opentype_language(locale
);
1212 props
= &dwritescripts_properties
[sa
.script
];
1213 *actual_tagcount
= 0;
1215 if (props
->scriptalttag
)
1216 hr
= opentype_get_typographic_features(fontface
, props
->scriptalttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1218 if (*actual_tagcount
== 0)
1219 hr
= opentype_get_typographic_features(fontface
, props
->scripttag
, language
, max_tagcount
, actual_tagcount
, tags
);
1224 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1225 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1226 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1228 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1233 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1234 dwritetextanalyzer_QueryInterface
,
1235 dwritetextanalyzer_AddRef
,
1236 dwritetextanalyzer_Release
,
1237 dwritetextanalyzer_AnalyzeScript
,
1238 dwritetextanalyzer_AnalyzeBidi
,
1239 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1240 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1241 dwritetextanalyzer_GetGlyphs
,
1242 dwritetextanalyzer_GetGlyphPlacements
,
1243 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1244 dwritetextanalyzer1_ApplyCharacterSpacing
,
1245 dwritetextanalyzer1_GetBaseline
,
1246 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1247 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1248 dwritetextanalyzer1_GetScriptProperties
,
1249 dwritetextanalyzer1_GetTextComplexity
,
1250 dwritetextanalyzer1_GetJustificationOpportunities
,
1251 dwritetextanalyzer1_JustifyGlyphAdvances
,
1252 dwritetextanalyzer1_GetJustifiedGlyphs
,
1253 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1254 dwritetextanalyzer2_GetTypographicFeatures
,
1255 dwritetextanalyzer2_CheckTypographicFeature
1258 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1260 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1262 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1266 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1268 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1270 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1272 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1273 IsEqualIID(riid
, &IID_IUnknown
))
1276 IDWriteNumberSubstitution_AddRef(iface
);
1282 return E_NOINTERFACE
;
1285 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1287 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1288 ULONG ref
= InterlockedIncrement(&This
->ref
);
1289 TRACE("(%p)->(%d)\n", This
, ref
);
1293 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1295 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1296 ULONG ref
= InterlockedDecrement(&This
->ref
);
1298 TRACE("(%p)->(%d)\n", This
, ref
);
1301 heap_free(This
->locale
);
1308 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1309 dwritenumbersubstitution_QueryInterface
,
1310 dwritenumbersubstitution_AddRef
,
1311 dwritenumbersubstitution_Release
1314 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1315 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1317 struct dwrite_numbersubstitution
*substitution
;
1321 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1322 return E_INVALIDARG
;
1324 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1325 return E_INVALIDARG
;
1327 substitution
= heap_alloc(sizeof(*substitution
));
1329 return E_OUTOFMEMORY
;
1331 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1332 substitution
->ref
= 1;
1333 substitution
->ignore_user_override
= ignore_user_override
;
1334 substitution
->method
= method
;
1335 substitution
->locale
= heap_strdupW(locale
);
1336 if (locale
&& !substitution
->locale
) {
1337 heap_free(substitution
);
1338 return E_OUTOFMEMORY
;
1341 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;