Update Harfbuzz to version 10.3.0

[ChangeLog][Third-Party Code] Upgraded Harfbuzz to version
10.3.0.

Pick-to: 6.8 6.5 5.15
Change-Id: I3a4daaa9c0747c4f4717ac8f0f9e7baf9a9a449e
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
(cherry picked from commit 82a3357d007837b1d54dcf6dd6309363649d186f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2025-02-26 08:35:53 +01:00 committed by Qt Cherry-pick Bot
parent 0777144b45
commit 1a4e2e869f
83 changed files with 2467 additions and 1257 deletions

View File

@ -25,6 +25,7 @@ qt_internal_add_3rdparty_library(BundledHarfbuzz
src/hb-cache.hh
src/hb-common.h
src/hb-debug.hh
src/hb-decycler.hh
src/hb-deprecated.h
src/hb-draw.cc src/hb-draw.h src/hb-draw.hh
src/hb-face.cc src/hb-face.h src/hb-face.hh

View File

@ -3,7 +3,6 @@
[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://app.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz)

View File

@ -7,8 +7,8 @@
"Description": "HarfBuzz is an OpenType text shaping engine.",
"Homepage": "http://harfbuzz.org",
"Version": "10.2.0",
"DownloadLocation": "https://github.com/harfbuzz/harfbuzz/releases/tag/10.2.0",
"Version": "10.3.0",
"DownloadLocation": "https://github.com/harfbuzz/harfbuzz/releases/tag/10.3.0",
"PURL": "pkg:github/harfbuzz/harfbuzz@$<VERSION>",
"CPE": "cpe:2.3:a:harfbuzz_project:harfbuzz:$<VERSION>:*:*:*:*:*:*:*",
"License": "MIT License",

View File

@ -29,11 +29,14 @@
#define OT_COLOR_COLR_COLR_HH
#include "../../../hb.hh"
#include "../../../hb-decycler.hh"
#include "../../../hb-open-type.hh"
#include "../../../hb-ot-var-common.hh"
#include "../../../hb-paint.hh"
#include "../../../hb-paint-extents.hh"
#include "../CPAL/CPAL.hh"
/*
* COLR -- Color
* https://docs.microsoft.com/en-us/typography/opentype/spec/colr
@ -66,11 +69,11 @@ public:
hb_paint_funcs_t *funcs;
void *data;
hb_font_t *font;
unsigned int palette_index;
hb_array_t<const BGRAColor> palette;
hb_color_t foreground;
ItemVarStoreInstancer &instancer;
hb_map_t current_glyphs;
hb_map_t current_layers;
hb_decycler_t glyphs_decycler;
hb_decycler_t layers_decycler;
int depth_left = HB_MAX_NESTING_LEVEL;
int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
@ -85,7 +88,11 @@ public:
funcs (funcs_),
data (data_),
font (font_),
palette_index (palette_),
palette (
#ifndef HB_NO_COLOR
font->face->table.CPAL->get_palette_colors (palette_)
#endif
),
foreground (foreground_),
instancer (instancer_)
{ }
@ -99,12 +106,7 @@ public:
if (color_index != 0xffff)
{
if (!funcs->custom_palette_color (data, color_index, &color))
{
unsigned int clen = 1;
hb_face_t *face = hb_font_get_face (font);
hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
}
color = palette[color_index];
*is_foreground = false;
}
@ -2134,12 +2136,16 @@ struct COLR
const ItemVariationStore &get_var_store () const
{ return colr->get_var_store (); }
const ItemVariationStore *get_var_store_ptr () const
{ return colr->get_var_store_ptr (); }
bool has_delta_set_index_map () const
{ return colr->has_delta_set_index_map (); }
const DeltaSetIndexMap &get_delta_set_index_map () const
{ return colr->get_delta_set_index_map (); }
const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
{ return colr->get_delta_set_index_map_ptr (); }
private:
hb_blob_ptr_t<COLR> colr;
@ -2232,9 +2238,13 @@ struct COLR
const DeltaSetIndexMap &get_delta_set_index_map () const
{ return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }
const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
{ return has_delta_set_index_map () && hb_barrier () ? &(this+varIdxMap) : nullptr; }
const ItemVariationStore &get_var_store () const
{ return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }
const ItemVariationStore *get_var_store_ptr () const
{ return has_var_store () && hb_barrier () ? &(this+varStore) : nullptr; }
const ClipList &get_clip_list () const
{ return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }
@ -2482,9 +2492,9 @@ struct COLR
* after instancing */
if (!subset_varstore (c, colr_prime)) return_trace (false);
ItemVarStoreInstancer instancer (&(get_var_store ()),
&(get_delta_set_index_map ()),
c->plan->normalized_coords.as_array ());
ItemVarStoreInstancer instancer (get_var_store_ptr (),
get_delta_set_index_map_ptr (),
c->plan->normalized_coords.as_array ());
if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
return_trace (false);
@ -2513,8 +2523,8 @@ struct COLR
get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
{
ItemVarStoreInstancer instancer (&(get_var_store ()),
&(get_delta_set_index_map ()),
ItemVarStoreInstancer instancer (get_var_store_ptr (),
get_delta_set_index_map_ptr (),
hb_array (font->coords, font->num_coords));
if (get_clip (glyph, extents, instancer))
@ -2575,11 +2585,13 @@ struct COLR
bool
paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
{
ItemVarStoreInstancer instancer (&(get_var_store ()),
&(get_delta_set_index_map ()),
hb_array (font->coords, font->num_coords));
ItemVarStoreInstancer instancer (get_var_store_ptr (),
get_delta_set_index_map_ptr (),
hb_array (font->coords, font->num_coords));
hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
c.current_glyphs.add (glyph);
hb_decycler_node_t node (c.glyphs_decycler);
node.visit (glyph);
if (version >= 1)
{
@ -2695,19 +2707,16 @@ void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
{
TRACE_PAINT (this);
const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
hb_decycler_node_t node (c->layers_decycler);
for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
{
if (unlikely (c->current_layers.has (i)))
continue;
c->current_layers.add (i);
if (unlikely (!node.visit (i)))
return;
const Paint &paint = paint_offset_lists.get_paint (i);
c->funcs->push_group (c->data);
c->recurse (paint);
c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
c->current_layers.del (i);
}
}
@ -2715,16 +2724,14 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
{
TRACE_PAINT (this);
if (unlikely (c->current_glyphs.has (gid)))
hb_decycler_node_t node (c->glyphs_decycler);
if (unlikely (!node.visit (gid)))
return;
c->current_glyphs.add (gid);
c->funcs->push_inverse_root_transform (c->data, c->font);
if (c->funcs->color_glyph (c->data, gid, c->font))
{
c->funcs->pop_transform (c->data);
c->current_glyphs.del (gid);
return;
}
c->funcs->pop_transform (c->data);
@ -2747,8 +2754,6 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
if (has_clip_box)
c->funcs->pop_clip (c->data);
c->current_glyphs.del (gid);
}
} /* namespace OT */

View File

@ -187,6 +187,14 @@ struct CPAL
hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
{ return v1 ().get_color_name_id (this, color_index, numColors); }
hb_array_t<const BGRAColor> get_palette_colors (unsigned int palette_index) const
{
if (unlikely (palette_index >= numPalettes))
return hb_array_t<const BGRAColor> ();
unsigned int start_index = colorRecordIndicesZ[palette_index];
hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
return all_colors.sub_array (start_index, numColors);
}
unsigned int get_palette_colors (unsigned int palette_index,
unsigned int start_offset,
unsigned int *color_count, /* IN/OUT. May be NULL. */

View File

@ -96,6 +96,15 @@ struct Coverage
default:return NOT_COVERED;
}
}
unsigned int get_coverage (hb_codepoint_t glyph_id,
hb_ot_lookup_cache_t *cache) const
{
unsigned coverage;
if (cache && cache->get (glyph_id, &coverage)) return coverage;
coverage = get_coverage (glyph_id);
if (cache) cache->set (glyph_id, coverage);
return coverage;
}
unsigned get_population () const
{
@ -201,6 +210,19 @@ struct Coverage
}
}
unsigned cost () const
{
switch (u.format) {
case 1: hb_barrier (); return u.format1.cost ();
case 2: hb_barrier (); return u.format2.cost ();
#ifndef HB_NO_BEYOND_64K
case 3: hb_barrier (); return u.format3.cost ();
case 4: hb_barrier (); return u.format4.cost ();
#endif
default:return 0u;
}
}
/* Might return false if array looks unsorted.
* Used for faster rejection of corrupt data. */
template <typename set_t>

View File

@ -103,6 +103,8 @@ struct CoverageFormat1_3
intersect_glyphs << glyphArray[i];
}
unsigned cost () const { return hb_bit_storage ((unsigned) glyphArray.len); /* bsearch cost */ }
template <typename set_t>
bool collect_coverage (set_t *glyphs) const
{ return glyphs->add_sorted_array (glyphArray.as_array ()); }

View File

@ -157,6 +157,8 @@ struct CoverageFormat2_4
}
}
unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ }
template <typename set_t>
bool collect_coverage (set_t *glyphs) const
{

View File

@ -103,12 +103,50 @@ struct PairPosFormat1_3
const Coverage &get_coverage () const { return this+coverage; }
bool apply (hb_ot_apply_context_t *c) const
unsigned cache_cost () const
{
return (this+coverage).cost ();
}
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
switch (op)
{
case hb_ot_lookup_cache_op_t::CREATE:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
if (likely (cache))
cache->clear ();
return cache;
}
case hb_ot_lookup_cache_op_t::ENTER:
return (void *) true;
case hb_ot_lookup_cache_op_t::LEAVE:
return nullptr;
case hb_ot_lookup_cache_op_t::DESTROY:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
hb_free (cache);
return nullptr;
}
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
bool _apply (hb_ot_apply_context_t *c, bool cached) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache);
#else
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
#endif
if (index == NOT_COVERED) return_trace (false);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset_fast (buffer->idx);
@ -156,7 +194,7 @@ struct PairPosFormat1_3
strip = true;
newFormats = compute_effective_value_formats (glyphset, strip, true);
}
out->valueFormat[0] = newFormats.first;
out->valueFormat[1] = newFormats.second;

View File

@ -123,12 +123,61 @@ struct PairPosFormat2_4 : ValueBase
const Coverage &get_coverage () const { return this+coverage; }
bool apply (hb_ot_apply_context_t *c) const
struct pair_pos_cache_t
{
hb_ot_lookup_cache_t coverage;
hb_ot_lookup_cache_t first;
hb_ot_lookup_cache_t second;
};
unsigned cache_cost () const
{
return (this+coverage).cost () + (this+classDef1).cost () + (this+classDef2).cost ();
}
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
switch (op)
{
case hb_ot_lookup_cache_op_t::CREATE:
{
pair_pos_cache_t *cache = (pair_pos_cache_t *) hb_malloc (sizeof (pair_pos_cache_t));
if (likely (cache))
{
cache->coverage.clear ();
cache->first.clear ();
cache->second.clear ();
}
return cache;
}
case hb_ot_lookup_cache_op_t::ENTER:
return (void *) true;
case hb_ot_lookup_cache_op_t::LEAVE:
return nullptr;
case hb_ot_lookup_cache_op_t::DESTROY:
{
pair_pos_cache_t *cache = (pair_pos_cache_t *) p;
hb_free (cache);
return nullptr;
}
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
bool _apply (hb_ot_apply_context_t *c, bool cached) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
pair_pos_cache_t *cache = cached ? (pair_pos_cache_t *) c->lookup_accel->cache : nullptr;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
#else
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
#endif
if (index == NOT_COVERED) return_trace (false);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset_fast (buffer->idx);
@ -139,8 +188,13 @@ struct PairPosFormat2_4 : ValueBase
return_trace (false);
}
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint, cache ? &cache->first : nullptr);
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint, cache ? &cache->second : nullptr);
#else
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
#endif
if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
{
buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);

View File

@ -67,7 +67,7 @@ struct SinglePosFormat1 : ValueBase
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{

View File

@ -66,7 +66,7 @@ struct SinglePosFormat2 : ValueBase
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
if (unlikely (index >= valueCount)) return_trace (false);

View File

@ -74,7 +74,7 @@ struct AlternateSubstFormat1_2
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
return_trace ((this+alternateSet[index]).apply (c));
}

View File

@ -78,12 +78,49 @@ struct LigatureSubstFormat1_2
return lig_set.would_apply (c);
}
bool apply (hb_ot_apply_context_t *c) const
unsigned cache_cost () const
{
return (this+coverage).cost ();
}
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
switch (op)
{
case hb_ot_lookup_cache_op_t::CREATE:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
if (likely (cache))
cache->clear ();
return cache;
}
case hb_ot_lookup_cache_op_t::ENTER:
return (void *) true;
case hb_ot_lookup_cache_op_t::LEAVE:
return nullptr;
case hb_ot_lookup_cache_op_t::DESTROY:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
hb_free (cache);
return nullptr;
}
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
bool _apply (hb_ot_apply_context_t *c, bool cached) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache);
#else
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
#endif
if (index == NOT_COVERED) return_trace (false);
const auto &lig_set = this+ligatureSet[index];
return_trace (lig_set.apply (c));

View File

@ -66,7 +66,7 @@ struct MultipleSubstFormat1_2
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
return_trace ((this+sequence[index]).apply (c));
}

View File

@ -112,7 +112,7 @@ struct ReverseChainSingleSubstFormat1
{
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
return_trace (false); /* No chaining to this type */

View File

@ -128,7 +128,7 @@ struct SingleSubstFormat1_3
TRACE_APPLY (this);
hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
unsigned int index = (this+coverage).get_coverage (glyph_id);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
hb_codepoint_t d = deltaGlyphID;
hb_codepoint_t mask = get_mask ();

View File

@ -104,7 +104,7 @@ struct SingleSubstFormat2_4
{
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
if (unlikely (index >= substitute.len)) return_trace (false);

View File

@ -29,6 +29,9 @@
#ifndef OT_LAYOUT_TYPES_HH
#define OT_LAYOUT_TYPES_HH
using hb_ot_lookup_cache_t = hb_cache_t<15, 8, 7>;
static_assert (sizeof (hb_ot_lookup_cache_t) == 256, "");
namespace OT {
namespace Layout {

View File

@ -3,7 +3,6 @@
#ifndef HB_NO_VAR_COMPOSITES
#include "../../../hb-draw.hh"
#include "../../../hb-geometry.hh"
#include "../../../hb-ot-layout-common.hh"
#include "../../../hb-ot-layout-gdef-table.hh"
@ -133,8 +132,9 @@ VarComponent::get_path_at (hb_font_t *font,
hb_codepoint_t parent_gid,
hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_transform_t total_transform,
hb_ubytes_t total_record,
hb_set_t *visited,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
VarRegionList::cache_t *cache) const
@ -312,26 +312,14 @@ VarComponent::get_path_at (hb_font_t *font,
if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
transform.scaleY = transform.scaleX;
// Scale the transform by the font's scale
float x_scale = font->x_multf;
float y_scale = font->y_multf;
transform.translateX *= x_scale;
transform.translateY *= y_scale;
transform.tCenterX *= x_scale;
transform.tCenterY *= y_scale;
// Build a transforming pen to apply the transform.
hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
hb_transforming_pen_context_t context {transform.to_transform (),
draw_session.funcs,
draw_session.draw_data,
&draw_session.st};
hb_draw_session_t transformer_session {transformer_funcs, &context};
total_transform.transform (transform.to_transform ());
total_transform.scale (font->x_mult ? 1.f / font->x_multf : 0.f,
font->y_mult ? 1.f / font->y_multf : 0.f);
VARC.get_path_at (font, gid,
transformer_session, component_coords,
draw_session, component_coords, total_transform,
parent_gid,
visited, edges_left, depth_left - 1);
decycler, edges_left, depth_left - 1);
}
#undef PROCESS_TRANSFORM_COMPONENTS
@ -340,6 +328,71 @@ VarComponent::get_path_at (hb_font_t *font,
return hb_ubytes_t (record, end - record);
}
bool
VARC::get_path_at (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_codepoint_t parent_glyph,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left) const
{
// Don't recurse on the same glyph.
unsigned idx = glyph == parent_glyph ?
NOT_COVERED :
(this+coverage).get_coverage (glyph);
if (idx == NOT_COVERED)
{
// Build a transforming pen to apply the transform.
hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
hb_transforming_pen_context_t context {transform,
draw_session.funcs,
draw_session.draw_data,
&draw_session.st};
hb_draw_session_t transformer_session {transformer_funcs, &context};
hb_draw_session_t &shape_draw_session = transform.is_identity () ? draw_session : transformer_session;
if (!font->face->table.glyf->get_path_at (font, glyph, shape_draw_session, coords))
#ifndef HB_NO_CFF
if (!font->face->table.cff2->get_path_at (font, glyph, shape_draw_session, coords))
if (!font->face->table.cff1->get_path (font, glyph, shape_draw_session)) // Doesn't have variations
#endif
return false;
return true;
}
if (depth_left <= 0)
return true;
if (*edges_left <= 0)
return true;
(*edges_left)--;
hb_decycler_node_t node (*decycler);
if (unlikely (!node.visit (glyph)))
return true;
hb_ubytes_t record = (this+glyphRecords)[idx];
VarRegionList::cache_t *cache = record.length >= 64 ? // Heuristic
(this+varStore).create_cache ()
: nullptr;
transform.scale (font->x_multf, font->y_multf);
VarCompositeGlyph::get_path_at (font, glyph,
draw_session, coords, transform,
record,
decycler, edges_left, depth_left,
cache);
(this+varStore).destroy_cache (cache);
return true;
}
//} // namespace Var
} // namespace OT

View File

@ -1,6 +1,8 @@
#ifndef OT_VAR_VARC_VARC_HH
#define OT_VAR_VARC_VARC_HH
#include "../../../hb-decycler.hh"
#include "../../../hb-geometry.hh"
#include "../../../hb-ot-layout-common.hh"
#include "../../../hb-ot-glyf-table.hh"
#include "../../../hb-ot-cff2-table.hh"
@ -46,8 +48,9 @@ struct VarComponent
hb_codepoint_t parent_gid,
hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_ubytes_t record,
hb_set_t *visited,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
VarRegionList::cache_t *cache = nullptr) const;
@ -60,8 +63,9 @@ struct VarCompositeGlyph
hb_codepoint_t glyph,
hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_ubytes_t record,
hb_set_t *visited,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
VarRegionList::cache_t *cache = nullptr)
@ -70,9 +74,9 @@ struct VarCompositeGlyph
{
const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
record = comp.get_path_at (font, glyph,
draw_session, coords,
draw_session, coords, transform,
record,
visited, edges_left, depth_left, cache);
decycler, edges_left, depth_left, cache);
}
}
};
@ -85,80 +89,32 @@ struct VARC
static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
bool
HB_INTERNAL bool
get_path_at (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_codepoint_t parent_glyph = HB_CODEPOINT_INVALID,
hb_set_t *visited = nullptr,
signed *edges_left = nullptr,
signed depth_left = HB_MAX_NESTING_LEVEL) const
{
hb_set_t stack_set;
if (visited == nullptr)
visited = &stack_set;
signed stack_edges = HB_MAX_GRAPH_EDGE_COUNT;
if (edges_left == nullptr)
edges_left = &stack_edges;
// Don't recurse on the same glyph.
unsigned idx = glyph == parent_glyph ?
NOT_COVERED :
(this+coverage).get_coverage (glyph);
if (idx == NOT_COVERED)
{
if (!font->face->table.glyf->get_path_at (font, glyph, draw_session, coords))
#ifndef HB_NO_CFF
if (!font->face->table.cff2->get_path_at (font, glyph, draw_session, coords))
if (!font->face->table.cff1->get_path (font, glyph, draw_session)) // Doesn't have variations
#endif
return false;
return true;
}
if (depth_left <= 0)
return true;
if (*edges_left <= 0)
return true;
(*edges_left)--;
if (visited->has (glyph) || visited->in_error ())
return true;
visited->add (glyph);
hb_ubytes_t record = (this+glyphRecords)[idx];
VarRegionList::cache_t *cache = record.length >= 64 ? // Heuristic
(this+varStore).create_cache ()
: nullptr;
VarCompositeGlyph::get_path_at (font, glyph,
draw_session, coords,
record,
visited, edges_left, depth_left,
cache);
(this+varStore).destroy_cache (cache);
visited->del (glyph);
return true;
}
hb_transform_t transform,
hb_codepoint_t parent_glyph,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left) const;
bool
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
{ return get_path_at (font, gid, draw_session, hb_array (font->coords, font->num_coords)); }
bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{
funcs->push_clip_glyph (data, gid, font);
funcs->color (data, true, foreground);
funcs->pop_clip (data);
hb_decycler_t decycler;
signed edges = HB_MAX_GRAPH_EDGE_COUNT;
return true;
}
return get_path_at (font,
gid,
draw_session,
hb_array (font->coords, font->num_coords),
HB_TRANSFORM_IDENTITY,
HB_CODEPOINT_INVALID,
&decycler,
&edges,
HB_MAX_NESTING_LEVEL); }
bool sanitize (hb_sanitize_context_t *c) const
{

View File

@ -190,7 +190,7 @@ struct SimpleGlyph
unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
unsigned old_length = points.length;
points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
points.alloc_exact (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy
if (unlikely (!points.resize (points.length + num_points, false))) return false;
auto points_ = points.as_array ().sub_array (old_length);
if (!phantom_only)
@ -281,9 +281,9 @@ struct SimpleGlyph
unsigned num_points = all_points.length - 4;
hb_vector_t<uint8_t> flags, x_coords, y_coords;
if (unlikely (!flags.alloc (num_points, true))) return false;
if (unlikely (!x_coords.alloc (2*num_points, true))) return false;
if (unlikely (!y_coords.alloc (2*num_points, true))) return false;
if (unlikely (!flags.alloc_exact (num_points))) return false;
if (unlikely (!x_coords.alloc_exact (2*num_points))) return false;
if (unlikely (!y_coords.alloc_exact (2*num_points))) return false;
unsigned lastflag = 255, repeat = 0;
int prev_x = 0, prev_y = 0;

View File

@ -94,7 +94,7 @@ struct glyf
}
hb_vector_t<unsigned> padded_offsets;
if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true)))
if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
return_trace (false);
hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
@ -229,8 +229,61 @@ struct glyf_accelerator_t
if (consumer.is_consuming_contour_points ())
{
for (auto &point : all_points.as_array ().sub_array (0, count))
consumer.consume_point (point);
auto *points = all_points.arrayZ;
if (false)
{
/* Our path-builder was designed to work with this simple loop.
* But FreeType and CoreText do it differently, so we match those
* with the other, more complicated, code branch below. */
for (unsigned i = 0; i < count; i++)
{
consumer.consume_point (points[i]);
if (points[i].is_end_point)
consumer.contour_end ();
}
}
else
{
for (unsigned i = 0; i < count; i++)
{
// Start of a contour.
if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
{
// First point is on-curve. Draw the contour.
for (; i < count; i++)
{
consumer.consume_point (points[i]);
if (points[i].is_end_point)
{
consumer.contour_end ();
break;
}
}
}
else
{
unsigned start = i;
// Find end of the contour.
for (; i < count; i++)
if (points[i].is_end_point)
break;
unsigned end = i;
// Enough to start from the end. Our path-builder takes care of the rest.
if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
consumer.consume_point (points[end]);
for (i = start; i < end; i++)
consumer.consume_point (points[i]);
consumer.contour_end ();
}
}
}
consumer.points_end ();
}
@ -303,6 +356,7 @@ struct glyf_accelerator_t
HB_ALWAYS_INLINE
void consume_point (const contour_point_t &point) { bounds.add (point); }
void contour_end () {}
void points_end () { bounds.get_extents (font, extents, scaled); }
bool is_consuming_contour_points () { return extents; }
@ -371,15 +425,6 @@ struct glyf_accelerator_t
return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
}
bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{
funcs->push_clip_glyph (data, gid, font);
funcs->color (data, true, foreground);
funcs->pop_clip (data);
return true;
}
const glyf_impl::Glyph
glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
{
@ -439,7 +484,7 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
{
OT::glyf_accelerator_t glyf (plan->source);
if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false;
if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
for (const auto &pair : plan->new_to_old_gid_list)
{

View File

@ -124,58 +124,60 @@ struct path_builder_t
}
}
if (unlikely (point.is_end_point))
{
if (first_offcurve && last_offcurve)
{
optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
first_offcurve2 :
first_offcurve);
if (last_offcurve2)
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
last_offcurve.x, last_offcurve.y,
mid.x, mid.y);
else
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
mid.x, mid.y);
last_offcurve = optional_point_t ();
}
/* now check the rest */
if (first_offcurve && first_oncurve)
{
if (first_offcurve2)
draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
first_offcurve.x, first_offcurve.y,
first_oncurve.x, first_oncurve.y);
else
draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
first_oncurve.x, first_oncurve.y);
}
else if (last_offcurve && first_oncurve)
{
if (last_offcurve2)
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
last_offcurve.x, last_offcurve.y,
first_oncurve.x, first_oncurve.y);
else
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
first_oncurve.x, first_oncurve.y);
}
else if (first_oncurve)
draw_session->line_to (first_oncurve.x, first_oncurve.y);
else if (first_offcurve)
{
float x = first_offcurve.x, y = first_offcurve.y;
draw_session->move_to (x, y);
draw_session->quadratic_to (x, y, x, y);
}
/* Getting ready for the next contour */
first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
draw_session->close_path ();
}
}
void contour_end ()
{
if (first_offcurve && last_offcurve)
{
optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
first_offcurve2 :
first_offcurve);
if (last_offcurve2)
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
last_offcurve.x, last_offcurve.y,
mid.x, mid.y);
else
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
mid.x, mid.y);
last_offcurve = optional_point_t ();
}
/* now check the rest */
if (first_offcurve && first_oncurve)
{
if (first_offcurve2)
draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
first_offcurve.x, first_offcurve.y,
first_oncurve.x, first_oncurve.y);
else
draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
first_oncurve.x, first_oncurve.y);
}
else if (last_offcurve && first_oncurve)
{
if (last_offcurve2)
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
last_offcurve.x, last_offcurve.y,
first_oncurve.x, first_oncurve.y);
else
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
first_oncurve.x, first_oncurve.y);
}
else if (first_oncurve)
draw_session->line_to (first_oncurve.x, first_oncurve.y);
else if (first_offcurve)
{
float x = first_offcurve.x, y = first_offcurve.y;
draw_session->move_to (x, y);
draw_session->quadratic_to (x, y, x, y);
}
/* Getting ready for the next contour */
first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
draw_session->close_path ();
}
void points_end () {}
bool is_consuming_contour_points () { return true; }

