8348597: Update HarfBuzz to 10.4.0

Reviewed-by: erikj, prr
This commit is contained in:
Harshitha Onkar 2025-03-10 17:47:54 +00:00
parent 7999091e3e
commit dbdbbd4730
193 changed files with 15663 additions and 8640 deletions

View File

@ -331,7 +331,7 @@ else
HARFBUZZ_DISABLED_WARNINGS_gcc := missing-field-initializers \
strict-aliasing unused-result array-bounds parentheses \
unused-variable
unused-variable dangling-pointer
# noexcept-type required for GCC 7 builds. Not required for GCC 8+.
# expansion-to-defined required for GCC 9 builds. Not required for GCC 10+.
# maybe-uninitialized required for GCC 8 builds. Not required for GCC 9+.

View File

@ -1,4 +1,4 @@
## Harfbuzz v8.2.2
## Harfbuzz v10.4.0
### Harfbuzz License
@ -8,14 +8,14 @@ HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
For parts of HarfBuzz that are licensed under different licenses see individual
files names COPYING in subdirectories where applicable.
Copyright © 2010-2023 Google, Inc.
Copyright © 2010-2024 Google, Inc.
Copyright © 2018-2020 Ebrahim Byagowi
Copyright © 2004-2013 Red Hat, Inc.
Copyright © 2019 Facebook, Inc.
Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com)
Copyright © 2007 Chris Wilson
Copyright © 2018-2019 Adobe Inc.
Copyright © 2006-2023 Behdad Esfahbod
Copyright © 2006-2025 Behdad Esfahbod
Copyright © 1998-2004 David Turner and Werner Lemberg
Copyright © 2009 Keith Stribley
Copyright © 2018 Khaled Hosny
@ -54,7 +54,7 @@ exception is licensed with a slightly different MIT variant:
The contents of this directory are licensed under the following terms:
---------------------------------
The below license applies to the following files:
The below applies to the following file(s):
libharfbuzz/hb-ucd.cc
Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net>
@ -72,13 +72,14 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------
The below license applies to the following files:
The below applies to the following file(s):
libharfbuzz/hb-unicode-emoji-table.hh
© 2023 Unicode®, Inc.
© 2024 Unicode®, Inc.
Unicode and the Unicode Logo are registered trademarks of Unicode, Inc.
in the U.S. and other countries.
For terms of use, see https://www.unicode.org/terms_of_use.html
For terms of use and license, see https://www.unicode.org/terms_of_use.html
</pre>

View File

@ -204,6 +204,7 @@ struct IndexSubtable
{
TRACE_SANITIZE (this);
if (!u.header.sanitize (c)) return_trace (false);
hb_barrier ();
switch (u.header.indexFormat)
{
case 1: return_trace (u.format1.sanitize (c, glyph_count));
@ -378,6 +379,7 @@ struct IndexSubtableRecord
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
firstGlyphIndex <= lastGlyphIndex &&
offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1));
}
@ -635,6 +637,7 @@ struct BitmapSizeTable
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
horizontal.sanitize (c) &&
vertical.sanitize (c));
@ -738,7 +741,9 @@ struct CBLC
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
likely (version.major == 2 || version.major == 3) &&
hb_barrier () &&
sizeTables.sanitize (c, this));
}
@ -936,10 +941,12 @@ struct CBDT
}
}
bool has_data () const { return cbdt.get_length (); }
bool has_data () const { return cbdt->version.major; }
bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
{
if (!has_data ()) return false;
hb_glyph_extents_t extents;
hb_glyph_extents_t pixel_extents;
hb_blob_t *blob = reference_png (font, glyph);
@ -975,6 +982,7 @@ struct CBDT
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
likely (version.major == 2 || version.major == 3));
}

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,13 +69,13 @@ 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;
VarStoreInstancer &instancer;
hb_map_t current_glyphs;
hb_map_t current_layers;
ItemVarStoreInstancer &instancer;
hb_decycler_t glyphs_decycler;
hb_decycler_t layers_decycler;
int depth_left = HB_MAX_NESTING_LEVEL;
int edge_count = HB_COLRV1_MAX_EDGE_COUNT;
int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
hb_paint_context_t (const void *base_,
hb_paint_funcs_t *funcs_,
@ -80,12 +83,16 @@ public:
hb_font_t *font_,
unsigned int palette_,
hb_color_t foreground_,
VarStoreInstancer &instancer_) :
ItemVarStoreInstancer &instancer_) :
base (base_),
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;
}
@ -159,23 +161,35 @@ struct hb_colrv1_closure_context_t :
void add_palette_index (unsigned palette_index)
{ palette_indices->add (palette_index); }
void add_var_idxes (unsigned first_var_idx, unsigned num_idxes)
{
if (!num_idxes || first_var_idx == VarIdx::NO_VARIATION) return;
variation_indices->add_range (first_var_idx, first_var_idx + num_idxes - 1);
}
public:
const void *base;
hb_set_t visited_paint;
hb_set_t *glyphs;
hb_set_t *layer_indices;
hb_set_t *palette_indices;
hb_set_t *variation_indices;
unsigned num_var_idxes;
unsigned nesting_level_left;
hb_colrv1_closure_context_t (const void *base_,
hb_set_t *glyphs_,
hb_set_t *layer_indices_,
hb_set_t *palette_indices_,
hb_set_t *variation_indices_,
unsigned num_var_idxes_ = 1,
unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
base (base_),
glyphs (glyphs_),
layer_indices (layer_indices_),
palette_indices (palette_indices_),
variation_indices (variation_indices_),
num_var_idxes (num_var_idxes_),
nesting_level_left (nesting_level_left_)
{}
};
@ -242,18 +256,33 @@ struct Variable
}
void closurev1 (hb_colrv1_closure_context_t* c) const
{ value.closurev1 (c); }
{
c->num_var_idxes = 0;
// update c->num_var_idxes during value closure
value.closurev1 (c);
c->add_var_idxes (varIdxBase, c->num_var_idxes);
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
if (c->plan->all_axes_pinned)
return_trace (true);
//TODO: update varIdxBase for partial-instancing
return_trace (c->serializer->embed (varIdxBase));
VarIdx new_varidx;
new_varidx = varIdxBase;
if (varIdxBase != VarIdx::NO_VARIATION)
{
hb_pair_t<unsigned, int> *new_varidx_delta;
if (!c->plan->colrv1_variation_idx_delta_map.has (varIdxBase, &new_varidx_delta))
return_trace (false);
new_varidx = hb_first (*new_varidx_delta);
}
return_trace (c->serializer->embed (new_varidx));
}
bool sanitize (hb_sanitize_context_t *c) const
@ -270,7 +299,7 @@ struct Variable
void get_color_stop (hb_paint_context_t *c,
hb_color_stop_t *stop,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
value.get_color_stop (c, stop, varIdxBase, instancer);
}
@ -305,7 +334,7 @@ struct NoVariable
{ value.closurev1 (c); }
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
return_trace (value.subset (c, instancer, varIdxBase));
@ -325,7 +354,7 @@ struct NoVariable
void get_color_stop (hb_paint_context_t *c,
hb_color_stop_t *stop,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
}
@ -345,10 +374,13 @@ struct NoVariable
struct ColorStop
{
void closurev1 (hb_colrv1_closure_context_t* c) const
{ c->add_palette_index (paletteIndex); }
{
c->add_palette_index (paletteIndex);
c->num_var_idxes = 2;
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -374,7 +406,7 @@ struct ColorStop
void get_color_stop (hb_paint_context_t *c,
hb_color_stop_t *out,
uint32_t varIdx,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
out->offset = stopOffset.to_float(instancer (varIdx, 0));
out->color = c->get_color (paletteIndex,
@ -410,7 +442,7 @@ struct ColorLine
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
@ -439,7 +471,7 @@ struct ColorLine
unsigned int start,
unsigned int *count,
hb_color_stop_t *color_stops,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
unsigned int len = stops.len;
@ -542,8 +574,11 @@ struct Affine2x3
return_trace (c->check_struct (this));
}
void closurev1 (hb_colrv1_closure_context_t* c) const
{ c->num_var_idxes = 6; }
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -588,7 +623,7 @@ struct PaintColrLayers
void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer HB_UNUSED) const
const ItemVarStoreInstancer &instancer HB_UNUSED) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
@ -617,10 +652,13 @@ struct PaintColrLayers
struct PaintSolid
{
void closurev1 (hb_colrv1_closure_context_t* c) const
{ c->add_palette_index (paletteIndex); }
{
c->add_palette_index (paletteIndex);
c->num_var_idxes = 1;
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -666,10 +704,13 @@ template <template<typename> class Var>
struct PaintLinearGradient
{
void closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+colorLine).closurev1 (c); }
{
(this+colorLine).closurev1 (c);
c->num_var_idxes = 6;
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -733,10 +774,13 @@ template <template<typename> class Var>
struct PaintRadialGradient
{
void closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+colorLine).closurev1 (c); }
{
(this+colorLine).closurev1 (c);
c->num_var_idxes = 6;
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -800,10 +844,13 @@ template <template<typename> class Var>
struct PaintSweepGradient
{
void closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+colorLine).closurev1 (c); }
{
(this+colorLine).closurev1 (c);
c->num_var_idxes = 4;
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -863,7 +910,7 @@ struct PaintGlyph
void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
@ -906,7 +953,7 @@ struct PaintColrGlyph
void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer HB_UNUSED) const
const ItemVarStoreInstancer &instancer HB_UNUSED) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
@ -936,7 +983,7 @@ struct PaintTransform
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
@ -958,7 +1005,7 @@ struct PaintTransform
void paint_glyph (hb_paint_context_t *c) const
{
TRACE_PAINT (this);
(this+transform).paint_glyph (c);
(this+transform).paint_glyph (c); // This does a push_transform()
c->recurse (this+src);
c->funcs->pop_transform (c->data);
}
@ -975,7 +1022,7 @@ struct PaintTranslate
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1024,7 +1071,7 @@ struct PaintScale
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1073,7 +1120,7 @@ struct PaintScaleAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1132,7 +1179,7 @@ struct PaintScaleUniform
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1176,7 +1223,7 @@ struct PaintScaleUniformAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1232,7 +1279,7 @@ struct PaintRotate
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1276,7 +1323,7 @@ struct PaintRotateAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1332,7 +1379,7 @@ struct PaintSkew
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1381,7 +1428,7 @@ struct PaintSkewAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1440,7 +1487,7 @@ struct PaintComposite
void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
@ -1491,7 +1538,7 @@ struct ClipBoxFormat1
return_trace (c->check_struct (this));
}
void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const
void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer HB_UNUSED) const
{
clip_box.xMin = xMin;
clip_box.yMin = yMin;
@ -1500,7 +1547,7 @@ struct ClipBoxFormat1
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
@ -1533,7 +1580,7 @@ struct ClipBoxFormat1
struct ClipBoxFormat2 : Variable<ClipBoxFormat1>
{
void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const
void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer) const
{
value.get_clip_box(clip_box, instancer);
if (instancer)
@ -1544,12 +1591,15 @@ struct ClipBoxFormat2 : Variable<ClipBoxFormat1>
clip_box.yMax += roundf (instancer (varIdxBase, 3));
}
}
void closurev1 (hb_colrv1_closure_context_t* c) const
{ c->variation_indices->add_range (varIdxBase, varIdxBase + 3); }
};
struct ClipBox
{
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
switch (u.format) {
@ -1559,6 +1609,14 @@ struct ClipBox
}
}
void closurev1 (hb_colrv1_closure_context_t* c) const
{
switch (u.format) {
case 2: u.format2.closurev1 (c);
default:return;
}
}
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
@ -1572,7 +1630,7 @@ struct ClipBox
}
bool get_extents (hb_glyph_extents_t *extents,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
ClipBoxData clip_box;
switch (u.format) {
@ -1606,9 +1664,15 @@ struct ClipRecord
int cmp (hb_codepoint_t g) const
{ return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; }
void closurev1 (hb_colrv1_closure_context_t* c, const void *base) const
{
if (!c->glyphs->intersects (startGlyphID, endGlyphID)) return;
(base+clipBox).closurev1 (c);
}
bool subset (hb_subset_context_t *c,
const void *base,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
@ -1625,7 +1689,7 @@ struct ClipRecord
bool get_extents (hb_glyph_extents_t *extents,
const void *base,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
return (base+clipBox).get_extents (extents, instancer);
}
@ -1642,7 +1706,7 @@ DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);
struct ClipList
{
unsigned serialize_clip_records (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const ItemVarStoreInstancer &instancer,
const hb_set_t& gids,
const hb_map_t& gid_offset_map) const
{
@ -1695,7 +1759,7 @@ struct ClipList
}
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
@ -1735,7 +1799,7 @@ struct ClipList
bool
get_extents (hb_codepoint_t gid,
hb_glyph_extents_t *extents,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
auto *rec = clips.as_array ().bsearch (gid);
if (rec)
@ -1855,7 +1919,7 @@ struct BaseGlyphPaintRecord
bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
const void* src_base, hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SERIALIZE (this);
auto *out = s->embed (this);
@ -1884,7 +1948,7 @@ struct BaseGlyphPaintRecord
struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord>
{
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
@ -1916,7 +1980,7 @@ struct LayerList : Array32OfOffset32To<Paint>
{ return this+(*this)[i]; }
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
const ItemVarStoreInstancer &instancer) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
@ -1941,6 +2005,76 @@ struct LayerList : Array32OfOffset32To<Paint>
}
};
struct delta_set_index_map_subset_plan_t
{
unsigned get_inner_bit_count () const { return inner_bit_count; }
unsigned get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); }
hb_array_t<const uint32_t> get_output_map () const { return output_map.as_array (); }
delta_set_index_map_subset_plan_t (const hb_map_t &new_deltaset_idx_varidx_map)
{
map_count = 0;
outer_bit_count = 0;
inner_bit_count = 1;
output_map.init ();
/* search backwards */
unsigned count = new_deltaset_idx_varidx_map.get_population ();
if (!count) return;
unsigned last_idx = (unsigned)-1;
unsigned last_varidx = (unsigned)-1;
for (unsigned i = count; i; i--)
{
unsigned delta_set_idx = i - 1;
unsigned var_idx = new_deltaset_idx_varidx_map.get (delta_set_idx);
if (i == count)
{
last_idx = delta_set_idx;
last_varidx = var_idx;
continue;
}
if (var_idx != last_varidx)
break;
last_idx = delta_set_idx;
}
map_count = last_idx + 1;
}
bool remap (const hb_map_t &new_deltaset_idx_varidx_map)
{
/* recalculate bit_count */
outer_bit_count = 1;
inner_bit_count = 1;
if (unlikely (!output_map.resize (map_count, false))) return false;
for (unsigned idx = 0; idx < map_count; idx++)
{
uint32_t *var_idx;
if (!new_deltaset_idx_varidx_map.has (idx, &var_idx)) return false;
output_map.arrayZ[idx] = *var_idx;
unsigned outer = (*var_idx) >> 16;
unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer);
outer_bit_count = hb_max (bit_count, outer_bit_count);
unsigned inner = (*var_idx) & 0xFFFF;
bit_count = (inner == 0) ? 1 : hb_bit_storage (inner);
inner_bit_count = hb_max (bit_count, inner_bit_count);
}
return true;
}
private:
unsigned map_count;
unsigned outer_bit_count;
unsigned inner_bit_count;
hb_vector_t<uint32_t> output_map;
};
struct COLR
{
static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
@ -1948,10 +2082,11 @@ struct COLR
bool has_v0_data () const { return numBaseGlyphs; }
bool has_v1_data () const
{
if (version == 1)
return (this+baseGlyphList).len > 0;
if (version < 1)
return false;
hb_barrier ();
return false;
return (this+baseGlyphList).len > 0;
}
unsigned int get_glyph_layers (hb_codepoint_t glyph,
@ -1991,8 +2126,26 @@ struct COLR
void closure_forV1 (hb_set_t *glyphset,
hb_set_t *layer_indices,
hb_set_t *palette_indices) const
{ colr->closure_forV1 (glyphset, layer_indices, palette_indices); }
hb_set_t *palette_indices,
hb_set_t *variation_indices,
hb_set_t *delta_set_indices) const
{ colr->closure_forV1 (glyphset, layer_indices, palette_indices, variation_indices, delta_set_indices); }
bool has_var_store () const
{ return colr->has_var_store (); }
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;
@ -2029,12 +2182,16 @@ struct COLR
void closure_forV1 (hb_set_t *glyphset,
hb_set_t *layer_indices,
hb_set_t *palette_indices) const
hb_set_t *palette_indices,
hb_set_t *variation_indices,
hb_set_t *delta_set_indices) const
{
if (version != 1) return;
if (version < 1) return;
hb_barrier ();
hb_set_t visited_glyphs;
hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices);
hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices, variation_indices);
const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ())
@ -2046,6 +2203,22 @@ struct COLR
paint.dispatch (&c);
}
hb_set_union (glyphset, &visited_glyphs);
const ClipList &cliplist = this+clipList;
c.glyphs = glyphset;
for (const ClipRecord &clip_record : cliplist.clips.iter())
clip_record.closurev1 (&c, &cliplist);
// if a DeltaSetIndexMap is included, collected variation indices are
// actually delta set indices, we need to map them into variation indices
if (has_delta_set_index_map ())
{
const DeltaSetIndexMap &var_idx_map = this+varIdxMap;
delta_set_indices->set (*variation_indices);
variation_indices->clear ();
for (unsigned delta_set_idx : *delta_set_indices)
variation_indices->add (var_idx_map.map (delta_set_idx));
}
}
const LayerList& get_layerList () const
@ -2054,14 +2227,37 @@ struct COLR
const BaseGlyphList& get_baseglyphList () const
{ return (this+baseGlyphList); }
bool has_var_store () const
{ return version >= 1 && hb_barrier () && varStore != 0; }
bool has_delta_set_index_map () const
{ return version >= 1 && hb_barrier () && varIdxMap != 0; }
bool has_clip_list () const
{ return version >= 1 && hb_barrier () && clipList != 0; }
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); }
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
(this+baseGlyphsZ).sanitize (c, numBaseGlyphs) &&
(this+layersZ).sanitize (c, numLayers) &&
(version == 0 ||
(version == 1 &&
(hb_barrier () &&
baseGlyphList.sanitize (c, this) &&
layerList.sanitize (c, this) &&
clipList.sanitize (c, this) &&
@ -2127,6 +2323,94 @@ struct COLR
return record;
}
bool downgrade_to_V0 (const hb_set_t &glyphset) const
{
//no more COLRv1 glyphs, downgrade to version 0
for (const BaseGlyphPaintRecord& _ : get_baseglyphList ())
if (glyphset.has (_.glyphId))
return false;
return true;
}
bool subset_varstore (hb_subset_context_t *c,
COLR* out /* OUT */) const
{
TRACE_SUBSET (this);
if (!varStore || c->plan->all_axes_pinned ||
!c->plan->colrv1_variation_idx_delta_map)
return_trace (true);
const ItemVariationStore& var_store = this+varStore;
if (c->plan->normalized_coords)
{
item_variations_t item_vars;
/* turn off varstore optimization when varIdxMap is null, so we maintain
* original var_idx sequence */
bool optimize = (varIdxMap != 0) ? true : false;
if (!item_vars.instantiate (var_store, c->plan,
optimize, /* optimization */
optimize, /* use_no_variation_idx = false */
c->plan->colrv1_varstore_inner_maps.as_array ()))
return_trace (false);
/* do not serialize varStore if there's no variation data after
* instancing: region_list or var_data is empty */
if (item_vars.get_region_list () &&
item_vars.get_vardata_encodings () &&
!out->varStore.serialize_serialize (c->serializer,
item_vars.has_long_word (),
c->plan->axis_tags,
item_vars.get_region_list (),
item_vars.get_vardata_encodings ()))
return_trace (false);
/* if varstore is optimized, update colrv1_new_deltaset_idx_varidx_map in
* subset plan.
* If varstore is empty after instancing, varidx_map would be empty and
* all var_idxes will be updated to VarIdx::NO_VARIATION */
if (optimize)
{
const hb_map_t &varidx_map = item_vars.get_varidx_map ();
for (auto _ : c->plan->colrv1_new_deltaset_idx_varidx_map.iter_ref ())
{
uint32_t varidx = _.second;
uint32_t *new_varidx;
if (varidx_map.has (varidx, &new_varidx))
_.second = *new_varidx;
else
_.second = VarIdx::NO_VARIATION;
}
}
}
else
{
if (unlikely (!out->varStore.serialize_serialize (c->serializer,
&var_store,
c->plan->colrv1_varstore_inner_maps.as_array ())))
return_trace (false);
}
return_trace (true);
}
bool subset_delta_set_index_map (hb_subset_context_t *c,
COLR* out /* OUT */) const
{
TRACE_SUBSET (this);
if (!varIdxMap || c->plan->all_axes_pinned ||
!c->plan->colrv1_new_deltaset_idx_varidx_map)
return_trace (true);
const hb_map_t &deltaset_idx_varidx_map = c->plan->colrv1_new_deltaset_idx_varidx_map;
delta_set_index_map_subset_plan_t index_map_plan (deltaset_idx_varidx_map);
if (unlikely (!index_map_plan.remap (deltaset_idx_varidx_map)))
return_trace (false);
return_trace (out->varIdxMap.serialize_serialize (c->serializer, index_map_plan));
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
@ -2195,34 +2479,30 @@ struct COLR
auto *colr_prime = c->serializer->start_embed<COLR> ();
if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false);
if (version == 0)
return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it));
if (version == 0 || downgrade_to_V0 (glyphset))
return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it));
auto snap = c->serializer->snapshot ();
hb_barrier ();
//start version 1
if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false);
if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr,
varIdxMap ? &(this+varIdxMap) : nullptr,
c->plan->normalized_coords.as_array ());
/* subset ItemVariationStore first, cause varidx_map needs to be updated
* after instancing */
if (!subset_varstore (c, colr_prime)) return_trace (false);
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))
{
if (c->serializer->in_error ()) return_trace (false);
//no more COLRv1 glyphs: downgrade to version 0
c->serializer->revert (snap);
return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it));
}
if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
return_trace (false);
colr_prime->layerList.serialize_subset (c, layerList, this, instancer);
colr_prime->clipList.serialize_subset (c, clipList, this, instancer);
if (!varStore || c->plan->all_axes_pinned)
return_trace (true);
colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this);
colr_prime->varStore.serialize_copy (c->serializer, varStore, this);
return_trace (true);
return_trace (subset_delta_set_index_map (c, colr_prime));
}
const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const
@ -2242,12 +2522,10 @@ struct COLR
bool
get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
{
if (version != 1)
return false;
VarStoreInstancer instancer (&(this+varStore),
&(this+varIdxMap),
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));
if (get_clip (glyph, extents, instancer))
{
@ -2282,8 +2560,10 @@ struct COLR
bool
has_paint_for_glyph (hb_codepoint_t glyph) const
{
if (version == 1)
if (version >= 1)
{
hb_barrier ();
const Paint *paint = get_base_glyph_paint (glyph);
return paint != nullptr;
@ -2294,9 +2574,9 @@ struct COLR
bool get_clip (hb_codepoint_t glyph,
hb_glyph_extents_t *extents,
const VarStoreInstancer instancer) const
const ItemVarStoreInstancer instancer) const
{
return (this+clipList).get_extents (glyph,
return get_clip_list ().get_extents (glyph,
extents,
instancer);
}
@ -2305,23 +2585,23 @@ 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
{
VarStoreInstancer instancer (&(this+varStore),
&(this+varIdxMap),
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);
if (version == 1)
hb_decycler_node_t node (c.glyphs_decycler);
node.visit (glyph);
if (version >= 1)
{
hb_barrier ();
const Paint *paint = get_base_glyph_paint (glyph);
if (paint)
{
// COLRv1 glyph
VarStoreInstancer instancer (&(this+varStore),
&(this+varIdxMap),
hb_array (font->coords, font->num_coords));
bool is_bounded = true;
if (clip)
{
@ -2404,7 +2684,7 @@ struct COLR
Offset32To<LayerList> layerList;
Offset32To<ClipList> clipList; // Offset to ClipList table (may be NULL)
Offset32To<DeltaSetIndexMap> varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL)
Offset32To<VariationStore> varStore;
Offset32To<ItemVariationStore> varStore;
public:
DEFINE_SIZE_MIN (14);
};
@ -2427,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);
}
}
@ -2447,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);
@ -2479,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

@ -66,34 +66,64 @@ HB_INTERNAL void PaintColrGlyph::closurev1 (hb_colrv1_closure_context_t* c) cons
template <template<typename> class Var>
HB_INTERNAL void PaintTransform<Var>::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
(this+transform).closurev1 (c);
}
HB_INTERNAL void PaintTranslate::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 2;
}
HB_INTERNAL void PaintScale::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 2;
}
HB_INTERNAL void PaintScaleAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 4;
}
HB_INTERNAL void PaintScaleUniform::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 1;
}
HB_INTERNAL void PaintScaleUniformAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 3;
}
HB_INTERNAL void PaintRotate::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 1;
}
HB_INTERNAL void PaintRotateAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 3;
}
HB_INTERNAL void PaintSkew::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 2;
}
HB_INTERNAL void PaintSkewAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+src).dispatch (c); }
{
(this+src).dispatch (c);
c->num_var_idxes = 4;
}
HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) const
{

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. */
@ -214,13 +222,17 @@ struct CPAL
hb_set_t *nameids_to_retain /* OUT */) const
{
if (version == 1)
{
hb_barrier ();
v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain);
}
}
private:
const CPALV1Tail& v1 () const
{
if (version == 0) return Null (CPALV1Tail);
hb_barrier ();
return StructAfter<CPALV1Tail> (*this);
}
@ -312,7 +324,10 @@ struct CPAL
return_trace (false);
if (version == 1)
{
hb_barrier ();
return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map));
}
return_trace (true);
}
@ -321,6 +336,7 @@ struct CPAL
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
(this+colorRecordsZ).sanitize (c, numColorRecords) &&
colorRecordIndicesZ.sanitize (c, numPalettes) &&
(version == 0 || v1 ().sanitize (c, this, numPalettes, numColors)));

