2009-12-20 19:58:26 +00:00
|
|
|
/*
|
2011-04-21 21:14:28 +00:00
|
|
|
* Copyright © 2009,2010 Red Hat, Inc.
|
2012-08-10 07:28:50 +00:00
|
|
|
* Copyright © 2010,2011,2012 Google, Inc.
|
2009-12-20 19:58:26 +00:00
|
|
|
*
|
2010-04-22 04:11:43 +00:00
|
|
|
* This is part of HarfBuzz, a text shaping library.
|
2009-12-20 19:58:26 +00:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, without written agreement and without
|
|
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
|
|
* software and its documentation for any purpose, provided that the
|
|
|
|
* above copyright notice and the following two paragraphs appear in
|
|
|
|
* all copies of this software.
|
|
|
|
*
|
|
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
|
|
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
|
|
|
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
|
|
* DAMAGE.
|
|
|
|
*
|
|
|
|
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|
|
|
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
|
|
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
|
|
*
|
|
|
|
* Red Hat Author(s): Behdad Esfahbod
|
2010-10-07 21:47:33 +00:00
|
|
|
* Google Author(s): Behdad Esfahbod
|
2009-12-20 19:58:26 +00:00
|
|
|
*/
|
|
|
|
|
2012-07-26 21:34:25 +00:00
|
|
|
#define HB_SHAPER ot
|
|
|
|
#define hb_ot_shaper_face_data_t hb_ot_layout_t
|
2012-07-27 06:29:32 +00:00
|
|
|
#define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t
|
2012-07-26 21:34:25 +00:00
|
|
|
#include "hb-shaper-impl-private.hh"
|
|
|
|
|
2010-10-08 23:18:16 +00:00
|
|
|
#include "hb-ot-shape-private.hh"
|
2012-08-02 13:38:28 +00:00
|
|
|
#include "hb-ot-shape-complex-private.hh"
|
2012-08-08 18:33:37 +00:00
|
|
|
#include "hb-ot-shape-fallback-private.hh"
|
2012-08-08 02:41:38 +00:00
|
|
|
#include "hb-ot-shape-normalize-private.hh"
|
2009-12-20 19:58:26 +00:00
|
|
|
|
2012-07-24 00:18:17 +00:00
|
|
|
#include "hb-ot-layout-private.hh"
|
2014-07-16 17:21:26 +00:00
|
|
|
#include "hb-unicode-private.hh"
|
2012-04-24 20:56:37 +00:00
|
|
|
#include "hb-set-private.hh"
|
2011-05-03 00:46:32 +00:00
|
|
|
|
2018-01-09 16:55:17 +00:00
|
|
|
#include "hb-ot-layout-gsubgpos-private.hh"
|
2018-07-17 16:14:45 +00:00
|
|
|
#include "hb-aat-layout-private.hh"
|
2010-07-23 19:11:18 +00:00
|
|
|
|
2012-08-08 22:04:29 +00:00
|
|
|
static hb_tag_t common_features[] = {
|
2009-12-20 19:58:26 +00:00
|
|
|
HB_TAG('c','c','m','p'),
|
2011-05-31 19:18:13 +00:00
|
|
|
HB_TAG('l','o','c','l'),
|
|
|
|
HB_TAG('m','a','r','k'),
|
|
|
|
HB_TAG('m','k','m','k'),
|
|
|
|
HB_TAG('r','l','i','g'),
|
|
|
|
};
|
|
|
|
|
2012-05-09 13:04:13 +00:00
|
|
|
|
2012-08-08 22:04:29 +00:00
|
|
|
static hb_tag_t horizontal_features[] = {
|
2011-05-31 19:18:13 +00:00
|
|
|
HB_TAG('c','a','l','t'),
|
2009-12-20 19:58:26 +00:00
|
|
|
HB_TAG('c','l','i','g'),
|
2010-05-21 17:06:35 +00:00
|
|
|
HB_TAG('c','u','r','s'),
|
2009-12-20 19:58:26 +00:00
|
|
|
HB_TAG('k','e','r','n'),
|
2014-08-02 20:31:16 +00:00
|
|
|
HB_TAG('l','i','g','a'),
|
2012-08-08 01:12:49 +00:00
|
|
|
HB_TAG('r','c','l','t'),
|
2011-05-31 19:18:13 +00:00
|
|
|
};
|
|
|
|
|
2012-04-15 00:23:58 +00:00
|
|
|
|
|
|
|
|
2009-12-20 19:58:26 +00:00
|
|
|
static void
|
2011-05-27 22:13:31 +00:00
|
|
|
hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
|
2010-10-12 20:50:36 +00:00
|
|
|
const hb_segment_properties_t *props,
|
2011-05-27 22:13:31 +00:00
|
|
|
const hb_feature_t *user_features,
|
|
|
|
unsigned int num_user_features)
|
2009-12-20 19:58:26 +00:00
|
|
|
{
|
2012-08-02 14:07:58 +00:00
|
|
|
hb_ot_map_builder_t *map = &planner->map;
|
|
|
|
|
2016-09-28 15:05:43 +00:00
|
|
|
map->add_global_bool_feature (HB_TAG('r','v','r','n'));
|
2017-10-15 10:11:08 +00:00
|
|
|
map->add_gsub_pause (nullptr);
|
2016-09-28 15:05:43 +00:00
|
|
|
|
2010-10-12 20:00:21 +00:00
|
|
|
switch (props->direction) {
|
2010-05-29 00:21:47 +00:00
|
|
|
case HB_DIRECTION_LTR:
|
2013-02-14 16:05:56 +00:00
|
|
|
map->add_global_bool_feature (HB_TAG ('l','t','r','a'));
|
|
|
|
map->add_global_bool_feature (HB_TAG ('l','t','r','m'));
|
2010-05-29 00:21:47 +00:00
|
|
|
break;
|
|
|
|
case HB_DIRECTION_RTL:
|
2013-02-14 16:05:56 +00:00
|
|
|
map->add_global_bool_feature (HB_TAG ('r','t','l','a'));
|
2013-02-14 16:25:10 +00:00
|
|
|
map->add_feature (HB_TAG ('r','t','l','m'), 1, F_NONE);
|
2010-05-29 00:21:47 +00:00
|
|
|
break;
|
|
|
|
case HB_DIRECTION_TTB:
|
|
|
|
case HB_DIRECTION_BTT:
|
2011-03-16 17:53:32 +00:00
|
|
|
case HB_DIRECTION_INVALID:
|
2010-05-29 00:21:47 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-12-22 21:17:54 +00:00
|
|
|
map->add_feature (HB_TAG ('f','r','a','c'), 1, F_NONE);
|
|
|
|
map->add_feature (HB_TAG ('n','u','m','r'), 1, F_NONE);
|
|
|
|
map->add_feature (HB_TAG ('d','n','o','m'), 1, F_NONE);
|
|
|
|
|
2012-07-31 01:08:51 +00:00
|
|
|
if (planner->shaper->collect_features)
|
2012-08-02 13:38:28 +00:00
|
|
|
planner->shaper->collect_features (planner);
|
2010-05-29 00:37:06 +00:00
|
|
|
|
2013-02-15 11:22:26 +00:00
|
|
|
for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++)
|
|
|
|
map->add_global_bool_feature (common_features[i]);
|
2011-05-31 19:18:13 +00:00
|
|
|
|
|
|
|
if (HB_DIRECTION_IS_HORIZONTAL (props->direction))
|
2013-02-15 11:22:26 +00:00
|
|
|
for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
|
2013-02-15 12:41:07 +00:00
|
|
|
map->add_feature (horizontal_features[i], 1, F_GLOBAL |
|
|
|
|
(horizontal_features[i] == HB_TAG('k','e','r','n') ?
|
|
|
|
F_HAS_FALLBACK : F_NONE));
|
2011-05-31 19:18:13 +00:00
|
|
|
else
|
2015-07-23 10:32:59 +00:00
|
|
|
{
|
2015-07-23 10:52:11 +00:00
|
|
|
/* We really want to find a 'vert' feature if there's any in the font, no
|
|
|
|
* matter which script/langsys it is listed (or not) under.
|
|
|
|
* See various bugs referenced from:
|
2017-11-20 19:49:22 +00:00
|
|
|
* https://github.com/harfbuzz/harfbuzz/issues/63 */
|
2015-07-23 10:52:11 +00:00
|
|
|
map->add_feature (HB_TAG ('v','e','r','t'), 1, F_GLOBAL | F_GLOBAL_SEARCH);
|
2015-07-23 10:32:59 +00:00
|
|
|
}
|
2011-05-31 19:18:13 +00:00
|
|
|
|
2012-07-31 01:08:51 +00:00
|
|
|
if (planner->shaper->override_features)
|
2012-08-02 13:38:28 +00:00
|
|
|
planner->shaper->override_features (planner);
|
2012-07-17 00:26:57 +00:00
|
|
|
|
2010-10-12 20:00:21 +00:00
|
|
|
for (unsigned int i = 0; i < num_user_features; i++) {
|
|
|
|
const hb_feature_t *feature = &user_features[i];
|
2013-02-14 16:25:10 +00:00
|
|
|
map->add_feature (feature->tag, feature->value,
|
|
|
|
(feature->start == 0 && feature->end == (unsigned int) -1) ?
|
|
|
|
F_GLOBAL : F_NONE);
|
2010-05-29 00:37:06 +00:00
|
|
|
}
|
2010-10-08 16:29:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-27 06:29:32 +00:00
|
|
|
/*
|
|
|
|
* shaper face data
|
|
|
|
*/
|
|
|
|
|
2017-02-04 00:43:25 +00:00
|
|
|
HB_SHAPER_DATA_ENSURE_DEFINE(ot, face)
|
|
|
|
|
2012-07-27 06:29:32 +00:00
|
|
|
hb_ot_shaper_face_data_t *
|
|
|
|
_hb_ot_shaper_face_data_create (hb_face_t *face)
|
|
|
|
{
|
|
|
|
return _hb_ot_layout_create (face);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data)
|
|
|
|
{
|
|
|
|
_hb_ot_layout_destroy (data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shaper font data
|
|
|
|
*/
|
|
|
|
|
2017-02-04 00:43:25 +00:00
|
|
|
HB_SHAPER_DATA_ENSURE_DEFINE(ot, font)
|
|
|
|
|
2012-07-27 06:29:32 +00:00
|
|
|
struct hb_ot_shaper_font_data_t {};
|
|
|
|
|
|
|
|
hb_ot_shaper_font_data_t *
|
2016-02-22 07:00:59 +00:00
|
|
|
_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED)
|
2012-07-27 06:29:32 +00:00
|
|
|
{
|
|
|
|
return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shaper shape_plan data
|
|
|
|
*/
|
|
|
|
|
|
|
|
hb_ot_shaper_shape_plan_data_t *
|
|
|
|
_hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan,
|
|
|
|
const hb_feature_t *user_features,
|
2016-09-10 10:57:24 +00:00
|
|
|
unsigned int num_user_features,
|
|
|
|
const int *coords,
|
|
|
|
unsigned int num_coords)
|
2012-07-27 06:29:32 +00:00
|
|
|
{
|
2012-08-02 14:46:34 +00:00
|
|
|
hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t));
|
|
|
|
if (unlikely (!plan))
|
2017-10-15 10:11:08 +00:00
|
|
|
return nullptr;
|
2012-07-27 06:29:32 +00:00
|
|
|
|
2018-06-02 22:30:59 +00:00
|
|
|
plan->init ();
|
|
|
|
|
2012-08-02 13:24:35 +00:00
|
|
|
hb_ot_shape_planner_t planner (shape_plan);
|
2012-07-27 06:29:32 +00:00
|
|
|
|
2012-11-13 02:23:38 +00:00
|
|
|
planner.shaper = hb_ot_shape_complex_categorize (&planner);
|
2012-07-27 06:29:32 +00:00
|
|
|
|
2016-09-10 10:57:24 +00:00
|
|
|
hb_ot_shape_collect_features (&planner, &shape_plan->props,
|
|
|
|
user_features, num_user_features);
|
2012-07-27 06:29:32 +00:00
|
|
|
|
2016-09-10 10:57:24 +00:00
|
|
|
planner.compile (*plan, coords, num_coords);
|
2012-07-27 06:29:32 +00:00
|
|
|
|
2012-08-02 14:46:34 +00:00
|
|
|
if (plan->shaper->data_create) {
|
|
|
|
plan->data = plan->shaper->data_create (plan);
|
|
|
|
if (unlikely (!plan->data))
|
2017-10-15 10:11:08 +00:00
|
|
|
return nullptr;
|
2012-08-02 14:46:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return plan;
|
2012-07-27 06:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-08-02 14:46:34 +00:00
|
|
|
_hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *plan)
|
2012-07-27 06:29:32 +00:00
|
|
|
{
|
2012-08-02 14:46:34 +00:00
|
|
|
if (plan->shaper->data_destroy)
|
|
|
|
plan->shaper->data_destroy (const_cast<void *> (plan->data));
|
|
|
|
|
2018-05-01 23:01:25 +00:00
|
|
|
plan->fini ();
|
2012-07-30 13:53:06 +00:00
|
|
|
|
2012-08-02 14:46:34 +00:00
|
|
|
free (plan);
|
2012-07-27 06:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shaper
|
|
|
|
*/
|
|
|
|
|
2012-04-15 00:23:58 +00:00
|
|
|
struct hb_ot_shape_context_t
|
|
|
|
{
|
|
|
|
hb_ot_shape_plan_t *plan;
|
|
|
|
hb_font_t *font;
|
|
|
|
hb_face_t *face;
|
|
|
|
hb_buffer_t *buffer;
|
|
|
|
const hb_feature_t *user_features;
|
|
|
|
unsigned int num_user_features;
|
|
|
|
|
|
|
|
/* Transient stuff */
|
2016-12-22 20:40:19 +00:00
|
|
|
bool fallback_positioning;
|
|
|
|
bool fallback_glyph_classes;
|
2012-04-15 00:23:58 +00:00
|
|
|
hb_direction_t target_direction;
|
|
|
|
};
|
|
|
|
|
2009-12-20 19:58:26 +00:00
|
|
|
|
|
|
|
|
2010-05-21 13:34:23 +00:00
|
|
|
/* Main shaper */
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
|
2010-05-21 13:34:23 +00:00
|
|
|
/* Prepare */
|
|
|
|
|
2012-04-15 00:23:58 +00:00
|
|
|
static void
|
|
|
|
hb_set_unicode_props (hb_buffer_t *buffer)
|
2010-11-03 20:37:24 +00:00
|
|
|
{
|
2011-07-21 15:34:59 +00:00
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 18:22:11 +00:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2012-04-12 14:06:52 +00:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2015-11-05 02:46:22 +00:00
|
|
|
_hb_glyph_info_set_unicode_props (&info[i], buffer);
|
2010-11-03 20:37:24 +00:00
|
|
|
}
|
|
|
|
|
2012-09-02 00:38:45 +00:00
|
|
|
static void
|
|
|
|
hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
|
|
|
|
{
|
2012-11-13 22:42:35 +00:00
|
|
|
if (!(buffer->flags & HB_BUFFER_FLAG_BOT) ||
|
Make it easier to use HB_BUFFER_FLAG_BOT/EOT
Previously, we expected users to provide BOT/EOT flags when the
text *segment* was at paragraph boundaries. This meant that for
clients that provide full paragraph to HarfBuzz (eg. Pango), they
had code like this:
hb_buffer_set_flags (hb_buffer,
(item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) |
(item_offset + item_length == paragraph_length ?
HB_BUFFER_FLAG_EOT : 0));
hb_buffer_add_utf8 (hb_buffer,
paragraph_text, paragraph_length,
item_offset, item_length);
After this change such clients can simply say:
hb_buffer_set_flags (hb_buffer,
HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
hb_buffer_add_utf8 (hb_buffer,
paragraph_text, paragraph_length,
item_offset, item_length);
Ie, HarfBuzz itself checks whether the segment is at the beginning/end
of the paragraph. Clients that only pass item-at-a-time to HarfBuzz
continue not setting any flags whatsoever.
Another way to put it is: if there's pre-context text in the buffer,
HarfBuzz ignores the BOT flag. If there's post-context, it ignores
EOT flag.
2014-08-02 20:17:44 +00:00
|
|
|
buffer->context_len[0] ||
|
2012-09-26 01:35:35 +00:00
|
|
|
_hb_glyph_info_get_general_category (&buffer->info[0]) !=
|
2012-09-02 00:38:45 +00:00
|
|
|
HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
|
|
|
|
return;
|
|
|
|
|
2014-07-11 18:54:42 +00:00
|
|
|
if (!font->has_glyph (0x25CCu))
|
2012-09-02 00:38:45 +00:00
|
|
|
return;
|
|
|
|
|
2014-07-26 22:44:15 +00:00
|
|
|
hb_glyph_info_t dottedcircle = {0};
|
2014-07-11 18:54:42 +00:00
|
|
|
dottedcircle.codepoint = 0x25CCu;
|
2015-11-05 02:46:22 +00:00
|
|
|
_hb_glyph_info_set_unicode_props (&dottedcircle, buffer);
|
2012-09-02 00:38:45 +00:00
|
|
|
|
|
|
|
buffer->clear_output ();
|
|
|
|
|
|
|
|
buffer->idx = 0;
|
|
|
|
hb_glyph_info_t info = dottedcircle;
|
|
|
|
info.cluster = buffer->cur().cluster;
|
|
|
|
info.mask = buffer->cur().mask;
|
|
|
|
buffer->output_info (info);
|
2018-06-01 03:03:00 +00:00
|
|
|
while (buffer->idx < buffer->len && buffer->successful)
|
2012-09-02 00:38:45 +00:00
|
|
|
buffer->next_glyph ();
|
|
|
|
|
|
|
|
buffer->swap_buffers ();
|
|
|
|
}
|
|
|
|
|
2010-05-21 13:34:23 +00:00
|
|
|
static void
|
2011-07-21 15:34:59 +00:00
|
|
|
hb_form_clusters (hb_buffer_t *buffer)
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2017-09-05 03:04:59 +00:00
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
|
2015-07-22 15:51:12 +00:00
|
|
|
return;
|
|
|
|
|
2016-02-22 06:07:20 +00:00
|
|
|
/* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */
|
2015-07-22 14:42:20 +00:00
|
|
|
unsigned int base = 0;
|
2011-07-21 15:34:59 +00:00
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 18:22:11 +00:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2010-05-21 13:34:23 +00:00
|
|
|
for (unsigned int i = 1; i < count; i++)
|
2015-07-22 14:42:20 +00:00
|
|
|
{
|
2016-02-22 09:22:44 +00:00
|
|
|
if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) &&
|
|
|
|
!_hb_glyph_info_is_joiner (&info[i])))
|
2015-07-22 14:42:20 +00:00
|
|
|
{
|
2017-09-05 03:04:59 +00:00
|
|
|
if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
|
|
|
|
buffer->merge_clusters (base, i);
|
|
|
|
else
|
|
|
|
buffer->unsafe_to_break (base, i);
|
2015-07-22 14:42:20 +00:00
|
|
|
base = i;
|
|
|
|
}
|
|
|
|
}
|
2017-09-05 03:04:59 +00:00
|
|
|
if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
|
|
|
|
buffer->merge_clusters (base, count);
|
|
|
|
else
|
|
|
|
buffer->unsafe_to_break (base, count);
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
|
|
|
|
2010-10-06 03:00:05 +00:00
|
|
|
static void
|
2011-07-21 15:34:59 +00:00
|
|
|
hb_ensure_native_direction (hb_buffer_t *buffer)
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2011-07-21 15:34:59 +00:00
|
|
|
hb_direction_t direction = buffer->props.direction;
|
2018-05-07 20:58:32 +00:00
|
|
|
hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script);
|
2010-05-21 13:34:23 +00:00
|
|
|
|
2011-05-25 01:04:15 +00:00
|
|
|
/* TODO vertical:
|
|
|
|
* The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
|
|
|
|
* Ogham fonts are supposed to be implemented BTT or not. Need to research that
|
|
|
|
* first. */
|
2018-05-07 20:58:32 +00:00
|
|
|
if ((HB_DIRECTION_IS_HORIZONTAL (direction) &&
|
|
|
|
direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) ||
|
|
|
|
(HB_DIRECTION_IS_VERTICAL (direction) &&
|
|
|
|
direction != HB_DIRECTION_TTB))
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2015-07-22 14:49:08 +00:00
|
|
|
/* Same loop as hb_form_clusters().
|
|
|
|
* Since form_clusters() merged clusters already, we don't merge. */
|
|
|
|
unsigned int base = 0;
|
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
for (unsigned int i = 1; i < count; i++)
|
|
|
|
{
|
|
|
|
if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
|
|
|
|
{
|
2015-07-22 15:51:12 +00:00
|
|
|
if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
|
|
|
|
buffer->merge_clusters (base, i);
|
2015-09-01 15:23:40 +00:00
|
|
|
buffer->reverse_range (base, i);
|
|
|
|
|
2015-07-22 14:49:08 +00:00
|
|
|
base = i;
|
|
|
|
}
|
|
|
|
}
|
2015-07-22 15:51:12 +00:00
|
|
|
if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
|
|
|
|
buffer->merge_clusters (base, count);
|
2015-09-01 15:23:40 +00:00
|
|
|
buffer->reverse_range (base, count);
|
2015-07-22 14:49:08 +00:00
|
|
|
|
|
|
|
buffer->reverse ();
|
|
|
|
|
2011-07-21 15:34:59 +00:00
|
|
|
buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Substitute */
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
|
|
|
hb_ot_mirror_chars (hb_ot_shape_context_t *c)
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2010-12-07 21:22:02 +00:00
|
|
|
if (HB_DIRECTION_IS_FORWARD (c->target_direction))
|
2010-05-21 13:34:23 +00:00
|
|
|
return;
|
|
|
|
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
hb_unicode_funcs_t *unicode = buffer->unicode;
|
2013-12-23 01:48:53 +00:00
|
|
|
hb_mask_t rtlm_mask = c->plan->rtlm_mask;
|
2010-07-23 21:22:11 +00:00
|
|
|
|
2013-10-18 17:33:09 +00:00
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2010-05-21 13:34:23 +00:00
|
|
|
for (unsigned int i = 0; i < count; i++) {
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint);
|
2015-07-22 14:24:26 +00:00
|
|
|
if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint)))
|
2013-10-18 17:33:09 +00:00
|
|
|
info[i].mask |= rtlm_mask;
|
2010-05-21 16:58:20 +00:00
|
|
|
else
|
2013-10-18 17:33:09 +00:00
|
|
|
info[i].codepoint = codepoint;
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-22 21:17:54 +00:00
|
|
|
static inline void
|
|
|
|
hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c)
|
|
|
|
{
|
2015-11-05 02:58:02 +00:00
|
|
|
if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
|
|
|
|
!c->plan->has_frac)
|
2013-12-23 01:48:53 +00:00
|
|
|
return;
|
|
|
|
|
2013-12-22 21:17:54 +00:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
2017-01-18 20:48:13 +00:00
|
|
|
hb_mask_t pre_mask, post_mask;
|
|
|
|
if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
|
|
|
|
{
|
|
|
|
pre_mask = c->plan->numr_mask | c->plan->frac_mask;
|
|
|
|
post_mask = c->plan->frac_mask | c->plan->dnom_mask;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pre_mask = c->plan->frac_mask | c->plan->dnom_mask;
|
|
|
|
post_mask = c->plan->numr_mask | c->plan->frac_mask;
|
|
|
|
}
|
|
|
|
|
2013-12-22 21:17:54 +00:00
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
2014-07-11 18:54:42 +00:00
|
|
|
if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */
|
2013-12-22 21:17:54 +00:00
|
|
|
{
|
|
|
|
unsigned int start = i, end = i + 1;
|
|
|
|
while (start &&
|
|
|
|
_hb_glyph_info_get_general_category (&info[start - 1]) ==
|
|
|
|
HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
|
|
|
|
start--;
|
|
|
|
while (end < count &&
|
|
|
|
_hb_glyph_info_get_general_category (&info[end]) ==
|
|
|
|
HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
|
|
|
|
end++;
|
|
|
|
|
2017-08-31 00:28:22 +00:00
|
|
|
buffer->unsafe_to_break (start, end);
|
|
|
|
|
2013-12-23 00:33:35 +00:00
|
|
|
for (unsigned int j = start; j < i; j++)
|
2017-01-18 20:48:13 +00:00
|
|
|
info[j].mask |= pre_mask;
|
2013-12-23 01:48:53 +00:00
|
|
|
info[i].mask |= c->plan->frac_mask;
|
2013-12-23 00:33:35 +00:00
|
|
|
for (unsigned int j = i + 1; j < end; j++)
|
2017-01-18 20:48:13 +00:00
|
|
|
info[j].mask |= post_mask;
|
2013-12-22 21:17:54 +00:00
|
|
|
|
|
|
|
i = end - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
2013-12-21 05:18:18 +00:00
|
|
|
hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c)
|
2012-08-10 01:58:07 +00:00
|
|
|
{
|
|
|
|
hb_ot_map_t *map = &c->plan->map;
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
2012-08-10 01:58:07 +00:00
|
|
|
|
|
|
|
hb_mask_t global_mask = map->get_global_mask ();
|
2013-10-18 17:33:09 +00:00
|
|
|
buffer->reset_masks (global_mask);
|
2013-12-21 05:18:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
hb_ot_map_t *map = &c->plan->map;
|
|
|
|
hb_buffer_t *buffer = c->buffer;
|
2012-08-10 01:58:07 +00:00
|
|
|
|
2013-12-22 21:17:54 +00:00
|
|
|
hb_ot_shape_setup_masks_fraction (c);
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
if (c->plan->shaper->setup_masks)
|
2013-10-18 17:33:09 +00:00
|
|
|
c->plan->shaper->setup_masks (c->plan, buffer, c->font);
|
2012-08-10 01:58:07 +00:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < c->num_user_features; i++)
|
|
|
|
{
|
|
|
|
const hb_feature_t *feature = &c->user_features[i];
|
|
|
|
if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
|
|
|
|
unsigned int shift;
|
|
|
|
hb_mask_t mask = map->get_mask (feature->tag, &shift);
|
2013-10-18 17:33:09 +00:00
|
|
|
buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
|
2012-08-10 01:58:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-22 17:41:10 +00:00
|
|
|
static void
|
|
|
|
hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
2015-11-05 02:46:22 +00:00
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
|
2018-01-10 02:35:20 +00:00
|
|
|
(buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) ||
|
|
|
|
(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES))
|
2015-07-22 17:41:10 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
hb_glyph_position_t *pos = buffer->pos;
|
|
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
|
|
|
|
pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
|
|
|
|
}
|
2015-07-22 16:36:23 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
2015-11-05 02:46:22 +00:00
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
|
|
|
|
(buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES))
|
2015-07-22 16:36:23 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
unsigned int count = buffer->len;
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
hb_glyph_position_t *pos = buffer->pos;
|
|
|
|
unsigned int i = 0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
2015-07-22 16:41:31 +00:00
|
|
|
if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
|
2015-07-22 16:36:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No default-ignorables found; return. */
|
|
|
|
if (i == count)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hb_codepoint_t space;
|
2018-01-10 02:35:20 +00:00
|
|
|
if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) &&
|
|
|
|
c->font->get_nominal_glyph (' ', &space))
|
2015-07-22 16:36:23 +00:00
|
|
|
{
|
|
|
|
/* Replace default-ignorables with a zero-advance space glyph. */
|
|
|
|
for (/*continue*/; i < count; i++)
|
|
|
|
{
|
2015-07-22 16:41:31 +00:00
|
|
|
if (_hb_glyph_info_is_default_ignorable (&info[i]))
|
2015-07-22 16:36:23 +00:00
|
|
|
info[i].codepoint = space;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-07-22 17:28:39 +00:00
|
|
|
/* Merge clusters and delete default-ignorables.
|
|
|
|
* NOTE! We can't use out-buffer as we have positioning data. */
|
|
|
|
unsigned int j = i;
|
|
|
|
for (; i < count; i++)
|
2015-07-22 16:36:23 +00:00
|
|
|
{
|
2015-07-22 17:28:39 +00:00
|
|
|
if (_hb_glyph_info_is_default_ignorable (&info[i]))
|
2015-07-22 16:36:23 +00:00
|
|
|
{
|
2015-07-22 17:28:39 +00:00
|
|
|
/* Merge clusters.
|
|
|
|
* Same logic as buffer->delete_glyph(), but for in-place removal. */
|
|
|
|
|
|
|
|
unsigned int cluster = info[i].cluster;
|
|
|
|
if (i + 1 < count && cluster == info[i + 1].cluster)
|
|
|
|
continue; /* Cluster survives; do nothing. */
|
|
|
|
|
|
|
|
if (j)
|
|
|
|
{
|
|
|
|
/* Merge cluster backward. */
|
|
|
|
if (cluster < info[j - 1].cluster)
|
|
|
|
{
|
2017-08-11 03:10:12 +00:00
|
|
|
unsigned int mask = info[i].mask;
|
2015-07-22 17:28:39 +00:00
|
|
|
unsigned int old_cluster = info[j - 1].cluster;
|
|
|
|
for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
|
2017-08-11 03:10:12 +00:00
|
|
|
buffer->set_cluster (info[k - 1], cluster, mask);
|
2015-07-22 17:28:39 +00:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i + 1 < count)
|
|
|
|
buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
|
|
|
|
|
2015-07-22 16:36:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-07-22 17:28:39 +00:00
|
|
|
|
|
|
|
if (j != i)
|
|
|
|
{
|
|
|
|
info[j] = info[i];
|
|
|
|
pos[j] = pos[i];
|
|
|
|
}
|
|
|
|
j++;
|
2015-07-22 16:36:23 +00:00
|
|
|
}
|
2015-07-22 17:28:39 +00:00
|
|
|
buffer->len = j;
|
2015-07-22 16:36:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
2012-08-10 02:33:32 +00:00
|
|
|
hb_ot_map_glyphs_fast (hb_buffer_t *buffer)
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2012-08-10 02:33:32 +00:00
|
|
|
/* Normalization process sets up glyph_index(), we just copy it. */
|
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 18:22:11 +00:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2012-08-10 02:33:32 +00:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2014-07-17 18:22:11 +00:00
|
|
|
info[i].codepoint = info[i].glyph_index();
|
2015-08-18 17:42:47 +00:00
|
|
|
|
|
|
|
buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
|
|
|
|
2016-12-22 19:33:54 +00:00
|
|
|
static inline void
|
|
|
|
hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
unsigned int count = c->buffer->len;
|
|
|
|
hb_glyph_info_t *info = c->buffer->info;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
hb_ot_layout_glyph_props_flags_t klass;
|
|
|
|
|
|
|
|
/* Never mark default-ignorables as marks.
|
|
|
|
* They won't get in the way of lookups anyway,
|
|
|
|
* but having them as mark will cause them to be skipped
|
|
|
|
* over if the lookup-flag says so, but at least for the
|
|
|
|
* Mongolian variation selectors, looks like Uniscribe
|
|
|
|
* marks them as non-mark. Some Mongolian fonts without
|
|
|
|
* GDEF rely on this. Another notable character that
|
|
|
|
* this applies to is COMBINING GRAPHEME JOINER. */
|
|
|
|
klass = (_hb_glyph_info_get_general_category (&info[i]) !=
|
|
|
|
HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ||
|
|
|
|
_hb_glyph_info_is_default_ignorable (&info[i])) ?
|
|
|
|
HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH :
|
|
|
|
HB_OT_LAYOUT_GLYPH_PROPS_MARK;
|
|
|
|
_hb_glyph_info_set_glyph_props (&info[i], klass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
|
|
|
hb_ot_substitute_default (hb_ot_shape_context_t *c)
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
hb_ot_mirror_chars (c);
|
|
|
|
|
2013-10-18 17:33:09 +00:00
|
|
|
HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
|
2012-08-10 02:33:32 +00:00
|
|
|
|
2013-10-18 17:33:09 +00:00
|
|
|
_hb_ot_shape_normalize (c->plan, buffer, c->font);
|
2012-08-10 01:58:07 +00:00
|
|
|
|
|
|
|
hb_ot_shape_setup_masks (c);
|
2011-07-28 20:48:43 +00:00
|
|
|
|
2012-09-01 23:20:41 +00:00
|
|
|
/* This is unfortunate to go here, but necessary... */
|
2016-12-22 20:40:19 +00:00
|
|
|
if (c->fallback_positioning)
|
2013-10-18 17:33:09 +00:00
|
|
|
_hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer);
|
2012-09-01 23:20:41 +00:00
|
|
|
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_ot_map_glyphs_fast (buffer);
|
2012-08-10 02:33:32 +00:00
|
|
|
|
2013-10-18 17:33:09 +00:00
|
|
|
HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index);
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
2011-07-25 04:44:50 +00:00
|
|
|
hb_ot_substitute_complex (hb_ot_shape_context_t *c)
|
|
|
|
{
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
|
|
|
|
hb_ot_layout_substitute_start (c->font, buffer);
|
2012-07-30 23:30:01 +00:00
|
|
|
|
2016-12-22 19:33:54 +00:00
|
|
|
if (!hb_ot_layout_has_glyph_classes (c->face))
|
|
|
|
hb_synthesize_glyph_classes (c);
|
|
|
|
|
2013-10-18 17:33:09 +00:00
|
|
|
c->plan->substitute (c->font, buffer);
|
2018-01-09 16:55:17 +00:00
|
|
|
|
2018-07-17 16:14:45 +00:00
|
|
|
if (0) /* XXX Call morx instead. */
|
|
|
|
hb_aat_layout_substitute (c->font, c->buffer);
|
2011-07-25 04:44:50 +00:00
|
|
|
}
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
|
|
|
hb_ot_substitute (hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
hb_ot_substitute_default (c);
|
2015-11-05 06:28:44 +00:00
|
|
|
|
|
|
|
_hb_buffer_allocate_gsubgpos_vars (c->buffer);
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
hb_ot_substitute_complex (c);
|
|
|
|
}
|
2010-05-21 13:34:23 +00:00
|
|
|
|
|
|
|
/* Position */
|
|
|
|
|
2013-05-20 13:18:52 +00:00
|
|
|
static inline void
|
2014-06-10 12:10:30 +00:00
|
|
|
adjust_mark_offsets (hb_glyph_position_t *pos)
|
|
|
|
{
|
|
|
|
pos->x_offset -= pos->x_advance;
|
|
|
|
pos->y_offset -= pos->y_advance;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
zero_mark_width (hb_glyph_position_t *pos)
|
|
|
|
{
|
|
|
|
pos->x_advance = 0;
|
|
|
|
pos->y_advance = 0;
|
|
|
|
}
|
|
|
|
|
2013-05-20 13:18:52 +00:00
|
|
|
static inline void
|
2014-06-10 12:10:30 +00:00
|
|
|
zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets)
|
2013-05-20 13:18:52 +00:00
|
|
|
{
|
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 18:22:11 +00:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2013-05-20 13:18:52 +00:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2014-07-17 18:22:11 +00:00
|
|
|
if (_hb_glyph_info_is_mark (&info[i]))
|
2013-05-20 13:18:52 +00:00
|
|
|
{
|
2014-06-10 12:10:30 +00:00
|
|
|
if (adjust_offsets)
|
|
|
|
adjust_mark_offsets (&buffer->pos[i]);
|
|
|
|
zero_mark_width (&buffer->pos[i]);
|
2013-05-20 13:18:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
|
|
|
hb_ot_position_default (hb_ot_shape_context_t *c)
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_direction_t direction = c->buffer->props.direction;
|
2010-10-06 03:00:05 +00:00
|
|
|
unsigned int count = c->buffer->len;
|
2013-10-18 17:33:09 +00:00
|
|
|
hb_glyph_info_t *info = c->buffer->info;
|
|
|
|
hb_glyph_position_t *pos = c->buffer->pos;
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 14:44:57 +00:00
|
|
|
|
2015-11-05 03:28:17 +00:00
|
|
|
if (HB_DIRECTION_IS_HORIZONTAL (direction))
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
pos[i].x_advance = c->font->get_glyph_h_advance (info[i].codepoint);
|
2015-11-26 23:48:42 +00:00
|
|
|
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
|
2015-11-05 05:53:16 +00:00
|
|
|
if (c->font->has_glyph_h_origin_func ())
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
c->font->subtract_glyph_h_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
2015-11-05 03:28:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2015-11-26 23:48:42 +00:00
|
|
|
{
|
2015-11-05 03:28:17 +00:00
|
|
|
pos[i].y_advance = c->font->get_glyph_v_advance (info[i].codepoint);
|
2015-11-26 23:48:42 +00:00
|
|
|
c->font->subtract_glyph_v_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
|
|
|
}
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 14:44:57 +00:00
|
|
|
}
|
2015-11-05 01:27:07 +00:00
|
|
|
if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK)
|
|
|
|
_hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer);
|
2013-09-14 00:17:42 +00:00
|
|
|
}
|
|
|
|
|
2016-12-22 20:40:19 +00:00
|
|
|
static inline void
|
2013-09-14 00:17:42 +00:00
|
|
|
hb_ot_position_complex (hb_ot_shape_context_t *c)
|
|
|
|
{
|
|
|
|
unsigned int count = c->buffer->len;
|
2017-10-03 15:22:43 +00:00
|
|
|
hb_glyph_info_t *info = c->buffer->info;
|
|
|
|
hb_glyph_position_t *pos = c->buffer->pos;
|
2016-02-11 09:27:41 +00:00
|
|
|
|
2014-06-10 12:10:30 +00:00
|
|
|
/* If the font has no GPOS, AND, no fallback positioning will
|
|
|
|
* happen, AND, direction is forward, then when zeroing mark
|
|
|
|
* widths, we shift the mark with it, such that the mark
|
|
|
|
* is positioned hanging over the previous glyph. When
|
|
|
|
* direction is backward we don't shift and it will end up
|
|
|
|
* hanging over the next glyph after the final reordering.
|
|
|
|
* If fallback positinoing happens or GPOS is present, we don't
|
|
|
|
* care.
|
|
|
|
*/
|
2016-12-22 20:40:19 +00:00
|
|
|
bool adjust_offsets_when_zeroing = c->fallback_positioning &&
|
|
|
|
!c->plan->shaper->fallback_position &&
|
|
|
|
HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 14:44:57 +00:00
|
|
|
|
2017-10-03 15:22:43 +00:00
|
|
|
/* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
|
|
|
|
|
|
|
|
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
|
|
|
|
if (c->font->has_glyph_h_origin_func ())
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
c->font->add_glyph_h_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
|
|
|
|
|
|
|
hb_ot_layout_position_start (c->font, c->buffer);
|
|
|
|
|
2013-10-27 23:20:59 +00:00
|
|
|
switch (c->plan->shaper->zero_width_marks)
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 14:44:57 +00:00
|
|
|
{
|
2013-05-20 13:11:35 +00:00
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
|
2014-06-10 12:10:30 +00:00
|
|
|
zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 14:44:57 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
|
2013-05-20 13:11:35 +00:00
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
|
Adjust mark advance-width zeroing logic for Myanmar
Before, we were zeroing advance width of attached marks for
non-Indic scripts, and not doing it for Indic.
We have now three different behaviors, which seem to better
reflect what Uniscribe is doing:
- For Indic, no explicit zeroing happens whatsoever, which
is the same as before,
- For Myanmar, zero advance width of glyphs marked as marks
*in GDEF*, and do that *before* applying GPOS. This seems
to be what the new Win8 Myanmar shaper does,
- For everything else, zero advance width of glyphs that are
from General_Category=Mn Unicode characters, and do so
before applying GPOS. This seems to be what Uniscribe does
for Latin at least.
With these changes, positioning of all tests matches for Myanmar,
except for the glitch in Uniscribe not applying 'mark'. See preivous
commit.
2013-02-12 14:44:57 +00:00
|
|
|
break;
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
2011-07-25 04:44:50 +00:00
|
|
|
|
2016-12-22 20:40:19 +00:00
|
|
|
if (likely (!c->fallback_positioning))
|
2012-08-02 14:07:58 +00:00
|
|
|
c->plan->position (c->font, c->buffer);
|
2011-07-25 04:44:50 +00:00
|
|
|
|
2013-10-27 23:20:59 +00:00
|
|
|
switch (c->plan->shaper->zero_width_marks)
|
2013-02-13 10:57:24 +00:00
|
|
|
{
|
2013-05-20 13:11:35 +00:00
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
|
2014-06-10 12:10:30 +00:00
|
|
|
zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
|
2013-05-20 13:11:35 +00:00
|
|
|
break;
|
|
|
|
|
2013-02-13 10:57:24 +00:00
|
|
|
default:
|
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
|
2013-05-20 13:11:35 +00:00
|
|
|
case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
|
2013-02-13 10:57:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-02-11 09:34:28 +00:00
|
|
|
/* Finishing off GPOS has to follow a certain order. */
|
|
|
|
hb_ot_layout_position_finish_advances (c->font, c->buffer);
|
2016-02-11 09:27:41 +00:00
|
|
|
hb_ot_zero_width_default_ignorables (c);
|
2016-02-11 09:34:28 +00:00
|
|
|
hb_ot_layout_position_finish_offsets (c->font, c->buffer);
|
2017-10-03 15:22:43 +00:00
|
|
|
|
|
|
|
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
|
|
|
|
if (c->font->has_glyph_h_origin_func ())
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
|
|
|
c->font->subtract_glyph_h_origin (info[i].codepoint,
|
|
|
|
&pos[i].x_offset,
|
|
|
|
&pos[i].y_offset);
|
2011-07-25 04:44:50 +00:00
|
|
|
}
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
static inline void
|
|
|
|
hb_ot_position (hb_ot_shape_context_t *c)
|
|
|
|
{
|
2016-02-11 09:27:41 +00:00
|
|
|
c->buffer->clear_positions ();
|
2013-09-14 00:17:42 +00:00
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
hb_ot_position_default (c);
|
|
|
|
|
2016-12-22 20:40:19 +00:00
|
|
|
hb_ot_position_complex (c);
|
2012-08-10 01:58:07 +00:00
|
|
|
|
2016-12-22 20:40:19 +00:00
|
|
|
if (c->fallback_positioning && c->plan->shaper->fallback_position)
|
2012-11-14 21:48:26 +00:00
|
|
|
_hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
|
2012-08-10 01:58:07 +00:00
|
|
|
|
|
|
|
if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
|
|
|
|
hb_buffer_reverse (c->buffer);
|
|
|
|
|
2012-11-14 21:48:26 +00:00
|
|
|
/* Visual fallback goes here. */
|
|
|
|
|
2016-12-22 20:40:19 +00:00
|
|
|
if (c->fallback_positioning)
|
2013-02-21 20:23:39 +00:00
|
|
|
_hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
|
2014-08-02 21:18:46 +00:00
|
|
|
|
|
|
|
_hb_buffer_deallocate_gsubgpos_vars (c->buffer);
|
2018-02-24 09:19:42 +00:00
|
|
|
|
|
|
|
//hb_aat_layout_position (c->font, c->buffer);
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
|
|
|
|
2017-08-12 02:06:07 +00:00
|
|
|
static inline void
|
|
|
|
hb_propagate_flags (hb_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
/* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
|
|
|
|
* Simplifies using them. */
|
|
|
|
|
|
|
|
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK))
|
|
|
|
return;
|
|
|
|
|
|
|
|
hb_glyph_info_t *info = buffer->info;
|
|
|
|
|
|
|
|
foreach_cluster (buffer, start, end)
|
|
|
|
{
|
|
|
|
unsigned int mask = 0;
|
|
|
|
for (unsigned int i = start; i < end; i++)
|
|
|
|
if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
|
|
|
|
{
|
|
|
|
mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (mask)
|
|
|
|
for (unsigned int i = start; i < end; i++)
|
|
|
|
info[i].mask |= mask;
|
|
|
|
}
|
|
|
|
}
|
2012-08-10 01:58:07 +00:00
|
|
|
|
|
|
|
/* Pull it all together! */
|
2010-05-21 13:34:23 +00:00
|
|
|
|
2010-10-06 03:00:05 +00:00
|
|
|
static void
|
2012-07-27 06:29:32 +00:00
|
|
|
hb_ot_shape_internal (hb_ot_shape_context_t *c)
|
2010-05-21 13:34:23 +00:00
|
|
|
{
|
2011-07-28 20:48:43 +00:00
|
|
|
c->buffer->deallocate_var_all ();
|
2015-11-05 01:27:07 +00:00
|
|
|
c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
|
2018-07-10 12:12:37 +00:00
|
|
|
if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
|
2015-11-06 07:44:59 +00:00
|
|
|
{
|
2017-11-15 05:53:48 +00:00
|
|
|
c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR,
|
2015-11-06 07:44:59 +00:00
|
|
|
(unsigned) HB_BUFFER_MAX_LEN_MIN);
|
|
|
|
}
|
2018-07-10 12:12:37 +00:00
|
|
|
if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
|
2017-11-15 05:53:48 +00:00
|
|
|
{
|
|
|
|
c->buffer->max_ops = MAX (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR,
|
|
|
|
(unsigned) HB_BUFFER_MAX_OPS_MIN);
|
|
|
|
}
|
2011-07-28 20:48:43 +00:00
|
|
|
|
2016-12-22 20:40:19 +00:00
|
|
|
bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan);
|
|
|
|
//c->fallback_substitute = disable_otl || !hb_ot_layout_has_substitution (c->face);
|
|
|
|
c->fallback_positioning = disable_otl || !hb_ot_layout_has_positioning (c->face);
|
|
|
|
c->fallback_glyph_classes = disable_otl || !hb_ot_layout_has_glyph_classes (c->face);
|
|
|
|
|
2010-10-12 19:35:45 +00:00
|
|
|
/* Save the original direction, we use it later. */
|
2010-12-07 21:22:02 +00:00
|
|
|
c->target_direction = c->buffer->props.direction;
|
2010-10-12 19:35:45 +00:00
|
|
|
|
2013-10-17 22:42:39 +00:00
|
|
|
_hb_buffer_allocate_unicode_vars (c->buffer);
|
2011-07-28 20:48:43 +00:00
|
|
|
|
2012-06-09 00:40:02 +00:00
|
|
|
c->buffer->clear_output ();
|
|
|
|
|
2017-09-05 03:04:59 +00:00
|
|
|
hb_ot_shape_initialize_masks (c);
|
2012-05-09 13:04:13 +00:00
|
|
|
hb_set_unicode_props (c->buffer);
|
2012-09-02 00:38:45 +00:00
|
|
|
hb_insert_dotted_circle (c->buffer, c->font);
|
2017-09-05 03:04:59 +00:00
|
|
|
|
2011-07-22 20:15:32 +00:00
|
|
|
hb_form_clusters (c->buffer);
|
|
|
|
|
2011-07-21 15:34:59 +00:00
|
|
|
hb_ensure_native_direction (c->buffer);
|
2011-07-21 05:11:09 +00:00
|
|
|
|
2015-11-05 21:24:15 +00:00
|
|
|
if (c->plan->shaper->preprocess_text)
|
|
|
|
c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
|
|
|
|
|
2012-08-10 01:58:07 +00:00
|
|
|
hb_ot_substitute (c);
|
|
|
|
hb_ot_position (c);
|
2010-05-21 13:34:23 +00:00
|
|
|
|
2015-07-22 17:41:10 +00:00
|
|
|
hb_ot_hide_default_ignorables (c);
|
|
|
|
|
2015-11-05 21:24:15 +00:00
|
|
|
if (c->plan->shaper->postprocess_glyphs)
|
|
|
|
c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
|
|
|
|
|
2017-08-12 02:06:07 +00:00
|
|
|
hb_propagate_flags (c->buffer);
|
|
|
|
|
2013-10-17 22:42:39 +00:00
|
|
|
_hb_buffer_deallocate_unicode_vars (c->buffer);
|
2011-07-28 20:48:43 +00:00
|
|
|
|
2010-12-07 21:22:02 +00:00
|
|
|
c->buffer->props.direction = c->target_direction;
|
2011-07-28 21:06:46 +00:00
|
|
|
|
2015-11-06 07:44:59 +00:00
|
|
|
c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
|
2017-11-15 05:53:48 +00:00
|
|
|
c->buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
|
2011-07-28 21:06:46 +00:00
|
|
|
c->buffer->deallocate_var_all ();
|
2010-10-06 03:00:05 +00:00
|
|
|
}
|
|
|
|
|
2010-10-12 20:57:47 +00:00
|
|
|
|
2011-08-05 02:31:05 +00:00
|
|
|
hb_bool_t
|
2012-07-27 02:05:39 +00:00
|
|
|
_hb_ot_shape (hb_shape_plan_t *shape_plan,
|
|
|
|
hb_font_t *font,
|
2012-04-12 18:53:53 +00:00
|
|
|
hb_buffer_t *buffer,
|
|
|
|
const hb_feature_t *features,
|
|
|
|
unsigned int num_features)
|
2010-10-06 03:00:05 +00:00
|
|
|
{
|
2012-07-27 06:29:32 +00:00
|
|
|
hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features};
|
|
|
|
hb_ot_shape_internal (&c);
|
2011-08-05 02:31:05 +00:00
|
|
|
|
2012-06-06 00:35:40 +00:00
|
|
|
return true;
|
2010-05-21 13:34:23 +00:00
|
|
|
}
|
2012-04-24 20:56:37 +00:00
|
|
|
|
|
|
|
|
2015-06-01 11:22:01 +00:00
|
|
|
/**
|
2015-11-27 00:30:37 +00:00
|
|
|
* hb_ot_shape_plan_collect_lookups:
|
|
|
|
*
|
2015-06-01 11:22:01 +00:00
|
|
|
* Since: 0.9.7
|
|
|
|
**/
|
2012-11-16 02:39:46 +00:00
|
|
|
void
|
|
|
|
hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
|
|
|
|
hb_tag_t table_tag,
|
|
|
|
hb_set_t *lookup_indexes /* OUT */)
|
|
|
|
{
|
2012-11-16 22:08:05 +00:00
|
|
|
/* XXX Does the first part always succeed? */
|
2012-11-16 02:39:46 +00:00
|
|
|
HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes);
|
|
|
|
}
|
2012-08-10 02:33:32 +00:00
|
|
|
|
2012-11-16 02:39:46 +00:00
|
|
|
|
|
|
|
/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
|
|
|
|
static void
|
|
|
|
add_char (hb_font_t *font,
|
|
|
|
hb_unicode_funcs_t *unicode,
|
|
|
|
hb_bool_t mirror,
|
|
|
|
hb_codepoint_t u,
|
|
|
|
hb_set_t *glyphs)
|
2012-08-10 02:33:32 +00:00
|
|
|
{
|
2012-11-16 02:39:46 +00:00
|
|
|
hb_codepoint_t glyph;
|
2016-02-24 10:05:23 +00:00
|
|
|
if (font->get_nominal_glyph (u, &glyph))
|
2012-11-16 02:39:46 +00:00
|
|
|
glyphs->add (glyph);
|
|
|
|
if (mirror)
|
|
|
|
{
|
|
|
|
hb_codepoint_t m = unicode->mirroring (u);
|
2016-02-24 10:05:23 +00:00
|
|
|
if (m != u && font->get_nominal_glyph (m, &glyph))
|
2012-11-16 02:39:46 +00:00
|
|
|
glyphs->add (glyph);
|
|
|
|
}
|
2012-08-10 02:33:32 +00:00
|
|
|
}
|
|
|
|
|
2012-11-16 02:39:46 +00:00
|
|
|
|
2015-06-01 11:22:01 +00:00
|
|
|
/**
|
2015-11-27 00:30:37 +00:00
|
|
|
* hb_ot_shape_glyphs_closure:
|
|
|
|
*
|
2015-06-01 11:22:01 +00:00
|
|
|
* Since: 0.9.2
|
|
|
|
**/
|
2012-04-24 20:56:37 +00:00
|
|
|
void
|
|
|
|
hb_ot_shape_glyphs_closure (hb_font_t *font,
|
|
|
|
hb_buffer_t *buffer,
|
|
|
|
const hb_feature_t *features,
|
|
|
|
unsigned int num_features,
|
|
|
|
hb_set_t *glyphs)
|
|
|
|
{
|
2017-10-15 10:11:08 +00:00
|
|
|
const char *shapers[] = {"ot", nullptr};
|
2012-11-16 02:39:46 +00:00
|
|
|
hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
|
|
|
|
features, num_features, shapers);
|
2012-04-24 20:56:37 +00:00
|
|
|
|
2012-11-16 02:39:46 +00:00
|
|
|
bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL;
|
2012-04-24 20:56:37 +00:00
|
|
|
|
|
|
|
unsigned int count = buffer->len;
|
2014-07-17 18:22:11 +00:00
|
|
|
hb_glyph_info_t *info = buffer->info;
|
2012-04-24 20:56:37 +00:00
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2014-07-17 18:22:11 +00:00
|
|
|
add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs);
|
2012-11-16 02:39:46 +00:00
|
|
|
|
2017-10-22 21:48:06 +00:00
|
|
|
hb_set_t *lookups = hb_set_create ();
|
|
|
|
hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups);
|
2018-06-06 23:02:51 +00:00
|
|
|
hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs);
|
2017-10-22 21:48:06 +00:00
|
|
|
|
|
|
|
hb_set_destroy (lookups);
|
2012-07-27 06:29:32 +00:00
|
|
|
|
|
|
|
hb_shape_plan_destroy (shape_plan);
|
2012-04-24 20:56:37 +00:00
|
|
|
}
|