View File

@ -163,7 +163,7 @@ struct NameRecord
if (platformID != 1)
{
unsigned text_size = hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, nullptr, nullptr);
text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf()
unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
name_str_utf16_be = (char *) hb_calloc (byte_len, 1);
@ -174,14 +174,14 @@ struct NameRecord
}
hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, &text_size,
(hb_utf16_be_t::codepoint_t *) name_str_utf16_be);
unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
c->revert (snap);
hb_free (name_str_utf16_be);
return_trace (nullptr);
}
encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len);
}
else
@ -392,7 +392,7 @@ struct name
const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
&c->plan->name_table_overrides;
#endif
auto it =
+ nameRecordZ.as_array (count)
| hb_filter (c->plan->name_ids, &NameRecord::nameID)
@ -485,7 +485,7 @@ struct name
const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
this->table->count);
this->names.alloc (all_names.length, true);
this->names.alloc_exact (all_names.length);
for (unsigned int i = 0; i < all_names.length; i++)
{

View File

@ -30,6 +30,10 @@
#include "hb-aat-layout.hh"
#include "hb-aat-map.hh"
#include "hb-open-type.hh"
#include "hb-cache.hh"
#include "hb-bit-set.hh"
#include "hb-bit-page.hh"
namespace OT {
struct GDEF;
@ -39,10 +43,11 @@ namespace AAT {
using namespace OT;
#define HB_AAT_BUFFER_DIGEST_THRESHOLD 32
struct ankr;
using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>;
static_assert (sizeof (hb_aat_class_cache_t) == 256, "");
struct hb_aat_apply_context_t :
hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
{
@ -61,10 +66,12 @@ struct hb_aat_apply_context_t :
const ankr *ankr_table;
const OT::GDEF *gdef_table;
const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
hb_set_digest_t buffer_digest = hb_set_digest_t::full ();
hb_set_digest_t machine_glyph_set = hb_set_digest_t::full ();
hb_set_digest_t left_set = hb_set_digest_t::full ();
hb_set_digest_t right_set = hb_set_digest_t::full ();
bool using_buffer_glyph_set = false;
hb_bit_set_t buffer_glyph_set;
const hb_bit_set_t *left_set = nullptr;
const hb_bit_set_t *right_set = nullptr;
const hb_bit_set_t *machine_glyph_set = nullptr;
hb_aat_class_cache_t *machine_class_cache = nullptr;
hb_mask_t subtable_flags = 0;
/* Unused. For debug tracing only. */
@ -80,6 +87,25 @@ struct hb_aat_apply_context_t :
HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
void set_lookup_index (unsigned int i) { lookup_index = i; }
void setup_buffer_glyph_set ()
{
using_buffer_glyph_set = buffer->len >= 4;
if (using_buffer_glyph_set)
buffer->collect_codepoints (buffer_glyph_set);
}
bool buffer_intersects_machine () const
{
if (using_buffer_glyph_set)
return buffer_glyph_set.intersects (*machine_glyph_set);
// Faster for shorter buffers.
for (unsigned i = 0; i < buffer->len; i++)
if (machine_glyph_set->has (buffer->info[i].codepoint))
return true;
return false;
}
};
@ -108,6 +134,13 @@ struct LookupFormat0
{
glyphs.add_range (0, num_glyphs - 1);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
{
for (unsigned i = 0; i < num_glyphs; i++)
if (filter (arrayZ[i]))
glyphs.add (i);
}
bool sanitize (hb_sanitize_context_t *c) const
{
@ -140,8 +173,13 @@ struct LookupSegmentSingle
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
if (first == DELETED_GLYPH)
return;
if (first == DELETED_GLYPH) return;
glyphs.add_range (first, last);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (!filter (value)) return;
glyphs.add_range (first, last);
}
@ -182,6 +220,13 @@ struct LookupFormat2
for (unsigned int i = 0; i < count; i++)
segments[i].collect_glyphs (glyphs);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
unsigned count = segments.get_length ();
for (unsigned int i = 0; i < count; i++)
segments[i].collect_glyphs_filtered (glyphs, filter);
}
bool sanitize (hb_sanitize_context_t *c) const
{
@ -217,10 +262,17 @@ struct LookupSegmentArray
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
if (first == DELETED_GLYPH)
return;
if (first == DELETED_GLYPH) return;
glyphs.add_range (first, last);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const void *base, const filter_t &filter) const
{
const auto &values = base+valuesZ;
for (hb_codepoint_t i = first; i <= last; i++)
if (filter (values[i - first]))
glyphs.add (i);
}
int cmp (hb_codepoint_t g) const
{ return g < first ? -1 : g <= last ? 0 : +1; }
@ -271,6 +323,13 @@ struct LookupFormat4
for (unsigned i = 0; i < count; i++)
segments[i].collect_glyphs (glyphs);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
unsigned count = segments.get_length ();
for (unsigned i = 0; i < count; i++)
segments[i].collect_glyphs_filtered (glyphs, this, filter);
}
bool sanitize (hb_sanitize_context_t *c) const
{
@ -303,8 +362,13 @@ struct LookupSingle
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
if (glyph == DELETED_GLYPH)
return;
if (glyph == DELETED_GLYPH) return;
glyphs.add (glyph);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (!filter (value)) return;
glyphs.add (glyph);
}
@ -344,6 +408,13 @@ struct LookupFormat6
for (unsigned i = 0; i < count; i++)
entries[i].collect_glyphs (glyphs);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
unsigned count = entries.get_length ();
for (unsigned i = 0; i < count; i++)
entries[i].collect_glyphs_filtered (glyphs, filter);
}
bool sanitize (hb_sanitize_context_t *c) const
{
@ -379,12 +450,20 @@ struct LookupFormat8
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
if (unlikely (!glyphCount))
return;
if (firstGlyph == DELETED_GLYPH)
return;
if (unlikely (!glyphCount)) return;
if (firstGlyph == DELETED_GLYPH) return;
glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (unlikely (!glyphCount)) return;
if (firstGlyph == DELETED_GLYPH) return;
const T *p = valueArrayZ.arrayZ;
for (unsigned i = 0; i < glyphCount; i++)
if (filter (p[i]))
glyphs.add (firstGlyph + i);
}
bool sanitize (hb_sanitize_context_t *c) const
{
@ -433,10 +512,8 @@ struct LookupFormat10
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
if (unlikely (!glyphCount))
return;
if (firstGlyph == DELETED_GLYPH)
return;
if (unlikely (!glyphCount)) return;
if (firstGlyph == DELETED_GLYPH) return;
glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
}
@ -501,6 +578,18 @@ struct Lookup
default:return;
}
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
{
switch (u.format) {
case 0: hb_barrier (); u.format0.collect_glyphs_filtered (glyphs, num_glyphs, filter); return;
case 2: hb_barrier (); u.format2.collect_glyphs_filtered (glyphs, filter); return;
case 4: hb_barrier (); u.format4.collect_glyphs_filtered (glyphs, filter); return;
case 6: hb_barrier (); u.format6.collect_glyphs_filtered (glyphs, filter); return;
case 8: hb_barrier (); u.format8.collect_glyphs_filtered (glyphs, filter); return;
default:return;
}
}
typename T::type get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs,
@ -563,7 +652,7 @@ DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
template <typename T>
struct Entry
{
// This does seem like it's ever called.
// This doesn't seem like it's ever called.
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -632,18 +721,47 @@ struct StateTable
{
(this+classTable).collect_glyphs (glyphs, num_glyphs);
}
template <typename set_t, typename table_t>
void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs, const table_t &table) const
{
unsigned num_classes = nClasses;
if (unlikely (num_classes > hb_bit_page_t::BITS))
{
(this+classTable).collect_glyphs (glyphs, num_glyphs);
return;
}
// Collect all classes going out from the start state.
hb_bit_page_t filter;
for (unsigned i = 0; i < num_classes; i++)
{
const auto &entry = get_entry (STATE_START_OF_TEXT, i);
if (new_state (entry.newState) == STATE_START_OF_TEXT &&
!table.is_action_initiable (entry) && !table.is_actionable (entry))
continue;
filter.add (i);
}
// And glyphs in those classes.
(this+classTable).collect_glyphs_filtered (glyphs, num_glyphs, filter);
}
int new_state (unsigned int newState) const
{ return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; }
template <typename set_t>
unsigned int get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs,
const set_t &glyphs) const
hb_aat_class_cache_t *cache = nullptr) const
{
unsigned klass;
if (cache && cache->get (glyph_id, &klass)) return klass;
if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
if (!glyphs[glyph_id]) return CLASS_OUT_OF_BOUNDS;
return (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
klass = (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
if (cache) cache->set (glyph_id, klass);
return klass;
}
const Entry<Extra> *get_entries () const
@ -651,13 +769,14 @@ struct StateTable
const Entry<Extra> &get_entry (int state, unsigned int klass) const
{
if (unlikely (klass >= nClasses))
unsigned n_classes = nClasses;
if (unlikely (klass >= n_classes))
klass = CLASS_OUT_OF_BOUNDS;
const HBUSHORT *states = (this+stateArrayTable).arrayZ;
const Entry<Extra> *entries = (this+entryTable).arrayZ;
unsigned int entry = states[state * nClasses + klass];
unsigned int entry = states[state * n_classes + klass];
DEBUG_MSG (APPLY, nullptr, "e%u", entry);
return entries[entry];
@ -803,6 +922,13 @@ struct ClassTable
if (classArray.arrayZ[i] != CLASS_OUT_OF_BOUNDS)
glyphs.add (firstGlyph + i);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
{
for (unsigned i = 0; i < classArray.len; i++)
if (filter (classArray.arrayZ[i]))
glyphs.add (firstGlyph + i);
}
bool sanitize (hb_sanitize_context_t *c) const
{
@ -918,7 +1044,7 @@ struct ExtendedTypes
}
};
template <typename Types, typename EntryData>
template <typename Types, typename EntryData, typename Flags>
struct StateTableDriver
{
using StateTableT = StateTable<Types, EntryData>;
@ -929,14 +1055,6 @@ struct StateTableDriver
machine (machine_),
num_glyphs (face_->get_num_glyphs ()) {}
template <typename context_t>
bool is_idempotent_on_all_out_of_bounds (context_t *c, hb_aat_apply_context_t *ac)
{
const auto entry = machine.get_entry (StateTableT::STATE_START_OF_TEXT, CLASS_OUT_OF_BOUNDS);
return !c->is_actionable (ac->buffer, this, entry) &&
machine.new_state (entry.newState) == StateTableT::STATE_START_OF_TEXT;
}
template <typename context_t>
void drive (context_t *c, hb_aat_apply_context_t *ac)
{
@ -977,7 +1095,7 @@ struct StateTableDriver
}
unsigned int klass = likely (buffer->idx < buffer->len) ?
machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_glyph_set) :
machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) :
(unsigned) CLASS_END_OF_TEXT;
DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
const EntryT &entry = machine.get_entry (state, klass);
@ -1011,41 +1129,36 @@ struct StateTableDriver
*
* https://github.com/harfbuzz/harfbuzz/issues/2860
*/
const auto is_safe_to_break_extra = [&]()
{
/* 2c. */
const auto &wouldbe_entry = machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass);
/* 2c'. */
if (c->is_actionable (buffer, this, wouldbe_entry))
return false;
/* 2c". */
return next_state == machine.new_state(wouldbe_entry.newState)
&& (entry.flags & context_t::DontAdvance) == (wouldbe_entry.flags & context_t::DontAdvance);
};
const auto is_safe_to_break = [&]()
{
const EntryT *wouldbe_entry;
bool is_safe_to_break =
(
/* 1. */
if (c->is_actionable (buffer, this, entry))
return false;
!c->table->is_actionable (entry) &&
/* 2. */
// This one is meh, I know...
const auto ok =
(
state == StateTableT::STATE_START_OF_TEXT
|| ((entry.flags & context_t::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
|| is_safe_to_break_extra();
if (!ok)
return false;
|| ((entry.flags & Flags::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
|| (
/* 2c. */
wouldbe_entry = &machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass)
,
/* 2c'. */
!c->table->is_actionable (*wouldbe_entry) &&
/* 2c". */
(
next_state == machine.new_state(wouldbe_entry->newState) &&
(entry.flags & Flags::DontAdvance) == (wouldbe_entry->flags & Flags::DontAdvance)
)
)
) &&
/* 3. */
return !c->is_actionable (buffer, this, machine.get_entry (state, CLASS_END_OF_TEXT));
};
!c->table->is_actionable (machine.get_entry (state, CLASS_END_OF_TEXT))
);
if (!is_safe_to_break () && buffer->backtrack_len () && buffer->idx < buffer->len)
if (!is_safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len)
buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
c->transition (buffer, this, entry);
@ -1056,7 +1169,7 @@ struct StateTableDriver
if (buffer->idx == buffer->len || unlikely (!buffer->successful))
break;
if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0)
if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0)
(void) buffer->next_glyph ();
}

View File

