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
26 #include "dwrite_private.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
{
38 DWRITE_SCRIPT_PROPERTIES props
;
39 UINT32 scripttags
[3]; /* Maximum 2 script tags, 0-terminated. */
41 const struct scriptshaping_ops
*ops
;
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') }, FALSE
, &latn_shaping_ops
},
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') } },
193 const char *debugstr_sa_script(UINT16 script
)
195 return script
< Script_LastId
? debugstr_tag(dwritescripts_properties
[script
].props
.isoScriptCode
) : "undefined";
198 /* system font falback configuration */
199 static const WCHAR meiryoW
[] = {'M','e','i','r','y','o',0};
201 static const WCHAR
*cjk_families
[] = { meiryoW
};
203 static const DWRITE_UNICODE_RANGE cjk_ranges
[] =
205 { 0x3000, 0x30ff }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
206 { 0x31f0, 0x31ff }, /* Katakana Phonetic Extensions */
207 { 0x4e00, 0x9fff }, /* CJK Unified Ideographs */
210 struct fallback_mapping
{
211 DWRITE_UNICODE_RANGE
*ranges
;
214 UINT32 families_count
;
215 IDWriteFontCollection
*collection
;
220 static const struct fallback_mapping fontfallback_neutral_data
[] = {
221 #define MAPPING_RANGE(ranges, families) \
222 { (DWRITE_UNICODE_RANGE *)ranges, ARRAY_SIZE(ranges), \
223 (WCHAR **)families, ARRAY_SIZE(families) }
225 MAPPING_RANGE(cjk_ranges
, cjk_families
),
230 struct dwrite_fontfallback
{
231 IDWriteFontFallback IDWriteFontFallback_iface
;
233 IDWriteFactory5
*factory
;
234 IDWriteFontCollection1
*systemcollection
;
235 struct fallback_mapping
*mappings
;
236 UINT32 mappings_count
;
239 struct dwrite_fontfallback_builder
{
240 IDWriteFontFallbackBuilder IDWriteFontFallbackBuilder_iface
;
242 IDWriteFactory5
*factory
;
243 struct fallback_mapping
*mappings
;
244 UINT32 mappings_count
;
245 UINT32 mappings_capacity
;
248 struct dwrite_numbersubstitution
{
249 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
252 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
254 BOOL ignore_user_override
;
257 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
259 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
262 static struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
);
264 static inline struct dwrite_fontfallback
*impl_from_IDWriteFontFallback(IDWriteFontFallback
*iface
)
266 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback
, IDWriteFontFallback_iface
);
269 static inline struct dwrite_fontfallback_builder
*impl_from_IDWriteFontFallbackBuilder(IDWriteFontFallbackBuilder
*iface
)
271 return CONTAINING_RECORD(iface
, struct dwrite_fontfallback_builder
, IDWriteFontFallbackBuilder_iface
);
274 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
276 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
277 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
278 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
284 static inline UINT16
get_char_script(WCHAR c
)
286 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
287 return script
== Script_Inherited
? Script_Unknown
: script
;
290 static DWRITE_SCRIPT_ANALYSIS
get_char_sa(WCHAR c
)
292 DWRITE_SCRIPT_ANALYSIS sa
;
294 sa
.script
= get_char_script(c
);
295 sa
.shapes
= iscntrlW(c
) || c
== 0x2028 /* LINE SEPARATOR */ || c
== 0x2029 /* PARAGRAPH SEPARATOR */ ?
296 DWRITE_SCRIPT_SHAPES_NO_VISUAL
: DWRITE_SCRIPT_SHAPES_DEFAULT
;
300 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
*sink
)
302 DWRITE_SCRIPT_ANALYSIS sa
;
303 UINT32 pos
, i
, seq_length
;
308 sa
= get_char_sa(*text
);
313 for (i
= 1; i
< length
; i
++)
315 DWRITE_SCRIPT_ANALYSIS cur_sa
= get_char_sa(text
[i
]);
317 /* Unknown type is ignored when preceded or followed by another script */
320 sa
.script
= cur_sa
.script
;
323 if (cur_sa
.script
== Script_Unknown
)
324 cur_sa
.script
= sa
.script
;
325 else if ((cur_sa
.script
!= Script_Common
) && sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
)
326 sa
.script
= cur_sa
.script
;
329 if ((cur_sa
.script
== Script_Common
&& cur_sa
.shapes
== DWRITE_SCRIPT_SHAPES_DEFAULT
) || cur_sa
.script
== Script_Unknown
)
330 cur_sa
.script
= sa
.script
;
333 /* this is a length of a sequence to be reported next */
334 if (sa
.script
== cur_sa
.script
&& sa
.shapes
== cur_sa
.shapes
)
339 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
340 if (FAILED(hr
)) return hr
;
347 /* one char length case or normal completion call */
348 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, seq_length
, &sa
);
351 struct linebreaking_state
{
352 DWRITE_LINE_BREAKPOINT
*breakpoints
;
356 enum BreakConditionLocation
{
357 BreakConditionBefore
,
361 enum linebreaking_classes
{
407 static BOOL
has_strong_condition(DWRITE_BREAK_CONDITION old_condition
, DWRITE_BREAK_CONDITION new_condition
)
409 if (old_condition
== DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
|| old_condition
== DWRITE_BREAK_CONDITION_MUST_BREAK
)
412 if (old_condition
== DWRITE_BREAK_CONDITION_CAN_BREAK
&& new_condition
!= DWRITE_BREAK_CONDITION_MUST_BREAK
)
418 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
419 set to "can break" and could only be changed once. */
420 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
421 struct linebreaking_state
*state
)
423 if (location
== BreakConditionBefore
) {
424 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionBefore
, condition
))
426 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
428 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
431 if (has_strong_condition(state
->breakpoints
[pos
].breakConditionAfter
, condition
))
433 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
434 if (pos
+ 1 < state
->count
)
435 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
439 BOOL
lb_is_newline_char(WCHAR ch
)
441 short c
= get_table_entry(wine_linebreak_table
, ch
);
442 return c
== b_LF
|| c
== b_NL
|| c
== b_CR
|| c
== b_BK
;
445 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
447 struct linebreaking_state state
;
451 break_class
= heap_calloc(count
, sizeof(*break_class
));
453 return E_OUTOFMEMORY
;
455 state
.breakpoints
= breakpoints
;
458 for (i
= 0; i
< count
; i
++)
460 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
462 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_NEUTRAL
;
463 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_NEUTRAL
;
464 breakpoints
[i
].isWhitespace
= !!isspaceW(text
[i
]);
465 breakpoints
[i
].isSoftHyphen
= text
[i
] == 0x00ad /* Unicode Soft Hyphen */;
466 breakpoints
[i
].padding
= 0;
468 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
469 switch (break_class
[i
])
475 break_class
[i
] = b_AL
;
478 break_class
[i
] = b_NS
;
483 /* LB2 - never break at the start */
484 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
485 /* LB3 - always break at the end. */
486 set_break_condition(count
- 1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
488 /* LB4 - LB6 - mandatory breaks. */
489 for (i
= 0; i
< count
; i
++)
491 switch (break_class
[i
])
495 /* LB5 - don't break CR x LF */
496 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
498 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
499 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
505 /* LB4 - LB5 - always break after hard breaks */
506 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
507 /* LB6 - do not break before hard breaks */
508 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
513 /* LB7 - LB8 - explicit breaks and non-breaks */
514 for (i
= 0; i
< count
; i
++)
516 switch (break_class
[i
])
518 /* LB7 - do not break before spaces or zero-width space */
520 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
523 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
525 /* LB8 - break before character after zero-width space, skip spaces in-between */
527 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
529 if (j
< count
-1 && break_class
[j
+1] != b_ZW
)
530 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
532 /* LB8a - do not break after ZWJ */
534 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
539 /* LB9 - LB10 - combining marks */
540 for (i
= 0; i
< count
; i
++)
542 if (break_class
[i
] == b_CM
|| break_class
[i
] == b_ZWJ
)
546 switch (break_class
[i
-1])
554 break_class
[i
] = b_AL
;
558 break_class
[i
] = break_class
[i
-1];
559 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
563 else break_class
[i
] = b_AL
;
567 for (i
= 0; i
< count
; i
++)
569 switch (break_class
[i
])
571 /* LB11 - don't break before and after word joiner */
573 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
574 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
576 /* LB12 - don't break after glue */
578 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
582 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
583 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
592 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
594 /* LB14 - do not break after OP, even after spaces */
597 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
599 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
601 /* LB15 - do not break within QU-OP, even with intervening spaces */
604 while (j
< count
-1 && break_class
[j
+1] == b_SP
)
606 if (j
< count
- 1 && break_class
[j
+1] == b_OP
)
607 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
612 while(j
> 0 && break_class
[j
] == b_SP
)
614 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
615 for (j
++; j
<= i
; j
++)
616 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
618 /* LB17 - do not break within B2, even with intervening spaces */
621 while (j
< count
&& break_class
[j
+1] == b_SP
)
623 if (j
< count
- 1 && break_class
[j
+1] == b_B2
)
624 set_break_condition(j
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
629 for (i
= 0; i
< count
; i
++)
631 switch(break_class
[i
])
633 /* LB18 - break is allowed after space */
635 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
637 /* LB19 - don't break before or after quotation mark */
639 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
640 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
644 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
645 if (i
< count
- 1 && break_class
[i
+1] != b_QU
)
646 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
652 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
655 if (i
< count
- 1 && break_class
[i
+1] != b_CB
)
656 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
662 switch (break_class
[i
+1])
666 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
669 if (i
> 0 && break_class
[i
-1] == b_SY
)
670 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
676 switch (break_class
[i
-1])
686 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
694 /* LB23 - do not break between digits and letters */
695 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
696 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
697 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
698 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
699 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
701 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
702 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
703 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EB
) ||
704 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_EM
) ||
705 (break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
706 (break_class
[i
] == b_EM
&& break_class
[i
+1] == b_PO
) ||
707 (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_PO
))
708 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
710 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
711 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
712 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
713 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
714 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
) ||
715 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PR
) ||
716 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PR
) ||
717 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_PO
) ||
718 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_PO
))
719 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
722 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
723 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
724 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
725 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
726 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
727 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
728 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
729 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
730 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
731 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
732 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
733 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
734 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
735 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
736 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
739 if (break_class
[i
] == b_JL
)
741 switch (break_class
[i
+1])
747 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
750 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
751 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
752 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
753 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
754 break_class
[i
+1] == b_JT
)
755 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
758 switch (break_class
[i
])
765 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
766 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
768 if (break_class
[i
] == b_PR
)
770 switch (break_class
[i
+1])
777 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
782 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
783 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
784 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
785 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
786 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
789 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
790 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
791 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
794 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
795 break_class
[i
+1] == b_OP
)
796 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
797 if (break_class
[i
] == b_CP
&&
798 (break_class
[i
+1] == b_AL
|| break_class
[i
+1] == b_HL
|| break_class
[i
+1] == b_NU
))
799 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
801 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
802 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
) {
806 while (j
> 0 && break_class
[--j
] == b_RI
)
810 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
813 /* LB30b - do not break between an emoji base and an emoji modifier */
814 if (break_class
[i
] == b_EB
&& break_class
[i
+1] == b_EM
)
815 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
819 /* LB31 - allow breaks everywhere else. */
820 for (i
= 0; i
< count
; i
++)
822 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
823 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
826 heap_free(break_class
);
830 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
832 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
834 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
835 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
836 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
837 IsEqualIID(riid
, &IID_IUnknown
))
843 WARN("%s not implemented.\n", debugstr_guid(riid
));
846 return E_NOINTERFACE
;
849 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
854 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
859 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
860 data after a first request. */
861 static HRESULT
get_text_source_ptr(IDWriteTextAnalysisSource
*source
, UINT32 position
, UINT32 length
, const WCHAR
**text
, WCHAR
**buff
)
869 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, text
, &len
);
870 if (FAILED(hr
)) return hr
;
875 *buff
= heap_alloc(length
*sizeof(WCHAR
));
877 return E_OUTOFMEMORY
;
878 memcpy(*buff
, *text
, len
*sizeof(WCHAR
));
881 while (read
< length
&& *text
) {
884 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, text
, &len
);
889 memcpy(*buff
+ read
, *text
, min(len
, length
-read
)*sizeof(WCHAR
));
899 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
900 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
906 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
911 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
915 hr
= analyze_script(text
, position
, length
, sink
);
921 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
922 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
924 UINT8
*levels
= NULL
, *explicit = NULL
;
925 UINT8 baselevel
, level
, explicit_level
;
926 UINT32 pos
, i
, seq_length
;
931 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
936 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
940 levels
= heap_calloc(length
, sizeof(*levels
));
941 explicit = heap_calloc(length
, sizeof(*explicit));
943 if (!levels
|| !explicit) {
948 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
949 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
954 explicit_level
= explicit[0];
958 for (i
= 1; i
< length
; i
++) {
959 if (levels
[i
] == level
&& explicit[i
] == explicit_level
)
962 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
969 explicit_level
= explicit[i
];
972 /* one char length case or normal completion call */
973 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, seq_length
, explicit_level
, level
);
983 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
984 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
989 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
993 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
994 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
996 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
1002 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
1007 /* get some, check for length */
1010 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
1011 if (FAILED(hr
)) return hr
;
1016 buff
= heap_calloc(length
, sizeof(*buff
));
1018 return E_OUTOFMEMORY
;
1019 memcpy(buff
, text
, len
*sizeof(WCHAR
));
1022 while (read
< length
&& text
) {
1025 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
1028 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
1035 breakpoints
= heap_calloc(length
, sizeof(*breakpoints
));
1041 hr
= analyze_linebreaks(text
, length
, breakpoints
);
1045 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
1048 heap_free(breakpoints
);
1054 static UINT32
get_opentype_language(const WCHAR
*locale
)
1056 UINT32 language
= DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
1060 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, ARRAY_SIZE(tag
)))
1061 language
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
1067 static DWRITE_NUMBER_SUBSTITUTION_METHOD
get_number_substitutes(IDWriteNumberSubstitution
*substitution
, WCHAR
*digits
)
1069 struct dwrite_numbersubstitution
*numbersubst
= unsafe_impl_from_IDWriteNumberSubstitution(substitution
);
1070 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1075 return DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1077 lctype
= numbersubst
->ignore_user_override
? LOCALE_NOUSEROVERRIDE
: 0;
1079 if (numbersubst
->method
== DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE
) {
1082 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1083 if (GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_IDIGITSUBSTITUTION
| LOCALE_RETURN_NUMBER
, (WCHAR
*)&value
, 2)) {
1087 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
;
1090 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
;
1095 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value
, debugstr_w(numbersubst
->locale
));
1099 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst
->locale
));
1102 method
= numbersubst
->method
;
1107 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
:
1108 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1110 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1111 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
:
1112 if (GetLocaleInfoEx(numbersubst
->locale
, LOCALE_SISO639LANGNAME
, isolang
, ARRAY_SIZE(isolang
))) {
1113 static const WCHAR arW
[] = {'a','r',0};
1114 static const WCHAR arabicW
[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1116 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1117 if (!strcmpW(arW
, isolang
)) {
1118 strcpyW(digits
, arabicW
);
1122 GetLocaleInfoEx(numbersubst
->locale
, lctype
| LOCALE_SNATIVEDIGITS
, digits
, NATIVE_DIGITS_LEN
);
1128 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !*digits
) {
1129 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst
->locale
), method
);
1130 method
= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
;
1136 static void analyzer_dump_user_features(DWRITE_TYPOGRAPHIC_FEATURES
const **features
,
1137 UINT32
const *feature_range_lengths
, UINT32 feature_ranges
)
1141 if (!TRACE_ON(dwrite
) || !features
)
1144 for (i
= 0, start
= 0; i
< feature_ranges
; start
+= feature_range_lengths
[i
++]) {
1145 TRACE("feature range [%u,%u)\n", start
, start
+ feature_range_lengths
[i
]);
1146 for (j
= 0; j
< features
[i
]->featureCount
; j
++)
1147 TRACE("feature %s, parameter %u\n", debugstr_tag(features
[i
]->features
[j
].nameTag
),
1148 features
[i
]->features
[j
].parameter
);
1152 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
1153 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
1154 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
1155 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1156 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
1157 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
1158 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
1160 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
1161 struct scriptshaping_context context
;
1162 struct dwrite_fontface
*font_obj
;
1163 WCHAR digits
[NATIVE_DIGITS_LEN
];
1164 BOOL update_cluster
;
1169 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
1170 length
, fontface
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), substitution
,
1171 features
, feature_range_lengths
, feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
,
1172 glyph_props
, actual_glyph_count
);
1174 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1176 if (max_glyph_count
< length
)
1177 return E_NOT_SUFFICIENT_BUFFER
;
1179 string
= heap_calloc(length
, sizeof(*string
));
1181 return E_OUTOFMEMORY
;
1183 method
= get_number_substitutes(substitution
, digits
);
1185 for (i
= 0; i
< length
; i
++) {
1186 /* FIXME: set to better values */
1187 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
1188 glyph_props
[i
].isClusterStart
= 1;
1189 glyph_props
[i
].isDiacritic
= 0;
1190 glyph_props
[i
].isZeroWidthSpace
= 0;
1191 glyph_props
[i
].reserved
= 0;
1193 /* FIXME: have the shaping engine set this */
1194 text_props
[i
].isShapedAlone
= 0;
1195 text_props
[i
].reserved
= 0;
1199 string
[i
] = text
[i
];
1202 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
:
1207 if (string
[i
] >= '0' && string
[i
] <= '9')
1208 string
[i
] = digits
[string
[i
] - '0'];
1210 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
:
1215 for (; i
< max_glyph_count
; i
++) {
1216 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
1217 glyph_props
[i
].isClusterStart
= 0;
1218 glyph_props
[i
].isDiacritic
= 0;
1219 glyph_props
[i
].isZeroWidthSpace
= 0;
1220 glyph_props
[i
].reserved
= 0;
1223 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
1226 if (!update_cluster
) {
1227 codepoint
= decode_surrogate_pair(string
, i
, length
);
1229 codepoint
= is_rtl
? bidi_get_mirrored_char(string
[i
]) : string
[i
];
1231 update_cluster
= TRUE
;
1233 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
1242 update_cluster
= FALSE
;
1243 /* mark surrogate halves with same cluster */
1244 clustermap
[i
] = clustermap
[i
-1];
1245 /* update following clusters */
1246 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
1250 *actual_glyph_count
= g
;
1252 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1254 context
.cache
= fontface_get_shaping_cache(font_obj
);
1255 context
.text
= text
;
1256 context
.length
= length
;
1257 context
.is_rtl
= is_rtl
;
1258 context
.language_tag
= get_opentype_language(locale
);
1260 /* FIXME: apply default features */
1262 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
1270 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1271 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1272 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1273 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, BOOL is_sideways
, BOOL is_rtl
,
1274 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1275 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1277 const struct dwritescript_properties
*scriptprops
;
1278 struct dwrite_fontface
*font_obj
;
1279 unsigned int i
, script
;
1282 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
),
1283 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, is_sideways
,
1284 is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
), features
, feature_range_lengths
,
1285 feature_ranges
, advances
, offsets
);
1287 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1289 if (glyph_count
== 0)
1292 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1294 for (i
= 0; i
< glyph_count
; ++i
)
1296 if (glyph_props
[i
].isZeroWidthSpace
)
1299 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, DWRITE_MEASURING_MODE_NATURAL
, emSize
, 1.0f
,
1300 NULL
, glyphs
[i
], is_sideways
);
1301 offsets
[i
].advanceOffset
= 0.0f
;
1302 offsets
[i
].ascenderOffset
= 0.0f
;
1305 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1307 scriptprops
= &dwritescripts_properties
[script
];
1308 if (scriptprops
->ops
&& scriptprops
->ops
->gpos_features
)
1310 struct scriptshaping_context context
;
1312 context
.cache
= fontface_get_shaping_cache(font_obj
);
1313 context
.text
= text
;
1314 context
.length
= text_len
;
1315 context
.is_rtl
= is_rtl
;
1316 context
.u
.pos
.glyphs
= glyphs
;
1317 context
.u
.pos
.glyph_props
= glyph_props
;
1318 context
.glyph_count
= glyph_count
;
1319 context
.emsize
= emSize
;
1320 context
.measuring_mode
= DWRITE_MEASURING_MODE_NATURAL
;
1321 context
.advances
= advances
;
1322 context
.offsets
= offsets
;
1323 context
.language_tag
= get_opentype_language(locale
);
1325 hr
= shape_get_positions(&context
, scriptprops
->scripttags
, scriptprops
->ops
->gpos_features
);
1331 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
1332 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
1333 UINT32 text_len
, UINT16
const* glyphs
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
1334 UINT32 glyph_count
, IDWriteFontFace
*fontface
, float emSize
, float ppdip
,
1335 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
1336 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
1337 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, float *advances
, DWRITE_GLYPH_OFFSET
*offsets
)
1339 const struct dwritescript_properties
*scriptprops
;
1340 DWRITE_MEASURING_MODE measuring_mode
;
1341 struct dwrite_fontface
*font_obj
;
1342 unsigned int i
, script
;
1345 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
),
1346 clustermap
, props
, text_len
, glyphs
, glyph_props
, glyph_count
, fontface
, emSize
, ppdip
,
1347 transform
, use_gdi_natural
, is_sideways
, is_rtl
, debugstr_sa_script(analysis
->script
), debugstr_w(locale
),
1348 features
, feature_range_lengths
, feature_ranges
, advances
, offsets
);
1350 analyzer_dump_user_features(features
, feature_range_lengths
, feature_ranges
);
1352 if (glyph_count
== 0)
1355 font_obj
= unsafe_impl_from_IDWriteFontFace(fontface
);
1357 measuring_mode
= use_gdi_natural
? DWRITE_MEASURING_MODE_GDI_NATURAL
: DWRITE_MEASURING_MODE_GDI_CLASSIC
;
1359 for (i
= 0; i
< glyph_count
; ++i
)
1361 if (glyph_props
[i
].isZeroWidthSpace
)
1364 advances
[i
] = fontface_get_scaled_design_advance(font_obj
, measuring_mode
, emSize
, ppdip
,
1365 transform
, glyphs
[i
], is_sideways
);
1366 offsets
[i
].advanceOffset
= 0.0f
;
1367 offsets
[i
].ascenderOffset
= 0.0f
;
1370 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
1372 scriptprops
= &dwritescripts_properties
[script
];
1373 if (scriptprops
->ops
&& scriptprops
->ops
->gpos_features
)
1375 struct scriptshaping_context context
;
1377 context
.cache
= fontface_get_shaping_cache(font_obj
);
1378 context
.text
= text
;
1379 context
.length
= text_len
;
1380 context
.is_rtl
= is_rtl
;
1381 context
.u
.pos
.glyphs
= glyphs
;
1382 context
.u
.pos
.glyph_props
= glyph_props
;
1383 context
.glyph_count
= glyph_count
;
1384 context
.emsize
= emSize
* ppdip
;
1385 context
.measuring_mode
= measuring_mode
;
1386 context
.advances
= advances
;
1387 context
.offsets
= offsets
;
1388 context
.language_tag
= get_opentype_language(locale
);
1390 hr
= shape_get_positions(&context
, scriptprops
->scripttags
, scriptprops
->ops
->gpos_features
);
1396 static inline FLOAT
get_cluster_advance(const FLOAT
*advances
, UINT32 start
, UINT32 end
)
1398 FLOAT advance
= 0.0f
;
1399 for (; start
< end
; start
++)
1400 advance
+= advances
[start
];
1404 static HRESULT
apply_cluster_spacing(float leading_spacing
, float trailing_spacing
, float min_advance_width
,
1405 unsigned int start
, unsigned int end
, float const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1406 DWRITE_SHAPING_GLYPH_PROPERTIES
const *glyph_props
, float *modified_advances
,
1407 DWRITE_GLYPH_OFFSET
*modified_offsets
)
1409 BOOL reduced
= leading_spacing
< 0.0f
|| trailing_spacing
< 0.0f
;
1410 unsigned int first_spacing
, last_spacing
, i
;
1411 float advance
, origin
= 0.0f
, *deltas
;
1412 BOOL is_spacing_cluster
= FALSE
;
1414 if (modified_advances
!= advances
)
1415 memcpy(&modified_advances
[start
], &advances
[start
], (end
- start
+ 1) * sizeof(*advances
));
1416 if (modified_offsets
!= offsets
)
1417 memcpy(&modified_offsets
[start
], &offsets
[start
], (end
- start
+ 1) * sizeof(*offsets
));
1419 for (first_spacing
= start
; first_spacing
<= end
; ++first_spacing
)
1421 if ((is_spacing_cluster
= !glyph_props
[first_spacing
].isZeroWidthSpace
))
1425 /* Nothing to adjust if there is no spacing glyphs. */
1426 if (!is_spacing_cluster
)
1429 for (last_spacing
= end
; last_spacing
>= start
; --last_spacing
)
1431 if (!glyph_props
[last_spacing
].isZeroWidthSpace
)
1435 deltas
= heap_calloc(end
- start
+ 1, sizeof(*deltas
));
1437 return E_OUTOFMEMORY
;
1439 /* Cluster advance, note that properties are ignored. */
1440 origin
= offsets
[start
].advanceOffset
;
1441 for (i
= start
, advance
= 0.0f
; i
<= end
; ++i
)
1443 float cur
= advance
+ offsets
[i
].advanceOffset
;
1445 deltas
[i
- start
] = cur
- origin
;
1447 advance
+= advances
[i
];
1451 /* Negative spacing. */
1452 if (leading_spacing
< 0.0f
)
1454 advance
+= leading_spacing
;
1455 modified_advances
[first_spacing
] += leading_spacing
;
1456 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1459 if (trailing_spacing
< 0.0f
)
1461 advance
+= trailing_spacing
;
1462 modified_advances
[last_spacing
] += trailing_spacing
;
1465 /* Minimal advance. */
1466 advance
= min_advance_width
- advance
;
1467 if (advance
> 0.0f
) {
1468 /* Additional spacing is only applied to leading and trailing spacing glyphs. */
1469 float half
= advance
/ 2.0f
;
1473 modified_advances
[first_spacing
] += half
;
1474 modified_advances
[last_spacing
] += half
;
1475 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1477 else if (leading_spacing
< 0.0f
&& trailing_spacing
< 0.0f
)
1479 modified_advances
[first_spacing
] += half
;
1480 modified_advances
[last_spacing
] += half
;
1481 modified_offsets
[first_spacing
].advanceOffset
+= half
;
1483 else if (leading_spacing
< 0.0f
)
1485 modified_advances
[first_spacing
] += advance
;
1486 modified_offsets
[first_spacing
].advanceOffset
+= advance
;
1489 modified_advances
[last_spacing
] += advance
;
1492 /* Positive spacing. */
1493 if (leading_spacing
> 0.0f
)
1495 modified_advances
[first_spacing
] += leading_spacing
;
1496 modified_offsets
[first_spacing
].advanceOffset
+= leading_spacing
;
1499 if (trailing_spacing
> 0.0f
)
1500 modified_advances
[last_spacing
] += trailing_spacing
;
1502 /* Update offsets to preserve original relative positions within cluster. */
1503 for (i
= first_spacing
; i
> start
; --i
)
1505 unsigned int cur
= i
- 1;
1506 modified_offsets
[cur
].advanceOffset
= modified_advances
[cur
] + modified_offsets
[i
].advanceOffset
-
1510 for (i
= first_spacing
+ 1; i
<= end
; ++i
)
1512 modified_offsets
[i
].advanceOffset
= deltas
[i
- start
] + modified_offsets
[i
- 1].advanceOffset
-
1513 modified_advances
[i
- 1];
1521 static inline UINT32
get_cluster_length(UINT16
const *clustermap
, UINT32 start
, UINT32 text_len
)
1523 UINT16 g
= clustermap
[start
];
1526 while (start
< text_len
&& clustermap
[++start
] == g
)
1531 /* Applies spacing adjustments to clusters.
1533 Adjustments are applied in the following order:
1535 1. Negative adjustments
1537 Leading and trailing spacing could be negative, at this step
1538 only negative ones are actually applied. Leading spacing is only
1539 applied to leading glyph, trailing - to trailing glyph.
1541 2. Minimum advance width
1543 Advances could only be reduced at this point or unchanged. In any
1544 case it's checked if cluster advance width is less than minimum width.
1545 If it's the case advance width is incremented up to minimum value.
1547 Important part is the direction in which this increment is applied;
1548 it depends on direction from which total cluster advance was trimmed
1549 at step 1. So it could be incremented from leading, trailing, or both
1550 sides. When applied to both sides, each side gets half of difference
1551 that brings advance to minimum width.
1553 3. Positive adjustments
1555 After minimum width rule was applied, positive spacing is applied in the same
1556 way as negative one on step 1.
1558 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1559 keeps its position in coordinate system where initial advance width is counted
1564 It's known that isZeroWidthSpace property keeps initial advance from changing.
1567 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
1568 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
1569 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
1570 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1574 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing
, trailing_spacing
, min_advance_width
,
1575 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1577 if (min_advance_width
< 0.0f
) {
1578 if (modified_advances
!= advances
)
1579 memset(modified_advances
, 0, glyph_count
*sizeof(*modified_advances
));
1580 return E_INVALIDARG
;
1583 for (i
= 0; i
< len
;)
1585 unsigned int length
= get_cluster_length(clustermap
, i
, len
);
1586 unsigned int start
, end
;
1588 start
= clustermap
[i
];
1589 end
= i
+ length
< len
? clustermap
[i
+ length
] : glyph_count
;
1591 apply_cluster_spacing(leading_spacing
, trailing_spacing
, min_advance_width
, start
, end
- 1, advances
,
1592 offsets
, props
, modified_advances
, modified_offsets
);
1600 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1601 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1602 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1604 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1605 baseline_coord
, exists
);
1609 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1610 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1612 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1616 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1617 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1619 TRACE("(%d %d %p)\n", angle
, is_sideways
, transform
);
1620 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface
, angle
, is_sideways
, 0.0, 0.0, transform
);
1623 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1624 DWRITE_SCRIPT_PROPERTIES
*props
)
1626 TRACE("(%u %p)\n", sa
.script
, props
);
1628 if (sa
.script
> Script_LastId
)
1629 return E_INVALIDARG
;
1631 *props
= dwritescripts_properties
[sa
.script
].props
;
1635 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1637 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
) ||
1638 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1639 c
== 0x200e || c
== 0x200f || (c
>= 0x202a && c
<= 0x202e))
1642 UINT16 script
= get_char_script(c
);
1643 return !dwritescripts_properties
[script
].is_complex
;
1647 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1648 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1653 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1659 return E_INVALIDARG
;
1666 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1667 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1668 if (is_char_from_simple_script(text
[i
])) {
1679 if (*is_simple
&& indices
) {
1680 UINT32
*codepoints
= heap_calloc(*len_read
, sizeof(*codepoints
));
1682 return E_OUTOFMEMORY
;
1684 for (i
= 0; i
< *len_read
; i
++)
1685 codepoints
[i
] = text
[i
];
1687 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1688 heap_free(codepoints
);
1694 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1695 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1696 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1698 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1699 debugstr_wn(text
, length
), clustermap
, prop
, jo
);
1703 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1704 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1705 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1707 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1712 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1713 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1714 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1715 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1716 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1717 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1719 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
,
1720 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1721 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1725 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1726 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*m
)
1728 static const DWRITE_MATRIX transforms
[] = {
1729 { 1.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
},
1730 { 0.0f
, 1.0f
, -1.0f
, 0.0f
, 0.0f
, 0.0f
},
1731 { -1.0f
, 0.0f
, 0.0f
, -1.0f
, 0.0f
, 0.0f
},
1732 { 0.0f
, -1.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
}
1735 TRACE("(%d %d %.2f %.2f %p)\n", angle
, is_sideways
, originX
, originY
, m
);
1737 if ((UINT32
)angle
> DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
) {
1738 memset(m
, 0, sizeof(*m
));
1739 return E_INVALIDARG
;
1742 /* for sideways case simply rotate 90 degrees more */
1745 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1746 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1748 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1749 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1751 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1752 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1754 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1755 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1762 *m
= transforms
[angle
];
1764 /* shift components represent transform necessary to get from original point to
1765 rotated one in new coordinate system */
1766 if ((originX
!= 0.0f
|| originY
!= 0.0f
) && angle
!= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
) {
1767 m
->dx
= originX
- (m
->m11
* originX
+ m
->m21
* originY
);
1768 m
->dy
= originY
- (m
->m12
* originX
+ m
->m22
* originY
);
1774 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1775 IDWriteFontFace
*fontface
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
,
1776 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1778 const struct dwritescript_properties
*props
;
1779 const DWORD
*scripts
;
1783 TRACE("(%p %u %s %u %p %p)\n", fontface
, sa
.script
, debugstr_w(locale
), max_tagcount
, actual_tagcount
,
1786 if (sa
.script
> Script_LastId
)
1787 return E_INVALIDARG
;
1789 language
= get_opentype_language(locale
);
1790 props
= &dwritescripts_properties
[sa
.script
];
1791 *actual_tagcount
= 0;
1793 scripts
= props
->scripttags
;
1794 while (*scripts
&& !*actual_tagcount
)
1796 hr
= opentype_get_typographic_features(fontface
, *scripts
, language
, max_tagcount
, actual_tagcount
, tags
);
1803 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1804 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*locale
, DWRITE_FONT_FEATURE_TAG feature
,
1805 UINT32 glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
1807 FIXME("(%p %u %s %s %u %p %p): stub\n", face
, sa
.script
, debugstr_w(locale
), debugstr_tag(feature
), glyph_count
,
1808 glyphs
, feature_applies
);
1812 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1813 dwritetextanalyzer_QueryInterface
,
1814 dwritetextanalyzer_AddRef
,
1815 dwritetextanalyzer_Release
,
1816 dwritetextanalyzer_AnalyzeScript
,
1817 dwritetextanalyzer_AnalyzeBidi
,
1818 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1819 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1820 dwritetextanalyzer_GetGlyphs
,
1821 dwritetextanalyzer_GetGlyphPlacements
,
1822 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1823 dwritetextanalyzer1_ApplyCharacterSpacing
,
1824 dwritetextanalyzer1_GetBaseline
,
1825 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1826 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1827 dwritetextanalyzer1_GetScriptProperties
,
1828 dwritetextanalyzer1_GetTextComplexity
,
1829 dwritetextanalyzer1_GetJustificationOpportunities
,
1830 dwritetextanalyzer1_JustifyGlyphAdvances
,
1831 dwritetextanalyzer1_GetJustifiedGlyphs
,
1832 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1833 dwritetextanalyzer2_GetTypographicFeatures
,
1834 dwritetextanalyzer2_CheckTypographicFeature
1837 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1839 IDWriteTextAnalyzer
*get_text_analyzer(void)
1841 return (IDWriteTextAnalyzer
*)&textanalyzer
;
1844 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1846 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1848 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1850 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1851 IsEqualIID(riid
, &IID_IUnknown
))
1854 IDWriteNumberSubstitution_AddRef(iface
);
1858 WARN("%s not implemented.\n", debugstr_guid(riid
));
1862 return E_NOINTERFACE
;
1865 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1867 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1868 ULONG ref
= InterlockedIncrement(&This
->ref
);
1869 TRACE("(%p)->(%d)\n", This
, ref
);
1873 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1875 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1876 ULONG ref
= InterlockedDecrement(&This
->ref
);
1878 TRACE("(%p)->(%d)\n", This
, ref
);
1881 heap_free(This
->locale
);
1888 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1889 dwritenumbersubstitution_QueryInterface
,
1890 dwritenumbersubstitution_AddRef
,
1891 dwritenumbersubstitution_Release
1894 struct dwrite_numbersubstitution
*unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
1896 if (!iface
|| iface
->lpVtbl
!= &numbersubstitutionvtbl
)
1898 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
1901 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1902 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1904 struct dwrite_numbersubstitution
*substitution
;
1908 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1909 return E_INVALIDARG
;
1911 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1912 return E_INVALIDARG
;
1914 substitution
= heap_alloc(sizeof(*substitution
));
1916 return E_OUTOFMEMORY
;
1918 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1919 substitution
->ref
= 1;
1920 substitution
->ignore_user_override
= ignore_user_override
;
1921 substitution
->method
= method
;
1922 substitution
->locale
= heap_strdupW(locale
);
1923 if (locale
&& !substitution
->locale
) {
1924 heap_free(substitution
);
1925 return E_OUTOFMEMORY
;
1928 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;
1932 /* IDWriteFontFallback */
1933 static HRESULT WINAPI
fontfallback_QueryInterface(IDWriteFontFallback
*iface
, REFIID riid
, void **obj
)
1935 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1937 TRACE("(%p)->(%s %p)\n", fallback
, debugstr_guid(riid
), obj
);
1939 if (IsEqualIID(riid
, &IID_IDWriteFontFallback
) || IsEqualIID(riid
, &IID_IUnknown
)) {
1941 IDWriteFontFallback_AddRef(iface
);
1945 WARN("%s not implemented.\n", debugstr_guid(riid
));
1948 return E_NOINTERFACE
;
1951 static ULONG WINAPI
fontfallback_AddRef(IDWriteFontFallback
*iface
)
1953 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1954 TRACE("(%p)\n", fallback
);
1955 return IDWriteFactory5_AddRef(fallback
->factory
);
1958 static ULONG WINAPI
fontfallback_Release(IDWriteFontFallback
*iface
)
1960 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
1961 TRACE("(%p)\n", fallback
);
1962 return IDWriteFactory5_Release(fallback
->factory
);
1965 static int compare_mapping_range(const void *a
, const void *b
)
1967 UINT32 ch
= *(UINT32
*)a
;
1968 DWRITE_UNICODE_RANGE
*range
= (DWRITE_UNICODE_RANGE
*)b
;
1970 if (ch
> range
->last
)
1972 else if (ch
< range
->first
)
1978 static const struct fallback_mapping
*find_fallback_mapping(struct dwrite_fontfallback
*fallback
, UINT32 ch
)
1982 for (i
= 0; i
< fallback
->mappings_count
; i
++) {
1983 struct fallback_mapping
*mapping
= &fallback
->mappings
[i
];
1985 if (bsearch(&ch
, mapping
->ranges
, mapping
->ranges_count
, sizeof(*mapping
->ranges
),
1986 compare_mapping_range
) != NULL
)
1993 HRESULT
create_matching_font(IDWriteFontCollection
*collection
, const WCHAR
*name
,
1994 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, IDWriteFont
**font
)
1996 IDWriteFontFamily
*family
;
1997 BOOL exists
= FALSE
;
2003 hr
= IDWriteFontCollection_FindFamilyName(collection
, name
, &i
, &exists
);
2010 hr
= IDWriteFontCollection_GetFontFamily(collection
, i
, &family
);
2014 hr
= IDWriteFontFamily_GetFirstMatchingFont(family
, weight
, stretch
, style
, font
);
2015 IDWriteFontFamily_Release(family
);
2019 static HRESULT
fallback_map_characters(IDWriteFont
*font
, const WCHAR
*text
, UINT32 length
, UINT32
*mapped_length
)
2024 for (i
= 0; i
< length
; i
++) {
2025 UINT16 script
= get_char_script(text
[i
]);
2028 if (script
== Script_Unknown
|| script
== Script_Common
) {
2033 /* stop on first unsupported character */
2035 hr
= IDWriteFont_HasCharacter(font
, text
[i
], &exists
);
2036 if (hr
== S_OK
&& exists
)
2045 static HRESULT
fallback_get_fallback_font(struct dwrite_fontfallback
*fallback
, const WCHAR
*text
, UINT32 length
,
2046 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2047 IDWriteFont
**mapped_font
)
2049 const struct fallback_mapping
*mapping
;
2053 *mapped_font
= NULL
;
2055 mapping
= find_fallback_mapping(fallback
, text
[0]);
2057 WARN("No mapping range for %#x.\n", text
[0]);
2061 /* Now let's see what fallback can handle. Pick first font that could be created. */
2062 for (i
= 0; i
< mapping
->families_count
; i
++) {
2063 hr
= create_matching_font((IDWriteFontCollection
*)fallback
->systemcollection
, mapping
->families
[i
],
2064 weight
, style
, stretch
, mapped_font
);
2066 TRACE("Created fallback font using family %s.\n", debugstr_w(mapping
->families
[i
]));
2071 if (!*mapped_font
) {
2072 WARN("Failed to create fallback font.\n");
2076 hr
= fallback_map_characters(*mapped_font
, text
, length
, mapped_length
);
2078 WARN("Mapping with fallback family %s failed, hr %#x.\n", debugstr_w(mapping
->families
[i
]), hr
);
2080 if (!*mapped_length
) {
2081 IDWriteFont_Release(*mapped_font
);
2082 *mapped_font
= NULL
;
2085 return *mapped_length
? S_OK
: E_FAIL
;
2088 static HRESULT WINAPI
fontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
2089 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2090 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2091 IDWriteFont
**ret_font
, FLOAT
*scale
)
2093 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2098 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
2099 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2106 return E_INVALIDARG
;
2111 if (!basecollection
)
2112 basecollection
= (IDWriteFontCollection
*)fallback
->systemcollection
;
2114 hr
= get_text_source_ptr(source
, position
, length
, &text
, &buff
);
2118 if (basefamily
&& *basefamily
) {
2119 hr
= create_matching_font(basecollection
, basefamily
, weight
, style
, stretch
, ret_font
);
2123 hr
= fallback_map_characters(*ret_font
, text
, length
, mapped_length
);
2128 if (!*mapped_length
) {
2129 IDWriteFont
*mapped_font
;
2131 hr
= fallback_get_fallback_font(fallback
, text
, length
, weight
, style
, stretch
, mapped_length
, &mapped_font
);
2133 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2135 *mapped_length
= length
;
2141 IDWriteFont_Release(*ret_font
);
2142 *ret_font
= mapped_font
;
2151 static const IDWriteFontFallbackVtbl fontfallbackvtbl
= {
2152 fontfallback_QueryInterface
,
2153 fontfallback_AddRef
,
2154 fontfallback_Release
,
2155 fontfallback_MapCharacters
2158 HRESULT
create_system_fontfallback(IDWriteFactory5
*factory
, IDWriteFontFallback
**ret
)
2160 struct dwrite_fontfallback
*fallback
;
2164 fallback
= heap_alloc(sizeof(*fallback
));
2166 return E_OUTOFMEMORY
;
2168 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &fontfallbackvtbl
;
2169 fallback
->factory
= factory
;
2170 fallback
->mappings
= (struct fallback_mapping
*)fontfallback_neutral_data
;
2171 fallback
->mappings_count
= ARRAY_SIZE(fontfallback_neutral_data
);
2172 IDWriteFactory5_GetSystemFontCollection(fallback
->factory
, FALSE
, &fallback
->systemcollection
, FALSE
);
2174 *ret
= &fallback
->IDWriteFontFallback_iface
;
2178 void release_system_fontfallback(IDWriteFontFallback
*iface
)
2180 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2181 IDWriteFontCollection1_Release(fallback
->systemcollection
);
2182 heap_free(fallback
);
2185 static ULONG WINAPI
customfontfallback_AddRef(IDWriteFontFallback
*iface
)
2187 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2188 ULONG ref
= InterlockedIncrement(&fallback
->ref
);
2189 TRACE("(%p)->(%d)\n", fallback
, ref
);
2193 static ULONG WINAPI
customfontfallback_Release(IDWriteFontFallback
*iface
)
2195 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2196 ULONG ref
= InterlockedDecrement(&fallback
->ref
);
2198 TRACE("(%p)->(%d)\n", fallback
, ref
);
2201 IDWriteFactory5_Release(fallback
->factory
);
2202 heap_free(fallback
);
2208 static HRESULT WINAPI
customfontfallback_MapCharacters(IDWriteFontFallback
*iface
, IDWriteTextAnalysisSource
*source
,
2209 UINT32 position
, UINT32 length
, IDWriteFontCollection
*basecollection
, const WCHAR
*basefamily
,
2210 DWRITE_FONT_WEIGHT weight
, DWRITE_FONT_STYLE style
, DWRITE_FONT_STRETCH stretch
, UINT32
*mapped_length
,
2211 IDWriteFont
**ret_font
, FLOAT
*scale
)
2213 struct dwrite_fontfallback
*fallback
= impl_from_IDWriteFontFallback(iface
);
2215 FIXME("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback
, source
, position
, length
,
2216 basecollection
, debugstr_w(basefamily
), weight
, style
, stretch
, mapped_length
, ret_font
, scale
);
2221 static const IDWriteFontFallbackVtbl customfontfallbackvtbl
=
2223 fontfallback_QueryInterface
,
2224 customfontfallback_AddRef
,
2225 customfontfallback_Release
,
2226 customfontfallback_MapCharacters
,
2229 static HRESULT WINAPI
fontfallbackbuilder_QueryInterface(IDWriteFontFallbackBuilder
*iface
, REFIID riid
, void **obj
)
2231 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2233 TRACE("(%p)->(%s %p)\n", fallbackbuilder
, debugstr_guid(riid
), obj
);
2235 if (IsEqualIID(riid
, &IID_IDWriteFontFallbackBuilder
) || IsEqualIID(riid
, &IID_IUnknown
)) {
2237 IDWriteFontFallbackBuilder_AddRef(iface
);
2241 WARN("%s not implemented.\n", debugstr_guid(riid
));
2244 return E_NOINTERFACE
;
2247 static ULONG WINAPI
fontfallbackbuilder_AddRef(IDWriteFontFallbackBuilder
*iface
)
2249 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2250 ULONG ref
= InterlockedIncrement(&fallbackbuilder
->ref
);
2251 TRACE("(%p)->(%d)\n", fallbackbuilder
, ref
);
2255 static ULONG WINAPI
fontfallbackbuilder_Release(IDWriteFontFallbackBuilder
*iface
)
2257 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2258 ULONG ref
= InterlockedDecrement(&fallbackbuilder
->ref
);
2260 TRACE("(%p)->(%d)\n", fallbackbuilder
, ref
);
2265 for (i
= 0; i
< fallbackbuilder
->mappings_count
; i
++) {
2266 struct fallback_mapping
*mapping
= &fallbackbuilder
->mappings
[i
];
2269 for (j
= 0; j
< mapping
->families_count
; j
++)
2270 heap_free(mapping
->families
[j
]);
2271 heap_free(mapping
->families
);
2273 if (mapping
->collection
)
2274 IDWriteFontCollection_Release(mapping
->collection
);
2275 heap_free(mapping
->ranges
);
2276 heap_free(mapping
->locale
);
2279 IDWriteFactory5_Release(fallbackbuilder
->factory
);
2280 heap_free(fallbackbuilder
->mappings
);
2281 heap_free(fallbackbuilder
);
2287 static HRESULT WINAPI
fontfallbackbuilder_AddMapping(IDWriteFontFallbackBuilder
*iface
,
2288 const DWRITE_UNICODE_RANGE
*ranges
, UINT32 ranges_count
, WCHAR
const **target_families
, UINT32 families_count
,
2289 IDWriteFontCollection
*collection
, WCHAR
const *locale
, WCHAR
const *base_family
, FLOAT scale
)
2291 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2292 struct fallback_mapping
*mapping
;
2295 TRACE("(%p)->(%p, %u, %p, %u, %p, %s, %s, %f)\n", fallbackbuilder
, ranges
, ranges_count
, target_families
,
2296 families_count
, collection
, debugstr_w(locale
), debugstr_w(base_family
), scale
);
2298 if (!ranges
|| ranges_count
== 0 || !target_families
|| families_count
== 0 || scale
< 0.0f
)
2299 return E_INVALIDARG
;
2302 FIXME("base family ignored.\n");
2304 if (fallbackbuilder
->mappings_count
== fallbackbuilder
->mappings_capacity
) {
2305 struct fallback_mapping
*mappings
;
2307 if (fallbackbuilder
->mappings_capacity
== 0) {
2308 if ((mappings
= heap_calloc(16, sizeof(*mappings
))))
2309 fallbackbuilder
->mappings_capacity
= 16;
2312 if ((mappings
= heap_realloc(fallbackbuilder
->mappings
, sizeof(*fallbackbuilder
->mappings
) *
2313 fallbackbuilder
->mappings_capacity
* 2)))
2314 fallbackbuilder
->mappings_capacity
*= 2;
2317 return E_OUTOFMEMORY
;
2319 fallbackbuilder
->mappings
= mappings
;
2322 mapping
= &fallbackbuilder
->mappings
[fallbackbuilder
->mappings_count
++];
2324 mapping
->ranges
= heap_calloc(ranges_count
, sizeof(*mapping
->ranges
));
2325 memcpy(mapping
->ranges
, ranges
, sizeof(*mapping
->ranges
) * ranges_count
);
2326 mapping
->ranges_count
= ranges_count
;
2327 mapping
->families
= heap_alloc_zero(sizeof(*mapping
->families
) * families_count
);
2328 mapping
->families_count
= families_count
;
2329 for (i
= 0; i
< families_count
; i
++)
2330 mapping
->families
[i
] = heap_strdupW(target_families
[i
]);
2331 mapping
->collection
= collection
;
2332 if (mapping
->collection
)
2333 IDWriteFontCollection_AddRef(mapping
->collection
);
2334 mapping
->locale
= heap_strdupW(locale
);
2335 mapping
->scale
= scale
;
2340 static HRESULT WINAPI
fontfallbackbuilder_AddMappings(IDWriteFontFallbackBuilder
*iface
, IDWriteFontFallback
*fallback
)
2342 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2344 FIXME("(%p)->(%p): stub\n", fallbackbuilder
, fallback
);
2349 static HRESULT WINAPI
fontfallbackbuilder_CreateFontFallback(IDWriteFontFallbackBuilder
*iface
,
2350 IDWriteFontFallback
**ret
)
2352 struct dwrite_fontfallback_builder
*fallbackbuilder
= impl_from_IDWriteFontFallbackBuilder(iface
);
2353 struct dwrite_fontfallback
*fallback
;
2355 FIXME("(%p)->(%p): stub\n", fallbackbuilder
, ret
);
2359 fallback
= heap_alloc(sizeof(*fallback
));
2361 return E_OUTOFMEMORY
;
2363 fallback
->IDWriteFontFallback_iface
.lpVtbl
= &customfontfallbackvtbl
;
2365 fallback
->factory
= fallbackbuilder
->factory
;
2366 IDWriteFactory5_AddRef(fallback
->factory
);
2368 *ret
= &fallback
->IDWriteFontFallback_iface
;
2372 static const IDWriteFontFallbackBuilderVtbl fontfallbackbuildervtbl
=
2374 fontfallbackbuilder_QueryInterface
,
2375 fontfallbackbuilder_AddRef
,
2376 fontfallbackbuilder_Release
,
2377 fontfallbackbuilder_AddMapping
,
2378 fontfallbackbuilder_AddMappings
,
2379 fontfallbackbuilder_CreateFontFallback
,
2382 HRESULT
create_fontfallback_builder(IDWriteFactory5
*factory
, IDWriteFontFallbackBuilder
**ret
)
2384 struct dwrite_fontfallback_builder
*builder
;
2388 builder
= heap_alloc_zero(sizeof(*builder
));
2390 return E_OUTOFMEMORY
;
2392 builder
->IDWriteFontFallbackBuilder_iface
.lpVtbl
= &fontfallbackbuildervtbl
;
2394 builder
->factory
= factory
;
2395 IDWriteFactory5_AddRef(builder
->factory
);
2397 *ret
= &builder
->IDWriteFontFallbackBuilder_iface
;