View File

@ -368,6 +368,7 @@ struct sbix
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
version >= 1 &&
strikes.sanitize (c, this)));
}

View File

@ -56,6 +56,7 @@ struct SVGDocumentIndexEntry
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
svgDoc.sanitize (c, base, svgDocLength));
}

View File

@ -64,6 +64,7 @@ struct Coverage
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
hb_barrier ();
switch (u.format)
{
case 1: return_trace (u.format1.sanitize (c));
@ -95,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
{
@ -200,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

@ -189,7 +189,7 @@ struct CaretValueFormat3
friend struct CaretValue;
hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction,
const VariationStore &var_store) const
const ItemVariationStore &var_store) const
{
return HB_DIRECTION_IS_HORIZONTAL (direction) ?
font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) :
@ -251,7 +251,7 @@ struct CaretValue
hb_position_t get_caret_value (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t glyph_id,
const VariationStore &var_store) const
const ItemVariationStore &var_store) const
{
switch (u.format) {
case 1: return u.format1.get_caret_value (font, direction);
@ -291,6 +291,7 @@ struct CaretValue
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
hb_barrier ();
switch (u.format) {
case 1: return_trace (u.format1.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
@ -315,7 +316,7 @@ struct LigGlyph
unsigned get_lig_carets (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t glyph_id,
const VariationStore &var_store,
const ItemVariationStore &var_store,
unsigned start_offset,
unsigned *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */) const
@ -371,7 +372,7 @@ struct LigCaretList
unsigned int get_lig_carets (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t glyph_id,
const VariationStore &var_store,
const ItemVariationStore &var_store,
unsigned int start_offset,
unsigned int *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */) const
@ -441,6 +442,20 @@ struct MarkGlyphSetsFormat1
bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
{ return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
void collect_used_mark_sets (const hb_set_t& glyph_set,
hb_set_t& used_mark_sets /* OUT */) const
{
unsigned i = 0;
for (const auto &offset : coverage)
{
const auto &cov = this+offset;
if (cov.intersects (&glyph_set))
used_mark_sets.add (i);
i++;
}
}
template <typename set_t>
void collect_coverage (hb_vector_t<set_t> &sets) const
{
@ -461,6 +476,7 @@ struct MarkGlyphSetsFormat1
bool ret = true;
for (const Offset32To<Coverage>& offset : coverage.iter ())
{
auto snap = c->serializer->snapshot ();
auto *o = out->coverage.serialize_append (c->serializer);
if (unlikely (!o))
{
@ -468,11 +484,17 @@ struct MarkGlyphSetsFormat1
break;
}
//not using o->serialize_subset (c, offset, this, out) here because
//OTS doesn't allow null offset.
//See issue: https://github.com/khaledhosny/ots/issues/172
//skip empty coverage
c->serializer->push ();
c->dispatch (this+offset);
bool res = false;
if (offset) res = c->dispatch (this+offset);
if (!res)
{
c->serializer->pop_discard ();
c->serializer->revert (snap);
(out->coverage.len)--;
continue;
}
c->serializer->add_link (*o, c->serializer->pop_pack ());
}
@ -513,6 +535,15 @@ struct MarkGlyphSets
}
}
void collect_used_mark_sets (const hb_set_t& glyph_set,
hb_set_t& used_mark_sets /* OUT */) const
{
switch (u.format) {
case 1: u.format1.collect_used_mark_sets (glyph_set, used_mark_sets); return;
default:return;
}
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
@ -526,6 +557,7 @@ struct MarkGlyphSets
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
hb_barrier ();
switch (u.format) {
case 1: return_trace (u.format1.sanitize (c));
default:return_trace (true);
@ -577,7 +609,7 @@ struct GDEFVersion1_2
* definitions--from beginning of GDEF
* header (may be NULL). Introduced
* in version 0x00010002. */
Offset32To<VariationStore>
Offset32To<ItemVariationStore>
varStore; /* Offset to the table of Item Variation
* Store--from beginning of GDEF
* header (may be NULL). Introduced
@ -600,8 +632,9 @@ struct GDEFVersion1_2
attachList.sanitize (c, this) &&
ligCaretList.sanitize (c, this) &&
markAttachClassDef.sanitize (c, this) &&
(version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
(version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
hb_barrier () &&
((version.to_int () < 0x00010002u && hb_barrier ()) || markGlyphSetsDef.sanitize (c, this)) &&
((version.to_int () < 0x00010003u && hb_barrier ()) || varStore.sanitize (c, this)));
}
static void remap_varidx_after_instantiation (const hb_map_t& varidx_map,
@ -627,23 +660,23 @@ struct GDEFVersion1_2
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true);
bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
bool subset_markglyphsetsdef = false;
if (version.to_int () >= 0x00010002u)
{
subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
}
// Push var store first (if it's needed) so that it's last in the
// serialization order. Some font consumers assume that varstore runs to
// the end of the GDEF table.
// See: https://github.com/harfbuzz/harfbuzz/issues/4636
auto snapshot_version0 = c->serializer->snapshot ();
if (unlikely (version.to_int () >= 0x00010002u && hb_barrier () && !c->serializer->embed (markGlyphSetsDef)))
return_trace (false);
bool subset_varstore = false;
if (version.to_int () >= 0x00010003u)
unsigned varstore_index = (unsigned) -1;
auto snapshot_version2 = c->serializer->snapshot ();
if (version.to_int () >= 0x00010003u && hb_barrier ())
{
if (unlikely (!c->serializer->embed (varStore))) return_trace (false);
if (c->plan->all_axes_pinned)
out->varStore = 0;
else if (c->plan->normalized_coords)
@ -652,27 +685,56 @@ struct GDEFVersion1_2
{
item_variations_t item_vars;
if (item_vars.instantiate (this+varStore, c->plan, true, true,
c->plan->gdef_varstore_inner_maps.as_array ()))
c->plan->gdef_varstore_inner_maps.as_array ())) {
subset_varstore = out->varStore.serialize_serialize (c->serializer,
item_vars.has_long_word (),
c->plan->axis_tags,
item_vars.get_region_list (),
item_vars.get_vardata_encodings ());
varstore_index = c->serializer->last_added_child_index();
}
remap_varidx_after_instantiation (item_vars.get_varidx_map (),
c->plan->layout_variation_idx_delta_map);
}
}
else
{
subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ());
varstore_index = c->serializer->last_added_child_index();
}
}
out->version.major = version.major;
out->version.minor = version.minor;
if (!subset_varstore && version.to_int () >= 0x00010002u) {
c->serializer->revert (snapshot_version2);
}
bool subset_markglyphsetsdef = false;
if (version.to_int () >= 0x00010002u && hb_barrier ())
{
subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
}
if (subset_varstore)
{
out->version.minor = 3;
c->plan->has_gdef_varstore = true;
} else if (subset_markglyphsetsdef) {
out->version.minor = 2;
} else {
out->version.minor = 0;
c->serializer->revert (snapshot_version0);
}
bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true);
bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
if (subset_varstore && varstore_index != (unsigned) -1) {
c->serializer->repack_last(varstore_index);
}
return_trace (subset_glyphclassdef || subset_attachlist ||
@ -709,6 +771,7 @@ struct GDEF
{
TRACE_SANITIZE (this);
if (unlikely (!u.version.sanitize (c))) return_trace (false);
hb_barrier ();
switch (u.version.major) {
case 1: return_trace (u.version1.sanitize (c));
#ifndef HB_NO_BEYOND_64K
@ -812,7 +875,7 @@ struct GDEF
bool has_mark_glyph_sets () const
{
switch (u.version.major) {
case 1: return u.version.to_int () >= 0x00010002u && u.version1.markGlyphSetsDef != 0;
case 1: return u.version.to_int () >= 0x00010002u && hb_barrier () && u.version1.markGlyphSetsDef != 0;
#ifndef HB_NO_BEYOND_64K
case 2: return u.version2.markGlyphSetsDef != 0;
#endif
@ -822,7 +885,7 @@ struct GDEF
const MarkGlyphSets &get_mark_glyph_sets () const
{
switch (u.version.major) {
case 1: return u.version.to_int () >= 0x00010002u ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets);
case 1: return u.version.to_int () >= 0x00010002u && hb_barrier () ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets);
#ifndef HB_NO_BEYOND_64K
case 2: return this+u.version2.markGlyphSetsDef;
#endif
@ -832,21 +895,21 @@ struct GDEF
bool has_var_store () const
{
switch (u.version.major) {
case 1: return u.version.to_int () >= 0x00010003u && u.version1.varStore != 0;
case 1: return u.version.to_int () >= 0x00010003u && hb_barrier () && u.version1.varStore != 0;
#ifndef HB_NO_BEYOND_64K
case 2: return u.version2.varStore != 0;
#endif
default: return false;
}
}
const VariationStore &get_var_store () const
const ItemVariationStore &get_var_store () const
{
switch (u.version.major) {
case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore);
case 1: return u.version.to_int () >= 0x00010003u && hb_barrier () ? this+u.version1.varStore : Null(ItemVariationStore);
#ifndef HB_NO_BEYOND_64K
case 2: return this+u.version2.varStore;
#endif
default: return Null(VariationStore);
default: return Null(ItemVariationStore);
}
}
@ -959,47 +1022,6 @@ struct GDEF
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{ get_lig_caret_list ().collect_variation_indices (c); }
void remap_layout_variation_indices (const hb_set_t *layout_variation_indices,
const hb_vector_t<int>& normalized_coords,
bool calculate_delta, /* not pinned at default */
bool no_variations, /* all axes pinned */
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map /* OUT */) const
{
if (!has_var_store ()) return;
const VariationStore &var_store = get_var_store ();
float *store_cache = var_store.create_cache ();
unsigned new_major = 0, new_minor = 0;
unsigned last_major = (layout_variation_indices->get_min ()) >> 16;
for (unsigned idx : layout_variation_indices->iter ())
{
int delta = 0;
if (calculate_delta)
delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ,
normalized_coords.length, store_cache));
if (no_variations)
{
layout_variation_idx_delta_map->set (idx, hb_pair_t<unsigned, int> (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta));
continue;
}
uint16_t major = idx >> 16;
if (major >= var_store.get_sub_table_count ()) break;
if (major != last_major)
{
new_minor = 0;
++new_major;
}
unsigned new_idx = (new_major << 16) + new_minor;
layout_variation_idx_delta_map->set (idx, hb_pair_t<unsigned, int> (new_idx, delta));
++new_minor;
last_major = major;
}
var_store.destroy_cache (store_cache);
}
protected:
union {
FixedVersion<> version; /* Version identifier */

View File

@ -25,6 +25,7 @@ struct Anchor
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
hb_barrier ();
switch (u.format) {
case 1: return_trace (u.format1.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));

View File

@ -38,9 +38,15 @@ struct AnchorFormat3
*y = font->em_fscale_y (yCoordinate);
if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
{
hb_barrier ();
*x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
}
if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
{
hb_barrier ();
*y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
}
}
bool subset (hb_subset_context_t *c) const

View File

@ -8,7 +8,7 @@ namespace GPOS_impl {
struct AnchorMatrix
{
HBUINT16 rows; /* Number of rows */
UnsizedArrayOf<Offset16To<Anchor>>
UnsizedArrayOf<Offset16To<Anchor, AnchorMatrix>>
matrixZ; /* Matrix of offsets to Anchor tables--
* from beginning of AnchorMatrix table */
public:
@ -18,6 +18,7 @@ struct AnchorMatrix
{
TRACE_SANITIZE (this);
if (!c->check_struct (this)) return_trace (false);
hb_barrier ();
if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
unsigned int count = rows * cols;
if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
@ -25,6 +26,7 @@ struct AnchorMatrix
if (c->lazy_some_gpos)
return_trace (true);
hb_barrier ();
for (unsigned int i = 0; i < count; i++)
if (!matrixZ[i].sanitize (c, this)) return_trace (false);
return_trace (true);
@ -38,6 +40,7 @@ struct AnchorMatrix
if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
auto &offset = matrixZ[row * cols + col];
if (unlikely (!offset.sanitize (&c->sanitizer, this))) return Null (Anchor);
hb_barrier ();
*found = !offset.is_null ();
return this+offset;
}
@ -65,15 +68,14 @@ struct AnchorMatrix
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->rows = num_rows;
bool ret = false;
for (const unsigned i : index_iter)
{
auto *offset = c->serializer->embed (matrixZ[i]);
if (!offset) return_trace (false);
ret |= offset->serialize_subset (c, matrixZ[i], this);
offset->serialize_subset (c, matrixZ[i], this);
}
return_trace (ret);
return_trace (true);
}
};

View File

@ -23,7 +23,7 @@ static void SinglePos_serialize (hb_serialize_context_t *c,
const SrcLookup *src,
Iterator it,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
bool all_axes_pinned);
unsigned new_format);
}

View File

@ -11,21 +11,21 @@ struct EntryExitRecord
{
friend struct CursivePosFormat1;
bool sanitize (hb_sanitize_context_t *c, const void *base) const
bool sanitize (hb_sanitize_context_t *c, const struct CursivePosFormat1 *base) const
{
TRACE_SANITIZE (this);
return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const void *src_base) const
const struct CursivePosFormat1 *src_base) const
{
(src_base+entryAnchor).collect_variation_indices (c);
(src_base+exitAnchor).collect_variation_indices (c);
}
bool subset (hb_subset_context_t *c,
const void *src_base) const
const struct CursivePosFormat1 *src_base) const
{
TRACE_SERIALIZE (this);
auto *out = c->serializer->embed (this);
@ -38,11 +38,11 @@ struct EntryExitRecord
}
protected:
Offset16To<Anchor>
Offset16To<Anchor, struct CursivePosFormat1>
entryAnchor; /* Offset to EntryAnchor table--from
* beginning of CursivePos
* subtable--may be NULL */
Offset16To<Anchor>
Offset16To<Anchor, struct CursivePosFormat1>
exitAnchor; /* Offset to ExitAnchor table--from
* beginning of CursivePos
* subtable--may be NULL */
@ -128,6 +128,7 @@ struct CursivePosFormat1
const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)];
if (!this_record.entryAnchor ||
unlikely (!this_record.entryAnchor.sanitize (&c->sanitizer, this))) return_trace (false);
hb_barrier ();
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset_fast (buffer->idx);
@ -145,6 +146,7 @@ struct CursivePosFormat1
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
}
hb_barrier ();
unsigned int i = skippy_iter.idx;
unsigned int j = buffer->idx;
@ -262,7 +264,7 @@ struct CursivePosFormat1
hb_requires (hb_is_iterator (Iterator))>
void serialize (hb_subset_context_t *c,
Iterator it,
const void *src_base)
const struct CursivePosFormat1 *src_base)
{
if (unlikely (!c->serializer->extend_min ((*this)))) return;
this->format = 1;

View File

@ -42,6 +42,7 @@ struct MarkMarkPosFormat1_2
mark1Coverage.sanitize (c, this) &&
mark2Coverage.sanitize (c, this) &&
mark1Array.sanitize (c, this) &&
hb_barrier () &&
mark2Array.sanitize (c, this, (unsigned int) classCount));
}

View File

@ -36,6 +36,7 @@ struct PairPosFormat1_3
TRACE_SANITIZE (this);
if (!c->check_struct (this)) return_trace (false);
hb_barrier ();
unsigned int len1 = valueFormat[0].get_len ();
unsigned int len2 = valueFormat[1].get_len ();
@ -102,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);
@ -131,20 +170,33 @@ struct PairPosFormat1_3
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->format = format;
out->valueFormat[0] = valueFormat[0];
out->valueFormat[1] = valueFormat[1];
if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat[0], valueFormat[1]);
if (c->plan->normalized_coords)
{
hb_pair_t<unsigned, unsigned> newFormats = compute_effective_value_formats (glyphset);
out->valueFormat[0] = newFormats.first;
out->valueFormat[1] = newFormats.second;
/* all device flags will be dropped when full instancing, no need to strip
* hints, also do not strip emtpy cause we don't compute the new default
* value during stripping */
newFormats = compute_effective_value_formats (glyphset, false, false, &c->plan->layout_variation_idx_delta_map);
}
/* do not strip hints for VF */
else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
{
hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
bool has_fvar = (blob != hb_blob_get_empty ());
hb_blob_destroy (blob);
bool strip = !has_fvar;
/* special case: strip hints when a VF has no GDEF varstore after
* subsetting*/
if (has_fvar && !c->plan->has_gdef_varstore)
strip = true;
newFormats = compute_effective_value_formats (glyphset, strip, true);
}
if (c->plan->all_axes_pinned)
{
out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags ();
out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags ();
}
out->valueFormat[0] = newFormats.first;
out->valueFormat[1] = newFormats.second;
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
@ -175,7 +227,9 @@ struct PairPosFormat1_3
}
hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset) const
hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset,
bool strip_hints, bool strip_empty,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) const
{
unsigned record_size = PairSet::get_size (valueFormat);
@ -195,8 +249,8 @@ struct PairPosFormat1_3
{
if (record->intersects (glyphset))
{
format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ());
format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]));
format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 (), strip_hints, strip_empty, &set, varidx_delta_map);
format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]), strip_hints, strip_empty, &set, varidx_delta_map);
}
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}

View File

@ -8,7 +8,7 @@ namespace Layout {
namespace GPOS_impl {
template <typename Types>
struct PairPosFormat2_4
struct PairPosFormat2_4 : ValueBase
{
protected:
HBUINT16 format; /* Format identifier--format = 2 */
@ -123,12 +123,61 @@ struct PairPosFormat2_4
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,14 +188,13 @@ struct PairPosFormat2_4
return_trace (false);
}
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
if (!klass2)
{
buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
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);
@ -287,18 +335,31 @@ struct PairPosFormat2_4
unsigned len2 = valueFormat2.get_len ();
hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat1, valueFormat2);
if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
newFormats = compute_effective_value_formats (klass1_map, klass2_map);
if (c->plan->normalized_coords)
{
/* in case of full instancing, all var device flags will be dropped so no
* need to strip hints here */
newFormats = compute_effective_value_formats (klass1_map, klass2_map, false, false, &c->plan->layout_variation_idx_delta_map);
}
/* do not strip hints for VF */
else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
{
hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
bool has_fvar = (blob != hb_blob_get_empty ());
hb_blob_destroy (blob);
bool strip = !has_fvar;
/* special case: strip hints when a VF has no GDEF varstore after
* subsetting*/
if (has_fvar && !c->plan->has_gdef_varstore)
strip = true;
newFormats = compute_effective_value_formats (klass1_map, klass2_map, strip, true);
}
out->valueFormat1 = newFormats.first;
out->valueFormat2 = newFormats.second;
if (c->plan->all_axes_pinned)
{
out->valueFormat1 = out->valueFormat1.drop_device_table_flags ();
out->valueFormat2 = out->valueFormat2.drop_device_table_flags ();
}
unsigned total_len = len1 + len2;
hb_vector_t<unsigned> class2_idxs (+ hb_range ((unsigned) class2Count) | hb_filter (klass2_map));
for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
@ -311,22 +372,15 @@ struct PairPosFormat2_4
}
}
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto it =
+ hb_iter (this+coverage)
| hb_filter (glyphset)
| hb_map_retains_sorting (glyph_map)
;
out->coverage.serialize_serialize (c->serializer, it);
return_trace (out->class1Count && out->class2Count && bool (it));
bool ret = out->coverage.serialize_subset(c, coverage, this);
return_trace (out->class1Count && out->class2Count && ret);
}
hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_map_t& klass1_map,
const hb_map_t& klass2_map) const
const hb_map_t& klass2_map,
bool strip_hints, bool strip_empty,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) const
{
unsigned len1 = valueFormat1.get_len ();
unsigned len2 = valueFormat2.get_len ();
@ -340,8 +394,8 @@ struct PairPosFormat2_4
for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
{
unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size;
format1 = format1 | valueFormat1.get_effective_format (&values[idx]);
format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]);
format1 = format1 | valueFormat1.get_effective_format (&values[idx], strip_hints, strip_empty, this, varidx_delta_map);
format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1], strip_hints, strip_empty, this, varidx_delta_map);
}
if (format1 == valueFormat1 && format2 == valueFormat2)