@ -112,10 +112,6 @@ struct KerxSubTableFormat0
if (header.coverage & header.Backwards)
return_trace (false);
if (!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -144,7 +140,7 @@ struct KerxSubTableFormat0
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{
if (!c->left_set[left] || !c->right_set[right]) return 0;
if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c);
}
};
@ -211,6 +207,9 @@ struct Format1Entry<false>
typedef void EntryData;
static bool initiateAction (const Entry<EntryData> &entry)
{ return entry.flags & Push; }
static bool performAction (const Entry<EntryData> &entry)
{ return entry.flags & Offset; }
@ -227,13 +226,23 @@ struct KerxSubTableFormat1
typedef Format1Entry<Types::extended> Format1EntryT;
typedef typename Format1EntryT::EntryData EntryData;
enum Flags
{
DontAdvance = Format1EntryT::DontAdvance,
};
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return Format1EntryT::initiateAction (entry);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return Format1EntryT::performAction (entry);
}
struct driver_context_t
{
static constexpr bool in_place = true;
enum
{
DontAdvance = Format1EntryT::DontAdvance,
};
driver_context_t (const KerxSubTableFormat1 *table_,
hb_aat_apply_context_t *c_) :
@ -246,12 +255,8 @@ struct KerxSubTableFormat1
depth (0),
crossStream (table->header.coverage & table->header.CrossStream) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry)
{ return Format1EntryT::performAction (entry); }
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
unsigned int flags = entry.flags;
@ -351,9 +356,10 @@ struct KerxSubTableFormat1
}
}
private:
public:
hb_aat_apply_context_t *c;
const KerxSubTableFormat1 *table;
private:
const UnsizedArrayOf<FWORD> &kernAction;
unsigned int stack[8];
unsigned int depth;
@ -370,12 +376,7 @@ struct KerxSubTableFormat1
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->font->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
driver.drive (&dc, c);
@ -440,10 +441,6 @@ struct KerxSubTableFormat2
if (header.coverage & header.Backwards)
return_trace (false);
if (!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -469,7 +466,7 @@ struct KerxSubTableFormat2
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{
if (!c->left_set[left] || !c->right_set[right]) return 0;
if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c);
}
};
@ -513,17 +510,26 @@ struct KerxSubTableFormat4
DEFINE_SIZE_STATIC (2);
};
enum Flags
{
Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
* going to the new state. */
Reserved = 0x3FFF, /* Not used; set to 0. */
};
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return (entry.flags & Mark);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return entry.data.ankrActionIndex != 0xFFFF;
}
struct driver_context_t
{
static constexpr bool in_place = true;
enum Flags
{
Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
* going to the new state. */
Reserved = 0x3FFF, /* Not used; set to 0. */
};
enum SubTableFlags
{
ActionType = 0xC0000000, /* A two-bit field containing the action type. */
@ -533,20 +539,17 @@ struct KerxSubTableFormat4
* point table. */
};
driver_context_t (const KerxSubTableFormat4 *table,
driver_context_t (const KerxSubTableFormat4 *table_,
hb_aat_apply_context_t *c_) :
c (c_),
table (table_),
action_type ((table->flags & ActionType) >> 30),
ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
mark_set (false),
mark (0) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry)
{ return entry.data.ankrActionIndex != 0xFFFF; }
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
@ -634,8 +637,10 @@ struct KerxSubTableFormat4
}
}
private:
public:
hb_aat_apply_context_t *c;
const KerxSubTableFormat4 *table;
private:
unsigned int action_type;
const HBUINT16 *ankrData;
bool mark_set;
@ -648,12 +653,7 @@ struct KerxSubTableFormat4
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->font->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
driver.drive (&dc, c);
@ -735,10 +735,6 @@ struct KerxSubTableFormat6
if (header.coverage & header.Backwards)
return_trace (false);
if (!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -793,7 +789,7 @@ struct KerxSubTableFormat6
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{
if (!c->left_set[left] || !c->right_set[right]) return 0;
if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c);
}
};
@ -925,7 +921,7 @@ struct KerxSubTable
* The 'kerx' Table
*/
using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_set_digest_t, hb_set_digest_t>>;
using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_bit_set_t, hb_bit_set_t>>;
template <typename T>
struct KerxTable
@ -985,15 +981,10 @@ struct KerxTable
}
bool apply (AAT::hb_aat_apply_context_t *c,
const kern_accelerator_data_t *accel_data = nullptr) const
const kern_accelerator_data_t &accel_data) const
{
c->buffer->unsafe_to_concat ();
if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD)
c->buffer_digest = c->buffer->digest ();
else
c->buffer_digest = hb_set_digest_t::full ();
typedef typename T::SubTable SubTable;
bool ret = false;
@ -1037,15 +1028,8 @@ struct KerxTable
if (reverse)
c->buffer->reverse ();
if (accel_data)
{
c->left_set = (*accel_data)[i].first;
c->right_set = (*accel_data)[i].second;
}
else
{
c->left_set = c->right_set = hb_set_digest_t::full ();
}
c->left_set = &accel_data[i].first;
c->right_set = &accel_data[i].second;
{
/* See comment in sanitize() for conditional here. */
@ -1122,7 +1106,7 @@ struct KerxTable
unsigned int count = thiz()->tableCount;
for (unsigned int i = 0; i < count; i++)
{
hb_set_digest_t left_set, right_set;
hb_bit_set_t left_set, right_set;
st->collect_glyphs (left_set, right_set, num_glyphs);
accel_data.push (hb_pair (left_set, right_set));
st = &StructAfter<SubTable> (*st);
@ -1148,7 +1132,7 @@ struct KerxTable
bool apply (AAT::hb_aat_apply_context_t *c) const
{
return table->apply (c, &accel_data);
return table->apply (c, accel_data);
}
hb_blob_ptr_t<T> table;

View File

@ -29,6 +29,7 @@
#include "hb-open-type.hh"
#include "hb-aat-layout-common.hh"
#include "hb-ot-layout.hh"
#include "hb-ot-layout-common.hh"
#include "hb-ot-layout-gdef-table.hh"
#include "hb-aat-map.hh"
@ -53,35 +54,40 @@ struct RearrangementSubtable
typedef void EntryData;
struct driver_context_t
enum Flags
{
static constexpr bool in_place = true;
enum Flags
{
MarkFirst = 0x8000, /* If set, make the current glyph the first
MarkFirst = 0x8000, /* If set, make the current glyph the first
* glyph to be rearranged. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph
DontAdvance = 0x4000, /* If set, don't advance to the next glyph
* before going to the new state. This means
* that the glyph index doesn't change, even
* if the glyph at that index has changed. */
MarkLast = 0x2000, /* If set, make the current glyph the last
MarkLast = 0x2000, /* If set, make the current glyph the last
* glyph to be rearranged. */
Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */
Verb = 0x000F, /* The type of rearrangement specified. */
};
Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */
Verb = 0x000F, /* The type of rearrangement specified. */
};
driver_context_t (const RearrangementSubtable *table HB_UNUSED) :
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return (entry.flags & MarkFirst);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return (entry.flags & Verb);
}
struct driver_context_t
{
static constexpr bool in_place = true;
driver_context_t (const RearrangementSubtable *table_) :
ret (false),
table (table_),
start (0), end (0) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry) const
{
return (entry.flags & Verb) && start < end;
}
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
unsigned int flags = entry.flags;
@ -158,6 +164,7 @@ struct RearrangementSubtable
public:
bool ret;
const RearrangementSubtable *table;
private:
unsigned int start;
unsigned int end;
@ -169,11 +176,13 @@ struct RearrangementSubtable
driver_context_t dc (this);
StateTableDriver<Types, EntryData> driver (machine, c->face);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.may_have (c->machine_glyph_set))
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
@ -207,39 +216,40 @@ struct ContextualSubtable
DEFINE_SIZE_STATIC (4);
};
enum Flags
{
SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
* going to the new state. */
Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */
};
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return (entry.flags & SetMark);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
}
struct driver_context_t
{
static constexpr bool in_place = true;
enum Flags
{
SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
* going to the new state. */
Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */
};
driver_context_t (const ContextualSubtable *table_,
hb_aat_apply_context_t *c_) :
ret (false),
c (c_),
table (table_),
gdef (*c->gdef_table),
mark_set (false),
has_glyph_classes (gdef.has_glyph_classes ()),
mark (0),
table (table_),
subs (table+table->substitutionTables) {}
bool is_actionable (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
const Entry<EntryData> &entry) const
{
if (buffer->idx == buffer->len && !mark_set)
return false;
return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
}
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
/* Looks like CoreText applies neither mark nor current substitution for
@ -271,8 +281,9 @@ struct ContextualSubtable
if (replacement)
{
buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
buffer->info[mark].codepoint = *replacement;
c->buffer_digest.add (*replacement);
hb_codepoint_t glyph = *replacement;
buffer->info[mark].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->info[mark],
gdef.get_glyph_props (*replacement));
@ -301,8 +312,9 @@ struct ContextualSubtable
}
if (replacement)
{
buffer->info[idx].codepoint = *replacement;
c->buffer_digest.add (*replacement);
hb_codepoint_t glyph = *replacement;
buffer->info[idx].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->info[idx],
gdef.get_glyph_props (*replacement));
@ -318,13 +330,13 @@ struct ContextualSubtable
public:
bool ret;
private:
hb_aat_apply_context_t *c;
const ContextualSubtable *table;
private:
const OT::GDEF &gdef;
bool mark_set;
bool has_glyph_classes;
unsigned int mark;
const ContextualSubtable *table;
const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs;
};
@ -334,11 +346,13 @@ struct ContextualSubtable
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->face);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.may_have (c->machine_glyph_set))
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
@ -389,6 +403,16 @@ struct LigatureEntry;
template <>
struct LigatureEntry<true>
{
struct EntryData
{
HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry
* for processing this group, if indicated
* by the flags. */
public:
DEFINE_SIZE_STATIC (2);
};
enum Flags
{
SetComponent = 0x8000, /* Push this glyph onto the component stack for
@ -400,14 +424,8 @@ struct LigatureEntry<true>
Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */
};
struct EntryData
{
HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry
* for processing this group, if indicated
* by the flags. */
public:
DEFINE_SIZE_STATIC (2);
};
static bool initiateAction (const Entry<EntryData> &entry)
{ return entry.flags & SetComponent; }
static bool performAction (const Entry<EntryData> &entry)
{ return entry.flags & PerformAction; }
@ -418,6 +436,8 @@ struct LigatureEntry<true>
template <>
struct LigatureEntry<false>
{
typedef void EntryData;
enum Flags
{
SetComponent = 0x8000, /* Push this glyph onto the component stack for
@ -429,7 +449,8 @@ struct LigatureEntry<false>
* multiple of 4. */
};
typedef void EntryData;
static bool initiateAction (const Entry<EntryData> &entry)
{ return entry.flags & SetComponent; }
static bool performAction (const Entry<EntryData> &entry)
{ return entry.flags & Offset; }
@ -447,13 +468,23 @@ struct LigatureSubtable
typedef LigatureEntry<Types::extended> LigatureEntryT;
typedef typename LigatureEntryT::EntryData EntryData;
enum Flags
{
DontAdvance = LigatureEntryT::DontAdvance,
};
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return LigatureEntryT::initiateAction (entry);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return LigatureEntryT::performAction (entry);
}
struct driver_context_t
{
static constexpr bool in_place = false;
enum
{
DontAdvance = LigatureEntryT::DontAdvance,
};
enum LigActionFlags
{
LigActionLast = 0x80000000, /* This is the last action in the list. This also
@ -476,14 +507,8 @@ struct LigatureSubtable
ligature (table+table->ligature),
match_length (0) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry) const
{
return LigatureEntryT::performAction (entry);
}
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
@ -564,7 +589,7 @@ struct LigatureSubtable
{
DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
buffer->cur().unicode_props() |= UPROPS_MASK_IGNORABLE;
_hb_glyph_info_set_default_ignorable (&buffer->cur());
if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
}
@ -581,9 +606,9 @@ struct LigatureSubtable
public:
bool ret;
private:
hb_aat_apply_context_t *c;
const LigatureSubtable *table;
private:
const UnsizedArrayOf<HBUINT32> &ligAction;
const UnsizedArrayOf<HBUINT16> &component;
const UnsizedArrayOf<HBGlyphID16> &ligature;
@ -597,11 +622,13 @@ struct LigatureSubtable
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->face);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.may_have (c->machine_glyph_set))
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
@ -638,6 +665,12 @@ struct NoncontextualSubtable
{
TRACE_APPLY (this);
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
const OT::GDEF &gdef (*c->gdef_table);
bool has_glyph_classes = gdef.has_glyph_classes ();
@ -670,8 +703,9 @@ struct NoncontextualSubtable
const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
if (replacement)
{
info[i].codepoint = *replacement;
c->buffer_digest.add (*replacement);
hb_codepoint_t glyph = *replacement;
info[i].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&info[i],
gdef.get_glyph_props (*replacement));
@ -682,6 +716,12 @@ struct NoncontextualSubtable
return_trace (ret);
}
template <typename set_t>
void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs) const
{
substitute.collect_glyphs (glyphs, num_glyphs);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -715,73 +755,78 @@ struct InsertionSubtable
DEFINE_SIZE_STATIC (4);
};
enum Flags
{
SetMark = 0x8000, /* If set, mark the current glyph. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
* going to the new state. This does not mean
* that the glyph pointed to is the same one as
* before. If you've made insertions immediately
* downstream of the current glyph, the next glyph
* processed would in fact be the first one
* inserted. */
CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero,
* then the specified glyph list will be inserted
* as a kashida-like insertion, either before or
* after the current glyph (depending on the state
* of the currentInsertBefore flag). If clear, and
* the currentInsertList is nonzero, then the
* specified glyph list will be inserted as a
* split-vowel-like insertion, either before or
* after the current glyph (depending on the state
* of the currentInsertBefore flag). */
MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero,
* then the specified glyph list will be inserted
* as a kashida-like insertion, either before or
* after the marked glyph (depending on the state
* of the markedInsertBefore flag). If clear, and
* the markedInsertList is nonzero, then the
* specified glyph list will be inserted as a
* split-vowel-like insertion, either before or
* after the marked glyph (depending on the state
* of the markedInsertBefore flag). */
CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made
* to the left of the current glyph. If clear,
* they're made to the right of the current glyph. */
MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be
* made to the left of the marked glyph. If clear,
* they're made to the right of the marked glyph. */
CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the
* number of glyphs to insert at the current
* position. Since zero means no insertions, the
* largest number of insertions at any given
* current location is 31 glyphs. */
MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the
* number of glyphs to insert at the marked
* position. Since zero means no insertions, the
* largest number of insertions at any given
* marked location is 31 glyphs. */
};
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return (entry.flags & SetMark);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
(entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
}
struct driver_context_t
{
static constexpr bool in_place = false;
enum Flags
{
SetMark = 0x8000, /* If set, mark the current glyph. */
DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
* going to the new state. This does not mean
* that the glyph pointed to is the same one as
* before. If you've made insertions immediately
* downstream of the current glyph, the next glyph
* processed would in fact be the first one
* inserted. */
CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero,
* then the specified glyph list will be inserted
* as a kashida-like insertion, either before or
* after the current glyph (depending on the state
* of the currentInsertBefore flag). If clear, and
* the currentInsertList is nonzero, then the
* specified glyph list will be inserted as a
* split-vowel-like insertion, either before or
* after the current glyph (depending on the state
* of the currentInsertBefore flag). */
MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero,
* then the specified glyph list will be inserted
* as a kashida-like insertion, either before or
* after the marked glyph (depending on the state
* of the markedInsertBefore flag). If clear, and
* the markedInsertList is nonzero, then the
* specified glyph list will be inserted as a
* split-vowel-like insertion, either before or
* after the marked glyph (depending on the state
* of the markedInsertBefore flag). */
CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made
* to the left of the current glyph. If clear,
* they're made to the right of the current glyph. */
MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be
* made to the left of the marked glyph. If clear,
* they're made to the right of the marked glyph. */
CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the
* number of glyphs to insert at the current
* position. Since zero means no insertions, the
* largest number of insertions at any given
* current location is 31 glyphs. */
MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the
* number of glyphs to insert at the marked
* position. Since zero means no insertions, the
* largest number of insertions at any given
* marked location is 31 glyphs. */
};
driver_context_t (const InsertionSubtable *table,
driver_context_t (const InsertionSubtable *table_,
hb_aat_apply_context_t *c_) :
ret (false),
c (c_),
table (table_),
mark (0),
insertionAction (table+table->insertionAction) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry) const
{
return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
(entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
}
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
unsigned int flags = entry.flags;
@ -807,7 +852,7 @@ struct InsertionSubtable
/* TODO We ignore KashidaLike setting. */
if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
for (unsigned int i = 0; i < count; i++)
c->buffer_digest.add (glyphs[i]);
c->buffer_glyph_set.add (glyphs[i]);
ret = true;
if (buffer->idx < buffer->len && !before)
buffer->skip_glyph ();
@ -861,8 +906,9 @@ struct InsertionSubtable
public:
bool ret;
private:
hb_aat_apply_context_t *c;
const InsertionSubtable *table;
private:
unsigned int mark;
const UnsizedArrayOf<HBGlyphID16> &insertionAction;
};
@ -873,11 +919,13 @@ struct InsertionSubtable
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->face);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.may_have (c->machine_glyph_set))
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
@ -935,24 +983,33 @@ struct hb_accelerate_subtables_context_t :
friend struct hb_aat_layout_lookup_accelerator_t;
public:
hb_set_digest_t digest;
hb_bit_set_t glyph_set;
mutable hb_aat_class_cache_t class_cache;
template <typename T>
auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN
(
obj_.machine.collect_glyphs (this->digest, num_glyphs)
obj_.machine.collect_initial_glyphs (glyph_set, num_glyphs, obj_)
)
template <typename T>
void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>)
{
digest = digest.full ();
obj_.collect_initial_glyphs (glyph_set, num_glyphs);
}
template <typename T>
void init (const T &obj_, unsigned num_glyphs)
{
glyph_set.init ();
init_ (obj_, num_glyphs, hb_prioritize);
class_cache.clear ();
}
void
fini ()
{
glyph_set.fini ();
}
};
@ -999,12 +1056,21 @@ struct hb_aat_layout_chain_accelerator_t
if (unlikely (!thiz))
return nullptr;
thiz->count = count;
hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs);
chain.dispatch (&c_accelerate_subtables);
return thiz;
}
void destroy ()
{
for (unsigned i = 0; i < count; i++)
subtables[i].fini ();
}
unsigned count;
hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
};
@ -1152,15 +1218,19 @@ struct Chain
{
bool reverse;
if (hb_none (hb_iter (c->range_flags) |
hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); })))
goto skip;
c->subtable_flags = subtable->subFeatureFlags;
c->machine_glyph_set = accel ? accel->subtables[i].digest : hb_set_digest_t::full ();
auto coverage = subtable->get_coverage ();
if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
hb_mask_t subtable_flags = subtable->subFeatureFlags;
if (hb_none (hb_iter (c->range_flags) |
hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); })))
goto skip;
c->subtable_flags = subtable_flags;
c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t);
c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr;
if (!(coverage & ChainSubtable<Types>::AllDirections) &&
HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical))
bool (coverage & ChainSubtable<Types>::Vertical))
goto skip;
/* Buffer contents is always in logical direction. Determine if
@ -1190,9 +1260,9 @@ struct Chain
(the order opposite that of the characters, which
may be right-to-left or left-to-right).
*/
reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ?
bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) :
bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) !=
reverse = coverage & ChainSubtable<Types>::Logical ?
bool (coverage & ChainSubtable<Types>::Backwards) :
bool (coverage & ChainSubtable<Types>::Backwards) !=
HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
@ -1298,6 +1368,12 @@ struct mortmorx
hb_sanitize_context_t sc;
this->table = sc.reference_table<T> (face);
if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
{
hb_blob_destroy (this->table.get_blob ());
this->table = hb_blob_get_empty ();
}
this->chain_count = table->get_chain_count ();
this->accels = (hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *) hb_calloc (this->chain_count, sizeof (*accels));
@ -1311,7 +1387,11 @@ struct mortmorx
~accelerator_t ()
{
for (unsigned int i = 0; i < this->chain_count; i++)
{
if (this->accels[i])
this->accels[i]->destroy ();
hb_free (this->accels[i]);
}
hb_free (this->accels);
this->table.destroy ();
}
@ -1365,9 +1445,8 @@ struct mortmorx
unsigned get_chain_count () const
{
return chainCount;
return chainCount;
}
void apply (hb_aat_apply_context_t *c,
const hb_aat_map_t &map,
const accelerator_t &accel) const
@ -1376,10 +1455,7 @@ struct mortmorx
c->buffer->unsafe_to_concat ();
if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD)
c->buffer_digest = c->buffer->digest ();
else
c->buffer_digest = hb_set_digest_t::full ();
c->setup_buffer_glyph_set ();
c->set_lookup_index (0);
const Chain<Types> *chain = &firstChain;
@ -1428,8 +1504,17 @@ struct mortmorx
DEFINE_SIZE_MIN (8);
};
struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx> {};
struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort> {};
struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx>
{
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const;
};
struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort>
{
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const;
};
struct morx_accelerator_t : morx::accelerator_t {
morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {}

View File

@ -48,17 +48,69 @@ struct TrackTableEntry
float get_track_value () const { return track.to_float (); }
int get_value (const void *base, unsigned int index,
unsigned int table_size) const
{ return (base+valuesZ).as_array (table_size)[index]; }
float interpolate_at (unsigned int idx,
float ptem,
const void *base,
hb_array_t<const F16DOT16> size_table) const
{
const FWORD *values = (base+valuesZ).arrayZ;
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
int v0 = values[idx];
int v1 = values[idx + 1];
// Deal with font bugs.
if (unlikely (s1 < s0))
{ hb_swap (s0, s1); hb_swap (v0, v1); }
if (unlikely (ptem < s0)) return v0;
if (unlikely (ptem > s1)) return v1;
if (unlikely (s0 == s1)) return (v0 + v1) * 0.5f;
float t = (ptem - s0) / (s1 - s0);
return v0 + t * (v1 - v0);
}
float get_value (float ptem,
const void *base,
hb_array_t<const F16DOT16> size_table) const
{
const FWORD *values = (base+valuesZ).arrayZ;
unsigned int n_sizes = size_table.length;
/*
* Choose size.
*/
if (!n_sizes) return 0.f;
if (n_sizes == 1) return values[0];
// At least two entries.
unsigned i;
for (i = 0; i < n_sizes; i++)
if (size_table[i].to_float () >= ptem)
break;
// Boundary conditions.
if (i == 0) return values[0];
if (i == n_sizes) return values[n_sizes - 1];
// Exact match.
if (size_table[i].to_float () == ptem) return values[i];
// Interpolate.
return interpolate_at (i - 1, ptem, base, size_table);
}
public:
bool sanitize (hb_sanitize_context_t *c, const void *base,
unsigned int table_size) const
bool sanitize (hb_sanitize_context_t *c,
const void *base,
unsigned int n_sizes) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
(valuesZ.sanitize (c, base, table_size))));
(valuesZ.sanitize (c, base, n_sizes))));
}
protected:
@ -76,58 +128,38 @@ struct TrackTableEntry
struct TrackData
{
float interpolate_at (unsigned int idx,
float target_size,
const TrackTableEntry &trackTableEntry,
const void *base) const
float get_tracking (const void *base, float ptem, float track = 0.f) const
{
unsigned int sizes = nSizes;
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
unsigned count = nTracks;
hb_array_t<const F16DOT16> size_table = (base+sizeTable).as_array (nSizes);
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
return t * trackTableEntry.get_value (base, idx + 1, sizes) +
(1.f - t) * trackTableEntry.get_value (base, idx, sizes);
}
if (!count) return 0.f;
if (count == 1) return trackTable[0].get_value (ptem, base, size_table);
int get_tracking (const void *base, float ptem) const
{
/*
* Choose track.
*/
const TrackTableEntry *trackTableEntry = nullptr;
unsigned int count = nTracks;
for (unsigned int i = 0; i < count; i++)
{
/* Note: Seems like the track entries are sorted by values. But the
* spec doesn't explicitly say that. It just mentions it in the example. */
// At least two entries.
/* For now we only seek for track entries with zero tracking value */
unsigned i = 0;
unsigned j = count - 1;
if (trackTable[i].get_track_value () == 0.f)
{
trackTableEntry = &trackTable[i];
break;
}
}
if (!trackTableEntry) return 0;
// Find the two entries that track is between.
while (i + 1 < count && trackTable[i + 1].get_track_value () < track)
i++;
while (j > 0 && trackTable[j - 1].get_track_value () > track)
j--;
/*
* Choose size.
*/
unsigned int sizes = nSizes;
if (!sizes) return 0;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
// Exact match.
if (i == j) return trackTable[i].get_value (ptem, base, size_table);
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
unsigned int size_index;
for (size_index = 0; size_index < sizes - 1; size_index++)
if (size_table[size_index].to_float () >= ptem)
break;
// Interpolate.
return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
*trackTableEntry, base));
float t0 = trackTable[i].get_track_value ();
float t1 = trackTable[j].get_track_value ();
float t = (track - t0) / (t1 - t0);
float a = trackTable[i].get_value (ptem, base, size_table);
float b = trackTable[j].get_value (ptem, base, size_table);
return a + t * (b - a);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
@ -158,45 +190,15 @@ struct trak
bool has_data () const { return version.to_int (); }
bool apply (hb_aat_apply_context_t *c) const
hb_position_t get_h_tracking (hb_font_t *font, float track = 0.f) const
{
TRACE_APPLY (this);
hb_mask_t trak_mask = c->plan->trak_mask;
const float ptem = c->font->ptem;
if (unlikely (ptem <= 0.f))
return_trace (false);
hb_buffer_t *buffer = c->buffer;
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{
const TrackData &trackData = this+horizData;
int tracking = trackData.get_tracking (this, ptem);
hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
foreach_grapheme (buffer, start, end)
{
if (!(buffer->info[start].mask & trak_mask)) continue;
buffer->pos[start].x_advance += advance_to_add;
buffer->pos[start].x_offset += offset_to_add;
}
}
else
{
const TrackData &trackData = this+vertData;
int tracking = trackData.get_tracking (this, ptem);
hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
foreach_grapheme (buffer, start, end)
{
if (!(buffer->info[start].mask & trak_mask)) continue;
buffer->pos[start].y_advance += advance_to_add;
buffer->pos[start].y_offset += offset_to_add;
}
}
return_trace (true);
float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
return font->em_scalef_x ((this+horizData).get_tracking (this, ptem, track));
}
hb_position_t get_v_tracking (hb_font_t *font, float track = 0.f) const
{
float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track));
}
bool sanitize (hb_sanitize_context_t *c) const

