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 cache
= heap_alloc_zero(sizeof(*cache
));
51 cache
->font
= font_ops
;
52 cache
->context
= context
;
54 opentype_layout_scriptshaping_cache_init(cache
);
55 cache
->upem
= cache
->font
->get_font_upem(cache
->context
);
60 void release_scriptshaping_cache(struct scriptshaping_cache
*cache
)
65 cache
->font
->release_font_table(cache
->context
, cache
->gdef
.table
.context
);
66 cache
->font
->release_font_table(cache
->context
, cache
->gsub
.table
.context
);
67 cache
->font
->release_font_table(cache
->context
, cache
->gpos
.table
.context
);
71 static unsigned int shape_select_script(const struct scriptshaping_cache
*cache
, DWORD kind
, const DWORD
*scripts
,
72 unsigned int *script_index
)
74 static const DWORD fallback_scripts
[] =
76 DWRITE_MAKE_OPENTYPE_TAG('D','F','L','T'),
77 DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'),
78 DWRITE_MAKE_OPENTYPE_TAG('l','a','t','n'),
83 /* Passed scripts in ascending priority. */
84 while (scripts
&& *scripts
)
86 if ((script
= opentype_layout_find_script(cache
, kind
, *scripts
, script_index
)))
92 /* 'DFLT' -> 'dflt' -> 'latn' */
93 scripts
= fallback_scripts
;
96 if ((script
= opentype_layout_find_script(cache
, kind
, *scripts
, script_index
)))
104 static DWORD
shape_select_language(const struct scriptshaping_cache
*cache
, DWORD kind
, unsigned int script_index
,
105 DWORD language
, unsigned int *language_index
)
107 /* Specified language -> 'dflt'. */
108 if ((language
= opentype_layout_find_language(cache
, kind
, language
, script_index
, language_index
)))
111 if ((language
= opentype_layout_find_language(cache
, kind
, DWRITE_MAKE_OPENTYPE_TAG('d','f','l','t'),
112 script_index
, language_index
)))
118 void shape_add_feature_full(struct shaping_features
*features
, unsigned int tag
, unsigned int flags
, unsigned int value
)
120 unsigned int i
= features
->count
;
122 if (!dwrite_array_reserve((void **)&features
->features
, &features
->capacity
, features
->count
+ 1,
123 sizeof(*features
->features
)))
126 features
->features
[i
].tag
= tag
;
127 features
->features
[i
].flags
= flags
;
128 features
->features
[i
].max_value
= value
;
129 features
->features
[i
].default_value
= flags
& FEATURE_GLOBAL
? value
: 0;
130 features
->features
[i
].stage
= features
->stage
;
134 static void shape_add_feature(struct shaping_features
*features
, unsigned int tag
)
136 shape_add_feature_full(features
, tag
, FEATURE_GLOBAL
, 1);
139 void shape_enable_feature(struct shaping_features
*features
, unsigned int tag
,
142 shape_add_feature_full(features
, tag
, FEATURE_GLOBAL
| flags
, 1);
145 void shape_start_next_stage(struct shaping_features
*features
, stage_func func
)
147 features
->stages
[features
->stage
].func
= func
;
151 static int __cdecl
features_sorting_compare(const void *a
, const void *b
)
153 const struct shaping_feature
*left
= a
, *right
= b
;
154 return left
->tag
!= right
->tag
? (left
->tag
< right
->tag
? -1 : 1) : 0;
157 static void shape_merge_features(struct scriptshaping_context
*context
, struct shaping_features
*features
)
159 const DWRITE_TYPOGRAPHIC_FEATURES
**user_features
= context
->user_features
.features
;
162 /* For now only consider global, enabled user features. */
163 if (user_features
&& context
->user_features
.range_lengths
)
165 unsigned int flags
= context
->user_features
.range_count
== 1 &&
166 context
->user_features
.range_lengths
[0] == context
->length
? FEATURE_GLOBAL
: 0;
168 for (i
= 0; i
< context
->user_features
.range_count
; ++i
)
170 for (j
= 0; j
< user_features
[i
]->featureCount
; ++j
)
171 shape_add_feature_full(features
, user_features
[i
]->features
[j
].nameTag
, flags
,
172 user_features
[i
]->features
[j
].parameter
);
176 /* Sort and merge duplicates. */
177 qsort(features
->features
, features
->count
, sizeof(*features
->features
), features_sorting_compare
);
179 for (i
= 1, j
= 0; i
< features
->count
; ++i
)
181 if (features
->features
[i
].tag
!= features
->features
[j
].tag
)
182 features
->features
[++j
] = features
->features
[i
];
185 if (features
->features
[i
].flags
& FEATURE_GLOBAL
)
187 features
->features
[j
].flags
|= FEATURE_GLOBAL
;
188 features
->features
[j
].max_value
= features
->features
[i
].max_value
;
189 features
->features
[j
].default_value
= features
->features
[i
].default_value
;
193 if (features
->features
[j
].flags
& FEATURE_GLOBAL
)
194 features
->features
[j
].flags
^= FEATURE_GLOBAL
;
195 features
->features
[j
].max_value
= max(features
->features
[j
].max_value
, features
->features
[i
].max_value
);
197 features
->features
[j
].flags
|= features
->features
[i
].flags
& FEATURE_HAS_FALLBACK
;
198 features
->features
[j
].stage
= min(features
->features
[j
].stage
, features
->features
[i
].stage
);
201 features
->count
= j
+ 1;
204 static const struct shaper null_shaper
;
206 static void shape_set_shaper(struct scriptshaping_context
*context
)
208 switch (context
->script
)
212 context
->shaper
= &arabic_shaper
;
215 context
->shaper
= &null_shaper
;
219 HRESULT
shape_get_positions(struct scriptshaping_context
*context
, const unsigned int *scripts
)
221 static const struct shaping_feature common_features
[] =
223 { DWRITE_MAKE_OPENTYPE_TAG('a','b','v','m') },
224 { DWRITE_MAKE_OPENTYPE_TAG('b','l','w','m') },
225 { DWRITE_MAKE_OPENTYPE_TAG('m','a','r','k'), FEATURE_MANUAL_JOINERS
},
226 { DWRITE_MAKE_OPENTYPE_TAG('m','k','m','k'), FEATURE_MANUAL_JOINERS
},
228 static const unsigned int horizontal_features
[] =
230 DWRITE_MAKE_OPENTYPE_TAG('c','u','r','s'),
231 DWRITE_MAKE_OPENTYPE_TAG('d','i','s','t'),
232 DWRITE_MAKE_OPENTYPE_TAG('k','e','r','n'),
234 struct scriptshaping_cache
*cache
= context
->cache
;
235 unsigned int script_index
, language_index
, script
, i
;
236 struct shaping_features features
= { 0 };
238 shape_set_shaper(context
);
240 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
241 shape_add_feature_full(&features
, common_features
[i
].tag
, FEATURE_GLOBAL
| common_features
[i
].flags
, 1);
243 /* Horizontal features */
244 if (!context
->is_sideways
)
246 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
247 shape_add_feature(&features
, horizontal_features
[i
]);
250 shape_merge_features(context
, &features
);
252 /* Resolve script tag to actually supported script. */
253 if (cache
->gpos
.table
.data
)
255 if ((script
= shape_select_script(cache
, MS_GPOS_TAG
, scripts
, &script_index
)))
257 DWORD language
= context
->language_tag
;
259 if ((language
= shape_select_language(cache
, MS_GPOS_TAG
, script_index
, language
, &language_index
)))
261 TRACE("script %s, language %s.\n", debugstr_tag(script
), language
!= ~0u ?
262 debugstr_tag(language
) : "deflangsys");
263 opentype_layout_apply_gpos_features(context
, script_index
, language_index
, &features
);
268 for (i
= 0; i
< context
->glyph_count
; ++i
)
269 if (context
->u
.pos
.glyph_props
[i
].isZeroWidthSpace
)
270 context
->advances
[i
] = 0.0f
;
272 heap_free(features
.features
);
277 static unsigned int shape_get_script_lang_index(struct scriptshaping_context
*context
, const unsigned int *scripts
,
278 unsigned int table
, unsigned int *script_index
, unsigned int *language_index
)
282 /* Resolve script tag to actually supported script. */
283 if ((script
= shape_select_script(context
->cache
, table
, scripts
, script_index
)))
285 unsigned int language
= context
->language_tag
;
287 if ((language
= shape_select_language(context
->cache
, table
, *script_index
, language
, language_index
)))
294 HRESULT
shape_get_glyphs(struct scriptshaping_context
*context
, const unsigned int *scripts
)
296 static const unsigned int common_features
[] =
298 DWRITE_MAKE_OPENTYPE_TAG('c','c','m','p'),
299 DWRITE_MAKE_OPENTYPE_TAG('l','o','c','l'),
300 DWRITE_MAKE_OPENTYPE_TAG('r','l','i','g'),
302 static const unsigned int horizontal_features
[] =
304 DWRITE_MAKE_OPENTYPE_TAG('c','a','l','t'),
305 DWRITE_MAKE_OPENTYPE_TAG('c','l','i','g'),
306 DWRITE_MAKE_OPENTYPE_TAG('l','i','g','a'),
307 DWRITE_MAKE_OPENTYPE_TAG('r','c','l','t'),
309 unsigned int script_index
, language_index
;
310 struct shaping_features features
= { 0 };
313 shape_set_shaper(context
);
315 if (!context
->is_sideways
)
319 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','a'), 0);
320 shape_add_feature_full(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), 0, 1);
324 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','a'), 0);
325 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','m'), 0);
329 if (context
->shaper
->collect_features
)
330 context
->shaper
->collect_features(context
, &features
);
332 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
333 shape_add_feature(&features
, common_features
[i
]);
335 /* Horizontal features */
336 if (!context
->is_sideways
)
338 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
339 shape_add_feature(&features
, horizontal_features
[i
]);
342 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t'), FEATURE_GLOBAL_SEARCH
);
344 shape_merge_features(context
, &features
);
346 /* Resolve script tag to actually supported script. */
347 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
348 opentype_layout_apply_gsub_features(context
, script_index
, language_index
, &features
);
350 heap_free(features
.features
);
352 return (context
->glyph_count
<= context
->u
.subst
.max_glyph_count
) ? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
355 static int __cdecl
tag_array_sorting_compare(const void *a
, const void *b
)
357 unsigned int left
= GET_BE_DWORD(*(unsigned int *)a
), right
= GET_BE_DWORD(*(unsigned int *)b
);
358 return left
!= right
? (left
< right
? -1 : 1) : 0;
361 HRESULT
shape_get_typographic_features(struct scriptshaping_context
*context
, const unsigned int *scripts
,
362 unsigned int max_tagcount
, unsigned int *actual_tagcount
, unsigned int *tags
)
364 unsigned int i
, j
, script_index
, language_index
;
365 struct tag_array t
= { 0 };
367 /* Collect from both tables, sort and remove duplicates. */
369 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
370 opentype_get_typographic_features(&context
->cache
->gsub
, script_index
, language_index
, &t
);
372 shape_get_script_lang_index(context
, scripts
, MS_GPOS_TAG
, &script_index
, &language_index
);
373 opentype_get_typographic_features(&context
->cache
->gpos
, script_index
, language_index
, &t
);
377 *actual_tagcount
= 0;
381 /* Sort and remove duplicates. */
382 qsort(t
.tags
, t
.count
, sizeof(*t
.tags
), tag_array_sorting_compare
);
384 for (i
= 1, j
= 0; i
< t
.count
; ++i
)
386 if (t
.tags
[i
] != t
.tags
[j
])
387 t
.tags
[++j
] = t
.tags
[i
];
391 if (t
.count
<= max_tagcount
)
392 memcpy(tags
, t
.tags
, t
.count
* sizeof(*t
.tags
));
394 *actual_tagcount
= t
.count
;
398 return t
.count
<= max_tagcount
? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
401 HRESULT
shape_check_typographic_feature(struct scriptshaping_context
*context
, const unsigned int *scripts
,
402 unsigned int tag
, unsigned int glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
404 static const unsigned int tables
[] = { MS_GSUB_TAG
, MS_GPOS_TAG
};
405 struct shaping_feature feature
= { .tag
= tag
};
406 unsigned int script_index
, language_index
;
409 memset(feature_applies
, 0, glyph_count
* sizeof(*feature_applies
));
411 for (i
= 0; i
< ARRAY_SIZE(tables
); ++i
)
413 shape_get_script_lang_index(context
, scripts
, tables
[i
], &script_index
, &language_index
);
414 context
->table
= tables
[i
] == MS_GSUB_TAG
? &context
->cache
->gsub
: &context
->cache
->gpos
;
415 /* Skip second table if feature applies to all. */
416 if (opentype_layout_check_feature(context
, script_index
, language_index
, &feature
, glyph_count
,
417 glyphs
, feature_applies
))