View File

@ -9,7 +9,7 @@ namespace GPOS_impl {
template <typename Types>
struct PairSet
struct PairSet : ValueBase
{
template <typename Types2>
friend struct PairPosFormat1_3;
@ -45,10 +45,12 @@ struct PairSet
bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
{
TRACE_SANITIZE (this);
if (!(c->check_struct (this)
&& c->check_range (&firstPairValueRecord,
if (!(c->check_struct (this) &&
hb_barrier () &&
c->check_range (&firstPairValueRecord,
len,
closure->stride))) return_trace (false);
hb_barrier ();
unsigned int count = len;
const PairValueRecord *record = &firstPairValueRecord;

View File

@ -29,7 +29,7 @@ struct PairValueRecord
struct context_t
{
const void *base;
const ValueBase *base;
const ValueFormat *valueFormats;
const ValueFormat *newFormats;
unsigned len1; /* valueFormats[0].get_len() */
@ -62,7 +62,7 @@ struct PairValueRecord
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const ValueFormat *valueFormats,
const void *base) const
const ValueBase *base) const
{
unsigned record1_len = valueFormats[0].get_len ();
unsigned record2_len = valueFormats[1].get_len ();

View File

@ -39,14 +39,12 @@ struct SinglePos
const SrcLookup* src,
Iterator glyph_val_iter_pairs,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
bool all_axes_pinned)
unsigned newFormat)
{
if (unlikely (!c->extend_min (u.format))) return;
unsigned format = 2;
ValueFormat new_format = src->get_value_format ();
if (all_axes_pinned)
new_format = new_format.drop_device_table_flags ();
ValueFormat new_format;
new_format = newFormat;
if (glyph_val_iter_pairs)
format = get_format (glyph_val_iter_pairs);
@ -89,8 +87,8 @@ SinglePos_serialize (hb_serialize_context_t *c,
const SrcLookup *src,
Iterator it,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
bool all_axes_pinned)
{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); }
unsigned new_format)
{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_delta_map, new_format); }
}

View File

@ -8,7 +8,7 @@ namespace OT {
namespace Layout {
namespace GPOS_impl {
struct SinglePosFormat1
struct SinglePosFormat1 : ValueBase
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
@ -28,6 +28,7 @@ struct SinglePosFormat1
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
coverage.sanitize (c, this) &&
hb_barrier () &&
/* The coverage table may use a range to represent a set
* of glyphs, which means a small number of bytes can
* generate a large glyph set. Manually modify the
@ -66,7 +67,7 @@ struct SinglePosFormat1
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 ())
{
@ -146,6 +147,30 @@ struct SinglePosFormat1
hb_set_t intersection;
(this+coverage).intersect_set (glyphset, intersection);
unsigned new_format = valueFormat;
if (c->plan->normalized_coords)
{
new_format = valueFormat.get_effective_format (values.arrayZ, false, false, this, &c->plan->layout_variation_idx_delta_map);
}
/* do not strip hints for VF */
else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
{
hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
bool has_fvar = (blob != hb_blob_get_empty ());
hb_blob_destroy (blob);
bool strip = !has_fvar;
/* special case: strip hints when a VF has no GDEF varstore after
* subsetting*/
if (has_fvar && !c->plan->has_gdef_varstore)
strip = true;
new_format = valueFormat.get_effective_format (values.arrayZ,
strip, /* strip hints */
true, /* strip empty */
this, nullptr);
}
auto it =
+ hb_iter (intersection)
| hb_map_retains_sorting (glyph_map)
@ -153,7 +178,7 @@ struct SinglePosFormat1
;
bool ret = bool (it);
SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, new_format);
return_trace (ret);
}
};

View File

@ -7,7 +7,7 @@ namespace OT {
namespace Layout {
namespace GPOS_impl {
struct SinglePosFormat2
struct SinglePosFormat2 : ValueBase
{
protected:
HBUINT16 format; /* Format identifier--format = 2 */
@ -66,7 +66,7 @@ struct SinglePosFormat2
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);
@ -143,6 +143,37 @@ struct SinglePosFormat2
coverage.serialize_serialize (c, glyphs);
}
template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
unsigned compute_effective_format (const hb_face_t *face,
Iterator it,
bool is_instancing, bool strip_hints,
bool has_gdef_varstore,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
{
hb_blob_t* blob = hb_face_reference_table (face, HB_TAG ('f','v','a','r'));
bool has_fvar = (blob != hb_blob_get_empty ());
hb_blob_destroy (blob);
unsigned new_format = 0;
if (is_instancing)
{
new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), false, false, this, varidx_delta_map);
}
/* do not strip hints for VF */
else if (strip_hints)
{
bool strip = !has_fvar;
if (has_fvar && !has_gdef_varstore)
strip = true;
new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), strip, true, this, nullptr);
}
else
new_format = valueFormat;
return new_format;
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
@ -163,8 +194,13 @@ struct SinglePosFormat2
})
;
unsigned new_format = compute_effective_format (c->plan->source, it,
bool (c->plan->normalized_coords),
bool (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING),
c->plan->has_gdef_varstore,
&c->plan->layout_variation_idx_delta_map);
bool ret = bool (it);
SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, new_format);
return_trace (ret);
}
};

View File

@ -9,6 +9,8 @@ namespace GPOS_impl {
typedef HBUINT16 Value;
struct ValueBase {}; // Dummy base class tag for OffsetTo<Value> bases.
typedef UnsizedArrayOf<Value> ValueRecord;
struct ValueFormat : HBUINT16
@ -78,7 +80,7 @@ struct ValueFormat : HBUINT16
}
bool apply_value (hb_ot_apply_context_t *c,
const void *base,
const ValueBase *base,
const Value *values,
hb_glyph_position_t &glyph_pos) const
{
@ -114,7 +116,7 @@ struct ValueFormat : HBUINT16
if (!use_x_device && !use_y_device) return ret;
const VariationStore &store = c->var_store;
const ItemVariationStore &store = c->var_store;
auto *cache = c->var_store_cache;
/* pixel -> fractional pixel */
@ -142,11 +144,29 @@ struct ValueFormat : HBUINT16
return ret;
}
unsigned int get_effective_format (const Value *values) const
unsigned int get_effective_format (const Value *values, bool strip_hints, bool strip_empty, const ValueBase *base,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
{
unsigned int format = *this;
for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
if (format & flag) should_drop (*values++, (Flags) flag, &format);
if (format & flag)
{
if (strip_hints && flag >= xPlaDevice)
{
format = format & ~flag;
values++;
continue;
}
if (varidx_delta_map && flag >= xPlaDevice)
{
update_var_flag (values++, (Flags) flag, &format, base, varidx_delta_map);
continue;
}
/* do not strip empty when instancing, cause we don't know whether the new
* default value is 0 or not */
if (strip_empty) should_drop (*values, (Flags) flag, &format);
values++;
}
}
return format;
@ -154,18 +174,19 @@ struct ValueFormat : HBUINT16
template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
unsigned int get_effective_format (Iterator it) const {
unsigned int get_effective_format (Iterator it, bool strip_hints, bool strip_empty, const ValueBase *base,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const {
unsigned int new_format = 0;
for (const hb_array_t<const Value>& values : it)
new_format = new_format | get_effective_format (&values);
new_format = new_format | get_effective_format (&values, strip_hints, strip_empty, base, varidx_delta_map);
return new_format;
}
void copy_values (hb_serialize_context_t *c,
unsigned int new_format,
const void *base,
const ValueBase *base,
const Value *values,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
{
@ -217,7 +238,7 @@ struct ValueFormat : HBUINT16
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const void *base,
const ValueBase *base,
const hb_array_t<const Value>& values) const
{
unsigned format = *this;
@ -251,17 +272,8 @@ struct ValueFormat : HBUINT16
}
}
unsigned drop_device_table_flags () const
{
unsigned format = *this;
for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1)
format = format & ~flag;
return format;
}
private:
bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
bool sanitize_value_devices (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
{
unsigned int format = *this;
@ -278,17 +290,17 @@ struct ValueFormat : HBUINT16
return true;
}
static inline Offset16To<Device>& get_device (Value* value)
static inline Offset16To<Device, ValueBase>& get_device (Value* value)
{
return *static_cast<Offset16To<Device> *> (value);
return *static_cast<Offset16To<Device, ValueBase> *> (value);
}
static inline const Offset16To<Device>& get_device (const Value* value)
static inline const Offset16To<Device, ValueBase>& get_device (const Value* value)
{
return *static_cast<const Offset16To<Device> *> (value);
return *static_cast<const Offset16To<Device, ValueBase> *> (value);
}
static inline const Device& get_device (const Value* value,
bool *worked,
const void *base,
const ValueBase *base,
hb_sanitize_context_t &c)
{
if (worked) *worked |= bool (*value);
@ -296,12 +308,13 @@ struct ValueFormat : HBUINT16
if (unlikely (!offset.sanitize (&c, base)))
return Null(Device);
hb_barrier ();
return base + offset;
}
void add_delta_to_value (HBINT16 *value,
const void *base,
const ValueBase *base,
const Value *src_value,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
{
@ -313,7 +326,8 @@ struct ValueFormat : HBUINT16
*value += hb_second (*varidx_delta);
}
bool copy_device (hb_serialize_context_t *c, const void *base,
bool copy_device (hb_serialize_context_t *c,
const ValueBase *base,
const Value *src_value,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
unsigned int new_format, Flags flag) const
@ -354,7 +368,7 @@ struct ValueFormat : HBUINT16
return (format & devices) != 0;
}
bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
bool sanitize_value (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
{
TRACE_SANITIZE (this);
@ -366,7 +380,7 @@ struct ValueFormat : HBUINT16
return_trace (!has_device () || sanitize_value_devices (c, base, values));
}
bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
bool sanitize_values (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count) const
{
TRACE_SANITIZE (this);
unsigned size = get_size ();
@ -376,11 +390,12 @@ struct ValueFormat : HBUINT16
if (c->lazy_some_gpos)
return_trace (true);
hb_barrier ();
return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
}
/* Just sanitize referenced Device tables. Doesn't check the values themselves. */
bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count, unsigned int stride) const
{
TRACE_SANITIZE (this);
@ -403,6 +418,20 @@ struct ValueFormat : HBUINT16
*format = *format & ~flag;
}
void update_var_flag (const Value* value, Flags flag,
unsigned int* format, const ValueBase *base,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
{
if (*value)
{
unsigned varidx = (base + get_device (value)).get_variation_index ();
hb_pair_t<unsigned, int> *varidx_delta;
if (varidx_delta_map->has (varidx, &varidx_delta) &&
varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
return;
}
*format = *format & ~flag;
}
};
}

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

@ -90,8 +90,17 @@ struct Ligature
unsigned int total_component_count = 0;
if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
unsigned match_positions_stack[4];
unsigned *match_positions = match_positions_stack;
if (unlikely (count > ARRAY_LENGTH (match_positions_stack)))
{
match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned));
if (unlikely (!match_positions))
return_trace (false);
}
unsigned int match_end = 0;
unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
if (likely (!match_input (c, count,
&component[1],
@ -102,6 +111,8 @@ struct Ligature
&total_component_count)))
{
c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
if (match_positions != match_positions_stack)
hb_free (match_positions);
return_trace (false);
}
@ -145,6 +156,8 @@ struct Ligature
pos);
}
if (match_positions != match_positions_stack)
hb_free (match_positions);
return_trace (true);
}

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

@ -33,9 +33,11 @@ struct ReverseChainSingleSubstFormat1
TRACE_SANITIZE (this);
if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
return_trace (false);
hb_barrier ();
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
if (!lookahead.sanitize (c, this))
return_trace (false);
hb_barrier ();
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
return_trace (substitute.sanitize (c));
}
@ -109,12 +111,12 @@ struct ReverseChainSingleSubstFormat1
bool apply (hb_ot_apply_context_t *c) const
{
TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
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 */
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);

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 {
@ -38,8 +41,8 @@ struct SmallTypes {
using HBUINT = HBUINT16;
using HBGlyphID = HBGlyphID16;
using Offset = Offset16;
template <typename Type, bool has_null=true>
using OffsetTo = OT::Offset16To<Type, has_null>;
template <typename Type, typename BaseType=void, bool has_null=true>
using OffsetTo = OT::Offset16To<Type, BaseType, has_null>;
template <typename Type>
using ArrayOf = OT::Array16Of<Type>;
template <typename Type>
@ -52,8 +55,8 @@ struct MediumTypes {
using HBUINT = HBUINT24;
using HBGlyphID = HBGlyphID24;
using Offset = Offset24;
template <typename Type, bool has_null=true>
using OffsetTo = OT::Offset24To<Type, has_null>;
template <typename Type, typename BaseType=void, bool has_null=true>
using OffsetTo = OT::Offset24To<Type, BaseType, has_null>;
template <typename Type>
using ArrayOf = OT::Array24Of<Type>;
template <typename Type>

View File

@ -0,0 +1,218 @@
#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"
#include "../../../hb-ot-cff1-table.hh"
#include "coord-setter.hh"
namespace OT {
//namespace Var {
/*
* VARC -- Variable Composites
* https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md
*/
#ifndef HB_NO_VAR_COMPOSITES
struct VarComponent
{
enum class flags_t : uint32_t
{
RESET_UNSPECIFIED_AXES = 1u << 0,
HAVE_AXES = 1u << 1,
AXIS_VALUES_HAVE_VARIATION = 1u << 2,
TRANSFORM_HAS_VARIATION = 1u << 3,
HAVE_TRANSLATE_X = 1u << 4,
HAVE_TRANSLATE_Y = 1u << 5,
HAVE_ROTATION = 1u << 6,
HAVE_CONDITION = 1u << 7,
HAVE_SCALE_X = 1u << 8,
HAVE_SCALE_Y = 1u << 9,
HAVE_TCENTER_X = 1u << 10,
HAVE_TCENTER_Y = 1u << 11,
GID_IS_24BIT = 1u << 12,
HAVE_SKEW_X = 1u << 13,
HAVE_SKEW_Y = 1u << 14,
RESERVED_MASK = ~((1u << 15) - 1),
};
HB_INTERNAL hb_ubytes_t
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 transform,
hb_ubytes_t record,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
hb_glyf_scratch_t &scratch,
VarRegionList::cache_t *cache = nullptr) const;
};
struct VarCompositeGlyph
{
static void
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_ubytes_t record,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
hb_glyf_scratch_t &scratch,
VarRegionList::cache_t *cache = nullptr)
{
while (record)
{
const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
record = comp.get_path_at (font, glyph,
draw_session, coords, transform,
record,
decycler, edges_left, depth_left, scratch, cache);
}
}
};
HB_MARK_AS_FLAG_T (VarComponent::flags_t);
struct VARC
{
friend struct VarComponent;
static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
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_transform_t transform,
hb_codepoint_t parent_glyph,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
hb_glyf_scratch_t &scratch) const;
bool
get_path (hb_font_t *font,
hb_codepoint_t gid,
hb_draw_session_t &draw_session,
hb_glyf_scratch_t &scratch) const
{
hb_decycler_t decycler;
signed edges = HB_MAX_GRAPH_EDGE_COUNT;
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,
scratch);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (version.sanitize (c) &&
hb_barrier () &&
version.major == 1 &&
coverage.sanitize (c, this) &&
varStore.sanitize (c, this) &&
conditionList.sanitize (c, this) &&
axisIndicesList.sanitize (c, this) &&
glyphRecords.sanitize (c, this));
}
struct accelerator_t
{
friend struct VarComponent;
accelerator_t (hb_face_t *face)
{
table = hb_sanitize_context_t ().reference_table<VARC> (face);
}
~accelerator_t ()
{
auto *scratch = cached_scratch.get_relaxed ();
if (scratch)
{
scratch->~hb_glyf_scratch_t ();
hb_free (scratch);
}
table.destroy ();
}
bool
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
{
if (!table->has_data ()) return false;
hb_glyf_scratch_t *scratch;
// Borrow the cached strach buffer.
{
scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
if (unlikely (!scratch))
return true;
}
}
bool ret = table->get_path (font, gid, draw_session, *scratch);
// Put it back.
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_glyf_scratch_t ();
hb_free (scratch);
}
return ret;
}
private:
hb_blob_ptr_t<VARC> table;
hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
};
bool has_data () const { return version.major != 0; }
protected:
FixedVersion<> version; /* Version identifier */
Offset32To<Coverage> coverage;
Offset32To<MultiItemVariationStore> varStore;
Offset32To<ConditionList> conditionList;
Offset32To<TupleList> axisIndicesList;
Offset32To<CFF2Index/*Of<VarCompositeGlyph>*/> glyphRecords;
public:
DEFINE_SIZE_STATIC (24);
};
struct VARC_accelerator_t : VARC::accelerator_t {
VARC_accelerator_t (hb_face_t *face) : VARC::accelerator_t (face) {}
};
#endif
//}
}
#endif /* OT_VAR_VARC_VARC_HH */

View File

@ -0,0 +1,63 @@
#ifndef OT_VAR_VARC_COORD_SETTER_HH
#define OT_VAR_VARC_COORD_SETTER_HH
#include "../../../hb.hh"
namespace OT {
//namespace Var {
struct coord_setter_t
{
coord_setter_t (hb_array_t<const int> coords_)
{
length = coords_.length;
if (length <= ARRAY_LENGTH (static_coords))
hb_memcpy (static_coords, coords_.arrayZ, length * sizeof (int));
else
dynamic_coords.extend (coords_);
}
int& operator [] (unsigned idx)
{
if (unlikely (idx >= HB_VAR_COMPOSITE_MAX_AXES))
return Crap(int);
if (length <= ARRAY_LENGTH (static_coords))
{
if (idx < ARRAY_LENGTH (static_coords))
{
while (length <= idx)
static_coords[length++] = 0;
return static_coords[idx];
}
else
dynamic_coords.extend (hb_array (static_coords, length));
}
if (dynamic_coords.length <= idx)
{
if (unlikely (!dynamic_coords.resize (idx + 1)))
return Crap(int);
length = idx + 1;
}
return dynamic_coords.arrayZ[idx];
}
hb_array_t<int> get_coords ()
{ return length <= ARRAY_LENGTH (static_coords) ? hb_array (static_coords, length) : dynamic_coords.as_array (); }
private:
hb_vector_t<int> dynamic_coords;
unsigned length;
int static_coords[sizeof (void *) * 8];
};
//} // namespace Var
} // namespace OT
#endif /* OT_VAR_VARC_COORD_SETTER_HH */

View File