View File

@ -34,9 +34,12 @@
#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-layout-kerx-table.hh"
#include "hb-aat-layout-morx-table.hh"
#include "hb-aat-layout-trak-table.hh"
#include "hb-aat-layout-trak-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-ltag-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gdef-table.hh"
/*
* hb_aat_apply_context_t
@ -207,6 +210,36 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag)
*/
bool
AAT::morx::is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const
{
#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
return false;
#endif
switch HB_CODEPOINT_ENCODE3 (blob->length,
face->table.GSUB->table.get_length (),
face->table.GDEF->table.get_length ())
{
/* https://github.com/harfbuzz/harfbuzz/issues/4108
sha1sum:a71ca6813b7e56a772cffff7c24a5166b087197c AALMAGHRIBI.ttf */
case HB_CODEPOINT_ENCODE3 (19892, 2794, 340):
return true;
}
return false;
}
bool
AAT::mort::is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const
{
#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
return false;
#endif
return false;
}
void
hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
hb_aat_map_t *map)
@ -361,17 +394,6 @@ hb_aat_layout_has_tracking (hb_face_t *face)
return face->table.trak->has_data ();
}
void
hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer)
{
const AAT::trak& trak = *font->face->table.trak;
AAT::hb_aat_apply_context_t c (plan, font, buffer);
trak.apply (&c);
}
/**
* hb_aat_layout_get_feature_types:
* @face: #hb_face_t to work upon

View File

@ -32,6 +32,9 @@
#include "hb-ot-shape.hh"
#include "hb-aat-ltag-table.hh"
/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
struct hb_aat_feature_mapping_t
{
hb_tag_t otFeatureTag;
@ -68,10 +71,5 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
HB_INTERNAL void
hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
#endif /* HB_AAT_LAYOUT_HH */

View File

@ -88,22 +88,23 @@ hb_aat_map_builder_t::compile (hb_aat_map_t &m)
/* Sort features by start/end events. */
hb_vector_t<feature_event_t> feature_events;
feature_events.alloc_exact (features.length * 2 + 1);
for (unsigned int i = 0; i < features.length; i++)
{
auto &feature = features[i];
auto &feature = features.arrayZ[i];
if (features[i].start == features[i].end)
if (feature.start == feature.end)
continue;
feature_event_t *event;
event = feature_events.push ();
event->index = features[i].start;
event->index = feature.start;
event->start = true;
event->feature = feature.info;
event = feature_events.push ();
event->index = features[i].end;
event->index = feature.end;
event->start = false;
event->feature = feature.info;
}
@ -139,12 +140,12 @@ hb_aat_map_builder_t::compile (hb_aat_map_t &m)
current_features.qsort ();
unsigned int j = 0;
for (unsigned int i = 1; i < current_features.length; i++)
if (current_features[i].type != current_features[j].type ||
if (current_features.arrayZ[i].type != current_features.arrayZ[j].type ||
/* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
* respectively, so we mask out the low-order bit when checking for "duplicates"
* (selectors referring to the same feature setting) here. */
(!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
current_features[++j] = current_features[i];
(!current_features.arrayZ[i].is_exclusive && ((current_features.arrayZ[i].setting & ~1) != (current_features.arrayZ[j].setting & ~1))))
current_features.arrayZ[++j] = current_features.arrayZ[i];
current_features.shrink (j + 1);
}

View File

@ -286,7 +286,7 @@ HB_FUNCOBJ (hb_bool);
// Compression function for Merkle-Damgard construction.
// This function is generated using the framework provided.
#define mix(h) ( \
#define fasthash_mix(h) ( \
(void) ((h) ^= (h) >> 23), \
(void) ((h) *= 0x2127599bf4325c37ULL), \
(h) ^= (h) >> 47)
@ -310,7 +310,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
#pragma GCC diagnostic ignored "-Wcast-align"
v = * (const uint64_t *) (pos++);
#pragma GCC diagnostic pop
h ^= mix(v);
h ^= fasthash_mix(v);
h *= m;
}
}
@ -320,7 +320,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
while (pos != end)
{
v = pos++->v;
h ^= mix(v);
h ^= fasthash_mix(v);
h *= m;
}
}
@ -336,11 +336,11 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
case 2: v ^= (uint64_t)pos2[1] << 8; HB_FALLTHROUGH;
case 1: v ^= (uint64_t)pos2[0];
h ^= mix(v);
h ^= fasthash_mix(v);
h *= m;
}
return mix(h);
return fasthash_mix(h);
}
static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)

View File

@ -251,7 +251,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
if (end < start + 2)
return;
for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--)
unsigned stop = start + (end - start) / 2;
for (unsigned lhs = start, rhs = end - 1; lhs < stop; lhs++, rhs--)
hb_swap (arrayZ[rhs], arrayZ[lhs]);
}

View File

@ -212,6 +212,7 @@ struct hb_atomic_ptr_t
T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
operator bool () const { return get_acquire () != nullptr; }
T * operator -> () const { return get_acquire (); }
template <typename C> operator C * () const { return get_acquire (); }

View File

@ -78,6 +78,28 @@ struct hb_vector_size_t
hb_vector_size_t operator ~ () const
{ return process (hb_bitwise_neg); }
operator bool () const
{
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
if (v[i])
return true;
return false;
}
operator unsigned int () const
{
unsigned int r = 0;
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
r += hb_popcount (v[i]);
return r;
}
bool operator == (const hb_vector_size_t &o) const
{
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
if (v[i] != o.v[i])
return false;
return true;
}
hb_array_t<const elt_t> iter () const
{ return hb_array (v); }
@ -89,6 +111,8 @@ struct hb_vector_size_t
struct hb_bit_page_t
{
hb_bit_page_t () { init0 (); }
void init0 () { v.init0 (); population = 0; }
void init1 () { v.init1 (); population = PAGE_BITS; }
@ -101,10 +125,9 @@ struct hb_bit_page_t
bool is_empty () const
{
if (has_population ()) return !population;
return
+ hb_iter (v)
| hb_none
;
bool empty = !v;
if (empty) population = 0;
return empty;
}
uint32_t hash () const
{
@ -115,6 +138,10 @@ struct hb_bit_page_t
void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); }
void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
bool may_have (hb_codepoint_t g) const { return get (g); }
bool operator [] (hb_codepoint_t g) const { return get (g); }
bool operator () (hb_codepoint_t g) const { return get (g); }
void add_range (hb_codepoint_t a, hb_codepoint_t b)
{
@ -220,13 +247,17 @@ struct hb_bit_page_t
}
bool operator == (const hb_bit_page_t &other) const { return is_equal (other); }
bool is_equal (const hb_bit_page_t &other) const
bool is_equal (const hb_bit_page_t &other) const { return v == other.v; }
bool intersects (const hb_bit_page_t &other) const
{
for (unsigned i = 0; i < len (); i++)
if (v[i] != other.v[i])
return false;
return true;
if (v[i] & other.v[i])
return true;
return false;
}
bool may_intersect (const hb_bit_page_t &other) const
{ return intersects (other); }
bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); }
bool is_subset (const hb_bit_page_t &larger_page) const
{
@ -241,14 +272,10 @@ struct hb_bit_page_t
}
bool has_population () const { return population != UINT_MAX; }
unsigned int get_population () const
unsigned get_population () const
{
if (has_population ()) return population;
population =
+ hb_iter (v)
| hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u)
;
return population;
return population = v;
}
bool next (hb_codepoint_t *codepoint) const

View File

