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
22 * Shaping features processing, default features, and lookups handling logic are
23 * derived from HarfBuzz implementation. Consult project documentation for the full
24 * list of its authors.
29 #include "dwrite_private.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
37 #ifdef WORDS_BIGENDIAN
38 #define GET_BE_DWORD(x) (x)
40 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
43 struct scriptshaping_cache
*create_scriptshaping_cache(void *context
, const struct shaping_font_ops
*font_ops
)
45 struct scriptshaping_cache
*cache
;
47 if (!(cache
= calloc(1, sizeof(*cache
))))
50 cache
->font
= font_ops
;
51 cache
->context
= context
;
53 opentype_layout_scriptshaping_cache_init(cache
);
54 cache
->upem
= cache
->font
->get_font_upem(cache
->context
);
59 void release_scriptshaping_cache(struct scriptshaping_cache
*cache
)
64 cache
->font
->release_font_table(cache
->context
, cache
->gdef
.table
.context
);
65 cache
->font
->release_font_table(cache
->context
, cache
->gsub
.table
.context
);
66 cache
->font
->release_font_table(cache
->context
, cache
->gpos
.table
.context
);
70 static unsigned int shape_select_script(const struct scriptshaping_cache
*cache
, DWORD kind
, const unsigned int *scripts
,
71 unsigned int *script_index
)
73 static const unsigned int fallback_scripts
[] =
75 DWRITE_MAKE_OPENTYPE_TAG('D','F','L','T'),
76 DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'),
77 DWRITE_MAKE_OPENTYPE_TAG('l','a','t','n'),
82 /* Passed scripts in ascending priority. */
83 while (scripts
&& *scripts
)
85 if ((script
= opentype_layout_find_script(cache
, kind
, *scripts
, script_index
)))
91 /* 'DFLT' -> 'dflt' -> 'latn' */
92 scripts
= fallback_scripts
;
95 if ((script
= opentype_layout_find_script(cache
, kind
, *scripts
, script_index
)))
103 static DWORD
shape_select_language(const struct scriptshaping_cache
*cache
, DWORD kind
, unsigned int script_index
,
104 DWORD language
, unsigned int *language_index
)
106 /* Specified language -> 'dflt'. */
107 if ((language
= opentype_layout_find_language(cache
, kind
, language
, script_index
, language_index
)))
110 if ((language
= opentype_layout_find_language(cache
, kind
, DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'),
111 script_index
, language_index
)))
117 void shape_add_feature_full(struct shaping_features
*features
, unsigned int tag
, unsigned int flags
, unsigned int value
)
119 unsigned int i
= features
->count
;
121 if (!dwrite_array_reserve((void **)&features
->features
, &features
->capacity
, features
->count
+ 1,
122 sizeof(*features
->features
)))
125 features
->features
[i
].tag
= tag
;
126 features
->features
[i
].flags
= flags
;
127 features
->features
[i
].max_value
= value
;
128 features
->features
[i
].default_value
= flags
& FEATURE_GLOBAL
? value
: 0;
129 features
->features
[i
].stage
= features
->stage
;
133 static void shape_add_feature(struct shaping_features
*features
, unsigned int tag
)
135 shape_add_feature_full(features
, tag
, FEATURE_GLOBAL
, 1);
138 void shape_enable_feature(struct shaping_features
*features
, unsigned int tag
,
141 shape_add_feature_full(features
, tag
, FEATURE_GLOBAL
| flags
, 1);
144 void shape_start_next_stage(struct shaping_features
*features
, stage_func func
)
146 features
->stages
[features
->stage
].func
= func
;
150 static int __cdecl
features_sorting_compare(const void *a
, const void *b
)
152 const struct shaping_feature
*left
= a
, *right
= b
;
153 return left
->tag
!= right
->tag
? (left
->tag
< right
->tag
? -1 : 1) : 0;
156 static void shape_merge_features(struct scriptshaping_context
*context
, struct shaping_features
*features
)
158 const DWRITE_TYPOGRAPHIC_FEATURES
**user_features
= context
->user_features
.features
;
161 /* For now only consider global, enabled user features. */
162 if (user_features
&& context
->user_features
.range_lengths
)
164 unsigned int flags
= context
->user_features
.range_count
== 1 &&
165 context
->user_features
.range_lengths
[0] == context
->length
? FEATURE_GLOBAL
: 0;
167 for (i
= 0; i
< context
->user_features
.range_count
; ++i
)
169 for (j
= 0; j
< user_features
[i
]->featureCount
; ++j
)
170 shape_add_feature_full(features
, user_features
[i
]->features
[j
].nameTag
, flags
,
171 user_features
[i
]->features
[j
].parameter
);
175 /* Sort and merge duplicates. */
176 qsort(features
->features
, features
->count
, sizeof(*features
->features
), features_sorting_compare
);
178 for (i
= 1, j
= 0; i
< features
->count
; ++i
)
180 if (features
->features
[i
].tag
!= features
->features
[j
].tag
)
181 features
->features
[++j
] = features
->features
[i
];
184 if (features
->features
[i
].flags
& FEATURE_GLOBAL
)
186 features
->features
[j
].flags
|= FEATURE_GLOBAL
;
187 features
->features
[j
].max_value
= features
->features
[i
].max_value
;
188 features
->features
[j
].default_value
= features
->features
[i
].default_value
;
192 if (features
->features
[j
].flags
& FEATURE_GLOBAL
)
193 features
->features
[j
].flags
^= FEATURE_GLOBAL
;
194 features
->features
[j
].max_value
= max(features
->features
[j
].max_value
, features
->features
[i
].max_value
);
196 features
->features
[j
].flags
|= features
->features
[i
].flags
& FEATURE_HAS_FALLBACK
;
197 features
->features
[j
].stage
= min(features
->features
[j
].stage
, features
->features
[i
].stage
);
200 features
->count
= j
+ 1;
203 static void default_shaper_setup_masks(struct scriptshaping_context
*context
,
204 const struct shaping_features
*features
)
208 for (i
= 0; i
< context
->glyph_count
; ++i
)
210 context
->u
.buffer
.glyph_props
[i
].justification
= iswspace(context
->glyph_infos
[i
].codepoint
) ?
211 SCRIPT_JUSTIFY_BLANK
: SCRIPT_JUSTIFY_CHARACTER
;
215 static const struct shaper default_shaper
=
217 .setup_masks
= default_shaper_setup_masks
220 static void shape_set_shaper(struct scriptshaping_context
*context
)
222 switch (context
->script
)
226 context
->shaper
= &arabic_shaper
;
229 context
->shaper
= &default_shaper
;
233 HRESULT
shape_get_positions(struct scriptshaping_context
*context
, const unsigned int *scripts
)
235 static const struct shaping_feature common_features
[] =
237 { DWRITE_MAKE_OPENTYPE_TAG('a','b','v','m') },
238 { DWRITE_MAKE_OPENTYPE_TAG('b','l','w','m') },
239 { DWRITE_MAKE_OPENTYPE_TAG('m','a','r','k'), FEATURE_MANUAL_JOINERS
},
240 { DWRITE_MAKE_OPENTYPE_TAG('m','k','m','k'), FEATURE_MANUAL_JOINERS
},
242 static const unsigned int horizontal_features
[] =
244 DWRITE_MAKE_OPENTYPE_TAG('c','u','r','s'),
245 DWRITE_MAKE_OPENTYPE_TAG('d','i','s','t'),
246 DWRITE_MAKE_OPENTYPE_TAG('k','e','r','n'),
248 struct scriptshaping_cache
*cache
= context
->cache
;
249 unsigned int script_index
, language_index
, script
, i
;
250 struct shaping_features features
= { 0 };
252 shape_set_shaper(context
);
254 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
255 shape_add_feature_full(&features
, common_features
[i
].tag
, FEATURE_GLOBAL
| common_features
[i
].flags
, 1);
257 /* Horizontal features */
258 if (!context
->is_sideways
)
260 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
261 shape_add_feature(&features
, horizontal_features
[i
]);
264 shape_merge_features(context
, &features
);
266 /* Resolve script tag to actually supported script. */
267 if (cache
->gpos
.table
.data
)
269 if ((script
= shape_select_script(cache
, MS_GPOS_TAG
, scripts
, &script_index
)))
271 DWORD language
= context
->language_tag
;
273 if ((language
= shape_select_language(cache
, MS_GPOS_TAG
, script_index
, language
, &language_index
)))
275 TRACE("script %s, language %s.\n", debugstr_tag(script
), language
!= ~0u ?
276 debugstr_tag(language
) : "deflangsys");
277 opentype_layout_apply_gpos_features(context
, script_index
, language_index
, &features
);
282 for (i
= 0; i
< context
->glyph_count
; ++i
)
283 if (context
->u
.pos
.glyph_props
[i
].isZeroWidthSpace
)
284 context
->advances
[i
] = 0.0f
;
286 free(features
.features
);
291 static unsigned int shape_get_script_lang_index(struct scriptshaping_context
*context
, const unsigned int *scripts
,
292 unsigned int table
, unsigned int *script_index
, unsigned int *language_index
)
296 /* Resolve script tag to actually supported script. */
297 if ((script
= shape_select_script(context
->cache
, table
, scripts
, script_index
)))
299 unsigned int language
= context
->language_tag
;
301 if ((language
= shape_select_language(context
->cache
, table
, *script_index
, language
, language_index
)))
308 HRESULT
shape_get_glyphs(struct scriptshaping_context
*context
, const unsigned int *scripts
)
310 static const unsigned int common_features
[] =
312 DWRITE_MAKE_OPENTYPE_TAG('c','c','m','p'),
313 DWRITE_MAKE_OPENTYPE_TAG('l','o','c','l'),
314 DWRITE_MAKE_OPENTYPE_TAG('r','l','i','g'),
316 static const unsigned int horizontal_features
[] =
318 DWRITE_MAKE_OPENTYPE_TAG('c','a','l','t'),
319 DWRITE_MAKE_OPENTYPE_TAG('c','l','i','g'),
320 DWRITE_MAKE_OPENTYPE_TAG('l','i','g','a'),
321 DWRITE_MAKE_OPENTYPE_TAG('r','c','l','t'),
323 unsigned int script_index
, language_index
;
324 struct shaping_features features
= { 0 };
327 shape_set_shaper(context
);
329 if (!context
->is_sideways
)
333 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','a'), 0);
334 shape_add_feature_full(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), 0, 1);
338 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','a'), 0);
339 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','m'), 0);
343 if (context
->shaper
->collect_features
)
344 context
->shaper
->collect_features(context
, &features
);
346 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
347 shape_add_feature(&features
, common_features
[i
]);
349 /* Horizontal features */
350 if (!context
->is_sideways
)
352 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
353 shape_add_feature(&features
, horizontal_features
[i
]);
356 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t'), FEATURE_GLOBAL_SEARCH
);
358 shape_merge_features(context
, &features
);
360 /* Resolve script tag to actually supported script. */
361 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
362 opentype_layout_apply_gsub_features(context
, script_index
, language_index
, &features
);
364 free(features
.features
);
366 return (context
->glyph_count
<= context
->u
.subst
.max_glyph_count
) ? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
369 static int __cdecl
tag_array_sorting_compare(const void *a
, const void *b
)
371 unsigned int left
= GET_BE_DWORD(*(unsigned int *)a
), right
= GET_BE_DWORD(*(unsigned int *)b
);
372 return left
!= right
? (left
< right
? -1 : 1) : 0;
375 HRESULT
shape_get_typographic_features(struct scriptshaping_context
*context
, const unsigned int *scripts
,
376 unsigned int max_tagcount
, unsigned int *actual_tagcount
, unsigned int *tags
)
378 unsigned int i
, j
, script_index
, language_index
;
379 struct tag_array t
= { 0 };
381 /* Collect from both tables, sort and remove duplicates. */
383 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
384 opentype_get_typographic_features(&context
->cache
->gsub
, script_index
, language_index
, &t
);
386 shape_get_script_lang_index(context
, scripts
, MS_GPOS_TAG
, &script_index
, &language_index
);
387 opentype_get_typographic_features(&context
->cache
->gpos
, script_index
, language_index
, &t
);
391 *actual_tagcount
= 0;
395 /* Sort and remove duplicates. */
396 qsort(t
.tags
, t
.count
, sizeof(*t
.tags
), tag_array_sorting_compare
);
398 for (i
= 1, j
= 0; i
< t
.count
; ++i
)
400 if (t
.tags
[i
] != t
.tags
[j
])
401 t
.tags
[++j
] = t
.tags
[i
];
405 if (t
.count
<= max_tagcount
)
406 memcpy(tags
, t
.tags
, t
.count
* sizeof(*t
.tags
));
408 *actual_tagcount
= t
.count
;
412 return t
.count
<= max_tagcount
? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
415 HRESULT
shape_check_typographic_feature(struct scriptshaping_context
*context
, const unsigned int *scripts
,
416 unsigned int tag
, unsigned int glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
418 static const unsigned int tables
[] = { MS_GSUB_TAG
, MS_GPOS_TAG
};
419 struct shaping_feature feature
= { .tag
= tag
};
420 unsigned int script_index
, language_index
;
423 memset(feature_applies
, 0, glyph_count
* sizeof(*feature_applies
));
425 for (i
= 0; i
< ARRAY_SIZE(tables
); ++i
)
427 shape_get_script_lang_index(context
, scripts
, tables
[i
], &script_index
, &language_index
);
428 context
->table
= tables
[i
] == MS_GSUB_TAG
? &context
->cache
->gsub
: &context
->cache
->gpos
;
429 /* Skip second table if feature applies to all. */
430 if (opentype_layout_check_feature(context
, script_index
, language_index
, &feature
, glyph_count
,
431 glyphs
, feature_applies
))