@ -143,7 +143,7 @@ struct CompositeGlyphRecord
float matrix[4];
contour_point_t trans;
get_transformation (matrix, trans);
if (unlikely (!points.alloc (points.length + 4))) return false; // For phantom points
if (unlikely (!points.alloc (points.length + 1 + 4))) return false; // For phantom points
points.push (trans);
return true;
}
@ -240,7 +240,8 @@ struct CompositeGlyphRecord
}
if (is_anchored ()) tx = ty = 0;
trans.init ((float) tx, (float) ty);
/* set is_end_point flag to true, used by IUP delta optimization */
trans.init ((float) tx, (float) ty, true);
{
const F2DOT14 *points = (const F2DOT14 *) p;

View File

@ -7,8 +7,6 @@
#include "GlyphHeader.hh"
#include "SimpleGlyph.hh"
#include "CompositeGlyph.hh"
#include "VarCompositeGlyph.hh"
#include "coord-setter.hh"
namespace OT {
@ -33,9 +31,6 @@ struct Glyph
EMPTY,
SIMPLE,
COMPOSITE,
#ifndef HB_NO_VAR_COMPOSITES
VAR_COMPOSITE,
#endif
};
public:
@ -44,22 +39,10 @@ struct Glyph
if (type != COMPOSITE) return composite_iter_t ();
return CompositeGlyph (*header, bytes).iter ();
}
var_composite_iter_t get_var_composite_iterator () const
{
#ifndef HB_NO_VAR_COMPOSITES
if (type != VAR_COMPOSITE) return var_composite_iter_t ();
return VarCompositeGlyph (*header, bytes).iter ();
#else
return var_composite_iter_t ();
#endif
}
const hb_bytes_t trim_padding () const
{
switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return VarCompositeGlyph (*header, bytes).trim_padding ();
#endif
case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding ();
case EMPTY: return bytes;
@ -70,9 +53,6 @@ struct Glyph
void drop_hints ()
{
switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return; // No hinting
#endif
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return;
case EMPTY: return;
@ -82,9 +62,6 @@ struct Glyph
void set_overlaps_flag ()
{
switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return; // No overlaps flag
#endif
case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
case EMPTY: return;
@ -94,15 +71,15 @@ struct Glyph
void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
{
switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return; // No hinting
#endif
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
case EMPTY: return;
}
}
bool is_composite () const
{ return type == COMPOSITE; }
bool get_all_points_without_var (const hb_face_t *face,
contour_point_vector_t &points /* OUT */) const
{
@ -117,14 +94,6 @@ struct Glyph
if (unlikely (!item.get_points (points))) return false;
break;
}
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE:
{
for (auto &item : get_var_composite_iterator ())
if (unlikely (!item.get_points (points))) return false;
break;
}
#endif
case EMPTY:
break;
}
@ -282,7 +251,8 @@ struct Glyph
composite_contours_p = nullptr;
}
if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
hb_glyf_scratch_t scratch;
if (!get_points (font, glyf, all_points, scratch, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
return false;
// .notdef, set type to empty so we only update metrics and don't compile bytes for
@ -300,13 +270,6 @@ struct Glyph
{
switch (type)
{
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE:
// TODO
dest_end = hb_bytes_t ();
break;
#endif
case COMPOSITE:
if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
points_with_deltas,
@ -343,27 +306,23 @@ struct Glyph
template <typename accelerator_t>
bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
contour_point_vector_t &all_points /* OUT */,
hb_glyf_scratch_t &scratch,
contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
unsigned *composite_contours = nullptr, /* OUT */
bool shift_points_hori = true,
bool use_my_metrics = true,
bool phantom_only = false,
hb_array_t<int> coords = hb_array_t<int> (),
hb_map_t *current_glyphs = nullptr,
hb_array_t<const int> coords = hb_array_t<const int> (),
unsigned int depth = 0,
unsigned *edge_count = nullptr) const
{
if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
unsigned stack_edge_count = 0;
if (!edge_count) edge_count = &stack_edge_count;
if (unlikely (*edge_count > HB_GLYF_MAX_EDGE_COUNT)) return false;
if (unlikely (*edge_count > HB_MAX_GRAPH_EDGE_COUNT)) return false;
(*edge_count)++;
hb_map_t current_glyphs_stack;
if (current_glyphs == nullptr)
current_glyphs = &current_glyphs_stack;
if (head_maxp_info)
{
head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
@ -372,8 +331,7 @@ struct Glyph
if (!coords)
coords = hb_array (font->coords, font->num_coords);
contour_point_vector_t stack_points;
contour_point_vector_t &points = type == SIMPLE ? all_points : stack_points;
contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
unsigned old_length = points.length;
switch (type) {
@ -391,14 +349,6 @@ struct Glyph
if (unlikely (!item.get_points (points))) return false;
break;
}
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE:
{
for (auto &item : get_var_composite_iterator ())
if (unlikely (!item.get_points (points))) return false;
break;
}
#endif
case EMPTY:
break;
}
@ -434,36 +384,53 @@ struct Glyph
#ifndef HB_NO_VAR
if (coords)
glyf_accelerator.gvar->apply_deltas_to_points (gid,
coords,
points.as_array ().sub_array (old_length),
phantom_only && type == SIMPLE);
{
#ifndef HB_NO_BEYOND_64K
if (glyf_accelerator.GVAR->has_data ())
glyf_accelerator.GVAR->apply_deltas_to_points (gid,
coords,
points.as_array ().sub_array (old_length),
scratch,
phantom_only && type == SIMPLE);
else
#endif
glyf_accelerator.gvar->apply_deltas_to_points (gid,
coords,
points.as_array ().sub_array (old_length),
scratch,
phantom_only && type == SIMPLE);
}
#endif
// mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
// with child glyphs' points
if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
{
if (unlikely (!points_with_deltas->resize (points.length))) return false;
assert (old_length == 0);
*points_with_deltas = points;
}
float shift = 0;
switch (type) {
case SIMPLE:
if (depth == 0 && head_maxp_info)
head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4);
shift = phantoms[PHANTOM_LEFT].x;
break;
case COMPOSITE:
{
hb_decycler_node_t decycler_node (scratch.decycler);
unsigned int comp_index = 0;
for (auto &item : get_composite_iterator ())
{
hb_codepoint_t item_gid = item.get_gid ();
if (unlikely (current_glyphs->has (item_gid)))
if (unlikely (!decycler_node.visit (item_gid)))
{
comp_index++;
continue;
current_glyphs->add (item_gid);
}
unsigned old_count = all_points.length;
@ -472,6 +439,7 @@ struct Glyph
.get_points (font,
glyf_accelerator,
all_points,
scratch,
points_with_deltas,
head_maxp_info,
composite_contours,
@ -479,14 +447,16 @@ struct Glyph
use_my_metrics,
phantom_only,
coords,
current_glyphs,
depth + 1,
edge_count)))
{
current_glyphs->del (item_gid);
points.resize (old_length);
return false;
}
// points might have been reallocated. Relocate phantoms.
phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
auto comp_points = all_points.as_array ().sub_array (old_count);
/* Copy phantom points from component if USE_MY_METRICS flag set */
@ -501,7 +471,7 @@ struct Glyph
item.get_transformation (matrix, default_trans);
/* Apply component transformation & translation (with deltas applied) */
item.transform_points (comp_points, matrix, points[comp_index]);
item.transform_points (comp_points, matrix, points[old_length + comp_index]);
}
if (item.is_anchored () && !phantom_only)
@ -522,12 +492,11 @@ struct Glyph
if (all_points.length > HB_GLYF_MAX_POINTS)
{
current_glyphs->del (item_gid);
points.resize (old_length);
return false;
}
comp_index++;
current_glyphs->del (item_gid);
}
if (head_maxp_info && depth == 0)
@ -538,84 +507,13 @@ struct Glyph
head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
}
all_points.extend (phantoms);
shift = phantoms[PHANTOM_LEFT].x;
points.resize (old_length);
} break;
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE:
{
hb_array_t<contour_point_t> points_left = points.as_array ();
for (auto &item : get_var_composite_iterator ())
{
hb_codepoint_t item_gid = item.get_gid ();
if (unlikely (current_glyphs->has (item_gid)))
continue;
current_glyphs->add (item_gid);
unsigned item_num_points = item.get_num_points ();
hb_array_t<contour_point_t> record_points = points_left.sub_array (0, item_num_points);
assert (record_points.length == item_num_points);
auto component_coords = coords;
/* Copying coords is expensive; so we have put an arbitrary
* limit on the max number of coords for now. */
if (item.is_reset_unspecified_axes () ||
coords.length > HB_GLYF_VAR_COMPOSITE_MAX_AXES)
component_coords = hb_array<int> ();
coord_setter_t coord_setter (component_coords);
item.set_variations (coord_setter, record_points);
unsigned old_count = all_points.length;
if (unlikely ((!phantom_only || (use_my_metrics && item.is_use_my_metrics ())) &&
!glyf_accelerator.glyph_for_gid (item_gid)
.get_points (font,
glyf_accelerator,
all_points,
points_with_deltas,
head_maxp_info,
nullptr,
shift_points_hori,
use_my_metrics,
phantom_only,
coord_setter.get_coords (),
current_glyphs,
depth + 1,
edge_count)))
{
current_glyphs->del (item_gid);
return false;
}
auto comp_points = all_points.as_array ().sub_array (old_count);
/* Apply component transformation */
if (comp_points) // Empty in case of phantom_only
item.transform_points (record_points, comp_points);
/* Copy phantom points from component if USE_MY_METRICS flag set */
if (use_my_metrics && item.is_use_my_metrics ())
for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
all_points.resize (all_points.length - PHANTOM_COUNT);
if (all_points.length > HB_GLYF_MAX_POINTS)
{
current_glyphs->del (item_gid);
return false;
}
points_left += item_num_points;
current_glyphs->del (item_gid);
}
all_points.extend (phantoms);
} break;
#endif
case EMPTY:
all_points.extend (phantoms);
shift = phantoms[PHANTOM_LEFT].x;
points.resize (old_length);
break;
}
@ -624,10 +522,9 @@ struct Glyph
/* Undocumented rasterizer behavior:
* Shift points horizontally by the updated left side bearing
*/
int v = -phantoms[PHANTOM_LEFT].x;
if (v)
if (shift)
for (auto &point : all_points)
point.x += v;
point.x -= shift;
}
return !all_points.in_error ();
@ -658,10 +555,7 @@ struct Glyph
int num_contours = header->numberOfContours;
if (unlikely (num_contours == 0)) type = EMPTY;
else if (num_contours > 0) type = SIMPLE;
else if (num_contours == -1) type = COMPOSITE;
#ifndef HB_NO_VAR_COMPOSITES
else if (num_contours == -2) type = VAR_COMPOSITE;
#endif
else if (num_contours <= -1) type = COMPOSITE;
else type = EMPTY; // Spec deviation; Spec says COMPOSITE, but not seen in the wild.
}

View File

@ -127,19 +127,20 @@ struct SimpleGlyph
hb_array_t<contour_point_t> points_ /* IN/OUT */,
const HBUINT8 *end)
{
auto *points = points_.arrayZ;
unsigned count = points_.length;
for (unsigned int i = 0; i < count;)
{
if (unlikely (p + 1 > end)) return false;
uint8_t flag = *p++;
points_.arrayZ[i++].flag = flag;
points[i++].flag = flag;
if (flag & FLAG_REPEAT)
{
if (unlikely (p + 1 > end)) return false;
unsigned int repeat_count = *p++;
unsigned stop = hb_min (i + repeat_count, count);
for (; i < stop; i++)
points_.arrayZ[i].flag = flag;
points[i].flag = flag;
}
}
return true;
@ -160,10 +161,7 @@ struct SimpleGlyph
if (flag & short_flag)
{
if (unlikely (p + 1 > end)) return false;
if (flag & same_flag)
v += *p++;
else
v -= *p++;
v += (bool(flag & same_flag) * 2 - 1) * *p++;
}
else
{
@ -190,7 +188,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 (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 +279,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

@ -53,23 +53,12 @@ struct SubsetGlyph
if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
const_cast<CompositeGlyphRecord &> (_).set_gid (new_gid);
}
#ifndef HB_NO_VAR_COMPOSITES
for (auto &_ : Glyph (dest_glyph).get_var_composite_iterator ())
{
hb_codepoint_t new_gid;
if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
const_cast<VarCompositeGlyphRecord &> (_).set_gid (new_gid);
}
#endif
#ifndef HB_NO_BEYOND_64K
auto it = Glyph (dest_glyph).get_composite_iterator ();
if (it)
{
/* lower GID24 to GID16 in components if possible.
*
* TODO: VarComposite. Not as critical, since VarComposite supports
* gid24 from the first version. */
/* lower GID24 to GID16 in components if possible. */
char *p = it ? (char *) &*it : nullptr;
char *q = p;
const char *end = dest_glyph.arrayZ + dest_glyph.length;

View File

@ -1,401 +0,0 @@
#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH
#define OT_GLYF_VARCOMPOSITEGLYPH_HH
#include "../../hb-open-type.hh"
#include "coord-setter.hh"
namespace OT {
namespace glyf_impl {
struct VarCompositeGlyphRecord
{
protected:
enum var_composite_glyph_flag_t
{
USE_MY_METRICS = 0x0001,
AXIS_INDICES_ARE_SHORT = 0x0002,
UNIFORM_SCALE = 0x0004,
HAVE_TRANSLATE_X = 0x0008,
HAVE_TRANSLATE_Y = 0x0010,
HAVE_ROTATION = 0x0020,
HAVE_SCALE_X = 0x0040,
HAVE_SCALE_Y = 0x0080,
HAVE_SKEW_X = 0x0100,
HAVE_SKEW_Y = 0x0200,
HAVE_TCENTER_X = 0x0400,
HAVE_TCENTER_Y = 0x0800,
GID_IS_24BIT = 0x1000,
AXES_HAVE_VARIATION = 0x2000,
RESET_UNSPECIFIED_AXES = 0x4000,
};
public:
unsigned int get_size () const
{
unsigned fl = flags;
unsigned int size = min_size;
unsigned axis_width = (fl & AXIS_INDICES_ARE_SHORT) ? 4 : 3;
size += numAxes * axis_width;
if (fl & GID_IS_24BIT) size += 1;
// 2 bytes each for the following flags
fl = fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y |
HAVE_ROTATION |
HAVE_SCALE_X | HAVE_SCALE_Y |
HAVE_SKEW_X | HAVE_SKEW_Y |
HAVE_TCENTER_X | HAVE_TCENTER_Y);
size += hb_popcount (fl) * 2;
return size;
}
bool has_more () const { return true; }
bool is_use_my_metrics () const { return flags & USE_MY_METRICS; }
bool is_reset_unspecified_axes () const { return flags & RESET_UNSPECIFIED_AXES; }
hb_codepoint_t get_gid () const
{
if (flags & GID_IS_24BIT)
return * (const HBGlyphID24 *) &pad;
else
return * (const HBGlyphID16 *) &pad;
}
void set_gid (hb_codepoint_t gid)
{
if (flags & GID_IS_24BIT)
* (HBGlyphID24 *) &pad = gid;
else
* (HBGlyphID16 *) &pad = gid;
}
unsigned get_numAxes () const
{
return numAxes;
}
unsigned get_num_points () const
{
unsigned fl = flags;
unsigned num = 0;
if (fl & AXES_HAVE_VARIATION) num += numAxes;
/* Hopefully faster code, relying on the value of the flags. */
fl = (((fl & (HAVE_TRANSLATE_Y | HAVE_SCALE_Y | HAVE_SKEW_Y | HAVE_TCENTER_Y)) >> 1) | fl) &
(HAVE_TRANSLATE_X | HAVE_ROTATION | HAVE_SCALE_X | HAVE_SKEW_X | HAVE_TCENTER_X);
num += hb_popcount (fl);
return num;
/* Slower but more readable code. */
if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) num++;
if (fl & HAVE_ROTATION) num++;
if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y)) num++;
if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y)) num++;
if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) num++;
return num;
}
void transform_points (hb_array_t<const contour_point_t> record_points,
hb_array_t<contour_point_t> points) const
{
float matrix[4];
contour_point_t trans;
get_transformation_from_points (record_points.arrayZ, matrix, trans);
auto arrayZ = points.arrayZ;
unsigned count = points.length;
if (matrix[0] != 1.f || matrix[1] != 0.f ||
matrix[2] != 0.f || matrix[3] != 1.f)
for (unsigned i = 0; i < count; i++)
arrayZ[i].transform (matrix);
if (trans.x != 0.f || trans.y != 0.f)
for (unsigned i = 0; i < count; i++)
arrayZ[i].translate (trans);
}
static inline void transform (float (&matrix)[4], contour_point_t &trans,
float (other)[6])
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268
float xx1 = other[0];
float xy1 = other[1];
float yx1 = other[2];
float yy1 = other[3];
float dx1 = other[4];
float dy1 = other[5];
float xx2 = matrix[0];
float xy2 = matrix[1];
float yx2 = matrix[2];
float yy2 = matrix[3];
float dx2 = trans.x;
float dy2 = trans.y;
matrix[0] = xx1*xx2 + xy1*yx2;
matrix[1] = xx1*xy2 + xy1*yy2;
matrix[2] = yx1*xx2 + yy1*yx2;
matrix[3] = yx1*xy2 + yy1*yy2;
trans.x = xx2*dx1 + yx2*dy1 + dx2;
trans.y = xy2*dx1 + yy2*dy1 + dy2;
}
static void translate (float (&matrix)[4], contour_point_t &trans,
float translateX, float translateY)
{
if (!translateX && !translateY)
return;
trans.x += matrix[0] * translateX + matrix[2] * translateY;
trans.y += matrix[1] * translateX + matrix[3] * translateY;
}
static void scale (float (&matrix)[4], contour_point_t &trans,
float scaleX, float scaleY)
{
if (scaleX == 1.f && scaleY == 1.f)
return;
matrix[0] *= scaleX;
matrix[1] *= scaleX;
matrix[2] *= scaleY;
matrix[3] *= scaleY;
}
static void rotate (float (&matrix)[4], contour_point_t &trans,
float rotation)
{
if (!rotation)
return;
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
rotation = rotation * HB_PI;
float c;
float s;
#ifdef HAVE_SINCOSF
sincosf (rotation, &s, &c);
#else
c = cosf (rotation);
s = sinf (rotation);
#endif
float other[6] = {c, s, -s, c, 0.f, 0.f};
transform (matrix, trans, other);
}
static void skew (float (&matrix)[4], contour_point_t &trans,
float skewX, float skewY)
{
if (!skewX && !skewY)
return;
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
skewX = skewX * HB_PI;
skewY = skewY * HB_PI;
float other[6] = {1.f,
skewY ? tanf (skewY) : 0.f,
skewX ? tanf (skewX) : 0.f,
1.f,
0.f, 0.f};
transform (matrix, trans, other);
}
bool get_points (contour_point_vector_t &points) const
{
unsigned num_points = get_num_points ();
points.alloc (points.length + num_points + 4); // For phantom points
if (unlikely (!points.resize (points.length + num_points, false))) return false;
contour_point_t *rec_points = points.arrayZ + (points.length - num_points);
hb_memset (rec_points, 0, num_points * sizeof (rec_points[0]));
unsigned fl = flags;
unsigned num_axes = numAxes;
unsigned axis_width = (fl & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
unsigned axes_size = num_axes * axis_width;
const F2DOT14 *q = (const F2DOT14 *) (axes_size +
(fl & GID_IS_24BIT ? 3 : 2) +
(const HBUINT8 *) &pad);
unsigned count = num_axes;
if (fl & AXES_HAVE_VARIATION)
{
for (unsigned i = 0; i < count; i++)
rec_points++->x = q++->to_int ();
}
else
q += count;
const HBUINT16 *p = (const HBUINT16 *) q;
if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
{
int translateX = (fl & HAVE_TRANSLATE_X) ? * (const FWORD *) p++ : 0;
int translateY = (fl & HAVE_TRANSLATE_Y) ? * (const FWORD *) p++ : 0;
rec_points->x = translateX;
rec_points->y = translateY;
rec_points++;
}
if (fl & HAVE_ROTATION)
{
int rotation = (fl & HAVE_ROTATION) ? ((const F4DOT12 *) p++)->to_int () : 0;
rec_points->x = rotation;
rec_points++;
}
if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y))
{
int scaleX = (fl & HAVE_SCALE_X) ? ((const F6DOT10 *) p++)->to_int () : 1 << 10;
int scaleY = (fl & HAVE_SCALE_Y) ? ((const F6DOT10 *) p++)->to_int () : 1 << 10;
if ((fl & UNIFORM_SCALE) && !(fl & HAVE_SCALE_Y))
scaleY = scaleX;
rec_points->x = scaleX;
rec_points->y = scaleY;
rec_points++;
}
if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y))
{
int skewX = (fl & HAVE_SKEW_X) ? ((const F4DOT12 *) p++)->to_int () : 0;
int skewY = (fl & HAVE_SKEW_Y) ? ((const F4DOT12 *) p++)->to_int () : 0;
rec_points->x = skewX;
rec_points->y = skewY;
rec_points++;
}
if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
{
int tCenterX = (fl & HAVE_TCENTER_X) ? * (const FWORD *) p++ : 0;
int tCenterY = (fl & HAVE_TCENTER_Y) ? * (const FWORD *) p++ : 0;
rec_points->x = tCenterX;
rec_points->y = tCenterY;
rec_points++;
}
return true;
}
void get_transformation_from_points (const contour_point_t *rec_points,
float (&matrix)[4], contour_point_t &trans) const
{
unsigned fl = flags;
if (fl & AXES_HAVE_VARIATION)
rec_points += numAxes;
matrix[0] = matrix[3] = 1.f;
matrix[1] = matrix[2] = 0.f;
trans.init (0.f, 0.f);
float translateX = 0.f;
float translateY = 0.f;
float rotation = 0.f;
float scaleX = 1.f;
float scaleY = 1.f;
float skewX = 0.f;
float skewY = 0.f;
float tCenterX = 0.f;
float tCenterY = 0.f;
if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
{
translateX = rec_points->x;
translateY = rec_points->y;
rec_points++;
}
if (fl & HAVE_ROTATION)
{
rotation = rec_points->x / (1 << 12);
rec_points++;
}
if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y))
{
scaleX = rec_points->x / (1 << 10);
scaleY = rec_points->y / (1 << 10);
rec_points++;
}
if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y))
{
skewX = rec_points->x / (1 << 12);
skewY = rec_points->y / (1 << 12);
rec_points++;
}
if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
{
tCenterX = rec_points->x;
tCenterY = rec_points->y;
rec_points++;
}
translate (matrix, trans, translateX + tCenterX, translateY + tCenterY);
rotate (matrix, trans, rotation);
scale (matrix, trans, scaleX, scaleY);
skew (matrix, trans, -skewX, skewY);
translate (matrix, trans, -tCenterX, -tCenterY);
}
void set_variations (coord_setter_t &setter,
hb_array_t<contour_point_t> rec_points) const
{
bool have_variations = flags & AXES_HAVE_VARIATION;
unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
unsigned num_axes = numAxes;
const HBUINT8 *p = (const HBUINT8 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2));
const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2));
const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + num_axes) : (HBUINT8 *) (q + num_axes)));
unsigned count = num_axes;
for (unsigned i = 0; i < count; i++)
{
unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++;
signed v = have_variations ? rec_points.arrayZ[i].x : a++->to_int ();
v = hb_clamp (v, -(1<<14), (1<<14));
setter[axis_index] = v;
}
}
protected:
HBUINT16 flags;
HBUINT8 numAxes;
HBUINT16 pad;
public:
DEFINE_SIZE_MIN (5);
};
using var_composite_iter_t = composite_iter_tmpl<VarCompositeGlyphRecord>;
struct VarCompositeGlyph
{
const GlyphHeader &header;
hb_bytes_t bytes;
VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
header (header_), bytes (bytes_) {}
var_composite_iter_t iter () const
{ return var_composite_iter_t (bytes, &StructAfter<VarCompositeGlyphRecord, GlyphHeader> (header)); }
const hb_bytes_t trim_padding () const
{
unsigned length = GlyphHeader::static_size;
for (auto &comp : iter ())
length += comp.get_size ();
return bytes.sub_array (0, length);
}
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */

View File

@ -1,36 +0,0 @@
#ifndef OT_GLYF_COORD_SETTER_HH
#define OT_GLYF_COORD_SETTER_HH
#include "../../hb.hh"
namespace OT {
namespace glyf_impl {
struct coord_setter_t
{
coord_setter_t (hb_array_t<int> coords) :
coords (coords) {}
int& operator [] (unsigned idx)
{
if (unlikely (idx >= HB_GLYF_VAR_COMPOSITE_MAX_AXES))
return Crap(int);
if (coords.length < idx + 1)
coords.resize (idx + 1);
return coords[idx];
}
hb_array_t<int> get_coords ()
{ return coords.as_array (); }
hb_vector_t<int> coords;
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_COORD_SETTER_HH */

View File

@ -38,7 +38,7 @@ _write_loca (IteratorIn&& it,
unsigned padded_size = *it++;
offset += padded_size;
DEBUG_MSG (SUBSET, nullptr, "loca entry gid %u offset %u padded-size %u", gid, offset, padded_size);
DEBUG_MSG (SUBSET, nullptr, "loca entry gid %" PRIu32 " offset %u padded-size %u", gid, offset, padded_size);
value = offset >> right_shift;
*dest++ = value;

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;
@ -172,6 +172,9 @@ struct glyf_accelerator_t
glyf_table = nullptr;
#ifndef HB_NO_VAR
gvar = nullptr;
#ifndef HB_NO_BEYOND_64K
GVAR = nullptr;
#endif
#endif
hmtx = nullptr;
#ifndef HB_NO_VERTICAL
@ -187,6 +190,9 @@ struct glyf_accelerator_t
glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
#ifndef HB_NO_VAR
gvar = face->table.gvar;
#ifndef HB_NO_BEYOND_64K
GVAR = face->table.GVAR;
#endif
#endif
hmtx = face->table.hmtx;
#ifndef HB_NO_VERTICAL
@ -198,6 +204,13 @@ struct glyf_accelerator_t
}
~glyf_accelerator_t ()
{
auto *scratch = cached_scratch.get_relaxed ();
if (scratch)
{
scratch->~hb_glyf_scratch_t ();
hb_free (scratch);
}
glyf_table.destroy ();
}
@ -205,18 +218,17 @@ struct glyf_accelerator_t
protected:
template<typename T>
bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const
bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
hb_array_t<const int> coords,
hb_glyf_scratch_t &scratch) const
{
if (gid >= num_glyphs) return false;
/* Making this allocfree is not that easy
https://github.com/harfbuzz/harfbuzz/issues/2095
mostly because of gvar handling in VF fonts,
perhaps a separate path for non-VF fonts can be considered */
contour_point_vector_t all_points;
auto &all_points = scratch.all_points;
all_points.resize (0);
bool phantom_only = !consumer.is_consuming_contour_points ();
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only)))
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords)))
return false;
unsigned count = all_points.length;
@ -225,8 +237,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 ();
}
@ -299,6 +364,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; }
@ -314,7 +380,12 @@ struct glyf_accelerator_t
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
if (font->num_coords)
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false));
{
hb_glyf_scratch_t scratch;
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
hb_array (font->coords, font->num_coords),
scratch);
}
if (unlikely (!success))
return
@ -334,9 +405,11 @@ struct glyf_accelerator_t
if (unlikely (gid >= num_glyphs)) return false;
hb_glyph_extents_t extents;
hb_glyf_scratch_t scratch;
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false))))
if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false),
hb_array (font->coords, font->num_coords),
scratch)))
return false;
*lsb = is_vertical
@ -362,20 +435,16 @@ struct glyf_accelerator_t
#ifndef HB_NO_VAR
if (font->num_coords)
return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true));
{
hb_glyf_scratch_t scratch;
return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true),
hb_array (font->coords, font->num_coords),
scratch);
}
#endif
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
{
@ -406,10 +475,52 @@ struct glyf_accelerator_t
bool
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
{ return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); }
{
if (!has_data ()) return false;
hb_glyf_scratch_t *scratch;
// Borrow the cached strach buffer.
{
scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
if (unlikely (!scratch))
return true;
}
}
bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
hb_array (font->coords, font->num_coords),
*scratch);
// Put it back.
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_glyf_scratch_t ();
hb_free (scratch);
}
return ret;
}
bool
get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_glyf_scratch_t &scratch) const
{
if (!has_data ()) return false;
return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
coords,
scratch);
}
#ifndef HB_NO_VAR
const gvar_accelerator_t *gvar;
#ifndef HB_NO_BEYOND_64K
const GVAR_accelerator_t *GVAR;
#endif
#endif
const hmtx_accelerator_t *hmtx;
#ifndef HB_NO_VERTICAL
@ -421,6 +532,7 @@ struct glyf_accelerator_t
unsigned int num_glyphs;
hb_blob_ptr_t<loca> loca_table;
hb_blob_ptr_t<glyf> glyf_table;
hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
};
@ -430,7 +542,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

