2 * Glyph shaping support
4 * Copyright 2010 Aric Stewart for CodeWeavers
5 * Copyright 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 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
31 #ifdef WORDS_BIGENDIAN
32 #define GET_BE_DWORD(x) (x)
34 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
37 struct scriptshaping_cache
*create_scriptshaping_cache(void *context
, const struct shaping_font_ops
*font_ops
)
39 struct scriptshaping_cache
*cache
;
41 cache
= heap_alloc_zero(sizeof(*cache
));
45 cache
->font
= font_ops
;
46 cache
->context
= context
;
48 opentype_layout_scriptshaping_cache_init(cache
);
49 cache
->upem
= cache
->font
->get_font_upem(cache
->context
);
54 void release_scriptshaping_cache(struct scriptshaping_cache
*cache
)
59 cache
->font
->release_font_table(cache
->context
, cache
->gdef
.table
.context
);
60 cache
->font
->release_font_table(cache
->context
, cache
->gsub
.table
.context
);
61 cache
->font
->release_font_table(cache
->context
, cache
->gpos
.table
.context
);
65 static unsigned int shape_select_script(const struct scriptshaping_cache
*cache
, DWORD kind
, const DWORD
*scripts
,
66 unsigned int *script_index
)
68 static const DWORD fallback_scripts
[] =
70 DWRITE_MAKE_OPENTYPE_TAG('D','F','L','T'),
71 DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'),
72 DWRITE_MAKE_OPENTYPE_TAG('l','a','t','n'),
77 /* Passed scripts in ascending priority. */
78 while (scripts
&& *scripts
)
80 if ((script
= opentype_layout_find_script(cache
, kind
, *scripts
, script_index
)))
86 /* 'DFLT' -> 'dflt' -> 'latn' */
87 scripts
= fallback_scripts
;
90 if ((script
= opentype_layout_find_script(cache
, kind
, *scripts
, script_index
)))
98 static DWORD
shape_select_language(const struct scriptshaping_cache
*cache
, DWORD kind
, unsigned int script_index
,
99 DWORD language
, unsigned int *language_index
)
101 /* Specified language -> 'dflt'. */
102 if ((language
= opentype_layout_find_language(cache
, kind
, language
, script_index
, language_index
)))
105 if ((language
= opentype_layout_find_language(cache
, kind
, DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'),
106 script_index
, language_index
)))
112 static void shape_add_feature_full(struct shaping_features
*features
, unsigned int tag
, unsigned int flags
, unsigned int value
)
114 unsigned int i
= features
->count
;
116 if (!dwrite_array_reserve((void **)&features
->features
, &features
->capacity
, features
->count
+ 1,
117 sizeof(*features
->features
)))
120 features
->features
[i
].tag
= tag
;
121 features
->features
[i
].flags
= flags
;
122 features
->features
[i
].max_value
= value
;
123 features
->features
[i
].default_value
= flags
& FEATURE_GLOBAL
? value
: 0;
124 features
->features
[i
].stage
= features
->stage
;
128 static void shape_add_feature(struct shaping_features
*features
, unsigned int tag
)
130 shape_add_feature_full(features
, tag
, FEATURE_GLOBAL
, 1);
133 static int features_sorting_compare(const void *a
, const void *b
)
135 const struct shaping_feature
*left
= a
, *right
= b
;
136 return left
->tag
!= right
->tag
? (left
->tag
< right
->tag
? -1 : 1) : 0;
139 static void shape_merge_features(struct scriptshaping_context
*context
, struct shaping_features
*features
)
141 const DWRITE_TYPOGRAPHIC_FEATURES
**user_features
= context
->user_features
.features
;
144 /* For now only consider global, enabled user features. */
145 if (user_features
&& context
->user_features
.range_lengths
)
147 unsigned int flags
= context
->user_features
.range_count
== 1 &&
148 context
->user_features
.range_lengths
[0] == context
->length
? FEATURE_GLOBAL
: 0;
150 for (i
= 0; i
< context
->user_features
.range_count
; ++i
)
152 for (j
= 0; j
< user_features
[i
]->featureCount
; ++j
)
153 shape_add_feature_full(features
, user_features
[i
]->features
[j
].nameTag
, flags
,
154 user_features
[i
]->features
[j
].parameter
);
158 /* Sort and merge duplicates. */
159 qsort(features
->features
, features
->count
, sizeof(*features
->features
), features_sorting_compare
);
161 for (i
= 1, j
= 0; i
< features
->count
; ++i
)
163 if (features
->features
[i
].tag
!= features
->features
[j
].tag
)
164 features
->features
[++j
] = features
->features
[i
];
167 if (features
->features
[i
].flags
& FEATURE_GLOBAL
)
169 features
->features
[j
].flags
|= FEATURE_GLOBAL
;
170 features
->features
[j
].max_value
= features
->features
[i
].max_value
;
171 features
->features
[j
].default_value
= features
->features
[i
].default_value
;
175 if (features
->features
[j
].flags
& FEATURE_GLOBAL
)
176 features
->features
[j
].flags
^= FEATURE_GLOBAL
;
177 features
->features
[j
].max_value
= max(features
->features
[j
].max_value
, features
->features
[i
].max_value
);
179 features
->features
[j
].stage
= min(features
->features
[j
].stage
, features
->features
[i
].stage
);
182 features
->count
= j
+ 1;
185 HRESULT
shape_get_positions(struct scriptshaping_context
*context
, const unsigned int *scripts
)
187 static const unsigned int common_features
[] =
189 DWRITE_MAKE_OPENTYPE_TAG('a','b','v','m'),
190 DWRITE_MAKE_OPENTYPE_TAG('b','l','w','m'),
191 DWRITE_MAKE_OPENTYPE_TAG('m','a','r','k'),
192 DWRITE_MAKE_OPENTYPE_TAG('m','k','m','k'),
194 static const unsigned int horizontal_features
[] =
196 DWRITE_MAKE_OPENTYPE_TAG('c','u','r','s'),
197 DWRITE_MAKE_OPENTYPE_TAG('d','i','s','t'),
198 DWRITE_MAKE_OPENTYPE_TAG('k','e','r','n'),
200 struct scriptshaping_cache
*cache
= context
->cache
;
201 unsigned int script_index
, language_index
, script
, i
;
202 struct shaping_features features
= { 0 };
204 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
205 shape_add_feature(&features
, common_features
[i
]);
207 /* Horizontal features */
208 if (!context
->is_sideways
)
210 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
211 shape_add_feature(&features
, horizontal_features
[i
]);
214 shape_merge_features(context
, &features
);
216 /* Resolve script tag to actually supported script. */
217 if (cache
->gpos
.table
.data
)
219 if ((script
= shape_select_script(cache
, MS_GPOS_TAG
, scripts
, &script_index
)))
221 DWORD language
= context
->language_tag
;
223 if ((language
= shape_select_language(cache
, MS_GPOS_TAG
, script_index
, language
, &language_index
)))
225 TRACE("script %s, language %s.\n", debugstr_tag(script
), language
!= ~0u ?
226 debugstr_tag(language
) : "deflangsys");
227 opentype_layout_apply_gpos_features(context
, script_index
, language_index
, &features
);
232 for (i
= 0; i
< context
->glyph_count
; ++i
)
233 if (context
->u
.pos
.glyph_props
[i
].isZeroWidthSpace
)
234 context
->advances
[i
] = 0.0f
;
236 heap_free(features
.features
);
241 static unsigned int shape_get_script_lang_index(struct scriptshaping_context
*context
, const unsigned int *scripts
,
242 unsigned int table
, unsigned int *script_index
, unsigned int *language_index
)
246 /* Resolve script tag to actually supported script. */
247 if ((script
= shape_select_script(context
->cache
, table
, scripts
, script_index
)))
249 unsigned int language
= context
->language_tag
;
251 if ((language
= shape_select_language(context
->cache
, table
, *script_index
, language
, language_index
)))
258 HRESULT
shape_get_glyphs(struct scriptshaping_context
*context
, const unsigned int *scripts
)
260 static const unsigned int common_features
[] =
262 DWRITE_MAKE_OPENTYPE_TAG('c','c','m','p'),
263 DWRITE_MAKE_OPENTYPE_TAG('l','o','c','l'),
264 DWRITE_MAKE_OPENTYPE_TAG('r','l','i','g'),
266 static const unsigned int horizontal_features
[] =
268 DWRITE_MAKE_OPENTYPE_TAG('c','a','l','t'),
269 DWRITE_MAKE_OPENTYPE_TAG('c','l','i','g'),
270 DWRITE_MAKE_OPENTYPE_TAG('l','i','g','a'),
271 DWRITE_MAKE_OPENTYPE_TAG('r','c','l','t'),
273 unsigned int script_index
, language_index
;
274 struct shaping_features features
= { 0 };
277 if (!context
->is_sideways
)
281 shape_add_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','a'));
282 shape_add_feature_full(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), 0, 1);
286 shape_add_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','a'));
287 shape_add_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','m'));
291 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
292 shape_add_feature(&features
, common_features
[i
]);
294 /* Horizontal features */
295 if (!context
->is_sideways
)
297 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
298 shape_add_feature(&features
, horizontal_features
[i
]);
301 shape_add_feature_full(&features
, DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t'), FEATURE_GLOBAL
| FEATURE_GLOBAL_SEARCH
, 1);
303 shape_merge_features(context
, &features
);
305 /* Resolve script tag to actually supported script. */
306 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
307 opentype_layout_apply_gsub_features(context
, script_index
, language_index
, &features
);
309 heap_free(features
.features
);
311 return (context
->glyph_count
<= context
->u
.subst
.max_glyph_count
) ? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
314 static int tag_array_sorting_compare(const void *a
, const void *b
)
316 unsigned int left
= GET_BE_DWORD(*(unsigned int *)a
), right
= GET_BE_DWORD(*(unsigned int *)b
);
317 return left
!= right
? (left
< right
? -1 : 1) : 0;
320 HRESULT
shape_get_typographic_features(struct scriptshaping_context
*context
, const unsigned int *scripts
,
321 unsigned int max_tagcount
, unsigned int *actual_tagcount
, unsigned int *tags
)
323 unsigned int i
, j
, script_index
, language_index
;
324 struct tag_array t
= { 0 };
326 /* Collect from both tables, sort and remove duplicates. */
328 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
329 opentype_get_typographic_features(&context
->cache
->gsub
, script_index
, language_index
, &t
);
331 shape_get_script_lang_index(context
, scripts
, MS_GPOS_TAG
, &script_index
, &language_index
);
332 opentype_get_typographic_features(&context
->cache
->gpos
, script_index
, language_index
, &t
);
334 /* Sort and remove duplicates. */
335 qsort(t
.tags
, t
.count
, sizeof(*t
.tags
), tag_array_sorting_compare
);
337 for (i
= 1, j
= 0; i
< t
.count
; ++i
)
339 if (t
.tags
[i
] != t
.tags
[j
])
340 t
.tags
[++j
] = t
.tags
[i
];
344 if (t
.count
<= max_tagcount
)
345 memcpy(tags
, t
.tags
, t
.count
* sizeof(*t
.tags
));
347 *actual_tagcount
= t
.count
;
351 return t
.count
<= max_tagcount
? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
354 HRESULT
shape_check_typographic_feature(struct scriptshaping_context
*context
, const unsigned int *scripts
,
355 unsigned int tag
, unsigned int glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
357 static const unsigned int tables
[] = { MS_GSUB_TAG
, MS_GPOS_TAG
};
358 struct shaping_feature feature
= { .tag
= tag
};
359 unsigned int script_index
, language_index
;
362 memset(feature_applies
, 0, glyph_count
* sizeof(*feature_applies
));
364 for (i
= 0; i
< ARRAY_SIZE(tables
); ++i
)
366 shape_get_script_lang_index(context
, scripts
, tables
[i
], &script_index
, &language_index
);
367 context
->table
= tables
[i
] == MS_GSUB_TAG
? &context
->cache
->gsub
: &context
->cache
->gpos
;
368 /* Skip second table if feature applies to all. */
369 if (opentype_layout_check_feature(context
, script_index
, language_index
, &feature
, glyph_count
,
370 glyphs
, feature_applies
))