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
[];
32 extern const unsigned short wine_scripts_table
[];
34 struct dwritescript_properties
{
35 DWRITE_SCRIPT_PROPERTIES props
;
37 const struct scriptshaping_ops
*ops
;
40 /* NOTE: keep this array synced with script ids from scripts.h */
41 static const struct dwritescript_properties dwritescripts_properties
[Script_LastId
+1] = {
42 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
43 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
44 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
45 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
46 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
47 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
48 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
49 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
51 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE
},
52 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
53 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
54 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
55 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
56 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
57 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
58 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
59 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
60 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
61 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
62 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
63 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
64 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
65 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
66 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
67 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
68 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE
},
69 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
70 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
71 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
72 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
73 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
74 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
75 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
76 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
77 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
78 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
79 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE
},
80 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
81 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, TRUE
},
82 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
83 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
84 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
85 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
86 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
87 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
88 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
89 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
90 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
91 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
92 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
93 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
94 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE
},
95 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
96 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
97 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE
},
98 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, FALSE
, &latn_shaping_ops
},
99 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
100 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
101 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
102 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
103 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
104 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
105 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
106 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
107 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
108 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 } },
109 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
110 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
111 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
112 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
113 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
115 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
116 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
117 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
118 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
119 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
120 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
121 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
122 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, TRUE
},
123 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
124 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
125 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
126 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
127 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, TRUE
},
128 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
129 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
130 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
131 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
132 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
133 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
134 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
135 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
136 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
137 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
138 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
139 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, TRUE
},
140 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
141 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
142 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
143 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
144 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
145 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
146 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
147 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
148 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
149 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE
},
150 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
151 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
152 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
153 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
154 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 } },
155 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
156 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
157 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
158 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
159 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE
},
160 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
161 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE
},
162 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
163 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
164 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE
},
165 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
166 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, TRUE
}
169 struct dwrite_numbersubstitution
{
170 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface
;
173 DWRITE_NUMBER_SUBSTITUTION_METHOD method
;
175 BOOL ignore_user_override
;
178 static inline struct dwrite_numbersubstitution
*impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution
*iface
)
180 return CONTAINING_RECORD(iface
, struct dwrite_numbersubstitution
, IDWriteNumberSubstitution_iface
);
183 static inline UINT32
decode_surrogate_pair(const WCHAR
*str
, UINT32 index
, UINT32 end
)
185 if (index
< end
-1 && IS_SURROGATE_PAIR(str
[index
], str
[index
+1])) {
186 UINT32 ch
= 0x10000 + ((str
[index
] - 0xd800) << 10) + (str
[index
+1] - 0xdc00);
187 TRACE("surrogate pair (%x %x) => %x\n", str
[index
], str
[index
+1], ch
);
193 static inline UINT16
get_char_script(WCHAR c
)
195 UINT16 script
= get_table_entry(wine_scripts_table
, c
);
196 if (script
== Script_Unknown
) {
198 if (GetStringTypeW(CT_CTYPE1
, &c
, 1, &type
) && (type
& C1_CNTRL
))
199 script
= Script_Common
;
204 static HRESULT
analyze_script(const WCHAR
*text
, UINT32 len
, IDWriteTextAnalysisSink
*sink
)
206 DWRITE_SCRIPT_ANALYSIS sa
;
207 UINT32 pos
, i
, length
;
209 if (!len
) return S_OK
;
211 sa
.script
= get_char_script(*text
);
216 for (i
= 1; i
< len
; i
++)
218 UINT16 script
= get_char_script(text
[i
]);
220 /* Unknown type is ignored when preceded or followed by another script */
221 if (sa
.script
== Script_Unknown
) sa
.script
= script
;
222 if (script
== Script_Unknown
&& sa
.script
!= Script_Common
) script
= sa
.script
;
223 /* this is a length of a sequence to be reported next */
224 if (sa
.script
== script
) length
++;
226 if (sa
.script
!= script
)
230 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
231 hr
= IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
232 if (FAILED(hr
)) return hr
;
239 /* 1 length case or normal completion call */
240 sa
.shapes
= sa
.script
!= Script_Common
? DWRITE_SCRIPT_SHAPES_DEFAULT
: DWRITE_SCRIPT_SHAPES_NO_VISUAL
;
241 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink
, pos
, length
, &sa
);
244 struct linebreaking_state
{
245 DWRITE_LINE_BREAKPOINT
*breakpoints
;
249 enum BreakConditionLocation
{
250 BreakConditionBefore
,
254 enum linebreaking_classes
{
297 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
298 set to "can break" and could only be changed once. */
299 static inline void set_break_condition(UINT32 pos
, enum BreakConditionLocation location
, DWRITE_BREAK_CONDITION condition
,
300 struct linebreaking_state
*state
)
302 if (location
== BreakConditionBefore
) {
303 if (state
->breakpoints
[pos
].breakConditionBefore
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
305 state
->breakpoints
[pos
].breakConditionBefore
= condition
;
307 state
->breakpoints
[pos
-1].breakConditionAfter
= condition
;
310 if (state
->breakpoints
[pos
].breakConditionAfter
!= DWRITE_BREAK_CONDITION_CAN_BREAK
)
312 state
->breakpoints
[pos
].breakConditionAfter
= condition
;
313 if (pos
+ 1 < state
->count
)
314 state
->breakpoints
[pos
+1].breakConditionBefore
= condition
;
318 static HRESULT
analyze_linebreaks(const WCHAR
*text
, UINT32 count
, DWRITE_LINE_BREAKPOINT
*breakpoints
)
320 struct linebreaking_state state
;
324 break_class
= heap_alloc(count
*sizeof(short));
326 return E_OUTOFMEMORY
;
328 state
.breakpoints
= breakpoints
;
331 /* LB31 - allow breaks everywhere. It will be overridden if needed as
332 other rules dictate. */
333 for (i
= 0; i
< count
; i
++)
335 break_class
[i
] = get_table_entry(wine_linebreak_table
, text
[i
]);
337 breakpoints
[i
].breakConditionBefore
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
338 breakpoints
[i
].breakConditionAfter
= DWRITE_BREAK_CONDITION_CAN_BREAK
;
339 breakpoints
[i
].isWhitespace
= break_class
[i
] == b_BK
|| break_class
[i
] == b_ZW
|| break_class
[i
] == b_SP
|| isspaceW(text
[i
]);
340 breakpoints
[i
].isSoftHyphen
= FALSE
;
341 breakpoints
[i
].padding
= 0;
343 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
344 switch (break_class
[i
])
350 break_class
[i
] = b_AL
;
353 break_class
[i
] = b_NS
;
358 /* LB2 - never break at the start */
359 set_break_condition(0, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
360 /* LB3 - always break at the end. This one is ignored. */
362 for (i
= 0; i
< count
; i
++)
364 switch (break_class
[i
])
368 /* LB5 - don't break CR x LF */
369 if (i
< count
-1 && break_class
[i
+1] == b_LF
)
371 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
372 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
378 /* LB4 - LB5 - always break after hard breaks */
379 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MUST_BREAK
, &state
);
380 /* LB6 - do not break before hard breaks */
381 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
383 /* LB7 - do not break before spaces */
385 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
388 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
389 /* LB8 - break before character after zero-width space, skip spaces in-between */
390 while (i
< count
-1 && break_class
[i
+1] == b_SP
)
392 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
398 for (i
= 0; i
< count
; i
++)
400 if (break_class
[i
] == b_CM
)
404 switch (break_class
[i
-1])
412 break_class
[i
] = b_AL
;
415 break_class
[i
] = break_class
[i
-1];
418 else break_class
[i
] = b_AL
;
422 for (i
= 0; i
< count
; i
++)
424 switch (break_class
[i
])
426 /* LB11 - don't break before and after word joiner */
428 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
429 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
431 /* LB12 - don't break after glue */
433 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
437 if (break_class
[i
-1] != b_SP
&& break_class
[i
-1] != b_BA
&& break_class
[i
-1] != b_HY
)
438 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
447 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
451 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
452 while (i
< count
-1 && break_class
[i
+1] == b_SP
) {
453 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
460 while (j
< count
-1 && break_class
[j
] == b_SP
)
462 if (break_class
[j
] == b_OP
)
464 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
469 while(j
> 0 && break_class
[j
] == b_SP
)
471 if (break_class
[j
] == b_CL
|| break_class
[j
] == b_CP
)
472 for (j
++; j
<= i
; j
++)
473 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
478 while (j
< count
&& break_class
[j
] == b_SP
)
480 if (break_class
[j
] == b_B2
)
482 set_break_condition(j
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
487 for (i
= 0; i
< count
; i
++)
489 switch(break_class
[i
])
491 /* LB18 - break is allowed after space */
493 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
495 /* LB19 - don't break before or after quotation mark */
497 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
498 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
502 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
503 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_CAN_BREAK
, &state
);
509 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
512 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
517 switch (break_class
[i
+1])
521 set_break_condition(i
+1, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
528 switch (break_class
[i
-1])
535 set_break_condition(i
, BreakConditionBefore
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
544 if ((break_class
[i
] == b_ID
&& break_class
[i
+1] == b_PO
) ||
545 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_NU
) ||
546 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_NU
) ||
547 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_AL
) ||
548 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_HL
))
549 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
551 if ((break_class
[i
] == b_PR
&& break_class
[i
+1] == b_ID
) ||
552 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_AL
) ||
553 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_HL
) ||
554 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_AL
) ||
555 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_HL
))
556 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
559 if ((break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PO
) ||
560 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PO
) ||
561 (break_class
[i
] == b_CL
&& break_class
[i
+1] == b_PR
) ||
562 (break_class
[i
] == b_CP
&& break_class
[i
+1] == b_PR
) ||
563 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PO
) ||
564 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_PR
) ||
565 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_OP
) ||
566 (break_class
[i
] == b_PO
&& break_class
[i
+1] == b_NU
) ||
567 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_OP
) ||
568 (break_class
[i
] == b_PR
&& break_class
[i
+1] == b_NU
) ||
569 (break_class
[i
] == b_HY
&& break_class
[i
+1] == b_NU
) ||
570 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_NU
) ||
571 (break_class
[i
] == b_NU
&& break_class
[i
+1] == b_NU
) ||
572 (break_class
[i
] == b_SY
&& break_class
[i
+1] == b_NU
))
573 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
576 if (break_class
[i
] == b_JL
)
578 switch (break_class
[i
+1])
584 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
587 if ((break_class
[i
] == b_JV
|| break_class
[i
] == b_H2
) &&
588 (break_class
[i
+1] == b_JV
|| break_class
[i
+1] == b_JT
))
589 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
590 if ((break_class
[i
] == b_JT
|| break_class
[i
] == b_H3
) &&
591 break_class
[i
+1] == b_JT
)
592 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
595 switch (break_class
[i
])
602 if (break_class
[i
+1] == b_IN
|| break_class
[i
+1] == b_PO
)
603 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
605 if (break_class
[i
] == b_PO
)
607 switch (break_class
[i
+1])
614 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
619 if ((break_class
[i
] == b_AL
&& break_class
[i
+1] == b_AL
) ||
620 (break_class
[i
] == b_AL
&& break_class
[i
+1] == b_HL
) ||
621 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_AL
) ||
622 (break_class
[i
] == b_HL
&& break_class
[i
+1] == b_HL
))
623 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
626 if ((break_class
[i
] == b_IS
&& break_class
[i
+1] == b_AL
) ||
627 (break_class
[i
] == b_IS
&& break_class
[i
+1] == b_HL
))
628 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
631 if ((break_class
[i
] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
) &&
632 break_class
[i
+1] == b_OP
)
633 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
634 if (break_class
[i
] == b_CP
&&
635 (break_class
[i
+1] == b_AL
|| break_class
[i
] == b_HL
|| break_class
[i
] == b_NU
))
636 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
639 if (break_class
[i
] == b_RI
&& break_class
[i
+1] == b_RI
)
640 set_break_condition(i
, BreakConditionAfter
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, &state
);
644 heap_free(break_class
);
648 static HRESULT WINAPI
dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2
*iface
, REFIID riid
, void **obj
)
650 TRACE("(%s %p)\n", debugstr_guid(riid
), obj
);
652 if (IsEqualIID(riid
, &IID_IDWriteTextAnalyzer2
) ||
653 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer1
) ||
654 IsEqualIID(riid
, &IID_IDWriteTextAnalyzer
) ||
655 IsEqualIID(riid
, &IID_IUnknown
))
662 return E_NOINTERFACE
;
666 static ULONG WINAPI
dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2
*iface
)
671 static ULONG WINAPI
dwritetextanalyzer_Release(IDWriteTextAnalyzer2
*iface
)
676 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2
*iface
,
677 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
683 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
685 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
686 if (FAILED(hr
)) return hr
;
688 return analyze_script(text
, len
, sink
);
691 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2
*iface
,
692 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
694 UINT8
*levels
= NULL
, *explicit = NULL
;
695 UINT8 baselevel
, level
, explicit_level
;
701 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
706 /* get some, check for length */
709 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
710 if (FAILED(hr
)) return hr
;
715 buff
= heap_alloc(length
*sizeof(WCHAR
));
717 return E_OUTOFMEMORY
;
718 memcpy(buff
, text
, len
*sizeof(WCHAR
));
721 while (read
< length
&& text
) {
724 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
727 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
734 levels
= heap_alloc(length
*sizeof(*levels
));
735 explicit = heap_alloc(length
*sizeof(*explicit));
737 if (!levels
|| !explicit) {
742 baselevel
= IDWriteTextAnalysisSource_GetParagraphReadingDirection(source
);
743 hr
= bidi_computelevels(text
, length
, baselevel
, explicit, levels
);
748 explicit_level
= explicit[0];
750 for (i
= 1; i
< length
; i
++) {
751 if (levels
[i
] != level
|| explicit[i
] != explicit_level
) {
752 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, i
- pos
, explicit_level
, level
);
756 explicit_level
= explicit[i
];
761 hr
= IDWriteTextAnalysisSink_SetBidiLevel(sink
, pos
, length
- pos
, explicit_level
, level
);
772 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2
*iface
,
773 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
775 FIXME("(%p %u %u %p): stub\n", source
, position
, length
, sink
);
779 static HRESULT WINAPI
dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2
*iface
,
780 IDWriteTextAnalysisSource
* source
, UINT32 position
, UINT32 length
, IDWriteTextAnalysisSink
* sink
)
782 DWRITE_LINE_BREAKPOINT
*breakpoints
= NULL
;
788 TRACE("(%p %u %u %p)\n", source
, position
, length
, sink
);
793 /* get some, check for length */
796 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, position
, &text
, &len
);
797 if (FAILED(hr
)) return hr
;
802 buff
= heap_alloc(length
*sizeof(WCHAR
));
804 return E_OUTOFMEMORY
;
805 memcpy(buff
, text
, len
*sizeof(WCHAR
));
808 while (read
< length
&& text
) {
811 hr
= IDWriteTextAnalysisSource_GetTextAtPosition(source
, read
, &text
, &len
);
814 memcpy(&buff
[read
], text
, min(len
, length
-read
)*sizeof(WCHAR
));
821 breakpoints
= heap_alloc(length
*sizeof(*breakpoints
));
827 hr
= analyze_linebreaks(text
, length
, breakpoints
);
831 hr
= IDWriteTextAnalysisSink_SetLineBreakpoints(sink
, position
, length
, breakpoints
);
834 heap_free(breakpoints
);
840 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2
*iface
,
841 WCHAR
const* text
, UINT32 length
, IDWriteFontFace
* fontface
, BOOL is_sideways
,
842 BOOL is_rtl
, DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
,
843 IDWriteNumberSubstitution
* substitution
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
844 UINT32
const* feature_range_len
, UINT32 feature_ranges
, UINT32 max_glyph_count
,
845 UINT16
* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* text_props
, UINT16
* glyph_indices
,
846 DWRITE_SHAPING_GLYPH_PROPERTIES
* glyph_props
, UINT32
* actual_glyph_count
)
848 const struct dwritescript_properties
*scriptprops
;
849 struct scriptshaping_cache
*cache
;
856 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text
, length
),
857 length
, fontface
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), substitution
, features
, feature_range_len
,
858 feature_ranges
, max_glyph_count
, clustermap
, text_props
, glyph_indices
, glyph_props
, actual_glyph_count
);
860 script
= analysis
->script
> Script_LastId
? Script_Unknown
: analysis
->script
;
862 if (max_glyph_count
< length
)
863 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
866 FIXME("number substitution is not supported.\n");
868 for (i
= 0; i
< length
; i
++) {
869 /* FIXME: set to better values */
870 glyph_props
[i
].justification
= text
[i
] == ' ' ? SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
871 glyph_props
[i
].isClusterStart
= 1;
872 glyph_props
[i
].isDiacritic
= 0;
873 glyph_props
[i
].isZeroWidthSpace
= 0;
874 glyph_props
[i
].reserved
= 0;
876 /* FIXME: have the shaping engine set this */
877 text_props
[i
].isShapedAlone
= 0;
878 text_props
[i
].reserved
= 0;
883 for (; i
< max_glyph_count
; i
++) {
884 glyph_props
[i
].justification
= SCRIPT_JUSTIFY_NONE
;
885 glyph_props
[i
].isClusterStart
= 0;
886 glyph_props
[i
].isDiacritic
= 0;
887 glyph_props
[i
].isZeroWidthSpace
= 0;
888 glyph_props
[i
].reserved
= 0;
891 string
= heap_alloc(sizeof(WCHAR
)*length
);
893 return E_OUTOFMEMORY
;
895 for (i
= 0, g
= 0, update_cluster
= FALSE
; i
< length
; i
++) {
898 if (!update_cluster
) {
899 codepoint
= decode_surrogate_pair(text
, i
, length
);
901 codepoint
= is_rtl
? bidi_get_mirrored_char(text
[i
]) : text
[i
];
902 string
[i
] = codepoint
;
906 string
[i
+1] = text
[i
+1];
907 update_cluster
= TRUE
;
910 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, &glyph_indices
[g
]);
919 update_cluster
= FALSE
;
920 /* mark surrogate halves with same cluster */
921 clustermap
[i
] = clustermap
[i
-1];
922 /* update following clusters */
923 for (k
= i
+ 1; k
>= 0 && k
< length
; k
++)
927 *actual_glyph_count
= g
;
929 hr
= create_scriptshaping_cache(fontface
, &cache
);
933 scriptprops
= &dwritescripts_properties
[script
];
934 if (scriptprops
->ops
&& scriptprops
->ops
->contextual_shaping
) {
935 hr
= scriptprops
->ops
->contextual_shaping(cache
, is_rtl
, string
, length
, max_glyph_count
, clustermap
, glyph_indices
, actual_glyph_count
);
940 /* FIXME: apply default features */
942 if (scriptprops
->ops
&& scriptprops
->ops
->set_text_glyphs_props
)
943 hr
= scriptprops
->ops
->set_text_glyphs_props(cache
, string
, length
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
945 hr
= default_shaping_ops
.set_text_glyphs_props(cache
, string
, length
, clustermap
, glyph_indices
, *actual_glyph_count
, text_props
, glyph_props
);
948 release_scriptshaping_cache(cache
);
954 static HRESULT WINAPI
dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
955 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
956 UINT32 text_len
, UINT16
const* glyph_indices
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
957 UINT32 glyph_count
, IDWriteFontFace
* font_face
, FLOAT fontEmSize
, BOOL is_sideways
, BOOL is_rtl
,
958 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
959 UINT32
const* feature_range_len
, UINT32 feature_ranges
, FLOAT
* glyph_advances
, DWRITE_GLYPH_OFFSET
* glyph_offsets
)
961 FIXME("(%s %p %p %u %p %p %u %p %f %d %d %p %s %p %p %u %p %p): stub\n", debugstr_w(text
),
962 clustermap
, props
, text_len
, glyph_indices
, glyph_props
, glyph_count
, font_face
, fontEmSize
, is_sideways
,
963 is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_len
, feature_ranges
, glyph_advances
, glyph_offsets
);
967 static HRESULT WINAPI
dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2
*iface
,
968 WCHAR
const* text
, UINT16
const* clustermap
, DWRITE_SHAPING_TEXT_PROPERTIES
* props
,
969 UINT32 text_len
, UINT16
const* glyph_indices
, DWRITE_SHAPING_GLYPH_PROPERTIES
const* glyph_props
,
970 UINT32 glyph_count
, IDWriteFontFace
* font_face
, FLOAT fontEmSize
, FLOAT pixels_per_dip
,
971 DWRITE_MATRIX
const* transform
, BOOL use_gdi_natural
, BOOL is_sideways
, BOOL is_rtl
,
972 DWRITE_SCRIPT_ANALYSIS
const* analysis
, WCHAR
const* locale
, DWRITE_TYPOGRAPHIC_FEATURES
const** features
,
973 UINT32
const* feature_range_lengths
, UINT32 feature_ranges
, FLOAT
* glyph_advances
, DWRITE_GLYPH_OFFSET
* glyph_offsets
)
975 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
),
976 clustermap
, props
, text_len
, glyph_indices
, glyph_props
, glyph_count
, font_face
, fontEmSize
, pixels_per_dip
,
977 transform
, use_gdi_natural
, is_sideways
, is_rtl
, analysis
, debugstr_w(locale
), features
, feature_range_lengths
,
978 feature_ranges
, glyph_advances
, glyph_offsets
);
982 static HRESULT WINAPI
dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2
*iface
,
983 FLOAT leading_spacing
, FLOAT trailing_spacing
, FLOAT min_advance_width
, UINT32 len
,
984 UINT32 glyph_count
, UINT16
const *clustermap
, FLOAT
const *advances
, DWRITE_GLYPH_OFFSET
const *offsets
,
985 DWRITE_SHAPING_GLYPH_PROPERTIES
const *props
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
987 FIXME("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p): stub\n", leading_spacing
, trailing_spacing
, min_advance_width
,
988 len
, glyph_count
, clustermap
, advances
, offsets
, props
, modified_advances
, modified_offsets
);
992 static HRESULT WINAPI
dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2
*iface
, IDWriteFontFace
*face
,
993 DWRITE_BASELINE baseline
, BOOL vertical
, BOOL is_simulation_allowed
, DWRITE_SCRIPT_ANALYSIS sa
,
994 const WCHAR
*localeName
, INT32
*baseline_coord
, BOOL
*exists
)
996 FIXME("(%p %d %d %u %s %p %p): stub\n", face
, vertical
, is_simulation_allowed
, sa
.script
, debugstr_w(localeName
),
997 baseline_coord
, exists
);
1001 static HRESULT WINAPI
dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2
*iface
,
1002 IDWriteTextAnalysisSource1
* source
, UINT32 text_pos
, UINT32 len
, IDWriteTextAnalysisSink1
*sink
)
1004 FIXME("(%p %u %u %p): stub\n", source
, text_pos
, len
, sink
);
1008 static HRESULT WINAPI
dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1009 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, DWRITE_MATRIX
*transform
)
1011 FIXME("(%d %d %p): stub\n", angle
, is_sideways
, transform
);
1015 static HRESULT WINAPI
dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2
*iface
, DWRITE_SCRIPT_ANALYSIS sa
,
1016 DWRITE_SCRIPT_PROPERTIES
*props
)
1018 TRACE("(%u %p)\n", sa
.script
, props
);
1020 if (sa
.script
> Script_LastId
)
1021 return E_INVALIDARG
;
1023 *props
= dwritescripts_properties
[sa
.script
].props
;
1027 static inline BOOL
is_char_from_simple_script(WCHAR c
)
1029 if (IS_HIGH_SURROGATE(c
) || IS_LOW_SURROGATE(c
))
1032 UINT16 script
= get_char_script(c
);
1033 return !dwritescripts_properties
[script
].is_complex
;
1037 static HRESULT WINAPI
dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2
*iface
, const WCHAR
*text
,
1038 UINT32 len
, IDWriteFontFace
*face
, BOOL
*is_simple
, UINT32
*len_read
, UINT16
*indices
)
1043 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text
, len
), len
, face
, is_simple
, len_read
, indices
);
1049 return E_INVALIDARG
;
1056 *is_simple
= text
[0] && is_char_from_simple_script(text
[0]);
1057 for (i
= 1; i
< len
&& text
[i
]; i
++) {
1058 if (is_char_from_simple_script(text
[i
])) {
1069 if (*is_simple
&& indices
) {
1070 UINT32
*codepoints
= heap_alloc(*len_read
*sizeof(UINT32
));
1072 return E_OUTOFMEMORY
;
1074 for (i
= 0; i
< *len_read
; i
++)
1075 codepoints
[i
] = text
[i
];
1077 hr
= IDWriteFontFace_GetGlyphIndices(face
, codepoints
, *len_read
, indices
);
1078 heap_free(codepoints
);
1084 static HRESULT WINAPI
dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2
*iface
,
1085 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
, UINT32 glyph_count
,
1086 const WCHAR
*text
, const UINT16
*clustermap
, const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
)
1088 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face
, font_em_size
, sa
.script
, length
, glyph_count
,
1089 debugstr_w(text
), clustermap
, prop
, jo
);
1093 static HRESULT WINAPI
dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2
*iface
,
1094 FLOAT width
, UINT32 glyph_count
, const DWRITE_JUSTIFICATION_OPPORTUNITY
*jo
, const FLOAT
*advances
,
1095 const DWRITE_GLYPH_OFFSET
*offsets
, FLOAT
*justifiedadvances
, DWRITE_GLYPH_OFFSET
*justifiedoffsets
)
1097 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width
, glyph_count
, jo
, advances
, offsets
, justifiedadvances
,
1102 static HRESULT WINAPI
dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2
*iface
,
1103 IDWriteFontFace
*face
, FLOAT font_em_size
, DWRITE_SCRIPT_ANALYSIS sa
, UINT32 length
,
1104 UINT32 glyph_count
, UINT32 max_glyphcount
, const UINT16
*clustermap
, const UINT16
*indices
,
1105 const FLOAT
*advances
, const FLOAT
*justifiedadvances
, const DWRITE_GLYPH_OFFSET
*justifiedoffsets
,
1106 const DWRITE_SHAPING_GLYPH_PROPERTIES
*prop
, UINT32
*actual_count
, UINT16
*modified_clustermap
,
1107 UINT16
*modified_indices
, FLOAT
*modified_advances
, DWRITE_GLYPH_OFFSET
*modified_offsets
)
1109 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
,
1110 length
, glyph_count
, max_glyphcount
, clustermap
, indices
, advances
, justifiedadvances
, justifiedoffsets
,
1111 prop
, actual_count
, modified_clustermap
, modified_indices
, modified_advances
, modified_offsets
);
1115 static HRESULT WINAPI
dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2
*iface
,
1116 DWRITE_GLYPH_ORIENTATION_ANGLE angle
, BOOL is_sideways
, FLOAT originX
, FLOAT originY
, DWRITE_MATRIX
*transform
)
1118 FIXME("(%d %d %.2f %.2f %p): stub\n", angle
, is_sideways
, originX
, originY
, transform
);
1122 static HRESULT WINAPI
dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2
*iface
,
1123 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1124 UINT32 max_tagcount
, UINT32
*actual_tagcount
, DWRITE_FONT_FEATURE_TAG
*tags
)
1126 FIXME("(%p %u %s %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), max_tagcount
, actual_tagcount
,
1131 static HRESULT WINAPI
dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2
*iface
,
1132 IDWriteFontFace
*face
, DWRITE_SCRIPT_ANALYSIS sa
, const WCHAR
*localeName
,
1133 DWRITE_FONT_FEATURE_TAG feature
, UINT32 glyph_count
, const UINT16
*indices
, UINT8
*feature_applies
)
1135 FIXME("(%p %u %s %x %u %p %p): stub\n", face
, sa
.script
, debugstr_w(localeName
), feature
, glyph_count
, indices
,
1140 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl
= {
1141 dwritetextanalyzer_QueryInterface
,
1142 dwritetextanalyzer_AddRef
,
1143 dwritetextanalyzer_Release
,
1144 dwritetextanalyzer_AnalyzeScript
,
1145 dwritetextanalyzer_AnalyzeBidi
,
1146 dwritetextanalyzer_AnalyzeNumberSubstitution
,
1147 dwritetextanalyzer_AnalyzeLineBreakpoints
,
1148 dwritetextanalyzer_GetGlyphs
,
1149 dwritetextanalyzer_GetGlyphPlacements
,
1150 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements
,
1151 dwritetextanalyzer1_ApplyCharacterSpacing
,
1152 dwritetextanalyzer1_GetBaseline
,
1153 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation
,
1154 dwritetextanalyzer1_GetGlyphOrientationTransform
,
1155 dwritetextanalyzer1_GetScriptProperties
,
1156 dwritetextanalyzer1_GetTextComplexity
,
1157 dwritetextanalyzer1_GetJustificationOpportunities
,
1158 dwritetextanalyzer1_JustifyGlyphAdvances
,
1159 dwritetextanalyzer1_GetJustifiedGlyphs
,
1160 dwritetextanalyzer2_GetGlyphOrientationTransform
,
1161 dwritetextanalyzer2_GetTypographicFeatures
,
1162 dwritetextanalyzer2_CheckTypographicFeature
1165 static IDWriteTextAnalyzer2 textanalyzer
= { &textanalyzervtbl
};
1167 HRESULT
get_textanalyzer(IDWriteTextAnalyzer
**ret
)
1169 *ret
= (IDWriteTextAnalyzer
*)&textanalyzer
;
1173 static HRESULT WINAPI
dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution
*iface
, REFIID riid
, void **obj
)
1175 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1177 TRACE("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), obj
);
1179 if (IsEqualIID(riid
, &IID_IDWriteNumberSubstitution
) ||
1180 IsEqualIID(riid
, &IID_IUnknown
))
1183 IDWriteNumberSubstitution_AddRef(iface
);
1189 return E_NOINTERFACE
;
1192 static ULONG WINAPI
dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution
*iface
)
1194 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1195 ULONG ref
= InterlockedIncrement(&This
->ref
);
1196 TRACE("(%p)->(%d)\n", This
, ref
);
1200 static ULONG WINAPI
dwritenumbersubstitution_Release(IDWriteNumberSubstitution
*iface
)
1202 struct dwrite_numbersubstitution
*This
= impl_from_IDWriteNumberSubstitution(iface
);
1203 ULONG ref
= InterlockedDecrement(&This
->ref
);
1205 TRACE("(%p)->(%d)\n", This
, ref
);
1208 heap_free(This
->locale
);
1215 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl
= {
1216 dwritenumbersubstitution_QueryInterface
,
1217 dwritenumbersubstitution_AddRef
,
1218 dwritenumbersubstitution_Release
1221 HRESULT
create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method
, const WCHAR
*locale
,
1222 BOOL ignore_user_override
, IDWriteNumberSubstitution
**ret
)
1224 struct dwrite_numbersubstitution
*substitution
;
1228 if (method
< DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE
|| method
> DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
)
1229 return E_INVALIDARG
;
1231 if (method
!= DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
&& !IsValidLocaleName(locale
))
1232 return E_INVALIDARG
;
1234 substitution
= heap_alloc(sizeof(*substitution
));
1236 return E_OUTOFMEMORY
;
1238 substitution
->IDWriteNumberSubstitution_iface
.lpVtbl
= &numbersubstitutionvtbl
;
1239 substitution
->ref
= 1;
1240 substitution
->ignore_user_override
= ignore_user_override
;
1241 substitution
->method
= method
;
1242 substitution
->locale
= heap_strdupW(locale
);
1243 if (locale
&& !substitution
->locale
) {
1244 heap_free(substitution
);
1245 return E_OUTOFMEMORY
;
1248 *ret
= &substitution
->IDWriteNumberSubstitution_iface
;