@ -42,7 +42,7 @@ struct path_builder_t
{
bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
#ifdef HB_NO_CUBIC_GLYF
bool is_cubic = false;
constexpr bool is_cubic = false;
#else
bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
#endif
@ -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

@ -242,7 +242,9 @@ struct NameRecord
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && offset.sanitize (c, base, length));
return_trace (c->check_struct (this) &&
hb_barrier () &&
offset.sanitize (c, base, length));
}
HBUINT16 platformID; /* Platform ID. */
@ -465,6 +467,7 @@ struct name
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
likely (format == 0 || format == 1) &&
c->check_array (nameRecordZ.arrayZ, count) &&
c->check_range (this, stringOffset) &&
@ -482,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

@ -53,9 +53,9 @@ STEP 2: BUILD CHANGES INCREMENTALLY
STEP 3: COMPILER WARNINGS AND SETTING FLAGS
-------------------------------------------
- Update make parameters in Awt2DLibraries.gmk
- Update make parameters in make/modules/java.desktop/lib/ClientLibraries.gmk
Since we don't use configure we need to manually specify the options
we need in the Harfbuzz section of Awt2DLibraries.gmk.
we need in the Harfbuzz section of ClientLibraries.gmk.
As well as adding new options, we may need to clean up obsolete options.
Note there may be platform variations in the flags.

View File

@ -39,6 +39,7 @@ struct ClassDefFormat1 : public OT::ClassDefFormat1_3<SmallTypes>
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::ClassDefFormat1_3<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size ();
}
};
@ -50,6 +51,7 @@ struct ClassDefFormat2 : public OT::ClassDefFormat2_4<SmallTypes>
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::ClassDefFormat2_4<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
}
};
@ -114,6 +116,7 @@ struct ClassDef : public OT::ClassDef
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::ClassDef::min_size) return false;
hb_barrier ();
switch (u.format)
{
case 1: return ((ClassDefFormat1*)this)->sanitize (vertex);
@ -131,20 +134,23 @@ struct ClassDef : public OT::ClassDef
struct class_def_size_estimator_t
{
// TODO(garretrieger): update to support beyond64k coverage/classdef tables.
constexpr static unsigned class_def_format1_base_size = 6;
constexpr static unsigned class_def_format2_base_size = 4;
constexpr static unsigned coverage_base_size = 4;
constexpr static unsigned bytes_per_range = 6;
constexpr static unsigned bytes_per_glyph = 2;
template<typename It>
class_def_size_estimator_t (It glyph_and_class)
: gids_consecutive (true), num_ranges_per_class (), glyphs_per_class ()
: num_ranges_per_class (), glyphs_per_class ()
{
unsigned last_gid = (unsigned) -1;
reset();
for (auto p : + glyph_and_class)
{
unsigned gid = p.first;
unsigned klass = p.second;
if (last_gid != (unsigned) -1 && gid != last_gid + 1)
gids_consecutive = false;
last_gid = gid;
hb_set_t* glyphs;
if (glyphs_per_class.has (klass, &glyphs) && glyphs) {
glyphs->add (gid);
@ -174,28 +180,54 @@ struct class_def_size_estimator_t
}
}
// Incremental increase in the Coverage and ClassDef table size
// (worst case) if all glyphs associated with 'klass' were added.
unsigned incremental_coverage_size (unsigned klass) const
{
// Coverage takes 2 bytes per glyph worst case,
return 2 * glyphs_per_class.get (klass).get_population ();
void reset() {
class_def_1_size = class_def_format1_base_size;
class_def_2_size = class_def_format2_base_size;
included_glyphs.clear();
included_classes.clear();
}
// Incremental increase in the Coverage and ClassDef table size
// (worst case) if all glyphs associated with 'klass' were added.
unsigned incremental_class_def_size (unsigned klass) const
// Compute the size of coverage for all glyphs added via 'add_class_def_size'.
unsigned coverage_size () const
{
// ClassDef takes 6 bytes per range
unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass);
if (gids_consecutive)
{
// ClassDef1 takes 2 bytes per glyph, but only can be used
// when gids are consecutive.
return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size);
unsigned format1_size = coverage_base_size + bytes_per_glyph * included_glyphs.get_population();
unsigned format2_size = coverage_base_size + bytes_per_range * num_glyph_ranges();
return hb_min(format1_size, format2_size);
}
// Compute the new size of the ClassDef table if all glyphs associated with 'klass' were added.
unsigned add_class_def_size (unsigned klass)
{
if (!included_classes.has(klass)) {
hb_set_t* glyphs = nullptr;
if (glyphs_per_class.has(klass, &glyphs)) {
included_glyphs.union_(*glyphs);
}
class_def_1_size = class_def_format1_base_size;
if (!included_glyphs.is_empty()) {
unsigned min_glyph = included_glyphs.get_min();
unsigned max_glyph = included_glyphs.get_max();
class_def_1_size += bytes_per_glyph * (max_glyph - min_glyph + 1);
}
class_def_2_size += bytes_per_range * num_ranges_per_class.get (klass);
included_classes.add(klass);
}
return class_def_2_size;
return hb_min (class_def_1_size, class_def_2_size);
}
unsigned num_glyph_ranges() const {
hb_codepoint_t start = HB_SET_VALUE_INVALID;
hb_codepoint_t end = HB_SET_VALUE_INVALID;
unsigned count = 0;
while (included_glyphs.next_range (&start, &end)) {
count++;
}
return count;
}
bool in_error ()
@ -211,9 +243,12 @@ struct class_def_size_estimator_t
}
private:
bool gids_consecutive;
hb_hashmap_t<unsigned, unsigned> num_ranges_per_class;
hb_hashmap_t<unsigned, hb_set_t> glyphs_per_class;
hb_set_t included_classes;
hb_set_t included_glyphs;
unsigned class_def_1_size;
unsigned class_def_2_size;
};

View File

@ -39,6 +39,7 @@ struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3<SmallTypes
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size ();
}
};
@ -50,6 +51,7 @@ struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4<SmallTypes
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
}
};
@ -138,6 +140,7 @@ struct Coverage : public OT::Layout::Common::Coverage
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::Layout::Common::Coverage::min_size) return false;
hb_barrier ();
switch (u.format)
{
case 1: return ((CoverageFormat1*)this)->sanitize (vertex);

View File

@ -195,6 +195,15 @@ struct graph_t
return incoming_edges_;
}
unsigned incoming_edges_from_parent (unsigned parent_index) const {
if (single_parent != (unsigned) -1) {
return single_parent == parent_index ? 1 : 0;
}
unsigned* count;
return parents.has(parent_index, &count) ? *count : 0;
}
void reset_parents ()
{
incoming_edges_ = 0;
@ -334,6 +343,16 @@ struct graph_t
return true;
}
bool give_max_priority ()
{
bool result = false;
while (!has_max_priority()) {
result = true;
priority++;
}
return result;
}
bool has_max_priority () const {
return priority >= 3;
}
@ -349,7 +368,7 @@ struct graph_t
// it's parent where possible.
int64_t modified_distance =
hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF);
hb_clamp (distance + distance_modifier (), (int64_t) 0, 0x7FFFFFFFFFF);
if (has_max_priority ()) {
modified_distance = 0;
}
@ -567,6 +586,7 @@ struct graph_t
update_distances ();
hb_priority_queue_t<int64_t> queue;
queue.alloc (vertices_.length);
hb_vector_t<vertex_t> &sorted_graph = vertices_scratch_;
if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return;
hb_vector_t<unsigned> id_map;
@ -1022,6 +1042,11 @@ struct graph_t
* Creates a copy of child and re-assigns the link from
* parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*
* Returns the index of the newly created duplicate.
*
* If the child_idx only has incoming edges from parent_idx, this
* will do nothing and return the original child_idx.
*/
unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx)
{
@ -1035,18 +1060,20 @@ struct graph_t
* Creates a copy of child and re-assigns the link from
* parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*
* Returns the index of the newly created duplicate.
*
* If the child_idx only has incoming edges from parent_idx,
* duplication isn't possible and this will return -1.
*/
unsigned duplicate (unsigned parent_idx, unsigned child_idx)
{
update_parents ();
unsigned links_to_child = 0;
for (const auto& l : vertices_[parent_idx].obj.all_links ())
{
if (l.objidx == child_idx) links_to_child++;
}
const auto& child = vertices_[child_idx];
unsigned links_to_child = child.incoming_edges_from_parent(parent_idx);
if (vertices_[child_idx].incoming_edges () <= links_to_child)
if (child.incoming_edges () <= links_to_child)
{
// Can't duplicate this node, doing so would orphan the original one as all remaining links
// to child are from parent.
@ -1059,7 +1086,7 @@ struct graph_t
parent_idx, child_idx);
unsigned clone_idx = duplicate (child_idx);
if (clone_idx == (unsigned) -1) return false;
if (clone_idx == (unsigned) -1) return -1;
// duplicate shifts the root node idx, so if parent_idx was root update it.
if (parent_idx == clone_idx) parent_idx++;
@ -1075,6 +1102,62 @@ struct graph_t
return clone_idx;
}
/*
* Creates a copy of child and re-assigns the links from
* parents to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*
* Returns the index of the newly created duplicate.
*
* If the child_idx only has incoming edges from parents,
* duplication isn't possible or duplication fails and this will
* return -1.
*/
unsigned duplicate (const hb_set_t* parents, unsigned child_idx)
{
if (parents->is_empty()) {
return -1;
}
update_parents ();
const auto& child = vertices_[child_idx];
unsigned links_to_child = 0;
unsigned last_parent = parents->get_max();
unsigned first_parent = parents->get_min();
for (unsigned parent_idx : *parents) {
links_to_child += child.incoming_edges_from_parent(parent_idx);
}
if (child.incoming_edges () <= links_to_child)
{
// Can't duplicate this node, doing so would orphan the original one as all remaining links
// to child are from parent.
DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
return -1;
}
DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
unsigned clone_idx = duplicate (child_idx);
if (clone_idx == (unsigned) -1) return false;
for (unsigned parent_idx : *parents) {
// duplicate shifts the root node idx, so if parent_idx was root update it.
if (parent_idx == clone_idx) parent_idx++;
auto& parent = vertices_[parent_idx];
for (auto& l : parent.obj.all_links_writer ())
{
if (l.objidx != child_idx)
continue;
reassign_link (l, parent_idx, clone_idx);
}
}
return clone_idx;
}
/*
* Adds a new node to the graph, not connected to anything.
@ -1370,6 +1453,7 @@ struct graph_t
vertices_.tail ().distance = 0;
hb_priority_queue_t<int64_t> queue;
queue.alloc (count);
queue.insert (0, vertices_.length - 1);
hb_vector_t<bool> visited;

View File

@ -76,6 +76,7 @@ struct Lookup : public OT::Lookup
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::Lookup::min_size) return false;
hb_barrier ();
return vertex_len >= this->get_size ();
}
@ -351,6 +352,7 @@ struct LookupList : public OT::LookupList<T>
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::LookupList<T>::min_size) return false;
hb_barrier ();
return vertex_len >= OT::LookupList<T>::item_size * this->len;
}
};
@ -364,6 +366,7 @@ struct GSTAR : public OT::GSUBGPOS
GSTAR* gstar = (GSTAR*) r.obj.head;
if (!gstar || !gstar->sanitize (r))
return nullptr;
hb_barrier ();
return gstar;
}
@ -383,6 +386,7 @@ struct GSTAR : public OT::GSUBGPOS
{
int64_t len = vertex.obj.tail - vertex.obj.head;
if (len < OT::GSUBGPOS::min_size) return false;
hb_barrier ();
return len >= get_size ();
}

View File

@ -40,6 +40,7 @@ struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < AnchorMatrix::min_size) return false;
hb_barrier ();
return vertex_len >= AnchorMatrix::min_size +
OT::Offset16::static_size * class_count * this->rows;
@ -128,6 +129,7 @@ struct MarkArray : public OT::Layout::GPOS_impl::MarkArray
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
unsigned min_size = MarkArray::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
return vertex_len >= get_size ();
}
@ -495,6 +497,7 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < u.format.get_size ()) return false;
hb_barrier ();
switch (u.format) {
case 1:

View File

@ -42,6 +42,7 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
return vertex_len >=
min_size + pairSet.get_size () - pairSet.len.get_size();
@ -198,6 +199,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
size_t vertex_len = vertex.table_size ();
unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
const unsigned class1_count = class1Count;
return vertex_len >=
@ -245,8 +247,8 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
for (unsigned i = 0; i < class1_count; i++)
{
unsigned accumulated_delta = class1_record_size;
coverage_size += estimator.incremental_coverage_size (i);
class_def_1_size += estimator.incremental_class_def_size (i);
class_def_1_size = estimator.add_class_def_size (i);
coverage_size = estimator.coverage_size ();
max_coverage_size = hb_max (max_coverage_size, coverage_size);
max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size);
@ -278,8 +280,10 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
split_points.push (i);
// split does not include i, so add the size for i when we reset the size counters.
accumulated = base_size + accumulated_delta;
coverage_size = 4 + estimator.incremental_coverage_size (i);
class_def_1_size = 4 + estimator.incremental_class_def_size (i);
estimator.reset();
class_def_1_size = estimator.add_class_def_size(i);
coverage_size = estimator.coverage_size();
visited.clear (); // node sharing isn't allowed between splits.
}
}
@ -625,6 +629,7 @@ struct PairPos : public OT::Layout::GPOS_impl::PairPos
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < u.format.get_size ()) return false;
hb_barrier ();
switch (u.format) {
case 1:

View File

@ -75,6 +75,7 @@ struct ankr
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
version == 0 &&
c->check_range (this, anchorData) &&
lookupTable.sanitize (c, this, &(this+anchorData))));

View File

@ -123,6 +123,7 @@ struct bsln
TRACE_SANITIZE (this);
if (unlikely (!(c->check_struct (this) && defaultBaseline < 32)))
return_trace (false);
hb_barrier ();
switch (format)
{

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,15 +43,18 @@ namespace AAT {
using namespace OT;
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>
{
const char *get_name () { return "APPLY"; }
template <typename T>
return_t dispatch (const T &obj) { return obj.apply (this); }
template <typename T, typename ...Ts>
return_t dispatch (const T &obj, Ts&&... ds)
{ return obj.apply (this, std::forward<Ts> (ds)...); }
static return_t default_return_value () { return false; }
bool stop_sublookup_iteration (return_t r) const { return r; }
@ -59,6 +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;
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. */
@ -74,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;
}
};
@ -81,6 +113,8 @@ struct hb_aat_apply_context_t :
* Lookup Table
*/
enum { DELETED_GLYPH = 0xFFFF };
template <typename T> struct Lookup;
template <typename T>
@ -95,6 +129,19 @@ struct LookupFormat0
return &arrayZ[glyph_id];
}
template <typename set_t>
void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const
{
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
{
TRACE_SANITIZE (this);
@ -123,6 +170,19 @@ struct LookupSegmentSingle
int cmp (hb_codepoint_t g) const
{ return g < first ? -1 : g <= last ? 0 : +1 ; }
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
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);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -153,6 +213,21 @@ struct LookupFormat2
return v ? &v->value : nullptr;
}
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
unsigned count = segments.get_length ();
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
{
TRACE_SANITIZE (this);
@ -184,6 +259,21 @@ struct LookupSegmentArray
return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr;
}
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
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; }
@ -191,6 +281,7 @@ struct LookupSegmentArray
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
first <= last &&
valuesZ.sanitize (c, base, last - first + 1));
}
@ -199,6 +290,7 @@ struct LookupSegmentArray
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
first <= last &&
valuesZ.sanitize (c, base, last - first + 1, std::forward<Ts> (ds)...));
}
@ -224,6 +316,21 @@ struct LookupFormat4
return v ? v->get_value (glyph_id, this) : nullptr;
}
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
unsigned count = segments.get_length ();
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
{
TRACE_SANITIZE (this);
@ -252,6 +359,19 @@ struct LookupSingle
int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
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);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -281,6 +401,21 @@ struct LookupFormat6
return v ? &v->value : nullptr;
}
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
unsigned count = entries.get_length ();
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
{
TRACE_SANITIZE (this);
@ -312,6 +447,24 @@ struct LookupFormat8
&valueArrayZ[glyph_id - firstGlyph] : nullptr;
}
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
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
{
TRACE_SANITIZE (this);
@ -356,10 +509,19 @@ struct LookupFormat10
return v;
}
template <typename set_t>
void collect_glyphs (set_t &glyphs) const
{
if (unlikely (!glyphCount)) return;
if (firstGlyph == DELETED_GLYPH) return;
glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
hb_barrier () &&
valueSize <= 4 &&
valueArrayZ.sanitize (c, glyphCount * valueSize));
}
@ -383,11 +545,11 @@ struct Lookup
const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
{
switch (u.format) {
case 0: return u.format0.get_value (glyph_id, num_glyphs);
case 2: return u.format2.get_value (glyph_id);
case 4: return u.format4.get_value (glyph_id);
case 6: return u.format6.get_value (glyph_id);
case 8: return u.format8.get_value (glyph_id);
case 0: hb_barrier (); return u.format0.get_value (glyph_id, num_glyphs);
case 2: hb_barrier (); return u.format2.get_value (glyph_id);
case 4: hb_barrier (); return u.format4.get_value (glyph_id);
case 6: hb_barrier (); return u.format6.get_value (glyph_id);
case 8: hb_barrier (); return u.format8.get_value (glyph_id);
default:return nullptr;
}
}
@ -396,13 +558,39 @@ struct Lookup
{
switch (u.format) {
/* Format 10 cannot return a pointer. */
case 10: return u.format10.get_value_or_null (glyph_id);
case 10: hb_barrier (); return u.format10.get_value_or_null (glyph_id);
default:
const T *v = get_value (glyph_id, num_glyphs);
return v ? *v : Null (T);
}
}
template <typename set_t>
void collect_glyphs (set_t &glyphs, unsigned int num_glyphs) const
{
switch (u.format) {
case 0: hb_barrier (); u.format0.collect_glyphs (glyphs, num_glyphs); return;
case 2: hb_barrier (); u.format2.collect_glyphs (glyphs); return;
case 4: hb_barrier (); u.format4.collect_glyphs (glyphs); return;
case 6: hb_barrier (); u.format6.collect_glyphs (glyphs); return;
case 8: hb_barrier (); u.format8.collect_glyphs (glyphs); return;
case 10: hb_barrier (); u.format10.collect_glyphs (glyphs); return;
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,
unsigned int outOfRange) const
@ -415,13 +603,14 @@ struct Lookup
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
hb_barrier ();
switch (u.format) {
case 0: return_trace (u.format0.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
case 4: return_trace (u.format4.sanitize (c));
case 6: return_trace (u.format6.sanitize (c));
case 8: return_trace (u.format8.sanitize (c));
case 10: return_trace (u.format10.sanitize (c));
case 0: hb_barrier (); return_trace (u.format0.sanitize (c));
case 2: hb_barrier (); return_trace (u.format2.sanitize (c));
case 4: hb_barrier (); return_trace (u.format4.sanitize (c));
case 6: hb_barrier (); return_trace (u.format6.sanitize (c));
case 8: hb_barrier (); return_trace (u.format8.sanitize (c));
case 10: hb_barrier (); return_trace (u.format10.sanitize (c));
default:return_trace (true);
}
}
@ -429,12 +618,13 @@ struct Lookup
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
hb_barrier ();
switch (u.format) {
case 0: return_trace (u.format0.sanitize (c, base));
case 2: return_trace (u.format2.sanitize (c, base));
case 4: return_trace (u.format4.sanitize (c, base));
case 6: return_trace (u.format6.sanitize (c, base));
case 8: return_trace (u.format8.sanitize (c, base));
case 0: hb_barrier (); return_trace (u.format0.sanitize (c, base));
case 2: hb_barrier (); return_trace (u.format2.sanitize (c, base));
case 4: hb_barrier (); return_trace (u.format4.sanitize (c, base));
case 6: hb_barrier (); return_trace (u.format6.sanitize (c, base));
case 8: hb_barrier (); return_trace (u.format8.sanitize (c, base));
case 10: return_trace (false); /* We don't support format10 here currently. */
default:return_trace (true);
}
@ -455,8 +645,6 @@ struct Lookup
};
DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
enum { DELETED_GLYPH = 0xFFFF };
/*
* (Extended) State Table
*/
@ -464,7 +652,7 @@ enum { DELETED_GLYPH = 0xFFFF };
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);
@ -507,6 +695,14 @@ struct Entry<void>
DEFINE_SIZE_STATIC (4);
};
enum Class
{
CLASS_END_OF_TEXT = 0,
CLASS_OUT_OF_BOUNDS = 1,
CLASS_DELETED_GLYPH = 2,
CLASS_END_OF_LINE = 3,
};
template <typename Types, typename Extra>
struct StateTable
{
@ -519,21 +715,53 @@ struct StateTable
STATE_START_OF_TEXT = 0,
STATE_START_OF_LINE = 1,
};
enum Class
template <typename set_t>
void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const
{
CLASS_END_OF_TEXT = 0,
CLASS_OUT_OF_BOUNDS = 1,
CLASS_DELETED_GLYPH = 2,
CLASS_END_OF_LINE = 3,
};
(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; }
unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
unsigned int get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs,
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;
return (this+classTable).get_class (glyph_id, num_glyphs, 1);
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
@ -541,13 +769,14 @@ struct StateTable
const Entry<Extra> &get_entry (int state, unsigned int klass) const
{
if (unlikely (klass >= nClasses))
klass = StateTable::CLASS_OUT_OF_BOUNDS;
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];
@ -558,6 +787,7 @@ struct StateTable
{
TRACE_SANITIZE (this);
if (unlikely (!(c->check_struct (this) &&
hb_barrier () &&
nClasses >= 4 /* Ensure pre-defined classes fit. */ &&
classTable.sanitize (c, this)))) return_trace (false);
@ -684,6 +914,22 @@ struct ClassTable
{
return get_class (glyph_id, outOfRange);
}
template <typename set_t>
void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const
{
for (unsigned i = 0; i < classArray.len; i++)
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
{
TRACE_SANITIZE (this);
@ -697,6 +943,38 @@ struct ClassTable
DEFINE_SIZE_ARRAY (4, classArray);
};
struct SubtableGlyphCoverage
{
bool sanitize (hb_sanitize_context_t *c, unsigned subtable_count) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_array (&subtableOffsets, subtable_count)))
return_trace (false);
unsigned bytes = (c->get_num_glyphs () + CHAR_BIT - 1) / CHAR_BIT;
for (unsigned i = 0; i < subtable_count; i++)
{
uint32_t offset = (uint32_t) subtableOffsets[i];
if (offset == 0 || offset == 0xFFFFFFFF)
continue;
if (unlikely (!subtableOffsets[i].sanitize (c, this, bytes)))
return_trace (false);
}
return_trace (true);
}
protected:
UnsizedArrayOf<NNOffset32To<UnsizedArrayOf<HBUINT8>>> subtableOffsets;
/* Array of offsets from the beginning of the
* subtable glyph coverage table to the glyph
* coverage bitfield for a given subtable; there
* is one offset for each subtable in the chain */
/* UnsizedArrayOf<HBUINT8> coverageBitfields; *//* The individual coverage bitfields. */
public:
DEFINE_SIZE_ARRAY (0, subtableOffsets);
};
struct ObsoleteTypes
{
static constexpr bool extended = false;
@ -766,22 +1044,22 @@ struct ExtendedTypes
}
};
template <typename Types, typename EntryData>
template <typename Types, typename EntryData, typename Flags>
struct StateTableDriver
{
using StateTableT = StateTable<Types, EntryData>;
using EntryT = Entry<EntryData>;
StateTableDriver (const StateTableT &machine_,
hb_buffer_t *buffer_,
hb_face_t *face_) :
machine (machine_),
buffer (buffer_),
num_glyphs (face_->get_num_glyphs ()) {}
template <typename context_t>
void drive (context_t *c, hb_aat_apply_context_t *ac)
{
hb_buffer_t *buffer = ac->buffer;
if (!c->in_place)
buffer->clear_output ();
@ -816,9 +1094,9 @@ struct StateTableDriver
}
}
unsigned int klass = buffer->idx < buffer->len ?
machine.get_class (buffer->cur().codepoint, num_glyphs) :
(unsigned) StateTableT::CLASS_END_OF_TEXT;
unsigned int klass = likely (buffer->idx < buffer->len) ?
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);
const int next_state = machine.new_state (entry.newState);
@ -851,44 +1129,39 @@ 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 (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 (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 (this, machine.get_entry (state, StateTableT::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 (this, entry);
c->transition (buffer, this, entry);
state = next_state;
DEBUG_MSG (APPLY, nullptr, "s%d", state);
@ -896,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 ();
}
@ -906,7 +1179,6 @@ struct StateTableDriver
public:
const StateTableT &machine;
hb_buffer_t *buffer;
unsigned int num_glyphs;
};

View File

@ -138,6 +138,7 @@ struct FeatureName
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
(base+settingTableZ).sanitize (c, nSettings)));
}
@ -200,6 +201,7 @@ struct feat
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
version.major == 1 &&
namesZ.sanitize (c, featureNameCount, this)));
}

