msvcp90: Implement time_get<char> ctors and dtors (Valgrind).
[wine/multimedia.git] / dlls / dwrite / analyzer.c
blobeedb41d1215d7948f100b222bc5bb6e415e8db69
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 inline FLOAT get_scaled_advance_width(INT32 advance, FLOAT emSize, const DWRITE_FONT_METRICS *metrics)
1021 return (FLOAT)advance * emSize / (FLOAT)metrics->designUnitsPerEm;
1024 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1025 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1026 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1027 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1028 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1029 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1031 DWRITE_FONT_METRICS metrics;
1032 IDWriteFontFace1 *fontface1;
1033 HRESULT hr;
1034 UINT32 i;
1036 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),
1037 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1038 is_rtl, analysis, debugstr_w(locale), features, feature_range_len, feature_ranges, advances, offsets);
1040 if (glyph_count == 0)
1041 return S_OK;
1043 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1044 if (FAILED(hr)) {
1045 WARN("failed to get IDWriteFontFace1.\n");
1046 return hr;
1049 IDWriteFontFace_GetMetrics(fontface, &metrics);
1050 for (i = 0; i < glyph_count; i++) {
1051 INT32 a;
1053 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1054 if (FAILED(hr))
1055 a = 0;
1057 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1058 offsets[i].advanceOffset = 0.0;
1059 offsets[i].ascenderOffset = 0.0;
1062 /* FIXME: actually apply features */
1064 IDWriteFontFace1_Release(fontface1);
1065 return S_OK;
1068 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1069 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1070 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1071 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1072 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1073 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1074 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1076 DWRITE_FONT_METRICS metrics;
1077 IDWriteFontFace1 *fontface1;
1078 HRESULT hr;
1079 UINT32 i;
1081 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),
1082 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1083 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
1084 feature_ranges, advances, offsets);
1086 if (glyph_count == 0)
1087 return S_OK;
1089 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1090 if (FAILED(hr)) {
1091 WARN("failed to get IDWriteFontFace1.\n");
1092 return hr;
1095 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1096 if (FAILED(hr)) {
1097 IDWriteFontFace1_Release(fontface1);
1098 WARN("failed to get compat metrics, 0x%08x\n", hr);
1099 return hr;
1101 for (i = 0; i < glyph_count; i++) {
1102 INT32 a;
1104 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1105 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1106 if (FAILED(hr))
1107 advances[i] = 0.0;
1108 else
1109 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1110 offsets[i].advanceOffset = 0.0;
1111 offsets[i].ascenderOffset = 0.0;
1114 /* FIXME: actually apply features */
1116 IDWriteFontFace1_Release(fontface1);
1117 return S_OK;
1120 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1122 FLOAT advance = 0.0;
1123 for (; start < end; start++)
1124 advance += advances[start];
1125 return advance;
1128 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1129 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1130 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1132 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1133 FLOAT advance = advances[g];
1134 FLOAT origin = 0.0;
1136 if (props[g].isZeroWidthSpace) {
1137 modified_advances[g] = advances[g];
1138 modified_offsets[g] = offsets[g];
1139 return;
1142 /* first apply negative spacing and check if we hit minimum width */
1143 if (leading_spacing < 0.0) {
1144 advance += leading_spacing;
1145 origin -= leading_spacing;
1147 if (trailing_spacing < 0.0)
1148 advance += trailing_spacing;
1150 if (advance < min_advance_width) {
1151 FLOAT half = (min_advance_width - advance) / 2.0;
1153 if (!reduced)
1154 origin -= half;
1155 else if (leading_spacing < 0.0 && trailing_spacing < 0.0)
1156 origin -= half;
1157 else if (leading_spacing < 0.0)
1158 origin -= min_advance_width - advance;
1160 advance = min_advance_width;
1163 /* now apply positive spacing adjustments */
1164 if (leading_spacing > 0.0) {
1165 advance += leading_spacing;
1166 origin -= leading_spacing;
1168 if (trailing_spacing > 0.0)
1169 advance += trailing_spacing;
1171 modified_advances[g] = advance;
1172 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1173 /* ascender is never touched, it's orthogonal to reading direction and is not
1174 affected by advance adjustments */
1175 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1178 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1179 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1180 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1182 BOOL reduced = leading_spacing < 0.0 || trailing_spacing < 0.0;
1183 FLOAT advance = get_cluster_advance(advances, start, end);
1184 FLOAT origin = 0.0;
1185 UINT16 g;
1187 modified_advances[start] = advances[start];
1188 modified_advances[end-1] = advances[end-1];
1190 /* first apply negative spacing and check if we hit minimum width */
1191 if (leading_spacing < 0.0) {
1192 advance += leading_spacing;
1193 modified_advances[start] += leading_spacing;
1194 origin -= leading_spacing;
1196 if (trailing_spacing < 0.0) {
1197 advance += trailing_spacing;
1198 modified_advances[end-1] += trailing_spacing;
1200 if (advance < min_advance_width) {
1201 /* additional spacing is only applied to leading and trailing glyph */
1202 FLOAT half = (min_advance_width - advance) / 2.0;
1204 if (!reduced) {
1205 origin -= half;
1206 modified_advances[start] += half;
1207 modified_advances[end-1] += half;
1209 else if (leading_spacing < 0.0 && trailing_spacing < 0.0) {
1210 origin -= half;
1211 modified_advances[start] += half;
1212 modified_advances[end-1] += half;
1214 else if (leading_spacing < 0.0) {
1215 origin -= min_advance_width - advance;
1216 modified_advances[start] += min_advance_width - advance;
1218 else
1219 modified_advances[end-1] += min_advance_width - advance;
1221 advance = min_advance_width;
1224 /* now apply positive spacing adjustments */
1225 if (leading_spacing > 0.0) {
1226 modified_advances[start] += leading_spacing;
1227 origin -= leading_spacing;
1229 if (trailing_spacing > 0.0)
1230 modified_advances[end-1] += trailing_spacing;
1232 for (g = start; g < end; g++) {
1233 if (g == start) {
1234 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1235 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1237 else if (g == end - 1)
1238 /* trailing glyph offset is not adjusted */
1239 modified_offsets[g] = offsets[g];
1240 else {
1241 /* for all glyphs within a cluster use original advances and offsets */
1242 modified_advances[g] = advances[g];
1243 modified_offsets[g] = offsets[g];
1248 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1250 UINT16 g = clustermap[start];
1251 UINT32 length = 1;
1253 while (start < text_len && clustermap[++start] == g)
1254 length++;
1255 return length;
1258 /* Applies spacing adjustments to clusters.
1260 Adjustments are applied in the following order:
1262 1. Negative adjustments
1264 Leading and trailing spacing could be negative, at this step
1265 only negative ones are actually applied. Leading spacing is only
1266 applied to leading glyph, trailing - to trailing glyph.
1268 2. Minimum advance width
1270 Advances could only be reduced at this point or unchanged. In any
1271 case it's checked if cluster advance width is less than minimum width.
1272 If it's the case advance width is incremented up to minimum value.
1274 Important part is the direction in which this increment is applied;
1275 it depends on from which directions total cluster advance was trimmed
1276 at step 1. So it could be incremented from leading, trailing, or both
1277 sides. When applied to both sides, each side gets half of difference
1278 that bring advance to minimum width.
1280 3. Positive adjustments
1282 After minimum width rule was applied, positive spacing is applied in same
1283 way as negative ones on step 1.
1285 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1286 keeps its position in coordinate system where initial advance width is counted
1287 from 0.
1289 Glyph properties
1291 It's known that isZeroWidthSpace property keeps initial advance from changing.
1293 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1294 with more than one glyph.
1297 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1298 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1299 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1300 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1302 UINT16 start;
1304 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1305 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1307 if (min_advance_width < 0.0) {
1308 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1309 return E_INVALIDARG;
1312 /* minimum advance is not applied if no adjustments were made */
1313 if (leading_spacing == 0.0 && trailing_spacing == 0.0) {
1314 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1315 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1316 return S_OK;
1319 start = 0;
1320 for (start = 0; start < len;) {
1321 UINT32 length = get_cluster_length(clustermap, start, len);
1323 if (length == 1) {
1324 UINT32 g = clustermap[start];
1326 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1327 g, advances, offsets, props, modified_advances, modified_offsets);
1329 else {
1330 UINT32 g_start, g_end;
1332 g_start = clustermap[start];
1333 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1335 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1336 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1339 start += length;
1342 return S_OK;
1345 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1346 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1347 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1349 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1350 baseline_coord, exists);
1351 return E_NOTIMPL;
1354 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1355 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1357 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1358 return E_NOTIMPL;
1361 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1362 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1364 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1365 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1368 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1369 DWRITE_SCRIPT_PROPERTIES *props)
1371 TRACE("(%u %p)\n", sa.script, props);
1373 if (sa.script > Script_LastId)
1374 return E_INVALIDARG;
1376 *props = dwritescripts_properties[sa.script].props;
1377 return S_OK;
1380 static inline BOOL is_char_from_simple_script(WCHAR c)
1382 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1383 return FALSE;
1384 else {
1385 UINT16 script = get_char_script(c);
1386 return !dwritescripts_properties[script].is_complex;
1390 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1391 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1393 HRESULT hr = S_OK;
1394 int i;
1396 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1398 *is_simple = FALSE;
1399 *len_read = 0;
1401 if (!face)
1402 return E_INVALIDARG;
1404 if (len == 0) {
1405 *is_simple = TRUE;
1406 return S_OK;
1409 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1410 for (i = 1; i < len && text[i]; i++) {
1411 if (is_char_from_simple_script(text[i])) {
1412 if (!*is_simple)
1413 break;
1415 else
1416 *is_simple = FALSE;
1419 *len_read = i;
1421 /* fetch indices */
1422 if (*is_simple && indices) {
1423 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1424 if (!codepoints)
1425 return E_OUTOFMEMORY;
1427 for (i = 0; i < *len_read; i++)
1428 codepoints[i] = text[i];
1430 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1431 heap_free(codepoints);
1434 return hr;
1437 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1438 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1439 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1441 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1442 debugstr_wn(text, length), clustermap, prop, jo);
1443 return E_NOTIMPL;
1446 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1447 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1448 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1450 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1451 justifiedoffsets);
1452 return E_NOTIMPL;
1455 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1456 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1457 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1458 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1459 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1460 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1462 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,
1463 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1464 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1465 return E_NOTIMPL;
1468 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1469 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1471 static const DWRITE_MATRIX transforms[] = {
1472 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
1473 { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 },
1474 { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 },
1475 { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 }
1478 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1480 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1481 memset(m, 0, sizeof(*m));
1482 return E_INVALIDARG;
1485 /* for sideways case simply rotate 90 degrees more */
1486 if (is_sideways) {
1487 switch (angle) {
1488 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1489 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1490 break;
1491 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1492 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1493 break;
1494 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1495 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1496 break;
1497 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1498 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1499 break;
1500 default:
1505 *m = transforms[angle];
1507 /* shift components represent transform necessary to get from original point to
1508 rotated one in new coordinate system */
1509 if ((originX != 0.0 || originY != 0.0) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1510 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1511 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1514 return S_OK;
1517 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1518 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1519 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1521 const struct dwritescript_properties *props;
1522 HRESULT hr = S_OK;
1523 UINT32 language;
1525 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1526 tags);
1528 if (sa.script > Script_LastId)
1529 return E_INVALIDARG;
1531 language = get_opentype_language(locale);
1532 props = &dwritescripts_properties[sa.script];
1533 *actual_tagcount = 0;
1535 if (props->scriptalttag)
1536 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1538 if (*actual_tagcount == 0)
1539 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1541 return hr;
1544 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1545 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1546 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1548 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1549 feature_applies);
1550 return E_NOTIMPL;
1553 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1554 dwritetextanalyzer_QueryInterface,
1555 dwritetextanalyzer_AddRef,
1556 dwritetextanalyzer_Release,
1557 dwritetextanalyzer_AnalyzeScript,
1558 dwritetextanalyzer_AnalyzeBidi,
1559 dwritetextanalyzer_AnalyzeNumberSubstitution,
1560 dwritetextanalyzer_AnalyzeLineBreakpoints,
1561 dwritetextanalyzer_GetGlyphs,
1562 dwritetextanalyzer_GetGlyphPlacements,
1563 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1564 dwritetextanalyzer1_ApplyCharacterSpacing,
1565 dwritetextanalyzer1_GetBaseline,
1566 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1567 dwritetextanalyzer1_GetGlyphOrientationTransform,
1568 dwritetextanalyzer1_GetScriptProperties,
1569 dwritetextanalyzer1_GetTextComplexity,
1570 dwritetextanalyzer1_GetJustificationOpportunities,
1571 dwritetextanalyzer1_JustifyGlyphAdvances,
1572 dwritetextanalyzer1_GetJustifiedGlyphs,
1573 dwritetextanalyzer2_GetGlyphOrientationTransform,
1574 dwritetextanalyzer2_GetTypographicFeatures,
1575 dwritetextanalyzer2_CheckTypographicFeature
1578 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1580 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1582 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1583 return S_OK;
1586 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1588 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1590 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1592 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1593 IsEqualIID(riid, &IID_IUnknown))
1595 *obj = iface;
1596 IDWriteNumberSubstitution_AddRef(iface);
1597 return S_OK;
1600 *obj = NULL;
1602 return E_NOINTERFACE;
1605 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1607 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1608 ULONG ref = InterlockedIncrement(&This->ref);
1609 TRACE("(%p)->(%d)\n", This, ref);
1610 return ref;
1613 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1615 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1616 ULONG ref = InterlockedDecrement(&This->ref);
1618 TRACE("(%p)->(%d)\n", This, ref);
1620 if (!ref) {
1621 heap_free(This->locale);
1622 heap_free(This);
1625 return ref;
1628 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1629 dwritenumbersubstitution_QueryInterface,
1630 dwritenumbersubstitution_AddRef,
1631 dwritenumbersubstitution_Release
1634 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1635 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1637 struct dwrite_numbersubstitution *substitution;
1639 *ret = NULL;
1641 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1642 return E_INVALIDARG;
1644 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1645 return E_INVALIDARG;
1647 substitution = heap_alloc(sizeof(*substitution));
1648 if (!substitution)
1649 return E_OUTOFMEMORY;
1651 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1652 substitution->ref = 1;
1653 substitution->ignore_user_override = ignore_user_override;
1654 substitution->method = method;
1655 substitution->locale = heap_strdupW(locale);
1656 if (locale && !substitution->locale) {
1657 heap_free(substitution);
1658 return E_OUTOFMEMORY;
1661 *ret = &substitution->IDWriteNumberSubstitution_iface;
1662 return S_OK;