1 : /*
2 : * Copyright © 2009,2010 Red Hat, Inc.
3 : * Copyright © 2010,2011 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 :
32 : #include "hb-font-private.hh"
33 :
34 :
35 :
36 : hb_tag_t common_features[] = {
37 : HB_TAG('c','c','m','p'),
38 : HB_TAG('l','o','c','l'),
39 : HB_TAG('m','a','r','k'),
40 : HB_TAG('m','k','m','k'),
41 : HB_TAG('r','l','i','g'),
42 : };
43 :
44 : hb_tag_t horizontal_features[] = {
45 : HB_TAG('c','a','l','t'),
46 : HB_TAG('c','l','i','g'),
47 : HB_TAG('c','u','r','s'),
48 : HB_TAG('k','e','r','n'),
49 : HB_TAG('l','i','g','a'),
50 : };
51 :
52 : /* Note:
53 : * Technically speaking, vrt2 and vert are mutually exclusive.
54 : * According to the spec, valt and vpal are also mutually exclusive.
55 : * But we apply them all for now.
56 : */
57 : hb_tag_t vertical_features[] = {
58 : HB_TAG('v','a','l','t'),
59 : HB_TAG('v','e','r','t'),
60 : HB_TAG('v','k','r','n'),
61 : HB_TAG('v','p','a','l'),
62 : HB_TAG('v','r','t','2'),
63 : };
64 :
65 : static void
66 0 : hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
67 : const hb_segment_properties_t *props,
68 : const hb_feature_t *user_features,
69 : unsigned int num_user_features)
70 : {
71 0 : switch (props->direction) {
72 : case HB_DIRECTION_LTR:
73 0 : planner->map.add_bool_feature (HB_TAG ('l','t','r','a'));
74 0 : planner->map.add_bool_feature (HB_TAG ('l','t','r','m'));
75 0 : break;
76 : case HB_DIRECTION_RTL:
77 0 : planner->map.add_bool_feature (HB_TAG ('r','t','l','a'));
78 0 : planner->map.add_bool_feature (HB_TAG ('r','t','l','m'), false);
79 0 : break;
80 : case HB_DIRECTION_TTB:
81 : case HB_DIRECTION_BTT:
82 : case HB_DIRECTION_INVALID:
83 : default:
84 0 : break;
85 : }
86 :
87 : #define ADD_FEATURES(array) \
88 : HB_STMT_START { \
89 : for (unsigned int i = 0; i < ARRAY_LENGTH (array); i++) \
90 : planner->map.add_bool_feature (array[i]); \
91 : } HB_STMT_END
92 :
93 0 : hb_ot_shape_complex_collect_features (planner->shaper, &planner->map, props);
94 :
95 0 : ADD_FEATURES (common_features);
96 :
97 0 : if (HB_DIRECTION_IS_HORIZONTAL (props->direction))
98 0 : ADD_FEATURES (horizontal_features);
99 : else
100 0 : ADD_FEATURES (vertical_features);
101 :
102 : #undef ADD_FEATURES
103 :
104 0 : for (unsigned int i = 0; i < num_user_features; i++) {
105 0 : const hb_feature_t *feature = &user_features[i];
106 0 : planner->map.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
107 : }
108 0 : }
109 :
110 :
111 : static void
112 0 : hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
113 : {
114 0 : hb_mask_t global_mask = c->plan->map.get_global_mask ();
115 0 : c->buffer->reset_masks (global_mask);
116 :
117 0 : hb_ot_shape_complex_setup_masks (c->plan->shaper, &c->plan->map, c->buffer);
118 :
119 0 : for (unsigned int i = 0; i < c->num_user_features; i++)
120 : {
121 0 : const hb_feature_t *feature = &c->user_features[i];
122 0 : if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
123 : unsigned int shift;
124 0 : hb_mask_t mask = c->plan->map.get_mask (feature->tag, &shift);
125 0 : c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
126 : }
127 : }
128 0 : }
129 :
130 :
131 : /* Main shaper */
132 :
133 : /* Prepare */
134 :
135 : void
136 0 : _hb_set_unicode_props (hb_buffer_t *buffer)
137 : {
138 0 : unsigned int count = buffer->len;
139 0 : for (unsigned int i = 1; i < count; i++)
140 0 : hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode);
141 0 : }
142 :
143 : static void
144 0 : hb_form_clusters (hb_buffer_t *buffer)
145 : {
146 0 : unsigned int count = buffer->len;
147 0 : for (unsigned int i = 1; i < count; i++)
148 0 : if (FLAG (buffer->info[i].general_category()) &
149 : (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
150 : FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
151 : FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
152 0 : buffer->info[i].cluster = buffer->info[i - 1].cluster;
153 0 : }
154 :
155 : static void
156 0 : hb_ensure_native_direction (hb_buffer_t *buffer)
157 : {
158 0 : hb_direction_t direction = buffer->props.direction;
159 :
160 : /* TODO vertical:
161 : * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
162 : * Ogham fonts are supposed to be implemented BTT or not. Need to research that
163 : * first. */
164 0 : if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) ||
165 : (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB))
166 : {
167 0 : hb_buffer_reverse_clusters (buffer);
168 0 : buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
169 : }
170 0 : }
171 :
172 :
173 : /* Substitute */
174 :
175 : static void
176 0 : hb_mirror_chars (hb_ot_shape_context_t *c)
177 : {
178 0 : hb_unicode_funcs_t *unicode = c->buffer->unicode;
179 :
180 0 : if (HB_DIRECTION_IS_FORWARD (c->target_direction))
181 0 : return;
182 :
183 0 : hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m'));
184 :
185 0 : unsigned int count = c->buffer->len;
186 0 : for (unsigned int i = 0; i < count; i++) {
187 0 : hb_codepoint_t codepoint = hb_unicode_mirroring (unicode, c->buffer->info[i].codepoint);
188 0 : if (likely (codepoint == c->buffer->info[i].codepoint))
189 0 : c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
190 : else
191 0 : c->buffer->info[i].codepoint = codepoint;
192 : }
193 : }
194 :
195 : static void
196 0 : hb_map_glyphs (hb_font_t *font,
197 : hb_buffer_t *buffer)
198 : {
199 : hb_codepoint_t glyph;
200 :
201 0 : if (unlikely (!buffer->len))
202 0 : return;
203 :
204 0 : buffer->clear_output ();
205 :
206 0 : unsigned int count = buffer->len - 1;
207 0 : for (buffer->idx = 0; buffer->idx < count;) {
208 0 : if (unlikely (is_variation_selector (buffer->info[buffer->idx + 1].codepoint))) {
209 0 : hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, buffer->info[buffer->idx + 1].codepoint, &glyph);
210 0 : buffer->replace_glyph (glyph);
211 0 : buffer->skip_glyph ();
212 : } else {
213 0 : hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph);
214 0 : buffer->replace_glyph (glyph);
215 : }
216 : }
217 0 : if (likely (buffer->idx < buffer->len)) {
218 0 : hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph);
219 0 : buffer->replace_glyph (glyph);
220 : }
221 0 : buffer->swap_buffers ();
222 : }
223 :
224 : static void
225 0 : hb_substitute_default (hb_ot_shape_context_t *c)
226 : {
227 0 : hb_ot_layout_substitute_start (c->buffer);
228 :
229 0 : hb_mirror_chars (c);
230 :
231 0 : hb_map_glyphs (c->font, c->buffer);
232 0 : }
233 :
234 : static void
235 0 : hb_ot_substitute_complex (hb_ot_shape_context_t *c)
236 : {
237 0 : if (hb_ot_layout_has_substitution (c->face)) {
238 0 : c->plan->map.substitute (c->face, c->buffer);
239 0 : c->applied_substitute_complex = TRUE;
240 : }
241 :
242 0 : hb_ot_layout_substitute_finish (c->buffer);
243 :
244 : return;
245 : }
246 :
247 : static void
248 0 : hb_substitute_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
249 : {
250 : /* TODO Arabic */
251 0 : }
252 :
253 :
254 : /* Position */
255 :
256 : static void
257 0 : hb_position_default (hb_ot_shape_context_t *c)
258 : {
259 0 : hb_ot_layout_position_start (c->buffer);
260 :
261 0 : unsigned int count = c->buffer->len;
262 0 : for (unsigned int i = 0; i < count; i++) {
263 0 : hb_font_get_glyph_advance_for_direction (c->font, c->buffer->info[i].codepoint,
264 : c->buffer->props.direction,
265 0 : &c->buffer->pos[i].x_advance,
266 0 : &c->buffer->pos[i].y_advance);
267 0 : hb_font_subtract_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint,
268 : c->buffer->props.direction,
269 0 : &c->buffer->pos[i].x_offset,
270 0 : &c->buffer->pos[i].y_offset);
271 : }
272 0 : }
273 :
274 : static void
275 0 : hb_ot_position_complex (hb_ot_shape_context_t *c)
276 : {
277 :
278 0 : if (hb_ot_layout_has_positioning (c->face))
279 : {
280 : /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */
281 :
282 0 : unsigned int count = c->buffer->len;
283 0 : for (unsigned int i = 0; i < count; i++) {
284 0 : hb_font_add_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint,
285 : HB_DIRECTION_LTR,
286 0 : &c->buffer->pos[i].x_offset,
287 0 : &c->buffer->pos[i].y_offset);
288 : }
289 :
290 0 : c->plan->map.position (c->font, c->buffer);
291 :
292 0 : for (unsigned int i = 0; i < count; i++) {
293 0 : hb_font_subtract_glyph_origin_for_direction (c->font, c->buffer->info[i].codepoint,
294 : HB_DIRECTION_LTR,
295 0 : &c->buffer->pos[i].x_offset,
296 0 : &c->buffer->pos[i].y_offset);
297 : }
298 :
299 0 : c->applied_position_complex = TRUE;
300 : }
301 :
302 0 : hb_ot_layout_position_finish (c->face, c->buffer);
303 :
304 : return;
305 : }
306 :
307 : static void
308 0 : hb_position_complex_fallback (hb_ot_shape_context_t *c)
309 : {
310 0 : unsigned int count = c->buffer->len;
311 0 : if (c->buffer->props.direction == HB_DIRECTION_RTL) {
312 0 : for (unsigned int i = 1; i < count; i++) {
313 0 : unsigned int gen_cat = c->buffer->info[i].general_category();
314 0 : if ((1<<gen_cat) & ((1<<HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)|
315 : (1<<HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK)|
316 : (1<<HB_UNICODE_GENERAL_CATEGORY_FORMAT))) {
317 0 : c->buffer->pos[i].x_advance = 0;
318 : }
319 : }
320 : } else {
321 0 : for (unsigned int i = 1; i < count; i++) {
322 0 : unsigned int gen_cat = c->buffer->info[i].general_category();
323 0 : if ((1<<gen_cat) & ((1<<HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)|
324 : (1<<HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK)|
325 : (1<<HB_UNICODE_GENERAL_CATEGORY_FORMAT))) {
326 0 : hb_glyph_position_t& pos = c->buffer->pos[i];
327 0 : pos.x_offset = -pos.x_advance;
328 0 : pos.x_advance = 0;
329 : }
330 : }
331 : }
332 0 : }
333 :
334 : static void
335 0 : hb_truetype_kern (hb_ot_shape_context_t *c)
336 : {
337 : /* TODO Check for kern=0 */
338 0 : unsigned int count = c->buffer->len;
339 0 : for (unsigned int i = 1; i < count; i++) {
340 : hb_position_t x_kern, y_kern, kern1, kern2;
341 : hb_font_get_glyph_kerning_for_direction (c->font,
342 0 : c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint,
343 : c->buffer->props.direction,
344 0 : &x_kern, &y_kern);
345 :
346 0 : kern1 = x_kern >> 1;
347 0 : kern2 = x_kern - kern1;
348 0 : c->buffer->pos[i - 1].x_advance += kern1;
349 0 : c->buffer->pos[i].x_advance += kern2;
350 0 : c->buffer->pos[i].x_offset += kern2;
351 :
352 0 : kern1 = y_kern >> 1;
353 0 : kern2 = y_kern - kern1;
354 0 : c->buffer->pos[i - 1].y_advance += kern1;
355 0 : c->buffer->pos[i].y_advance += kern2;
356 0 : c->buffer->pos[i].y_offset += kern2;
357 : }
358 0 : }
359 :
360 : static void
361 0 : hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
362 : {
363 0 : hb_truetype_kern (c);
364 0 : }
365 :
366 :
367 : /* Do it! */
368 :
369 : static void
370 0 : hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
371 : {
372 0 : c->buffer->deallocate_var_all ();
373 :
374 : /* Save the original direction, we use it later. */
375 0 : c->target_direction = c->buffer->props.direction;
376 :
377 0 : HB_BUFFER_ALLOCATE_VAR (c->buffer, general_category);
378 0 : HB_BUFFER_ALLOCATE_VAR (c->buffer, combining_class);
379 :
380 0 : _hb_set_unicode_props (c->buffer); /* BUFFER: Set general_category and combining_class */
381 :
382 0 : hb_form_clusters (c->buffer);
383 :
384 0 : hb_ensure_native_direction (c->buffer);
385 :
386 0 : _hb_ot_shape_normalize (c);
387 :
388 0 : hb_ot_shape_setup_masks (c);
389 :
390 : /* SUBSTITUTE */
391 : {
392 0 : hb_substitute_default (c);
393 :
394 0 : hb_ot_substitute_complex (c);
395 :
396 0 : if (!c->applied_substitute_complex)
397 0 : hb_substitute_complex_fallback (c);
398 : }
399 :
400 : /* POSITION */
401 : {
402 0 : hb_position_default (c);
403 :
404 0 : hb_ot_position_complex (c);
405 :
406 0 : hb_bool_t position_fallback = !c->applied_position_complex;
407 0 : if (position_fallback)
408 0 : hb_position_complex_fallback (c);
409 :
410 0 : if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
411 0 : hb_buffer_reverse (c->buffer);
412 :
413 0 : if (position_fallback)
414 0 : hb_position_complex_fallback_visual (c);
415 : }
416 :
417 0 : HB_BUFFER_DEALLOCATE_VAR (c->buffer, combining_class);
418 0 : HB_BUFFER_DEALLOCATE_VAR (c->buffer, general_category);
419 :
420 0 : c->buffer->props.direction = c->target_direction;
421 :
422 0 : c->buffer->deallocate_var_all ();
423 0 : }
424 :
425 : static void
426 0 : hb_ot_shape_plan_internal (hb_ot_shape_plan_t *plan,
427 : hb_face_t *face,
428 : const hb_segment_properties_t *props,
429 : const hb_feature_t *user_features,
430 : unsigned int num_user_features)
431 : {
432 0 : hb_ot_shape_planner_t planner;
433 :
434 0 : planner.shaper = hb_ot_shape_complex_categorize (props);
435 :
436 0 : hb_ot_shape_collect_features (&planner, props, user_features, num_user_features);
437 :
438 0 : planner.compile (face, props, *plan);
439 0 : }
440 :
441 : static void
442 0 : hb_ot_shape_execute (hb_ot_shape_plan_t *plan,
443 : hb_font_t *font,
444 : hb_buffer_t *buffer,
445 : const hb_feature_t *user_features,
446 : unsigned int num_user_features)
447 : {
448 0 : hb_ot_shape_context_t c = {plan, font, font->face, buffer, user_features, num_user_features};
449 0 : hb_ot_shape_execute_internal (&c);
450 0 : }
451 :
452 : hb_bool_t
453 0 : hb_ot_shape (hb_font_t *font,
454 : hb_buffer_t *buffer,
455 : const hb_feature_t *features,
456 : unsigned int num_features,
457 : const char * const *shaper_options)
458 : {
459 0 : hb_ot_shape_plan_t plan;
460 :
461 0 : buffer->guess_properties ();
462 :
463 0 : hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features);
464 0 : hb_ot_shape_execute (&plan, font, buffer, features, num_features);
465 :
466 0 : return TRUE;
467 : }
468 :
469 :
|