View File

@ -185,15 +185,16 @@ struct ActionSubrecord
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
return_trace (false);
hb_barrier ();
switch (u.header.actionType)
{
case 0: return_trace (u.decompositionAction.sanitize (c));
case 1: return_trace (u.unconditionalAddGlyphAction.sanitize (c));
case 2: return_trace (u.conditionalAddGlyphAction.sanitize (c));
// case 3: return_trace (u.stretchGlyphAction.sanitize (c));
case 4: return_trace (u.decompositionAction.sanitize (c));
case 5: return_trace (u.decompositionAction.sanitize (c));
case 0: hb_barrier (); return_trace (u.decompositionAction.sanitize (c));
case 1: hb_barrier (); return_trace (u.unconditionalAddGlyphAction.sanitize (c));
case 2: hb_barrier (); return_trace (u.conditionalAddGlyphAction.sanitize (c));
// case 3: hb_barrier (); return_trace (u.stretchGlyphAction.sanitize (c));
case 4: hb_barrier (); return_trace (u.decompositionAction.sanitize (c));
case 5: hb_barrier (); return_trace (u.decompositionAction.sanitize (c));
default: return_trace (true);
}
}
@ -220,6 +221,7 @@ struct PostcompensationActionChain
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
return_trace (false);
hb_barrier ();
unsigned int offset = min_size;
for (unsigned int i = 0; i < count; i++)
@ -389,6 +391,7 @@ struct just
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
version.major == 1 &&
horizData.sanitize (c, this, this) &&
vertData.sanitize (c, this, this)));

View File

@ -30,6 +30,7 @@
#include "hb-kern.hh"
#include "hb-aat-layout-ankr-table.hh"
#include "hb-set-digest.hh"
/*
* kerx -- Extended Kerning
@ -54,6 +55,7 @@ kerxTupleKern (int value,
unsigned int offset = value;
const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0;
hb_barrier ();
return *pv;
}
@ -81,7 +83,7 @@ struct KernPair
return_trace (c->check_struct (this));
}
protected:
public:
HBGlyphID16 left;
HBGlyphID16 right;
FWORD value;
@ -105,10 +107,10 @@ struct KerxSubTableFormat0
TRACE_APPLY (this);
if (!c->plan->requested_kerning)
return false;
return_trace (false);
if (header.coverage & header.Backwards)
return false;
return_trace (false);
accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
@ -117,6 +119,16 @@ struct KerxSubTableFormat0
return_trace (true);
}
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
for (const KernPair& pair : pairs)
{
left_set.add (pair.left);
right_set.add (pair.right);
}
}
struct accelerator_t
{
const KerxSubTableFormat0 &table;
@ -127,7 +139,10 @@ struct KerxSubTableFormat0
table (table_), c (c_) {}
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ return table.get_kerning (left, right, c); }
{
if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c);
}
};
@ -192,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; }
@ -208,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_) :
@ -227,13 +255,10 @@ struct KerxSubTableFormat1
depth (0),
crossStream (table->header.coverage & table->header.CrossStream) {}
bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry)
{ return Format1EntryT::performAction (entry); }
void transition (StateTableDriver<Types, EntryData> *driver,
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
hb_buffer_t *buffer = driver->buffer;
unsigned int flags = entry.flags;
if (flags & Format1EntryT::Reset)
@ -259,6 +284,7 @@ struct KerxSubTableFormat1
depth = 0;
return;
}
hb_barrier ();
hb_mask_t kern_mask = c->plan->kern_mask;
@ -330,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;
@ -349,7 +376,8 @@ struct KerxSubTableFormat1
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
driver.drive (&dc, c);
return_trace (true);
@ -363,12 +391,21 @@ struct KerxSubTableFormat1
machine.sanitize (c)));
}
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
set_t set;
machine.collect_glyphs (set, num_glyphs);
left_set.union_ (set);
right_set.union_ (set);
}
protected:
KernSubTableHeader header;
StateTable<Types, EntryData> machine;
NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT> kernAction;
public:
DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT));
DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + (StateTable<Types, EntryData>::static_size + HBUINT::static_size));
};
template <typename KernSubTableHeader>
@ -389,6 +426,7 @@ struct KerxSubTableFormat2
kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
const FWORD *v = &arrayZ[kern_idx];
if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
hb_barrier ();
return kerxTupleKern (*v, header.tuple_count (), this, c);
}
@ -398,10 +436,10 @@ struct KerxSubTableFormat2
TRACE_APPLY (this);
if (!c->plan->requested_kerning)
return false;
return_trace (false);
if (header.coverage & header.Backwards)
return false;
return_trace (false);
accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
@ -410,6 +448,13 @@ struct KerxSubTableFormat2
return_trace (true);
}
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
(this+leftClassTable).collect_glyphs (left_set, num_glyphs);
(this+rightClassTable).collect_glyphs (right_set, num_glyphs);
}
struct accelerator_t
{
const KerxSubTableFormat2 &table;
@ -420,7 +465,10 @@ struct KerxSubTableFormat2
table (table_), c (c_) {}
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ return table.get_kerning (left, right, c); }
{
if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c);
}
};
bool sanitize (hb_sanitize_context_t *c) const
@ -429,6 +477,7 @@ struct KerxSubTableFormat2
return_trace (likely (c->check_struct (this) &&
leftClassTable.sanitize (c, this) &&
rightClassTable.sanitize (c, this) &&
hb_barrier () &&
c->check_range (this, array)));
}
@ -461,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. */
@ -481,22 +539,19 @@ 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 (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry)
{ return entry.data.ankrActionIndex != 0xFFFF; }
void transition (StateTableDriver<Types, EntryData> *driver,
void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry)
{
hb_buffer_t *buffer = driver->buffer;
if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
{
hb_glyph_position_t &o = buffer->cur_pos();
@ -509,6 +564,7 @@ struct KerxSubTableFormat4
double the ankrActionIndex to get the correct offset here. */
const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
if (!c->sanitizer.check_array (data, 2)) return;
hb_barrier ();
unsigned int markControlPoint = *data++;
unsigned int currControlPoint = *data++;
hb_position_t markX = 0;
@ -537,6 +593,7 @@ struct KerxSubTableFormat4
double the ankrActionIndex to get the correct offset here. */
const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
if (!c->sanitizer.check_array (data, 2)) return;
hb_barrier ();
unsigned int markAnchorPoint = *data++;
unsigned int currAnchorPoint = *data++;
const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
@ -557,6 +614,7 @@ struct KerxSubTableFormat4
by 4 to get the correct offset for the given action. */
const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4];
if (!c->sanitizer.check_array (data, 4)) return;
hb_barrier ();
int markX = *data++;
int markY = *data++;
int currX = *data++;
@ -579,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;
@ -593,7 +653,8 @@ struct KerxSubTableFormat4
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
driver.drive (&dc, c);
return_trace (true);
@ -607,12 +668,21 @@ struct KerxSubTableFormat4
machine.sanitize (c)));
}
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
set_t set;
machine.collect_glyphs (set, num_glyphs);
left_set.union_ (set);
right_set.union_ (set);
}
protected:
KernSubTableHeader header;
StateTable<Types, EntryData> machine;
HBUINT32 flags;
public:
DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + (StateTable<Types, EntryData>::static_size + HBUINT32::static_size));
};
template <typename KernSubTableHeader>
@ -631,7 +701,7 @@ struct KerxSubTableFormat6
unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
if (is_long ())
{
const typename U::Long &t = u.l;
const auto &t = u.l;
unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
unsigned int offset = l + r;
@ -639,16 +709,18 @@ struct KerxSubTableFormat6
if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
hb_barrier ();
return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
}
else
{
const typename U::Short &t = u.s;
const auto &t = u.s;
unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
unsigned int offset = l + r;
const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
hb_barrier ();
return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
}
}
@ -658,10 +730,10 @@ struct KerxSubTableFormat6
TRACE_APPLY (this);
if (!c->plan->requested_kerning)
return false;
return_trace (false);
if (header.coverage & header.Backwards)
return false;
return_trace (false);
accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
@ -674,6 +746,7 @@ struct KerxSubTableFormat6
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
(is_long () ?
(
u.l.rowIndexTable.sanitize (c, this) &&
@ -688,6 +761,23 @@ struct KerxSubTableFormat6
c->check_range (this, vector))));
}
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
if (is_long ())
{
const auto &t = u.l;
(this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs);
(this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs);
}
else
{
const auto &t = u.s;
(this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs);
(this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs);
}
}
struct accelerator_t
{
const KerxSubTableFormat6 &table;
@ -698,7 +788,10 @@ struct KerxSubTableFormat6
table (table_), c (c_) {}
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ return table.get_kerning (left, right, c); }
{
if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c);
}
};
protected:
@ -784,12 +877,27 @@ struct KerxSubTable
}
}
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
unsigned int subtable_type = get_type ();
switch (subtable_type) {
case 0: u.format0.collect_glyphs (left_set, right_set, num_glyphs); return;
case 1: u.format1.collect_glyphs (left_set, right_set, num_glyphs); return;
case 2: u.format2.collect_glyphs (left_set, right_set, num_glyphs); return;
case 4: u.format4.collect_glyphs (left_set, right_set, num_glyphs); return;
case 6: u.format6.collect_glyphs (left_set, right_set, num_glyphs); return;
default: return;
}
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.header.sanitize (c) ||
u.header.length <= u.header.static_size ||
!c->check_range (this, u.header.length))
if (!(u.header.sanitize (c) &&
hb_barrier () &&
u.header.length >= u.header.static_size &&
c->check_range (this, u.header.length)))
return_trace (false);
return_trace (dispatch (c));
@ -813,6 +921,8 @@ struct KerxSubTable
* The 'kerx' Table
*/
using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_bit_set_t, hb_bit_set_t>>;
template <typename T>
struct KerxTable
{
@ -829,6 +939,9 @@ struct KerxTable
{
if (st->get_type () == 1)
return true;
// TODO: What about format 4? What's this API used for anyway?
st = &StructAfter<SubTable> (*st);
}
return false;
@ -867,7 +980,8 @@ struct KerxTable
return v;
}
bool apply (AAT::hb_aat_apply_context_t *c) const
bool apply (AAT::hb_aat_apply_context_t *c,
const kern_accelerator_data_t &accel_data) const
{
c->buffer->unsafe_to_concat ();
@ -914,6 +1028,9 @@ struct KerxTable
if (reverse)
c->buffer->reverse ();
c->left_set = &accel_data[i].first;
c->right_set = &accel_data[i].second;
{
/* See comment in sanitize() for conditional here. */
hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr);
@ -936,9 +1053,10 @@ struct KerxTable
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (unlikely (!thiz()->version.sanitize (c) ||
(unsigned) thiz()->version < (unsigned) T::minVersion ||
!thiz()->tableCount.sanitize (c)))
if (unlikely (!(thiz()->version.sanitize (c) &&
hb_barrier () &&
(unsigned) thiz()->version >= (unsigned) T::minVersion &&
thiz()->tableCount.sanitize (c))))
return_trace (false);
typedef typename T::SubTable SubTable;
@ -949,6 +1067,7 @@ struct KerxTable
{
if (unlikely (!st->u.header.sanitize (c)))
return_trace (false);
hb_barrier ();
/* OpenType kern table has 2-byte subtable lengths. That's limiting.
* MS implementation also only supports one subtable, of format 0,
* anyway. Certain versions of some fonts, like Calibry, contain
@ -964,8 +1083,61 @@ struct KerxTable
st = &StructAfter<SubTable> (*st);
}
unsigned majorVersion = thiz()->version;
if (sizeof (thiz()->version) == 4)
majorVersion = majorVersion >> 16;
if (majorVersion >= 3)
{
const SubtableGlyphCoverage *coverage = (const SubtableGlyphCoverage *) st;
if (!coverage->sanitize (c, count))
return_trace (false);
}
return_trace (true);
}
kern_accelerator_data_t create_accelerator_data (unsigned num_glyphs) const
{
kern_accelerator_data_t accel_data;
typedef typename T::SubTable SubTable;
const SubTable *st = &thiz()->firstSubTable;
unsigned int count = thiz()->tableCount;
for (unsigned int i = 0; i < count; i++)
{
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);
}
return accel_data;
}
struct accelerator_t
{
accelerator_t (hb_face_t *face)
{
hb_sanitize_context_t sc;
this->table = sc.reference_table<T> (face);
this->accel_data = this->table->create_accelerator_data (face->get_num_glyphs ());
}
~accelerator_t ()
{
this->table.destroy ();
}
hb_blob_t *get_blob () const { return table.get_blob (); }
bool apply (AAT::hb_aat_apply_context_t *c) const
{
return table->apply (c, accel_data);
}
hb_blob_ptr_t<T> table;
kern_accelerator_data_t accel_data;
};
};
struct kerx : KerxTable<kerx>
@ -994,8 +1166,10 @@ struct kerx : KerxTable<kerx>
DEFINE_SIZE_MIN (8);
};
struct kerx_accelerator_t : kerx::accelerator_t {
kerx_accelerator_t (hb_face_t *face) : kerx::accelerator_t (face) {}
};
} /* namespace AAT */
#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */

View File

@ -133,8 +133,8 @@ struct opbd
{
switch (format)
{
case 0: return u.format0.get_bounds (font, glyph_id, extents, this);
case 1: return u.format1.get_bounds (font, glyph_id, extents, this);
case 0: hb_barrier (); return u.format0.get_bounds (font, glyph_id, extents, this);
case 1: hb_barrier (); return u.format1.get_bounds (font, glyph_id, extents, this);
default:return false;
}
}
@ -144,11 +144,12 @@ struct opbd
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this) || version.major != 1))
return_trace (false);
hb_barrier ();
switch (format)
{
case 0: return_trace (u.format0.sanitize (c, this));
case 1: return_trace (u.format1.sanitize (c, this));
case 0: hb_barrier (); return_trace (u.format0.sanitize (c, this));
case 1: hb_barrier (); return_trace (u.format1.sanitize (c, this));
default:return_trace (true);
}
}

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,64 +128,45 @@ 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
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
sizeTable.sanitize (c, base, nSizes) &&
trackTable.sanitize (c, nTracks, base, nSizes)));
}
@ -157,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
@ -203,6 +206,7 @@ struct trak
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
version.major == 1 &&
horizData.sanitize (c, this, this) &&
vertData.sanitize (c, this, this)));

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,18 +210,48 @@ 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)
{
const AAT::morx& morx = *mapper->face->table.morx;
const AAT::morx& morx = *mapper->face->table.morx->table;
if (morx.has_data ())
{
morx.compile_flags (mapper, map);
return;
}
const AAT::mort& mort = *mapper->face->table.mort;
const AAT::mort& mort = *mapper->face->table.mort->table;
if (mort.has_data ())
{
mort.compile_flags (mapper, map);
@ -243,8 +276,8 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
hb_bool_t
hb_aat_layout_has_substitution (hb_face_t *face)
{
return face->table.morx->has_data () ||
face->table.mort->has_data ();
return face->table.morx->table->has_data () ||
face->table.mort->table->has_data ();
}
void
@ -260,26 +293,30 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
hb_aat_map_t map;
builder.compile (map);
hb_blob_t *morx_blob = font->face->table.morx.get_blob ();
const AAT::morx& morx = *morx_blob->as<AAT::morx> ();
if (morx.has_data ())
{
AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob);
if (!buffer->message (font, "start table morx")) return;
morx.apply (&c, map);
(void) buffer->message (font, "end table morx");
return;
auto &accel = *font->face->table.morx;
const AAT::morx& morx = *accel.table;
if (morx.has_data ())
{
AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
if (!buffer->message (font, "start table morx")) return;
morx.apply (&c, map, accel);
(void) buffer->message (font, "end table morx");
return;
}
}
hb_blob_t *mort_blob = font->face->table.mort.get_blob ();
const AAT::mort& mort = *mort_blob->as<AAT::mort> ();
if (mort.has_data ())
{
AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob);
if (!buffer->message (font, "start table mort")) return;
mort.apply (&c, map);
(void) buffer->message (font, "end table mort");
return;
auto &accel = *font->face->table.mort;
const AAT::mort& mort = *accel.table;
if (mort.has_data ())
{
AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
if (!buffer->message (font, "start table mort")) return;
mort.apply (&c, map, accel);
(void) buffer->message (font, "end table mort");
return;
}
}
}
@ -322,7 +359,7 @@ hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
hb_bool_t
hb_aat_layout_has_positioning (hb_face_t *face)
{
return face->table.kerx->has_data ();
return face->table.kerx->table->has_data ();
}
void
@ -330,13 +367,12 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer)
{
hb_blob_t *kerx_blob = font->face->table.kerx.get_blob ();
const AAT::kerx& kerx = *kerx_blob->as<AAT::kerx> ();
auto &accel = *font->face->table.kerx;
AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob);
AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
if (!buffer->message (font, "start table kerx")) return;
c.set_ankr_table (font->face->table.ankr.get ());
kerx.apply (&c);
accel.apply (&c);
(void) buffer->message (font, "end table kerx");
}
@ -358,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

@ -40,7 +40,7 @@ HB_BEGIN_DECLS
* @HB_AAT_LAYOUT_FEATURE_TYPE_INVALID: Initial, unset feature type
* @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC: [All Typographic Features](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type0)
* @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES: [Ligatures](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type1)
* @HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2)
* @HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2)
* @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE: [Letter Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type3)
* @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION: [Vertical Substitution](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type4)
* @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT: [Linguistic Rearrangement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type5)
@ -88,7 +88,7 @@ typedef enum
HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0,
HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1,
HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION = 2,
HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION = 2,
HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3,
HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4,
HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5,

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

@ -46,7 +46,9 @@ struct FTStringRange
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && (base+tag).sanitize (c, length));
return_trace (c->check_struct (this) &&
hb_barrier () &&
(base+tag).sanitize (c, length));
}
protected:
@ -73,6 +75,7 @@ struct ltag
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
version >= 1 &&
tagRanges.sanitize (c, this)));
}

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

@ -202,8 +202,12 @@ struct BEInt<Type, 4>
/* Floats. */
/* We want our rounding towards +infinity. */
static inline double
_hb_roundf (double x) { return floor (x + .5); }
static inline float
_hb_roundf (float x) { return floorf (x + .5f); }
#define roundf(x) _hb_roundf(x)
@ -282,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)
@ -306,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;
}
}
@ -316,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;
}
}
@ -332,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)
@ -671,7 +675,7 @@ struct hb_pair_t
return 0;
}
friend void swap (hb_pair_t& a, hb_pair_t& b)
friend void swap (hb_pair_t& a, hb_pair_t& b) noexcept
{
hb_swap (a.first, b.first);
hb_swap (a.second, b.second);
@ -1053,6 +1057,18 @@ _hb_cmp_method (const void *pkey, const void *pval, Ts... ds)
return val.cmp (key, ds...);
}
template <typename K, typename V>
static int
_hb_cmp_operator (const void *pkey, const void *pval)
{
const K& key = * (const K*) pkey;
const V& val = * (const V*) pval;
if (key < val) return -1;
if (key > val) return 1;
return 0;
}
template <typename V, typename K, typename ...Ts>
static inline bool
hb_bsearch_impl (unsigned *pos, /* Out */

View File

@ -47,6 +47,8 @@ enum hb_not_found_t
template <typename Type>
struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
{
static constexpr bool realloc_move = true;
/*
* Constructors.
*/
@ -249,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

@ -118,12 +118,12 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
*/
#ifndef _hb_compiler_memory_r_barrier
#if defined(__ATOMIC_ACQUIRE) // gcc-like
#define _hb_compiler_memory_r_barrier() asm volatile("": : :"memory")
static inline void _hb_compiler_memory_r_barrier () { asm volatile("": : :"memory"); }
#elif !defined(_MSC_VER)
#include <atomic>
#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire)
#else
#define _hb_compiler_memory_r_barrier() do {} while (0)
static inline void _hb_compiler_memory_r_barrier () {}
#endif
#endif
@ -212,11 +212,18 @@ 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 (); }
T *v = nullptr;
};
static inline bool hb_barrier ()
{
_hb_compiler_memory_r_barrier ();
return true;
}
#endif /* HB_ATOMIC_HH */

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