@ -126,6 +126,7 @@ struct hb_bit_set_invertible_t
{ unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); }
bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; }
bool may_have (hb_codepoint_t g) const { return get (g); }
/* Has interface. */
bool operator [] (hb_codepoint_t k) const { return get (k); }
@ -139,6 +140,9 @@ struct hb_bit_set_invertible_t
hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool may_intersect (const hb_bit_set_invertible_t &other) const
{ return inverted || other.inverted || s.intersects (other.s); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{
hb_codepoint_t c = first - 1;

View File

@ -88,10 +88,11 @@ struct hb_bit_set_t
{
if (unlikely (!successful)) return false;
if (pages.length == 0 && count == 1)
if (pages.length < count && count <= 2)
exact_size = true; // Most sets are small and local
if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size)))
if (unlikely (!pages.resize (count, clear, exact_size) ||
!page_map.resize (count, clear)))
{
pages.resize (page_map.length, clear, exact_size);
successful = false;
@ -297,9 +298,9 @@ struct hb_bit_set_t
unsigned int write_index = 0;
for (unsigned int i = 0; i < page_map.length; i++)
{
int m = (int) page_map[i].major;
int m = (int) page_map.arrayZ[i].major;
if (m < ds || de < m)
page_map[write_index++] = page_map[i];
page_map.arrayZ[write_index++] = page_map.arrayZ[i];
}
compact (compact_workspace, write_index);
resize (write_index);
@ -345,6 +346,7 @@ struct hb_bit_set_t
return false;
return page->get (g);
}
bool may_have (hb_codepoint_t g) const { return get (g); }
/* Has interface. */
bool operator [] (hb_codepoint_t k) const { return get (k); }
@ -358,6 +360,31 @@ struct hb_bit_set_t
hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool intersects (const hb_bit_set_t &other) const
{
unsigned int na = pages.length;
unsigned int nb = other.pages.length;
unsigned int a = 0, b = 0;
for (; a < na && b < nb; )
{
if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
{
if (page_at (a).intersects (other.page_at (b)))
return true;
a++;
b++;
}
else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
a++;
else
b++;
}
return false;
}
bool may_intersect (const hb_bit_set_t &other) const
{ return intersects (other); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{
hb_codepoint_t c = first - 1;
@ -389,7 +416,7 @@ struct hb_bit_set_t
{
if (page_at (a).is_empty ()) { a++; continue; }
if (other.page_at (b).is_empty ()) { b++; continue; }
if (page_map[a].major != other.page_map[b].major ||
if (page_map.arrayZ[a].major != other.page_map.arrayZ[b].major ||
!page_at (a).is_equal (other.page_at (b)))
return false;
a++;
@ -412,8 +439,8 @@ struct hb_bit_set_t
uint32_t spi = 0;
for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
{
uint32_t spm = page_map[spi].major;
uint32_t lpm = larger_set.page_map[lpi].major;
uint32_t spm = page_map.arrayZ[spi].major;
uint32_t lpm = larger_set.page_map.arrayZ[lpi].major;
auto sp = page_at (spi);
if (spm < lpm && !sp.is_empty ())
@ -503,7 +530,7 @@ struct hb_bit_set_t
for (; a < na && b < nb; )
{
if (page_map[a].major == other.page_map[b].major)
if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
{
if (!passthru_left)
{
@ -512,7 +539,7 @@ struct hb_bit_set_t
// passthru_left is set since no left side pages will be removed
// in that case.
if (write_index < a)
page_map[write_index] = page_map[a];
page_map.arrayZ[write_index] = page_map.arrayZ[a];
write_index++;
}
@ -520,7 +547,7 @@ struct hb_bit_set_t
a++;
b++;
}
else if (page_map[a].major < other.page_map[b].major)
else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
{
if (passthru_left)
count++;
@ -765,8 +792,8 @@ struct hb_bit_set_t
unsigned int initial_size = size;
for (unsigned int i = start_page; i < page_map.length && size; i++)
{
uint32_t base = major_start (page_map[i].major);
unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size);
uint32_t base = major_start (page_map.arrayZ[i].major);
unsigned int n = pages[page_map.arrayZ[i].index].write (base, start_page_value, out, size);
out += n;
size -= n;
start_page_value = 0;
@ -814,8 +841,8 @@ struct hb_bit_set_t
hb_codepoint_t next_value = codepoint + 1;
for (unsigned int i=start_page; i<page_map.length && size; i++)
{
uint32_t base = major_start (page_map[i].major);
unsigned int n = pages[page_map[i].index].write_inverted (base, start_page_value, out, size, &next_value);
uint32_t base = major_start (page_map.arrayZ[i].major);
unsigned int n = pages[page_map.arrayZ[i].index].write_inverted (base, start_page_value, out, size, &next_value);
out += n;
size -= n;
start_page_value = 0;
@ -846,8 +873,8 @@ struct hb_bit_set_t
unsigned count = pages.length;
for (unsigned i = 0; i < count; i++)
{
const auto& map = page_map[i];
const auto& page = pages[map.index];
const auto& map = page_map.arrayZ[i];
const auto& page = pages.arrayZ[map.index];
if (!page.is_empty ())
return map.major * page_t::PAGE_BITS + page.get_min ();
@ -859,8 +886,8 @@ struct hb_bit_set_t
unsigned count = pages.length;
for (signed i = count - 1; i >= 0; i--)
{
const auto& map = page_map[(unsigned) i];
const auto& page = pages[map.index];
const auto& map = page_map.arrayZ[(unsigned) i];
const auto& page = pages.arrayZ[map.index];
if (!page.is_empty ())
return map.major * page_t::PAGE_BITS + page.get_max ();
@ -961,7 +988,7 @@ struct hb_bit_set_t
return nullptr;
last_page_lookup = i;
return &pages.arrayZ[page_map[i].index];
return &pages.arrayZ[page_map.arrayZ[i].index];
}
page_t &page_at (unsigned int i)
{

View File

@ -34,36 +34,36 @@
#line 36 "hb-buffer-deserialize-json.hh"
static const unsigned char _deserialize_json_trans_keys[] = {
0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u,
48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u,
0u, 0u, 9u, 123u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u,
9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u,
48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u,
9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u,
34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u,
9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u,
9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u,
9u, 123u, 0u, 0u, 0
9u, 93u, 9u, 123u, 0u, 0u, 0
};
static const char _deserialize_json_key_spans[] = {
0, 115, 26, 21, 2, 1, 50, 49,
10, 117, 117, 85, 117, 1, 50, 49,
0, 115, 115, 26, 21, 2, 1, 50,
49, 10, 117, 117, 117, 1, 50, 49,
10, 117, 117, 1, 1, 50, 49, 117,
117, 2, 1, 50, 49, 10, 117, 117,
1, 50, 49, 10, 117, 117, 1, 1,
50, 49, 117, 117, 1, 50, 49, 59,
117, 59, 117, 117, 1, 50, 49, 117,
115, 0
85, 115, 0
};
static const short _deserialize_json_index_offsets[] = {
0, 0, 116, 143, 165, 168, 170, 221,
271, 282, 400, 518, 604, 722, 724, 775,
825, 836, 954, 1072, 1074, 1076, 1127, 1177,
1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648,
1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118,
2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560,
2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137,
3255, 3371
0, 0, 116, 232, 259, 281, 284, 286,
337, 387, 398, 516, 634, 752, 754, 805,
855, 866, 984, 1102, 1104, 1106, 1157, 1207,
1325, 1443, 1446, 1448, 1499, 1549, 1560, 1678,
1796, 1798, 1849, 1899, 1910, 2028, 2146, 2148,
2150, 2201, 2251, 2369, 2487, 2489, 2540, 2590,
2650, 2768, 2828, 2946, 3064, 3066, 3117, 3167,
3285, 3371, 3487
};
static const char _deserialize_json_indicies[] = {
@ -77,51 +77,51 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 3, 1, 2, 2, 2,
2, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 3, 3, 3,
3, 3, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 3, 1, 4, 1,
5, 1, 6, 7, 1, 8, 9, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 10, 1, 11, 12,
1, 13, 1, 13, 13, 13, 13, 13,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 13, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 14, 1, 14, 14,
14, 14, 14, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 14, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 15, 1, 1, 16, 17, 17,
17, 17, 17, 17, 17, 17, 17, 1,
18, 19, 19, 19, 19, 19, 19, 19,
19, 19, 1, 20, 20, 20, 20, 20,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 20, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 3,
1, 4, 4, 4, 4, 4, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
4, 1, 5, 1, 6, 1, 7, 8,
1, 9, 10, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
11, 1, 12, 13, 1, 14, 1, 14,
14, 14, 14, 14, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 14, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
15, 1, 15, 15, 15, 15, 15, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 15, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 16, 1,
1, 17, 18, 18, 18, 18, 18, 18,
18, 18, 18, 1, 19, 20, 20, 20,
20, 20, 20, 20, 20, 20, 1, 21,
21, 21, 21, 21, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 21, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 22,
1, 23, 23, 23, 23, 23, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
23, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 3, 1, 1, 1,
1, 1, 22, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -131,24 +131,55 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 24, 1, 25,
25, 25, 25, 25, 1, 1, 1, 1,
1, 1, 1, 23, 1, 24, 24, 24,
24, 24, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 24, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
4, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 25, 1, 21, 21, 21, 21, 21,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 21, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 22, 1,
1, 1, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 23,
1, 26, 1, 26, 26, 26, 26, 26,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 25, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 26, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 27, 1, 27, 27,
27, 27, 27, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 27, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 28, 1, 1, 29, 30, 30,
30, 30, 30, 30, 30, 30, 30, 1,
31, 32, 32, 32, 32, 32, 32, 32,
32, 32, 1, 33, 33, 33, 33, 33,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 27, 1, 20, 20, 20,
20, 20, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 20, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
21, 1, 1, 1, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 1, 1,
1, 1, 33, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 34, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -157,26 +188,15 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 22, 1, 28, 1, 28, 28, 28,
28, 28, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 28, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 35,
1, 33, 33, 33, 33, 33, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 29, 1,
29, 29, 29, 29, 29, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 29,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 30, 1, 1, 31,
33, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 34, 1, 1, 1,
32, 32, 32, 32, 32, 32, 32, 32,
32, 1, 33, 34, 34, 34, 34, 34,
34, 34, 34, 34, 1, 35, 35, 35,
35, 35, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 35, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
36, 1, 1, 1, 1, 1, 1, 1,
32, 32, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -184,41 +204,25 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 35, 1, 36,
1, 37, 1, 37, 37, 37, 37, 37,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 37, 1, 35, 35, 35, 35, 35,
1, 1, 37, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 35, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 36, 1,
1, 1, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 1, 1, 1, 1,
1, 1, 1, 1, 38, 1, 38, 38,
38, 38, 38, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 38, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 37,
1, 38, 1, 39, 1, 39, 39, 39,
39, 39, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 39, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 40, 1,
40, 40, 40, 40, 40, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 40,
1, 1, 1, 1, 1, 39, 40, 40,
40, 40, 40, 40, 40, 40, 40, 1,
41, 41, 41, 41, 41, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 41,
42, 42, 42, 42, 42, 42, 42, 42,
42, 1, 43, 43, 43, 43, 43, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 43, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 44, 1, 1,
1, 1, 1, 42, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -228,14 +232,13 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 45, 1,
43, 43, 43, 43, 43, 1, 1, 1,
1, 1, 1, 1, 43, 1, 41, 41,
41, 41, 41, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 43,
1, 1, 1, 1, 1, 41, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 44, 1, 1, 1, 46,
46, 46, 46, 46, 46, 46, 46, 46,
46, 1, 1, 1, 1, 1, 1, 1,
1, 42, 1, 1, 1, 44, 44, 44,
44, 44, 44, 44, 44, 44, 44, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -243,86 +246,116 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 45, 1, 47, 48,
1, 49, 1, 49, 49, 49, 49, 49,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 43, 1, 45, 46, 1, 47,
1, 47, 47, 47, 47, 47, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 49, 1, 1, 1, 1, 1,
47, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 50, 1, 50, 50,
50, 50, 50, 1, 1, 1, 1, 1,
1, 1, 48, 1, 48, 48, 48, 48,
48, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 50, 1, 1,
1, 1, 1, 48, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 51, 1, 1, 52, 53, 53,
53, 53, 53, 53, 53, 53, 53, 1,
54, 55, 55, 55, 55, 55, 55, 55,
55, 55, 1, 56, 56, 56, 56, 56,
49, 1, 1, 50, 51, 51, 51, 51,
51, 51, 51, 51, 51, 1, 52, 53,
53, 53, 53, 53, 53, 53, 53, 53,
1, 54, 54, 54, 54, 54, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 56, 1, 1, 1, 1, 1,
54, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 55, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 56, 1, 54,
54, 54, 54, 54, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 54, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 55, 1, 1, 1, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 56, 1, 57, 1, 57,
57, 57, 57, 57, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 57, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
58, 1, 58, 58, 58, 58, 58, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 58, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 59, 1,
1, 60, 61, 61, 61, 61, 61, 61,
61, 61, 61, 1, 62, 63, 63, 63,
63, 63, 63, 63, 63, 63, 1, 64,
64, 64, 64, 64, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 64, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 65, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 58,
1, 56, 56, 56, 56, 56, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
56, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 57, 1, 1, 1,
55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 66, 1, 64, 64, 64,
64, 64, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 64, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 58, 1, 59,
1, 59, 59, 59, 59, 59, 1, 1,
65, 1, 1, 1, 63, 63, 63, 63,
63, 63, 63, 63, 63, 63, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
59, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 60, 1, 60, 60, 60, 60,
60, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 60, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
61, 1, 1, 62, 63, 63, 63, 63,
63, 63, 63, 63, 63, 1, 64, 65,
65, 65, 65, 65, 65, 65, 65, 65,
1, 66, 66, 66, 66, 66, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
66, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 67, 1, 1, 1,
1, 66, 1, 67, 1, 68, 1, 68,
68, 68, 68, 68, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 68, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
69, 1, 69, 69, 69, 69, 69, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 69, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 70, 71, 71, 71, 71, 71, 71,
71, 71, 71, 1, 72, 72, 72, 72,
72, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 72, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 73,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 68, 1, 66,
66, 66, 66, 66, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 66, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 67, 1, 1, 1, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -330,42 +363,48 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
74, 1, 72, 72, 72, 72, 72, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 68, 1, 69, 1, 70,
1, 70, 70, 70, 70, 70, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 72, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 73, 1, 1,
1, 75, 75, 75, 75, 75, 75, 75,
75, 75, 75, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
70, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 71, 1, 71, 71, 71, 71,
71, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 71, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 72, 73, 73, 73, 73,
73, 73, 73, 73, 73, 1, 74, 74,
74, 74, 74, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 74, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 75, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 74, 1,
76, 1, 76, 76, 76, 76, 76, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 76, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 77, 1, 77, 77, 77,
77, 77, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 77, 1, 78, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 79, 80, 80, 80,
80, 80, 80, 80, 80, 80, 1, 82,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 83, 81, 84, 84, 84, 84, 84,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 84, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 85, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 76, 1, 74, 74, 74, 74,
74, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 74, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 75,
1, 1, 1, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -373,34 +412,20 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 86,
1, 81, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
76, 1, 78, 1, 78, 78, 78, 78,
78, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 78, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 79, 1, 79,
79, 79, 79, 79, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 79, 1,
80, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 81, 82,
82, 82, 82, 82, 82, 82, 82, 82,
1, 84, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 85, 83, 86, 86, 86,
86, 86, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 86, 1, 1, 1,
1, 1, 1, 81, 1, 87, 87, 87,
87, 87, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
87, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 87, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
88, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -409,63 +434,65 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 88, 1, 83, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 89, 1, 87, 87, 87, 87, 87,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 87, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 88, 1,
1, 1, 90, 90, 90, 90, 90, 90,
90, 90, 90, 90, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 83, 1, 89,
89, 89, 89, 89, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 89, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 90, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 89,
1, 91, 1, 91, 91, 91, 91, 91,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 91, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 92, 1, 92, 92,
92, 92, 92, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 92, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 93, 94, 94,
94, 94, 94, 94, 94, 94, 94, 1,
87, 87, 87, 87, 87, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 91, 1, 89, 89, 89,
89, 89, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 87,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 89, 1, 1, 1,
1, 1, 1, 88, 1, 1, 1, 95,
95, 95, 95, 95, 95, 95, 95, 95,
95, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
90, 1, 1, 1, 92, 92, 92, 92,
92, 92, 92, 92, 92, 92, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 89, 1, 96, 96,
96, 96, 96, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 96, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 91, 1, 93, 1, 93, 93, 93,
93, 93, 1, 1, 1, 1, 1, 1,
1, 97, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 93, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 94, 1,
94, 94, 94, 94, 94, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 94,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 95,
96, 96, 96, 96, 96, 96, 96, 96,
96, 1, 89, 89, 89, 89, 89, 1,
1, 1, 98, 1, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 89, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 90, 1, 1,
1, 97, 97, 97, 97, 97, 97, 97,
97, 97, 97, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -473,54 +500,42 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 91, 1,
0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 1, 1, 0
1, 1, 1, 1, 1, 1, 3, 1,
1, 0
};
static const char _deserialize_json_trans_targs[] = {
1, 0, 2, 2, 3, 4, 19, 25,
38, 44, 52, 5, 13, 6, 7, 8,
9, 12, 9, 12, 10, 2, 11, 10,
11, 11, 56, 57, 14, 15, 16, 17,
18, 17, 18, 10, 2, 11, 20, 21,
22, 23, 24, 10, 2, 11, 24, 26,
32, 27, 28, 29, 30, 31, 30, 31,
10, 2, 11, 33, 34, 35, 36, 37,
36, 37, 10, 2, 11, 39, 40, 41,
42, 43, 10, 2, 11, 43, 45, 46,
47, 50, 51, 47, 48, 49, 10, 2,
11, 10, 2, 11, 51, 53, 54, 50,
55, 55
1, 0, 2, 3, 3, 4, 5, 19,
25, 38, 44, 52, 6, 13, 7, 8,
9, 10, 12, 10, 12, 11, 3, 56,
11, 56, 14, 15, 16, 17, 18, 17,
18, 11, 3, 56, 20, 21, 22, 23,
24, 11, 3, 56, 24, 26, 32, 27,
28, 29, 30, 31, 30, 31, 11, 3,
56, 33, 34, 35, 36, 37, 36, 37,
11, 3, 56, 39, 40, 41, 42, 43,
11, 3, 56, 43, 45, 46, 47, 50,
51, 47, 48, 49, 11, 3, 56, 11,
3, 56, 51, 53, 54, 50, 55, 55,
56, 57, 58
};
static const char _deserialize_json_trans_actions[] = {
0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2,
2, 2, 0, 0, 3, 3, 4, 0,
5, 0, 0, 0, 0, 0, 2, 2,
2, 0, 0, 6, 6, 7, 0, 0,
0, 2, 2, 8, 8, 9, 0, 0,
0, 0, 0, 2, 2, 2, 0, 0,
10, 10, 11, 0, 0, 2, 2, 2,
0, 0, 12, 12, 13, 0, 0, 0,
2, 2, 14, 14, 15, 0, 0, 0,
2, 16, 16, 0, 17, 0, 18, 18,
19, 20, 20, 21, 17, 0, 0, 22,
22, 23
0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 0, 0, 3, 3, 4,
0, 5, 0, 0, 2, 2, 2, 0,
0, 6, 6, 7, 0, 0, 0, 2,
2, 8, 8, 9, 0, 0, 0, 0,
0, 2, 2, 2, 0, 0, 10, 10,
11, 0, 0, 2, 2, 2, 0, 0,
12, 12, 13, 0, 0, 0, 2, 2,
14, 14, 15, 0, 0, 0, 2, 16,
16, 0, 17, 0, 18, 18, 19, 20,
20, 21, 17, 0, 0, 22, 22, 23,
0, 0, 0
};
static const int deserialize_json_start = 1;
@ -545,22 +560,17 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
/* Ensure we have positions. */
(void) hb_buffer_get_glyph_positions (buffer, nullptr);
while (p < pe && ISSPACE (*p))
p++;
if (p < pe && *p == (buffer->len ? ',' : '['))
*end_ptr = ++p;
const char *tok = nullptr;
int cs;
hb_glyph_info_t info = {0};
hb_glyph_position_t pos = {0};
#line 559 "hb-buffer-deserialize-json.hh"
#line 569 "hb-buffer-deserialize-json.hh"
{
cs = deserialize_json_start;
}
#line 564 "hb-buffer-deserialize-json.hh"
#line 574 "hb-buffer-deserialize-json.hh"
{
int _slen;
int _trans;
@ -772,7 +782,7 @@ _resume:
*end_ptr = p;
}
break;
#line 776 "hb-buffer-deserialize-json.hh"
#line 786 "hb-buffer-deserialize-json.hh"
}
_again:
@ -784,7 +794,7 @@ _again:
_out: {}
}
#line 137 "hb-buffer-deserialize-json.rl"
#line 132 "hb-buffer-deserialize-json.rl"
*end_ptr = p;

View File

@ -860,7 +860,7 @@ hb_buffer_destroy (hb_buffer_t *buffer)
* @destroy: (nullable): A callback to call when @data is not needed anymore
* @replace: Whether to replace an existing data with the same key
*
* Attaches a user-data key/data pair to the specified buffer.
* Attaches a user-data key/data pair to the specified buffer.
*
* Return value: `true` if success, `false` otherwise
*
@ -1209,7 +1209,7 @@ hb_buffer_get_flags (const hb_buffer_t *buffer)
* @cluster_level: The cluster level to set on the buffer
*
* Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t
* dictates one aspect of how HarfBuzz will treat non-base characters
* dictates one aspect of how HarfBuzz will treat non-base characters
* during shaping.
*
* Since: 0.9.42
@ -1229,7 +1229,7 @@ hb_buffer_set_cluster_level (hb_buffer_t *buffer,
* @buffer: An #hb_buffer_t
*
* Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t
* dictates one aspect of how HarfBuzz will treat non-base characters
* dictates one aspect of how HarfBuzz will treat non-base characters
* during shaping.
*
* Return value: The cluster level of @buffer
@ -1983,7 +1983,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer,
* @buffer: An #hb_buffer_t
* @source: source #hb_buffer_t
* @start: start index into source buffer to copy. Use 0 to copy from start of buffer.
* @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer.
* @end: end index into source buffer to copy. Use @UINT_MAX (or ((unsigned int) -1)) to copy to end of buffer.
*
* Append (part of) contents of another buffer to this buffer.
*

View File

@ -32,7 +32,6 @@
#include "hb.hh"
#include "hb-unicode.hh"
#include "hb-set-digest.hh"
static_assert ((sizeof (hb_glyph_info_t) == 20), "");
@ -182,22 +181,24 @@ struct hb_buffer_t
allocated_var_bits = 0;
}
HB_ALWAYS_INLINE
hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; }
HB_ALWAYS_INLINE
hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
hb_set_digest_t digest () const
{
hb_set_digest_t d;
d.init ();
d.add_array (&info[0].codepoint, len, sizeof (info[0]));
return d;
}
template <typename set_t>
void collect_codepoints (set_t &d) const
{ d.clear (); d.add_array (&info[0].codepoint, len, sizeof (info[0])); }
HB_INTERNAL void similar (const hb_buffer_t &src);
HB_INTERNAL void reset ();

View File

@ -522,7 +522,7 @@ struct parsed_values_t
void alloc (unsigned n)
{
values.alloc (n, true);
values.alloc_exact (n);
}
void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ())

View File

@ -157,6 +157,7 @@
#define HB_NO_FALLBACK_SHAPE
#define HB_NO_OT_KERN
#define HB_NO_OT_LAYOUT_BLOCKLIST
#define HB_NO_AAT_LAYOUT_BLOCKLIST
#define HB_NO_OT_SHAPE_FALLBACK
#endif

View File

@ -60,11 +60,25 @@ hb_coretext_get_nominal_glyph (hb_font_t *font HB_UNUSED,
void *user_data HB_UNUSED)
{
CTFontRef ct_font = (CTFontRef) font_data;
UniChar ch = unicode;
CGGlyph cg_glyph;
if (CTFontGetGlyphsForCharacters (ct_font, &ch, &cg_glyph, 1))
UniChar ch[2];
CGGlyph cg_glyph[2];
unsigned count = 0;
if (unicode <= 0xFFFF)
{
*glyph = cg_glyph;
ch[count++] = unicode;
}
else if (unicode <= 0x10FFFF)
{
ch[count++] = (unicode >> 10) + 0xD7C0;
ch[count++] = (unicode & 0x3FF) + 0xDC00;
}
else
ch[count++] = 0xFFFD;
if (CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count))
{
*glyph = cg_glyph[0];
return true;
}
return false;
@ -80,6 +94,31 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
unsigned int glyph_stride,
void *user_data HB_UNUSED)
{
// If any non-BMP codepoint is requested, use the slow path.
bool slow_path = false;
auto *unicode = first_unicode;
for (unsigned i = 0; i < count; i++)
{
if (*unicode > 0xFFFF)
{
slow_path = true;
break;
}
unicode = &StructAtOffset<const hb_codepoint_t> (unicode, unicode_stride);
}
if (unlikely (slow_path))
{
for (unsigned i = 0; i < count; i++)
{
if (!hb_coretext_get_nominal_glyph (font, font_data, *first_unicode, first_glyph, nullptr))
return i;
first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
}
return count;
}
CTFontRef ct_font = (CTFontRef) font_data;
UniChar ch[MAX_GLYPHS];
@ -92,7 +131,16 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
ch[j] = *first_unicode;
first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
}
CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c);
if (unlikely (!CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c)))
{
// Use slow path partially and return at first failure.
for (unsigned j = 0; j < c; j++)
{
if (!hb_coretext_get_nominal_glyph (font, font_data, ch[j], first_glyph, nullptr))
return i + j;
first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
}
}
for (unsigned j = 0; j < c; j++)
{
*first_glyph = cg_glyph[j];
@ -113,13 +161,38 @@ hb_coretext_get_variation_glyph (hb_font_t *font HB_UNUSED,
{
CTFontRef ct_font = (CTFontRef) font_data;
UniChar ch[2] = { unicode, variation_selector };
CGGlyph cg_glyph[2];
UniChar ch[4];
CGGlyph cg_glyph[4];
unsigned count = 0;
CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, 2);
// Add Unicode, then variation selector. Ugly, but works.
//
if (unicode <= 0xFFFF)
ch[count++] = unicode;
else if (unicode <= 0x10FFFF)
{
ch[count++] = (unicode >> 10) + 0xD7C0;
ch[count++] = (unicode & 0x3FF) + 0xDC00;
}
else
ch[count++] = 0xFFFD;
if (cg_glyph[1])
return false;
if (variation_selector <= 0xFFFF)
ch[count++] = variation_selector;
else if (variation_selector <= 0x10FFFF)
{
ch[count++] = (variation_selector >> 10) + 0xD7C0;
ch[count++] = (variation_selector & 0x3FF) + 0xDC00;
}
else
ch[count++] = 0xFFFD;
CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count);
// All except for first should be zero if we succeeded
for (unsigned i = 1; i < count; i++)
if (cg_glyph[i])
return false;
*glyph = cg_glyph[0];
return true;
@ -438,10 +511,6 @@ _hb_coretext_get_font_funcs ()
* created with hb_face_create(), and therefore was not
* initially configured to use CoreText font functions.
*
* An #hb_font_t object created with hb_coretext_font_create()
* is preconfigured for CoreText font functions and does not
* require this function to be used.
*
* <note>Note: Internally, this function creates a CTFont.
* </note>
*
@ -452,7 +521,12 @@ hb_coretext_font_set_funcs (hb_font_t *font)
{
CTFontRef ct_font = hb_coretext_font_get_ct_font (font);
if (unlikely (!ct_font))
{
hb_font_set_funcs (font,
hb_font_funcs_get_empty (),
nullptr, nullptr);
return;
}
hb_font_set_funcs (font,
_hb_coretext_get_font_funcs (),

View File

@ -45,9 +45,6 @@
* Functions for using HarfBuzz with the CoreText fonts.
**/
/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
static CTFontRef create_ct_font (CGFontRef cg_font, CGFloat font_size);
static void
@ -384,9 +381,9 @@ hb_coretext_face_create_from_file_or_fail (const char *file_name,
(CTFontDescriptorRef) CFArrayGetValueAtIndex (ct_font_desc_array, index) : nullptr;
if (unlikely (!ct_font_desc))
{
CFRelease (ct_font_desc_array);
CFRelease (url);
return nullptr;
CFRelease (ct_font_desc_array);
CFRelease (url);
return nullptr;
}
CFRelease (url);
auto ct_font = ct_font_desc ? CTFontCreateWithFontDescriptor (ct_font_desc, 0, nullptr) : nullptr;
@ -400,6 +397,7 @@ hb_coretext_face_create_from_file_or_fail (const char *file_name,
return nullptr;
hb_face_t *face = hb_coretext_face_create (cg_font);
CFRelease (cg_font);
if (unlikely (hb_face_is_immutable (face)))
return nullptr;
@ -432,7 +430,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
if (unlikely (!face_data)) return nullptr;
CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem);
CGFloat font_size = (CGFloat) (font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE);
CTFontRef ct_font = create_ct_font (cg_font, font_size);
if (unlikely (!ct_font))
@ -451,11 +449,11 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
for (unsigned i = 0; i < font->num_coords; i++)
{
if (font->coords[i] == 0.) continue;
hb_ot_var_axis_info_t info;
unsigned int c = 1;
hb_ot_var_get_axis_infos (font->face, i, &c, &info);
if (font->design_coords[i] == info.default_value)
continue;
float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value);
CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag);
@ -499,7 +497,7 @@ _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
* CTFontRef.
*
* The created font uses the default font functions implemented
* navitely by HarfBuzz. If you want to use the CoreText font functions
* natively by HarfBuzz. If you want to use the CoreText font functions
* instead (rarely needed), you can do so by calling
* by hb_coretext_font_set_funcs().
*
@ -521,6 +519,36 @@ hb_coretext_font_create (CTFontRef ct_font)
hb_font_set_ptem (font, CTFontGetSize (ct_font));
/* Copy font variations */
CFDictionaryRef variations = CTFontCopyVariation (ct_font);
if (variations)
{
hb_vector_t<hb_variation_t> vars;
hb_vector_t<CFTypeRef> keys;
hb_vector_t<CFTypeRef> values;
CFIndex count = CFDictionaryGetCount (variations);
if (unlikely (!vars.alloc_exact (count) || !keys.resize_exact (count) || !values.resize_exact (count)))
goto done;
// Fetch them one by one and collect in a vector of our own.
CFDictionaryGetKeysAndValues (variations, keys.arrayZ, values.arrayZ);
for (CFIndex i = 0; i < count; i++)
{
int tag;
float value;
CFNumberGetValue ((CFNumberRef) keys.arrayZ[i], kCFNumberIntType, &tag);
CFNumberGetValue ((CFNumberRef) values.arrayZ[i], kCFNumberFloatType, &value);
hb_variation_t var = {tag, value};
vars.push (var);
}
hb_font_set_variations (font, vars.arrayZ, vars.length);
done:
CFRelease (variations);
}
/* Let there be dragons here... */
font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font));

View File

@ -0,0 +1,161 @@
/*
* Copyright © 2025 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* 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.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_DECYCLER_HH
#define HB_DECYCLER_HH
#include "hb.hh"
/*
* hb_decycler_t is an efficient cycle detector for graph traversal.
* It's a simple tortoise-and-hare algorithm with a twist: it's
* designed to detect cycles while traversing a graph in a DFS manner,
* instead of just a linked list.
*
* For Floyd's tortoise and hare algorithm, see:
* https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare
*
* Like Floyd's algorithm, hb_decycler_t is O(n) in the number of nodes
* in the graph. Unlike Floyd's algorithm, hb_decycler_t is designed
* to be used in a DFS traversal, where the graph is not a simple
* linked list, but a tree with cycles. Like Floyd's algorithm, it is
* constant-memory (just two pointers).
*
* The decycler works by creating an implicit linked-list on the stack,
* of the path from the root to the current node, and apply Floyd's
* algorithm on that list as it goes.
*
* The decycler is malloc-free, and as such, much faster to use than a
* hb_set_t or hb_map_t equivalent.
*
* The decycler detects cycles in the graph *eventually*, not *immediately*.
* That is, it may not detect a cycle until the cycle is fully traversed,
* even multiple times. See Floyd's algorithm analysis for details.
*
* The implementation saves a pointer storage on the stack by combining
* this->u.decycler and this->u.next into a union. This is possible because
* at any point we only need one of those values. The invariant is that
* after construction, and before destruction, of a node, the u.decycler
* field is always valid. The u.next field is only valid when the node is
* in the traversal path, parent to another node.
*
* There are three method's:
*
* - hb_decycler_node_t() constructor: Creates a new node in the traversal.
* The constructor takes a reference to the decycler object and inserts
* itself as the latest node in the traversal path, by advancing the hare
* pointer, and for every other descent, advancing the tortoise pointer.
*
* - ~hb_decycler_node_t() destructor: Restores the decycler object to its
* previous state by removing the node from the traversal path.
*
* - bool visit(uintptr_t value): Called on every node in the graph. Returns
* true if the node is not part of a cycle, and false if it is. The value
* parameter is used to detect cycles. It's the caller's responsibility
* to ensure that the value is unique for each node in the graph.
* The cycle detection is as simple as comparing the value to the value
* held by the tortoise pointer, which is the Floyd's algorithm.
*
* For usage examples see test-decycler.cc.
*/
struct hb_decycler_node_t;
struct hb_decycler_t
{
friend struct hb_decycler_node_t;
private:
bool tortoise_asleep = true;
hb_decycler_node_t *tortoise = nullptr;
hb_decycler_node_t *hare = nullptr;
};
struct hb_decycler_node_t
{
hb_decycler_node_t (hb_decycler_t &decycler)
{
u.decycler = &decycler;
decycler.tortoise_asleep = !decycler.tortoise_asleep;
if (!decycler.tortoise)
{
// First node.
decycler.tortoise = decycler.hare = this;
return;
}
if (!decycler.tortoise_asleep)
decycler.tortoise = decycler.tortoise->u.next; // Time to move.
this->prev = decycler.hare;
decycler.hare->u.next = this;
decycler.hare = this;
}
~hb_decycler_node_t ()
{
hb_decycler_t &decycler = *u.decycler;
// Inverse of the constructor.
assert (decycler.hare == this);
decycler.hare = prev;
if (prev)
prev->u.decycler = &decycler;
assert (decycler.tortoise);
if (!decycler.tortoise_asleep)
decycler.tortoise = decycler.tortoise->prev;
decycler.tortoise_asleep = !decycler.tortoise_asleep;
}
bool visit (uintptr_t value_)
{
value = value_;
hb_decycler_t &decycler = *u.decycler;
if (decycler.tortoise == this)
return true; // First node; not a cycle.
if (decycler.tortoise->value == value)
return false; // Cycle detected.
return true;
}
private:
union {
hb_decycler_t *decycler;
hb_decycler_node_t *next;
} u = {nullptr};
hb_decycler_node_t *prev = nullptr;
uintptr_t value = 0;
};
#endif /* HB_DECYCLER_HH */

View File

@ -29,6 +29,7 @@
#include "hb-shaper-impl.hh"
#include <dwrite_1.h>
#include <dwrite_3.h>
#include "hb-directwrite.h"
@ -275,6 +276,8 @@ _hb_directwrite_shaper_font_data_create (hb_font_t *font)
void
_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
{
if (data != HB_SHAPER_DATA_SUCCEEDED)
((IDWriteFont *) data)->Release();
}
@ -839,7 +842,7 @@ _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *
}
static void
_hb_directwrite_font_release (void *data)
_hb_directwrite_face_release (void *data)
{
if (data)
((IDWriteFontFace *) data)->Release ();
@ -847,7 +850,7 @@ _hb_directwrite_font_release (void *data)
/**
* hb_directwrite_face_create:
* @font_face: a DirectWrite IDWriteFontFace object.
* @dw_face: a DirectWrite IDWriteFontFace object.
*
* Constructs a new face object from the specified DirectWrite IDWriteFontFace.
*
@ -856,12 +859,12 @@ _hb_directwrite_font_release (void *data)
* Since: 2.4.0
**/
hb_face_t *
hb_directwrite_face_create (IDWriteFontFace *font_face)
hb_directwrite_face_create (IDWriteFontFace *dw_face)
{
if (font_face)
font_face->AddRef ();
return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
_hb_directwrite_font_release);
if (dw_face)
dw_face->AddRef ();
return hb_face_create_for_tables (_hb_directwrite_reference_table, dw_face,
_hb_directwrite_face_release);
}
/**
@ -880,5 +883,80 @@ hb_directwrite_face_get_font_face (hb_face_t *face)
return face->data.directwrite->fontFace;
}
/**
* hb_directwrite_font_create:
* @dw_font: a DirectWrite IDWriteFont object.
*
* Constructs a new font object from the specified DirectWrite IDWriteFont.
*
* Return value: #hb_font_t object corresponding to the given input
*
* Since: 10.3.0
**/
hb_font_t *
hb_directwrite_font_create (IDWriteFont *dw_font)
{
IDWriteFontFace *dw_face = nullptr;
IDWriteFontFace5 *dw_face5 = nullptr;
if (FAILED (dw_font->CreateFontFace (&dw_face)))
return hb_font_get_empty ();
hb_face_t *face = hb_directwrite_face_create (dw_face);
hb_font_t *font = hb_font_create (face);
hb_face_destroy (face);
if (unlikely (hb_object_is_immutable (font)))
goto done;
/* Copy font variations */
if (SUCCEEDED (dw_face->QueryInterface (__uuidof (IDWriteFontFace5), (void**) &dw_face5)))
{
if (dw_face5->HasVariations ())
{
hb_vector_t<DWRITE_FONT_AXIS_VALUE> values;
uint32_t count = dw_face5->GetFontAxisValueCount ();
if (likely (values.resize_exact (count)) &&
SUCCEEDED (dw_face5->GetFontAxisValues (values.arrayZ, count)))
{
hb_vector_t<hb_variation_t> vars;
if (likely (vars.resize_exact (count)))
{
for (uint32_t i = 0; i < count; ++i)
{
hb_tag_t tag = values[i].axisTag;
float value = values[i].value;
vars[i] = {tag, value};
}
hb_font_set_variations (font, vars.arrayZ, vars.length);
}
}
}
dw_face5->Release ();
}
dw_font->AddRef ();
font->data.directwrite.cmpexch (nullptr, (hb_directwrite_font_data_t *) dw_font);
done:
dw_face->Release ();
return font;
}
/**
* hb_directwrite_font_get_dw_font:
* @font: a #hb_font_t object
*
* Gets the DirectWrite IDWriteFont associated with @font.
*
* Return value: DirectWrite IDWriteFont object corresponding to the given input
*
* Since: 10.3.0
**/
IDWriteFont *
hb_directwrite_font_get_dw_font (hb_font_t *font)
{
return (IDWriteFont *) (const void *) font->data.directwrite;
}
#endif

View File

@ -30,11 +30,17 @@
HB_BEGIN_DECLS
HB_EXTERN hb_face_t *
hb_directwrite_face_create (IDWriteFontFace *font_face);
hb_directwrite_face_create (IDWriteFontFace *dw_face);
HB_EXTERN IDWriteFontFace *
hb_directwrite_face_get_font_face (hb_face_t *face);
HB_EXTERN hb_font_t *
hb_directwrite_font_create (IDWriteFont *dw_font);
HB_EXTERN IDWriteFont *
hb_directwrite_font_get_dw_font (hb_font_t *font);
HB_END_DECLS
#endif /* HB_DIRECTWRITE_H */

View File

@ -291,6 +291,7 @@ hb_face_create_or_fail (hb_blob_t *blob,
return face;
}
#ifndef HB_NO_OPEN
/**
* hb_face_create_from_file_or_fail:
* @file_name: A font filename
@ -317,6 +318,7 @@ hb_face_create_from_file_or_fail (const char *file_name,
return face;
}
#endif
/**
* hb_face_get_empty:
@ -491,9 +493,10 @@ hb_face_reference_table (const hb_face_t *face,
* hb_face_reference_blob:
* @face: A face object
*
* Fetches a pointer to the binary blob that contains the
* specified face. Returns an empty blob if referencing face data is not
* possible.
* Fetches a pointer to the binary blob that contains the specified face.
* If referencing the face data is not possible, this function creates a blob
* out of individual table blobs if hb_face_get_table_tags() works with this
* face, otherwise it returns an empty blob.
*
* Return value: (transfer full): A pointer to the blob for @face
*
@ -502,7 +505,37 @@ hb_face_reference_table (const hb_face_t *face,
hb_blob_t *
hb_face_reference_blob (hb_face_t *face)
{
return face->reference_table (HB_TAG_NONE);
hb_blob_t *blob = face->reference_table (HB_TAG_NONE);
if (blob == hb_blob_get_empty ())
{
// If referencing the face blob is not possible (e.g. not implemented by the
// font functions), use face builder to create a blob out of individual
// table blobs.
unsigned total_count = hb_face_get_table_tags (face, 0, nullptr, nullptr);
if (total_count)
{
hb_tag_t tags[64];
unsigned count = ARRAY_LENGTH (tags);
hb_face_t* builder = hb_face_builder_create ();
for (unsigned offset = 0; offset < total_count; offset += count)
{
hb_face_get_table_tags (face, offset, &count, tags);
for (unsigned i = 0; i < count; i++)
{
hb_blob_t *table = hb_face_reference_table (face, tags[i]);
hb_face_builder_add_table (builder, tags[i], table);
hb_blob_destroy (table);
}
}
blob = hb_face_reference_blob (builder);
hb_face_destroy (builder);
}
}
return blob;
}
/**
@ -644,6 +677,7 @@ hb_face_set_get_table_tags_func (hb_face_t *face,
{
if (destroy)
destroy (user_data);
return;
}
if (face->get_table_tags_destroy)

View File

@ -73,9 +73,14 @@ hb_face_create_from_file_or_fail (const char *file_name,
* @tag: the tag of the table to reference
* @user_data: User data pointer passed by the caller
*
* Callback function for hb_face_create_for_tables().
* Callback function for hb_face_create_for_tables(). The @tag is the tag of the
* table to reference, and the special tag #HB_TAG_NONE is used to reference the
* blob of the face itself. If referencing the face blob is not possible, it is
* recommended to set hb_get_table_tags_func_t on the @face to allow
* hb_face_reference_blob() to create a face blob out of individual table blobs.
*
* Return value: (transfer full): A pointer to the @tag table within @face
* Return value: (transfer full): A pointer to the @tag table within @face or
* `NULL` if the table is not found or cannot be referenced.
*
* Since: 0.9.2
*/

View File

@ -27,6 +27,7 @@
#include "hb.hh"
#include "hb-decycler.hh"
#include "hb-paint-extents.hh"
#include FT_COLOR_H
@ -105,8 +106,8 @@ struct hb_ft_paint_context_t
FT_Color *palette;
unsigned palette_index;
hb_color_t foreground;
hb_map_t current_glyphs;
hb_map_t current_layers;
hb_decycler_t glyphs_decycler;
hb_decycler_t layers_decycler;
int depth_left = HB_MAX_NESTING_LEVEL;
int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
};
@ -218,22 +219,19 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
case FT_COLR_PAINTFORMAT_COLR_LAYERS:
{
FT_OpaquePaint other_paint = {0};
hb_decycler_node_t node (c->layers_decycler);
while (FT_Get_Paint_Layers (ft_face,
&paint.u.colr_layers.layer_iterator,
&other_paint))
{
unsigned i = paint.u.colr_layers.layer_iterator.layer;
if (unlikely (c->current_layers.has (i)))
// FreeType doesn't provide a way to get the layer index, so we use the pointer
// for cycle detection.
if (unlikely (!node.visit ((uintptr_t) other_paint.p)))
continue;
c->current_layers.add (i);
c->funcs->push_group (c->data);
c->recurse (other_paint);
c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
c->current_layers.del (i);
}
}
break;
@ -333,18 +331,16 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
{
hb_codepoint_t gid = paint.u.colr_glyph.glyphID;
if (unlikely (c->current_glyphs.has (gid)))
hb_decycler_node_t node (c->glyphs_decycler);
if (unlikely (!node.visit (gid)))
return;
c->current_glyphs.add (gid);
c->funcs->push_inverse_root_transform (c->data, c->font);
c->ft_font->lock.unlock ();
if (c->funcs->color_glyph (c->data, gid, c->font))
{
c->ft_font->lock.lock ();
c->funcs->pop_transform (c->data);
c->current_glyphs.del (gid);
return;
}
c->ft_font->lock.lock ();
@ -380,8 +376,6 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
if (has_clip_box)
c->funcs->pop_clip (c->data);
c->current_glyphs.del (gid);
}
}
break;
@ -506,7 +500,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
hb_ft_paint_context_t c (ft_font, font,
paint_funcs, paint_data,
palette, palette_index, foreground);
c.current_glyphs.add (gid);
hb_decycler_node_t node (c.glyphs_decycler);
node.visit (gid);
bool is_bounded = true;
FT_ClipBox clip_box;
@ -530,7 +525,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
hb_ft_paint_context_t ce (ft_font, font,
extents_funcs, &extents_data,
palette, palette_index, foreground);
ce.current_glyphs.add (gid);
hb_decycler_node_t node2 (ce.glyphs_decycler);
node2.visit (gid);
ce.funcs->push_root_transform (ce.data, font);
ce.recurse (paint);
ce.funcs->pop_transform (ce.data);

View File

@ -37,7 +37,11 @@
#include "hb-draw.hh"
#include "hb-font.hh"
#include "hb-machinery.hh"
#ifndef HB_NO_AAT
#include "hb-aat-layout-trak-table.hh"
#endif
#include "hb-ot-os2-table.hh"
#include "hb-ot-stat-table.hh"
#include "hb-ot-shaper-arabic-pua.hh"
#include "hb-paint.hh"
@ -502,6 +506,26 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#ifndef HB_NO_AAT
/* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
#ifndef HB_NO_STYLE
bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
#else
bool apply_trak = false;
#endif
if (apply_trak)
{
hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++)
{
*first_advance += tracking;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#endif
}
#ifndef HB_NO_VERTICAL
@ -538,7 +562,20 @@ hb_ft_get_glyph_v_advance (hb_font_t *font,
* have a Y growing upward. Hence the extra negation. */
hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
v = ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
#ifndef HB_NO_AAT
/* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
#ifndef HB_NO_STYLE
bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
#else
bool apply_trak = false;
#endif
if (apply_trak)
v += font->face->table.trak->get_v_tracking (font);
#endif
return v;
}
#endif
@ -1290,7 +1327,7 @@ hb_ft_face_create_cached (FT_Face ft_face)
*
* If you know you have valid reasons not to use hb_ft_font_create_referenced(),
* then it is the client program's responsibility to destroy @ft_face
* after the #hb_font_t font object has been destroyed.
* only after the #hb_font_t font object has been destroyed.
*
* HarfBuzz will use the @destroy callback on the #hb_font_t font object
* if it is supplied when you use this function. However, even if @destroy
@ -1598,6 +1635,11 @@ _release_blob (void *arg)
void
hb_ft_font_set_funcs (hb_font_t *font)
{
// In case of failure...
hb_font_set_funcs (font,
hb_font_funcs_get_empty (),
nullptr, nullptr);
hb_blob_t *blob = hb_face_reference_blob (font->face);
unsigned int blob_length;
const char *blob_data = hb_blob_get_data (blob, &blob_length);

View File

@ -83,6 +83,13 @@ struct hb_transform_t
float x0, float y0) :
xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
bool is_identity () const
{
return xx == 1.f && yx == 0.f &&
xy == 0.f && yy == 1.f &&
x0 == 0.f && y0 == 0.f;
}
void multiply (const hb_transform_t &o)
{
/* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
@ -201,6 +208,8 @@ struct hb_transform_t
float y0 = 0.f;
};
#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}
struct hb_bounds_t
{
enum status_t {

View File

@ -86,21 +86,12 @@ struct IntType
return pb->cmp (*pa);
}
template <typename Type2,
hb_enable_if (std::is_integral<Type2>::value &&
sizeof (Type2) < sizeof (int) &&
sizeof (Type) < sizeof (int))>
int cmp (Type2 a) const
{
Type b = v;
return (int) a - (int) b;
}
template <typename Type2,
hb_enable_if (hb_is_convertible (Type2, Type))>
int cmp (Type2 a) const
{
Type b = v;
return a < b ? -1 : a == b ? 0 : +1;
return (a > b) - (a < b);
}
bool sanitize (hb_sanitize_context_t *c) const
{
@ -299,11 +290,6 @@ typedef Index NameID;
struct VarIdx : HBUINT32 {
static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu;
static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, "");
static uint32_t add (uint32_t i, unsigned short v)
{
if (i == NO_VARIATION) return i;
return i + v;
}
VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; }
};
DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx);

View File

@ -553,15 +553,6 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
return true;
}
bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{
funcs->push_clip_glyph (data, glyph, font);
funcs->color (data, true, foreground);
funcs->pop_clip (data);
return true;
}
bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
{
#ifdef HB_NO_OT_FONT_CFF

View File

@ -1462,7 +1462,6 @@ struct cff1
}
HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
private:

View File

@ -143,15 +143,6 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
return true;
}
bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{
funcs->push_clip_glyph (data, glyph, font);
funcs->color (data, true, foreground);
funcs->pop_clip (data);
return true;
}
struct cff2_path_param_t
{
cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_)

View File

@ -518,7 +518,6 @@ struct cff2
HB_INTERNAL bool get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const;
HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const;
};

View File

@ -36,13 +36,16 @@
#include "hb-ot-face.hh"
#include "hb-outline.hh"
#ifndef HB_NO_AAT
#include "hb-aat-layout-trak-table.hh"
#endif
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
#include "hb-ot-stat-table.hh"
#include "hb-ot-var-varc-table.hh"
#include "hb-ot-vorg-table.hh"
#include "OT/Color/CBDT/CBDT.hh"
@ -73,6 +76,10 @@ struct hb_ot_font_t
{
const hb_ot_face_t *ot_face;
#ifndef HB_NO_AAT
bool apply_trak;
#endif
#ifndef HB_NO_OT_FONT_CMAP_CACHE
hb_ot_font_cmap_cache_t *cmap_cache;
#endif
@ -91,6 +98,15 @@ _hb_ot_font_create (hb_font_t *font)
ot_font->ot_face = &font->face->table;
#ifndef HB_NO_AAT
/* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
#ifndef HB_NO_STYLE
ot_font->apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
#else
ot_font->apply_trak = false;
#endif
#endif
#ifndef HB_NO_OT_FONT_CMAP_CACHE
// retry:
auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face,
@ -200,7 +216,6 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
unsigned advance_stride,
void *user_data HB_UNUSED)
{
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
const hb_ot_face_t *ot_face = ot_font->ot_face;
const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
@ -292,6 +307,20 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#ifndef HB_NO_AAT
if (ot_font->apply_trak)
{
hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++)
{
*first_advance += tracking;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#endif
}
#ifndef HB_NO_VERTICAL
@ -356,6 +385,20 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#ifndef HB_NO_AAT
if (ot_font->apply_trak)
{
hb_position_t tracking = font->face->table.trak->get_v_tracking (font);
first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++)
{
*first_advance += tracking;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#endif
}
#endif
@ -568,14 +611,11 @@ hb_ot_paint_glyph (hb_font_t *font,
if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return;
#endif
#endif
#ifndef HB_NO_VAR_COMPOSITES
if (font->face->table.VARC->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#endif
if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#ifndef HB_NO_CFF
if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#endif
// Outline glyph
paint_funcs->push_clip_glyph (paint_data, glyph, font);
paint_funcs->color (paint_data, true, foreground);
paint_funcs->pop_clip (paint_data);
}
#endif

View File

@ -342,7 +342,7 @@ struct kern
}
bool apply (AAT::hb_aat_apply_context_t *c,
const AAT::kern_accelerator_data_t *accel_data = nullptr) const
const AAT::kern_accelerator_data_t &accel_data) const
{ return dispatch (c, accel_data); }
template <typename context_t, typename ...Ts>
@ -395,7 +395,7 @@ struct kern
bool apply (AAT::hb_aat_apply_context_t *c) const
{
return table->apply (c, &accel_data);
return table->apply (c, accel_data);
}
hb_blob_ptr_t<kern> table;

View File

@ -34,6 +34,7 @@
#include "hb-open-type.hh"
#include "hb-set.hh"
#include "hb-bimap.hh"
#include "hb-cache.hh"
#include "OT/Layout/Common/Coverage.hh"
#include "OT/Layout/types.hh"
@ -2076,6 +2077,15 @@ struct ClassDef
default:return 0;
}
}
unsigned int get_class (hb_codepoint_t glyph_id,
hb_ot_lookup_cache_t *cache) const
{
unsigned klass;
if (cache && cache->get (glyph_id, &klass)) return klass;
klass = get_class (glyph_id);
if (cache) cache->set (glyph_id, klass);
return klass;
}
unsigned get_population () const
{
@ -3731,11 +3741,13 @@ struct ItemVarStoreInstancer
float operator() (uint32_t varIdx, unsigned short offset = 0) const
{
if (!coords || varIdx == VarIdx::NO_VARIATION)
return 0.f;
varIdx += offset;
if (varIdxMap)
varIdx = varIdxMap->map (VarIdx::add (varIdx, offset));
else
varIdx += offset;
return coords ? varStore->get_delta (varIdx, coords, cache) : 0.f;
varIdx = varIdxMap->map (varIdx);
return varStore->get_delta (varIdx, coords, cache);
}
const ItemVariationStore *varStore;
@ -3767,12 +3779,11 @@ struct MultiItemVarStoreInstancer
void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const
{
if (coords)
if (coords && varIdx != VarIdx::NO_VARIATION)
{
varIdx += offset;
if (varIdxMap)
varIdx = varIdxMap->map (VarIdx::add (varIdx, offset));
else
varIdx += offset;
varIdx = varIdxMap->map (varIdx);
varStore->get_delta (varIdx, coords, out, cache);
}
else
@ -3890,8 +3901,8 @@ struct ConditionAxisRange
{
// add axisIndex->value into the hashmap so we can check if the record is
// unique with variations
int16_t int_filter_max_val = filterRangeMaxValue.to_int ();
int16_t int_filter_min_val = filterRangeMinValue.to_int ();
uint16_t int_filter_max_val = (uint16_t) filterRangeMaxValue.to_int ();
uint16_t int_filter_min_val = (uint16_t) filterRangeMinValue.to_int ();
hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val;
condition_map->set (axisIndex, val);

View File

@ -713,6 +713,7 @@ struct hb_ot_apply_context_t :
recurse_func_t recurse_func = nullptr;
const GDEF &gdef;
const GDEF::accelerator_t &gdef_accel;
const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr;
const ItemVariationStore &var_store;
ItemVariationStore::cache_t *var_store_cache;
hb_set_digest_t digest;
@ -762,10 +763,12 @@ struct hb_ot_apply_context_t :
nullptr
#endif
),
digest (buffer_->digest ()),
direction (buffer_->props.direction),
has_glyph_classes (gdef.has_glyph_classes ())
{ init_iters (); }
{
init_iters ();
buffer->collect_codepoints (digest);
}
~hb_ot_apply_context_t ()
{
@ -899,6 +902,13 @@ struct hb_ot_apply_context_t :
}
};
enum class hb_ot_lookup_cache_op_t
{
CREATE,
ENTER,
LEAVE,
DESTROY,
};
struct hb_accelerate_subtables_context_t :
hb_dispatch_context_t<hb_accelerate_subtables_context_t>
@ -923,19 +933,23 @@ struct hb_accelerate_subtables_context_t :
}
template <typename T>
static inline auto cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) )
template <typename T>
static inline bool cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; }
static inline auto cache_func_ (void *p,
hb_ot_lookup_cache_op_t op,
hb_priority<1>) HB_RETURN (void *, T::cache_func (p, op) )
template <typename T=void>
static inline void * cache_func_ (void *p,
hb_ot_lookup_cache_op_t op HB_UNUSED,
hb_priority<0>) { return (void *) false; }
template <typename Type>
static inline bool cache_func_to (const void *obj, hb_ot_apply_context_t *c, bool enter)
static inline void * cache_func_to (void *p,
hb_ot_lookup_cache_op_t op)
{
const Type *typed_obj = (const Type *) obj;
return cache_func_ (typed_obj, c, enter, hb_prioritize);
return cache_func_<Type> (p, op, hb_prioritize);
}
#endif
typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c);
typedef bool (*hb_cache_func_t) (const void *obj, hb_ot_apply_context_t *c, bool enter);
typedef void * (*hb_cache_func_t) (void *p, hb_ot_lookup_cache_op_t op);
struct hb_applicable_t
{
@ -972,11 +986,11 @@ struct hb_accelerate_subtables_context_t :
}
bool cache_enter (hb_ot_apply_context_t *c) const
{
return cache_func (obj, c, true);
return (bool) cache_func (c, hb_ot_lookup_cache_op_t::ENTER);
}
void cache_leave (hb_ot_apply_context_t *c) const
{
cache_func (obj, c, false);
cache_func (c, hb_ot_lookup_cache_op_t::LEAVE);
}
#endif
@ -2623,25 +2637,35 @@ struct ContextFormat2_5
unsigned c = (this+classDef).cost () * ruleSet.len;
return c >= 4 ? c : 0;
}
bool cache_func (hb_ot_apply_context_t *c, bool enter) const
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
if (enter)
switch (op)
{
if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
return false;
auto &info = c->buffer->info;
unsigned count = c->buffer->len;
for (unsigned i = 0; i < count; i++)
info[i].syllable() = 255;
c->new_syllables = 255;
return true;
}
else
{
c->new_syllables = (unsigned) -1;
HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
return true;
case hb_ot_lookup_cache_op_t::CREATE:
return (void *) true;
case hb_ot_lookup_cache_op_t::ENTER:
{
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
return (void *) false;
auto &info = c->buffer->info;
unsigned count = c->buffer->len;
for (unsigned i = 0; i < count; i++)
info[i].syllable() = 255;
c->new_syllables = 255;
return (void *) true;
}
case hb_ot_lookup_cache_op_t::LEAVE:
{
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
c->new_syllables = (unsigned) -1;
HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
return nullptr;
}
case hb_ot_lookup_cache_op_t::DESTROY:
return nullptr;
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@ -2650,7 +2674,7 @@ struct ContextFormat2_5
{
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
const ClassDef &class_def = this+classDef;
@ -2836,7 +2860,7 @@ struct ContextFormat3
{
TRACE_APPLY (this);
unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
struct ContextApplyLookupContext lookup_context = {
@ -3650,7 +3674,7 @@ struct ChainContextFormat1_4
{
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
const ChainRuleSet &rule_set = this+ruleSet[index];
struct ChainContextApplyLookupContext lookup_context = {
@ -3861,28 +3885,37 @@ struct ChainContextFormat2_5
unsigned cache_cost () const
{
unsigned c = (this+lookaheadClassDef).cost () * ruleSet.len;
return c >= 4 ? c : 0;
return (this+lookaheadClassDef).cost () * ruleSet.len;
}
bool cache_func (hb_ot_apply_context_t *c, bool enter) const
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
if (enter)
switch (op)
{
if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
return false;
auto &info = c->buffer->info;
unsigned count = c->buffer->len;
for (unsigned i = 0; i < count; i++)
info[i].syllable() = 255;
c->new_syllables = 255;
return true;
}
else
{
c->new_syllables = (unsigned) -1;
HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
return true;
case hb_ot_lookup_cache_op_t::CREATE:
return (void *) true;
case hb_ot_lookup_cache_op_t::ENTER:
{
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
return (void *) false;
auto &info = c->buffer->info;
unsigned count = c->buffer->len;
for (unsigned i = 0; i < count; i++)
info[i].syllable() = 255;
c->new_syllables = 255;
return (void *) true;
}
case hb_ot_lookup_cache_op_t::LEAVE:
{
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
c->new_syllables = (unsigned) -1;
HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
return nullptr;
}
case hb_ot_lookup_cache_op_t::DESTROY:
return nullptr;
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@ -3891,7 +3924,7 @@ struct ChainContextFormat2_5
{
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
const ClassDef &backtrack_class_def = this+backtrackClassDef;
const ClassDef &input_class_def = this+inputClassDef;
@ -4137,7 +4170,7 @@ struct ChainContextFormat3
const auto &input = StructAfter<decltype (inputX)> (backtrack);
unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (index == NOT_COVERED) return_trace (false);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
@ -4408,7 +4441,18 @@ struct hb_ot_layout_lookup_accelerator_t
thiz->digest.union_ (subtable.digest);
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (c_accelerate_subtables.cache_user_cost < 4)
c_accelerate_subtables.cache_user_idx = (unsigned) -1;
thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx;
if (thiz->cache_user_idx != (unsigned) -1)
{
thiz->cache = thiz->subtables[thiz->cache_user_idx].cache_func (nullptr, hb_ot_lookup_cache_op_t::CREATE);
if (!thiz->cache)
thiz->cache_user_idx = (unsigned) -1;
}
for (unsigned i = 0; i < count; i++)
if (i != thiz->cache_user_idx)
thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func;
@ -4417,6 +4461,17 @@ struct hb_ot_layout_lookup_accelerator_t
return thiz;
}
void fini ()
{
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (cache)
{
assert (cache_user_idx != (unsigned) -1);
subtables[cache_user_idx].cache_func (cache, hb_ot_lookup_cache_op_t::DESTROY);
}
#endif
}
bool may_have (hb_codepoint_t g) const
{ return digest.may_have (g); }
@ -4425,6 +4480,7 @@ struct hb_ot_layout_lookup_accelerator_t
#endif
bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const
{
c->lookup_accel = this;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (use_cache)
{
@ -4464,10 +4520,13 @@ struct hb_ot_layout_lookup_accelerator_t
hb_set_digest_t digest;
private:
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
public:
void *cache = nullptr;
private:
unsigned cache_user_idx = (unsigned) -1;
#endif
private:
hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
};
@ -4852,7 +4911,12 @@ struct GSUBGPOS
~accelerator_t ()
{
for (unsigned int i = 0; i < this->lookup_count; i++)
hb_free (this->accels[i]);
{
auto *accel = this->accels[i].get_relaxed ();
if (accel)
accel->fini ();
hb_free (accel);
}
hb_free (this->accels);
this->table.destroy ();
}
@ -4873,6 +4937,7 @@ struct GSUBGPOS
if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel)))
{
accel->fini ();
hb_free (accel);
goto retry;
}

View File

@ -1923,9 +1923,10 @@ apply_forward (OT::hb_ot_apply_context_t *c,
while (buffer->idx < buffer->len && buffer->successful)
{
bool applied = false;
if (accel.digest.may_have (buffer->cur().codepoint) &&
(buffer->cur().mask & c->lookup_mask) &&
c->check_glyph_property (&buffer->cur(), c->lookup_props))
auto &cur = buffer->cur();
if (accel.digest.may_have (cur.codepoint) &&
(cur.mask & c->lookup_mask) &&
c->check_glyph_property (&cur, c->lookup_props))
{
applied = accel.apply (c, subtable_count, use_cache);
}
@ -1951,9 +1952,10 @@ apply_backward (OT::hb_ot_apply_context_t *c,
hb_buffer_t *buffer = c->buffer;
do
{
if (accel.digest.may_have (buffer->cur().codepoint) &&
(buffer->cur().mask & c->lookup_mask) &&
c->check_glyph_property (&buffer->cur(), c->lookup_props))
auto &cur = buffer->cur();
if (accel.digest.may_have (cur.codepoint) &&
(cur.mask & c->lookup_mask) &&
c->check_glyph_property (&cur, c->lookup_props))
ret |= accel.apply (c, subtable_count, false);
/* The reverse lookup doesn't "advance" cursor (for good reason). */
@ -2033,7 +2035,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
* (plus some past glyphs).
*
* Only try applying the lookup if there is any overlap. */
if (accel->digest.may_have (c.digest))
if (accel->digest.may_intersect (c.digest))
{
c.set_lookup_index (lookup_index);
c.set_lookup_mask (lookup.mask, false);
@ -2059,7 +2061,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
if (stage->pause_func (plan, font, buffer))
{
/* Refresh working buffer digest since buffer changed. */
c.digest = buffer->digest ();
buffer->collect_codepoints (c.digest);
}
}
}

