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
24 #include "dwrite_private.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
29 extern const unsigned short wine_linebreak_table
[];
30 extern const unsigned short wine_scripts_table
[];
32 struct dwritescript_properties
{
33 DWRITE_SCRIPT_PROPERTIES props
;
35 const struct scriptshaping_ops
*ops
;
38 /* NOTE: keep this array synced with script ids from scripts.h */
39 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
40 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
41 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
42 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
43 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
44 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
45 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
46 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
47 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
48 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
49 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE
},
50 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
51 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
52 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
53 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
54 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
55 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
56 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
57 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
58 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
59 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
60 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
61 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
62 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
63 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
64 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
65 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
66 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE
},
67 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
68 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
69 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
70 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
71 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
72 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
73 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
74 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
75 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
76 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
77 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE
},
78 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
79 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, TRUE
},
80 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
81 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
82 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
83 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
84 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
85 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
86 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
87 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
88 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
89 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
90 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
91 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
92 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE
},
93 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
94 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
95 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE
},
96 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, FALSE
, &latn_shaping_ops
},
97 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
98 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
99 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
100 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
101 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
102 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
103 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
104 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
105 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
106 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 } },
107 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
108 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
109 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
110 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
111 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
112 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
113 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
115 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
116 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
117 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
118 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
119 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
120 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, TRUE
},
121 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
122 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
123 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
124 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
125 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, TRUE
},
126 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
127 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
128 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
129 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
130 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
131 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
132 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
133 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
134 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
135 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
136 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
137 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, TRUE
},
138 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
139 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
140 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
141 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
142 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
143 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
144 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
145 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
146 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
147 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
148 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
149 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
150 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
151 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
152 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 } },
153 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
154 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
155 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
156 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
157 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE
},
158 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
159 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
160 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
161 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
162 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
163 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
164 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, TRUE
}
167 struct dwrite_numbersubstitution
{
168 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
171 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
173 BOOL ignore_user_override
;
176 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
178 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
181 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
183 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
184 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
185 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
191 static inline UINT16
get_char_script(WCHAR c
)
193 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
194 if (script
== Script_Unknown
) {
196 if (GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
) && (type
& C1_CNTRL
))
197 script
= Script_Common
;
202 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 len
, IDWriteTextAnalysisSink
*sink
)
204 DWRITE_SCRIPT_ANALYSIS sa
;
205 UINT32 pos
, i
, length
;
207 if (!len
) return S_OK
;
209 sa
.script
= get_char_script(*text
);
214 for (i
= 1; i
< len
; i
++)
216 UINT16 script
= get_char_script(text
[i
]);
218 /* Unknown type is ignored when preceded or followed by another script */
219 if (sa
.script
== Script_Unknown
) sa
.script
= script
;
220 if (script
== Script_Unknown
&& sa
.script
!= Script_Common
) script
= sa
.script
;
221 /* this is a length of a sequence to be reported next */
222 if (sa
.script
== script
) length
++;
224 if (sa
.script
!= script
)
228 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
229 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
230 if (FAILED(hr
)) return hr
;
237 /* 1 length case or normal completion call */
238 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
239 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
242 struct linebreaking_state
{
243 DWRITE_LINE_BREAKPOINT
*breakpoints
;
247 enum BreakConditionLocation
{
248 BreakConditionBefore
,
252 enum linebreaking_classes
{
295 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
296 set to "can break" and could only be changed once. */
297 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
298 struct linebreaking_state
*state
)
300 if (location
== BreakConditionBefore
) {
301 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
303 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
305 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
308 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
310 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
311 if (pos
+ 1 < state
->count
)
312 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
316 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
318 struct linebreaking_state state
;
322 break_class
= heap_alloc(count
*sizeof(short));
324 return E_OUTOFMEMORY
;
326 state
.breakpoints
= breakpoints
;
329 /* LB31 - allow breaks everywhere. It will be overridden if needed as
330 other rules dictate. */
331 for (i
= 0; i
< count
; i
++)
333 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
335 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
336 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
337 breakpoints
[i
].isWhitespace
= break_class
[i
] == b_BK
|| break_class
[i
] == b_ZW
|| break_class
[i
] == b_SP
|| isspaceW(text
[i
]);
338 breakpoints
[i
].isSoftHyphen
= FALSE
;
339 breakpoints
[i
].padding
= 0;
341 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
342 switch (break_class
[i
])
348 break_class
[i
] = b_AL
;
351 break_class
[i
] = b_NS
;
356 /* LB2 - never break at the start */
357 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
358 /* LB3 - always break at the end. This one is ignored. */
360 for (i
= 0; i
< count
; i
++)
362 switch (break_class
[i
])
366 /* LB5 - don't break CR x LF */
367 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
369 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
370 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
376 /* LB4 - LB5 - always break after hard breaks */
377 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
378 /* LB6 - do not break before hard breaks */
379 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
381 /* LB7 - do not break before spaces */
383 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
386 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
387 /* LB8 - break before character after zero-width space, skip spaces in-between */
388 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
390 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
396 for (i
= 0; i
< count
; i
++)
398 if (break_class
[i
] == b_CM
)
402 switch (break_class
[i
-1])
410 break_class
[i
] = b_AL
;
413 break_class
[i
] = break_class
[i
-1];
416 else break_class
[i
] = b_AL
;
420 for (i
= 0; i
< count
; i
++)
422 switch (break_class
[i
])
424 /* LB11 - don't break before and after word joiner */
426 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
427 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
429 /* LB12 - don't break after glue */
431 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
435 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
436 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
445 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
449 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
450 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
451 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
458 while (j
< count
-1 && break_class
[j
] == b_SP
)
460 if (break_class
[j
] == b_OP
)
462 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
467 while(j
> 0 && break_class
[j
] == b_SP
)
469 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
470 for (j
++; j
<= i
; j
++)
471 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
476 while (j
< count
&& break_class
[j
] == b_SP
)
478 if (break_class
[j
] == b_B2
)
480 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
485 for (i
= 0; i
< count
; i
++)
487 switch(break_class
[i
])
489 /* LB18 - break is allowed after space */
491 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
493 /* LB19 - don't break before or after quotation mark */
495 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
496 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
500 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
501 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
507 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
510 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
515 switch (break_class
[i
+1])
519 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
526 switch (break_class
[i
-1])
533 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
542 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
543 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
544 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
545 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
546 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
547 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
549 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
550 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
551 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
552 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
553 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
554 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
557 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
558 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
559 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
560 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
561 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
562 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
563 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
564 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
565 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
566 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
567 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
568 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
569 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
570 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
571 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
574 if (break_class
[i
] == b_JL
)
576 switch (break_class
[i
+1])
582 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
585 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
586 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
587 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
588 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
589 break_class
[i
+1] == b_JT
)
590 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
593 switch (break_class
[i
])
600 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
601 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
603 if (break_class
[i
] == b_PO
)
605 switch (break_class
[i
+1])
612 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
617 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
618 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
619 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
620 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
621 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
624 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
625 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
626 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
629 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
630 break_class
[i
+1] == b_OP
)
631 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
632 if (break_class
[i
] == b_CP
&&
633 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
634 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
637 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
638 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
642 heap_free(break_class
);
646 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
648 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
650 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
651 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
652 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
653 IsEqualIID(riid
, &IID_IUnknown
))
660 return E_NOINTERFACE
;
664 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
669 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
674 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
675 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
681 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
683 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
684 if (FAILED(hr
)) return hr
;
686 return analyze_script(text
, len
, sink
);
689 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
690 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
692 UINT8
*levels
= NULL
, *explicit = NULL
;
693 UINT8 baselevel
, level
, explicit_level
;
699 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
704 /* get some, check for length */
707 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
708 if (FAILED(hr
)) return hr
;
713 buff
= heap_alloc(length
*sizeof(WCHAR
));
715 return E_OUTOFMEMORY
;
716 memcpy(buff
, text
, len
*sizeof(WCHAR
));
719 while (read
< length
&& text
) {
722 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
725 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
732 levels
= heap_alloc(length
*sizeof(*levels
));
733 explicit = heap_alloc(length
*sizeof(*explicit));
735 if (!levels
|| !explicit) {
740 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
741 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
746 explicit_level
= explicit[0];
748 for (i
= 1; i
< length
; i
++) {
749 if (levels
[i
] != level
|| explicit[i
] != explicit_level
) {
750 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, i
- pos
, explicit_level
, level
);
754 explicit_level
= explicit[i
];
759 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, length
- pos
, explicit_level
, level
);
770 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
771 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
773 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
777 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
778 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
780 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
786 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
791 /* get some, check for length */
794 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
795 if (FAILED(hr
)) return hr
;
800 buff
= heap_alloc(length
*sizeof(WCHAR
));
802 return E_OUTOFMEMORY
;
803 memcpy(buff
, text
, len
*sizeof(WCHAR
));
806 while (read
< length
&& text
) {
809 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
812 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
819 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
825 hr
= analyze_linebreaks(text
, length
, breakpoints
);
829 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
832 heap_free(breakpoints
);
838 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
839 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
840 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
841 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
842 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
843 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
844 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
846 const struct dwritescript_properties
*scriptprops
;
847 struct scriptshaping_context context
;
848 struct scriptshaping_cache
*cache
;
855 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
856 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
857 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
859 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
861 if (max_glyph_count
< length
)
862 return E_NOT_SUFFICIENT_BUFFER
;
865 FIXME("number substitution is not supported.\n");
867 for (i
= 0; i
< length
; i
++) {
868 /* FIXME: set to better values */
869 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
870 glyph_props
[i
].isClusterStart
= 1;
871 glyph_props
[i
].isDiacritic
= 0;
872 glyph_props
[i
].isZeroWidthSpace
= 0;
873 glyph_props
[i
].reserved
= 0;
875 /* FIXME: have the shaping engine set this */
876 text_props
[i
].isShapedAlone
= 0;
877 text_props
[i
].reserved
= 0;
882 for (; i
< max_glyph_count
; i
++) {
883 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
884 glyph_props
[i
].isClusterStart
= 0;
885 glyph_props
[i
].isDiacritic
= 0;
886 glyph_props
[i
].isZeroWidthSpace
= 0;
887 glyph_props
[i
].reserved
= 0;
890 string
= heap_alloc(sizeof(WCHAR
)*length
);
892 return E_OUTOFMEMORY
;
894 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
897 if (!update_cluster
) {
898 codepoint
= decode_surrogate_pair(text
, i
, length
);
900 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
901 string
[i
] = codepoint
;
905 string
[i
+1] = text
[i
+1];
906 update_cluster
= TRUE
;
909 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
918 update_cluster
= FALSE
;
919 /* mark surrogate halves with same cluster */
920 clustermap
[i
] = clustermap
[i
-1];
921 /* update following clusters */
922 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
926 *actual_glyph_count
= g
;
928 hr
= create_scriptshaping_cache(fontface
, &cache
);
932 context
.cache
= cache
;
934 context
.length
= length
;
935 context
.is_rtl
= is_rtl
;
936 context
.max_glyph_count
= max_glyph_count
;
937 context
.language_tag
= DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t');
940 if (GetLocaleInfoEx(locale
, LOCALE_SOPENTYPELANGUAGETAG
, tag
, sizeof(tag
)/sizeof(WCHAR
)))
941 context
.language_tag
= DWRITE_MAKE_OPENTYPE_TAG(tag
[0],tag
[1],tag
[2],tag
[3]);
944 scriptprops
= &dwritescripts_properties
[script
];
945 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
946 hr
= scriptprops
->ops
->contextual_shaping(&context
, clustermap
, glyph_indices
, actual_glyph_count
);
951 /* FIXME: apply default features */
953 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
954 hr
= scriptprops
->ops
->set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
956 hr
= default_shaping_ops
.set_text_glyphs_props(&context
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
959 release_scriptshaping_cache(cache
);
965 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
966 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
967 UINT32 text_len
, UINT16
const* glyph_indices
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
968 UINT32 glyph_count
, IDWriteFontFace
* font_face
, FLOAT fontEmSize
, BOOL is_sideways
, BOOL is_rtl
,
969 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
970 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
* glyph_advances
, DWRITE_GLYPH_OFFSET
* glyph_offsets
)
972 FIXME("(%s %p %p %u %p %p %u %p %f %d %d %p %s %p %p %u %p %p): stub\n", debugstr_w(text
),
973 clustermap
, props
, text_len
, glyph_indices
, glyph_props
, glyph_count
, font_face
, fontEmSize
, is_sideways
,
974 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, glyph_advances
, glyph_offsets
);
978 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
979 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
980 UINT32 text_len
, UINT16
const* glyph_indices
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
981 UINT32 glyph_count
, IDWriteFontFace
* font_face
, FLOAT fontEmSize
, FLOAT pixels_per_dip
,
982 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
983 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
984 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
* glyph_advances
, DWRITE_GLYPH_OFFSET
* glyph_offsets
)
986 FIXME("(%s %p %p %u %p %p %u %p %f %f %p %d %d %d %p %s %p %p %u %p %p): stub\n", debugstr_w(text
),
987 clustermap
, props
, text_len
, glyph_indices
, glyph_props
, glyph_count
, font_face
, fontEmSize
, pixels_per_dip
,
988 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
989 feature_ranges
, glyph_advances
, glyph_offsets
);
993 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
994 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
995 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
996 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
998 FIXME("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p): stub\n", leading_spacing
, trailing_spacing
, min_advance_width
,
999 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
1003 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
1004 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
1005 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
1007 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
1008 baseline_coord
, exists
);
1012 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1013 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1015 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1019 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1020 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1022 FIXME("(%d %d %p): stub\n", angle
, is_sideways
, transform
);
1026 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1027 DWRITE_SCRIPT_PROPERTIES
*props
)
1029 TRACE("(%u %p)\n", sa
.script
, props
);
1031 if (sa
.script
> Script_LastId
)
1032 return E_INVALIDARG
;
1034 *props
= dwritescripts_properties
[sa
.script
].props
;
1038 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1040 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1043 UINT16 script
= get_char_script(c
);
1044 return !dwritescripts_properties
[script
].is_complex
;
1048 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1049 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1054 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1060 return E_INVALIDARG
;
1067 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1068 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1069 if (is_char_from_simple_script(text
[i
])) {
1080 if (*is_simple
&& indices
) {
1081 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1083 return E_OUTOFMEMORY
;
1085 for (i
= 0; i
< *len_read
; i
++)
1086 codepoints
[i
] = text
[i
];
1088 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1089 heap_free(codepoints
);
1095 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1096 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1097 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1099 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1100 debugstr_w(text
), clustermap
, prop
, jo
);
1104 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1105 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1106 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1108 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1113 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1114 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1115 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1116 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1117 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1118 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1120 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
,
1121 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1122 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1126 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1127 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*transform
)
1129 FIXME("(%d %d %.2f %.2f %p): stub\n", angle
, is_sideways
, originX
, originY
, transform
);
1133 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1134 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1135 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1137 FIXME("(%p %u %s %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), max_tagcount
, actual_tagcount
,
1142 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1143 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1144 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1146 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1151 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1152 dwritetextanalyzer_QueryInterface
,
1153 dwritetextanalyzer_AddRef
,
1154 dwritetextanalyzer_Release
,
1155 dwritetextanalyzer_AnalyzeScript
,
1156 dwritetextanalyzer_AnalyzeBidi
,
1157 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1158 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1159 dwritetextanalyzer_GetGlyphs
,
1160 dwritetextanalyzer_GetGlyphPlacements
,
1161 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1162 dwritetextanalyzer1_ApplyCharacterSpacing
,
1163 dwritetextanalyzer1_GetBaseline
,
1164 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1165 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1166 dwritetextanalyzer1_GetScriptProperties
,
1167 dwritetextanalyzer1_GetTextComplexity
,
1168 dwritetextanalyzer1_GetJustificationOpportunities
,
1169 dwritetextanalyzer1_JustifyGlyphAdvances
,
1170 dwritetextanalyzer1_GetJustifiedGlyphs
,
1171 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1172 dwritetextanalyzer2_GetTypographicFeatures
,
1173 dwritetextanalyzer2_CheckTypographicFeature
1176 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1178 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1180 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1184 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1186 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1188 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1190 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1191 IsEqualIID(riid
, &IID_IUnknown
))
1194 IDWriteNumberSubstitution_AddRef(iface
);
1200 return E_NOINTERFACE
;
1203 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1205 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1206 ULONG ref
= InterlockedIncrement(&This
->ref
);
1207 TRACE("(%p)->(%d)\n", This
, ref
);
1211 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1213 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1214 ULONG ref
= InterlockedDecrement(&This
->ref
);
1216 TRACE("(%p)->(%d)\n", This
, ref
);
1219 heap_free(This
->locale
);
1226 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1227 dwritenumbersubstitution_QueryInterface
,
1228 dwritenumbersubstitution_AddRef
,
1229 dwritenumbersubstitution_Release
1232 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1233 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1235 struct dwrite_numbersubstitution
*substitution
;
1239 if ((UINT32
)method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1240 return E_INVALIDARG
;
1242 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1243 return E_INVALIDARG
;
1245 substitution
= heap_alloc(sizeof(*substitution
));
1247 return E_OUTOFMEMORY
;
1249 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1250 substitution
->ref
= 1;
1251 substitution
->ignore_user_override
= ignore_user_override
;
1252 substitution
->method
= method
;
1253 substitution
->locale
= heap_strdupW(locale
);
1254 if (locale
&& !substitution
->locale
) {
1255 heap_free(substitution
);
1256 return E_OUTOFMEMORY
;
1259 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;