@ -39,10 +39,10 @@ struct hb_bit_set_invertible_t
hb_bit_set_invertible_t () = default;
hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default;
hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); }
hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) noexcept : hb_bit_set_invertible_t () { hb_swap (*this, other); }
hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default;
hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; }
friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b)
hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) noexcept { hb_swap (*this, other); return *this; }
friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) noexcept
{
if (likely (!a.s.successful || !b.s.successful))
return;
@ -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;
@ -359,8 +363,8 @@ struct hb_bit_set_invertible_t
typedef hb_codepoint_t __item_t__;
hb_codepoint_t __item__ () const { return v; }
bool __more__ () const { return v != INVALID; }
void __next__ () { s->next (&v); if (l) l--; }
void __prev__ () { s->previous (&v); }
void __next__ () { s->next (&v); if (likely (l)) l--; }
void __prev__ () { s->previous (&v); l++; }
unsigned __len__ () const { return l; }
iter_t end () const { return iter_t (*s, false); }
bool operator != (const iter_t& o) const

View File

@ -38,10 +38,10 @@ struct hb_bit_set_t
~hb_bit_set_t () = default;
hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); }
hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); }
hb_bit_set_t ( hb_bit_set_t&& other) noexcept : hb_bit_set_t () { hb_swap (*this, other); }
hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; }
hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; }
friend void swap (hb_bit_set_t &a, hb_bit_set_t &b)
hb_bit_set_t& operator= (hb_bit_set_t&& other) noexcept { hb_swap (*this, other); return *this; }
friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) noexcept
{
if (likely (!a.successful || !b.successful))
return;
@ -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

@ -598,6 +598,11 @@ _open_resource_fork (const char *file_name, hb_mapped_file_t *file)
* Creates a new blob containing the data from the
* specified binary font file.
*
* The filename is passed directly to the system on all platforms,
* except on Windows, where the filename is interpreted as UTF-8.
* Only if the filename is not valid UTF-8, it will be interpreted
* according to the system codepage.
*
* Returns: An #hb_blob_t pointer with the content of the file,
* or hb_blob_get_empty() if failed.
*
@ -612,10 +617,14 @@ hb_blob_create_from_file (const char *file_name)
/**
* hb_blob_create_from_file_or_fail:
* @file_name: A font filename
* @file_name: A filename
*
* Creates a new blob containing the data from the
* specified binary font file.
* Creates a new blob containing the data from the specified file.
*
* The filename is passed directly to the system on all platforms,
* except on Windows, where the filename is interpreted as UTF-8.
* Only if the filename is not valid UTF-8, it will be interpreted
* according to the system codepage.
*
* Returns: An #hb_blob_t pointer with the content of the file,
* or `NULL` if failed.
@ -672,10 +681,19 @@ fail_without_close:
if (unlikely (!file)) return nullptr;
HANDLE fd;
int conversion;
unsigned int size = strlen (file_name) + 1;
wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size);
if (unlikely (!wchar_file_name)) goto fail_without_close;
mbstowcs (wchar_file_name, file_name, size);
/* Assume file name is given in UTF-8 encoding */
conversion = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, file_name, -1, wchar_file_name, size);
if (conversion <= 0)
{
/* Conversion failed due to invalid UTF-8 characters,
Repeat conversion based on system code page */
mbstowcs(wchar_file_name, file_name, size);
}
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
{
CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };

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

@ -149,7 +149,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer,
}
assert (text_start < text_end);
if (0)
if (false)
printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
hb_buffer_clear_contents (fragment);
@ -288,7 +288,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer,
}
assert (text_start < text_end);
if (0)
if (false)
printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
#if 0
@ -412,7 +412,7 @@ hb_buffer_t::verify (hb_buffer_t *text_buffer,
&len,
HB_BUFFER_SERIALIZE_FORMAT_TEXT,
HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS);
buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ);
buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ ? bytes.arrayZ : "");
}
#endif
}

View File

@ -271,6 +271,7 @@ hb_buffer_t::similar (const hb_buffer_t &src)
replacement = src.replacement;
invisible = src.invisible;
not_found = src.not_found;
not_found_variation_selector = src.not_found_variation_selector;
}
void
@ -283,6 +284,7 @@ hb_buffer_t::reset ()
replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
invisible = 0;
not_found = 0;
not_found_variation_selector = HB_CODEPOINT_INVALID;
clear ();
}
@ -309,6 +311,7 @@ hb_buffer_t::clear ()
deallocate_var_all ();
serial = 0;
random_state = 1;
scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
}
@ -704,6 +707,7 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) =
HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
0, /* invisible */
0, /* not_found */
HB_CODEPOINT_INVALID, /* not_found_variation_selector */
HB_BUFFER_CONTENT_TYPE_INVALID,
@ -1359,6 +1363,89 @@ hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer)
return buffer->not_found;
}
/**
* hb_buffer_set_not_found_variation_selector_glyph:
* @buffer: An #hb_buffer_t
* @not_found_variation_selector: the not-found-variation-selector #hb_codepoint_t
*
* Sets the #hb_codepoint_t that replaces variation-selector characters not resolved
* in the font during shaping.
*
* The not-found-variation-selector glyph defaults to #HB_CODEPOINT_INVALID,
* in which case an unresolved variation-selector will be removed from the glyph
* string during shaping. This API allows for changing that and retaining a glyph,
* such that the situation can be detected by the client and handled accordingly
* (e.g. by using a different font).
*
* Since: 10.0.0
**/
void
hb_buffer_set_not_found_variation_selector_glyph (hb_buffer_t *buffer,
hb_codepoint_t not_found_variation_selector)
{
buffer->not_found_variation_selector = not_found_variation_selector;
}
/**
* hb_buffer_get_not_found_variation_selector_glyph:
* @buffer: An #hb_buffer_t
*
* See hb_buffer_set_not_found_variation_selector_glyph().
*
* Return value:
* The @buffer not-found-variation-selector #hb_codepoint_t
*
* Since: 10.0.0
**/
hb_codepoint_t
hb_buffer_get_not_found_variation_selector_glyph (const hb_buffer_t *buffer)
{
return buffer->not_found_variation_selector;
}
/**
* hb_buffer_set_random_state:
* @buffer: An #hb_buffer_t
* @state: the new random state
*
* Sets the random state of the buffer. The state changes
* every time a glyph uses randomness (eg. the `rand`
* OpenType feature). This function together with
* hb_buffer_get_random_state() allow for transferring
* the current random state to a subsequent buffer, to
* get better randomness distribution.
*
* Defaults to 1 and when buffer contents are cleared.
* A value of 0 disables randomness during shaping.
*
* Since: 8.4.0
**/
void
hb_buffer_set_random_state (hb_buffer_t *buffer,
unsigned state)
{
if (unlikely (hb_object_is_immutable (buffer)))
return;
buffer->random_state = state;
}
/**
* hb_buffer_get_random_state:
* @buffer: An #hb_buffer_t
*
* See hb_buffer_set_random_state().
*
* Return value:
* The @buffer random state
*
* Since: 8.4.0
**/
unsigned
hb_buffer_get_random_state (const hb_buffer_t *buffer)
{
return buffer->random_state;
}
/**
* hb_buffer_clear_contents:
@ -1896,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

@ -487,6 +487,19 @@ hb_buffer_set_not_found_glyph (hb_buffer_t *buffer,
HB_EXTERN hb_codepoint_t
hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer);
HB_EXTERN void
hb_buffer_set_not_found_variation_selector_glyph (hb_buffer_t *buffer,
hb_codepoint_t not_found_variation_selector);
HB_EXTERN hb_codepoint_t
hb_buffer_get_not_found_variation_selector_glyph (const hb_buffer_t *buffer);
HB_EXTERN void
hb_buffer_set_random_state (hb_buffer_t *buffer,
unsigned state);
HB_EXTERN unsigned
hb_buffer_get_random_state (const hb_buffer_t *buffer);
/*
* Content API.

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), "");
@ -52,6 +51,7 @@ enum hb_buffer_scratch_flags_t {
HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u,
HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u,
HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000040u,
HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK= 0x00000080u,
/* Reserved for shapers' internal use. */
HB_BUFFER_SCRATCH_FLAG_SHAPER0 = 0x01000000u,
@ -80,6 +80,7 @@ struct hb_buffer_t
hb_codepoint_t replacement; /* U+FFFD or something else. */
hb_codepoint_t invisible; /* 0 or something else. */
hb_codepoint_t not_found; /* 0 or something else. */
hb_codepoint_t not_found_variation_selector; /* HB_CODEPOINT_INVALID or something else. */
/*
* Buffer contents
@ -116,6 +117,7 @@ struct hb_buffer_t
uint8_t allocated_var_bits;
uint8_t serial;
uint32_t random_state;
hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
unsigned int max_len; /* Maximum allowed len. */
int max_ops; /* Maximum allowed operations. */
@ -179,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 ())
@ -624,7 +624,6 @@ struct opset_t
} else {
/* invalid unknown operator */
env.clear_args ();
env.set_error ();
}
break;
}

View File

@ -54,8 +54,8 @@ struct top_dict_values_t : dict_values_t<OPSTR>
}
void fini () { dict_values_t<OPSTR>::fini (); }
unsigned int charStringsOffset;
unsigned int FDArrayOffset;
int charStringsOffset;
int FDArrayOffset;
};
struct dict_opset_t : opset_t<number_t>
@ -84,7 +84,7 @@ struct dict_opset_t : opset_t<number_t>
enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
char buf[32];
char buf[32] = {0};
unsigned char byte = 0;
for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count)
{
@ -157,11 +157,11 @@ struct top_dict_opset_t : dict_opset_t
{
switch (op) {
case OpCode_CharStrings:
dictval.charStringsOffset = env.argStack.pop_uint ();
dictval.charStringsOffset = env.argStack.pop_int ();
env.clear_args ();
break;
case OpCode_FDArray:
dictval.FDArrayOffset = env.argStack.pop_uint ();
dictval.FDArrayOffset = env.argStack.pop_int ();
env.clear_args ();
break;
case OpCode_FontMatrix:

View File

@ -76,16 +76,12 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
coords = coords_;
num_coords = num_coords_;
varStore = acc.varStore;
seen_blend = false;
seen_vsindex_ = false;
scalars.init ();
do_blend = num_coords && coords && varStore->size;
set_ivs (acc.privateDicts[fd].ivs);
}
void fini ()
{
scalars.fini ();
SUPER::fini ();
}
@ -168,13 +164,13 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
protected:
const int *coords;
unsigned int num_coords;
const CFF2VariationStore *varStore;
const CFF2ItemVariationStore *varStore;
unsigned int region_count;
unsigned int ivs;
hb_vector_t<float> scalars;
bool do_blend;
bool seen_vsindex_;
bool seen_blend;
bool seen_vsindex_ = false;
bool seen_blend = false;
typedef cs_interp_env_t<ELEM, CFF2Subrs> SUPER;
};

View File

@ -625,6 +625,9 @@ hb_script_get_horizontal_direction (hb_script_t script)
/* Unicode-14.0 additions */
case HB_SCRIPT_OLD_UYGHUR:
/* Unicode-16.0 additions */
case HB_SCRIPT_GARAY:
return HB_DIRECTION_RTL;
@ -996,7 +999,7 @@ hb_feature_to_string (hb_feature_t *feature,
if (feature->value > 1)
{
s[len++] = '=';
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%" PRIu32, feature->value));
}
assert (len < ARRAY_LENGTH (s));
len = hb_min (len, size - 1);

View File

@ -47,14 +47,10 @@
# endif /* !__cplusplus */
#endif
#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \
defined (_sgi) || defined (__sun) || defined (sun) || \
defined (__digital__) || defined (__HP_cc)
# include <inttypes.h>
#elif defined (_AIX)
#if defined (_AIX)
# include <sys/inttypes.h>
#elif defined (_MSC_VER) && _MSC_VER < 1600
/* VS 2010 (_MSC_VER 1600) has stdint.h */
/* VS 2010 (_MSC_VER 1600) has stdint.h */
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
@ -63,10 +59,11 @@ typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#elif defined (__KERNEL__)
# include <linux/types.h>
#else
#elif defined (_MSC_VER) && _MSC_VER < 1800
/* VS 2013 (_MSC_VER 1800) has inttypes.h */
# include <stdint.h>
#else
# include <inttypes.h>
#endif
#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
@ -507,6 +504,13 @@ hb_language_matches (hb_language_t language,
* @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0
* @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0
* @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0
* @HB_SCRIPT_GARAY: `Gara`, Since: 10.0.0
* @HB_SCRIPT_GURUNG_KHEMA: `Gukh`, Since: 10.0.0
* @HB_SCRIPT_KIRAT_RAI: `Krai`, Since: 10.0.0
* @HB_SCRIPT_OL_ONAL: `Onao`, Since: 10.0.0
* @HB_SCRIPT_SUNUWAR: `Sunu`, Since: 10.0.0
* @HB_SCRIPT_TODHRI: `Todr`, Since: 10.0.0
* @HB_SCRIPT_TULU_TIGALARI: `Tutg`, Since: 10.0.0
* @HB_SCRIPT_INVALID: No script set
*
* Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding
@ -734,6 +738,17 @@ typedef enum
HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/
HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/
/*
* Since 10.0.0
*/
HB_SCRIPT_GARAY = HB_TAG ('G','a','r','a'), /*16.0*/
HB_SCRIPT_GURUNG_KHEMA = HB_TAG ('G','u','k','h'), /*16.0*/
HB_SCRIPT_KIRAT_RAI = HB_TAG ('K','r','a','i'), /*16.0*/
HB_SCRIPT_OL_ONAL = HB_TAG ('O','n','a','o'), /*16.0*/
HB_SCRIPT_SUNUWAR = HB_TAG ('S','u','n','u'), /*16.0*/
HB_SCRIPT_TODHRI = HB_TAG ('T','o','d','r'), /*16.0*/
HB_SCRIPT_TULU_TIGALARI = HB_TAG ('T','u','t','g'), /*16.0*/
/* No script set. */
HB_SCRIPT_INVALID = HB_TAG_NONE,

View File

@ -56,7 +56,6 @@
#ifdef HB_LEAN
#define HB_DISABLE_DEPRECATED
#define HB_NDEBUG
#define HB_NO_ATEXIT
#define HB_NO_BUFFER_MESSAGE
#define HB_NO_BUFFER_SERIALIZE
@ -69,8 +68,6 @@
#define HB_NO_FACE_COLLECT_UNICODES
#define HB_NO_GETENV
#define HB_NO_HINTING
#define HB_NO_LANGUAGE_LONG
#define HB_NO_LANGUAGE_PRIVATE_SUBTAG
#define HB_NO_LAYOUT_FEATURE_PARAMS
#define HB_NO_LAYOUT_COLLECT_GLYPHS
#define HB_NO_LAYOUT_RARELY_USED
@ -118,6 +115,10 @@
#define HB_NO_VAR_COMPOSITES
#endif
#ifdef HB_NO_VAR
#define HB_NO_VAR_COMPOSITES
#endif
#ifdef HB_DISABLE_DEPRECATED
#define HB_IF_NOT_DEPRECATED(x)
#else
@ -156,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

@ -27,9 +27,6 @@
#include "hb.h"
HB_BEGIN_DECLS
HB_END_DECLS
#ifdef __cplusplus
#include <functional>
@ -56,15 +53,15 @@ struct shared_ptr
explicit shared_ptr (T *p = nullptr) : p (p) {}
shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {}
shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; }
shared_ptr (shared_ptr &&o) noexcept : p (o.p) { o.p = nullptr; }
shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; }
shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
shared_ptr& operator = (shared_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
~shared_ptr () { v::destroy (p); p = nullptr; }
T* get() const { return p; }
void swap (shared_ptr &o) { std::swap (p, o.p); }
friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); }
void swap (shared_ptr &o) noexcept { std::swap (p, o.p); }
friend void swap (shared_ptr &a, shared_ptr &b) noexcept { std::swap (a.p, b.p); }
operator T * () const { return p; }
T& operator * () const { return *get (); }
@ -98,16 +95,16 @@ struct unique_ptr
explicit unique_ptr (T *p = nullptr) : p (p) {}
unique_ptr (const unique_ptr &o) = delete;
unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; }
unique_ptr (unique_ptr &&o) noexcept : p (o.p) { o.p = nullptr; }
unique_ptr& operator = (const unique_ptr &o) = delete;
unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
unique_ptr& operator = (unique_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
~unique_ptr () { v::destroy (p); p = nullptr; }
T* get() const { return p; }
T* release () { T* v = p; p = nullptr; return v; }
void swap (unique_ptr &o) { std::swap (p, o.p); }
friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); }
void swap (unique_ptr &o) noexcept { std::swap (p, o.p); }
friend void swap (unique_ptr &a, unique_ptr &b) noexcept { std::swap (a.p, b.p); }
operator T * () const { return p; }
T& operator * () const { return *get (); }

View File

@ -0,0 +1,164 @@
/*
* 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
*
* hb_decycler_t is O(n) in the number of nodes in the DFS traversal
* if there are no cycles. Unlike Floyd's algorithm, hb_decycler_t
* can be used in a DFS traversal, where the graph is not a simple
* linked list, but a tree with possible cycles. Like Floyd's algorithm,
* it is constant-memory (~three 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_awake = false;
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_awake = !decycler.tortoise_awake;
if (!decycler.tortoise)
{
// First node.
assert (decycler.tortoise_awake);
assert (!decycler.hare);
decycler.tortoise = decycler.hare = this;
return;
}
if (decycler.tortoise_awake)
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_awake)
decycler.tortoise = decycler.tortoise->prev;
decycler.tortoise_awake = !decycler.tortoise_awake;
}
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

@ -56,7 +56,7 @@ HB_BEGIN_DECLS
/**
* HB_SCRIPT_CANADIAN_ABORIGINAL:
*
* Use #HB_SCRIPT_CANADIAN_SYLLABICS instead:
* Use #HB_SCRIPT_CANADIAN_SYLLABICS instead.
*
* Deprecated: 0.9.20
*/
@ -301,6 +301,15 @@ hb_font_get_glyph_shape (hb_font_t *font,
hb_draw_funcs_t *dfuncs, void *draw_data);
/**
* HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION:
*
* Use #HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION instead.
*
* Deprecated: 8.3.0
*/
#define HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION
#endif

View File

@ -70,7 +70,7 @@ typedef struct hb_draw_state_t {
*
* The default #hb_draw_state_t at the start of glyph drawing.
*/
#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}}
#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}, {0.}, {0.}, {0.}, {0.}}
/**

View File

@ -232,7 +232,7 @@ struct hb_draw_session_t
funcs->close_path (draw_data, st);
}
protected:
public:
float slant;
bool not_slanted;
hb_draw_funcs_t *funcs;

View File

@ -42,7 +42,7 @@
struct face_table_info_t
{
hb_blob_t* data;
signed order;
unsigned order;
};
struct hb_face_builder_data_t
@ -153,6 +153,50 @@ _hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
return hb_blob_reference (data->tables[tag].data);
}
static unsigned
_hb_face_builder_get_table_tags (const hb_face_t *face HB_UNUSED,
unsigned int start_offset,
unsigned int *table_count,
hb_tag_t *table_tags,
void *user_data)
{
hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
unsigned population = data->tables.get_population ();
if (!table_count)
return population;
if (unlikely (start_offset >= population))
{
if (table_count)
*table_count = 0;
return population;
}
// Sort the tags.
hb_vector_t<hb_tag_t> sorted_tags;
data->tables.keys () | hb_sink (sorted_tags);
if (unlikely (sorted_tags.in_error ()))
{
// Not much to do...
}
sorted_tags.qsort ([] (const void* a, const void* b) {
return * (hb_tag_t *) a < * (hb_tag_t *) b ? -1 :
* (hb_tag_t *) a == * (hb_tag_t *) b ? 0 :
+1;
});
auto array = sorted_tags.as_array ().sub_array (start_offset, table_count);
auto out = hb_array (table_tags, *table_count);
+ array.iter ()
| hb_sink (out)
;
return population;
}
/**
* hb_face_builder_create:
@ -171,9 +215,16 @@ hb_face_builder_create ()
hb_face_builder_data_t *data = _hb_face_builder_data_create ();
if (unlikely (!data)) return hb_face_get_empty ();
return hb_face_create_for_tables (_hb_face_builder_reference_table,
data,
_hb_face_builder_data_destroy);
hb_face_t *face = hb_face_create_for_tables (_hb_face_builder_reference_table,
data,
_hb_face_builder_data_destroy);
hb_face_set_get_table_tags_func (face,
_hb_face_builder_get_table_tags,
data,
nullptr);
return face;
}
/**
@ -199,7 +250,7 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
hb_blob_t* previous = data->tables.get (tag).data;
if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1}))
if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), (unsigned) -1}))
{
hb_blob_destroy (blob);
return false;

View File

@ -90,10 +90,6 @@ DEFINE_NULL_INSTANCE (hb_face_t) =
{
HB_OBJECT_HEADER_STATIC,
nullptr, /* reference_table_func */
nullptr, /* user_data */
nullptr, /* destroy */
0, /* index */
1000, /* upem */
0, /* num_glyphs */
@ -110,8 +106,9 @@ DEFINE_NULL_INSTANCE (hb_face_t) =
*
* Variant of hb_face_create(), built for those cases where it is more
* convenient to provide data for individual tables instead of the whole font
* data. With the caveat that hb_face_get_table_tags() does not currently work
* with faces created this way.
* data. With the caveat that hb_face_get_table_tags() would not work
* with faces created this way. You can address that by calling the
* hb_face_set_get_table_tags_func() function and setting the appropriate callback.
*
* Creates a new face object from the specified @user_data and @reference_table_func,
* with the @destroy callback.
@ -194,6 +191,22 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
return blob;
}
static unsigned
_hb_face_for_data_get_table_tags (const hb_face_t *face HB_UNUSED,
unsigned int start_offset,
unsigned int *table_count,
hb_tag_t *table_tags,
void *user_data)
{
hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data;
const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> ();
const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
return ot_face.get_table_tags (start_offset, table_count, table_tags);
}
/**
* hb_face_create:
* @blob: #hb_blob_t to work upon
@ -240,12 +253,73 @@ hb_face_create (hb_blob_t *blob,
face = hb_face_create_for_tables (_hb_face_for_data_reference_table,
closure,
_hb_face_for_data_closure_destroy);
hb_face_set_get_table_tags_func (face,
_hb_face_for_data_get_table_tags,
closure,
nullptr);
face->index = index;
return face;
}
/**
* hb_face_create_or_fail:
* @blob: #hb_blob_t to work upon
* @index: The index of the face within @blob
*
* Like hb_face_create(), but returns `NULL` if the blob data
* contains no usable font face at the specified index.
*
* Return value: (transfer full): The new face object, or `NULL` if
* no face is found at the specified index.
*
* Since: 10.1.0
**/
hb_face_t *
hb_face_create_or_fail (hb_blob_t *blob,
unsigned int index)
{
unsigned num_faces = hb_face_count (blob);
if (index >= num_faces)
return nullptr;
hb_face_t *face = hb_face_create (blob, index);
if (hb_object_is_immutable (face))
return nullptr;
return face;
}
#ifndef HB_NO_OPEN
/**
* hb_face_create_from_file_or_fail:
* @file_name: A font filename
* @index: The index of the face within the file
*
* A thin wrapper around hb_blob_create_from_file_or_fail()
* followed by hb_face_create_or_fail().
*
* Return value: (transfer full): The new face object, or `NULL` if
* no face is found at the specified index or the file cannot be read.
*
* Since: 10.1.0
**/
HB_EXTERN hb_face_t *
hb_face_create_from_file_or_fail (const char *file_name,
unsigned int index)
{
hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name);
if (unlikely (!blob))
return nullptr;
hb_face_t *face = hb_face_create_or_fail (blob, index);
hb_blob_destroy (blob);
return face;
}
#endif
/**
* hb_face_get_empty:
*
@ -306,6 +380,9 @@ hb_face_destroy (hb_face_t *face)
face->data.fini ();
face->table.fini ();
if (face->get_table_tags_destroy)
face->get_table_tags_destroy (face->get_table_tags_user_data);
if (face->destroy)
face->destroy (face->user_data);
@ -395,7 +472,8 @@ hb_face_is_immutable (const hb_face_t *face)
* @tag: The #hb_tag_t of the table to query
*
* Fetches a reference to the specified table within
* the specified face.
* the specified face. Returns an empty blob if referencing table data is not
* possible.
*
* Return value: (transfer full): A pointer to the @tag table within @face
*
@ -415,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
*
@ -426,7 +505,41 @@ 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);
if (unlikely (!count))
break; // Allocation error
for (unsigned i = 0; i < count; i++)
{
if (unlikely (!tags[i]))
continue;
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;
}
/**
@ -547,6 +660,38 @@ hb_face_get_glyph_count (const hb_face_t *face)
return face->get_num_glyphs ();
}
/**
* hb_face_set_get_table_tags_func:
* @face: A face object
* @func: (closure user_data) (destroy destroy) (scope notified): The table-tag-fetching function
* @user_data: A pointer to the user data, to be destroyed by @destroy when not needed anymore
* @destroy: (nullable): A callback to call when @func is not needed anymore
*
* Sets the table-tag-fetching function for the specified face object.
*
* Since: 10.0.0
*/
HB_EXTERN void
hb_face_set_get_table_tags_func (hb_face_t *face,
hb_get_table_tags_func_t func,
void *user_data,
hb_destroy_func_t destroy)
{
if (hb_object_is_immutable (face))
{
if (destroy)
destroy (user_data);
return;
}
if (face->get_table_tags_destroy)
face->get_table_tags_destroy (face->get_table_tags_user_data);
face->get_table_tags_func = func;
face->get_table_tags_user_data = user_data;
face->get_table_tags_destroy = destroy;
}
/**
* hb_face_get_table_tags:
* @face: A face object
@ -568,19 +713,14 @@ hb_face_get_table_tags (const hb_face_t *face,
unsigned int *table_count, /* IN/OUT */
hb_tag_t *table_tags /* OUT */)
{
if (face->destroy != (hb_destroy_func_t) _hb_face_for_data_closure_destroy)
if (!face->get_table_tags_func)
{
if (table_count)
*table_count = 0;
return 0;
}
hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data;
const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> ();
const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
return ot_face.get_table_tags (start_offset, table_count, table_tags);
return face->get_table_tags_func (face, start_offset, table_count, table_tags, face->get_table_tags_user_data);
}