View File

@ -339,6 +339,11 @@ _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
!_hb_glyph_info_substituted (info);
}
static inline void
_hb_glyph_info_set_default_ignorable (hb_glyph_info_t *info)
{
info->unicode_props() |= UPROPS_MASK_IGNORABLE;
}
static inline void
_hb_glyph_info_clear_default_ignorable (hb_glyph_info_t *info)
{
info->unicode_props() &= ~ UPROPS_MASK_IGNORABLE;
@ -360,7 +365,7 @@ _hb_glyph_info_set_continuation (hb_glyph_info_t *info)
info->unicode_props() |= UPROPS_MASK_CONTINUATION;
}
static inline void
_hb_glyph_info_reset_continuation (hb_glyph_info_t *info)
_hb_glyph_info_clear_continuation (hb_glyph_info_t *info)
{
info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION;
}
@ -633,8 +638,7 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer)
}
/* Make sure no one directly touches our props... */
#undef unicode_props0
#undef unicode_props1
#undef unicode_props
#undef lig_props
#undef glyph_props

View File

@ -390,5 +390,19 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m,
}
}
unsigned int hb_ot_map_t::get_feature_tags (unsigned int start_offset, unsigned int *tag_count, hb_tag_t *tags) const
{
if (tag_count)
{
auto sub_features = features.as_array ().sub_array (start_offset, tag_count);
if (tags)
{
for (unsigned int i = 0; i < sub_features.length; i++)
tags[i] = sub_features[i].tag;
}
}
return features.length;
}
#endif

