wined3d: Get rid of the "render_to_fbo" field from the wined3d_swapchain structure.
[wine.git] / dlls / dwrite / analyzer.c
blob89c76da0bb9b28105003030e7bf722595c771ffe
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 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
35 #define NATIVE_DIGITS_LEN 11
37 struct dwritescript_properties
39 DWRITE_SCRIPT_PROPERTIES props;
40 UINT32 scripttags[3]; /* Maximum 2 script tags, 0-terminated. */
41 BOOL is_complex;
44 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
46 /* NOTE: keep this array synced with script ids from scripts.h */
47 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
48 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
49 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
50 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
51 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('a','r','a','b') }, TRUE },
52 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','n') } },
53 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','v','s','t') } },
54 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('b','a','l','i') } },
55 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','m','u') } },
56 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','t','k') } },
57 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('b','n','g','2'), _OT('b','e','n','g') }, TRUE },
58 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('b','o','p','o') } },
59 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','h') } },
60 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','r','a','i') }, TRUE },
61 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','u','g','i') } },
62 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('b','u','h','d') } },
63 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','n','s') }, TRUE },
64 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('c','a','r','i') } },
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') }, 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') }, TRUE },
72 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('d','e','v','2'), _OT('d','e','v','a') }, TRUE },
73 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('e','g','y','p') } },
74 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','t','h','i') }, TRUE },
75 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','e','o','r') } },
76 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','l','a','g') } },
77 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','t','h') } },
78 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','e','k') } },
79 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','j','r','2'), _OT('g','u','j','r') }, TRUE },
80 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','u','r','2'), _OT('g','u','r','u') }, TRUE },
81 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('h','a','n','i') } },
82 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, { _OT('h','a','n','g') }, TRUE },
83 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('h','a','n','o') } },
84 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('h','e','b','r') }, TRUE },
85 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
86 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','r','m','i') } },
87 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','h','l','i') } },
88 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','r','t','i') } },
89 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('j','a','v','a') } },
90 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('k','t','h','i') } },
91 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','n','d','2'), _OT('k','n','d','a') }, TRUE },
92 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('k','a','n','a') } },
93 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('k','a','l','i') } },
94 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('k','h','a','r') } },
95 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('k','h','m','r') }, TRUE },
96 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('l','a','o',' ') }, TRUE },
97 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','a','t','n') } },
98 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','e','p','c') } },
99 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','m','b') } },
100 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','b') } },
101 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','i','s','u') } },
102 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','c','i') } },
103 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('l','y','d','i') } },
104 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','l','m','2'), _OT('m','l','y','m') }, TRUE },
105 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','d') } },
106 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','t','e','i') } },
107 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','o','n','g') }, TRUE },
108 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('m','y','m','r') }, TRUE },
109 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','u') }, TRUE },
110 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('n','k','o',' ') }, TRUE },
111 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, { _OT('o','g','a','m') }, TRUE },
112 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','l','c','k') } },
113 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('i','t','a','l') } },
114 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('x','p','e','o') }, TRUE },
115 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','r','b') } },
116 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','k','h') } },
117 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('o','r','y','2'), _OT('o','r','y','a') }, TRUE },
118 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','m','a') }, TRUE },
119 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','a','g') }, TRUE },
120 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('p','h','n','x') } },
121 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('r','j','n','g') } },
122 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('r','u','n','r') }, TRUE },
123 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','m','r') } },
124 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','a','u','r') } },
125 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','a','w') } },
126 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','h') }, TRUE },
127 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','u','n','d') } },
128 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('s','y','l','o') } },
129 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('s','y','r','c') }, TRUE },
130 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','g','l','g') } },
131 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','g','b') } },
132 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','l','e') }, TRUE },
133 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('l','a','n','a') } },
134 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','a','v','t') } },
135 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','m','l','2'), _OT('t','a','m','l') }, TRUE },
136 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','e','l','2'), _OT('t','e','l','u') }, TRUE },
137 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','h','a','a') }, TRUE },
138 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, { _OT('t','h','a','i') }, TRUE },
139 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','i','b','t') }, TRUE },
140 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','f','n','g') }, TRUE },
141 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('u','g','a','r') } },
142 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('v','a','i',' ') }, TRUE },
143 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('y','i',' ',' ') }, TRUE },
144 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('c','a','k','m') } },
145 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','r','c') } },
146 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, { _OT('m','e','r','o') } },
147 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, { _OT('p','l','r','d') } },
148 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','h','r','d') } },
149 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','r','a') } },
150 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','k','r') } },
151 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','a','s','s') } },
152 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','g','h','b') } },
153 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('d','u','p','l') } },
154 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('e','l','b','a') } },
155 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('g','r','a','n') } },
156 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('k','h','o','j') } },
157 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','i','n','d') } },
158 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('l','i','n','a') } },
159 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','h','j') } },
160 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('m','a','n','i') } },
161 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','n','d') } },
162 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('m','o','d','i') } },
163 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','r','o','o') } },
164 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('n','b','a','t') } },
165 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','a','r','b') } },
166 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','e','r','m') } },
167 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','m','n','g') } },
168 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','l','m') } },
169 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('p','a','u','c') } },
170 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, { _OT('p','h','l','p') } },
171 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, { _OT('s','i','d','d') } },
172 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('t','i','r','h') } },
173 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('w','a','r','a') } },
174 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','d','l','m') } },
175 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('a','h','o','m') } },
176 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','l','u','w') } },
177 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('b','h','k','s') } },
178 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','a','t','r') } },
179 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','r','c') } },
180 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','u','l','t') } },
181 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('n','e','w','a') } },
182 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('h','u','n','g') } },
183 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('o','s','g','e') } },
184 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','g','n','w') } },
185 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('t','a','n','g') } },
186 { /* Gonm */ { 0x6d6e6f47, 313, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('g','o','n','m') } },
187 { /* Nshu */ { 0x7568734e, 499, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, { _OT('n','s','h','u') } },
188 { /* Soyo */ { 0x6f796f53, 329, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','y','o') } },
189 { /* Zanb */ { 0x626e615a, 339, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('z','a','n','b') } },
190 { /* Dogr */ { 0x72676f44, 328, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('d','o','g','r') } },
191 { /* Gong */ { 0x676e6f47, 312, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('g','o','n','g') } },
192 { /* Rohg */ { 0x67686f52, 167, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, { _OT('r','o','h','g') } },
193 { /* Maka */ { 0x616b614d, 366, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','a','k','a') } },
194 { /* Medf */ { 0x6664654d, 265, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, { _OT('m','e','d','f') } },
195 { /* Sogo */ { 0x6f676f53, 142, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, { _OT('s','o','g','o') } },
196 { /* Sogd */ { 0x64676f53, 141, 8, 0x0020, 1, 1, 0, 0, 0, 1, 1 }, { _OT('s','o','g','d') } },
197 { /* Elym */ { 0x6d796c45, 128, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, { _OT('e','l','y','m') } },
198 { /* Hmnp */ { 0x706e6d48, 451, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('h','m','n','p') } },
199 { /* Nand */ { 0x646e614e, 311, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, { _OT('n','a','n','d') } },
200 { /* Wcho */ { 0x6f686357, 283, 8, 0x0020, 1, 1, 0, 0, 0, 0, 0 }, { _OT('w','c','h','o') } },
202 #undef _OT
204 const char *debugstr_sa_script(UINT16 script)
206 return script < Script_LastId ? debugstr_tag(dwritescripts_properties[script].props.isoScriptCode) : "undefined";
209 /* system font falback configuration */
210 static const WCHAR *cjk_families[] = { L"Meiryo" };
212 static const DWRITE_UNICODE_RANGE cjk_ranges[] =
214 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
215 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
216 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
219 struct fallback_mapping {
220 DWRITE_UNICODE_RANGE *ranges;
221 UINT32 ranges_count;
222 WCHAR **families;
223 UINT32 families_count;
224 IDWriteFontCollection *collection;
225 WCHAR *locale;
226 FLOAT scale;
229 static const struct fallback_mapping fontfallback_neutral_data[] = {
230 #define MAPPING_RANGE(ranges, families) \
231 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
232 (WCHAR **)families, ARRAY_SIZE(families) }
234 MAPPING_RANGE(cjk_ranges, cjk_families),
236 #undef MAPPING_RANGE
239 struct dwrite_fontfallback
241 IDWriteFontFallback1 IDWriteFontFallback1_iface;
242 LONG refcount;
243 IDWriteFactory7 *factory;
244 IDWriteFontCollection1 *systemcollection;
245 struct fallback_mapping *mappings;
246 UINT32 mappings_count;
249 struct dwrite_fontfallback_builder
251 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface;
252 LONG refcount;
253 IDWriteFactory7 *factory;
254 struct fallback_mapping *mappings;
255 size_t size;
256 size_t count;
259 struct dwrite_numbersubstitution
261 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
262 LONG refcount;
264 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
265 WCHAR *locale;
266 BOOL ignore_user_override;
269 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
271 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
274 static struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface);
276 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback1(IDWriteFontFallback1 *iface)
278 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback1_iface);
281 static inline struct dwrite_fontfallback_builder *impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder *iface)
283 return CONTAINING_RECORD(iface, struct dwrite_fontfallback_builder, IDWriteFontFallbackBuilder_iface);
286 static inline UINT16 get_char_script(WCHAR c)
288 UINT16 script = get_table_entry(wine_scripts_table, c);
289 return script == Script_Inherited ? Script_Unknown : script;
292 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
294 DWRITE_SCRIPT_ANALYSIS sa;
295 WORD type;
297 GetStringTypeW(CT_CTYPE1, &c, 1, &type);
298 sa.script = get_char_script(c);
299 sa.shapes = (type & C1_CNTRL) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
300 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
301 return sa;
304 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
306 DWRITE_SCRIPT_ANALYSIS sa;
307 UINT32 pos, i, seq_length;
309 if (!length)
310 return S_OK;
312 sa = get_char_sa(*text);
314 pos = position;
315 seq_length = 1;
317 for (i = 1; i < length; i++)
319 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
321 /* Unknown type is ignored when preceded or followed by another script */
322 switch (sa.script) {
323 case Script_Unknown:
324 sa.script = cur_sa.script;
325 break;
326 case Script_Common:
327 if (cur_sa.script == Script_Unknown)
328 cur_sa.script = sa.script;
329 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
330 sa.script = cur_sa.script;
331 break;
332 default:
333 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
334 cur_sa.script = sa.script;
337 /* this is a length of a sequence to be reported next */
338 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
339 seq_length++;
340 else {
341 HRESULT hr;
343 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
344 if (FAILED(hr)) return hr;
345 pos = position + i;
346 seq_length = 1;
347 sa = cur_sa;
351 /* one char length case or normal completion call */
352 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
355 struct linebreaking_state {
356 DWRITE_LINE_BREAKPOINT *breakpoints;
357 UINT32 count;
360 enum BreakConditionLocation {
361 BreakConditionBefore,
362 BreakConditionAfter
365 enum linebreaking_classes {
366 b_BK = 1,
367 b_CR,
368 b_LF,
369 b_CM,
370 b_SG,
371 b_GL,
372 b_CB,
373 b_SP,
374 b_ZW,
375 b_NL,
376 b_WJ,
377 b_JL,
378 b_JV,
379 b_JT,
380 b_H2,
381 b_H3,
382 b_XX,
383 b_OP,
384 b_CL,
385 b_CP,
386 b_QU,
387 b_NS,
388 b_EX,
389 b_SY,
390 b_IS,
391 b_PR,
392 b_PO,
393 b_NU,
394 b_AL,
395 b_ID,
396 b_IN,
397 b_HY,
398 b_BB,
399 b_BA,
400 b_SA,
401 b_AI,
402 b_B2,
403 b_HL,
404 b_CJ,
405 b_RI,
406 b_EB,
407 b_EM,
408 b_ZWJ,
411 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
413 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
414 return TRUE;
416 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
417 return TRUE;
419 return FALSE;
422 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
423 set to "can break" and could only be changed once. */
424 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
425 struct linebreaking_state *state)
427 if (location == BreakConditionBefore) {
428 if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
429 return;
430 state->breakpoints[pos].breakConditionBefore = condition;
431 if (pos > 0)
432 state->breakpoints[pos-1].breakConditionAfter = condition;
434 else {
435 if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
436 return;
437 state->breakpoints[pos].breakConditionAfter = condition;
438 if (pos + 1 < state->count)
439 state->breakpoints[pos+1].breakConditionBefore = condition;
443 BOOL lb_is_newline_char(WCHAR ch)
445 short c = get_table_entry(wine_linebreak_table, ch);
446 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
449 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
451 struct linebreaking_state state;
452 short *break_class;
453 int i, j;
455 break_class = heap_calloc(count, sizeof(*break_class));
456 if (!break_class)
457 return E_OUTOFMEMORY;
459 state.breakpoints = breakpoints;
460 state.count = count;
462 for (i = 0; i < count; i++)
464 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
466 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
467 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
468 breakpoints[i].isWhitespace = !!iswspace(text[i]);
469 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
470 breakpoints[i].padding = 0;
472 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
473 switch (break_class[i])
475 case b_AI:
476 case b_SA:
477 case b_SG:
478 case b_XX:
479 break_class[i] = b_AL;
480 break;
481 case b_CJ:
482 break_class[i] = b_NS;
483 break;
487 /* LB2 - never break at the start */
488 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
489 /* LB3 - always break at the end. */
490 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
492 /* LB4 - LB6 - mandatory breaks. */
493 for (i = 0; i < count; i++)
495 switch (break_class[i])
497 /* LB4 - LB6 */
498 case b_CR:
499 /* LB5 - don't break CR x LF */
500 if (i < count-1 && break_class[i+1] == b_LF)
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;
506 case b_LF:
507 case b_NL:
508 case b_BK:
509 /* LB4 - LB5 - always break after hard breaks */
510 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
511 /* LB6 - do not break before hard breaks */
512 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
513 break;
517 /* LB7 - LB8 - explicit breaks and non-breaks */
518 for (i = 0; i < count; i++)
520 switch (break_class[i])
522 /* LB7 - do not break before spaces or zero-width space */
523 case b_SP:
524 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
525 break;
526 case b_ZW:
527 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
529 /* LB8 - break before character after zero-width space, skip spaces in-between */
530 j = i;
531 while (j < count-1 && break_class[j+1] == b_SP)
532 j++;
533 if (j < count-1 && break_class[j+1] != b_ZW)
534 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
535 break;
536 /* LB8a - do not break after ZWJ */
537 case b_ZWJ:
538 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
539 break;
543 /* LB9 - LB10 - combining marks */
544 for (i = 0; i < count; i++)
546 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
548 if (i > 0)
550 switch (break_class[i-1])
552 case b_SP:
553 case b_BK:
554 case b_CR:
555 case b_LF:
556 case b_NL:
557 case b_ZW:
558 break_class[i] = b_AL;
559 break;
560 default:
562 break_class[i] = break_class[i-1];
563 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
567 else break_class[i] = b_AL;
571 for (i = 0; i < count; i++)
573 switch (break_class[i])
575 /* LB11 - don't break before and after word joiner */
576 case b_WJ:
577 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
578 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
579 break;
580 /* LB12 - don't break after glue */
581 case b_GL:
582 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
583 /* LB12a */
584 if (i > 0)
586 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
587 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
589 break;
590 /* LB13 */
591 case b_CL:
592 case b_CP:
593 case b_EX:
594 case b_IS:
595 case b_SY:
596 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
597 break;
598 /* LB14 - do not break after OP, even after spaces */
599 case b_OP:
600 j = i;
601 while (j < count-1 && break_class[j+1] == b_SP)
602 j++;
603 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
604 break;
605 /* LB15 - do not break within QU-OP, even with intervening spaces */
606 case b_QU:
607 j = i;
608 while (j < count-1 && break_class[j+1] == b_SP)
609 j++;
610 if (j < count - 1 && break_class[j+1] == b_OP)
611 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
612 break;
613 /* LB16 */
614 case b_NS:
615 j = i-1;
616 while(j > 0 && break_class[j] == b_SP)
617 j--;
618 if (break_class[j] == b_CL || break_class[j] == b_CP)
619 for (j++; j <= i; j++)
620 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
621 break;
622 /* LB17 - do not break within B2, even with intervening spaces */
623 case b_B2:
624 j = i;
625 while (j < count && break_class[j+1] == b_SP)
626 j++;
627 if (j < count - 1 && break_class[j+1] == b_B2)
628 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
629 break;
633 for (i = 0; i < count; i++)
635 switch(break_class[i])
637 /* LB18 - break is allowed after space */
638 case b_SP:
639 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
640 break;
641 /* LB19 - don't break before or after quotation mark */
642 case b_QU:
643 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
644 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
645 break;
646 /* LB20 */
647 case b_CB:
648 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
649 if (i < count - 1 && break_class[i+1] != b_QU)
650 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
651 break;
652 /* LB21 */
653 case b_BA:
654 case b_HY:
655 case b_NS:
656 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
657 break;
658 case b_BB:
659 if (i < count - 1 && break_class[i+1] != b_CB)
660 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
661 break;
662 /* LB21a, LB21b */
663 case b_HL:
664 /* LB21a */
665 if (i < count-1)
666 switch (break_class[i+1])
668 case b_HY:
669 case b_BA:
670 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
672 /* LB21b */
673 if (i > 0 && break_class[i-1] == b_SY)
674 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
675 break;
676 /* LB22 */
677 case b_IN:
678 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
679 break;
682 if (i < count-1)
684 /* LB23 - do not break between digits and letters */
685 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
686 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
687 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
688 (break_class[i] == b_NU && break_class[i+1] == b_HL))
689 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
691 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
692 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
693 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
694 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
695 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
696 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
697 (break_class[i] == b_EB && break_class[i+1] == b_PO))
698 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
700 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
701 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
702 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
703 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
704 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
705 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
706 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
707 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
708 (break_class[i] == b_HL && break_class[i+1] == b_PO))
709 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
711 /* LB25 */
712 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
713 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
714 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
715 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
716 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
717 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
718 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
719 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
720 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
721 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
722 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
723 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
724 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
725 (break_class[i] == b_SY && break_class[i+1] == b_NU))
726 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
728 /* LB26 */
729 if (break_class[i] == b_JL)
731 switch (break_class[i+1])
733 case b_JL:
734 case b_JV:
735 case b_H2:
736 case b_H3:
737 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
740 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
741 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
742 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
743 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
744 break_class[i+1] == b_JT)
745 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
747 /* LB27 */
748 switch (break_class[i])
750 case b_JL:
751 case b_JV:
752 case b_JT:
753 case b_H2:
754 case b_H3:
755 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
756 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
758 if (break_class[i] == b_PR)
760 switch (break_class[i+1])
762 case b_JL:
763 case b_JV:
764 case b_JT:
765 case b_H2:
766 case b_H3:
767 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
771 /* LB28 */
772 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
773 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
774 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
775 (break_class[i] == b_HL && break_class[i+1] == b_HL))
776 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
778 /* LB29 */
779 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
780 (break_class[i] == b_IS && break_class[i+1] == b_HL))
781 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
783 /* LB30 */
784 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
785 break_class[i+1] == b_OP)
786 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
787 if (break_class[i] == b_CP &&
788 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
789 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
791 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
792 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
793 unsigned int c = 0;
795 j = i + 1;
796 while (j > 0 && break_class[--j] == b_RI)
797 c++;
799 if ((c & 1) == 0)
800 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
803 /* LB30b - do not break between an emoji base and an emoji modifier */
804 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
805 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
809 /* LB31 - allow breaks everywhere else. */
810 for (i = 0; i < count; i++)
812 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
813 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
816 heap_free(break_class);
817 return S_OK;
820 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
822 TRACE("%s, %p.\n", debugstr_guid(riid), obj);
824 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
825 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
826 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
827 IsEqualIID(riid, &IID_IUnknown))
829 *obj = iface;
830 return S_OK;
833 WARN("%s not implemented.\n", debugstr_guid(riid));
835 *obj = NULL;
836 return E_NOINTERFACE;
839 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
841 return 2;
844 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
846 return 1;
849 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
850 data after a first request. */
851 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
853 HRESULT hr;
854 UINT32 len;
856 *buff = NULL;
857 *text = NULL;
858 len = 0;
859 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
860 if (FAILED(hr)) return hr;
862 if (len < length) {
863 UINT32 read;
865 *buff = heap_alloc(length*sizeof(WCHAR));
866 if (!*buff)
867 return E_OUTOFMEMORY;
868 memcpy(*buff, *text, len*sizeof(WCHAR));
869 read = len;
871 while (read < length && *text) {
872 *text = NULL;
873 len = 0;
874 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
875 if (FAILED(hr)) {
876 heap_free(*buff);
877 return hr;
879 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
880 read += len;
883 *text = *buff;
886 return hr;
889 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
890 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
892 WCHAR *buff = NULL;
893 const WCHAR *text;
894 HRESULT hr;
896 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
898 if (length == 0)
899 return S_OK;
901 hr = get_text_source_ptr(source, position, length, &text, &buff);
902 if (FAILED(hr))
903 return hr;
905 hr = analyze_script(text, position, length, sink);
906 heap_free(buff);
908 return hr;
911 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
912 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
914 UINT8 *levels = NULL, *explicit = NULL;
915 UINT8 baselevel, level, explicit_level;
916 UINT32 pos, i, seq_length;
917 WCHAR *buff = NULL;
918 const WCHAR *text;
919 HRESULT hr;
921 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
923 if (!length)
924 return S_OK;
926 hr = get_text_source_ptr(source, position, length, &text, &buff);
927 if (FAILED(hr))
928 return hr;
930 levels = heap_calloc(length, sizeof(*levels));
931 explicit = heap_calloc(length, sizeof(*explicit));
933 if (!levels || !explicit) {
934 hr = E_OUTOFMEMORY;
935 goto done;
938 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
939 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
940 if (FAILED(hr))
941 goto done;
943 level = levels[0];
944 explicit_level = explicit[0];
945 pos = position;
946 seq_length = 1;
948 for (i = 1; i < length; i++) {
949 if (levels[i] == level && explicit[i] == explicit_level)
950 seq_length++;
951 else {
952 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
953 if (FAILED(hr))
954 goto done;
956 pos += seq_length;
957 seq_length = 1;
958 level = levels[i];
959 explicit_level = explicit[i];
962 /* one char length case or normal completion call */
963 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
965 done:
966 heap_free(explicit);
967 heap_free(levels);
968 heap_free(buff);
970 return hr;
973 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
974 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
976 static int once;
978 if (!once++)
979 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
980 return S_OK;
983 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
984 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
986 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
987 WCHAR *buff = NULL;
988 const WCHAR *text;
989 HRESULT hr;
990 UINT32 len;
992 TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
994 if (length == 0)
995 return S_OK;
997 /* get some, check for length */
998 text = NULL;
999 len = 0;
1000 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
1001 if (FAILED(hr)) return hr;
1003 if (len < length) {
1004 UINT32 read;
1006 buff = heap_calloc(length, sizeof(*buff));
1007 if (!buff)
1008 return E_OUTOFMEMORY;
1009 memcpy(buff, text, len*sizeof(WCHAR));
1010 read = len;
1012 while (read < length && text) {
1013 text = NULL;
1014 len = 0;
1015 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
1016 if (FAILED(hr))
1017 goto done;
1018 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
1019 read += len;
1022 text = buff;
1025 breakpoints = heap_calloc(length, sizeof(*breakpoints));
1026 if (!breakpoints) {
1027 hr = E_OUTOFMEMORY;
1028 goto done;
1031 hr = analyze_linebreaks(text, length, breakpoints);
1032 if (FAILED(hr))
1033 goto done;
1035 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1037 done:
1038 heap_free(breakpoints);
1039 heap_free(buff);
1041 return hr;
1044 static UINT32 get_opentype_language(const WCHAR *locale)
1046 UINT32 language = DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1048 if (locale) {
1049 WCHAR tag[5];
1050 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, ARRAY_SIZE(tag)))
1051 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1054 return language;
1057 static void get_number_substitutes(IDWriteNumberSubstitution *substitution, BOOL is_rtl, WCHAR *digits)
1059 struct dwrite_numbersubstitution *numbersubst = unsafe_impl_from_IDWriteNumberSubstitution(substitution);
1060 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1061 WCHAR isolang[9];
1062 DWORD lctype;
1064 digits[0] = 0;
1066 if (!numbersubst)
1067 return;
1069 lctype = numbersubst->ignore_user_override ? LOCALE_NOUSEROVERRIDE : 0;
1071 if (numbersubst->method == DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE) {
1072 DWORD value;
1074 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1075 if (GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, (WCHAR *)&value, 2)) {
1076 switch (value)
1078 case 0:
1079 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL;
1080 break;
1081 case 2:
1082 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL;
1083 break;
1084 case 1:
1085 default:
1086 if (value != 1)
1087 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value, debugstr_w(numbersubst->locale));
1090 else
1091 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst->locale));
1093 else
1094 method = numbersubst->method;
1096 switch (method)
1098 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL:
1099 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1100 break;
1101 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1102 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL:
1103 if (GetLocaleInfoEx(numbersubst->locale, LOCALE_SISO639LANGNAME, isolang, ARRAY_SIZE(isolang)))
1105 static const WCHAR arabicW[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1107 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1108 if (!wcscmp(L"ar", isolang))
1110 wcscpy(digits, arabicW);
1111 break;
1114 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1115 break;
1116 default:
1120 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !*digits) {
1121 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst->locale), method);
1122 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1125 if ((method == DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL && !is_rtl) || method == DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE)
1126 digits[0] = 0;
1129 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES const **features,
1130 UINT32 const *feature_range_lengths, UINT32 feature_ranges)
1132 UINT32 i, j, start;
1134 if (!TRACE_ON(dwrite) || !features)
1135 return;
1137 for (i = 0, start = 0; i < feature_ranges; start += feature_range_lengths[i++]) {
1138 TRACE("feature range [%u,%u)\n", start, start + feature_range_lengths[i]);
1139 for (j = 0; j < features[i]->featureCount; j++)
1140 TRACE("feature %s, parameter %u\n", debugstr_tag(features[i]->features[j].nameTag),
1141 features[i]->features[j].parameter);
1145 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1146 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1147 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1148 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1149 UINT32 const* feature_range_lengths, UINT32 feature_ranges, UINT32 max_glyph_count,
1150 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16 *glyphs,
1151 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1153 const struct dwritescript_properties *scriptprops;
1154 struct scriptshaping_context context = { 0 };
1155 struct dwrite_fontface *font_obj;
1156 WCHAR digits[NATIVE_DIGITS_LEN];
1157 unsigned int glyph_count;
1158 HRESULT hr;
1160 TRACE("%s:%u, %p, %d, %d, %s, %s, %p, %p, %p, %u, %u, %p, %p, %p, %p, %p.\n", debugstr_wn(text, length),
1161 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1162 features, feature_range_lengths, feature_ranges, max_glyph_count, clustermap, text_props, glyphs,
1163 glyph_props, actual_glyph_count);
1165 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1167 get_number_substitutes(substitution, is_rtl, digits);
1168 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1169 glyph_count = max(max_glyph_count, length);
1171 context.cache = fontface_get_shaping_cache(font_obj);
1172 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1173 context.text = text;
1174 context.length = length;
1175 context.is_rtl = is_rtl;
1176 context.is_sideways = is_sideways;
1177 context.u.subst.glyphs = heap_calloc(glyph_count, sizeof(*glyphs));
1178 context.u.subst.glyph_props = heap_calloc(glyph_count, sizeof(*glyph_props));
1179 context.u.subst.text_props = text_props;
1180 context.u.subst.clustermap = clustermap;
1181 context.u.subst.max_glyph_count = max_glyph_count;
1182 context.u.subst.capacity = glyph_count;
1183 context.u.subst.digits = digits;
1184 context.language_tag = get_opentype_language(locale);
1185 context.user_features.features = features;
1186 context.user_features.range_lengths = feature_range_lengths;
1187 context.user_features.range_count = feature_ranges;
1188 context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos));
1189 context.table = &context.cache->gsub;
1191 *actual_glyph_count = 0;
1193 if (!context.u.subst.glyphs || !context.u.subst.glyph_props || !context.glyph_infos)
1195 hr = E_OUTOFMEMORY;
1196 goto failed;
1199 scriptprops = &dwritescripts_properties[context.script];
1200 hr = shape_get_glyphs(&context, scriptprops->scripttags);
1201 if (SUCCEEDED(hr))
1203 *actual_glyph_count = context.glyph_count;
1204 memcpy(glyphs, context.u.subst.glyphs, context.glyph_count * sizeof(*glyphs));
1205 memcpy(glyph_props, context.u.subst.glyph_props, context.glyph_count * sizeof(*glyph_props));
1208 failed:
1209 heap_free(context.u.subst.glyph_props);
1210 heap_free(context.u.subst.glyphs);
1211 heap_free(context.glyph_infos);
1213 return hr;
1216 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1217 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1218 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1219 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, BOOL is_sideways, BOOL is_rtl,
1220 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1221 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1223 const struct dwritescript_properties *scriptprops;
1224 struct scriptshaping_context context;
1225 struct dwrite_fontface *font_obj;
1226 unsigned int i;
1227 HRESULT hr;
1229 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),
1230 clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1231 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_lengths,
1232 feature_ranges, advances, offsets);
1234 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1236 if (glyph_count == 0)
1237 return S_OK;
1239 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1241 for (i = 0; i < glyph_count; ++i)
1243 if (glyph_props[i].isZeroWidthSpace)
1244 advances[i] = 0.0f;
1245 else
1246 advances[i] = fontface_get_scaled_design_advance(font_obj, DWRITE_MEASURING_MODE_NATURAL, emSize, 1.0f,
1247 NULL, glyphs[i], is_sideways);
1248 offsets[i].advanceOffset = 0.0f;
1249 offsets[i].ascenderOffset = 0.0f;
1252 context.cache = fontface_get_shaping_cache(font_obj);
1253 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1254 context.text = text;
1255 context.length = text_len;
1256 context.is_rtl = is_rtl;
1257 context.is_sideways = is_sideways;
1258 context.u.pos.glyphs = glyphs;
1259 context.u.pos.glyph_props = glyph_props;
1260 context.u.pos.text_props = text_props;
1261 context.u.pos.clustermap = clustermap;
1262 context.glyph_count = glyph_count;
1263 context.emsize = emSize;
1264 context.measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
1265 context.advances = advances;
1266 context.offsets = offsets;
1267 context.language_tag = get_opentype_language(locale);
1268 context.user_features.features = features;
1269 context.user_features.range_lengths = feature_range_lengths;
1270 context.user_features.range_count = feature_ranges;
1271 context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos));
1272 context.table = &context.cache->gpos;
1274 if (!context.glyph_infos)
1276 hr = E_OUTOFMEMORY;
1277 goto failed;
1280 scriptprops = &dwritescripts_properties[context.script];
1281 hr = shape_get_positions(&context, scriptprops->scripttags);
1283 failed:
1284 heap_free(context.glyph_infos);
1286 return hr;
1289 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1290 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES *text_props,
1291 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1292 UINT32 glyph_count, IDWriteFontFace *fontface, float emSize, float ppdip,
1293 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1294 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1295 UINT32 const* feature_range_lengths, UINT32 feature_ranges, float *advances, DWRITE_GLYPH_OFFSET *offsets)
1297 const struct dwritescript_properties *scriptprops;
1298 struct scriptshaping_context context;
1299 DWRITE_MEASURING_MODE measuring_mode;
1300 struct dwrite_fontface *font_obj;
1301 unsigned int i;
1302 HRESULT hr;
1304 TRACE("%s, %p, %p, %u, %p, %p, %u, %p, %.2f, %.2f, %p, %d, %d, %d, %s, %s, %p, %p, %u, %p, %p.\n",
1305 debugstr_wn(text, text_len), clustermap, text_props, text_len, glyphs, glyph_props, glyph_count, fontface,
1306 emSize, ppdip, transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script),
1307 debugstr_w(locale), features, feature_range_lengths, feature_ranges, advances, offsets);
1309 analyzer_dump_user_features(features, feature_range_lengths, feature_ranges);
1311 if (glyph_count == 0)
1312 return S_OK;
1314 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1316 measuring_mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
1318 for (i = 0; i < glyph_count; ++i)
1320 if (glyph_props[i].isZeroWidthSpace)
1321 advances[i] = 0.0f;
1322 else
1323 advances[i] = fontface_get_scaled_design_advance(font_obj, measuring_mode, emSize, ppdip,
1324 transform, glyphs[i], is_sideways);
1325 offsets[i].advanceOffset = 0.0f;
1326 offsets[i].ascenderOffset = 0.0f;
1329 context.cache = fontface_get_shaping_cache(font_obj);
1330 context.script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1331 context.text = text;
1332 context.length = text_len;
1333 context.is_rtl = is_rtl;
1334 context.is_sideways = is_sideways;
1335 context.u.pos.glyphs = glyphs;
1336 context.u.pos.glyph_props = glyph_props;
1337 context.u.pos.text_props = text_props;
1338 context.u.pos.clustermap = clustermap;
1339 context.glyph_count = glyph_count;
1340 context.emsize = emSize * ppdip;
1341 context.measuring_mode = measuring_mode;
1342 context.advances = advances;
1343 context.offsets = offsets;
1344 context.language_tag = get_opentype_language(locale);
1345 context.user_features.features = features;
1346 context.user_features.range_lengths = feature_range_lengths;
1347 context.user_features.range_count = feature_ranges;
1348 context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos));
1349 context.table = &context.cache->gpos;
1351 if (!context.glyph_infos)
1353 hr = E_OUTOFMEMORY;
1354 goto failed;
1357 scriptprops = &dwritescripts_properties[context.script];
1358 hr = shape_get_positions(&context, scriptprops->scripttags);
1360 failed:
1361 heap_free(context.glyph_infos);
1363 return hr;
1366 static HRESULT apply_cluster_spacing(float leading_spacing, float trailing_spacing, float min_advance_width,
1367 unsigned int start, unsigned int end, float const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1368 DWRITE_SHAPING_GLYPH_PROPERTIES const *glyph_props, float *modified_advances,
1369 DWRITE_GLYPH_OFFSET *modified_offsets)
1371 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1372 unsigned int first_spacing, last_spacing, i;
1373 float advance, origin = 0.0f, *deltas;
1374 BOOL is_spacing_cluster = FALSE;
1376 if (modified_advances != advances)
1377 memcpy(&modified_advances[start], &advances[start], (end - start + 1) * sizeof(*advances));
1378 if (modified_offsets != offsets)
1379 memcpy(&modified_offsets[start], &offsets[start], (end - start + 1) * sizeof(*offsets));
1381 for (first_spacing = start; first_spacing <= end; ++first_spacing)
1383 if ((is_spacing_cluster = !glyph_props[first_spacing].isZeroWidthSpace))
1384 break;
1387 /* Nothing to adjust if there is no spacing glyphs. */
1388 if (!is_spacing_cluster)
1389 return S_OK;
1391 for (last_spacing = end; last_spacing >= start; --last_spacing)
1393 if (!glyph_props[last_spacing].isZeroWidthSpace)
1394 break;
1397 deltas = heap_calloc(end - start + 1, sizeof(*deltas));
1398 if (!deltas)
1399 return E_OUTOFMEMORY;
1401 /* Cluster advance, note that properties are ignored. */
1402 origin = offsets[start].advanceOffset;
1403 for (i = start, advance = 0.0f; i <= end; ++i)
1405 float cur = advance + offsets[i].advanceOffset;
1407 deltas[i - start] = cur - origin;
1409 advance += advances[i];
1410 origin = cur;
1413 /* Negative spacing. */
1414 if (leading_spacing < 0.0f)
1416 advance += leading_spacing;
1417 modified_advances[first_spacing] += leading_spacing;
1418 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1421 if (trailing_spacing < 0.0f)
1423 advance += trailing_spacing;
1424 modified_advances[last_spacing] += trailing_spacing;
1427 /* Minimal advance. */
1428 advance = min_advance_width - advance;
1429 if (advance > 0.0f) {
1430 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1431 float half = advance / 2.0f;
1433 if (!reduced)
1435 modified_advances[first_spacing] += half;
1436 modified_advances[last_spacing] += half;
1437 modified_offsets[first_spacing].advanceOffset += half;
1439 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1441 modified_advances[first_spacing] += half;
1442 modified_advances[last_spacing] += half;
1443 modified_offsets[first_spacing].advanceOffset += half;
1445 else if (leading_spacing < 0.0f)
1447 modified_advances[first_spacing] += advance;
1448 modified_offsets[first_spacing].advanceOffset += advance;
1450 else
1451 modified_advances[last_spacing] += advance;
1454 /* Positive spacing. */
1455 if (leading_spacing > 0.0f)
1457 modified_advances[first_spacing] += leading_spacing;
1458 modified_offsets[first_spacing].advanceOffset += leading_spacing;
1461 if (trailing_spacing > 0.0f)
1462 modified_advances[last_spacing] += trailing_spacing;
1464 /* Update offsets to preserve original relative positions within cluster. */
1465 for (i = first_spacing; i > start; --i)
1467 unsigned int cur = i - 1;
1468 modified_offsets[cur].advanceOffset = modified_advances[cur] + modified_offsets[i].advanceOffset -
1469 deltas[i - start];
1472 for (i = first_spacing + 1; i <= end; ++i)
1474 modified_offsets[i].advanceOffset = deltas[i - start] + modified_offsets[i - 1].advanceOffset -
1475 modified_advances[i - 1];
1478 heap_free(deltas);
1480 return S_OK;
1483 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1485 UINT16 g = clustermap[start];
1486 UINT32 length = 1;
1488 while (start < text_len && clustermap[++start] == g)
1489 length++;
1490 return length;
1493 /* Applies spacing adjustments to clusters.
1495 Adjustments are applied in the following order:
1497 1. Negative adjustments
1499 Leading and trailing spacing could be negative, at this step
1500 only negative ones are actually applied. Leading spacing is only
1501 applied to leading glyph, trailing - to trailing glyph.
1503 2. Minimum advance width
1505 Advances could only be reduced at this point or unchanged. In any
1506 case it's checked if cluster advance width is less than minimum width.
1507 If it's the case advance width is incremented up to minimum value.
1509 Important part is the direction in which this increment is applied;
1510 it depends on direction from which total cluster advance was trimmed
1511 at step 1. So it could be incremented from leading, trailing, or both
1512 sides. When applied to both sides, each side gets half of difference
1513 that brings advance to minimum width.
1515 3. Positive adjustments
1517 After minimum width rule was applied, positive spacing is applied in the same
1518 way as negative one on step 1.
1520 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1521 keeps its position in coordinate system where initial advance width is counted
1522 from 0.
1524 Glyph properties
1526 It's known that isZeroWidthSpace property keeps initial advance from changing.
1529 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1530 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1531 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1532 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1534 unsigned int i;
1536 TRACE("%.2f, %.2f, %.2f, %u, %u, %p, %p, %p, %p, %p, %p.\n", leading_spacing, trailing_spacing, min_advance_width,
1537 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1539 if (min_advance_width < 0.0f) {
1540 if (modified_advances != advances)
1541 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1542 return E_INVALIDARG;
1545 for (i = 0; i < len;)
1547 unsigned int length = get_cluster_length(clustermap, i, len);
1548 unsigned int start, end;
1550 start = clustermap[i];
1551 end = i + length < len ? clustermap[i + length] : glyph_count;
1553 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width, start, end - 1, advances,
1554 offsets, props, modified_advances, modified_offsets);
1556 i += length;
1559 return S_OK;
1562 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *fontface,
1563 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1564 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1566 struct dwrite_fontface *font_obj;
1567 const DWRITE_FONT_METRICS1 *metrics;
1569 TRACE("%p, %d, %d, %u, %s, %p, %p.\n", fontface, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1570 baseline_coord, exists);
1572 *exists = FALSE;
1573 *baseline_coord = 0;
1575 if (baseline == DWRITE_BASELINE_DEFAULT)
1576 baseline = vertical ? DWRITE_BASELINE_CENTRAL : DWRITE_BASELINE_ROMAN;
1578 if ((unsigned int)baseline > DWRITE_BASELINE_MAXIMUM)
1579 return E_INVALIDARG;
1581 /* TODO: fetch BASE table data if available. */
1583 if (!*exists && is_simulation_allowed)
1585 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1586 metrics = &font_obj->metrics;
1588 switch (baseline)
1590 case DWRITE_BASELINE_ROMAN:
1591 *baseline_coord = vertical ? metrics->descent : 0;
1592 break;
1593 case DWRITE_BASELINE_CENTRAL:
1594 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1595 -(metrics->ascent - metrics->descent) / 2;
1596 break;
1597 case DWRITE_BASELINE_MATH:
1598 *baseline_coord = vertical ? (metrics->ascent + metrics->descent) / 2 :
1599 -(metrics->ascent + metrics->descent) / 2;
1600 break;
1601 case DWRITE_BASELINE_HANGING:
1602 /* FIXME: this one isn't accurate, but close. */
1603 *baseline_coord = vertical ? metrics->capHeight * 6 / 7 + metrics->descent : metrics->capHeight * 6 / 7;
1604 break;
1605 case DWRITE_BASELINE_IDEOGRAPHIC_BOTTOM:
1606 case DWRITE_BASELINE_MINIMUM:
1607 *baseline_coord = vertical ? 0 : metrics->descent;
1608 break;
1609 case DWRITE_BASELINE_IDEOGRAPHIC_TOP:
1610 case DWRITE_BASELINE_MAXIMUM:
1611 *baseline_coord = vertical ? metrics->ascent + metrics->descent : -metrics->ascent;
1612 break;
1613 default:
1618 return S_OK;
1621 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1622 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1624 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1625 return E_NOTIMPL;
1628 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1629 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1631 TRACE("%d, %d, %p.\n", angle, is_sideways, transform);
1633 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1636 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1637 DWRITE_SCRIPT_PROPERTIES *props)
1639 TRACE("%u, %p.\n", sa.script, props);
1641 if (sa.script > Script_LastId)
1642 return E_INVALIDARG;
1644 *props = dwritescripts_properties[sa.script].props;
1645 return S_OK;
1648 static inline BOOL is_char_from_simple_script(WCHAR c)
1650 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c) ||
1651 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1652 c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e))
1653 return FALSE;
1654 else {
1655 UINT16 script = get_char_script(c);
1656 return !dwritescripts_properties[script].is_complex;
1660 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1661 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1663 HRESULT hr = S_OK;
1664 int i;
1666 TRACE("%s:%u, %p, %p, %p, %p.\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1668 *is_simple = FALSE;
1669 *len_read = 0;
1671 if (!face)
1672 return E_INVALIDARG;
1674 if (len == 0) {
1675 *is_simple = TRUE;
1676 return S_OK;
1679 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1680 for (i = 1; i < len && text[i]; i++) {
1681 if (is_char_from_simple_script(text[i])) {
1682 if (!*is_simple)
1683 break;
1685 else
1686 *is_simple = FALSE;
1689 *len_read = i;
1691 /* fetch indices */
1692 if (*is_simple && indices) {
1693 UINT32 *codepoints = heap_calloc(*len_read, sizeof(*codepoints));
1694 if (!codepoints)
1695 return E_OUTOFMEMORY;
1697 for (i = 0; i < *len_read; i++)
1698 codepoints[i] = text[i];
1700 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1701 heap_free(codepoints);
1704 return hr;
1707 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1708 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1709 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1711 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1712 debugstr_wn(text, length), clustermap, prop, jo);
1713 return E_NOTIMPL;
1716 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1717 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1718 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1720 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1721 justifiedoffsets);
1722 return E_NOTIMPL;
1725 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1726 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1727 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1728 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1729 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1730 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1732 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,
1733 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1734 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1735 return E_NOTIMPL;
1738 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1739 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1741 static const DWRITE_MATRIX transforms[] = {
1742 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1743 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1744 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1745 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1748 TRACE("%d, %d, %.2f, %.2f, %p.\n", angle, is_sideways, originX, originY, m);
1750 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1751 memset(m, 0, sizeof(*m));
1752 return E_INVALIDARG;
1755 /* for sideways case simply rotate 90 degrees more */
1756 if (is_sideways) {
1757 switch (angle) {
1758 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1759 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1760 break;
1761 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1762 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1763 break;
1764 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1765 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1766 break;
1767 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1768 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1769 break;
1770 default:
1775 *m = transforms[angle];
1777 /* shift components represent transform necessary to get from original point to
1778 rotated one in new coordinate system */
1779 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1780 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1781 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1784 return S_OK;
1787 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1788 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1789 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1791 struct scriptshaping_context context = { 0 };
1792 const struct dwritescript_properties *props;
1793 struct dwrite_fontface *font_obj;
1795 TRACE("%p, %p, %u, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), max_tagcount,
1796 actual_tagcount, tags);
1798 if (sa.script > Script_LastId)
1799 return E_INVALIDARG;
1801 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1803 context.cache = fontface_get_shaping_cache(font_obj);
1804 context.language_tag = get_opentype_language(locale);
1805 props = &dwritescripts_properties[sa.script];
1807 return shape_get_typographic_features(&context, props->scripttags, max_tagcount, actual_tagcount, tags);
1810 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1811 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale, DWRITE_FONT_FEATURE_TAG feature,
1812 UINT32 glyph_count, const UINT16 *glyphs, UINT8 *feature_applies)
1814 struct scriptshaping_context context = { 0 };
1815 const struct dwritescript_properties *props;
1816 struct dwrite_fontface *font_obj;
1817 HRESULT hr;
1819 TRACE("%p, %p, %u, %s, %s, %u, %p, %p.\n", iface, fontface, sa.script, debugstr_w(locale), debugstr_tag(feature),
1820 glyph_count, glyphs, feature_applies);
1822 if (sa.script > Script_LastId)
1823 return E_INVALIDARG;
1825 font_obj = unsafe_impl_from_IDWriteFontFace(fontface);
1827 context.cache = fontface_get_shaping_cache(font_obj);
1828 context.language_tag = get_opentype_language(locale);
1829 if (!(context.glyph_infos = heap_calloc(glyph_count, sizeof(*context.glyph_infos))))
1830 return E_OUTOFMEMORY;
1832 props = &dwritescripts_properties[sa.script];
1834 hr = shape_check_typographic_feature(&context, props->scripttags, feature, glyph_count, glyphs, feature_applies);
1836 heap_free(context.glyph_infos);
1838 return hr;
1841 static const IDWriteTextAnalyzer2Vtbl textanalyzervtbl =
1843 dwritetextanalyzer_QueryInterface,
1844 dwritetextanalyzer_AddRef,
1845 dwritetextanalyzer_Release,
1846 dwritetextanalyzer_AnalyzeScript,
1847 dwritetextanalyzer_AnalyzeBidi,
1848 dwritetextanalyzer_AnalyzeNumberSubstitution,
1849 dwritetextanalyzer_AnalyzeLineBreakpoints,
1850 dwritetextanalyzer_GetGlyphs,
1851 dwritetextanalyzer_GetGlyphPlacements,
1852 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1853 dwritetextanalyzer1_ApplyCharacterSpacing,
1854 dwritetextanalyzer1_GetBaseline,
1855 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1856 dwritetextanalyzer1_GetGlyphOrientationTransform,
1857 dwritetextanalyzer1_GetScriptProperties,
1858 dwritetextanalyzer1_GetTextComplexity,
1859 dwritetextanalyzer1_GetJustificationOpportunities,
1860 dwritetextanalyzer1_JustifyGlyphAdvances,
1861 dwritetextanalyzer1_GetJustifiedGlyphs,
1862 dwritetextanalyzer2_GetGlyphOrientationTransform,
1863 dwritetextanalyzer2_GetTypographicFeatures,
1864 dwritetextanalyzer2_CheckTypographicFeature
1867 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1869 IDWriteTextAnalyzer2 *get_text_analyzer(void)
1871 return &textanalyzer;
1874 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1876 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
1878 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1879 IsEqualIID(riid, &IID_IUnknown))
1881 *obj = iface;
1882 IDWriteNumberSubstitution_AddRef(iface);
1883 return S_OK;
1886 WARN("%s not implemented.\n", debugstr_guid(riid));
1888 *obj = NULL;
1890 return E_NOINTERFACE;
1893 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1895 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
1896 ULONG refcount = InterlockedIncrement(&object->refcount);
1898 TRACE("%p, refcount %d.\n", iface, refcount);
1900 return refcount;
1903 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1905 struct dwrite_numbersubstitution *object = impl_from_IDWriteNumberSubstitution(iface);
1906 ULONG refcount = InterlockedDecrement(&object->refcount);
1908 TRACE("%p, refcount %d.\n", iface, refcount);
1910 if (!refcount)
1912 heap_free(object->locale);
1913 heap_free(object);
1916 return refcount;
1919 static const IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl =
1921 dwritenumbersubstitution_QueryInterface,
1922 dwritenumbersubstitution_AddRef,
1923 dwritenumbersubstitution_Release
1926 struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
1928 if (!iface || iface->lpVtbl != &numbersubstitutionvtbl)
1929 return NULL;
1930 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
1933 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1934 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1936 struct dwrite_numbersubstitution *substitution;
1938 *ret = NULL;
1940 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1941 return E_INVALIDARG;
1943 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1944 return E_INVALIDARG;
1946 substitution = heap_alloc(sizeof(*substitution));
1947 if (!substitution)
1948 return E_OUTOFMEMORY;
1950 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1951 substitution->refcount = 1;
1952 substitution->ignore_user_override = ignore_user_override;
1953 substitution->method = method;
1954 substitution->locale = heap_strdupW(locale);
1955 if (locale && !substitution->locale) {
1956 heap_free(substitution);
1957 return E_OUTOFMEMORY;
1960 *ret = &substitution->IDWriteNumberSubstitution_iface;
1961 return S_OK;
1964 /* IDWriteFontFallback */
1965 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback1 *iface, REFIID riid, void **obj)
1967 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
1969 if (IsEqualIID(riid, &IID_IDWriteFontFallback1) ||
1970 IsEqualIID(riid, &IID_IDWriteFontFallback) ||
1971 IsEqualIID(riid, &IID_IUnknown))
1973 *obj = iface;
1974 IDWriteFontFallback1_AddRef(iface);
1975 return S_OK;
1978 WARN("%s not implemented.\n", debugstr_guid(riid));
1980 *obj = NULL;
1981 return E_NOINTERFACE;
1984 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback1 *iface)
1986 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
1988 TRACE("%p.\n", iface);
1990 return IDWriteFactory7_AddRef(fallback->factory);
1993 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback1 *iface)
1995 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
1997 TRACE("%p.\n", fallback);
1999 return IDWriteFactory7_Release(fallback->factory);
2002 static int __cdecl compare_mapping_range(const void *a, const void *b)
2004 UINT32 ch = *(UINT32 *)a;
2005 DWRITE_UNICODE_RANGE *range = (DWRITE_UNICODE_RANGE *)b;
2007 if (ch > range->last)
2008 return 1;
2009 else if (ch < range->first)
2010 return -1;
2011 else
2012 return 0;
2015 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
2017 UINT32 i;
2019 for (i = 0; i < fallback->mappings_count; i++) {
2020 struct fallback_mapping *mapping = &fallback->mappings[i];
2022 if (bsearch(&ch, mapping->ranges, mapping->ranges_count, sizeof(*mapping->ranges),
2023 compare_mapping_range) != NULL)
2024 return mapping;
2027 return NULL;
2030 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
2031 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
2033 IDWriteFontFamily *family;
2034 BOOL exists = FALSE;
2035 HRESULT hr;
2036 UINT32 i;
2038 *font = NULL;
2040 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
2041 if (FAILED(hr))
2042 return hr;
2044 if (!exists)
2045 return E_FAIL;
2047 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
2048 if (FAILED(hr))
2049 return hr;
2051 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
2052 IDWriteFontFamily_Release(family);
2053 return hr;
2056 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
2058 HRESULT hr = S_OK;
2059 UINT32 i;
2061 for (i = 0; i < length; i++) {
2062 UINT16 script = get_char_script(text[i]);
2063 BOOL exists;
2065 if (script == Script_Unknown || script == Script_Common) {
2066 ++*mapped_length;
2067 continue;
2070 /* stop on first unsupported character */
2071 exists = FALSE;
2072 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
2073 if (hr == S_OK && exists)
2074 ++*mapped_length;
2075 else
2076 break;
2079 return hr;
2082 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
2083 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2084 IDWriteFont **mapped_font)
2086 const struct fallback_mapping *mapping;
2087 HRESULT hr;
2088 UINT32 i;
2090 *mapped_font = NULL;
2092 mapping = find_fallback_mapping(fallback, text[0]);
2093 if (!mapping) {
2094 WARN("No mapping range for %#x.\n", text[0]);
2095 return E_FAIL;
2098 /* Now let's see what fallback can handle. Pick first font that could be created. */
2099 for (i = 0; i < mapping->families_count; i++) {
2100 hr = create_matching_font((IDWriteFontCollection *)fallback->systemcollection, mapping->families[i],
2101 weight, style, stretch, mapped_font);
2102 if (hr == S_OK) {
2103 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping->families[i]));
2104 break;
2108 if (!*mapped_font) {
2109 WARN("Failed to create fallback font.\n");
2110 return E_FAIL;
2113 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
2114 if (FAILED(hr))
2115 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping->families[i]), hr);
2117 if (!*mapped_length) {
2118 IDWriteFont_Release(*mapped_font);
2119 *mapped_font = NULL;
2122 return *mapped_length ? S_OK : E_FAIL;
2125 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2126 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2127 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2128 IDWriteFont **ret_font, FLOAT *scale)
2130 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2131 WCHAR *buff = NULL;
2132 const WCHAR *text;
2133 HRESULT hr;
2135 TRACE("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2136 debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2138 *mapped_length = 0;
2139 *scale = 1.0f;
2140 *ret_font = NULL;
2142 if (!source)
2143 return E_INVALIDARG;
2145 if (length == 0)
2146 return S_OK;
2148 if (!basecollection)
2149 basecollection = (IDWriteFontCollection*)fallback->systemcollection;
2151 hr = get_text_source_ptr(source, position, length, &text, &buff);
2152 if (FAILED(hr))
2153 goto done;
2155 if (basefamily && *basefamily) {
2156 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
2157 if (FAILED(hr))
2158 goto done;
2160 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
2161 if (FAILED(hr))
2162 goto done;
2165 if (!*mapped_length) {
2166 IDWriteFont *mapped_font;
2168 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
2169 if (FAILED(hr)) {
2170 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2171 if (*ret_font) {
2172 *mapped_length = length;
2173 hr = S_OK;
2176 else {
2177 if (*ret_font)
2178 IDWriteFont_Release(*ret_font);
2179 *ret_font = mapped_font;
2183 done:
2184 heap_free(buff);
2185 return hr;
2188 static HRESULT WINAPI fontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2189 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2190 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 values_count, UINT32 *mapped_length, FLOAT *scale,
2191 IDWriteFontFace5 **ret_fontface)
2193 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2194 debugstr_w(basefamily), axis_values, values_count, mapped_length, scale, ret_fontface);
2196 return E_NOTIMPL;
2199 static const IDWriteFontFallback1Vtbl fontfallbackvtbl =
2201 fontfallback_QueryInterface,
2202 fontfallback_AddRef,
2203 fontfallback_Release,
2204 fontfallback_MapCharacters,
2205 fontfallback1_MapCharacters,
2208 HRESULT create_system_fontfallback(IDWriteFactory7 *factory, IDWriteFontFallback1 **ret)
2210 struct dwrite_fontfallback *fallback;
2212 *ret = NULL;
2214 fallback = heap_alloc(sizeof(*fallback));
2215 if (!fallback)
2216 return E_OUTOFMEMORY;
2218 fallback->IDWriteFontFallback1_iface.lpVtbl = &fontfallbackvtbl;
2219 fallback->factory = factory;
2220 fallback->mappings = (struct fallback_mapping *)fontfallback_neutral_data;
2221 fallback->mappings_count = ARRAY_SIZE(fontfallback_neutral_data);
2222 IDWriteFactory5_GetSystemFontCollection((IDWriteFactory5 *)fallback->factory, FALSE, &fallback->systemcollection, FALSE);
2224 *ret = &fallback->IDWriteFontFallback1_iface;
2226 return S_OK;
2229 void release_system_fontfallback(IDWriteFontFallback1 *iface)
2231 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2232 IDWriteFontCollection1_Release(fallback->systemcollection);
2233 heap_free(fallback);
2236 static ULONG WINAPI customfontfallback_AddRef(IDWriteFontFallback1 *iface)
2238 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2239 ULONG refcount = InterlockedIncrement(&fallback->refcount);
2241 TRACE("%p, refcount %u.\n", iface, refcount);
2243 return refcount;
2246 static ULONG WINAPI customfontfallback_Release(IDWriteFontFallback1 *iface)
2248 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback1(iface);
2249 ULONG refcount = InterlockedDecrement(&fallback->refcount);
2251 TRACE("%p, refcount %u.\n", iface, refcount);
2253 if (!refcount)
2255 IDWriteFactory7_Release(fallback->factory);
2256 heap_free(fallback);
2259 return refcount;
2262 static HRESULT WINAPI customfontfallback_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2263 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2264 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2265 IDWriteFont **ret_font, FLOAT *scale)
2267 FIXME("%p, %p, %u, %u, %p, %s, %u, %u, %u, %p, %p, %p.\n", iface, source, position, length,
2268 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2270 return E_NOTIMPL;
2273 static HRESULT WINAPI customfontfallback1_MapCharacters(IDWriteFontFallback1 *iface, IDWriteTextAnalysisSource *source,
2274 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2275 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 values_count, UINT32 *mapped_length, FLOAT *scale,
2276 IDWriteFontFace5 **ret_fontface)
2278 FIXME("%p, %p, %u, %u, %p, %s, %p, %u, %p, %p, %p.\n", iface, source, position, length, basecollection,
2279 debugstr_w(basefamily), axis_values, values_count, mapped_length, scale, ret_fontface);
2281 return E_NOTIMPL;
2284 static const IDWriteFontFallback1Vtbl customfontfallbackvtbl =
2286 fontfallback_QueryInterface,
2287 customfontfallback_AddRef,
2288 customfontfallback_Release,
2289 customfontfallback_MapCharacters,
2290 customfontfallback1_MapCharacters,
2293 static HRESULT WINAPI fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder *iface, REFIID riid, void **obj)
2295 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2297 if (IsEqualIID(riid, &IID_IDWriteFontFallbackBuilder) || IsEqualIID(riid, &IID_IUnknown)) {
2298 *obj = iface;
2299 IDWriteFontFallbackBuilder_AddRef(iface);
2300 return S_OK;
2303 WARN("%s not implemented.\n", debugstr_guid(riid));
2305 *obj = NULL;
2306 return E_NOINTERFACE;
2309 static ULONG WINAPI fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder *iface)
2311 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2312 ULONG refcount = InterlockedIncrement(&fallbackbuilder->refcount);
2314 TRACE("%p, refcount %d.\n", iface, refcount);
2316 return refcount;
2319 static ULONG WINAPI fontfallbackbuilder_Release(IDWriteFontFallbackBuilder *iface)
2321 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2322 ULONG refcount = InterlockedDecrement(&fallbackbuilder->refcount);
2323 size_t i;
2325 TRACE("%p, refcount %d.\n", iface, refcount);
2327 if (!refcount)
2329 for (i = 0; i < fallbackbuilder->count; ++i)
2331 struct fallback_mapping *mapping = &fallbackbuilder->mappings[i];
2332 UINT32 j;
2334 for (j = 0; j < mapping->families_count; j++)
2335 heap_free(mapping->families[j]);
2336 heap_free(mapping->families);
2338 if (mapping->collection)
2339 IDWriteFontCollection_Release(mapping->collection);
2340 heap_free(mapping->ranges);
2341 heap_free(mapping->locale);
2344 IDWriteFactory7_Release(fallbackbuilder->factory);
2345 heap_free(fallbackbuilder->mappings);
2346 heap_free(fallbackbuilder);
2349 return refcount;
2352 static HRESULT WINAPI fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder *iface,
2353 const DWRITE_UNICODE_RANGE *ranges, UINT32 ranges_count, WCHAR const **target_families, UINT32 families_count,
2354 IDWriteFontCollection *collection, WCHAR const *locale, WCHAR const *base_family, FLOAT scale)
2356 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2357 struct fallback_mapping *mapping;
2358 UINT32 i;
2360 TRACE("%p, %p, %u, %p, %u, %p, %s, %s, %f.\n", iface, ranges, ranges_count, target_families, families_count,
2361 collection, debugstr_w(locale), debugstr_w(base_family), scale);
2363 if (!ranges || ranges_count == 0 || !target_families || families_count == 0 || scale < 0.0f)
2364 return E_INVALIDARG;
2366 if (base_family)
2367 FIXME("base family ignored.\n");
2369 if (!dwrite_array_reserve((void **)&fallbackbuilder->mappings, &fallbackbuilder->size, fallbackbuilder->count + 1,
2370 sizeof(*fallbackbuilder->mappings)))
2372 return E_OUTOFMEMORY;
2375 mapping = &fallbackbuilder->mappings[fallbackbuilder->count++];
2377 mapping->ranges = heap_calloc(ranges_count, sizeof(*mapping->ranges));
2378 memcpy(mapping->ranges, ranges, sizeof(*mapping->ranges) * ranges_count);
2379 mapping->ranges_count = ranges_count;
2380 mapping->families = heap_calloc(families_count, sizeof(*mapping->families));
2381 mapping->families_count = families_count;
2382 for (i = 0; i < families_count; i++)
2383 mapping->families[i] = heap_strdupW(target_families[i]);
2384 mapping->collection = collection;
2385 if (mapping->collection)
2386 IDWriteFontCollection_AddRef(mapping->collection);
2387 mapping->locale = heap_strdupW(locale);
2388 mapping->scale = scale;
2390 return S_OK;
2393 static HRESULT WINAPI fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder *iface, IDWriteFontFallback *fallback)
2395 FIXME("%p, %p stub.\n", iface, fallback);
2397 return E_NOTIMPL;
2400 static HRESULT WINAPI fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder *iface,
2401 IDWriteFontFallback **ret)
2403 struct dwrite_fontfallback_builder *fallbackbuilder = impl_from_IDWriteFontFallbackBuilder(iface);
2404 struct dwrite_fontfallback *fallback;
2406 TRACE("%p, %p.\n", iface, ret);
2408 *ret = NULL;
2410 fallback = heap_alloc(sizeof(*fallback));
2411 if (!fallback)
2412 return E_OUTOFMEMORY;
2414 fallback->IDWriteFontFallback1_iface.lpVtbl = &customfontfallbackvtbl;
2415 fallback->refcount = 1;
2416 fallback->factory = fallbackbuilder->factory;
2417 IDWriteFactory7_AddRef(fallback->factory);
2419 *ret = (IDWriteFontFallback *)&fallback->IDWriteFontFallback1_iface;
2420 return S_OK;
2423 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl =
2425 fontfallbackbuilder_QueryInterface,
2426 fontfallbackbuilder_AddRef,
2427 fontfallbackbuilder_Release,
2428 fontfallbackbuilder_AddMapping,
2429 fontfallbackbuilder_AddMappings,
2430 fontfallbackbuilder_CreateFontFallback,
2433 HRESULT create_fontfallback_builder(IDWriteFactory7 *factory, IDWriteFontFallbackBuilder **ret)
2435 struct dwrite_fontfallback_builder *builder;
2437 *ret = NULL;
2439 builder = heap_alloc_zero(sizeof(*builder));
2440 if (!builder)
2441 return E_OUTOFMEMORY;
2443 builder->IDWriteFontFallbackBuilder_iface.lpVtbl = &fontfallbackbuildervtbl;
2444 builder->refcount = 1;
2445 builder->factory = factory;
2446 IDWriteFactory7_AddRef(builder->factory);
2448 *ret = &builder->IDWriteFontFallbackBuilder_iface;
2449 return S_OK;