1 : /*
2 : * Copyright © 1998-2004 David Turner and Werner Lemberg
3 : * Copyright © 2006 Behdad Esfahbod
4 : * Copyright © 2007,2008,2009 Red Hat, Inc.
5 : *
6 : * This is part of HarfBuzz, a text shaping library.
7 : *
8 : * Permission is hereby granted, without written agreement and without
9 : * license or royalty fees, to use, copy, modify, and distribute this
10 : * software and its documentation for any purpose, provided that the
11 : * above copyright notice and the following two paragraphs appear in
12 : * all copies of this software.
13 : *
14 : * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15 : * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16 : * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17 : * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 : * DAMAGE.
19 : *
20 : * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21 : * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 : * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 : * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24 : * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 : *
26 : * Red Hat Author(s): Behdad Esfahbod
27 : */
28 :
29 : #include "hb-ot-layout-private.hh"
30 :
31 : #include "hb-ot-layout-gdef-table.hh"
32 : #include "hb-ot-layout-gsub-table.hh"
33 : #include "hb-ot-layout-gpos-table.hh"
34 : #include "hb-ot-maxp-table.hh"
35 : #include "hb-ot-shape-private.hh"
36 :
37 :
38 : #include <stdlib.h>
39 : #include <string.h>
40 :
41 :
42 :
43 : hb_ot_layout_t *
44 0 : _hb_ot_layout_create (hb_face_t *face)
45 : {
46 : /* TODO Remove this object altogether */
47 0 : hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
48 :
49 0 : layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF));
50 0 : layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
51 :
52 0 : layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB));
53 0 : layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
54 :
55 0 : layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
56 0 : layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
57 :
58 0 : return layout;
59 : }
60 :
61 : void
62 0 : _hb_ot_layout_destroy (hb_ot_layout_t *layout)
63 : {
64 0 : hb_blob_destroy (layout->gdef_blob);
65 0 : hb_blob_destroy (layout->gsub_blob);
66 0 : hb_blob_destroy (layout->gpos_blob);
67 :
68 0 : free (layout);
69 0 : }
70 :
71 : static inline const GDEF&
72 0 : _get_gdef (hb_face_t *face)
73 : {
74 0 : return likely (face->ot_layout && face->ot_layout->gdef) ? *face->ot_layout->gdef : Null(GDEF);
75 : }
76 : static inline const GSUB&
77 0 : _get_gsub (hb_face_t *face)
78 : {
79 0 : return likely (face->ot_layout && face->ot_layout->gsub) ? *face->ot_layout->gsub : Null(GSUB);
80 : }
81 : static inline const GPOS&
82 0 : _get_gpos (hb_face_t *face)
83 : {
84 0 : return likely (face->ot_layout && face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS);
85 : }
86 :
87 :
88 : /*
89 : * GDEF
90 : */
91 :
92 : hb_bool_t
93 0 : hb_ot_layout_has_glyph_classes (hb_face_t *face)
94 : {
95 0 : return _get_gdef (face).has_glyph_classes ();
96 : }
97 :
98 : unsigned int
99 0 : _hb_ot_layout_get_glyph_property (hb_face_t *face,
100 : hb_glyph_info_t *info)
101 : {
102 0 : if (!info->props_cache())
103 : {
104 0 : const GDEF &gdef = _get_gdef (face);
105 0 : info->props_cache() = gdef.get_glyph_props (info->codepoint);
106 : }
107 :
108 0 : return info->props_cache();
109 : }
110 :
111 : static hb_bool_t
112 0 : _hb_ot_layout_match_properties (hb_face_t *face,
113 : hb_codepoint_t codepoint,
114 : unsigned int glyph_props,
115 : unsigned int lookup_props)
116 : {
117 : /* Not covered, if, for example, glyph class is ligature and
118 : * lookup_props includes LookupFlags::IgnoreLigatures
119 : */
120 0 : if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
121 0 : return false;
122 :
123 0 : if (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
124 : {
125 : /* If using mark filtering sets, the high short of
126 : * lookup_props has the set index.
127 : */
128 0 : if (lookup_props & LookupFlag::UseMarkFilteringSet)
129 0 : return _get_gdef (face).mark_set_covers (lookup_props >> 16, codepoint);
130 :
131 : /* The second byte of lookup_props has the meaning
132 : * "ignore marks of attachment type different than
133 : * the attachment type specified."
134 : */
135 0 : if (lookup_props & LookupFlag::MarkAttachmentType && glyph_props & LookupFlag::MarkAttachmentType)
136 0 : return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
137 : }
138 :
139 0 : return true;
140 : }
141 :
142 : hb_bool_t
143 0 : _hb_ot_layout_check_glyph_property (hb_face_t *face,
144 : hb_glyph_info_t *ginfo,
145 : unsigned int lookup_props,
146 : unsigned int *property_out)
147 : {
148 : unsigned int property;
149 :
150 0 : property = _hb_ot_layout_get_glyph_property (face, ginfo);
151 0 : (void) (property_out && (*property_out = property));
152 :
153 0 : return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
154 : }
155 :
156 : hb_bool_t
157 0 : _hb_ot_layout_skip_mark (hb_face_t *face,
158 : hb_glyph_info_t *ginfo,
159 : unsigned int lookup_props,
160 : unsigned int *property_out)
161 : {
162 : unsigned int property;
163 :
164 0 : property = _hb_ot_layout_get_glyph_property (face, ginfo);
165 0 : (void) (property_out && (*property_out = property));
166 :
167 : /* If it's a mark, skip it we don't accept it. */
168 0 : if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
169 0 : return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
170 :
171 : /* If not a mark, don't skip. */
172 0 : return false;
173 : }
174 :
175 :
176 :
177 : unsigned int
178 0 : hb_ot_layout_get_attach_points (hb_face_t *face,
179 : hb_codepoint_t glyph,
180 : unsigned int start_offset,
181 : unsigned int *point_count /* IN/OUT */,
182 : unsigned int *point_array /* OUT */)
183 : {
184 0 : return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
185 : }
186 :
187 : unsigned int
188 0 : hb_ot_layout_get_ligature_carets (hb_font_t *font,
189 : hb_direction_t direction,
190 : hb_codepoint_t glyph,
191 : unsigned int start_offset,
192 : unsigned int *caret_count /* IN/OUT */,
193 : int *caret_array /* OUT */)
194 : {
195 0 : return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
196 : }
197 :
198 : /*
199 : * GSUB/GPOS
200 : */
201 :
202 : static const GSUBGPOS&
203 0 : get_gsubgpos_table (hb_face_t *face,
204 : hb_tag_t table_tag)
205 : {
206 0 : switch (table_tag) {
207 0 : case HB_OT_TAG_GSUB: return _get_gsub (face);
208 0 : case HB_OT_TAG_GPOS: return _get_gpos (face);
209 0 : default: return Null(GSUBGPOS);
210 : }
211 : }
212 :
213 :
214 : unsigned int
215 0 : hb_ot_layout_table_get_script_tags (hb_face_t *face,
216 : hb_tag_t table_tag,
217 : unsigned int start_offset,
218 : unsigned int *script_count /* IN/OUT */,
219 : hb_tag_t *script_tags /* OUT */)
220 : {
221 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
222 :
223 0 : return g.get_script_tags (start_offset, script_count, script_tags);
224 : }
225 :
226 : hb_bool_t
227 0 : hb_ot_layout_table_find_script (hb_face_t *face,
228 : hb_tag_t table_tag,
229 : hb_tag_t script_tag,
230 : unsigned int *script_index)
231 : {
232 : ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
233 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
234 :
235 0 : if (g.find_script_index (script_tag, script_index))
236 0 : return TRUE;
237 :
238 : /* try finding 'DFLT' */
239 0 : if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
240 0 : return FALSE;
241 :
242 : /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
243 : * including many versions of DejaVu Sans Mono! */
244 0 : if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
245 0 : return FALSE;
246 :
247 0 : if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
248 0 : return FALSE;
249 : }
250 :
251 : hb_bool_t
252 0 : hb_ot_layout_table_choose_script (hb_face_t *face,
253 : hb_tag_t table_tag,
254 : const hb_tag_t *script_tags,
255 : unsigned int *script_index,
256 : hb_tag_t *chosen_script)
257 : {
258 : ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
259 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
260 :
261 0 : while (*script_tags)
262 : {
263 0 : if (g.find_script_index (*script_tags, script_index)) {
264 0 : if (chosen_script)
265 0 : *chosen_script = *script_tags;
266 0 : return TRUE;
267 : }
268 0 : script_tags++;
269 : }
270 :
271 : /* try finding 'DFLT' */
272 0 : if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
273 0 : if (chosen_script)
274 0 : *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
275 0 : return FALSE;
276 : }
277 :
278 : /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
279 0 : if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
280 0 : if (chosen_script)
281 0 : *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
282 0 : return FALSE;
283 : }
284 :
285 : /* try with 'latn'; some old fonts put their features there even though
286 : they're really trying to support Thai, for example :( */
287 : #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n')
288 0 : if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
289 0 : if (chosen_script)
290 0 : *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
291 0 : return FALSE;
292 : }
293 :
294 0 : if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
295 0 : if (chosen_script)
296 0 : *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
297 0 : return FALSE;
298 : }
299 :
300 : unsigned int
301 0 : hb_ot_layout_table_get_feature_tags (hb_face_t *face,
302 : hb_tag_t table_tag,
303 : unsigned int start_offset,
304 : unsigned int *feature_count /* IN/OUT */,
305 : hb_tag_t *feature_tags /* OUT */)
306 : {
307 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
308 :
309 0 : return g.get_feature_tags (start_offset, feature_count, feature_tags);
310 : }
311 :
312 :
313 : unsigned int
314 0 : hb_ot_layout_script_get_language_tags (hb_face_t *face,
315 : hb_tag_t table_tag,
316 : unsigned int script_index,
317 : unsigned int start_offset,
318 : unsigned int *language_count /* IN/OUT */,
319 : hb_tag_t *language_tags /* OUT */)
320 : {
321 0 : const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
322 :
323 0 : return s.get_lang_sys_tags (start_offset, language_count, language_tags);
324 : }
325 :
326 : hb_bool_t
327 0 : hb_ot_layout_script_find_language (hb_face_t *face,
328 : hb_tag_t table_tag,
329 : unsigned int script_index,
330 : hb_tag_t language_tag,
331 : unsigned int *language_index)
332 : {
333 : ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
334 0 : const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
335 :
336 0 : if (s.find_lang_sys_index (language_tag, language_index))
337 0 : return TRUE;
338 :
339 : /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
340 0 : if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
341 0 : return FALSE;
342 :
343 0 : if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
344 0 : return FALSE;
345 : }
346 :
347 : hb_bool_t
348 0 : hb_ot_layout_language_get_required_feature_index (hb_face_t *face,
349 : hb_tag_t table_tag,
350 : unsigned int script_index,
351 : unsigned int language_index,
352 : unsigned int *feature_index)
353 : {
354 0 : const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
355 :
356 0 : if (feature_index) *feature_index = l.get_required_feature_index ();
357 :
358 0 : return l.has_required_feature ();
359 : }
360 :
361 : unsigned int
362 0 : hb_ot_layout_language_get_feature_indexes (hb_face_t *face,
363 : hb_tag_t table_tag,
364 : unsigned int script_index,
365 : unsigned int language_index,
366 : unsigned int start_offset,
367 : unsigned int *feature_count /* IN/OUT */,
368 : unsigned int *feature_indexes /* OUT */)
369 : {
370 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
371 0 : const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
372 :
373 0 : return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
374 : }
375 :
376 : unsigned int
377 0 : hb_ot_layout_language_get_feature_tags (hb_face_t *face,
378 : hb_tag_t table_tag,
379 : unsigned int script_index,
380 : unsigned int language_index,
381 : unsigned int start_offset,
382 : unsigned int *feature_count /* IN/OUT */,
383 : hb_tag_t *feature_tags /* OUT */)
384 : {
385 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
386 0 : const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
387 :
388 : ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
389 0 : unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
390 :
391 0 : if (feature_tags) {
392 0 : unsigned int count = *feature_count;
393 0 : for (unsigned int i = 0; i < count; i++)
394 0 : feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
395 : }
396 :
397 0 : return ret;
398 : }
399 :
400 :
401 : hb_bool_t
402 0 : hb_ot_layout_language_find_feature (hb_face_t *face,
403 : hb_tag_t table_tag,
404 : unsigned int script_index,
405 : unsigned int language_index,
406 : hb_tag_t feature_tag,
407 : unsigned int *feature_index)
408 : {
409 : ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
410 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
411 0 : const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
412 :
413 0 : unsigned int num_features = l.get_feature_count ();
414 0 : for (unsigned int i = 0; i < num_features; i++) {
415 0 : unsigned int f_index = l.get_feature_index (i);
416 :
417 0 : if (feature_tag == g.get_feature_tag (f_index)) {
418 0 : if (feature_index) *feature_index = f_index;
419 0 : return TRUE;
420 : }
421 : }
422 :
423 0 : if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
424 0 : return FALSE;
425 : }
426 :
427 : unsigned int
428 0 : hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face,
429 : hb_tag_t table_tag,
430 : unsigned int feature_index,
431 : unsigned int start_offset,
432 : unsigned int *lookup_count /* IN/OUT */,
433 : unsigned int *lookup_indexes /* OUT */)
434 : {
435 0 : const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
436 0 : const Feature &f = g.get_feature (feature_index);
437 :
438 0 : return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
439 : }
440 :
441 :
442 : /*
443 : * GSUB
444 : */
445 :
446 : hb_bool_t
447 0 : hb_ot_layout_has_substitution (hb_face_t *face)
448 : {
449 0 : return &_get_gsub (face) != &Null(GSUB);
450 : }
451 :
452 : void
453 0 : hb_ot_layout_substitute_start (hb_buffer_t *buffer)
454 : {
455 0 : GSUB::substitute_start (buffer);
456 0 : }
457 :
458 : hb_bool_t
459 0 : hb_ot_layout_substitute_lookup (hb_face_t *face,
460 : hb_buffer_t *buffer,
461 : unsigned int lookup_index,
462 : hb_mask_t mask)
463 : {
464 0 : return _get_gsub (face).substitute_lookup (face, buffer, lookup_index, mask);
465 : }
466 :
467 : void
468 0 : hb_ot_layout_substitute_finish (hb_buffer_t *buffer HB_UNUSED)
469 : {
470 0 : GSUB::substitute_finish (buffer);
471 0 : }
472 :
473 :
474 : /*
475 : * GPOS
476 : */
477 :
478 : hb_bool_t
479 0 : hb_ot_layout_has_positioning (hb_face_t *face)
480 : {
481 0 : return &_get_gpos (face) != &Null(GPOS);
482 : }
483 :
484 : void
485 0 : hb_ot_layout_position_start (hb_buffer_t *buffer)
486 : {
487 0 : GPOS::position_start (buffer);
488 0 : }
489 :
490 : hb_bool_t
491 0 : hb_ot_layout_position_lookup (hb_font_t *font,
492 : hb_buffer_t *buffer,
493 : unsigned int lookup_index,
494 : hb_mask_t mask)
495 : {
496 0 : return _get_gpos (font->face).position_lookup (font, buffer, lookup_index, mask);
497 : }
498 :
499 : void
500 0 : hb_ot_layout_position_finish (hb_face_t *face, hb_buffer_t *buffer)
501 : {
502 : /* force diacritics to have zero width */
503 0 : unsigned int count = buffer->len;
504 0 : if (hb_ot_layout_has_glyph_classes (face)) {
505 0 : const GDEF& gdef = _get_gdef (face);
506 0 : if (buffer->props.direction == HB_DIRECTION_RTL) {
507 0 : for (unsigned int i = 1; i < count; i++) {
508 0 : if (gdef.get_glyph_class (buffer->info[i].codepoint) == GDEF::MarkGlyph) {
509 0 : buffer->pos[i].x_advance = 0;
510 : }
511 : }
512 : } else {
513 0 : for (unsigned int i = 1; i < count; i++) {
514 0 : if (gdef.get_glyph_class (buffer->info[i].codepoint) == GDEF::MarkGlyph) {
515 0 : hb_glyph_position_t& pos = buffer->pos[i];
516 0 : pos.x_offset -= pos.x_advance;
517 0 : pos.x_advance = 0;
518 : }
519 : }
520 : }
521 : } else {
522 : /* no GDEF classes available, so use General Category as a fallback */
523 0 : if (buffer->props.direction == HB_DIRECTION_RTL) {
524 0 : for (unsigned int i = 1; i < count; i++) {
525 0 : if (buffer->info[i].general_category() == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
526 0 : buffer->pos[i].x_advance = 0;
527 : }
528 : }
529 : } else {
530 0 : for (unsigned int i = 1; i < count; i++) {
531 0 : if (buffer->info[i].general_category() == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
532 0 : hb_glyph_position_t& pos = buffer->pos[i];
533 0 : pos.x_offset -= pos.x_advance;
534 0 : pos.x_advance = 0;
535 : }
536 : }
537 : }
538 : }
539 :
540 0 : GPOS::position_finish (buffer);
541 0 : }
542 :
543 :
|