View File

@ -166,6 +166,9 @@ struct hb_ot_map_t
const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
HB_INTERNAL unsigned int get_feature_tags (unsigned int start_offset,
unsigned int *tag_count, /* IN/OUT */
hb_tag_t *tags /* OUT */) const;
public:
hb_tag_t chosen_script[2];

View File

@ -46,6 +46,8 @@
#include "hb-set.hh"
#include "hb-aat-layout.hh"
#include "hb-ot-stat-table.hh"
static inline bool
_hb_codepoint_is_regional_indicator (hb_codepoint_t u)
@ -121,10 +123,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
plan.kern_mask = plan.map.get_mask (kern_tag);
plan.requested_kerning = !!plan.kern_mask;
#endif
#ifndef HB_NO_AAT_SHAPE
plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k'));
plan.requested_tracking = !!plan.trak_mask;
#endif
bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
bool disable_gpos = plan.shaper->gpos_tag &&
@ -207,9 +205,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
https://github.com/harfbuzz/harfbuzz/issues/2967. */
if (plan.apply_morx)
plan.adjust_mark_positioning_when_zeroing = false;
/* Currently we always apply trak. */
plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
#endif
}
@ -274,11 +269,6 @@ hb_ot_shape_plan_t::position (hb_font_t *font,
#endif
else if (this->apply_fallback_kern)
_hb_ot_shape_fallback_kern (this, font, buffer);
#ifndef HB_NO_AAT_SHAPE
if (this->apply_trak)
hb_aat_layout_track (this, font, buffer);
#endif
}
@ -346,13 +336,6 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
/* Random! */
map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
#ifndef HB_NO_AAT_SHAPE
/* Tracking. We enable dummy feature here just to allow disabling
* AAT 'trak' table using features.
* https://github.com/harfbuzz/harfbuzz/issues/1303 */
map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK);
#endif
map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */
map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
@ -1277,6 +1260,36 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
}
/**
* hb_ot_shape_plan_get_feature_tags:
* @shape_plan: A shaping plan
* @start_offset: The index of first feature to retrieve
* @tag_count: (inout): Input = the maximum number of features to return;
* Output = the actual number of features returned (may be zero)
* @tags: (out) (array length=tag_count): The array of enabled feature
*
* Fetches the list of OpenType feature tags enabled for a shaping plan, if possible.
*
* Return value: Total number of feature tagss.
*
* Since: 10.3.0
*/
unsigned int
hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
unsigned int start_offset,
unsigned int *tag_count, /* IN/OUT */
hb_tag_t *tags /* OUT */)
{
#ifndef HB_NO_OT_SHAPE
return shape_plan->ot.map.get_feature_tags (start_offset, tag_count, tags);
#else
if (tag_count)
*tag_count = 0;
return 0;
#endif
}
/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
static void
add_char (hb_font_t *font,

View File

@ -48,6 +48,12 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
hb_tag_t table_tag,
hb_set_t *lookup_indexes /* OUT */);
HB_EXTERN unsigned int
hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
unsigned int start_offset,
unsigned int *tag_count, /* IN/OUT */
hb_tag_t *tags /* OUT */);
HB_END_DECLS
#endif /* HB_OT_SHAPE_H */

View File

@ -51,7 +51,8 @@ struct hb_ot_shape_plan_key_t
bool equal (const hb_ot_shape_plan_key_t *other)
{
return 0 == hb_memcmp (this, other, sizeof (*this));
return variations_index[0] == other->variations_index[0] &&
variations_index[1] == other->variations_index[1];
}
};
@ -79,22 +80,12 @@ struct hb_ot_shape_plan_t
#else
static constexpr hb_mask_t kern_mask = 0;
#endif
#ifndef HB_NO_AAT_SHAPE
hb_mask_t trak_mask;
#else
static constexpr hb_mask_t trak_mask = 0;
#endif
#ifndef HB_NO_OT_KERN
bool requested_kerning : 1;
#else
static constexpr bool requested_kerning = false;
#endif
#ifndef HB_NO_AAT_SHAPE
bool requested_tracking : 1;
#else
static constexpr bool requested_tracking = false;
#endif
#ifndef HB_NO_OT_SHAPE_FRACTIONS
bool has_frac : 1;
#else
@ -117,11 +108,9 @@ struct hb_ot_shape_plan_t
#ifndef HB_NO_AAT_SHAPE
bool apply_kerx : 1;
bool apply_morx : 1;
bool apply_trak : 1;
#else
static constexpr bool apply_kerx = false;
static constexpr bool apply_morx = false;
static constexpr bool apply_trak = false;
#endif
void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const

