d3d11: Implement d3d11_texture2d_GetDevice.
[wine.git] / dlls / dwrite / analyzer.c
blob448a212af01068caee8089c3a0cea9f66132dd20
1 /*
2 * Text analyzer
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
22 #define COBJMACROS
24 #include <math.h>
26 #include "dwrite_private.h"
27 #include "scripts.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
31 extern const unsigned short wine_linebreak_table[];
32 extern const unsigned short wine_scripts_table[];
34 struct dwritescript_properties {
35 DWRITE_SCRIPT_PROPERTIES props;
36 UINT32 scripttag; /* OpenType script tag */
37 UINT32 scriptalttag; /* Version 2 tag, 0 is not defined */
38 BOOL is_complex;
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 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE },
49 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
50 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
51 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
52 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
53 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
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 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
64 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('c','a','k','m') },
65 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
66 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE },
67 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
68 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
69 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
70 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
71 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE },
72 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE },
73 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
74 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
75 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
76 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE },
77 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
78 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
79 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
80 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
81 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
82 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE },
83 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE },
84 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
85 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE },
86 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
87 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE },
88 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
89 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
90 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
91 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
92 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
93 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
94 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE },
95 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
96 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
97 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
98 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE },
99 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
100 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
101 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE },
102 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE, &latn_shaping_ops },
103 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
104 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
105 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
106 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
107 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
108 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
109 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
110 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
111 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE },
112 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
113 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
115 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
116 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','c') },
117 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('m','e','r','o') },
118 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
119 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
120 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE },
121 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
122 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE },
123 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
124 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE },
125 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE },
126 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE },
127 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
128 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
129 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
130 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
131 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE },
132 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
133 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
134 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE },
135 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE },
136 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
137 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
138 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
139 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE },
140 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
141 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
142 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
143 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE },
144 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
145 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
146 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','h','r','d') },
147 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
148 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
149 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE },
150 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('s','o','r','a') },
151 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
152 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
153 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE },
154 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
155 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
156 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE },
157 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
158 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
159 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 }, _OT('t','a','k','r') },
160 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE },
161 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE },
162 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE },
163 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE },
164 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE },
165 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE },
166 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
167 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
168 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE },
169 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
170 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE }
172 #undef _OT
174 struct dwrite_numbersubstitution {
175 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
176 LONG ref;
178 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
179 WCHAR *locale;
180 BOOL ignore_user_override;
183 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
185 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
188 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
190 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
191 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
192 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
193 return ch;
195 return 0;
198 static inline UINT16 get_char_script(WCHAR c)
200 UINT16 script = get_table_entry(wine_scripts_table, c);
201 if (script == Script_Unknown) {
202 WORD type;
203 if (GetStringTypeW(CT_CTYPE1, &c, 1, &type) && (type & C1_CNTRL))
204 script = Script_Common;
206 return script;
209 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 len, IDWriteTextAnalysisSink *sink)
211 DWRITE_SCRIPT_ANALYSIS sa;
212 UINT32 pos, i, length;
214 if (!len) return S_OK;
216 sa.script = get_char_script(*text);
218 pos = position;
219 length = 1;
221 for (i = 1; i < len; i++)
223 UINT16 script = get_char_script(text[i]);
225 /* Unknown type is ignored when preceded or followed by another script */
226 if (sa.script == Script_Unknown) sa.script = script;
227 if (script == Script_Unknown && sa.script != Script_Common) script = sa.script;
228 /* this is a length of a sequence to be reported next */
229 if (sa.script == script) length++;
231 if (sa.script != script)
233 HRESULT hr;
235 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
236 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
237 if (FAILED(hr)) return hr;
238 pos = position + i;
239 length = 1;
240 sa.script = script;
244 /* 1 length case or normal completion call */
245 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
246 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
249 struct linebreaking_state {
250 DWRITE_LINE_BREAKPOINT *breakpoints;
251 UINT32 count;
254 enum BreakConditionLocation {
255 BreakConditionBefore,
256 BreakConditionAfter
259 enum linebreaking_classes {
260 b_BK = 1,
261 b_CR,
262 b_LF,
263 b_CM,
264 b_SG,
265 b_GL,
266 b_CB,
267 b_SP,
268 b_ZW,
269 b_NL,
270 b_WJ,
271 b_JL,
272 b_JV,
273 b_JT,
274 b_H2,
275 b_H3,
276 b_XX,
277 b_OP,
278 b_CL,
279 b_CP,
280 b_QU,
281 b_NS,
282 b_EX,
283 b_SY,
284 b_IS,
285 b_PR,
286 b_PO,
287 b_NU,
288 b_AL,
289 b_ID,
290 b_IN,
291 b_HY,
292 b_BB,
293 b_BA,
294 b_SA,
295 b_AI,
296 b_B2,
297 b_HL,
298 b_CJ,
299 b_RI
302 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
303 set to "can break" and could only be changed once. */
304 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
305 struct linebreaking_state *state)
307 if (location == BreakConditionBefore) {
308 if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK)
309 return;
310 state->breakpoints[pos].breakConditionBefore = condition;
311 if (pos > 0)
312 state->breakpoints[pos-1].breakConditionAfter = condition;
314 else {
315 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
316 return;
317 state->breakpoints[pos].breakConditionAfter = condition;
318 if (pos + 1 < state->count)
319 state->breakpoints[pos+1].breakConditionBefore = condition;
323 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
325 struct linebreaking_state state;
326 short *break_class;
327 int i, j;
329 break_class = heap_alloc(count*sizeof(short));
330 if (!break_class)
331 return E_OUTOFMEMORY;
333 state.breakpoints = breakpoints;
334 state.count = count;
336 /* LB31 - allow breaks everywhere. It will be overridden if needed as
337 other rules dictate. */
338 for (i = 0; i < count; i++)
340 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
342 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK;
343 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK;
344 breakpoints[i].isWhitespace = break_class[i] == b_BK || break_class[i] == b_ZW || break_class[i] == b_SP || isspaceW(text[i]);
345 breakpoints[i].isSoftHyphen = FALSE;
346 breakpoints[i].padding = 0;
348 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
349 switch (break_class[i])
351 case b_AI:
352 case b_SA:
353 case b_SG:
354 case b_XX:
355 break_class[i] = b_AL;
356 break;
357 case b_CJ:
358 break_class[i] = b_NS;
359 break;
363 /* LB2 - never break at the start */
364 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
365 /* LB3 - always break at the end. This one is ignored. */
367 for (i = 0; i < count; i++)
369 switch (break_class[i])
371 /* LB4 - LB6 */
372 case b_CR:
373 /* LB5 - don't break CR x LF */
374 if (i < count-1 && break_class[i+1] == b_LF)
376 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
377 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
378 break;
380 case b_LF:
381 case b_NL:
382 case b_BK:
383 /* LB4 - LB5 - always break after hard breaks */
384 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
385 /* LB6 - do not break before hard breaks */
386 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
387 break;
388 /* LB7 - do not break before spaces */
389 case b_SP:
390 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
391 break;
392 case b_ZW:
393 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
394 /* LB8 - break before character after zero-width space, skip spaces in-between */
395 while (i < count-1 && break_class[i+1] == b_SP)
396 i++;
397 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
398 break;
402 /* LB9 - LB10 */
403 for (i = 0; i < count; i++)
405 if (break_class[i] == b_CM)
407 if (i > 0)
409 switch (break_class[i-1])
411 case b_SP:
412 case b_BK:
413 case b_CR:
414 case b_LF:
415 case b_NL:
416 case b_ZW:
417 break_class[i] = b_AL;
418 break;
419 default:
420 break_class[i] = break_class[i-1];
423 else break_class[i] = b_AL;
427 for (i = 0; i < count; i++)
429 switch (break_class[i])
431 /* LB11 - don't break before and after word joiner */
432 case b_WJ:
433 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
434 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
435 break;
436 /* LB12 - don't break after glue */
437 case b_GL:
438 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
439 /* LB12a */
440 if (i > 0)
442 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
443 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
445 break;
446 /* LB13 */
447 case b_CL:
448 case b_CP:
449 case b_EX:
450 case b_IS:
451 case b_SY:
452 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
453 break;
454 /* LB14 */
455 case b_OP:
456 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
457 while (i < count-1 && break_class[i+1] == b_SP) {
458 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
459 i++;
461 break;
462 /* LB15 */
463 case b_QU:
464 j = i+1;
465 while (j < count-1 && break_class[j] == b_SP)
466 j++;
467 if (break_class[j] == b_OP)
468 for (; j > i; j--)
469 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
470 break;
471 /* LB16 */
472 case b_NS:
473 j = i-1;
474 while(j > 0 && break_class[j] == b_SP)
475 j--;
476 if (break_class[j] == b_CL || break_class[j] == b_CP)
477 for (j++; j <= i; j++)
478 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
479 break;
480 /* LB17 */
481 case b_B2:
482 j = i+1;
483 while (j < count && break_class[j] == b_SP)
484 j++;
485 if (break_class[j] == b_B2)
486 for (; j > i; j--)
487 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
488 break;
492 for (i = 0; i < count; i++)
494 switch(break_class[i])
496 /* LB18 - break is allowed after space */
497 case b_SP:
498 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
499 break;
500 /* LB19 - don't break before or after quotation mark */
501 case b_QU:
502 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
503 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
504 break;
505 /* LB20 */
506 case b_CB:
507 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
508 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
509 break;
510 /* LB21 */
511 case b_BA:
512 case b_HY:
513 case b_NS:
514 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
515 break;
516 case b_BB:
517 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
518 break;
519 /* LB21a */
520 case b_HL:
521 if (i < count-2)
522 switch (break_class[i+1])
524 case b_HY:
525 case b_BA:
526 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
528 break;
529 /* LB22 */
530 case b_IN:
531 if (i > 0)
533 switch (break_class[i-1])
535 case b_AL:
536 case b_HL:
537 case b_ID:
538 case b_IN:
539 case b_NU:
540 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
543 break;
546 if (i < count-1)
548 /* LB23 */
549 if ((break_class[i] == b_ID && break_class[i+1] == b_PO) ||
550 (break_class[i] == b_AL && break_class[i+1] == b_NU) ||
551 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
552 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
553 (break_class[i] == b_NU && break_class[i+1] == b_HL))
554 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
555 /* LB24 */
556 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
557 (break_class[i] == b_PR && break_class[i+1] == b_AL) ||
558 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
559 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
560 (break_class[i] == b_PO && break_class[i+1] == b_HL))
561 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
563 /* LB25 */
564 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
565 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
566 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
567 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
568 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
569 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
570 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
571 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
572 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
573 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
574 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
575 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
576 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
577 (break_class[i] == b_SY && break_class[i+1] == b_NU))
578 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
580 /* LB26 */
581 if (break_class[i] == b_JL)
583 switch (break_class[i+1])
585 case b_JL:
586 case b_JV:
587 case b_H2:
588 case b_H3:
589 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
592 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
593 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
594 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
595 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
596 break_class[i+1] == b_JT)
597 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
599 /* LB27 */
600 switch (break_class[i])
602 case b_JL:
603 case b_JV:
604 case b_JT:
605 case b_H2:
606 case b_H3:
607 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
608 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
610 if (break_class[i] == b_PO)
612 switch (break_class[i+1])
614 case b_JL:
615 case b_JV:
616 case b_JT:
617 case b_H2:
618 case b_H3:
619 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
623 /* LB28 */
624 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
625 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
626 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
627 (break_class[i] == b_HL && break_class[i+1] == b_HL))
628 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
630 /* LB29 */
631 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
632 (break_class[i] == b_IS && break_class[i+1] == b_HL))
633 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
635 /* LB30 */
636 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
637 break_class[i+1] == b_OP)
638 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
639 if (break_class[i] == b_CP &&
640 (break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU))
641 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
643 /* LB30a */
644 if (break_class[i] == b_RI && break_class[i+1] == b_RI)
645 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
649 heap_free(break_class);
650 return S_OK;
653 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
655 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
657 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
658 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
659 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
660 IsEqualIID(riid, &IID_IUnknown))
662 *obj = iface;
663 return S_OK;
666 *obj = NULL;
667 return E_NOINTERFACE;
670 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
672 return 2;
675 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
677 return 1;
680 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
681 data after a first request. */
682 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
684 HRESULT hr;
685 UINT32 len;
687 *buff = NULL;
688 *text = NULL;
689 len = 0;
690 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
691 if (FAILED(hr)) return hr;
693 if (len < length) {
694 UINT32 read;
696 *buff = heap_alloc(length*sizeof(WCHAR));
697 if (!*buff)
698 return E_OUTOFMEMORY;
699 memcpy(*buff, *text, len*sizeof(WCHAR));
700 read = len;
702 while (read < length && *text) {
703 *text = NULL;
704 len = 0;
705 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
706 if (FAILED(hr)) {
707 heap_free(*buff);
708 return hr;
710 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
711 read += len;
714 *text = *buff;
717 return hr;
720 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
721 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
723 WCHAR *buff = NULL;
724 const WCHAR *text;
725 HRESULT hr;
727 TRACE("(%p %u %u %p)\n", source, position, length, sink);
729 if (length == 0)
730 return S_OK;
732 hr = get_text_source_ptr(source, position, length, &text, &buff);
733 if (FAILED(hr))
734 return hr;
736 hr = analyze_script(text, position, length, sink);
737 heap_free(buff);
739 return hr;
742 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
743 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
745 UINT8 *levels = NULL, *explicit = NULL;
746 UINT8 baselevel, level, explicit_level;
747 WCHAR *buff = NULL;
748 const WCHAR *text;
749 UINT32 pos, i;
750 HRESULT hr;
752 TRACE("(%p %u %u %p)\n", source, position, length, sink);
754 if (length == 0)
755 return S_OK;
757 hr = get_text_source_ptr(source, position, length, &text, &buff);
758 if (FAILED(hr))
759 return hr;
761 levels = heap_alloc(length*sizeof(*levels));
762 explicit = heap_alloc(length*sizeof(*explicit));
764 if (!levels || !explicit) {
765 hr = E_OUTOFMEMORY;
766 goto done;
769 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
770 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
771 if (FAILED(hr))
772 goto done;
774 level = levels[0];
775 explicit_level = explicit[0];
776 pos = 0;
777 for (i = 1; i < length; i++) {
778 if (levels[i] != level || explicit[i] != explicit_level) {
779 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, i - pos, explicit_level, level);
780 if (FAILED(hr))
781 break;
782 level = levels[i];
783 explicit_level = explicit[i];
784 pos = i;
787 if (i == length - 1)
788 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, length - pos, explicit_level, level);
791 done:
792 heap_free(explicit);
793 heap_free(levels);
794 heap_free(buff);
796 return hr;
799 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
800 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
802 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
803 return S_OK;
806 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
807 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
809 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
810 WCHAR *buff = NULL;
811 const WCHAR *text;
812 HRESULT hr;
813 UINT32 len;
815 TRACE("(%p %u %u %p)\n", source, position, length, sink);
817 if (length == 0)
818 return S_OK;
820 /* get some, check for length */
821 text = NULL;
822 len = 0;
823 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
824 if (FAILED(hr)) return hr;
826 if (len < length) {
827 UINT32 read;
829 buff = heap_alloc(length*sizeof(WCHAR));
830 if (!buff)
831 return E_OUTOFMEMORY;
832 memcpy(buff, text, len*sizeof(WCHAR));
833 read = len;
835 while (read < length && text) {
836 text = NULL;
837 len = 0;
838 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
839 if (FAILED(hr))
840 goto done;
841 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
842 read += len;
845 text = buff;
848 breakpoints = heap_alloc(length*sizeof(*breakpoints));
849 if (!breakpoints) {
850 hr = E_OUTOFMEMORY;
851 goto done;
854 hr = analyze_linebreaks(text, length, breakpoints);
855 if (FAILED(hr))
856 goto done;
858 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
860 done:
861 heap_free(breakpoints);
862 heap_free(buff);
864 return hr;
867 static UINT32 get_opentype_language(const WCHAR *locale)
869 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
871 if (locale) {
872 WCHAR tag[5];
873 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
874 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
877 return language;
880 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
881 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
882 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
883 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
884 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
885 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
886 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
888 const struct dwritescript_properties *scriptprops;
889 struct scriptshaping_context context;
890 struct scriptshaping_cache *cache = NULL;
891 BOOL update_cluster, need_vertical;
892 IDWriteFontFace1 *fontface1;
893 WCHAR *string;
894 UINT32 i, g;
895 HRESULT hr = S_OK;
896 UINT16 script;
898 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
899 length, fontface, is_sideways, is_rtl, analysis, debugstr_w(locale), substitution, features, feature_range_len,
900 feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices, glyph_props, actual_glyph_count);
902 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
904 if (max_glyph_count < length)
905 return E_NOT_SUFFICIENT_BUFFER;
907 if (substitution)
908 FIXME("number substitution is not supported.\n");
910 for (i = 0; i < length; i++) {
911 /* FIXME: set to better values */
912 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
913 glyph_props[i].isClusterStart = 1;
914 glyph_props[i].isDiacritic = 0;
915 glyph_props[i].isZeroWidthSpace = 0;
916 glyph_props[i].reserved = 0;
918 /* FIXME: have the shaping engine set this */
919 text_props[i].isShapedAlone = 0;
920 text_props[i].reserved = 0;
922 clustermap[i] = i;
925 for (; i < max_glyph_count; i++) {
926 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
927 glyph_props[i].isClusterStart = 0;
928 glyph_props[i].isDiacritic = 0;
929 glyph_props[i].isZeroWidthSpace = 0;
930 glyph_props[i].reserved = 0;
933 string = heap_alloc(sizeof(WCHAR)*length);
934 if (!string)
935 return E_OUTOFMEMORY;
937 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
938 if (FAILED(hr))
939 WARN("failed to get IDWriteFontFace1\n");
941 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
943 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
944 UINT32 codepoint;
946 if (!update_cluster) {
947 codepoint = decode_surrogate_pair(text, i, length);
948 if (!codepoint) {
949 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
950 string[i] = codepoint;
952 else {
953 string[i] = text[i];
954 string[i+1] = text[i+1];
955 update_cluster = TRUE;
958 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
959 if (FAILED(hr))
960 goto done;
962 if (need_vertical) {
963 UINT16 vertical;
965 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
966 if (hr == S_OK)
967 glyph_indices[g] = vertical;
970 g++;
972 else {
973 INT32 k;
975 update_cluster = FALSE;
976 /* mark surrogate halves with same cluster */
977 clustermap[i] = clustermap[i-1];
978 /* update following clusters */
979 for (k = i + 1; k >= 0 && k < length; k++)
980 clustermap[k]--;
983 *actual_glyph_count = g;
985 hr = create_scriptshaping_cache(fontface, &cache);
986 if (FAILED(hr))
987 goto done;
989 context.cache = cache;
990 context.text = text;
991 context.length = length;
992 context.is_rtl = is_rtl;
993 context.max_glyph_count = max_glyph_count;
994 context.language_tag = get_opentype_language(locale);
996 scriptprops = &dwritescripts_properties[script];
997 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
998 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
999 if (FAILED(hr))
1000 goto done;
1003 /* FIXME: apply default features */
1005 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1006 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1007 else
1008 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1010 done:
1011 if (fontface1)
1012 IDWriteFontFace1_Release(fontface1);
1013 release_scriptshaping_cache(cache);
1014 heap_free(string);
1016 return hr;
1019 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1020 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1021 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1022 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1023 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1024 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1026 DWRITE_FONT_METRICS metrics;
1027 IDWriteFontFace1 *fontface1;
1028 HRESULT hr;
1029 UINT32 i;
1031 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %p %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1032 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1033 is_rtl, analysis, debugstr_w(locale), features, feature_range_len, feature_ranges, advances, offsets);
1035 if (glyph_count == 0)
1036 return S_OK;
1038 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1039 if (FAILED(hr)) {
1040 WARN("failed to get IDWriteFontFace1.\n");
1041 return hr;
1044 IDWriteFontFace_GetMetrics(fontface, &metrics);
1045 for (i = 0; i < glyph_count; i++) {
1046 INT32 a;
1048 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1049 if (FAILED(hr))
1050 a = 0;
1052 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1053 offsets[i].advanceOffset = 0.0;
1054 offsets[i].ascenderOffset = 0.0;
1057 /* FIXME: actually apply features */
1059 IDWriteFontFace1_Release(fontface1);
1060 return S_OK;
1063 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1064 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1065 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1066 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1067 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1068 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1069 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1071 DWRITE_FONT_METRICS metrics;
1072 IDWriteFontFace1 *fontface1;
1073 HRESULT hr;
1074 UINT32 i;
1076 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %p %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1077 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1078 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
1079 feature_ranges, advances, offsets);
1081 if (glyph_count == 0)
1082 return S_OK;
1084 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1085 if (FAILED(hr)) {
1086 WARN("failed to get IDWriteFontFace1.\n");
1087 return hr;
1090 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1091 if (FAILED(hr)) {
1092 IDWriteFontFace1_Release(fontface1);
1093 WARN("failed to get compat metrics, 0x%08x\n", hr);
1094 return hr;
1096 for (i = 0; i < glyph_count; i++) {
1097 INT32 a;
1099 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1100 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1101 if (FAILED(hr))
1102 advances[i] = 0.0;
1103 else
1104 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1105 offsets[i].advanceOffset = 0.0;
1106 offsets[i].ascenderOffset = 0.0;
1109 /* FIXME: actually apply features */
1111 IDWriteFontFace1_Release(fontface1);
1112 return S_OK;
1115 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1117 FLOAT advance = 0.0;
1118 for (; start < end; start++)
1119 advance += advances[start];
1120 return advance;
1123 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1124 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1125 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1127 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1128 FLOAT advance = advances[g];
1129 FLOAT origin = 0.0;
1131 if (props[g].isZeroWidthSpace) {
1132 modified_advances[g] = advances[g];
1133 modified_offsets[g] = offsets[g];
1134 return;
1137 /* first apply negative spacing and check if we hit minimum width */
1138 if (leading_spacing < 0.0) {
1139 advance += leading_spacing;
1140 origin -= leading_spacing;
1142 if (trailing_spacing < 0.0)
1143 advance += trailing_spacing;
1145 if (advance < min_advance_width) {
1146 FLOAT half = (min_advance_width - advance) / 2.0;
1148 if (!reduced)
1149 origin -= half;
1150 else if (leading_spacing < 0.0 && trailing_spacing < 0.0)
1151 origin -= half;
1152 else if (leading_spacing < 0.0)
1153 origin -= min_advance_width - advance;
1155 advance = min_advance_width;
1158 /* now apply positive spacing adjustments */
1159 if (leading_spacing > 0.0) {
1160 advance += leading_spacing;
1161 origin -= leading_spacing;
1163 if (trailing_spacing > 0.0)
1164 advance += trailing_spacing;
1166 modified_advances[g] = advance;
1167 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1168 /* ascender is never touched, it's orthogonal to reading direction and is not
1169 affected by advance adjustments */
1170 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1173 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1174 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1175 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1177 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1178 FLOAT advance = get_cluster_advance(advances, start, end);
1179 FLOAT origin = 0.0;
1180 UINT16 g;
1182 modified_advances[start] = advances[start];
1183 modified_advances[end-1] = advances[end-1];
1185 /* first apply negative spacing and check if we hit minimum width */
1186 if (leading_spacing < 0.0) {
1187 advance += leading_spacing;
1188 modified_advances[start] += leading_spacing;
1189 origin -= leading_spacing;
1191 if (trailing_spacing < 0.0) {
1192 advance += trailing_spacing;
1193 modified_advances[end-1] += trailing_spacing;
1195 if (advance < min_advance_width) {
1196 /* additional spacing is only applied to leading and trailing glyph */
1197 FLOAT half = (min_advance_width - advance) / 2.0;
1199 if (!reduced) {
1200 origin -= half;
1201 modified_advances[start] += half;
1202 modified_advances[end-1] += half;
1204 else if (leading_spacing < 0.0 && trailing_spacing < 0.0) {
1205 origin -= half;
1206 modified_advances[start] += half;
1207 modified_advances[end-1] += half;
1209 else if (leading_spacing < 0.0) {
1210 origin -= min_advance_width - advance;
1211 modified_advances[start] += min_advance_width - advance;
1213 else
1214 modified_advances[end-1] += min_advance_width - advance;
1216 advance = min_advance_width;
1219 /* now apply positive spacing adjustments */
1220 if (leading_spacing > 0.0) {
1221 modified_advances[start] += leading_spacing;
1222 origin -= leading_spacing;
1224 if (trailing_spacing > 0.0)
1225 modified_advances[end-1] += trailing_spacing;
1227 for (g = start; g < end; g++) {
1228 if (g == start) {
1229 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1230 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1232 else if (g == end - 1)
1233 /* trailing glyph offset is not adjusted */
1234 modified_offsets[g] = offsets[g];
1235 else {
1236 /* for all glyphs within a cluster use original advances and offsets */
1237 modified_advances[g] = advances[g];
1238 modified_offsets[g] = offsets[g];
1243 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1245 UINT16 g = clustermap[start];
1246 UINT32 length = 1;
1248 while (start < text_len && clustermap[++start] == g)
1249 length++;
1250 return length;
1253 /* Applies spacing adjustments to clusters.
1255 Adjustments are applied in the following order:
1257 1. Negative adjustments
1259 Leading and trailing spacing could be negative, at this step
1260 only negative ones are actually applied. Leading spacing is only
1261 applied to leading glyph, trailing - to trailing glyph.
1263 2. Minimum advance width
1265 Advances could only be reduced at this point or unchanged. In any
1266 case it's checked if cluster advance width is less than minimum width.
1267 If it's the case advance width is incremented up to minimum value.
1269 Important part is the direction in which this increment is applied;
1270 it depends on from which directions total cluster advance was trimmed
1271 at step 1. So it could be incremented from leading, trailing, or both
1272 sides. When applied to both sides, each side gets half of difference
1273 that bring advance to minimum width.
1275 3. Positive adjustments
1277 After minimum width rule was applied, positive spacing is applied in same
1278 way as negative ones on step 1.
1280 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1281 keeps its position in coordinate system where initial advance width is counted
1282 from 0.
1284 Glyph properties
1286 It's known that isZeroWidthSpace property keeps initial advance from changing.
1288 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1289 with more than one glyph.
1292 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1293 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1294 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1295 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1297 UINT16 start;
1299 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1300 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1302 if (min_advance_width < 0.0) {
1303 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1304 return E_INVALIDARG;
1307 /* minimum advance is not applied if no adjustments were made */
1308 if (leading_spacing == 0.0 && trailing_spacing == 0.0) {
1309 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1310 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1311 return S_OK;
1314 start = 0;
1315 for (start = 0; start < len;) {
1316 UINT32 length = get_cluster_length(clustermap, start, len);
1318 if (length == 1) {
1319 UINT32 g = clustermap[start];
1321 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1322 g, advances, offsets, props, modified_advances, modified_offsets);
1324 else {
1325 UINT32 g_start, g_end;
1327 g_start = clustermap[start];
1328 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1330 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1331 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1334 start += length;
1337 return S_OK;
1340 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1341 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1342 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1344 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1345 baseline_coord, exists);
1346 return E_NOTIMPL;
1349 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1350 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1352 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1353 return E_NOTIMPL;
1356 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1357 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1359 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1360 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1363 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1364 DWRITE_SCRIPT_PROPERTIES *props)
1366 TRACE("(%u %p)\n", sa.script, props);
1368 if (sa.script > Script_LastId)
1369 return E_INVALIDARG;
1371 *props = dwritescripts_properties[sa.script].props;
1372 return S_OK;
1375 static inline BOOL is_char_from_simple_script(WCHAR c)
1377 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1378 return FALSE;
1379 else {
1380 UINT16 script = get_char_script(c);
1381 return !dwritescripts_properties[script].is_complex;
1385 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1386 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1388 HRESULT hr = S_OK;
1389 int i;
1391 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1393 *is_simple = FALSE;
1394 *len_read = 0;
1396 if (!face)
1397 return E_INVALIDARG;
1399 if (len == 0) {
1400 *is_simple = TRUE;
1401 return S_OK;
1404 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1405 for (i = 1; i < len && text[i]; i++) {
1406 if (is_char_from_simple_script(text[i])) {
1407 if (!*is_simple)
1408 break;
1410 else
1411 *is_simple = FALSE;
1414 *len_read = i;
1416 /* fetch indices */
1417 if (*is_simple && indices) {
1418 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1419 if (!codepoints)
1420 return E_OUTOFMEMORY;
1422 for (i = 0; i < *len_read; i++)
1423 codepoints[i] = text[i];
1425 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1426 heap_free(codepoints);
1429 return hr;
1432 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1433 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1434 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1436 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1437 debugstr_wn(text, length), clustermap, prop, jo);
1438 return E_NOTIMPL;
1441 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1442 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1443 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1445 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1446 justifiedoffsets);
1447 return E_NOTIMPL;
1450 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1451 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1452 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1453 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1454 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1455 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1457 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,
1458 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1459 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1460 return E_NOTIMPL;
1463 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1464 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1466 static const DWRITE_MATRIX transforms[] = {
1467 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
1468 { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 },
1469 { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 },
1470 { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 }
1473 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1475 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1476 memset(m, 0, sizeof(*m));
1477 return E_INVALIDARG;
1480 /* for sideways case simply rotate 90 degrees more */
1481 if (is_sideways) {
1482 switch (angle) {
1483 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1484 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1485 break;
1486 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1487 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1488 break;
1489 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1490 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1491 break;
1492 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1493 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1494 break;
1495 default:
1500 *m = transforms[angle];
1502 /* shift components represent transform necessary to get from original point to
1503 rotated one in new coordinate system */
1504 if ((originX != 0.0 || originY != 0.0) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1505 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1506 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1509 return S_OK;
1512 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1513 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1514 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1516 const struct dwritescript_properties *props;
1517 HRESULT hr = S_OK;
1518 UINT32 language;
1520 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1521 tags);
1523 if (sa.script > Script_LastId)
1524 return E_INVALIDARG;
1526 language = get_opentype_language(locale);
1527 props = &dwritescripts_properties[sa.script];
1528 *actual_tagcount = 0;
1530 if (props->scriptalttag)
1531 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1533 if (*actual_tagcount == 0)
1534 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1536 return hr;
1539 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1540 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1541 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1543 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1544 feature_applies);
1545 return E_NOTIMPL;
1548 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1549 dwritetextanalyzer_QueryInterface,
1550 dwritetextanalyzer_AddRef,
1551 dwritetextanalyzer_Release,
1552 dwritetextanalyzer_AnalyzeScript,
1553 dwritetextanalyzer_AnalyzeBidi,
1554 dwritetextanalyzer_AnalyzeNumberSubstitution,
1555 dwritetextanalyzer_AnalyzeLineBreakpoints,
1556 dwritetextanalyzer_GetGlyphs,
1557 dwritetextanalyzer_GetGlyphPlacements,
1558 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1559 dwritetextanalyzer1_ApplyCharacterSpacing,
1560 dwritetextanalyzer1_GetBaseline,
1561 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1562 dwritetextanalyzer1_GetGlyphOrientationTransform,
1563 dwritetextanalyzer1_GetScriptProperties,
1564 dwritetextanalyzer1_GetTextComplexity,
1565 dwritetextanalyzer1_GetJustificationOpportunities,
1566 dwritetextanalyzer1_JustifyGlyphAdvances,
1567 dwritetextanalyzer1_GetJustifiedGlyphs,
1568 dwritetextanalyzer2_GetGlyphOrientationTransform,
1569 dwritetextanalyzer2_GetTypographicFeatures,
1570 dwritetextanalyzer2_CheckTypographicFeature
1573 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1575 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1577 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1578 return S_OK;
1581 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1583 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1585 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1587 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1588 IsEqualIID(riid, &IID_IUnknown))
1590 *obj = iface;
1591 IDWriteNumberSubstitution_AddRef(iface);
1592 return S_OK;
1595 *obj = NULL;
1597 return E_NOINTERFACE;
1600 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1602 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1603 ULONG ref = InterlockedIncrement(&This->ref);
1604 TRACE("(%p)->(%d)\n", This, ref);
1605 return ref;
1608 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1610 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1611 ULONG ref = InterlockedDecrement(&This->ref);
1613 TRACE("(%p)->(%d)\n", This, ref);
1615 if (!ref) {
1616 heap_free(This->locale);
1617 heap_free(This);
1620 return ref;
1623 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1624 dwritenumbersubstitution_QueryInterface,
1625 dwritenumbersubstitution_AddRef,
1626 dwritenumbersubstitution_Release
1629 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1630 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1632 struct dwrite_numbersubstitution *substitution;
1634 *ret = NULL;
1636 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1637 return E_INVALIDARG;
1639 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1640 return E_INVALIDARG;
1642 substitution = heap_alloc(sizeof(*substitution));
1643 if (!substitution)
1644 return E_OUTOFMEMORY;
1646 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1647 substitution->ref = 1;
1648 substitution->ignore_user_override = ignore_user_override;
1649 substitution->method = method;
1650 substitution->locale = heap_strdupW(locale);
1651 if (locale && !substitution->locale) {
1652 heap_free(substitution);
1653 return E_OUTOFMEMORY;
1656 *ret = &substitution->IDWriteNumberSubstitution_iface;
1657 return S_OK;