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 const struct shaper null_shaper
;
205 static void shape_set_shaper(struct scriptshaping_context
*context
)
207 switch (context
->script
)
211 context
->shaper
= &arabic_shaper
;
214 context
->shaper
= &null_shaper
;
218 HRESULT
shape_get_positions(struct scriptshaping_context
*context
, const unsigned int *scripts
)
220 static const struct shaping_feature common_features
[] =
222 { DWRITE_MAKE_OPENTYPE_TAG('a','b','v','m') },
223 { DWRITE_MAKE_OPENTYPE_TAG('b','l','w','m') },
224 { DWRITE_MAKE_OPENTYPE_TAG('m','a','r','k'), FEATURE_MANUAL_JOINERS
},
225 { DWRITE_MAKE_OPENTYPE_TAG('m','k','m','k'), FEATURE_MANUAL_JOINERS
},
227 static const unsigned int horizontal_features
[] =
229 DWRITE_MAKE_OPENTYPE_TAG('c','u','r','s'),
230 DWRITE_MAKE_OPENTYPE_TAG('d','i','s','t'),
231 DWRITE_MAKE_OPENTYPE_TAG('k','e','r','n'),
233 struct scriptshaping_cache
*cache
= context
->cache
;
234 unsigned int script_index
, language_index
, script
, i
;
235 struct shaping_features features
= { 0 };
237 shape_set_shaper(context
);
239 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
240 shape_add_feature_full(&features
, common_features
[i
].tag
, FEATURE_GLOBAL
| common_features
[i
].flags
, 1);
242 /* Horizontal features */
243 if (!context
->is_sideways
)
245 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
246 shape_add_feature(&features
, horizontal_features
[i
]);
249 shape_merge_features(context
, &features
);
251 /* Resolve script tag to actually supported script. */
252 if (cache
->gpos
.table
.data
)
254 if ((script
= shape_select_script(cache
, MS_GPOS_TAG
, scripts
, &script_index
)))
256 DWORD language
= context
->language_tag
;
258 if ((language
= shape_select_language(cache
, MS_GPOS_TAG
, script_index
, language
, &language_index
)))
260 TRACE("script %s, language %s.\n", debugstr_tag(script
), language
!= ~0u ?
261 debugstr_tag(language
) : "deflangsys");
262 opentype_layout_apply_gpos_features(context
, script_index
, language_index
, &features
);
267 for (i
= 0; i
< context
->glyph_count
; ++i
)
268 if (context
->u
.pos
.glyph_props
[i
].isZeroWidthSpace
)
269 context
->advances
[i
] = 0.0f
;
271 free(features
.features
);
276 static unsigned int shape_get_script_lang_index(struct scriptshaping_context
*context
, const unsigned int *scripts
,
277 unsigned int table
, unsigned int *script_index
, unsigned int *language_index
)
281 /* Resolve script tag to actually supported script. */
282 if ((script
= shape_select_script(context
->cache
, table
, scripts
, script_index
)))
284 unsigned int language
= context
->language_tag
;
286 if ((language
= shape_select_language(context
->cache
, table
, *script_index
, language
, language_index
)))
293 HRESULT
shape_get_glyphs(struct scriptshaping_context
*context
, const unsigned int *scripts
)
295 static const unsigned int common_features
[] =
297 DWRITE_MAKE_OPENTYPE_TAG('c','c','m','p'),
298 DWRITE_MAKE_OPENTYPE_TAG('l','o','c','l'),
299 DWRITE_MAKE_OPENTYPE_TAG('r','l','i','g'),
301 static const unsigned int horizontal_features
[] =
303 DWRITE_MAKE_OPENTYPE_TAG('c','a','l','t'),
304 DWRITE_MAKE_OPENTYPE_TAG('c','l','i','g'),
305 DWRITE_MAKE_OPENTYPE_TAG('l','i','g','a'),
306 DWRITE_MAKE_OPENTYPE_TAG('r','c','l','t'),
308 unsigned int script_index
, language_index
;
309 struct shaping_features features
= { 0 };
312 shape_set_shaper(context
);
314 if (!context
->is_sideways
)
318 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','a'), 0);
319 shape_add_feature_full(&features
, DWRITE_MAKE_OPENTYPE_TAG('r','t','l','m'), 0, 1);
323 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','a'), 0);
324 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('l','t','r','m'), 0);
328 if (context
->shaper
->collect_features
)
329 context
->shaper
->collect_features(context
, &features
);
331 for (i
= 0; i
< ARRAY_SIZE(common_features
); ++i
)
332 shape_add_feature(&features
, common_features
[i
]);
334 /* Horizontal features */
335 if (!context
->is_sideways
)
337 for (i
= 0; i
< ARRAY_SIZE(horizontal_features
); ++i
)
338 shape_add_feature(&features
, horizontal_features
[i
]);
341 shape_enable_feature(&features
, DWRITE_MAKE_OPENTYPE_TAG('v','e','r','t'), FEATURE_GLOBAL_SEARCH
);
343 shape_merge_features(context
, &features
);
345 /* Resolve script tag to actually supported script. */
346 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
347 opentype_layout_apply_gsub_features(context
, script_index
, language_index
, &features
);
349 free(features
.features
);
351 return (context
->glyph_count
<= context
->u
.subst
.max_glyph_count
) ? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
354 static int __cdecl
tag_array_sorting_compare(const void *a
, const void *b
)
356 unsigned int left
= GET_BE_DWORD(*(unsigned int *)a
), right
= GET_BE_DWORD(*(unsigned int *)b
);
357 return left
!= right
? (left
< right
? -1 : 1) : 0;
360 HRESULT
shape_get_typographic_features(struct scriptshaping_context
*context
, const unsigned int *scripts
,
361 unsigned int max_tagcount
, unsigned int *actual_tagcount
, unsigned int *tags
)
363 unsigned int i
, j
, script_index
, language_index
;
364 struct tag_array t
= { 0 };
366 /* Collect from both tables, sort and remove duplicates. */
368 shape_get_script_lang_index(context
, scripts
, MS_GSUB_TAG
, &script_index
, &language_index
);
369 opentype_get_typographic_features(&context
->cache
->gsub
, script_index
, language_index
, &t
);
371 shape_get_script_lang_index(context
, scripts
, MS_GPOS_TAG
, &script_index
, &language_index
);
372 opentype_get_typographic_features(&context
->cache
->gpos
, script_index
, language_index
, &t
);
376 *actual_tagcount
= 0;
380 /* Sort and remove duplicates. */
381 qsort(t
.tags
, t
.count
, sizeof(*t
.tags
), tag_array_sorting_compare
);
383 for (i
= 1, j
= 0; i
< t
.count
; ++i
)
385 if (t
.tags
[i
] != t
.tags
[j
])
386 t
.tags
[++j
] = t
.tags
[i
];
390 if (t
.count
<= max_tagcount
)
391 memcpy(tags
, t
.tags
, t
.count
* sizeof(*t
.tags
));
393 *actual_tagcount
= t
.count
;
397 return t
.count
<= max_tagcount
? S_OK
: E_NOT_SUFFICIENT_BUFFER
;
400 HRESULT
shape_check_typographic_feature(struct scriptshaping_context
*context
, const unsigned int *scripts
,
401 unsigned int tag
, unsigned int glyph_count
, const UINT16
*glyphs
, UINT8
*feature_applies
)
403 static const unsigned int tables
[] = { MS_GSUB_TAG
, MS_GPOS_TAG
};
404 struct shaping_feature feature
= { .tag
= tag
};
405 unsigned int script_index
, language_index
;
408 memset(feature_applies
, 0, glyph_count
* sizeof(*feature_applies
));
410 for (i
= 0; i
< ARRAY_SIZE(tables
); ++i
)
412 shape_get_script_lang_index(context
, scripts
, tables
[i
], &script_index
, &language_index
);
413 context
->table
= tables
[i
] == MS_GSUB_TAG
? &context
->cache
->gsub
: &context
->cache
->gpos
;
414 /* Skip second table if feature applies to all. */
415 if (opentype_layout_check_feature(context
, script_index
, language_index
, &feature
, glyph_count
,
416 glyphs
, feature_applies
))