View File

@ -355,6 +355,8 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
if (fallback_plan->lookup_array[i])
{
if (fallback_plan->accel_array[i])
fallback_plan->accel_array[i]->fini ();
hb_free (fallback_plan->accel_array[i]);
if (fallback_plan->free_lookups)
hb_free (fallback_plan->lookup_array[i]);

View File

@ -24,7 +24,7 @@ static void
_output_dotted_circle (hb_buffer_t *buffer)
{
(void) buffer->output_glyph (0x25CCu);
_hb_glyph_info_reset_continuation (&buffer->prev());
_hb_glyph_info_clear_continuation (&buffer->prev());
}
static void

View File

@ -6,8 +6,8 @@
*
* on files with these headers:
*
* <meta name="updated_at" content="2024-12-05 07:13 PM" />
* File-Date: 2024-11-19
* <meta name="updated_at" content="2024-12-06 06:35 AM" />
* File-Date: 2025-01-21
*/
#ifndef HB_OT_TAG_TABLE_HH
@ -745,6 +745,7 @@ static const LangTag ot_languages3[] = {
/*{HB_TAG('h','n','d',' '), HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */
{HB_TAG('h','n','e',' '), HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */
{HB_TAG('h','n','j',' '), HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */
{HB_TAG('h','n','m',' '), HB_TAG('Z','H','S',' ')}, /* Hainanese -> Chinese, Simplified */
{HB_TAG('h','n','o',' '), HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */
{HB_TAG('h','o','c',' '), HB_TAG('H','O',' ',' ')}, /* Ho */
{HB_TAG('h','o','i',' '), HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */
@ -981,9 +982,11 @@ static const LangTag ot_languages3[] = {
{HB_TAG('l','t','o',' '), HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */
{HB_TAG('l','t','s',' '), HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */
/*{HB_TAG('l','u','a',' '), HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */
{HB_TAG('l','u','h',' '), HB_TAG('Z','H','S',' ')}, /* Leizhou Chinese -> Chinese, Simplified */
/*{HB_TAG('l','u','o',' '), HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */
{HB_TAG('l','u','s',' '), HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */
{HB_TAG('l','u','s',' '), HB_TAG('Q','I','N',' ')}, /* Lushai -> Chin */
/*{HB_TAG('l','u','t',' '), HB_TAG('L','U','T',' ')},*/ /* Lushootseed */
{HB_TAG('l','u','y',' '), HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */
{HB_TAG('l','u','z',' '), HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */
{HB_TAG('l','v','i',' '), HB_TAG_NONE }, /* Lavi != Latvian */
@ -1404,6 +1407,7 @@ static const LangTag ot_languages3[] = {
{HB_TAG('s','i','g',' '), HB_TAG_NONE }, /* Paasaal != Silte Gurage */
{HB_TAG('s','i','z',' '), HB_TAG('B','B','R',' ')}, /* Siwi -> Berber */
/*{HB_TAG('s','j','a',' '), HB_TAG('S','J','A',' ')},*/ /* Epena */
{HB_TAG('s','j','c',' '), HB_TAG('Z','H','S',' ')}, /* Shaojiang Chinese -> Chinese, Simplified */
{HB_TAG('s','j','d',' '), HB_TAG('K','S','M',' ')}, /* Kildin Sami */
/*{HB_TAG('s','j','e',' '), HB_TAG('S','J','E',' ')},*/ /* Pite Sami */
{HB_TAG('s','j','o',' '), HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */
@ -2386,6 +2390,26 @@ out:
*count = i;
return true;
}
if (lang_matches (&lang_str[1], limit, "nm-hant-hk", 10))
{
/* Hainanese; Han (Traditional variant); Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "nm-hant-mo", 10))
{
/* Hainanese; Han (Traditional variant); Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10))
{
/* Xiang Chinese; Han (Traditional variant); Hong Kong */
@ -2420,6 +2444,20 @@ out:
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "nm-hans", 7))
{
/* Hainanese; Han (Simplified variant) */
tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "nm-hant", 7))
{
/* Hainanese; Han (Traditional variant) */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "sn-hans", 7))
{
/* Xiang Chinese; Han (Simplified variant) */
@ -2464,6 +2502,36 @@ out:
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "nm-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3))
{
/* Hainanese; Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "nm-", 3)
&& subtag_matches (lang_str, limit, "-mo", 3))
{
/* Hainanese; Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (0 == strncmp (&lang_str[1], "nm-", 3)
&& subtag_matches (lang_str, limit, "-tw", 3))
{
/* Hainanese; Taiwan, Province of China */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "sn-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3))
{
@ -2525,6 +2593,40 @@ out:
}
break;
case 'l':
if (lang_matches (&lang_str[1], limit, "uh-hant-hk", 10))
{
/* Leizhou Chinese; Han (Traditional variant); Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "uh-hant-mo", 10))
{
/* Leizhou Chinese; Han (Traditional variant); Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (lang_matches (&lang_str[1], limit, "uh-hans", 7))
{
/* Leizhou Chinese; Han (Simplified variant) */
tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "uh-hant", 7))
{
/* Leizhou Chinese; Han (Traditional variant) */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
{
/* Literary Chinese; Han (Simplified variant) */
@ -2532,6 +2634,36 @@ out:
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "uh-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3))
{
/* Leizhou Chinese; Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "uh-", 3)
&& subtag_matches (lang_str, limit, "-mo", 3))
{
/* Leizhou Chinese; Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (0 == strncmp (&lang_str[1], "uh-", 3)
&& subtag_matches (lang_str, limit, "-tw", 3))
{
/* Leizhou Chinese; Taiwan, Province of China */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
break;
case 'm':
if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
@ -2703,6 +2835,72 @@ out:
return true;
}
break;
case 's':
if (lang_matches (&lang_str[1], limit, "jc-hant-hk", 10))
{
/* Shaojiang Chinese; Han (Traditional variant); Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "jc-hant-mo", 10))
{
/* Shaojiang Chinese; Han (Traditional variant); Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (lang_matches (&lang_str[1], limit, "jc-hans", 7))
{
/* Shaojiang Chinese; Han (Simplified variant) */
tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "jc-hant", 7))
{
/* Shaojiang Chinese; Han (Traditional variant) */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "jc-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3))
{
/* Shaojiang Chinese; Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "jc-", 3)
&& subtag_matches (lang_str, limit, "-mo", 3))
{
/* Shaojiang Chinese; Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (0 == strncmp (&lang_str[1], "jc-", 3)
&& subtag_matches (lang_str, limit, "-tw", 3))
{
/* Shaojiang Chinese; Taiwan, Province of China */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
break;
case 'w':
if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10))
{

View File

@ -72,7 +72,7 @@ struct glyph_variations_t
const hb_subset_plan_t *plan,
const hb_hashmap_t<hb_codepoint_t, hb_bytes_t>& new_gid_var_data_map)
{
if (unlikely (!glyph_variations.alloc (plan->new_to_old_gid_list.length, true)))
if (unlikely (!glyph_variations.alloc_exact (plan->new_to_old_gid_list.length)))
return false;
auto it = hb_iter (plan->new_to_old_gid_list);

View File

@ -78,11 +78,11 @@ struct hb_serialize_context_t
head = o.head;
tail = o.tail;
next = nullptr;
real_links.alloc (o.num_real_links, true);
real_links.alloc_exact (o.num_real_links);
for (unsigned i = 0 ; i < o.num_real_links; i++)
real_links.push (o.real_links[i]);
virtual_links.alloc (o.num_virtual_links, true);
virtual_links.alloc_exact (o.num_virtual_links);
for (unsigned i = 0; i < o.num_virtual_links; i++)
virtual_links.push (o.virtual_links[i]);
}
@ -172,7 +172,7 @@ struct hb_serialize_context_t
auto all_links () const HB_AUTO_RETURN
(( hb_concat (real_links, virtual_links) ));
auto all_links_writer () HB_AUTO_RETURN
(( hb_concat (real_links.writer (), virtual_links.writer ()) ));
(( hb_concat (real_links.writer (), virtual_links.writer ()) ));
};
struct snapshot_t

View File

@ -56,7 +56,7 @@
* - For each glyph, if it doesn't match the subtable digest,
* skip it.
*
* The main filter we use is a combination of three bits-pattern
* The main filter we use is a combination of four bits-pattern
* filters. A bits-pattern filter checks a number of bits (5 or 6)
* of the input number (glyph-id in this case) and checks whether
* its pattern is amongst the patterns of any of the accepted values.
@ -64,45 +64,60 @@
* check is done using four bitwise operations only.
*/
template <typename mask_t, unsigned int shift>
struct hb_set_digest_bits_pattern_t
static constexpr unsigned hb_set_digest_shifts[] = {4, 0, 6};
struct hb_set_digest_t
{
// No science in these. Intuition and testing only.
using mask_t = uint64_t;
static constexpr unsigned n = ARRAY_LENGTH_CONST (hb_set_digest_shifts);
static constexpr unsigned mask_bytes = sizeof (mask_t);
static constexpr unsigned mask_bits = sizeof (mask_t) * 8;
static constexpr unsigned num_bits = 0
+ (mask_bytes >= 1 ? 3 : 0)
+ (mask_bytes >= 2 ? 1 : 0)
+ (mask_bytes >= 4 ? 1 : 0)
+ (mask_bytes >= 8 ? 1 : 0)
+ (mask_bytes >= 16? 1 : 0)
+ 0;
static constexpr hb_codepoint_t mb1 = mask_bits - 1;
static constexpr mask_t one = 1;
static constexpr mask_t all = (mask_t) -1;
static_assert ((shift < sizeof (hb_codepoint_t) * 8), "");
static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), "");
void init ()
{ for (unsigned i = 0; i < n; i++) masks[i] = 0; }
void init () { mask = 0; }
void clear () { init (); }
static hb_set_digest_bits_pattern_t full () { hb_set_digest_bits_pattern_t d; d.mask = (mask_t) -1; return d; }
static hb_set_digest_t full ()
{
hb_set_digest_t d;
for (unsigned i = 0; i < n; i++) d.masks[i] = all;
return d;
}
void union_ (const hb_set_digest_bits_pattern_t &o) { mask |= o.mask; }
void add (hb_codepoint_t g) { mask |= mask_for (g); }
void union_ (const hb_set_digest_t &o)
{ for (unsigned i = 0; i < n; i++) masks[i] |= o.masks[i]; }
bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{
if (mask == (mask_t) -1) return false;
if ((b >> shift) - (a >> shift) >= mask_bits - 1)
bool ret;
ret = false;
for (unsigned i = 0; i < n; i++)
if (masks[i] != all)
ret = true;
if (!ret) return false;
ret = false;
for (unsigned i = 0; i < n; i++)
{
mask = (mask_t) -1;
return false;
}
else
{
mask_t ma = mask_for (a);
mask_t mb = mask_for (b);
mask |= mb + (mb - ma) - (mb < ma);
return true;
mask_t shift = hb_set_digest_shifts[i];
if ((b >> shift) - (a >> shift) >= mb1)
masks[i] = all;
else
{
mask_t ma = one << ((a >> shift) & mb1);
mask_t mb = one << ((b >> shift) & mb1);
masks[i] |= mb + (mb - ma) - (mb < ma);
ret = true;
}
}
return ret;
}
template <typename T>
@ -125,103 +140,37 @@ struct hb_set_digest_bits_pattern_t
template <typename T>
bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
bool may_have (const hb_set_digest_bits_pattern_t &o) const
{ return mask & o.mask; }
bool may_have (hb_codepoint_t g) const
{ return mask & mask_for (g); }
bool operator [] (hb_codepoint_t g) const
{ return may_have (g); }
private:
static mask_t mask_for (hb_codepoint_t g)
{ return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); }
mask_t mask = 0;
};
template <typename head_t, typename tail_t>
struct hb_set_digest_combiner_t
{
void init ()
{
head.init ();
tail.init ();
}
static hb_set_digest_combiner_t full () { hb_set_digest_combiner_t d; d.head = head_t::full(); d.tail = tail_t::full (); return d; }
void union_ (const hb_set_digest_combiner_t &o)
{
head.union_ (o.head);
tail.union_(o.tail);
}
void add (hb_codepoint_t g)
{
head.add (g);
tail.add (g);
}
bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{
return (int) head.add_range (a, b) | (int) tail.add_range (a, b);
}
template <typename T>
void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{
head.add_array (array, count, stride);
tail.add_array (array, count, stride);
}
template <typename T>
void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); }
template <typename T>
bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{
return head.add_sorted_array (array, count, stride) &&
tail.add_sorted_array (array, count, stride);
}
template <typename T>
bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
bool may_have (const hb_set_digest_combiner_t &o) const
{
return head.may_have (o.head) && tail.may_have (o.tail);
for (unsigned i = 0; i < n; i++)
masks[i] |= one << ((g >> hb_set_digest_shifts[i]) & mb1);
}
HB_ALWAYS_INLINE
bool may_have (hb_codepoint_t g) const
{
return head.may_have (g) && tail.may_have (g);
for (unsigned i = 0; i < n; i++)
if (!(masks[i] & (one << ((g >> hb_set_digest_shifts[i]) & mb1))))
return false;
return true;
}
bool operator [] (hb_codepoint_t g) const
{ return may_have (g); }
bool may_intersect (const hb_set_digest_t &o) const
{
for (unsigned i = 0; i < n; i++)
if (!(masks[i] & o.masks[i]))
return false;
return true;
}
private:
head_t head;
tail_t tail;
mask_t masks[n] = {};
};
/*
* hb_set_digest_t
*
* This is a combination of digests that performs "best".
* There is not much science to this: it's a result of intuition
* and testing.
*/
using hb_set_digest_t =
hb_set_digest_combiner_t
<
hb_set_digest_bits_pattern_t<unsigned long, 4>,
hb_set_digest_combiner_t
<
hb_set_digest_bits_pattern_t<unsigned long, 0>,
hb_set_digest_bits_pattern_t<unsigned long, 9>
>
>
;
#endif /* HB_SET_DIGEST_HH */

View File

@ -106,6 +106,7 @@ struct hb_sparseset_t
void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); }
bool get (hb_codepoint_t g) const { return s.get (g); }
bool may_have (hb_codepoint_t g) const { return get (g); }
/* Has interface. */
bool operator [] (hb_codepoint_t k) const { return get (k); }
@ -120,6 +121,9 @@ struct hb_sparseset_t
hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool may_intersect (const hb_sparseset_t &other) const
{ return s.may_intersect (other.s); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{ return s.intersects (first, last); }

View File

@ -209,7 +209,7 @@ hb_shape_plan_create (hb_face_t *face,
* @num_coords: The number of variation-space coordinates
* @shaper_list: (array zero-terminated=1): List of shapers to try
*
* The variable-font version of #hb_shape_plan_create.
* The variable-font version of #hb_shape_plan_create.
* Constructs a shaping plan for a combination of @face, @user_features, @props,
* and @shaper_list, plus the variation-space coordinates @coords.
*
@ -233,7 +233,7 @@ hb_shape_plan_create2 (hb_face_t *face,
num_coords,
shaper_list);
if (unlikely (props->direction == HB_DIRECTION_INVALID))
if (unlikely (!HB_DIRECTION_IS_VALID (props->direction)))
return hb_shape_plan_get_empty ();
hb_shape_plan_t *shape_plan;
@ -331,7 +331,7 @@ hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
* @destroy: (nullable): A callback to call when @data is not needed anymore
* @replace: Whether to replace an existing data with the same key
*
* Attaches a user-data key/data pair to the given shaping plan.
* Attaches a user-data key/data pair to the given shaping plan.
*
* Return value: `true` if success, `false` otherwise.
*
@ -352,7 +352,7 @@ hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan,
* @shape_plan: A shaping plan
* @key: The user-data key to query
*
* Fetches the user data associated with the specified key,
* Fetches the user data associated with the specified key,
* attached to the specified shaping plan.
*
* Return value: (transfer none): A pointer to the user data
@ -501,7 +501,7 @@ hb_shape_plan_create_cached (hb_face_t *face,
* @num_coords: The number of variation-space coordinates
* @shaper_list: (array zero-terminated=1): List of shapers to try
*
* The variable-font version of #hb_shape_plan_create_cached.
* The variable-font version of #hb_shape_plan_create_cached.
* Creates a cached shaping plan suitable for reuse, for a combination
* of @face, @user_features, @props, and @shaper_list, plus the
* variation-space coordinates @coords.

View File

@ -1128,7 +1128,7 @@ struct subr_subsetter_t
if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr)
size += 3;
}
if (!buff.alloc (buff.length + size, true))
if (!buff.alloc_exact (buff.length + size))
return false;
for (auto &opstr : str.values)

View File

@ -45,7 +45,7 @@ struct remap_sid_t
void alloc (unsigned size)
{
map.alloc (size);
vector.alloc (size, true);
vector.alloc_exact (size);
}
bool in_error () const

View File

@ -296,7 +296,7 @@ _try_subset (const TableType *table,
HB_UNTAG (c->table_tag), buf_size);
if (unlikely (buf_size > c->source_blob->length * 256 ||
!buf->alloc (buf_size, true)))
!buf->alloc_exact (buf_size)))
{
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.",
HB_UNTAG (c->table_tag), buf_size);

View File

@ -62,19 +62,19 @@ struct hb_vector_t
}
hb_vector_t (const hb_vector_t &o) : hb_vector_t ()
{
alloc (o.length, true);
alloc_exact (o.length);
if (unlikely (in_error ())) return;
copy_array (o.as_array ());
}
hb_vector_t (array_t o) : hb_vector_t ()
{
alloc (o.length, true);
alloc_exact (o.length);
if (unlikely (in_error ())) return;
copy_array (o);
}
hb_vector_t (c_array_t o) : hb_vector_t ()
{
alloc (o.length, true);
alloc_exact (o.length);
if (unlikely (in_error ())) return;
copy_array (o);
}
@ -132,7 +132,7 @@ struct hb_vector_t
hb_vector_t& operator = (const hb_vector_t &o)
{
reset ();
alloc (o.length, true);
alloc_exact (o.length);
if (unlikely (in_error ())) return *this;
copy_array (o.as_array ());
@ -432,6 +432,10 @@ struct hb_vector_t
return true;
}
bool alloc_exact (unsigned int size)
{
return alloc (size, true);
}
bool resize (int size_, bool initialize = true, bool exact = false)
{
@ -497,7 +501,7 @@ struct hb_vector_t
shrink_vector (size);
if (shrink_memory)
alloc (size, true); /* To force shrinking memory if needed. */
alloc_exact (size); /* To force shrinking memory if needed. */
}

View File

@ -47,7 +47,7 @@ HB_BEGIN_DECLS
*
* The minor component of the library version available at compile-time.
*/
#define HB_VERSION_MINOR 2
#define HB_VERSION_MINOR 3
/**
* HB_VERSION_MICRO:
*
@ -60,7 +60,7 @@ HB_BEGIN_DECLS
*
* A string literal containing the library version available at compile-time.
*/
#define HB_VERSION_STRING "10.2.0"
#define HB_VERSION_STRING "10.3.0"
/**
* HB_VERSION_ATLEAST:

View File

@ -131,6 +131,7 @@
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wcast-function-type-strict" // https://github.com/harfbuzz/harfbuzz/pull/3859#issuecomment-1295409126
#pragma GCC diagnostic ignored "-Wdangling-reference" // https://github.com/harfbuzz/harfbuzz/issues/4043
#pragma GCC diagnostic ignored "-Wdangling-pointer" // Trigerred by hb_decycler_node_t().
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#pragma GCC diagnostic ignored "-Wformat-zero-length"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
@ -281,7 +282,9 @@ extern "C" void hb_free_impl(void *ptr);
#define __attribute__(x)
#endif
#if defined(__GNUC__) && (__GNUC__ >= 3)
#if defined(__MINGW32__) && (__GNUC__ >= 3)
#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (gnu_printf, format_idx, arg_idx)))
#elif defined(__GNUC__) && (__GNUC__ >= 3)
#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx)))
#else
#define HB_PRINTF_FUNC(format_idx, arg_idx)