View File

@ -59,15 +59,28 @@ HB_EXTERN hb_face_t *
hb_face_create (hb_blob_t *blob,
unsigned int index);
HB_EXTERN hb_face_t *
hb_face_create_or_fail (hb_blob_t *blob,
unsigned int index);
HB_EXTERN hb_face_t *
hb_face_create_from_file_or_fail (const char *file_name,
unsigned int index);
/**
* hb_reference_table_func_t:
* @face: an #hb_face_t to reference table for
* @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
*/
@ -135,6 +148,34 @@ hb_face_set_glyph_count (hb_face_t *face,
HB_EXTERN unsigned int
hb_face_get_glyph_count (const hb_face_t *face);
/**
* hb_get_table_tags_func_t:
* @face: A face object
* @start_offset: The index of first table tag to retrieve
* @table_count: (inout): Input = the maximum number of table tags to return;
* Output = the actual number of table tags returned (may be zero)
* @table_tags: (out) (array length=table_count): The array of table tags found
* @user_data: User data pointer passed by the caller
*
* Callback function for hb_face_get_table_tags().
*
* Return value: Total number of tables, or zero if it is not possible to list
*
* Since: 10.0.0
*/
typedef unsigned int (*hb_get_table_tags_func_t) (const hb_face_t *face,
unsigned int start_offset,
unsigned int *table_count, /* IN/OUT */
hb_tag_t *table_tags /* OUT */,
void *user_data);
HB_EXTERN void
hb_face_set_get_table_tags_func (hb_face_t *face,
hb_get_table_tags_func_t func,
void *user_data,
hb_destroy_func_t destroy);
HB_EXTERN unsigned int
hb_face_get_table_tags (const hb_face_t *face,
unsigned int start_offset,

View File

@ -48,13 +48,17 @@ struct hb_face_t
{
hb_object_header_t header;
unsigned int index; /* Face index in a collection, zero-based. */
mutable hb_atomic_int_t upem; /* Units-per-EM. */
mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */
hb_reference_table_func_t reference_table_func;
void *user_data;
hb_destroy_func_t destroy;
unsigned int index; /* Face index in a collection, zero-based. */
mutable hb_atomic_int_t upem; /* Units-per-EM. */
mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */
hb_get_table_tags_func_t get_table_tags_func;
void *get_table_tags_user_data;
hb_destroy_func_t get_table_tags_destroy;
hb_shaper_object_dataset_t<hb_face_t> data;/* Various shaper data. */
hb_ot_face_t table; /* All the face's tables. */

View File

@ -231,7 +231,7 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font,
void *user_data HB_UNUSED)
{
/* TODO use font_extents.ascender+descender */
return font->y_scale;
return -font->y_scale;
}
static hb_position_t

View File

@ -651,7 +651,7 @@ struct hb_font_t
{
if (get_glyph_name (glyph, s, size)) return;
if (size && snprintf (s, size, "gid%u", glyph) < 0)
if (size && snprintf (s, size, "gid%" PRIu32, glyph) < 0)
*s = '\0';
}

View File

@ -37,10 +37,15 @@
#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"
#include FT_MODULE_H
#include FT_ADVANCES_H
#include FT_MULTIPLE_MASTERS_H
#include FT_OUTLINE_H
@ -225,7 +230,7 @@ _hb_ft_hb_font_check_changed (hb_font_t *font,
* Sets the FT_Load_Glyph load flags for the specified #hb_font_t.
*
* For more information, see
* https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx
* <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx>
*
* This function works with #hb_font_t objects created by
* hb_ft_font_create() or hb_ft_font_create_referenced().
@ -253,7 +258,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags)
* Fetches the FT_Load_Glyph load flags of the specified #hb_font_t.
*
* For more information, see
* https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx
* <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx>
*
* This function works with #hb_font_t objects created by
* hb_ft_font_create() or hb_ft_font_create_referenced().
@ -273,6 +278,33 @@ hb_ft_font_get_load_flags (hb_font_t *font)
return ft_font->load_flags;
}
/**
* hb_ft_font_get_ft_face: (skip)
* @font: #hb_font_t to work upon
*
* Fetches the FT_Face associated with the specified #hb_font_t
* font object.
*
* This function works with #hb_font_t objects created by
* hb_ft_font_create() or hb_ft_font_create_referenced().
*
* Return value: (nullable): the FT_Face found or `NULL`
*
* Since: 10.4.0
**/
FT_Face
hb_ft_font_get_ft_face (hb_font_t *font)
{
if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
return nullptr;
const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
return ft_font->ft_face;
}
#ifndef HB_DISABLE_DEPRECATED
/**
* hb_ft_font_get_face: (skip)
* @font: #hb_font_t to work upon
@ -286,18 +318,16 @@ hb_ft_font_get_load_flags (hb_font_t *font)
* Return value: (nullable): the FT_Face found or `NULL`
*
* Since: 0.9.2
* Deprecated: 10.4.0: Use hb_ft_font_get_ft_face() instead.
**/
FT_Face
hb_ft_font_get_face (hb_font_t *font)
{
if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
return nullptr;
const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
return ft_font->ft_face;
return hb_ft_font_get_ft_face (font);
}
#endif
/**
* hb_ft_font_lock_face: (skip)
* @font: #hb_font_t to work upon
@ -501,6 +531,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
@ -537,7 +587,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
@ -930,11 +993,15 @@ hb_ft_paint_glyph (hb_font_t *font,
hb_lock_t lock (ft_font->lock);
FT_Face ft_face = ft_font->ft_face;
FT_Long load_flags = ft_font->load_flags | FT_LOAD_NO_BITMAP | FT_LOAD_COLOR;
#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21301
load_flags |= FT_LOAD_NO_SVG;
#endif
/* We release the lock before calling into glyph callbacks, such that
* eg. draw API can call back into the face.*/
if (unlikely (FT_Load_Glyph (ft_face, gid,
ft_font->load_flags | FT_LOAD_COLOR)))
if (unlikely (FT_Load_Glyph (ft_face, gid, load_flags)))
return;
if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
@ -1104,6 +1171,45 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data
buffer, hb_free);
}
static unsigned
_hb_ft_get_table_tags (const hb_face_t *face HB_UNUSED,
unsigned int start_offset,
unsigned int *table_count,
hb_tag_t *table_tags,
void *user_data)
{
FT_Face ft_face = (FT_Face) user_data;
FT_ULong population = 0;
FT_Sfnt_Table_Info (ft_face,
0, // table_index; ignored
nullptr,
&population);
if (!table_count)
return population;
else
*table_count = 0;
if (unlikely (start_offset >= population))
return population;
unsigned end_offset = hb_min (start_offset + *table_count, (unsigned) population);
if (unlikely (end_offset < start_offset))
return population;
*table_count = end_offset - start_offset;
for (unsigned i = start_offset; i < end_offset; i++)
{
FT_ULong tag = 0, length;
FT_Sfnt_Table_Info (ft_face, i, &tag, &length);
table_tags[i - start_offset] = tag;
}
return population;
}
/**
* hb_ft_face_create:
* @ft_face: (destroy destroy) (scope notified): FT_Face to work upon
@ -1145,6 +1251,7 @@ hb_ft_face_create (FT_Face ft_face,
hb_blob_destroy (blob);
} else {
face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy);
hb_face_set_get_table_tags_func (face, _hb_ft_get_table_tags, ft_face, nullptr);
}
hb_face_set_index (face, ft_face->face_index);
@ -1215,7 +1322,7 @@ hb_ft_face_finalize (void *arg)
hb_face_t *
hb_ft_face_create_cached (FT_Face ft_face)
{
if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize))
if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != hb_ft_face_finalize))
{
if (ft_face->generic.finalizer)
ft_face->generic.finalizer (ft_face);
@ -1245,7 +1352,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
@ -1392,6 +1499,24 @@ hb_ft_font_create_referenced (FT_Face ft_face)
return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
}
static void * _hb_ft_alloc (FT_Memory memory, long size)
{ return hb_malloc (size); }
static void _hb_ft_free (FT_Memory memory, void *block)
{ hb_free (block); }
static void * _hb_ft_realloc (FT_Memory memory, long cur_size, long new_size, void *block)
{ return hb_realloc (block, new_size); }
static FT_MemoryRec_ m =
{
nullptr,
_hb_ft_alloc,
_hb_ft_free,
_hb_ft_realloc
};
static inline void free_static_ft_library ();
static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<FT_Library>,
@ -1400,16 +1525,19 @@ static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<F
static FT_Library create ()
{
FT_Library l;
if (FT_Init_FreeType (&l))
if (FT_New_Library (&m, &l))
return nullptr;
FT_Add_Default_Modules (l);
FT_Set_Default_Properties (l);
hb_atexit (free_static_ft_library);
return l;
}
static void destroy (FT_Library l)
{
FT_Done_FreeType (l);
FT_Done_Library (l);
}
static FT_Library get_null ()
{
@ -1424,9 +1552,76 @@ void free_static_ft_library ()
}
static FT_Library
get_ft_library ()
reference_ft_library ()
{
return static_ft_library.get_unconst ();
FT_Library l = static_ft_library.get_unconst ();
if (unlikely (FT_Reference_Library (l)))
{
DEBUG_MSG (FT, l, "FT_Reference_Library() failed");
return nullptr;
}
return l;
}
static hb_user_data_key_t ft_library_key = {0};
static void
finalize_ft_library (void *arg)
{
FT_Face ft_face = (FT_Face) arg;
FT_Done_Library ((FT_Library) ft_face->generic.data);
}
static void
destroy_ft_library (void *arg)
{
FT_Done_Library ((FT_Library) arg);
}
/**
* hb_ft_face_create_from_file_or_fail:
* @file_name: A font filename
* @index: The index of the face within the file
*
* Creates an #hb_face_t face object from the specified
* font file and face index.
*
* This is similar in functionality to hb_face_create_from_file_or_fail(),
* but uses the FreeType library for loading the font file.
*
* Return value: (transfer full): The new face object, or `NULL` if
* no face is found at the specified index or the file cannot be read.
*
* Since: 10.1.0
*/
hb_face_t *
hb_ft_face_create_from_file_or_fail (const char *file_name,
unsigned int index)
{
FT_Library ft_library = reference_ft_library ();
if (unlikely (!ft_library))
{
DEBUG_MSG (FT, ft_library, "reference_ft_library failed");
return nullptr;
}
FT_Face ft_face;
if (unlikely (FT_New_Face (ft_library,
file_name,
index,
&ft_face)))
return nullptr;
hb_face_t *face = hb_ft_face_create_referenced (ft_face);
FT_Done_Face (ft_face);
ft_face->generic.data = ft_library;
ft_face->generic.finalizer = finalize_ft_library;
if (hb_face_is_immutable (face))
return nullptr;
return face;
}
static void
@ -1465,32 +1660,47 @@ _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);
if (unlikely (!blob_length))
DEBUG_MSG (FT, font, "Font face has empty blob");
FT_Face ft_face = nullptr;
FT_Error err = FT_New_Memory_Face (get_ft_library (),
(const FT_Byte *) blob_data,
blob_length,
hb_face_get_index (font->face),
&ft_face);
if (unlikely (err)) {
FT_Library ft_library = reference_ft_library ();
if (unlikely (!ft_library))
{
hb_blob_destroy (blob);
DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed");
DEBUG_MSG (FT, font, "reference_ft_library failed");
return;
}
FT_Face ft_face = nullptr;
if (unlikely (FT_New_Memory_Face (ft_library,
(const FT_Byte *) blob_data,
blob_length,
hb_face_get_index (font->face),
&ft_face)))
{
hb_blob_destroy (blob);
DEBUG_MSG (FT, font, "FT_New_Memory_Face() failed");
return;
}
if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL))
FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
// Hook the blob to the FT_Face
ft_face->generic.data = blob;
ft_face->generic.finalizer = _release_blob;
// And the FT_Library to the blob
hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true);
_hb_ft_font_set_funcs (font, ft_face, true);
hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);

View File

@ -84,6 +84,9 @@ hb_ft_face_create_cached (FT_Face ft_face);
HB_EXTERN hb_face_t *
hb_ft_face_create_referenced (FT_Face ft_face);
HB_EXTERN hb_face_t *
hb_ft_face_create_from_file_or_fail (const char *file_name,
unsigned int index);
/*
* hb-font from ft-face.
@ -108,7 +111,7 @@ HB_EXTERN hb_font_t *
hb_ft_font_create_referenced (FT_Face ft_face);
HB_EXTERN FT_Face
hb_ft_font_get_face (hb_font_t *font);
hb_ft_font_get_ft_face (hb_font_t *font);
HB_EXTERN FT_Face
hb_ft_font_lock_face (hb_font_t *font);
@ -139,6 +142,13 @@ hb_ft_hb_font_changed (hb_font_t *font);
HB_EXTERN void
hb_ft_font_set_funcs (hb_font_t *font);
#ifndef HB_DISABLE_DEPRECATED
HB_DEPRECATED_FOR (hb_ft_font_get_ft_face)
HB_EXTERN FT_Face
hb_ft_font_get_face (hb_font_t *font);
#endif
HB_END_DECLS

View File

@ -0,0 +1,293 @@
/*
* Copyright © 2022 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.
*/
#ifndef HB_GEOMETRY_HH
#define HB_GEOMETRY_HH
#include "hb.hh"
struct hb_extents_t
{
hb_extents_t () {}
hb_extents_t (float xmin, float ymin, float xmax, float ymax) :
xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {}
bool is_empty () const { return xmin >= xmax || ymin >= ymax; }
bool is_void () const { return xmin > xmax; }
void union_ (const hb_extents_t &o)
{
xmin = hb_min (xmin, o.xmin);
ymin = hb_min (ymin, o.ymin);
xmax = hb_max (xmax, o.xmax);
ymax = hb_max (ymax, o.ymax);
}
void intersect (const hb_extents_t &o)
{
xmin = hb_max (xmin, o.xmin);
ymin = hb_max (ymin, o.ymin);
xmax = hb_min (xmax, o.xmax);
ymax = hb_min (ymax, o.ymax);
}
void
add_point (float x, float y)
{
if (unlikely (is_void ()))
{
xmin = xmax = x;
ymin = ymax = y;
}
else
{
xmin = hb_min (xmin, x);
ymin = hb_min (ymin, y);
xmax = hb_max (xmax, x);
ymax = hb_max (ymax, y);
}
}
float xmin = 0.f;
float ymin = 0.f;
float xmax = -1.f;
float ymax = -1.f;
};
struct hb_transform_t
{
hb_transform_t () {}
hb_transform_t (float xx, float yx,
float xy, float yy,
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. */
hb_transform_t r;
r.xx = o.xx * xx + o.yx * xy;
r.yx = o.xx * yx + o.yx * yy;
r.xy = o.xy * xx + o.yy * xy;
r.yy = o.xy * yx + o.yy * yy;
r.x0 = o.x0 * xx + o.y0 * xy + x0;
r.y0 = o.x0 * yx + o.y0 * yy + y0;
*this = r;
}
void transform_distance (float &dx, float &dy) const
{
float new_x = xx * dx + xy * dy;
float new_y = yx * dx + yy * dy;
dx = new_x;
dy = new_y;
}
void transform_point (float &x, float &y) const
{
transform_distance (x, y);
x += x0;
y += y0;
}
void transform_extents (hb_extents_t &extents) const
{
float quad_x[4], quad_y[4];
quad_x[0] = extents.xmin;
quad_y[0] = extents.ymin;
quad_x[1] = extents.xmin;
quad_y[1] = extents.ymax;
quad_x[2] = extents.xmax;
quad_y[2] = extents.ymin;
quad_x[3] = extents.xmax;
quad_y[3] = extents.ymax;
extents = hb_extents_t {};
for (unsigned i = 0; i < 4; i++)
{
transform_point (quad_x[i], quad_y[i]);
extents.add_point (quad_x[i], quad_y[i]);
}
}
void transform (const hb_transform_t &o) { multiply (o); }
void translate (float x, float y)
{
if (x == 0.f && y == 0.f)
return;
x0 += xx * x + xy * y;
y0 += yx * x + yy * y;
}
void scale (float scaleX, float scaleY)
{
if (scaleX == 1.f && scaleY == 1.f)
return;
xx *= scaleX;
yx *= scaleX;
xy *= scaleY;
yy *= scaleY;
}
void rotate (float rotation)
{
if (rotation == 0.f)
return;
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
rotation = rotation * HB_PI;
float c;
float s;
#ifdef HAVE_SINCOSF
sincosf (rotation, &s, &c);
#else
c = cosf (rotation);
s = sinf (rotation);
#endif
auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f};
transform (other);
}
void skew (float skewX, float skewY)
{
if (skewX == 0.f && skewY == 0.f)
return;
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
skewX = skewX * HB_PI;
skewY = skewY * HB_PI;
auto other = hb_transform_t{1.f,
skewY ? tanf (skewY) : 0.f,
skewX ? tanf (skewX) : 0.f,
1.f,
0.f, 0.f};
transform (other);
}
float xx = 1.f;
float yx = 0.f;
float xy = 0.f;
float yy = 1.f;
float x0 = 0.f;
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 {
UNBOUNDED,
BOUNDED,
EMPTY,
};
hb_bounds_t (status_t status) : status (status) {}
hb_bounds_t (const hb_extents_t &extents) :
status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {}
void union_ (const hb_bounds_t &o)
{
if (o.status == UNBOUNDED)
status = UNBOUNDED;
else if (o.status == BOUNDED)
{
if (status == EMPTY)
*this = o;
else if (status == BOUNDED)
extents.union_ (o.extents);
}
}
void intersect (const hb_bounds_t &o)
{
if (o.status == EMPTY)
status = EMPTY;
else if (o.status == BOUNDED)
{
if (status == UNBOUNDED)
*this = o;
else if (status == BOUNDED)
{
extents.intersect (o.extents);
if (extents.is_empty ())
status = EMPTY;
}
}
}
status_t status;
hb_extents_t extents;
};
struct hb_transform_decomposed_t
{
float translateX = 0;
float translateY = 0;
float rotation = 0; // in degrees, counter-clockwise
float scaleX = 1;
float scaleY = 1;
float skewX = 0; // in degrees, counter-clockwise
float skewY = 0; // in degrees, counter-clockwise
float tCenterX = 0;
float tCenterY = 0;
operator bool () const
{
return translateX || translateY ||
rotation ||
scaleX != 1 || scaleY != 1 ||
skewX || skewY ||
tCenterX || tCenterY;
}
hb_transform_t to_transform () const
{
hb_transform_t t;
t.translate (translateX + tCenterX, translateY + tCenterY);
t.rotate (rotation);
t.scale (scaleX, scaleY);
t.skew (-skewX, skewY);
t.translate (-tCenterX, -tCenterY);
return t;
}
};
#endif /* HB_GEOMETRY_HH */

View File

@ -324,6 +324,16 @@ struct hb_is_sink_of
(hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator)
struct
{
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
unsigned operator () (const Iterable &_) const { return hb_len (hb_iter (_)); }
unsigned operator () (unsigned _) const { return _; }
}
HB_FUNCOBJ (hb_len_of);
/* Range-based 'for' for iterables. */
template <typename Iterable,

Some files were not shown because too many files have changed in this diff Show More