hb-ot-layout.cc revision 706ab25a4cb043d46e6088aa0a7184ee200276c9
1/*
2 * Copyright (C) 1998-2004  David Turner and Werner Lemberg
3 * Copyright (C) 2006  Behdad Esfahbod
4 * Copyright (C) 2007,2008  Red Hat, Inc.
5 *
6 *  This is part of HarfBuzz, an OpenType Layout engine 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#define HB_OT_LAYOUT_CC
30
31#include "hb-ot-layout.h"
32#include "hb-ot-layout-private.h"
33
34#include "hb-ot-layout-open-private.h"
35#include "hb-ot-layout-gdef-private.h"
36#include "hb-ot-layout-gsub-private.h"
37
38
39#include <stdlib.h>
40#include <string.h>
41
42
43struct _hb_ot_layout_t {
44  const GDEF *gdef;
45  const GSUB *gsub;
46  const /*XXX*/GSUBGPOS *gpos;
47
48  struct {
49    unsigned char *klasses;
50    unsigned int len;
51  } new_gdef;
52
53};
54
55hb_ot_layout_t *
56hb_ot_layout_create_for_data (const char *font_data,
57			      int         face_index)
58{
59  hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
60
61  const OpenTypeFontFile &font = OpenTypeFontFile::get_for_data (font_data);
62  const OpenTypeFontFace &face = font.get_face (face_index);
63
64  layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag)));
65  layout->gsub = &GSUB::get_for_data (font.get_table_data (face.get_table_by_tag (GSUB::Tag)));
66  layout->gpos = &/*XXX*/GSUBGPOS::get_for_data (font.get_table_data (face.get_table_by_tag (/*XXX*/GSUBGPOS::GPOSTag)));
67
68  return layout;
69}
70
71void
72hb_ot_layout_destroy (hb_ot_layout_t *layout)
73{
74  free (layout);
75}
76
77/*
78 * GDEF
79 */
80
81hb_bool_t
82hb_ot_layout_has_font_glyph_classes (hb_ot_layout_t *layout)
83{
84  return layout->gdef->has_glyph_classes ();
85}
86
87HB_OT_LAYOUT_INTERNAL hb_bool_t
88_hb_ot_layout_has_new_glyph_classes (hb_ot_layout_t *layout)
89{
90  return layout->new_gdef.len > 0;
91}
92
93HB_OT_LAYOUT_INTERNAL hb_ot_layout_glyph_properties_t
94_hb_ot_layout_get_glyph_properties (hb_ot_layout_t *layout,
95				    hb_glyph_t      glyph)
96{
97  hb_ot_layout_class_t klass;
98
99  /* TODO old harfbuzz doesn't always parse mark attachments as it says it was
100   * introduced without a version bump, so it may not be safe */
101  klass = layout->gdef->get_mark_attachment_type (glyph);
102  if (klass)
103    return klass << 8;
104
105  klass = layout->gdef->get_glyph_class (glyph);
106
107  if (!klass && glyph < layout->new_gdef.len)
108    klass = layout->new_gdef.klasses[glyph];
109
110  switch (klass) {
111  default:
112  case GDEF::UnclassifiedGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
113  case GDEF::BaseGlyph:		return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
114  case GDEF::LigatureGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
115  case GDEF::MarkGlyph:		return HB_OT_LAYOUT_GLYPH_CLASS_MARK;
116  case GDEF::ComponentGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
117  }
118}
119
120HB_OT_LAYOUT_INTERNAL hb_bool_t
121_hb_ot_layout_check_glyph_properties (hb_ot_layout_t                  *layout,
122				      HB_GlyphItem                     gitem,
123				      hb_ot_layout_lookup_flags_t      lookup_flags,
124				      hb_ot_layout_glyph_properties_t *property)
125{
126  hb_ot_layout_glyph_class_t basic_glyph_class;
127  hb_ot_layout_glyph_properties_t desired_attachment_class;
128
129  if (gitem->gproperties == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
130  {
131    gitem->gproperties = *property = _hb_ot_layout_get_glyph_properties (layout, gitem->gindex);
132    if (gitem->gproperties == HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED)
133      return false;
134  }
135
136  *property = gitem->gproperties;
137
138  /* If the glyph was found in the MarkAttachmentClass table,
139   * then that class value is the high byte of the result,
140   * otherwise the low byte contains the basic type of the glyph
141   * as defined by the GlyphClassDef table.
142   */
143  if (*property & LookupFlag::MarkAttachmentType)
144    basic_glyph_class = HB_OT_LAYOUT_GLYPH_CLASS_MARK;
145  else
146    basic_glyph_class = (hb_ot_layout_glyph_class_t) *property;
147
148  /* Not covered, if, for example, basic_glyph_class
149   * is HB_GDEF_LIGATURE and lookup_flags includes LookupFlags::IgnoreLigatures
150   */
151  if (lookup_flags & basic_glyph_class)
152    return false;
153
154  /* The high byte of lookup_flags has the meaning
155   * "ignore marks of attachment type different than
156   * the attachment type specified."
157   */
158  desired_attachment_class = lookup_flags & LookupFlag::MarkAttachmentType;
159  if (desired_attachment_class)
160  {
161    if (basic_glyph_class == HB_OT_LAYOUT_GLYPH_CLASS_MARK &&
162        *property != desired_attachment_class )
163      return false;
164  }
165
166  return true;
167}
168
169
170hb_ot_layout_glyph_class_t
171hb_ot_layout_get_glyph_class (hb_ot_layout_t *layout,
172			      hb_glyph_t      glyph)
173{
174  hb_ot_layout_glyph_properties_t properties;
175  hb_ot_layout_class_t klass;
176
177  properties = _hb_ot_layout_get_glyph_properties (layout, glyph);
178
179  if (properties & 0xFF00)
180    return HB_OT_LAYOUT_GLYPH_CLASS_MARK;
181
182  return (hb_ot_layout_glyph_class_t) properties;
183}
184
185void
186hb_ot_layout_set_glyph_class (hb_ot_layout_t             *layout,
187			      hb_glyph_t                  glyph,
188			      hb_ot_layout_glyph_class_t  klass)
189{
190  /* TODO optimize this, similar to old harfbuzz code for example */
191
192  hb_ot_layout_class_t gdef_klass;
193  int len = layout->new_gdef.len;
194
195  if (glyph >= len) {
196    int new_len;
197    unsigned char *new_klasses;
198
199    new_len = len == 0 ? 120 : 2 * len;
200    if (new_len > 65535)
201      new_len = 65535;
202    new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
203
204    if (G_UNLIKELY (!new_klasses))
205      return;
206
207    memset (new_klasses + len, 0, new_len - len);
208
209    layout->new_gdef.klasses = new_klasses;
210    layout->new_gdef.len = new_len;
211  }
212
213  switch (klass) {
214  default:
215  case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED:	gdef_klass = GDEF::UnclassifiedGlyph;	break;
216  case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH:	gdef_klass = GDEF::BaseGlyph;		break;
217  case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE:	gdef_klass = GDEF::LigatureGlyph;	break;
218  case HB_OT_LAYOUT_GLYPH_CLASS_MARK:		gdef_klass = GDEF::MarkGlyph;		break;
219  case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT:	gdef_klass = GDEF::ComponentGlyph;	break;
220  }
221
222  layout->new_gdef.klasses[glyph] = gdef_klass;
223  return;
224}
225
226void
227hb_ot_layout_build_glyph_classes (hb_ot_layout_t *layout,
228				  uint16_t        num_total_glyphs,
229				  hb_glyph_t     *glyphs,
230				  unsigned char  *klasses,
231				  uint16_t        count)
232{
233  int i;
234
235  if (G_UNLIKELY (!count || !glyphs || !klasses))
236    return;
237
238  if (layout->new_gdef.len == 0) {
239    layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
240    layout->new_gdef.len = count;
241  }
242
243  for (i = 0; i < count; i++)
244    hb_ot_layout_set_glyph_class (layout, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
245}
246
247/*
248 * GSUB/GPOS
249 */
250
251static const GSUBGPOS&
252get_gsubgpos_table (hb_ot_layout_t            *layout,
253		    hb_ot_layout_table_type_t  table_type)
254{
255  switch (table_type) {
256    case HB_OT_LAYOUT_TABLE_TYPE_GSUB: return *(layout->gsub);
257    case HB_OT_LAYOUT_TABLE_TYPE_GPOS: return *(layout->gpos);
258    default:                           return NullGSUBGPOS;
259  }
260}
261
262
263unsigned int
264hb_ot_layout_get_script_count (hb_ot_layout_t            *layout,
265			       hb_ot_layout_table_type_t  table_type)
266{
267  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
268
269  return g.get_script_count ();
270}
271
272hb_tag_t
273hb_ot_layout_get_script_tag (hb_ot_layout_t            *layout,
274			     hb_ot_layout_table_type_t  table_type,
275			     unsigned int               script_index)
276{
277  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
278
279  return g.get_script_tag (script_index);
280}
281
282hb_bool_t
283hb_ot_layout_find_script (hb_ot_layout_t            *layout,
284			  hb_ot_layout_table_type_t  table_type,
285			  hb_tag_t                   script_tag,
286			  unsigned int              *script_index)
287{
288  unsigned int i;
289  const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
290
291  if (g.find_script_index (script_tag, script_index))
292    return TRUE;
293
294  /* try finding 'DFLT' */
295  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
296    return FALSE;
297
298  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
299  if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
300    return FALSE;
301
302  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
303  return FALSE;
304}
305
306
307unsigned int
308hb_ot_layout_get_language_count (hb_ot_layout_t            *layout,
309				 hb_ot_layout_table_type_t  table_type,
310				 unsigned int               script_index)
311{
312  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
313
314  return s.get_lang_sys_count ();
315}
316
317hb_tag_t
318hb_ot_layout_get_language_tag (hb_ot_layout_t            *layout,
319			       hb_ot_layout_table_type_t  table_type,
320			       unsigned int               script_index,
321			       unsigned int               language_index)
322{
323  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
324
325  return s.get_lang_sys_tag (language_index);
326}
327
328hb_bool_t
329hb_ot_layout_find_language (hb_ot_layout_t            *layout,
330			    hb_ot_layout_table_type_t  table_type,
331			    unsigned int               script_index,
332			    hb_tag_t                   language_tag,
333			    unsigned int              *language_index,
334			    unsigned int              *required_features_index)
335{
336  unsigned int i;
337  const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
338
339#if 0
340  if (s.find_script_index (script_tag, script_index))
341    return TRUE;
342
343  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
344  if (s.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
345    return FALSE;
346
347  ////////////////////////
348  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
349  return FALSE;
350
351  if (language_index)
352    *language_index = PANGO_OT_DEFAULT_LANGUAGE;
353  if (required_feature_index)
354    *required_feature_index = PANGO_OT_NO_FEATURE;
355
356  if (script_index == PANGO_OT_NO_SCRIPT)
357    return FALSE;
358
359
360  /* DefaultLangSys */
361  if (language_index)
362    *language_index = PANGO_OT_DEFAULT_LANGUAGE;
363  if (required_feature_index)
364    *required_feature_index = script->DefaultLangSys.ReqFeatureIndex;
365#endif
366
367  return FALSE;
368}
369