dwrite: Fix recently added script properties.
[wine.git] / dlls / dwrite / analyzer.c
blob81052f94e044db80441b59f4068c7677f6fd5c4b
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[] DECLSPEC_HIDDEN;
32 extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN;
34 struct dwritescript_properties {
35 DWRITE_SCRIPT_PROPERTIES props;
36 UINT32 scripttag; /* OpenType script tag */
37 UINT32 scriptalttag; /* Version 2 tag, 0 if not defined */
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 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
49 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE },
50 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
51 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
52 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
53 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
54 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
55 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE },
56 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
57 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
58 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE },
59 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
60 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
61 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE },
62 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
63 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
64 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE },
65 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
66 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
67 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
68 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
69 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE },
70 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE },
71 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
72 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE },
73 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
74 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
75 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
76 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
77 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE },
78 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE },
79 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
80 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE },
81 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
82 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE },
83 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
84 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
85 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
86 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
87 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
88 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
89 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE },
90 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
91 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
92 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
93 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE },
94 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE },
95 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE, &latn_shaping_ops },
96 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
97 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
98 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
99 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
100 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
101 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
102 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE },
103 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
104 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
105 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE },
106 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE },
107 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE },
108 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE },
109 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE },
110 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
111 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
112 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE },
113 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
114 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
115 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE },
116 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE },
117 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE },
118 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
119 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
120 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE },
121 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
122 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
123 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
124 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE },
125 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
126 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
127 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE },
128 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
129 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
130 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE },
131 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
132 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
133 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE },
134 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE },
135 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE },
136 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE },
137 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE },
138 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE },
139 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
140 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE },
141 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE },
142 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','a','k','m') },
143 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','r','c') },
144 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('m','e','r','o') },
145 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
146 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','h','r','d') },
147 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','o','r','a') },
148 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','k','r') },
149 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
150 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
151 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 } },
152 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
153 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
154 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
155 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
156 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
157 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
158 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 } },
159 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
160 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
161 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
162 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
163 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
164 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
165 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
166 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
167 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
168 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 } },
169 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 } },
170 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
171 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
173 #undef _OT
175 const char *debugstr_sa_script(UINT16 script)
177 return script < Script_LastId ? debugstr_an((char*)&dwritescripts_properties[script].props.isoScriptCode, 4): "not defined";
180 /* system font falback configuration */
181 static const WCHAR meiryoW[] = {'M','e','i','r','y','o',0};
183 struct fallback_mapping {
184 DWRITE_UNICODE_RANGE range;
185 const WCHAR *family;
188 static const struct fallback_mapping fontfallback_neutral_data[] = {
189 { { 0x3000, 0x30ff }, meiryoW }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
190 { { 0x31f0, 0x31ff }, meiryoW }, /* Katakana Phonetic Extensions */
191 { { 0x4e00, 0x9fff }, meiryoW }, /* CJK Unified Ideographs */
194 struct dwrite_fontfallback {
195 IDWriteFontFallback IDWriteFontFallback_iface;
196 IDWriteFactory3 *factory;
197 IDWriteFontCollection *systemcollection;
198 const struct fallback_mapping *mappings;
199 UINT32 count;
202 struct dwrite_numbersubstitution {
203 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
204 LONG ref;
206 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
207 WCHAR *locale;
208 BOOL ignore_user_override;
211 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
213 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
216 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback(IDWriteFontFallback *iface)
218 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback_iface);
221 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
223 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
224 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
225 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
226 return ch;
228 return 0;
231 static inline UINT16 get_char_script(WCHAR c)
233 UINT16 script = get_table_entry(wine_scripts_table, c);
234 return script == Script_Inherited ? Script_Unknown : script;
237 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
239 DWRITE_SCRIPT_ANALYSIS sa;
241 sa.script = get_char_script(c);
242 sa.shapes = iscntrlW(c) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
243 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
244 return sa;
247 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
249 DWRITE_SCRIPT_ANALYSIS sa;
250 UINT32 pos, i, seq_length;
252 if (!length)
253 return S_OK;
255 sa = get_char_sa(*text);
257 pos = position;
258 seq_length = 1;
260 for (i = 1; i < length; i++)
262 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
264 /* Unknown type is ignored when preceded or followed by another script */
265 switch (sa.script) {
266 case Script_Unknown:
267 sa.script = cur_sa.script;
268 break;
269 case Script_Common:
270 if (cur_sa.script == Script_Unknown)
271 cur_sa.script = sa.script;
272 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
273 sa.script = cur_sa.script;
274 break;
275 default:
276 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
277 cur_sa.script = sa.script;
280 /* this is a length of a sequence to be reported next */
281 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
282 seq_length++;
283 else {
284 HRESULT hr;
286 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
287 if (FAILED(hr)) return hr;
288 pos = position + i;
289 seq_length = 1;
290 sa = cur_sa;
294 /* one char length case or normal completion call */
295 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
298 struct linebreaking_state {
299 DWRITE_LINE_BREAKPOINT *breakpoints;
300 UINT32 count;
303 enum BreakConditionLocation {
304 BreakConditionBefore,
305 BreakConditionAfter
308 enum linebreaking_classes {
309 b_BK = 1,
310 b_CR,
311 b_LF,
312 b_CM,
313 b_SG,
314 b_GL,
315 b_CB,
316 b_SP,
317 b_ZW,
318 b_NL,
319 b_WJ,
320 b_JL,
321 b_JV,
322 b_JT,
323 b_H2,
324 b_H3,
325 b_XX,
326 b_OP,
327 b_CL,
328 b_CP,
329 b_QU,
330 b_NS,
331 b_EX,
332 b_SY,
333 b_IS,
334 b_PR,
335 b_PO,
336 b_NU,
337 b_AL,
338 b_ID,
339 b_IN,
340 b_HY,
341 b_BB,
342 b_BA,
343 b_SA,
344 b_AI,
345 b_B2,
346 b_HL,
347 b_CJ,
348 b_RI,
349 b_EB,
350 b_EM,
351 b_ZWJ,
354 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
355 set to "can break" and could only be changed once. */
356 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
357 struct linebreaking_state *state)
359 if (location == BreakConditionBefore) {
360 if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK)
361 return;
362 state->breakpoints[pos].breakConditionBefore = condition;
363 if (pos > 0)
364 state->breakpoints[pos-1].breakConditionAfter = condition;
366 else {
367 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
368 return;
369 state->breakpoints[pos].breakConditionAfter = condition;
370 if (pos + 1 < state->count)
371 state->breakpoints[pos+1].breakConditionBefore = condition;
375 BOOL lb_is_newline_char(WCHAR ch)
377 short c = get_table_entry(wine_linebreak_table, ch);
378 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
381 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
383 struct linebreaking_state state;
384 short *break_class;
385 int i, j;
387 break_class = heap_alloc(count*sizeof(short));
388 if (!break_class)
389 return E_OUTOFMEMORY;
391 state.breakpoints = breakpoints;
392 state.count = count;
394 /* LB31 - allow breaks everywhere. It will be overridden if needed as
395 other rules dictate. */
396 for (i = 0; i < count; i++)
398 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
400 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK;
401 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK;
402 breakpoints[i].isWhitespace = !!isspaceW(text[i]);
403 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
404 breakpoints[i].padding = 0;
406 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
407 switch (break_class[i])
409 case b_AI:
410 case b_SA:
411 case b_SG:
412 case b_XX:
413 break_class[i] = b_AL;
414 break;
415 case b_CJ:
416 break_class[i] = b_NS;
417 break;
421 /* LB2 - never break at the start */
422 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
423 /* LB3 - always break at the end. This one is ignored. */
425 for (i = 0; i < count; i++)
427 switch (break_class[i])
429 /* LB4 - LB6 */
430 case b_CR:
431 /* LB5 - don't break CR x LF */
432 if (i < count-1 && break_class[i+1] == b_LF)
434 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
435 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
436 break;
438 case b_LF:
439 case b_NL:
440 case b_BK:
441 /* LB4 - LB5 - always break after hard breaks */
442 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
443 /* LB6 - do not break before hard breaks */
444 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
445 break;
446 /* LB7 - do not break before spaces */
447 case b_SP:
448 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
449 break;
450 case b_ZW:
451 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
452 /* LB8 - break before character after zero-width space, skip spaces in-between */
453 while (i < count-1 && break_class[i+1] == b_SP)
454 i++;
455 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
456 break;
457 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
458 case b_ZWJ:
459 if (i < count-1 && (break_class[i+1] == b_ID || break_class[i+1] == b_EB || break_class[i+1] == b_EM))
460 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
461 break;
465 /* LB9 - LB10 */
466 for (i = 0; i < count; i++)
468 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
470 if (i > 0)
472 switch (break_class[i-1])
474 case b_SP:
475 case b_BK:
476 case b_CR:
477 case b_LF:
478 case b_NL:
479 case b_ZW:
480 break_class[i] = b_AL;
481 break;
482 default:
483 break_class[i] = break_class[i-1];
486 else break_class[i] = b_AL;
490 for (i = 0; i < count; i++)
492 switch (break_class[i])
494 /* LB11 - don't break before and after word joiner */
495 case b_WJ:
496 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
497 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
498 break;
499 /* LB12 - don't break after glue */
500 case b_GL:
501 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
502 /* LB12a */
503 if (i > 0)
505 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
506 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
508 break;
509 /* LB13 */
510 case b_CL:
511 case b_CP:
512 case b_EX:
513 case b_IS:
514 case b_SY:
515 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
516 break;
517 /* LB14 */
518 case b_OP:
519 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
520 while (i < count-1 && break_class[i+1] == b_SP) {
521 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
522 i++;
524 break;
525 /* LB15 */
526 case b_QU:
527 j = i+1;
528 while (j < count-1 && break_class[j] == b_SP)
529 j++;
530 if (break_class[j] == b_OP)
531 for (; j > i; j--)
532 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
533 break;
534 /* LB16 */
535 case b_NS:
536 j = i-1;
537 while(j > 0 && break_class[j] == b_SP)
538 j--;
539 if (break_class[j] == b_CL || break_class[j] == b_CP)
540 for (j++; j <= i; j++)
541 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
542 break;
543 /* LB17 */
544 case b_B2:
545 j = i+1;
546 while (j < count && break_class[j] == b_SP)
547 j++;
548 if (break_class[j] == b_B2)
549 for (; j > i; j--)
550 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
551 break;
555 for (i = 0; i < count; i++)
557 switch(break_class[i])
559 /* LB18 - break is allowed after space */
560 case b_SP:
561 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
562 break;
563 /* LB19 - don't break before or after quotation mark */
564 case b_QU:
565 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
566 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
567 break;
568 /* LB20 */
569 case b_CB:
570 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
571 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
572 break;
573 /* LB21 */
574 case b_BA:
575 case b_HY:
576 case b_NS:
577 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
578 break;
579 case b_BB:
580 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
581 break;
582 /* LB21a, LB21b */
583 case b_HL:
584 /* LB21a */
585 if (i < count-1)
586 switch (break_class[i+1])
588 case b_HY:
589 case b_BA:
590 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
592 /* LB21b */
593 if (i > 0 && break_class[i-1] == b_SY)
594 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
595 break;
596 /* LB22 */
597 case b_IN:
598 if (i > 0)
600 switch (break_class[i-1])
602 case b_AL:
603 case b_HL:
604 case b_EX:
605 case b_ID:
606 case b_EB:
607 case b_EM:
608 case b_IN:
609 case b_NU:
610 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
613 break;
616 if (i < count-1)
618 /* LB23 - do not break between digits and letters */
619 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
620 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
621 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
622 (break_class[i] == b_NU && break_class[i+1] == b_HL))
623 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
625 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
626 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
627 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
628 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
629 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
630 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
631 (break_class[i] == b_EB && break_class[i+1] == b_PO))
632 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
634 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
635 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
636 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
637 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
638 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
639 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
640 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
641 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
642 (break_class[i] == b_HL && break_class[i+1] == b_PO))
643 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
645 /* LB25 */
646 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
647 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
648 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
649 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
650 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
651 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
652 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
653 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
654 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
655 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
656 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
657 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
658 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
659 (break_class[i] == b_SY && break_class[i+1] == b_NU))
660 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
662 /* LB26 */
663 if (break_class[i] == b_JL)
665 switch (break_class[i+1])
667 case b_JL:
668 case b_JV:
669 case b_H2:
670 case b_H3:
671 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
674 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
675 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
676 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
677 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
678 break_class[i+1] == b_JT)
679 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
681 /* LB27 */
682 switch (break_class[i])
684 case b_JL:
685 case b_JV:
686 case b_JT:
687 case b_H2:
688 case b_H3:
689 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
690 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
692 if (break_class[i] == b_PO)
694 switch (break_class[i+1])
696 case b_JL:
697 case b_JV:
698 case b_JT:
699 case b_H2:
700 case b_H3:
701 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
705 /* LB28 */
706 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
707 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
708 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
709 (break_class[i] == b_HL && break_class[i+1] == b_HL))
710 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
712 /* LB29 */
713 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
714 (break_class[i] == b_IS && break_class[i+1] == b_HL))
715 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
717 /* LB30 */
718 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
719 break_class[i+1] == b_OP)
720 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
721 if (break_class[i] == b_CP &&
722 (break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU))
723 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
725 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
726 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
727 unsigned int c = 0;
729 j = i + 1;
730 while (j > 0 && break_class[--j] == b_RI)
731 c++;
733 if ((c & 1) == 0)
734 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
737 /* LB30b - do not break between an emoji base and an emoji modifier */
738 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
739 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
743 heap_free(break_class);
744 return S_OK;
747 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
749 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
751 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
752 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
753 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
754 IsEqualIID(riid, &IID_IUnknown))
756 *obj = iface;
757 return S_OK;
760 *obj = NULL;
761 return E_NOINTERFACE;
764 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
766 return 2;
769 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
771 return 1;
774 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
775 data after a first request. */
776 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
778 HRESULT hr;
779 UINT32 len;
781 *buff = NULL;
782 *text = NULL;
783 len = 0;
784 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
785 if (FAILED(hr)) return hr;
787 if (len < length) {
788 UINT32 read;
790 *buff = heap_alloc(length*sizeof(WCHAR));
791 if (!*buff)
792 return E_OUTOFMEMORY;
793 memcpy(*buff, *text, len*sizeof(WCHAR));
794 read = len;
796 while (read < length && *text) {
797 *text = NULL;
798 len = 0;
799 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
800 if (FAILED(hr)) {
801 heap_free(*buff);
802 return hr;
804 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
805 read += len;
808 *text = *buff;
811 return hr;
814 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
815 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
817 WCHAR *buff = NULL;
818 const WCHAR *text;
819 HRESULT hr;
821 TRACE("(%p %u %u %p)\n", source, position, length, sink);
823 if (length == 0)
824 return S_OK;
826 hr = get_text_source_ptr(source, position, length, &text, &buff);
827 if (FAILED(hr))
828 return hr;
830 hr = analyze_script(text, position, length, sink);
831 heap_free(buff);
833 return hr;
836 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
837 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
839 UINT8 *levels = NULL, *explicit = NULL;
840 UINT8 baselevel, level, explicit_level;
841 UINT32 pos, i, seq_length;
842 WCHAR *buff = NULL;
843 const WCHAR *text;
844 HRESULT hr;
846 TRACE("(%p %u %u %p)\n", source, position, length, sink);
848 if (!length)
849 return S_OK;
851 hr = get_text_source_ptr(source, position, length, &text, &buff);
852 if (FAILED(hr))
853 return hr;
855 levels = heap_alloc(length*sizeof(*levels));
856 explicit = heap_alloc(length*sizeof(*explicit));
858 if (!levels || !explicit) {
859 hr = E_OUTOFMEMORY;
860 goto done;
863 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
864 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
865 if (FAILED(hr))
866 goto done;
868 level = levels[0];
869 explicit_level = explicit[0];
870 pos = position;
871 seq_length = 1;
873 for (i = 1; i < length; i++) {
874 if (levels[i] == level && explicit[i] == explicit_level)
875 seq_length++;
876 else {
877 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
878 if (FAILED(hr))
879 goto done;
881 pos += seq_length;
882 seq_length = 1;
883 level = levels[i];
884 explicit_level = explicit[i];
887 /* one char length case or normal completion call */
888 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
890 done:
891 heap_free(explicit);
892 heap_free(levels);
893 heap_free(buff);
895 return hr;
898 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
899 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
901 static int once;
903 if (!once++)
904 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
905 return S_OK;
908 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
909 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
911 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
912 WCHAR *buff = NULL;
913 const WCHAR *text;
914 HRESULT hr;
915 UINT32 len;
917 TRACE("(%p %u %u %p)\n", source, position, length, sink);
919 if (length == 0)
920 return S_OK;
922 /* get some, check for length */
923 text = NULL;
924 len = 0;
925 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
926 if (FAILED(hr)) return hr;
928 if (len < length) {
929 UINT32 read;
931 buff = heap_alloc(length*sizeof(WCHAR));
932 if (!buff)
933 return E_OUTOFMEMORY;
934 memcpy(buff, text, len*sizeof(WCHAR));
935 read = len;
937 while (read < length && text) {
938 text = NULL;
939 len = 0;
940 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
941 if (FAILED(hr))
942 goto done;
943 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
944 read += len;
947 text = buff;
950 breakpoints = heap_alloc(length*sizeof(*breakpoints));
951 if (!breakpoints) {
952 hr = E_OUTOFMEMORY;
953 goto done;
956 hr = analyze_linebreaks(text, length, breakpoints);
957 if (FAILED(hr))
958 goto done;
960 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
962 done:
963 heap_free(breakpoints);
964 heap_free(buff);
966 return hr;
969 static UINT32 get_opentype_language(const WCHAR *locale)
971 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
973 if (locale) {
974 WCHAR tag[5];
975 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
976 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
979 return language;
982 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
983 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
984 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
985 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
986 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
987 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
988 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
990 const struct dwritescript_properties *scriptprops;
991 struct scriptshaping_context context;
992 struct scriptshaping_cache *cache = NULL;
993 BOOL update_cluster, need_vertical;
994 IDWriteFontFace1 *fontface1;
995 WCHAR *string;
996 UINT32 i, g;
997 HRESULT hr = S_OK;
998 UINT16 script;
1000 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
1001 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1002 features, feature_range_len, feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices,
1003 glyph_props, actual_glyph_count);
1005 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1007 if (max_glyph_count < length)
1008 return E_NOT_SUFFICIENT_BUFFER;
1010 if (substitution)
1011 FIXME("number substitution is not supported.\n");
1013 for (i = 0; i < length; i++) {
1014 /* FIXME: set to better values */
1015 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
1016 glyph_props[i].isClusterStart = 1;
1017 glyph_props[i].isDiacritic = 0;
1018 glyph_props[i].isZeroWidthSpace = 0;
1019 glyph_props[i].reserved = 0;
1021 /* FIXME: have the shaping engine set this */
1022 text_props[i].isShapedAlone = 0;
1023 text_props[i].reserved = 0;
1025 clustermap[i] = i;
1028 for (; i < max_glyph_count; i++) {
1029 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
1030 glyph_props[i].isClusterStart = 0;
1031 glyph_props[i].isDiacritic = 0;
1032 glyph_props[i].isZeroWidthSpace = 0;
1033 glyph_props[i].reserved = 0;
1036 string = heap_alloc(sizeof(WCHAR)*length);
1037 if (!string)
1038 return E_OUTOFMEMORY;
1040 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1041 if (FAILED(hr))
1042 WARN("failed to get IDWriteFontFace1\n");
1044 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
1046 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
1047 UINT32 codepoint;
1049 if (!update_cluster) {
1050 codepoint = decode_surrogate_pair(text, i, length);
1051 if (!codepoint) {
1052 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
1053 string[i] = codepoint;
1055 else {
1056 string[i] = text[i];
1057 string[i+1] = text[i+1];
1058 update_cluster = TRUE;
1061 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
1062 if (FAILED(hr))
1063 goto done;
1065 if (need_vertical) {
1066 UINT16 vertical;
1068 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
1069 if (hr == S_OK)
1070 glyph_indices[g] = vertical;
1073 g++;
1075 else {
1076 INT32 k;
1078 update_cluster = FALSE;
1079 /* mark surrogate halves with same cluster */
1080 clustermap[i] = clustermap[i-1];
1081 /* update following clusters */
1082 for (k = i + 1; k >= 0 && k < length; k++)
1083 clustermap[k]--;
1086 *actual_glyph_count = g;
1088 hr = create_scriptshaping_cache(fontface, &cache);
1089 if (FAILED(hr))
1090 goto done;
1092 context.cache = cache;
1093 context.text = text;
1094 context.length = length;
1095 context.is_rtl = is_rtl;
1096 context.max_glyph_count = max_glyph_count;
1097 context.language_tag = get_opentype_language(locale);
1099 scriptprops = &dwritescripts_properties[script];
1100 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
1101 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
1102 if (FAILED(hr))
1103 goto done;
1106 /* FIXME: apply default features */
1108 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1109 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1110 else
1111 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1113 done:
1114 if (fontface1)
1115 IDWriteFontFace1_Release(fontface1);
1116 release_scriptshaping_cache(cache);
1117 heap_free(string);
1119 return hr;
1122 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1123 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1124 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1125 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1126 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1127 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1129 DWRITE_FONT_METRICS metrics;
1130 IDWriteFontFace1 *fontface1;
1131 HRESULT hr;
1132 UINT32 i;
1134 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1135 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1136 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_len,
1137 feature_ranges, advances, offsets);
1139 if (glyph_count == 0)
1140 return S_OK;
1142 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1143 if (FAILED(hr)) {
1144 WARN("failed to get IDWriteFontFace1.\n");
1145 return hr;
1148 IDWriteFontFace_GetMetrics(fontface, &metrics);
1149 for (i = 0; i < glyph_count; i++) {
1150 if (glyph_props[i].isZeroWidthSpace)
1151 advances[i] = 0.0f;
1152 else {
1153 INT32 a;
1155 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1156 if (FAILED(hr))
1157 a = 0;
1158 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1160 offsets[i].advanceOffset = 0.0f;
1161 offsets[i].ascenderOffset = 0.0f;
1164 /* FIXME: actually apply features */
1166 IDWriteFontFace1_Release(fontface1);
1167 return S_OK;
1170 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1171 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1172 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1173 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1174 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1175 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1176 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1178 DWRITE_FONT_METRICS metrics;
1179 IDWriteFontFace1 *fontface1;
1180 HRESULT hr;
1181 UINT32 i;
1183 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1184 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1185 transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale),
1186 features, feature_range_lengths, feature_ranges, advances, offsets);
1188 if (glyph_count == 0)
1189 return S_OK;
1191 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1192 if (FAILED(hr)) {
1193 WARN("failed to get IDWriteFontFace1.\n");
1194 return hr;
1197 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1198 if (FAILED(hr)) {
1199 IDWriteFontFace1_Release(fontface1);
1200 WARN("failed to get compat metrics, 0x%08x\n", hr);
1201 return hr;
1203 for (i = 0; i < glyph_count; i++) {
1204 INT32 a;
1206 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1207 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1208 if (FAILED(hr))
1209 advances[i] = 0.0f;
1210 else
1211 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1212 offsets[i].advanceOffset = 0.0f;
1213 offsets[i].ascenderOffset = 0.0f;
1216 /* FIXME: actually apply features */
1218 IDWriteFontFace1_Release(fontface1);
1219 return S_OK;
1222 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1224 FLOAT advance = 0.0f;
1225 for (; start < end; start++)
1226 advance += advances[start];
1227 return advance;
1230 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1231 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1232 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1234 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1235 FLOAT advance = advances[g];
1236 FLOAT origin = 0.0f;
1238 if (props[g].isZeroWidthSpace) {
1239 modified_advances[g] = advances[g];
1240 modified_offsets[g] = offsets[g];
1241 return;
1244 /* first apply negative spacing and check if we hit minimum width */
1245 if (leading_spacing < 0.0f) {
1246 advance += leading_spacing;
1247 origin -= leading_spacing;
1249 if (trailing_spacing < 0.0f)
1250 advance += trailing_spacing;
1252 if (advance < min_advance_width) {
1253 FLOAT half = (min_advance_width - advance) / 2.0f;
1255 if (!reduced)
1256 origin -= half;
1257 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1258 origin -= half;
1259 else if (leading_spacing < 0.0f)
1260 origin -= min_advance_width - advance;
1262 advance = min_advance_width;
1265 /* now apply positive spacing adjustments */
1266 if (leading_spacing > 0.0f) {
1267 advance += leading_spacing;
1268 origin -= leading_spacing;
1270 if (trailing_spacing > 0.0f)
1271 advance += trailing_spacing;
1273 modified_advances[g] = advance;
1274 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1275 /* ascender is never touched, it's orthogonal to reading direction and is not
1276 affected by advance adjustments */
1277 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1280 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1281 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1282 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1284 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1285 FLOAT advance = get_cluster_advance(advances, start, end);
1286 FLOAT origin = 0.0f;
1287 UINT16 g;
1289 modified_advances[start] = advances[start];
1290 modified_advances[end-1] = advances[end-1];
1292 /* first apply negative spacing and check if we hit minimum width */
1293 if (leading_spacing < 0.0f) {
1294 advance += leading_spacing;
1295 modified_advances[start] += leading_spacing;
1296 origin -= leading_spacing;
1298 if (trailing_spacing < 0.0f) {
1299 advance += trailing_spacing;
1300 modified_advances[end-1] += trailing_spacing;
1303 advance = min_advance_width - advance;
1304 if (advance > 0.0f) {
1305 /* additional spacing is only applied to leading and trailing glyph */
1306 FLOAT half = advance / 2.0f;
1308 if (!reduced) {
1309 origin -= half;
1310 modified_advances[start] += half;
1311 modified_advances[end-1] += half;
1313 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f) {
1314 origin -= half;
1315 modified_advances[start] += half;
1316 modified_advances[end-1] += half;
1318 else if (leading_spacing < 0.0f) {
1319 origin -= advance;
1320 modified_advances[start] += advance;
1322 else
1323 modified_advances[end-1] += advance;
1326 /* now apply positive spacing adjustments */
1327 if (leading_spacing > 0.0f) {
1328 modified_advances[start] += leading_spacing;
1329 origin -= leading_spacing;
1331 if (trailing_spacing > 0.0f)
1332 modified_advances[end-1] += trailing_spacing;
1334 for (g = start; g < end; g++) {
1335 if (g == start) {
1336 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1337 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1339 else if (g == end - 1)
1340 /* trailing glyph offset is not adjusted */
1341 modified_offsets[g] = offsets[g];
1342 else {
1343 /* for all glyphs within a cluster use original advances and offsets */
1344 modified_advances[g] = advances[g];
1345 modified_offsets[g] = offsets[g];
1350 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1352 UINT16 g = clustermap[start];
1353 UINT32 length = 1;
1355 while (start < text_len && clustermap[++start] == g)
1356 length++;
1357 return length;
1360 /* Applies spacing adjustments to clusters.
1362 Adjustments are applied in the following order:
1364 1. Negative adjustments
1366 Leading and trailing spacing could be negative, at this step
1367 only negative ones are actually applied. Leading spacing is only
1368 applied to leading glyph, trailing - to trailing glyph.
1370 2. Minimum advance width
1372 Advances could only be reduced at this point or unchanged. In any
1373 case it's checked if cluster advance width is less than minimum width.
1374 If it's the case advance width is incremented up to minimum value.
1376 Important part is the direction in which this increment is applied;
1377 it depends on direction from which total cluster advance was trimmed
1378 at step 1. So it could be incremented from leading, trailing, or both
1379 sides. When applied to both sides, each side gets half of difference
1380 that brings advance to minimum width.
1382 3. Positive adjustments
1384 After minimum width rule was applied, positive spacing is applied in the same
1385 way as negative one on step 1.
1387 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1388 keeps its position in coordinate system where initial advance width is counted
1389 from 0.
1391 Glyph properties
1393 It's known that isZeroWidthSpace property keeps initial advance from changing.
1395 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1396 with more than one glyph.
1399 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1400 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1401 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1402 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1404 UINT16 start;
1406 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1407 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1409 if (min_advance_width < 0.0f) {
1410 if (modified_advances != advances)
1411 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1412 return E_INVALIDARG;
1415 /* minimum advance is not applied if no adjustments were made */
1416 if (leading_spacing == 0.0f && trailing_spacing == 0.0f) {
1417 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1418 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1419 return S_OK;
1422 for (start = 0; start < len;) {
1423 UINT32 length = get_cluster_length(clustermap, start, len);
1425 if (length == 1) {
1426 UINT32 g = clustermap[start];
1428 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1429 g, advances, offsets, props, modified_advances, modified_offsets);
1431 else {
1432 UINT32 g_start, g_end;
1434 g_start = clustermap[start];
1435 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1437 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1438 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1441 start += length;
1444 return S_OK;
1447 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1448 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1449 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1451 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1452 baseline_coord, exists);
1453 return E_NOTIMPL;
1456 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1457 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1459 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1460 return E_NOTIMPL;
1463 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1464 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1466 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1467 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1470 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1471 DWRITE_SCRIPT_PROPERTIES *props)
1473 TRACE("(%u %p)\n", sa.script, props);
1475 if (sa.script > Script_LastId)
1476 return E_INVALIDARG;
1478 *props = dwritescripts_properties[sa.script].props;
1479 return S_OK;
1482 static inline BOOL is_char_from_simple_script(WCHAR c)
1484 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1485 return FALSE;
1486 else {
1487 UINT16 script = get_char_script(c);
1488 return !dwritescripts_properties[script].is_complex;
1492 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1493 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1495 HRESULT hr = S_OK;
1496 int i;
1498 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1500 *is_simple = FALSE;
1501 *len_read = 0;
1503 if (!face)
1504 return E_INVALIDARG;
1506 if (len == 0) {
1507 *is_simple = TRUE;
1508 return S_OK;
1511 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1512 for (i = 1; i < len && text[i]; i++) {
1513 if (is_char_from_simple_script(text[i])) {
1514 if (!*is_simple)
1515 break;
1517 else
1518 *is_simple = FALSE;
1521 *len_read = i;
1523 /* fetch indices */
1524 if (*is_simple && indices) {
1525 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1526 if (!codepoints)
1527 return E_OUTOFMEMORY;
1529 for (i = 0; i < *len_read; i++)
1530 codepoints[i] = text[i];
1532 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1533 heap_free(codepoints);
1536 return hr;
1539 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1540 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1541 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1543 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1544 debugstr_wn(text, length), clustermap, prop, jo);
1545 return E_NOTIMPL;
1548 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1549 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1550 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1552 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1553 justifiedoffsets);
1554 return E_NOTIMPL;
1557 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1558 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1559 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1560 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1561 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1562 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1564 FIXME("(%p %.2f %u %u %u %u %p %p %p %p %p %p %p %p %p %p %p): stub\n", face, font_em_size, sa.script,
1565 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1566 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1567 return E_NOTIMPL;
1570 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1571 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1573 static const DWRITE_MATRIX transforms[] = {
1574 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1575 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1576 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1577 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1580 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1582 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1583 memset(m, 0, sizeof(*m));
1584 return E_INVALIDARG;
1587 /* for sideways case simply rotate 90 degrees more */
1588 if (is_sideways) {
1589 switch (angle) {
1590 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1591 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1592 break;
1593 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1594 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1595 break;
1596 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1597 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1598 break;
1599 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1600 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1601 break;
1602 default:
1607 *m = transforms[angle];
1609 /* shift components represent transform necessary to get from original point to
1610 rotated one in new coordinate system */
1611 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1612 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1613 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1616 return S_OK;
1619 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1620 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1621 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1623 const struct dwritescript_properties *props;
1624 HRESULT hr = S_OK;
1625 UINT32 language;
1627 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1628 tags);
1630 if (sa.script > Script_LastId)
1631 return E_INVALIDARG;
1633 language = get_opentype_language(locale);
1634 props = &dwritescripts_properties[sa.script];
1635 *actual_tagcount = 0;
1637 if (props->scriptalttag)
1638 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1640 if (*actual_tagcount == 0)
1641 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1643 return hr;
1646 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1647 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1648 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1650 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1651 feature_applies);
1652 return E_NOTIMPL;
1655 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1656 dwritetextanalyzer_QueryInterface,
1657 dwritetextanalyzer_AddRef,
1658 dwritetextanalyzer_Release,
1659 dwritetextanalyzer_AnalyzeScript,
1660 dwritetextanalyzer_AnalyzeBidi,
1661 dwritetextanalyzer_AnalyzeNumberSubstitution,
1662 dwritetextanalyzer_AnalyzeLineBreakpoints,
1663 dwritetextanalyzer_GetGlyphs,
1664 dwritetextanalyzer_GetGlyphPlacements,
1665 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1666 dwritetextanalyzer1_ApplyCharacterSpacing,
1667 dwritetextanalyzer1_GetBaseline,
1668 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1669 dwritetextanalyzer1_GetGlyphOrientationTransform,
1670 dwritetextanalyzer1_GetScriptProperties,
1671 dwritetextanalyzer1_GetTextComplexity,
1672 dwritetextanalyzer1_GetJustificationOpportunities,
1673 dwritetextanalyzer1_JustifyGlyphAdvances,
1674 dwritetextanalyzer1_GetJustifiedGlyphs,
1675 dwritetextanalyzer2_GetGlyphOrientationTransform,
1676 dwritetextanalyzer2_GetTypographicFeatures,
1677 dwritetextanalyzer2_CheckTypographicFeature
1680 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1682 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1684 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1685 return S_OK;
1688 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1690 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1692 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1694 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1695 IsEqualIID(riid, &IID_IUnknown))
1697 *obj = iface;
1698 IDWriteNumberSubstitution_AddRef(iface);
1699 return S_OK;
1702 *obj = NULL;
1704 return E_NOINTERFACE;
1707 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1709 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1710 ULONG ref = InterlockedIncrement(&This->ref);
1711 TRACE("(%p)->(%d)\n", This, ref);
1712 return ref;
1715 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1717 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1718 ULONG ref = InterlockedDecrement(&This->ref);
1720 TRACE("(%p)->(%d)\n", This, ref);
1722 if (!ref) {
1723 heap_free(This->locale);
1724 heap_free(This);
1727 return ref;
1730 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1731 dwritenumbersubstitution_QueryInterface,
1732 dwritenumbersubstitution_AddRef,
1733 dwritenumbersubstitution_Release
1736 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1737 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1739 struct dwrite_numbersubstitution *substitution;
1741 *ret = NULL;
1743 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1744 return E_INVALIDARG;
1746 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1747 return E_INVALIDARG;
1749 substitution = heap_alloc(sizeof(*substitution));
1750 if (!substitution)
1751 return E_OUTOFMEMORY;
1753 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1754 substitution->ref = 1;
1755 substitution->ignore_user_override = ignore_user_override;
1756 substitution->method = method;
1757 substitution->locale = heap_strdupW(locale);
1758 if (locale && !substitution->locale) {
1759 heap_free(substitution);
1760 return E_OUTOFMEMORY;
1763 *ret = &substitution->IDWriteNumberSubstitution_iface;
1764 return S_OK;
1767 /* IDWriteFontFallback */
1768 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback *iface, REFIID riid, void **obj)
1770 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1772 TRACE("(%p)->(%s %p)\n", fallback, debugstr_guid(riid), obj);
1774 if (IsEqualIID(riid, &IID_IDWriteFontFallback) || IsEqualIID(riid, &IID_IUnknown)) {
1775 *obj = iface;
1776 IDWriteFontFallback_AddRef(iface);
1777 return S_OK;
1780 *obj = NULL;
1781 return E_NOINTERFACE;
1784 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback *iface)
1786 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1787 TRACE("(%p)\n", fallback);
1788 return IDWriteFactory3_AddRef(fallback->factory);
1791 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback *iface)
1793 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1794 TRACE("(%p)\n", fallback);
1795 return IDWriteFactory3_Release(fallback->factory);
1798 static int compare_fallback_mapping(const void *a, const void *b)
1800 UINT32 ch = *(UINT32*)a;
1801 struct fallback_mapping *mapping = (struct fallback_mapping*)b;
1803 if (ch > mapping->range.last)
1804 return 1;
1805 else if (ch < mapping->range.first)
1806 return -1;
1807 else
1808 return 0;
1811 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
1813 return bsearch(&ch, fallback->mappings, fallback->count, sizeof(*fallback->mappings),
1814 compare_fallback_mapping);
1817 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
1818 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
1820 IDWriteFontFamily *family;
1821 BOOL exists = FALSE;
1822 HRESULT hr;
1823 UINT32 i;
1825 *font = NULL;
1827 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
1828 if (FAILED(hr))
1829 return hr;
1831 if (!exists)
1832 return E_FAIL;
1834 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
1835 if (FAILED(hr))
1836 return hr;
1838 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
1839 IDWriteFontFamily_Release(family);
1840 return hr;
1843 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
1845 HRESULT hr = S_OK;
1846 UINT32 i;
1848 for (i = 0; i < length; i++) {
1849 UINT16 script = get_char_script(text[i]);
1850 BOOL exists;
1852 if (script == Script_Unknown || script == Script_Common) {
1853 ++*mapped_length;
1854 continue;
1857 /* stop on first unsupported character */
1858 exists = FALSE;
1859 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
1860 if (hr == S_OK && exists)
1861 ++*mapped_length;
1862 else
1863 break;
1866 return hr;
1869 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
1870 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length, IDWriteFont **mapped_font)
1872 const struct fallback_mapping *mapping;
1873 HRESULT hr;
1875 mapping = find_fallback_mapping(fallback, text[0]);
1876 if (!mapping) {
1877 WARN("no mapping for 0x%x\n", text[0]);
1878 return E_FAIL;
1881 /* now let's see what fallback can handle */
1882 hr = create_matching_font(fallback->systemcollection, mapping->family, weight, style, stretch, mapped_font);
1883 if (FAILED(hr)) {
1884 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping->family),
1885 mapping->range.first, mapping->range.last, hr);
1886 return hr;
1889 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
1890 if (FAILED(hr))
1891 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping->family), hr);
1893 if (!*mapped_length) {
1894 IDWriteFont_Release(*mapped_font);
1895 *mapped_font = NULL;
1898 return *mapped_length ? S_OK : E_FAIL;
1901 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
1902 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
1903 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
1904 IDWriteFont **ret_font, FLOAT *scale)
1906 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1907 WCHAR *buff = NULL;
1908 const WCHAR *text;
1909 HRESULT hr;
1911 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
1912 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
1914 *mapped_length = 0;
1915 *scale = 1.0f;
1916 *ret_font = NULL;
1918 if (!source)
1919 return E_INVALIDARG;
1921 if (length == 0)
1922 return S_OK;
1924 if (!basecollection)
1925 basecollection = fallback->systemcollection;
1927 hr = get_text_source_ptr(source, position, length, &text, &buff);
1928 if (FAILED(hr))
1929 goto done;
1931 if (basefamily && *basefamily) {
1932 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
1933 if (FAILED(hr))
1934 goto done;
1936 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
1937 if (FAILED(hr))
1938 goto done;
1941 if (!*mapped_length) {
1942 IDWriteFont *mapped_font;
1944 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
1945 if (FAILED(hr)) {
1946 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
1947 if (*ret_font) {
1948 *mapped_length = length;
1949 hr = S_OK;
1952 else {
1953 if (*ret_font)
1954 IDWriteFont_Release(*ret_font);
1955 *ret_font = mapped_font;
1959 done:
1960 heap_free(buff);
1961 return hr;
1964 static const IDWriteFontFallbackVtbl fontfallbackvtbl = {
1965 fontfallback_QueryInterface,
1966 fontfallback_AddRef,
1967 fontfallback_Release,
1968 fontfallback_MapCharacters
1971 HRESULT create_system_fontfallback(IDWriteFactory3 *factory, IDWriteFontFallback **ret)
1973 struct dwrite_fontfallback *fallback;
1975 *ret = NULL;
1977 fallback = heap_alloc(sizeof(*fallback));
1978 if (!fallback)
1979 return E_OUTOFMEMORY;
1981 fallback->IDWriteFontFallback_iface.lpVtbl = &fontfallbackvtbl;
1982 fallback->factory = factory;
1983 fallback->mappings = fontfallback_neutral_data;
1984 fallback->count = sizeof(fontfallback_neutral_data)/sizeof(fontfallback_neutral_data[0]);
1985 IDWriteFactory2_GetSystemFontCollection((IDWriteFactory2*)fallback->factory, &fallback->systemcollection, FALSE);
1987 *ret = &fallback->IDWriteFontFallback_iface;
1988 return S_OK;
1991 void release_system_fontfallback(IDWriteFontFallback *iface)
1993 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1994 IDWriteFontCollection_Release(fallback->systemcollection);
1995 heap_free(fallback);