hb-ot-shape.cc revision bd7378b2ef9793de4e7f57b920f29f48ac9d0c25
1/*
2 * Copyright (C) 2009,2010  Red Hat, Inc.
3 * Copyright (C) 2010  Google, Inc.
4 *
5 *  This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#include "hb-ot-shape-private.hh"
30#include "hb-ot-shape-complex-private.hh"
31
32HB_BEGIN_DECLS
33
34
35/* XXX vertical */
36hb_tag_t default_features[] = {
37  HB_TAG('c','a','l','t'),
38  HB_TAG('c','c','m','p'),
39  HB_TAG('c','l','i','g'),
40  HB_TAG('c','s','w','h'),
41  HB_TAG('c','u','r','s'),
42  HB_TAG('k','e','r','n'),
43  HB_TAG('l','i','g','a'),
44  HB_TAG('l','o','c','l'),
45  HB_TAG('m','a','r','k'),
46  HB_TAG('m','k','m','k'),
47  HB_TAG('r','l','i','g')
48};
49
50static void
51hb_ot_shape_collect_features (hb_ot_shape_plan_t       *plan,
52			      const hb_segment_properties_t  *props,
53			      const hb_feature_t       *user_features,
54			      unsigned int              num_user_features)
55{
56  switch (props->direction) {
57    case HB_DIRECTION_LTR:
58      plan->map.add_bool_feature (HB_TAG ('l','t','r','a'));
59      plan->map.add_bool_feature (HB_TAG ('l','t','r','m'));
60      break;
61    case HB_DIRECTION_RTL:
62      plan->map.add_bool_feature (HB_TAG ('r','t','l','a'));
63      plan->map.add_bool_feature (HB_TAG ('r','t','l','m'), false);
64      break;
65    case HB_DIRECTION_TTB:
66    case HB_DIRECTION_BTT:
67    default:
68      break;
69  }
70
71  for (unsigned int i = 0; i < ARRAY_LENGTH (default_features); i++)
72    plan->map.add_bool_feature (default_features[i]);
73
74  hb_ot_shape_complex_collect_features (plan, props);
75
76  for (unsigned int i = 0; i < num_user_features; i++) {
77    const hb_feature_t *feature = &user_features[i];
78    plan->map.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
79  }
80}
81
82
83static void
84hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
85{
86  hb_mask_t global_mask = c->plan->map.get_global_mask ();
87  c->buffer->reset_masks (global_mask);
88
89  hb_ot_shape_complex_setup_masks (c);
90
91  c->buffer->reset_masks (global_mask);
92
93  for (unsigned int i = 0; i < c->num_user_features; i++)
94  {
95    const hb_feature_t *feature = &c->user_features[i];
96    if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
97      unsigned int shift;
98      hb_mask_t mask = c->plan->map.get_mask (feature->tag, &shift);
99      c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
100    }
101  }
102}
103
104
105static void
106hb_ot_substitute_complex (hb_ot_shape_context_t *c)
107{
108  if (!hb_ot_layout_has_substitution (c->face))
109    return;
110
111  c->plan->map.substitute (c->face, c->buffer);
112
113  c->applied_substitute_complex = TRUE;
114  return;
115}
116
117static void
118hb_ot_position_complex (hb_ot_shape_context_t *c)
119{
120
121  if (!hb_ot_layout_has_positioning (c->face))
122    return;
123
124  c->plan->map.position (c->font, c->face, c->buffer);
125
126  hb_ot_layout_position_finish (c->font, c->face, c->buffer);
127
128  c->applied_position_complex = TRUE;
129  return;
130}
131
132
133/* Main shaper */
134
135/* Prepare */
136
137static inline hb_bool_t
138is_variation_selector (hb_codepoint_t unicode)
139{
140  return unlikely ((unicode >=  0x180B && unicode <=  0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */
141		   (unicode >=  0xFE00 && unicode <=  0xFE0F) || /* VARIATION SELECTOR-1..16 */
142		   (unicode >= 0xE0100 && unicode <= 0xE01EF));  /* VARIATION SELECTOR-17..256 */
143}
144
145static void
146hb_form_clusters (hb_ot_shape_context_t *c)
147{
148  unsigned int count = c->buffer->len;
149  for (unsigned int i = 1; i < count; i++)
150    if (c->buffer->unicode->v.get_general_category (c->buffer->info[i].codepoint) == HB_CATEGORY_NON_SPACING_MARK)
151      c->buffer->info[i].cluster = c->buffer->info[i - 1].cluster;
152}
153
154static void
155hb_ensure_native_direction (hb_ot_shape_context_t *c)
156{
157  hb_direction_t direction = c->buffer->props.direction;
158
159  /* TODO vertical */
160  if (HB_DIRECTION_IS_HORIZONTAL (direction) &&
161      direction != _hb_script_get_horizontal_direction (c->buffer->props.script))
162  {
163    hb_buffer_reverse_clusters (c->buffer);
164    c->buffer->props.direction = HB_DIRECTION_REVERSE (c->buffer->props.direction);
165  }
166}
167
168
169/* Substitute */
170
171static void
172hb_mirror_chars (hb_ot_shape_context_t *c)
173{
174  hb_unicode_get_mirroring_func_t get_mirroring = c->buffer->unicode->v.get_mirroring;
175
176  if (HB_DIRECTION_IS_FORWARD (c->buffer->props.direction))
177    return;
178
179  hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m'));
180
181  unsigned int count = c->buffer->len;
182  for (unsigned int i = 0; i < count; i++) {
183    hb_codepoint_t codepoint = get_mirroring (c->buffer->info[i].codepoint);
184    if (likely (codepoint == c->buffer->info[i].codepoint))
185      c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
186    else
187      c->buffer->info[i].codepoint = codepoint;
188  }
189}
190
191static void
192hb_map_glyphs (hb_font_t    *font,
193	       hb_face_t    *face,
194	       hb_buffer_t  *buffer)
195{
196  if (unlikely (!buffer->len))
197    return;
198
199  buffer->clear_output ();
200  unsigned int count = buffer->len - 1;
201  for (buffer->i = 0; buffer->i < count;) {
202    if (unlikely (is_variation_selector (buffer->info[buffer->i + 1].codepoint))) {
203      buffer->add_output_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, buffer->info[buffer->i + 1].codepoint));
204      buffer->i++;
205    } else {
206      buffer->add_output_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, 0));
207    }
208  }
209  if (likely (buffer->i < buffer->len))
210    buffer->add_output_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, 0));
211  buffer->swap ();
212}
213
214static void
215hb_substitute_default (hb_ot_shape_context_t *c)
216{
217  hb_map_glyphs (c->font, c->face, c->buffer);
218}
219
220static void
221hb_substitute_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
222{
223  /* TODO Arabic */
224}
225
226
227/* Position */
228
229static void
230hb_position_default (hb_ot_shape_context_t *c)
231{
232  hb_buffer_clear_positions (c->buffer);
233
234  unsigned int count = c->buffer->len;
235  for (unsigned int i = 0; i < count; i++) {
236    hb_glyph_metrics_t metrics;
237    hb_font_get_glyph_metrics (c->font, c->face, c->buffer->info[i].codepoint, &metrics);
238    c->buffer->pos[i].x_advance = metrics.x_advance;
239    c->buffer->pos[i].y_advance = metrics.y_advance;
240  }
241}
242
243static void
244hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
245{
246  /* TODO Mark pos */
247}
248
249static void
250hb_truetype_kern (hb_ot_shape_context_t *c)
251{
252  /* TODO Check for kern=0 */
253  unsigned int count = c->buffer->len;
254  for (unsigned int i = 1; i < count; i++) {
255    hb_position_t kern, kern1, kern2;
256    kern = hb_font_get_kerning (c->font, c->face, c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint);
257    kern1 = kern >> 1;
258    kern2 = kern - kern1;
259    c->buffer->pos[i - 1].x_advance += kern1;
260    c->buffer->pos[i].x_advance += kern2;
261    c->buffer->pos[i].x_offset += kern2;
262  }
263}
264
265static void
266hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
267{
268  hb_truetype_kern (c);
269}
270
271
272/* Do it! */
273
274static void
275hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
276{
277  /* Save the original direction, we use it later. */
278  c->original_direction = c->buffer->props.direction;
279
280  hb_form_clusters (c);
281
282  hb_ot_shape_setup_masks (c);
283
284  /* SUBSTITUTE */
285  {
286    /* Mirroring needs to see the original direction */
287    hb_mirror_chars (c);
288
289    hb_ensure_native_direction (c);
290
291    hb_substitute_default (c);
292
293    hb_ot_substitute_complex (c);
294
295    if (!c->applied_substitute_complex)
296      hb_substitute_complex_fallback (c);
297  }
298
299  /* POSITION */
300  {
301    hb_position_default (c);
302
303    hb_ot_position_complex (c);
304
305    hb_bool_t position_fallback = !c->applied_position_complex;
306    if (position_fallback)
307      hb_position_complex_fallback (c);
308
309    if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
310      hb_buffer_reverse (c->buffer);
311
312    if (position_fallback)
313      hb_position_complex_fallback_visual (c);
314  }
315
316  c->buffer->props.direction = c->original_direction;
317}
318
319void
320hb_ot_shape_plan_internal (hb_ot_shape_plan_t       *plan,
321			   hb_face_t                *face,
322			   const hb_segment_properties_t  *props,
323			   const hb_feature_t       *user_features,
324			   unsigned int              num_user_features)
325{
326  plan->shaper = hb_ot_shape_complex_categorize (props);
327
328  hb_ot_shape_collect_features (plan, props, user_features, num_user_features);
329
330  plan->map.compile (face, props);
331}
332
333void
334hb_ot_shape_execute (hb_ot_shape_plan_t *plan,
335		     hb_font_t          *font,
336		     hb_face_t          *face,
337		     hb_buffer_t        *buffer,
338		     const hb_feature_t *user_features,
339		     unsigned int        num_user_features)
340{
341  hb_ot_shape_context_t c = {plan, font, face, buffer, user_features, num_user_features};
342  hb_ot_shape_execute_internal (&c);
343}
344
345void
346hb_ot_shape (hb_font_t    *font,
347	     hb_face_t    *face,
348	     hb_buffer_t  *buffer,
349	     const hb_feature_t *user_features,
350	     unsigned int        num_user_features)
351{
352  hb_ot_shape_plan_t plan;
353
354  hb_ot_shape_plan_internal (&plan, face, &buffer->props, user_features, num_user_features);
355  hb_ot_shape_execute (&plan, font, face, buffer, user_features, num_user_features);
356}
357
358
359HB_END_DECLS
360