diff --git a/gfx/harfbuzz/Makefile.am b/gfx/harfbuzz/Makefile.am --- a/gfx/harfbuzz/Makefile.am +++ b/gfx/harfbuzz/Makefile.am @@ -24,10 +24,11 @@ subprojects/glib.wrap \ subprojects/google-benchmark.wrap \ subprojects/ragel.wrap \ subprojects/packagefiles/ragel/meson.build \ mingw-configure.sh \ + xkcd.png \ $(NULL) MAINTAINERCLEANFILES = \ $(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \ $(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL) \ diff --git a/gfx/harfbuzz/NEWS b/gfx/harfbuzz/NEWS --- a/gfx/harfbuzz/NEWS +++ b/gfx/harfbuzz/NEWS @@ -1,5 +1,20 @@ +Overview of changes leading to 8.3.0 +Saturday, November 11, 2023 +==================================== +- Improve memory barrier to fix potential segfaults. +- Various build fixes. +- Various subsetting and instancing fixes. +- Rename “hb-subset” option “--instance” to “--variations” to match the other + tools. Old option is kept as an alias. + +- New API: +HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION + +- Deprecated API: +HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION + Overview of changes leading to 8.2.2 Wednesday, October 18, 2023 “From the river to the sea, Palestine will be free” ==================================== - Fix regression from 8.1.0 in shaping fonts with duplicate feature tags. diff --git a/gfx/harfbuzz/README.md b/gfx/harfbuzz/README.md --- a/gfx/harfbuzz/README.md +++ b/gfx/harfbuzz/README.md @@ -11,12 +11,14 @@ # HarfBuzz HarfBuzz is a text shaping engine. It primarily supports [OpenType][1], but also [Apple Advanced Typography][2]. HarfBuzz is used in Android, Chrome, ChromeOS, Firefox, GNOME, GTK+, KDE, Qt, LibreOffice, OpenJDK, XeTeX, -PlayStation, Microsoft Edge, Photoshop, Illustrator, InDesign, -and other places. +PlayStation, Microsoft Edge, Adobe Photoshop, Illustrator, InDesign, +Godot Engine, and other places. + +[![xkcd-derived image](xkcd.png)](https://xkcd.com/2347/) For bug reports, mailing list, and other information please visit: http://harfbuzz.org/ diff --git a/gfx/harfbuzz/configure.ac b/gfx/harfbuzz/configure.ac --- a/gfx/harfbuzz/configure.ac +++ b/gfx/harfbuzz/configure.ac @@ -1,8 +1,8 @@ AC_PREREQ([2.64]) AC_INIT([HarfBuzz], - [8.2.2], + [8.3.0], [https://github.com/harfbuzz/harfbuzz/issues/new], [harfbuzz], [http://harfbuzz.org/]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/gfx/harfbuzz/moz.yaml b/gfx/harfbuzz/moz.yaml --- a/gfx/harfbuzz/moz.yaml +++ b/gfx/harfbuzz/moz.yaml @@ -18,15 +18,15 @@ # Usually different from repository url url: https://github.com/harfbuzz/harfbuzz # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: 8.2.2 (2023-10-18T01:18:14+03:00). + release: 8.3.0 (2023-11-11T16:07:57+02:00). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: 8.2.2 + revision: 8.3.0 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ # Multiple licenses can be specified (as a YAML list) # A "LICENSE" file must exist containing the full license text diff --git a/gfx/harfbuzz/src/OT/Color/CBDT/CBDT.hh b/gfx/harfbuzz/src/OT/Color/CBDT/CBDT.hh --- a/gfx/harfbuzz/src/OT/Color/CBDT/CBDT.hh +++ b/gfx/harfbuzz/src/OT/Color/CBDT/CBDT.hh @@ -202,10 +202,11 @@ { bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const { 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)); case 3: return_trace (u.format3.sanitize (c, glyph_count)); default:return_trace (true); @@ -376,10 +377,11 @@ bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && firstGlyphIndex <= lastGlyphIndex && offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1)); } const IndexSubtable* get_subtable (const void *base) const @@ -633,10 +635,11 @@ bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && horizontal.sanitize (c) && vertical.sanitize (c)); } @@ -736,11 +739,13 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version.major == 2 || version.major == 3) && + hb_barrier () && sizeTables.sanitize (c, this)); } static bool sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime) @@ -973,10 +978,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version.major == 2 || version.major == 3)); } protected: FixedVersion<> version; diff --git a/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh b/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh --- a/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh +++ b/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh @@ -1946,14 +1946,15 @@ static constexpr hb_tag_t tableTag = HB_OT_TAG_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, unsigned int start_offset, unsigned int *count, /* IN/OUT. May be NULL. */ @@ -2030,10 +2031,12 @@ void closure_forV1 (hb_set_t *glyphset, hb_set_t *layer_indices, hb_set_t *palette_indices) const { if (version != 1) return; + hb_barrier (); + hb_set_t visited_glyphs; hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices); const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; @@ -2056,14 +2059,16 @@ 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 () && + version == 1 && baseGlyphList.sanitize (c, this) && layerList.sanitize (c, this) && clipList.sanitize (c, this) && varIdxMap.sanitize (c, this) && varStore.sanitize (c, this)))); @@ -2282,10 +2287,12 @@ bool has_paint_for_glyph (hb_codepoint_t glyph) const { if (version == 1) { + hb_barrier (); + const Paint *paint = get_base_glyph_paint (glyph); return paint != nullptr; } @@ -2311,10 +2318,12 @@ hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); c.current_glyphs.add (glyph); if (version == 1) { + hb_barrier (); + const Paint *paint = get_base_glyph_paint (glyph); if (paint) { // COLRv1 glyph diff --git a/gfx/harfbuzz/src/OT/Color/CPAL/CPAL.hh b/gfx/harfbuzz/src/OT/Color/CPAL/CPAL.hh --- a/gfx/harfbuzz/src/OT/Color/CPAL/CPAL.hh +++ b/gfx/harfbuzz/src/OT/Color/CPAL/CPAL.hh @@ -212,17 +212,21 @@ void collect_name_ids (const hb_map_t *color_index_map, 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 (*this); } public: bool serialize (hb_serialize_context_t *c, @@ -310,19 +314,23 @@ first_color_to_layer_index, retained_color_indices)) return_trace (false); if (version == 1) + { + hb_barrier (); return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map)); + } return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { 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))); } diff --git a/gfx/harfbuzz/src/OT/Color/sbix/sbix.hh b/gfx/harfbuzz/src/OT/Color/sbix/sbix.hh --- a/gfx/harfbuzz/src/OT/Color/sbix/sbix.hh +++ b/gfx/harfbuzz/src/OT/Color/sbix/sbix.hh @@ -366,10 +366,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && version >= 1 && strikes.sanitize (c, this))); } bool diff --git a/gfx/harfbuzz/src/OT/Color/svg/svg.hh b/gfx/harfbuzz/src/OT/Color/svg/svg.hh --- a/gfx/harfbuzz/src/OT/Color/svg/svg.hh +++ b/gfx/harfbuzz/src/OT/Color/svg/svg.hh @@ -54,10 +54,11 @@ bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && svgDoc.sanitize (c, base, svgDocLength)); } protected: HBUINT16 startGlyphID; /* The first glyph ID in the range described by diff --git a/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh b/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh --- a/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh +++ b/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh @@ -62,10 +62,11 @@ #endif bool sanitize (hb_sanitize_context_t *c) const { 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)); #ifndef HB_NO_BEYOND_64K diff --git a/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh b/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh --- a/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh +++ b/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh @@ -289,10 +289,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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)); case 3: return_trace (u.format3.sanitize (c)); default:return_trace (true); @@ -439,10 +440,24 @@ 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 void collect_coverage (hb_vector_t &sets) const { for (const auto &offset : coverage) { @@ -459,22 +474,29 @@ out->format = format; bool ret = true; for (const Offset32To& offset : coverage.iter ()) { + auto snap = c->serializer->snapshot (); auto *o = out->coverage.serialize_append (c->serializer); if (unlikely (!o)) { ret = false; 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 ()); } return_trace (ret && out->coverage.len); } @@ -511,10 +533,19 @@ case 1: u.format1.collect_coverage (sets); return; default:return; } } + 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); switch (u.format) { case 1: return_trace (u.format1.subset (c)); @@ -524,10 +555,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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); } } @@ -598,10 +630,11 @@ return_trace (version.sanitize (c) && glyphClassDef.sanitize (c, this) && attachList.sanitize (c, this) && ligCaretList.sanitize (c, this) && markAttachClassDef.sanitize (c, this) && + hb_barrier () && (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) && (version.to_int () < 0x00010003u || varStore.sanitize (c, this))); } static void remap_varidx_after_instantiation (const hb_map_t& varidx_map, @@ -625,27 +658,32 @@ } 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); + out->version.major = version.major; + out->version.minor = version.minor; 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; + auto snapshot_version0 = c->serializer->snapshot (); if (version.to_int () >= 0x00010002u) { + if (unlikely (!c->serializer->embed (markGlyphSetsDef))) return_trace (false); subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); } bool subset_varstore = false; + auto snapshot_version2 = c->serializer->snapshot (); if (version.to_int () >= 0x00010003u) { + if (unlikely (!c->serializer->embed (varStore))) return_trace (false); if (c->plan->all_axes_pinned) out->varStore = 0; else if (c->plan->normalized_coords) { if (varStore) @@ -664,19 +702,25 @@ } else subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ()); } + if (subset_varstore) { out->version.minor = 3; + c->plan->has_gdef_varstore = true; } else if (subset_markglyphsetsdef) { out->version.minor = 2; + c->serializer->revert (snapshot_version2); } else { out->version.minor = 0; + c->serializer->revert (snapshot_version0); } + bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + return_trace (subset_glyphclassdef || subset_attachlist || subset_ligcaretlist || subset_markattachclassdef || (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || (out->version.to_int () >= 0x00010003u && subset_varstore)); } @@ -707,10 +751,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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 case 2: return_trace (u.version2.sanitize (c)); #endif diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh @@ -23,10 +23,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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)); case 3: return_trace (u.format3.sanitize (c)); default:return_trace (true); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh @@ -36,13 +36,19 @@ hb_font_t *font = c->font; *x = font->em_fscale_x (xCoordinate); *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 { TRACE_SUBSET (this); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh @@ -6,27 +6,29 @@ namespace GPOS_impl { struct AnchorMatrix { HBUINT16 rows; /* Number of rows */ - UnsizedArrayOf> + UnsizedArrayOf> matrixZ; /* Matrix of offsets to Anchor tables-- * from beginning of AnchorMatrix table */ public: DEFINE_SIZE_ARRAY (2, matrixZ); bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const { 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); 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); } @@ -36,10 +38,11 @@ { *found = false; 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; } template 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); } }; } diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/Common.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/Common.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/Common.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/Common.hh @@ -21,11 +21,11 @@ template static void SinglePos_serialize (hb_serialize_context_t *c, const SrcLookup *src, Iterator it, const hb_hashmap_t> *layout_variation_idx_delta_map, - bool all_axes_pinned); + unsigned new_format); } } } diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh @@ -9,25 +9,25 @@ 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); if (unlikely (!out)) return_trace (false); @@ -36,15 +36,15 @@ ret |= out->exitAnchor.serialize_subset (c, exitAnchor, src_base); return_trace (ret); } protected: - Offset16To + Offset16To entryAnchor; /* Offset to EntryAnchor table--from * beginning of CursivePos * subtable--may be NULL */ - Offset16To + Offset16To exitAnchor; /* Offset to ExitAnchor table--from * beginning of CursivePos * subtable--may be NULL */ public: DEFINE_SIZE_STATIC (4); @@ -126,10 +126,11 @@ hb_buffer_t *buffer = c->buffer; 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); unsigned unsafe_from; if (unlikely (!skippy_iter.prev (&unsafe_from))) @@ -143,10 +144,11 @@ unlikely (!prev_record.exitAnchor.sanitize (&c->sanitizer, this))) { 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; if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) @@ -260,11 +262,11 @@ template 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; this->entryExitRecord.len = it.len (); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -40,10 +40,11 @@ TRACE_SANITIZE (this); return_trace (c->check_struct (this) && mark1Coverage.sanitize (c, this) && mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this) && + hb_barrier () && mark2Array.sanitize (c, this, (unsigned int) classCount)); } bool intersects (const hb_set_t *glyphs) const { diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh @@ -34,10 +34,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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 (); typename PairSet::sanitize_closure_t closure = { @@ -129,24 +130,37 @@ const hb_map_t &glyph_map = *c->plan->glyph_map; 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 newFormats = hb_pair (valueFormat[0], valueFormat[1]); + + if (c->plan->normalized_coords) + { + /* 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_pair_t newFormats = compute_effective_value_formats (glyphset); - out->valueFormat[0] = newFormats.first; - out->valueFormat[1] = newFormats.second; - } + 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); - 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 (); + 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); } + + out->valueFormat[0] = newFormats.first; + out->valueFormat[1] = newFormats.second; hb_sorted_vector_t new_coverage; + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) @@ -173,11 +187,13 @@ return_trace (bool (new_coverage)); } - hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset) const + hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset, + bool strip_hints, bool strip_empty, + const hb_hashmap_t> *varidx_delta_map = nullptr) const { unsigned record_size = PairSet::get_size (valueFormat); unsigned format1 = 0; unsigned format2 = 0; @@ -193,12 +209,12 @@ unsigned count = set.len; for (unsigned i = 0; i < count; i++) { 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 (record, record_size); } if (format1 == valueFormat[0] && format2 == valueFormat[1]) diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -6,11 +6,11 @@ namespace OT { namespace Layout { namespace GPOS_impl { template -struct PairPosFormat2_4 +struct PairPosFormat2_4 : ValueBase { protected: HBUINT16 format; /* Format identifier--format = 2 */ typename Types::template OffsetTo coverage; /* Offset to Coverage table--from @@ -285,22 +285,35 @@ unsigned len1 = valueFormat1.get_len (); unsigned len2 = valueFormat2.get_len (); hb_pair_t 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 class2_idxs (+ hb_range ((unsigned) class2Count) | hb_filter (klass2_map)); for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) { for (unsigned class2_idx : class2_idxs) @@ -324,11 +337,13 @@ return_trace (out->class1Count && out->class2Count && bool (it)); } hb_pair_t 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> *varidx_delta_map = nullptr) const { unsigned len1 = valueFormat1.get_len (); unsigned len2 = valueFormat2.get_len (); unsigned record_size = len1 + len2; @@ -338,12 +353,12 @@ for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) { 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) break; } diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairSet.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairSet.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/PairSet.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/PairSet.hh @@ -7,11 +7,11 @@ namespace Layout { namespace GPOS_impl { template -struct PairSet +struct PairSet : ValueBase { template friend struct PairPosFormat1_3; using PairValueRecord = GPOS_impl::PairValueRecord; @@ -43,14 +43,16 @@ }; 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; return_trace (c->lazy_some_gpos || (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh @@ -27,11 +27,11 @@ int cmp (hb_codepoint_t k) const { return secondGlyph.cmp (k); } struct context_t { - const void *base; + const ValueBase *base; const ValueFormat *valueFormats; const ValueFormat *newFormats; unsigned len1; /* valueFormats[0].get_len() */ const hb_map_t *glyph_map; const hb_hashmap_t> *layout_variation_idx_delta_map; @@ -60,11 +60,11 @@ return_trace (true); } 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 (); const hb_array_t values_array = values.as_array (record1_len + record2_len); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh @@ -37,18 +37,16 @@ hb_requires (hb_is_iterator (Iterator))> void serialize (hb_serialize_context_t *c, const SrcLookup* src, Iterator glyph_val_iter_pairs, const hb_hashmap_t> *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); u.format = format; @@ -87,12 +85,12 @@ static void SinglePos_serialize (hb_serialize_context_t *c, const SrcLookup *src, Iterator it, const hb_hashmap_t> *layout_variation_idx_delta_map, - bool all_axes_pinned) -{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); } + unsigned new_format) +{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, new_format); } } } } diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh @@ -6,11 +6,11 @@ namespace OT { namespace Layout { namespace GPOS_impl { -struct SinglePosFormat1 +struct SinglePosFormat1 : ValueBase { protected: HBUINT16 format; /* Format identifier--format = 1 */ Offset16To coverage; /* Offset to Coverage table--from @@ -26,10 +26,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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 * sanitizer max ops to take this into account. * @@ -144,18 +145,42 @@ const hb_map_t &glyph_map = *c->plan->glyph_map; 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) | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) ; 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); } }; } diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh @@ -5,11 +5,11 @@ namespace OT { namespace Layout { namespace GPOS_impl { -struct SinglePosFormat2 +struct SinglePosFormat2 : ValueBase { protected: HBUINT16 format; /* Format identifier--format = 2 */ Offset16To coverage; /* Offset to Coverage table--from @@ -141,10 +141,41 @@ ; coverage.serialize_serialize (c, glyphs); } + template + 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> *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); const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; @@ -161,12 +192,17 @@ values_array.sub_array (_.second * sub_length, sub_length)); }) ; + 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); } }; diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh --- a/gfx/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh @@ -7,10 +7,12 @@ namespace Layout { namespace GPOS_impl { typedef HBUINT16 Value; +struct ValueBase {}; // Dummy base class tag for OffsetTo bases. + typedef UnsizedArrayOf ValueRecord; struct ValueFormat : HBUINT16 { enum Flags { @@ -76,11 +78,11 @@ return result; } 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 { bool ret = false; unsigned int format = *this; @@ -140,34 +142,53 @@ values++; } 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> *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; } template - 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> *varidx_delta_map) const { unsigned int new_format = 0; for (const hb_array_t& 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> *layout_variation_idx_delta_map) const { unsigned int format = *this; if (!format) return; @@ -215,11 +236,11 @@ if (!(new_format & flag)) return nullptr; return reinterpret_cast (c->copy (value)); } void collect_variation_indices (hb_collect_variation_indices_context_t *c, - const void *base, + const ValueBase *base, const hb_array_t& values) const { unsigned format = *this; unsigned i = 0; if (format & xPlacement) i++; @@ -249,21 +270,12 @@ (base + get_device (&(values[i]))).collect_variation_indices (c); i++; } } - 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; if (format & xPlacement) values++; if (format & yPlacement) values++; @@ -276,34 +288,35 @@ if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false; return true; } - static inline Offset16To& get_device (Value* value) + static inline Offset16To& get_device (Value* value) { - return *static_cast *> (value); + return *static_cast *> (value); } - static inline const Offset16To& get_device (const Value* value) + static inline const Offset16To& get_device (const Value* value) { - return *static_cast *> (value); + return *static_cast *> (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); auto &offset = *static_cast *> (value); 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> *layout_variation_idx_delta_map) const { if (!value) return; unsigned varidx = (base + get_device (src_value)).get_variation_index (); @@ -311,11 +324,12 @@ if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return; *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> *layout_variation_idx_delta_map, unsigned int new_format, Flags flag) const { // Filter by new format. @@ -352,11 +366,11 @@ { unsigned int format = *this; 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); if (unlikely (!c->check_range (values, get_size ()))) return_trace (false); @@ -364,25 +378,26 @@ return_trace (true); 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 (); if (!c->check_range (values, count, size)) return_trace (false); 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); if (!has_device ()) return_trace (true); @@ -401,10 +416,24 @@ { if (value) return; *format = *format & ~flag; } + void update_var_flag (const Value* value, Flags flag, + unsigned int* format, const ValueBase *base, + const hb_hashmap_t> *varidx_delta_map) const + { + if (*value) + { + unsigned varidx = (base + get_device (value)).get_variation_index (); + hb_pair_t *varidx_delta; + if (varidx_delta_map->has (varidx, &varidx_delta) && + varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + return; + } + *format = *format & ~flag; + } }; } } } diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh --- a/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh +++ b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh @@ -31,13 +31,15 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) return_trace (false); + hb_barrier (); const auto &lookahead = StructAfter (backtrack); if (!lookahead.sanitize (c, this)) return_trace (false); + hb_barrier (); const auto &substitute = StructAfter (lookahead); return_trace (substitute.sanitize (c)); } bool intersects (const hb_set_t *glyphs) const @@ -107,16 +109,16 @@ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (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 (backtrack); const auto &substitute = StructAfter (lookahead); if (unlikely (index >= substitute.len)) return_trace (false); diff --git a/gfx/harfbuzz/src/OT/Layout/types.hh b/gfx/harfbuzz/src/OT/Layout/types.hh --- a/gfx/harfbuzz/src/OT/Layout/types.hh +++ b/gfx/harfbuzz/src/OT/Layout/types.hh @@ -36,12 +36,12 @@ static constexpr unsigned size = 2; using large_int = uint32_t; using HBUINT = HBUINT16; using HBGlyphID = HBGlyphID16; using Offset = Offset16; - template - using OffsetTo = OT::Offset16To; + template + using OffsetTo = OT::Offset16To; template using ArrayOf = OT::Array16Of; template using SortedArrayOf = OT::SortedArray16Of; }; @@ -50,12 +50,12 @@ static constexpr unsigned size = 3; using large_int = uint64_t; using HBUINT = HBUINT24; using HBGlyphID = HBGlyphID24; using Offset = Offset24; - template - using OffsetTo = OT::Offset24To; + template + using OffsetTo = OT::Offset24To; template using ArrayOf = OT::Array24Of; template using SortedArrayOf = OT::SortedArray24Of; }; diff --git a/gfx/harfbuzz/src/OT/name/name.hh b/gfx/harfbuzz/src/OT/name/name.hh --- a/gfx/harfbuzz/src/OT/name/name.hh +++ b/gfx/harfbuzz/src/OT/name/name.hh @@ -240,11 +240,13 @@ } 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. */ HBUINT16 encodingID; /* Platform-specific encoding ID. */ HBUINT16 languageID; /* Language ID. */ @@ -463,10 +465,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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) && sanitize_records (c)); } diff --git a/gfx/harfbuzz/src/gen-use-table.py b/gfx/harfbuzz/src/gen-use-table.py --- a/gfx/harfbuzz/src/gen-use-table.py +++ b/gfx/harfbuzz/src/gen-use-table.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 # flake8: noqa: F821 -import logging -logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) - """usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt Input files: * https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt * https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt @@ -16,10 +13,14 @@ * https://unicode.org/Public/UCD/latest/ucd/Scripts.txt * ms-use/IndicSyllabicCategory-Additional.txt * ms-use/IndicPositionalCategory-Additional.txt """ +import logging +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) + + import sys if len (sys.argv) != 10: sys.exit (__doc__) diff --git a/gfx/harfbuzz/src/graph/classdef-graph.hh b/gfx/harfbuzz/src/graph/classdef-graph.hh --- a/gfx/harfbuzz/src/graph/classdef-graph.hh +++ b/gfx/harfbuzz/src/graph/classdef-graph.hh @@ -37,10 +37,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; constexpr unsigned min_size = OT::ClassDefFormat1_3::min_size; if (vertex_len < min_size) return false; + hb_barrier (); return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size (); } }; struct ClassDefFormat2 : public OT::ClassDefFormat2_4 @@ -48,10 +49,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; constexpr unsigned min_size = OT::ClassDefFormat2_4::min_size; if (vertex_len < min_size) return false; + hb_barrier (); return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); } }; struct ClassDef : public OT::ClassDef @@ -112,10 +114,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { 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); case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); #ifndef HB_NO_BEYOND_64K diff --git a/gfx/harfbuzz/src/graph/coverage-graph.hh b/gfx/harfbuzz/src/graph/coverage-graph.hh --- a/gfx/harfbuzz/src/graph/coverage-graph.hh +++ b/gfx/harfbuzz/src/graph/coverage-graph.hh @@ -37,10 +37,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size; if (vertex_len < min_size) return false; + hb_barrier (); return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); } }; struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4 @@ -48,10 +49,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size; if (vertex_len < min_size) return false; + hb_barrier (); return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); } }; struct Coverage : public OT::Layout::Common::Coverage @@ -136,10 +138,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { 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); case 2: return ((CoverageFormat2*)this)->sanitize (vertex); #ifndef HB_NO_BEYOND_64K diff --git a/gfx/harfbuzz/src/graph/graph.hh b/gfx/harfbuzz/src/graph/graph.hh --- a/gfx/harfbuzz/src/graph/graph.hh +++ b/gfx/harfbuzz/src/graph/graph.hh @@ -565,10 +565,11 @@ } update_distances (); hb_priority_queue_t queue; + queue.alloc (vertices_.length); hb_vector_t &sorted_graph = vertices_scratch_; if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; hb_vector_t id_map; if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; @@ -1368,10 +1369,11 @@ for (unsigned i = 0; i < count; i++) vertices_.arrayZ[i].distance = hb_int_max (int64_t); vertices_.tail ().distance = 0; hb_priority_queue_t queue; + queue.alloc (count); queue.insert (0, vertices_.length - 1); hb_vector_t visited; visited.resize (vertices_.length); diff --git a/gfx/harfbuzz/src/graph/gsubgpos-graph.hh b/gfx/harfbuzz/src/graph/gsubgpos-graph.hh --- a/gfx/harfbuzz/src/graph/gsubgpos-graph.hh +++ b/gfx/harfbuzz/src/graph/gsubgpos-graph.hh @@ -74,10 +74,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { 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 (); } bool is_extension (hb_tag_t table_tag) const { @@ -349,10 +350,11 @@ { bool sanitize (const graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; if (vertex_len < OT::LookupList::min_size) return false; + hb_barrier (); return vertex_len >= OT::LookupList::item_size * this->len; } }; struct GSTAR : public OT::GSUBGPOS @@ -362,10 +364,11 @@ const auto& r = graph.root (); GSTAR* gstar = (GSTAR*) r.obj.head; if (!gstar || !gstar->sanitize (r)) return nullptr; + hb_barrier (); return gstar; } const void* get_lookup_list_field_offset () const @@ -381,10 +384,11 @@ bool sanitize (const graph_t::vertex_t& vertex) { int64_t len = vertex.obj.tail - vertex.obj.head; if (len < OT::GSUBGPOS::min_size) return false; + hb_barrier (); return len >= get_size (); } void find_lookups (graph_t& graph, hb_hashmap_t& lookups /* OUT */) diff --git a/gfx/harfbuzz/src/graph/markbasepos-graph.hh b/gfx/harfbuzz/src/graph/markbasepos-graph.hh --- a/gfx/harfbuzz/src/graph/markbasepos-graph.hh +++ b/gfx/harfbuzz/src/graph/markbasepos-graph.hh @@ -38,10 +38,11 @@ { bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const { 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; } @@ -126,10 +127,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { 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 (); } bool shrink (gsubgpos_graph_context_t& c, @@ -493,10 +495,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { 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: return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); #ifndef HB_NO_BEYOND_64K diff --git a/gfx/harfbuzz/src/graph/pairpos-graph.hh b/gfx/harfbuzz/src/graph/pairpos-graph.hh --- a/gfx/harfbuzz/src/graph/pairpos-graph.hh +++ b/gfx/harfbuzz/src/graph/pairpos-graph.hh @@ -40,10 +40,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; if (vertex_len < min_size) return false; + hb_barrier (); return vertex_len >= min_size + pairSet.get_size () - pairSet.len.get_size(); } @@ -196,10 +197,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { size_t vertex_len = vertex.table_size (); unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; if (vertex_len < min_size) return false; + hb_barrier (); const unsigned class1_count = class1Count; return vertex_len >= min_size + class1_count * get_class1_record_size (); } @@ -623,10 +625,11 @@ bool sanitize (graph_t::vertex_t& vertex) const { 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: return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); case 2: diff --git a/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh b/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh @@ -73,10 +73,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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)))); } diff --git a/gfx/harfbuzz/src/hb-aat-layout-bsln-table.hh b/gfx/harfbuzz/src/hb-aat-layout-bsln-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-bsln-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-bsln-table.hh @@ -121,10 +121,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this) && defaultBaseline < 32))) return_trace (false); + hb_barrier (); switch (format) { case 0: return_trace (parts.format0.sanitize (c)); case 1: return_trace (parts.format1.sanitize (c)); diff --git a/gfx/harfbuzz/src/hb-aat-layout-common.hh b/gfx/harfbuzz/src/hb-aat-layout-common.hh --- a/gfx/harfbuzz/src/hb-aat-layout-common.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-common.hh @@ -189,18 +189,20 @@ bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && first <= last && valuesZ.sanitize (c, base, last - first + 1)); } template bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && first <= last && valuesZ.sanitize (c, base, last - first + 1, std::forward (ds)...)); } HBGlyphID16 last; /* Last GlyphID in this segment */ @@ -358,10 +360,11 @@ 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)); } protected: @@ -413,10 +416,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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)); @@ -427,10 +431,11 @@ } bool sanitize (hb_sanitize_context_t *c, const void *base) const { 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)); @@ -556,10 +561,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned int *num_entries_out = nullptr) const { 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); const HBUSHORT *states = (this+stateArrayTable).arrayZ; const Entry *entries = (this+entryTable).arrayZ; diff --git a/gfx/harfbuzz/src/hb-aat-layout-feat-table.hh b/gfx/harfbuzz/src/hb-aat-layout-feat-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-feat-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-feat-table.hh @@ -136,10 +136,11 @@ bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && (base+settingTableZ).sanitize (c, nSettings))); } protected: HBUINT16 feature; /* Feature type. */ @@ -198,10 +199,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && version.major == 1 && namesZ.sanitize (c, featureNameCount, this))); } protected: diff --git a/gfx/harfbuzz/src/hb-aat-layout-just-table.hh b/gfx/harfbuzz/src/hb-aat-layout-just-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-just-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-just-table.hh @@ -183,10 +183,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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)); @@ -218,10 +219,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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++) { const ActionSubrecord& subrecord = StructAtOffset (this, offset); @@ -387,10 +389,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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))); } diff --git a/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh b/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh @@ -52,10 +52,11 @@ if (likely (!tupleCount || !c)) return value; unsigned int offset = value; const FWORD *pv = &StructAtOffset (base, offset); if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0; + hb_barrier (); return *pv; } struct hb_glyph_pair_t @@ -257,10 +258,11 @@ if (!c->sanitizer.check_array (actions, depth, tuple_count)) { depth = 0; return; } + hb_barrier (); hb_mask_t kern_mask = c->plan->kern_mask; /* From Apple 'kern' spec: * "Each pops one glyph from the kerning stack and applies the kerning value to it. @@ -387,10 +389,11 @@ const UnsizedArrayOf &arrayZ = this+array; unsigned int kern_idx = l + r; 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); } bool apply (hb_aat_apply_context_t *c) const @@ -427,10 +430,11 @@ { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && leftClassTable.sanitize (c, this) && rightClassTable.sanitize (c, this) && + hb_barrier () && c->check_range (this, array))); } protected: KernSubTableHeader header; @@ -507,10 +511,11 @@ /* Indexed into glyph outline. */ /* Each action (record in ankrData) contains two 16-bit fields, so we must 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; hb_position_t markY = 0; hb_position_t currX = 0; @@ -535,10 +540,11 @@ /* Indexed into 'ankr' table. */ /* Each action (record in ankrData) contains two 16-bit fields, so we must 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, markAnchorPoint, c->sanitizer.get_num_glyphs ()); @@ -555,10 +561,11 @@ { /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex 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++; int currY = *data++; @@ -637,20 +644,22 @@ unsigned int offset = l + r; if (unlikely (offset < l)) return 0; /* Addition overflow. */ if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0; const FWORD32 *v = &StructAtOffset (&(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; 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 (&(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); } } bool apply (hb_aat_apply_context_t *c) const @@ -672,10 +681,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && (is_long () ? ( u.l.rowIndexTable.sanitize (c, this) && u.l.columnIndexTable.sanitize (c, this) && c->check_range (this, u.l.array) @@ -785,13 +795,14 @@ } 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)); } @@ -934,23 +945,25 @@ } 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; const SubTable *st = &thiz()->firstSubTable; unsigned int count = thiz()->tableCount; for (unsigned int i = 0; i < count; i++) { 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 * kern subtable that exceeds 64kb. Looks like, the subtable length * is simply ignored. Which makes sense. It's only needed if you diff --git a/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh b/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -257,11 +257,13 @@ else { unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; - if (!replacement->sanitize (&c->sanitizer) || !*replacement) + if (!(replacement->sanitize (&c->sanitizer) && + hb_barrier () && + *replacement)) replacement = nullptr; } if (replacement) { buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); @@ -285,11 +287,13 @@ else { unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; - if (!replacement->sanitize (&c->sanitizer) || !*replacement) + if (!(replacement->sanitize (&c->sanitizer) && + hb_barrier () && + *replacement)) replacement = nullptr; } if (replacement) { buffer->info[idx].codepoint = *replacement; @@ -313,11 +317,11 @@ const OT::GDEF &gdef; bool mark_set; bool has_glyph_classes; unsigned int mark; const ContextualSubtable *table; - const UnsizedListOfOffset16To, HBUINT, false> &subs; + const UnsizedListOfOffset16To, HBUINT, void, false> &subs; }; bool apply (hb_aat_apply_context_t *c) const { TRACE_APPLY (this); @@ -334,10 +338,11 @@ { TRACE_SANITIZE (this); unsigned int num_entries = 0; if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); + hb_barrier (); if (!Types::extended) return_trace (substitutionTables.sanitize (c, this, 0)); unsigned int num_lookups = 0; @@ -357,11 +362,11 @@ } protected: StateTable machine; - NNOffsetTo, HBUINT, false>, HBUINT> + NNOffsetTo, HBUINT, void, false>, HBUINT> substitutionTables; public: DEFINE_SIZE_STATIC (20); }; @@ -511,30 +516,33 @@ DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); if (unlikely (!buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]))) return; if (unlikely (!actionData->sanitize (&c->sanitizer))) break; + hb_barrier (); action = *actionData; uint32_t uoffset = action & LigActionOffset; if (uoffset & 0x20000000) uoffset |= 0xC0000000; /* Sign-extend. */ int32_t offset = (int32_t) uoffset; unsigned int component_idx = buffer->cur().codepoint + offset; component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); const HBUINT16 &componentData = component[component_idx]; if (unlikely (!componentData.sanitize (&c->sanitizer))) break; + hb_barrier (); ligature_idx += componentData; DEBUG_MSG (APPLY, nullptr, "Action store %d last %d", bool (action & LigActionStore), bool (action & LigActionLast)); if (action & (LigActionStore | LigActionLast)) { ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); const HBGlyphID16 &ligatureData = ligature[ligature_idx]; if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; + hb_barrier (); hb_codepoint_t lig = ligatureData; DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); if (unlikely (!buffer->replace_glyph (lig))) return; @@ -585,10 +593,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); /* The rest of array sanitizations are done at run-time. */ return_trace (c->check_struct (this) && machine.sanitize (c) && + hb_barrier () && ligAction && component && ligature); } protected: StateTable @@ -763,10 +772,11 @@ unsigned int count = (flags & MarkedInsertCount); if (unlikely ((buffer->max_ops -= count) <= 0)) return; unsigned int start = entry.data.markedInsertIndex; const HBGlyphID16 *glyphs = &insertionAction[start]; if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + hb_barrier (); bool before = flags & MarkedInsertBefore; unsigned int end = buffer->out_len; if (unlikely (!buffer->move_to (mark))) return; @@ -791,10 +801,11 @@ unsigned int count = (flags & CurrentInsertCount) >> 5; if (unlikely ((buffer->max_ops -= count) <= 0)) return; unsigned int start = entry.data.currentInsertIndex; const HBGlyphID16 *glyphs = &insertionAction[start]; if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + hb_barrier (); bool before = flags & CurrentInsertBefore; unsigned int end = buffer->out_len; @@ -847,10 +858,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); /* The rest of array sanitizations are done at run-time. */ return_trace (c->check_struct (this) && machine.sanitize (c) && + hb_barrier () && insertionAction); } protected: StateTable @@ -942,13 +954,14 @@ } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!length.sanitize (c) || - length <= min_size || - !c->check_range (this, length)) + if (!(length.sanitize (c) && + hb_barrier () && + length >= min_size && + c->check_range (this, length))) return_trace (false); hb_sanitize_with_object_t with (c, this); return_trace (dispatch (c)); } @@ -1087,13 +1100,14 @@ unsigned int get_size () const { return length; } bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const { TRACE_SANITIZE (this); - if (!length.sanitize (c) || - length < min_size || - !c->check_range (this, length)) + if (!(length.sanitize (c) && + hb_barrier () && + length >= min_size && + c->check_range (this, length))) return_trace (false); if (!c->check_array (featureZ.arrayZ, featureCount)) return_trace (false); @@ -1101,10 +1115,11 @@ unsigned int count = subtableCount; for (unsigned int i = 0; i < count; i++) { if (!subtable->sanitize (c)) return_trace (false); + hb_barrier (); subtable = &StructAfter> (*subtable); } return_trace (true); } @@ -1171,19 +1186,23 @@ } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) + if (!(version.sanitize (c) && + hb_barrier () && + version && + chainCount.sanitize (c))) return_trace (false); const Chain *chain = &firstChain; unsigned int count = chainCount; for (unsigned int i = 0; i < count; i++) { if (!chain->sanitize (c, version)) return_trace (false); + hb_barrier (); chain = &StructAfter> (*chain); } return_trace (true); } diff --git a/gfx/harfbuzz/src/hb-aat-layout-opbd-table.hh b/gfx/harfbuzz/src/hb-aat-layout-opbd-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-opbd-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-opbd-table.hh @@ -142,10 +142,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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)); diff --git a/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh b/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh --- a/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh +++ b/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh @@ -132,10 +132,11 @@ 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))); } protected: @@ -201,10 +202,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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))); } diff --git a/gfx/harfbuzz/src/hb-aat-layout.h b/gfx/harfbuzz/src/hb-aat-layout.h --- a/gfx/harfbuzz/src/hb-aat-layout.h +++ b/gfx/harfbuzz/src/hb-aat-layout.h @@ -38,11 +38,11 @@ /** * hb_aat_layout_feature_type_t: * @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) * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING: [Number Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type6) * @HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE: [Smart Swash](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type8) @@ -86,11 +86,11 @@ { HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF, 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, HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6, HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8, diff --git a/gfx/harfbuzz/src/hb-aat-ltag-table.hh b/gfx/harfbuzz/src/hb-aat-ltag-table.hh --- a/gfx/harfbuzz/src/hb-aat-ltag-table.hh +++ b/gfx/harfbuzz/src/hb-aat-ltag-table.hh @@ -44,11 +44,13 @@ friend struct ltag; 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: NNOffset16To> tag; /* Offset from the start of the table to @@ -71,10 +73,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && version >= 1 && tagRanges.sanitize (c, this))); } protected: diff --git a/gfx/harfbuzz/src/hb-array.hh b/gfx/harfbuzz/src/hb-array.hh --- a/gfx/harfbuzz/src/hb-array.hh +++ b/gfx/harfbuzz/src/hb-array.hh @@ -45,10 +45,12 @@ template struct hb_array_t : hb_iter_with_fallback_t, Type&> { + static constexpr bool realloc_move = true; + /* * Constructors. */ hb_array_t () = default; hb_array_t (const hb_array_t&) = default; diff --git a/gfx/harfbuzz/src/hb-atomic.hh b/gfx/harfbuzz/src/hb-atomic.hh --- a/gfx/harfbuzz/src/hb-atomic.hh +++ b/gfx/harfbuzz/src/hb-atomic.hh @@ -116,16 +116,16 @@ * * https://github.com/harfbuzz/harfbuzz/pull/4119 */ #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 #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 @@ -216,7 +216,13 @@ template 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 */ diff --git a/gfx/harfbuzz/src/hb-bit-set-invertible.hh b/gfx/harfbuzz/src/hb-bit-set-invertible.hh --- a/gfx/harfbuzz/src/hb-bit-set-invertible.hh +++ b/gfx/harfbuzz/src/hb-bit-set-invertible.hh @@ -357,12 +357,12 @@ } 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 { return v != o.v || s != o.s; } diff --git a/gfx/harfbuzz/src/hb-deprecated.h b/gfx/harfbuzz/src/hb-deprecated.h --- a/gfx/harfbuzz/src/hb-deprecated.h +++ b/gfx/harfbuzz/src/hb-deprecated.h @@ -54,11 +54,11 @@ /** * HB_SCRIPT_CANADIAN_ABORIGINAL: * - * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead: + * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead. * * Deprecated: 0.9.20 */ #define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS @@ -299,10 +299,19 @@ hb_font_get_glyph_shape (hb_font_t *font, hb_codepoint_t glyph, 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 HB_END_DECLS diff --git a/gfx/harfbuzz/src/hb-ft.cc b/gfx/harfbuzz/src/hb-ft.cc --- a/gfx/harfbuzz/src/hb-ft.cc +++ b/gfx/harfbuzz/src/hb-ft.cc @@ -223,11 +223,11 @@ * @load_flags: The FreeType load flags to set * * 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 + * * * This function works with #hb_font_t objects created by * hb_ft_font_create() or hb_ft_font_create_referenced(). * * Since: 1.0.5 @@ -251,11 +251,11 @@ * @font: #hb_font_t to work upon * * 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 + * * * This function works with #hb_font_t objects created by * hb_ft_font_create() or hb_ft_font_create_referenced(). * * Return value: FT_Load_Glyph flags found, or 0 diff --git a/gfx/harfbuzz/src/hb-map.hh b/gfx/harfbuzz/src/hb-map.hh --- a/gfx/harfbuzz/src/hb-map.hh +++ b/gfx/harfbuzz/src/hb-map.hh @@ -40,14 +40,38 @@ template struct hb_hashmap_t { + static constexpr bool realloc_move = true; + hb_hashmap_t () { init (); } ~hb_hashmap_t () { fini (); } - hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { alloc (o.population); hb_copy (o, *this); } + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () + { + if (unlikely (!o.mask)) return; + + if (item_t::is_trivial) + { + items = (item_t *) hb_malloc (sizeof (item_t) * (o.mask + 1)); + if (unlikely (!items)) + { + successful = false; + return; + } + population = o.population; + occupancy = o.occupancy; + mask = o.mask; + prime = o.prime; + max_chain_length = o.max_chain_length; + memcpy (items, o.items, sizeof (item_t) * (mask + 1)); + return; + } + + alloc (o.population); hb_copy (o, *this); + } hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; } hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () @@ -207,13 +231,14 @@ { set_with_hash (std::move (old_items[i].key), old_items[i].hash, std::move (old_items[i].value)); } - if (!item_t::is_trivial) + } + if (!item_t::is_trivial) + for (unsigned int i = 0; i < old_size; i++) old_items[i].~item_t (); - } hb_free (old_items); return true; } diff --git a/gfx/harfbuzz/src/hb-open-file.hh b/gfx/harfbuzz/src/hb-open-file.hh --- a/gfx/harfbuzz/src/hb-open-file.hh +++ b/gfx/harfbuzz/src/hb-open-file.hh @@ -265,10 +265,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + hb_barrier (); switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ case 1: return_trace (u.version1.sanitize (c)); default:return_trace (true); } @@ -300,10 +301,11 @@ const void *data_base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && offset.sanitize (c, data_base) && + hb_barrier () && get_face (data_base).sanitize (c)); } protected: HBUINT16 id; /* Resource ID. */ @@ -335,10 +337,11 @@ const void *type_base, const void *data_base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && resourcesZ.sanitize (c, type_base, get_resource_count (), data_base)); } @@ -383,10 +386,11 @@ bool sanitize (hb_sanitize_context_t *c, const void *data_base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && typeList.sanitize (c, this, &(this+typeList), data_base)); } @@ -426,10 +430,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && data.sanitize (c, this, dataLen) && map.sanitize (c, this, &(this+data))); } protected: @@ -506,10 +511,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!u.tag.sanitize (c))) return_trace (false); + hb_barrier (); switch (u.tag) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); diff --git a/gfx/harfbuzz/src/hb-open-type.hh b/gfx/harfbuzz/src/hb-open-type.hh --- a/gfx/harfbuzz/src/hb-open-type.hh +++ b/gfx/harfbuzz/src/hb-open-type.hh @@ -307,11 +307,11 @@ { static const Type *get_null () { return &Null (Type); } static Type *get_crap () { return &Crap (Type); } }; -template +template struct OffsetTo : Offset { using target_t = Type; // Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time. @@ -333,26 +333,26 @@ if (unlikely (this->is_null ())) return *_hb_has_null::get_crap (); return StructAtOffset (base, *this); } template + hb_enable_if (hb_is_convertible (const Base, const BaseType *))> friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } template + hb_enable_if (hb_is_convertible (const Base, const BaseType *))> friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } template + hb_enable_if (hb_is_convertible (Base, BaseType *))> friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } template + hb_enable_if (hb_is_convertible (Base, BaseType *))> friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } - template + template bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, - const void *src_base, Ts&&... ds) + const Base *src_base, Ts&&... ds) { *this = 0; if (src.is_null ()) return false; @@ -412,27 +412,29 @@ bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, const void *src_base, unsigned dst_bias = 0) { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } - bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const + bool sanitize_shallow (hb_sanitize_context_t *c, const BaseType *base) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); //if (unlikely (this->is_null ())) return_trace (true); if (unlikely ((const char *) base + (unsigned) *this < (const char *) base)) return_trace (false); return_trace (true); } template #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE #endif - bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + bool sanitize (hb_sanitize_context_t *c, const BaseType *base, Ts&&... ds) const { TRACE_SANITIZE (this); return_trace (sanitize_shallow (c, base) && + hb_barrier () && (this->is_null () || c->dispatch (StructAtOffset (base, *this), std::forward (ds)...) || neuter (c))); } @@ -443,18 +445,18 @@ return c->try_set (this, 0); } DEFINE_SIZE_STATIC (sizeof (OffsetType)); }; /* Partial specializations. */ -template using Offset16To = OffsetTo; -template using Offset24To = OffsetTo; -template using Offset32To = OffsetTo; +template using Offset16To = OffsetTo; +template using Offset24To = OffsetTo; +template using Offset32To = OffsetTo; -template using NNOffsetTo = OffsetTo; -template using NNOffset16To = Offset16To; -template using NNOffset24To = Offset24To; -template using NNOffset32To = Offset32To; +template using NNOffsetTo = OffsetTo; +template using NNOffset16To = Offset16To; +template using NNOffset24To = Offset24To; +template using NNOffset32To = Offset32To; /* * Array Types */ @@ -534,10 +536,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c, count))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) return_trace (false); return_trace (true); } @@ -553,39 +556,39 @@ public: DEFINE_SIZE_UNBOUNDED (0); }; /* Unsized array of offset's */ -template -using UnsizedArray16OfOffsetTo = UnsizedArrayOf>; +template +using UnsizedArray16OfOffsetTo = UnsizedArrayOf>; /* Unsized array of offsets relative to the beginning of the array itself. */ -template -struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo +template +struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo { const Type& operator [] (int i_) const { unsigned int i = (unsigned int) i_; - const OffsetTo *p = &this->arrayZ[i]; + const OffsetTo *p = &this->arrayZ[i]; if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Null (Type); /* Overflowed. */ _hb_compiler_memory_r_barrier (); return this+*p; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; - const OffsetTo *p = &this->arrayZ[i]; + const OffsetTo *p = &this->arrayZ[i]; if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Crap (Type); /* Overflowed. */ _hb_compiler_memory_r_barrier (); return this+*p; } template bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const { TRACE_SANITIZE (this); - return_trace ((UnsizedArray16OfOffsetTo + return_trace ((UnsizedArray16OfOffsetTo ::sanitize (c, count, this, std::forward (ds)...))); } }; /* An array with sorted elements. Supports binary searching. */ @@ -723,21 +726,24 @@ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = len; for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) return_trace (false); return_trace (true); } bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (len.sanitize (c) && c->check_array_sized (arrayZ, len, sizeof (LenType))); + return_trace (len.sanitize (c) && + hb_barrier () && + c->check_array_sized (arrayZ, len, sizeof (LenType))); } public: LenType len; Type arrayZ[HB_VAR_ARRAY]; @@ -864,10 +870,11 @@ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = get_length (); for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) return_trace (false); return_trace (true); @@ -876,10 +883,11 @@ private: bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (lenP1.sanitize (c) && + hb_barrier () && (!lenP1 || c->check_array_sized (arrayZ, lenP1 - 1, sizeof (LenType)))); } public: LenType lenP1; @@ -917,10 +925,11 @@ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = lenM1 + 1; for (unsigned int i = 0; i < count; i++) if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) return_trace (false); return_trace (true); @@ -929,10 +938,11 @@ private: bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (lenM1.sanitize (c) && + hb_barrier () && (c->check_array_sized (arrayZ, lenM1 + 1, sizeof (LenType)))); } public: LenType lenM1; @@ -1102,10 +1112,11 @@ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + hb_barrier (); unsigned int count = get_length (); for (unsigned int i = 0; i < count; i++) if (unlikely (!(*this)[i].sanitize (c, std::forward (ds)...))) return_trace (false); return_trace (true); @@ -1128,10 +1139,11 @@ private: bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (header.sanitize (c) && + hb_barrier () && Type::static_size <= header.unitSize && c->check_range (bytesZ.arrayZ, header.nUnits, header.unitSize)); } diff --git a/gfx/harfbuzz/src/hb-ot-cff-common.hh b/gfx/harfbuzz/src/hb-ot-cff-common.hh --- a/gfx/harfbuzz/src/hb-ot-cff-common.hh +++ b/gfx/harfbuzz/src/hb-ot-cff-common.hh @@ -76,21 +76,22 @@ template bool serialize (hb_serialize_context_t *c, const Iterable &iterable, - const unsigned *p_data_size = nullptr) + const unsigned *p_data_size = nullptr, + unsigned min_off_size = 0) { TRACE_SERIALIZE (this); unsigned data_size; if (p_data_size) data_size = *p_data_size; else total_size (iterable, &data_size); auto it = hb_iter (iterable); - if (unlikely (!serialize_header (c, +it, data_size))) return_trace (false); + if (unlikely (!serialize_header (c, +it, data_size, min_off_size))) return_trace (false); unsigned char *ret = c->allocate_size (data_size, false); if (unlikely (!ret)) return_trace (false); for (const auto &_ : +it) { unsigned len = _.length; @@ -109,15 +110,17 @@ template bool serialize_header (hb_serialize_context_t *c, Iterator it, - unsigned data_size) + unsigned data_size, + unsigned min_off_size = 0) { TRACE_SERIALIZE (this); unsigned off_size = (hb_bit_storage (data_size + 1) + 7) / 8; + off_size = hb_max(min_off_size, off_size); /* serialize CFFIndex header */ if (unlikely (!c->extend_min (this))) return_trace (false); this->count = hb_len (it); if (!this->count) return_trace (true); @@ -193,11 +196,11 @@ return_trace (true); } template - static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr) + static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr, unsigned min_off_size = 0) { auto it = + hb_iter (iterable); if (!it) { if (data_size) *data_size = 0; @@ -209,10 +212,11 @@ total += length_f (_); if (data_size) *data_size = total; unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + off_size = hb_max(min_off_size, off_size); return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total; } void set_offset_at (unsigned int index, unsigned int offset) @@ -272,12 +276,14 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && (count == 0 || /* empty INDEX */ (count < count + 1u && + hb_barrier () && c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 && c->check_array (offsets, offSize, count + 1u) && c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count)))))); } @@ -410,10 +416,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const { TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this)))) return_trace (false); + hb_barrier (); if (unlikely (!c->check_array (fds, c->get_num_glyphs ()))) return_trace (false); return_trace (true); } @@ -436,11 +443,13 @@ struct FDSelect3_4_Range { bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const { TRACE_SANITIZE (this); - return_trace (first < c->get_num_glyphs () && (fd < fdcount)); + return_trace (c->check_struct (this) && + hb_barrier () && + first < c->get_num_glyphs () && (fd < fdcount)); } GID_TYPE first; FD_TYPE fd; public: @@ -454,19 +463,24 @@ { return GID_TYPE::static_size * 2 + ranges.get_size (); } bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const { TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) || - (nRanges () == 0) || ranges[0].first != 0)) + if (unlikely (!(c->check_struct (this) && + ranges.sanitize (c, nullptr, fdcount) && + hb_barrier () && + (nRanges () != 0) && + ranges[0].first == 0))) return_trace (false); for (unsigned int i = 1; i < nRanges (); i++) if (unlikely (ranges[i - 1].first >= ranges[i].first)) return_trace (false); - if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) + if (unlikely (!(sentinel().sanitize (c) && + hb_barrier () && + (sentinel() == c->get_num_glyphs ())))) return_trace (false); return_trace (true); } @@ -557,10 +571,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (format) { case 0: return_trace (u.format0.sanitize (c, fdcount)); case 3: return_trace (u.format3.sanitize (c, fdcount)); diff --git a/gfx/harfbuzz/src/hb-ot-cff1-table.hh b/gfx/harfbuzz/src/hb-ot-cff1-table.hh --- a/gfx/harfbuzz/src/hb-ot-cff1-table.hh +++ b/gfx/harfbuzz/src/hb-ot-cff1-table.hh @@ -273,10 +273,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (table_format ()) { case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; @@ -374,17 +375,17 @@ template struct Charset1_2 { bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const { TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this))) - return_trace (false); num_glyphs--; unsigned i; for (i = 0; num_glyphs > 0; i++) { - if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1))) + if (unlikely (!(ranges[i].sanitize (c) && + hb_barrier () && + (num_glyphs >= ranges[i].nLeft + 1)))) return_trace (false); num_glyphs -= (ranges[i].nLeft + 1); } if (num_charset_entries) *num_charset_entries = i; @@ -613,10 +614,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned *num_charset_entries) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (format) { case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs (), num_charset_entries)); case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs (), num_charset_entries)); @@ -1053,10 +1055,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version.major == 1)); } template struct accelerator_templ_t @@ -1083,18 +1086,21 @@ goto fail; nameIndex = &cff->nameIndex (cff); if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) goto fail; + hb_barrier (); topDictIndex = &StructAtOffset (nameIndex, nameIndex->get_size ()); if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) goto fail; + hb_barrier (); { /* parse top dict */ const hb_ubytes_t topDictStr = (*topDictIndex)[0]; if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + hb_barrier (); cff1_top_dict_interp_env_t env (topDictStr); cff1_top_dict_interpreter_t top_interp (env); if (unlikely (!top_interp.interpret (topDict))) goto fail; } @@ -1102,20 +1108,22 @@ charset = &Null (Charset); else { charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail; + hb_barrier (); } fdCount = 1; if (is_CID ()) { fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) goto fail; + hb_barrier (); fdCount = fdArray->count; } else { @@ -1132,25 +1140,29 @@ { if (!is_predef_encoding ()) { encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail; + hb_barrier (); } } stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) goto fail; + hb_barrier (); globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) goto fail; + hb_barrier (); charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) goto fail; + hb_barrier (); num_glyphs = charStrings->count; if (num_glyphs != sc.get_num_glyphs ()) goto fail; @@ -1164,10 +1176,11 @@ { for (unsigned int i = 0; i < fdCount; i++) { hb_ubytes_t fontDictStr = (*fdArray)[i]; if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + hb_barrier (); cff1_font_dict_values_t *font; cff1_top_dict_interp_env_t env (fontDictStr); cff1_font_dict_interpreter_t font_interp (env); font = fontDicts.push (); if (unlikely (fontDicts.in_error ())) goto fail; @@ -1175,37 +1188,41 @@ font->init (); if (unlikely (!font_interp.interpret (*font))) goto fail; PRIVDICTVAL *priv = &privateDicts[i]; const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + hb_barrier (); num_interp_env_t env2 (privDictStr); dict_interpreter_t priv_interp (env2); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) goto fail; priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); if (priv->localSubrs != &Null (CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) goto fail; + hb_barrier (); } } else /* non-CID */ { cff1_top_dict_values_t *font = &topDict; PRIVDICTVAL *priv = &privateDicts[0]; const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + hb_barrier (); num_interp_env_t env (privDictStr); dict_interpreter_t priv_interp (env); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) goto fail; priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); if (priv->localSubrs != &Null (CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) goto fail; + hb_barrier (); } return; fail: diff --git a/gfx/harfbuzz/src/hb-ot-cff2-table.hh b/gfx/harfbuzz/src/hb-ot-cff2-table.hh --- a/gfx/harfbuzz/src/hb-ot-cff2-table.hh +++ b/gfx/harfbuzz/src/hb-ot-cff2-table.hh @@ -88,10 +88,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (format) { case 0: return_trace (u.format0.sanitize (c, fdcount)); case 3: return_trace (u.format3.sanitize (c, fdcount)); @@ -113,11 +114,14 @@ struct CFF2VariationStore { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c)); + return_trace (c->check_struct (this) && + hb_barrier () && + c->check_range (&varStore, size) && + varStore.sanitize (c)); } bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) { TRACE_SERIALIZE (this); @@ -382,10 +386,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version.major == 2)); } template struct accelerator_templ_t @@ -412,10 +417,11 @@ goto fail; { /* parse top dict */ hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize); if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + hb_barrier (); num_interp_env_t env (topDictStr); cff2_top_dict_interpreter_t top_interp (env); topDict.init (); if (unlikely (!top_interp.interpret (topDict))) goto fail; } @@ -428,10 +434,11 @@ if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || + !hb_barrier () || (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) goto fail; num_glyphs = charStrings->count; if (num_glyphs != sc.get_num_glyphs ()) @@ -444,29 +451,32 @@ /* parse font dicts and gather private dicts */ for (unsigned int i = 0; i < fdCount; i++) { const hb_ubytes_t fontDictStr = (*fdArray)[i]; if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + hb_barrier (); cff2_font_dict_values_t *font; num_interp_env_t env (fontDictStr); cff2_font_dict_interpreter_t font_interp (env); font = fontDicts.push (); if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail; font->init (); if (unlikely (!font_interp.interpret (*font))) goto fail; const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + hb_barrier (); cff2_priv_dict_interp_env_t env2 (privDictStr); dict_interpreter_t priv_interp (env2); privateDicts[i].init (); if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) goto fail; + hb_barrier (); } return; fail: diff --git a/gfx/harfbuzz/src/hb-ot-cmap-table.hh b/gfx/harfbuzz/src/hb-ot-cmap-table.hh --- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh +++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh @@ -554,10 +554,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); if (unlikely (!c->check_range (this, length))) { /* Some broken fonts have too long of a "length" value. * If that is the case, just change the value to truncate @@ -740,23 +741,24 @@ void collect_mapping (hb_set_t *unicodes, /* OUT */ hb_map_t *mapping, /* OUT */ unsigned num_glyphs) const { hb_codepoint_t last_end = 0; - for (unsigned i = 0; i < this->groups.len; i++) + unsigned count = this->groups.len; + for (unsigned i = 0; i < count; i++) { - hb_codepoint_t start = this->groups[i].startCharCode; - hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + hb_codepoint_t start = this->groups.arrayZ[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups.arrayZ[i].endCharCode, (hb_codepoint_t) HB_UNICODE_MAX); if (unlikely (start > end || start < last_end)) { // Range is not in order and is invalid, skip it. continue; } last_end = end; - hb_codepoint_t gid = this->groups[i].glyphID; + hb_codepoint_t gid = this->groups.arrayZ[i].glyphID; if (!gid) { if (T::formatNumber == 13) continue; start++; gid++; @@ -765,13 +767,13 @@ if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) end = start + (hb_codepoint_t) num_glyphs - gid; mapping->alloc (mapping->get_population () + end - start + 1); + unicodes->add_range (start, end); for (unsigned cp = start; cp <= end; cp++) { - unicodes->add (cp); mapping->set (cp, gid); gid += T::increment; } } } @@ -1425,10 +1427,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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 4: return_trace (u.format4 .sanitize (c)); case 6: return_trace (u.format6 .sanitize (c)); case 10: return_trace (u.format10.sanitize (c)); @@ -2058,10 +2061,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && likely (version == 0) && encodingRecord.sanitize (c, this)); } private: diff --git a/gfx/harfbuzz/src/hb-ot-hdmx-table.hh b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh --- a/gfx/harfbuzz/src/hb-ot-hdmx-table.hh +++ b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh @@ -69,10 +69,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && c->check_range (this, sizeDeviceRecord))); } HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */ HBUINT8 maxWidth; /* Maximum width. */ @@ -150,10 +151,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && min_size + numRecords * sizeDeviceRecord > numRecords * sizeDeviceRecord && sizeDeviceRecord >= DeviceRecord::min_size && c->check_range (this, get_size ())); } diff --git a/gfx/harfbuzz/src/hb-ot-head-table.hh b/gfx/harfbuzz/src/hb-ot-head-table.hh --- a/gfx/harfbuzz/src/hb-ot-head-table.hh +++ b/gfx/harfbuzz/src/hb-ot-head-table.hh @@ -101,10 +101,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && version.major == 1 && magicNumber == 0x5F0F3CF5u); } protected: diff --git a/gfx/harfbuzz/src/hb-ot-hhea-table.hh b/gfx/harfbuzz/src/hb-ot-hhea-table.hh --- a/gfx/harfbuzz/src/hb-ot-hhea-table.hh +++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh @@ -48,11 +48,13 @@ bool has_data () const { return version.major; } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && likely (version.major == 1)); + return_trace (c->check_struct (this) && + hb_barrier () && + likely (version.major == 1)); } public: FixedVersion<>version; /* 0x00010000u for version 1.0. */ FWORD ascender; /* Typographic ascent. */ diff --git a/gfx/harfbuzz/src/hb-ot-kern-table.hh b/gfx/harfbuzz/src/hb-ot-kern-table.hh --- a/gfx/harfbuzz/src/hb-ot-kern-table.hh +++ b/gfx/harfbuzz/src/hb-ot-kern-table.hh @@ -77,10 +77,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_range (kernValueZ, kernValueCount * sizeof (FWORD) + glyphCount * 2 + leftClassCount * rightClassCount)); } @@ -145,13 +146,14 @@ } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.header.sanitize (c) || - u.header.length < u.header.min_size || - !c->check_range (this, u.header.length))) return_trace (false); + if (unlikely (!(u.header.sanitize (c) && + hb_barrier () && + u.header.length >= u.header.min_size && + c->check_range (this, u.header.length)))) return_trace (false); return_trace (dispatch (c)); } public: @@ -335,10 +337,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!u.version32.sanitize (c)) return_trace (false); + hb_barrier (); return_trace (dispatch (c)); } protected: union { diff --git a/gfx/harfbuzz/src/hb-ot-layout-base-table.hh b/gfx/harfbuzz/src/hb-ot-layout-base-table.hh --- a/gfx/harfbuzz/src/hb-ot-layout-base-table.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-base-table.hh @@ -133,10 +133,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!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)); case 3: return_trace (u.format3.sanitize (c)); default:return_trace (false); @@ -494,10 +495,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && likely (version.major == 1) && hAxis.sanitize (c, this) && vAxis.sanitize (c, this) && (version.to_int () < 0x00010001u || varStore.sanitize (c, this)))); } diff --git a/gfx/harfbuzz/src/hb-ot-layout-common.hh b/gfx/harfbuzz/src/hb-ot-layout-common.hh --- a/gfx/harfbuzz/src/hb-ot-layout-common.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-common.hh @@ -62,11 +62,11 @@ { const hb_map_t *axes_index_tag_map; const hb_hashmap_t *axes_location; hb_hashmap_t> *record_cond_idx_map; hb_hashmap_t *feature_substitutes_map; - bool& insert_catch_all_feature_variation_record; + hb_set_t& catch_all_record_feature_idxes; // not stored in subset_plan hb_set_t *feature_indices; bool apply; bool variation_applied; @@ -140,10 +140,12 @@ const hb_map_t *lookup_index_map; const hb_hashmap_t> *script_langsys_map; const hb_map_t *feature_index_map; const hb_hashmap_t *feature_substitutes_map; hb_hashmap_t> *feature_record_cond_idx_map; + const hb_set_t *catch_all_record_feature_idxes; + const hb_hashmap_t> *feature_idx_tag_map; unsigned cur_script_index; unsigned cur_feature_var_record_idx; hb_subset_layout_context_t (hb_subset_context_t *c_, @@ -162,18 +164,22 @@ lookup_index_map = &c_->plan->gsub_lookups; script_langsys_map = &c_->plan->gsub_langsys; feature_index_map = &c_->plan->gsub_features; feature_substitutes_map = &c_->plan->gsub_feature_substitutes_map; feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gsub_feature_record_cond_idx_map; + catch_all_record_feature_idxes = &c_->plan->gsub_old_features; + feature_idx_tag_map = &c_->plan->gsub_old_feature_idx_tag_map; } else { lookup_index_map = &c_->plan->gpos_lookups; script_langsys_map = &c_->plan->gpos_langsys; feature_index_map = &c_->plan->gpos_features; feature_substitutes_map = &c_->plan->gpos_feature_substitutes_map; feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gpos_feature_record_cond_idx_map; + catch_all_record_feature_idxes = &c_->plan->gpos_old_features; + feature_idx_tag_map = &c_->plan->gpos_old_feature_idx_tag_map; } } private: unsigned script_count; @@ -452,10 +458,11 @@ { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); /* This subtable has some "history", if you will. Some earlier versions of * Adobe tools calculated the offset of the FeatureParams subtable from the * beginning of the FeatureList table! Now, that is dealt with in the * Feature implementation. But we still need to be able to tell junk from @@ -818,10 +825,11 @@ const Record_sanitize_closure_t *closure = nullptr) const { TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) return_trace (false); + hb_barrier (); /* Some earlier versions of Adobe tools calculated the offset of the * FeatureParams subtable from the beginning of the FeatureList table! * * If sanitizing "failed" for the FeatureParams subtable, try it with the @@ -836,10 +844,11 @@ return_trace (true); unsigned int orig_offset = featureParams; if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) return_trace (false); + hb_barrier (); if (featureParams == 0 && closure && closure->tag == HB_TAG ('s','i','z','e') && closure->list_base && closure->list_base < this) { @@ -898,11 +907,12 @@ bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); const Record_sanitize_closure_t closure = {tag, base}; - return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); + return_trace (c->check_struct (this) && + offset.sanitize (c, base, &closure)); } Tag tag; /* 4-byte Tag identifier */ Offset16To offset; /* Offset from beginning of object holding @@ -1369,14 +1379,24 @@ | hb_apply (subset_offset_array (c, out->get_subtables (), this, lookup_type)) ; if (lookupFlag & LookupFlag::UseMarkFilteringSet) { - if (unlikely (!c->serializer->extend (out))) return_trace (false); const HBUINT16 &markFilteringSet = StructAfter (subTable); - HBUINT16 &outMarkFilteringSet = StructAfter (out->subTable); - outMarkFilteringSet = markFilteringSet; + hb_codepoint_t *idx; + if (!c->plan->used_mark_sets_map.has (markFilteringSet, &idx)) + { + unsigned new_flag = lookupFlag; + new_flag &= ~LookupFlag::UseMarkFilteringSet; + out->lookupFlag = new_flag; + } + else + { + if (unlikely (!c->serializer->extend (out))) return_trace (false); + HBUINT16 &outMarkFilteringSet = StructAfter (out->subTable); + outMarkFilteringSet = *idx; + } } // Always keep the lookup even if it's empty. The rest of layout subsetting depends on lookup // indices being consistent with those computed during planning. So if an empty lookup is // discarded during the subset phase it will invalidate all subsequent lookup indices. @@ -1389,10 +1409,11 @@ template bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false); + hb_barrier (); unsigned subtables = get_subtable_count (); if (unlikely (!c->visit_subtables (subtables))) return_trace (false); if (lookupFlag & LookupFlag::UseMarkFilteringSet) @@ -1404,10 +1425,12 @@ if (unlikely (!get_subtables ().sanitize (c, this, get_type ()))) return_trace (false); if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) { + hb_barrier (); + /* The spec says all subtables of an Extension lookup should * have the same type, which shall not be the Extension type * itself (but we already checked for that). * This is specially important if one has a reverse type! * @@ -2154,10 +2177,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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)); #ifndef HB_NO_BEYOND_64K case 3: return_trace (u.format3.sanitize (c)); @@ -2532,11 +2556,13 @@ } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount)); + return_trace (c->check_struct (this) && + hb_barrier () && + axesZ.sanitize (c, axisCount * regionCount)); } bool serialize (hb_serialize_context_t *c, const hb_vector_t& axis_tags, const hb_vector_t*>& regions) @@ -2726,10 +2752,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && regionIndices.sanitize (c) && + hb_barrier () && wordCount () <= regionIndices.len && c->check_range (get_delta_bytes (), itemCount, get_row_size ())); } @@ -3075,10 +3102,11 @@ return true; #endif TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && format == 1 && regions.sanitize (c, this) && dataSets.sanitize (c, this)); } @@ -3328,12 +3356,16 @@ hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex); Triple axis_range (-1.f, 0.f, 1.f); Triple *axis_limit; + bool axis_set_by_user = false; if (c->axes_location->has (axis_tag, &axis_limit)) + { axis_range = *axis_limit; + axis_set_by_user = true; + } float axis_min_val = axis_range.minimum; float axis_default_val = axis_range.middle; float axis_max_val = axis_range.maximum; @@ -3348,12 +3380,11 @@ if (axis_min_val > filter_max_val || axis_max_val < filter_min_val || filter_min_val > filter_max_val) return DROP_RECORD_WITH_VAR; //condition met and axis pinned, drop the condition - if (c->axes_location->has (axis_tag) && - c->axes_location->get (axis_tag).is_point ()) + if (axis_set_by_user && axis_range.is_point ()) return DROP_COND_WITH_VAR; if (filter_max_val != axis_max_val || filter_min_val != axis_min_val) { // add axisIndex->value into the hashmap so we can check if the record is @@ -3363,11 +3394,10 @@ hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val; condition_map->set (axisIndex, val); return KEEP_COND_WITH_VAR; } - return KEEP_RECORD_WITH_VAR; } bool evaluate (const int *coords, unsigned int coord_len) const { @@ -3422,10 +3452,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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); } } @@ -3495,16 +3526,19 @@ if (should_keep && num_kept_cond == 0) c->universal = true; } bool subset (hb_subset_context_t *c, - hb_subset_layout_context_t *l) const + hb_subset_layout_context_t *l, + bool insert_catch_all) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + if (insert_catch_all) return_trace (true); + hb_set_t *retained_cond_set = nullptr; if (l->feature_record_cond_idx_map != nullptr) retained_cond_set = l->feature_record_cond_idx_map->get (l->cur_feature_var_record_idx); unsigned int count = conditions.len; @@ -3546,31 +3580,55 @@ if ((base+feature).intersects_lookup_indexes (lookup_indexes)) feature_indexes->add (featureIndex); } void collect_feature_substitutes_with_variations (hb_hashmap_t *feature_substitutes_map, + hb_set_t& catch_all_record_feature_idxes, const hb_set_t *feature_indices, const void *base) const { if (feature_indices->has (featureIndex)) + { feature_substitutes_map->set (featureIndex, &(base+feature)); + catch_all_record_feature_idxes.add (featureIndex); + } + } + + bool serialize (hb_subset_layout_context_t *c, + unsigned feature_index, + const Feature *f, const Tag *tag) + { + TRACE_SERIALIZE (this); + hb_serialize_context_t *s = c->subset_context->serializer; + if (unlikely (!s->extend_min (this))) return_trace (false); + + uint32_t *new_feature_idx; + if (!c->feature_index_map->has (feature_index, &new_feature_idx)) + return_trace (false); + + if (!s->check_assign (featureIndex, *new_feature_idx, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + s->push (); + bool ret = f->subset (c->subset_context, c, tag); + if (ret) s->add_link (feature, s->pop_pack ()); + else s->pop_discard (); + + return_trace (ret); } bool subset (hb_subset_layout_context_t *c, const void *base) const { TRACE_SUBSET (this); - if (!c->feature_index_map->has (featureIndex) || - c->feature_substitutes_map->has (featureIndex)) { - // Feature that is being substituted is not being retained, so we don't - // need this. + uint32_t *new_feature_index; + if (!c->feature_index_map->has (featureIndex, &new_feature_index)) return_trace (false); - } auto *out = c->subset_context->serializer->embed (this); if (unlikely (!out)) return_trace (false); - out->featureIndex = c->feature_index_map->get (featureIndex); + out->featureIndex = *new_feature_index; return_trace (out->feature.serialize_subset (c->subset_context, feature, base, c)); } bool sanitize (hb_sanitize_context_t *c, const void *base) const { @@ -3598,20 +3656,14 @@ } return nullptr; } void collect_lookups (const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, hb_set_t *lookup_indexes /* OUT */) const { + hb_iter (substitutions) | hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex) - | hb_filter ([feature_substitutes_map] (const FeatureTableSubstitutionRecord& record) - { - if (feature_substitutes_map == nullptr) return true; - return !feature_substitutes_map->has (record.featureIndex); - }) | hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r) { r.collect_lookups (this, lookup_indexes); }) ; } @@ -3632,23 +3684,42 @@ } void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const { for (const FeatureTableSubstitutionRecord& record : substitutions) - record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, c->feature_indices, this); + record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, + c->catch_all_record_feature_idxes, + c->feature_indices, this); } bool subset (hb_subset_context_t *c, - hb_subset_layout_context_t *l) const + hb_subset_layout_context_t *l, + bool insert_catch_all) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (*this); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); out->version.major = version.major; out->version.minor = version.minor; + if (insert_catch_all) + { + for (unsigned feature_index : *(l->catch_all_record_feature_idxes)) + { + hb_pair_t *p; + if (!l->feature_idx_tag_map->has (feature_index, &p)) + return_trace (false); + auto *o = out->substitutions.serialize_append (c->serializer); + if (!o->serialize (l, feature_index, + reinterpret_cast (p->first), + reinterpret_cast (p->second))) + return_trace (false); + } + return_trace (true); + } + + substitutions.iter () | hb_apply (subset_record_array (l, &(out->substitutions), this)) ; return_trace (bool (out->substitutions)); @@ -3656,10 +3727,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && substitutions.sanitize (c, this)); } protected: @@ -3674,14 +3746,13 @@ { friend struct FeatureVariations; void collect_lookups (const void *base, const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, hb_set_t *lookup_indexes /* OUT */) const { - return (base+substitutions).collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes); + return (base+substitutions).collect_lookups (feature_indexes, lookup_indexes); } void closure_features (const void *base, const hb_map_t *lookup_indexes, hb_set_t *feature_indexes /* OUT */) const @@ -3703,18 +3774,19 @@ (base+substitutions).collect_feature_substitutes_with_variations (c); c->variation_applied = true; // set variations only once } } - bool subset (hb_subset_layout_context_t *c, const void *base) const + bool subset (hb_subset_layout_context_t *c, const void *base, + bool insert_catch_all = false) const { TRACE_SUBSET (this); auto *out = c->subset_context->serializer->embed (this); if (unlikely (!out)) return_trace (false); - out->conditions.serialize_subset (c->subset_context, conditions, base, c); - out->substitutions.serialize_subset (c->subset_context, substitutions, base, c); + out->conditions.serialize_subset (c->subset_context, conditions, base, c, insert_catch_all); + out->substitutions.serialize_subset (c->subset_context, substitutions, base, c, insert_catch_all); return_trace (true); } bool sanitize (hb_sanitize_context_t *c, const void *base) const @@ -3769,27 +3841,32 @@ c->cur_record_idx = i; varRecords[i].collect_feature_substitutes_with_variations (c, this); if (c->universal) break; } - if (c->variation_applied && !c->universal && - !c->record_cond_idx_map->is_empty ()) - c->insert_catch_all_feature_variation_record = true; + if (c->universal || c->record_cond_idx_map->is_empty ()) + c->catch_all_record_feature_idxes.reset (); } FeatureVariations* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); return_trace (c->embed (*this)); } void collect_lookups (const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, + const hb_hashmap_t> *feature_record_cond_idx_map, hb_set_t *lookup_indexes /* OUT */) const { - for (const FeatureVariationRecord& r : varRecords) - r.collect_lookups (this, feature_indexes, feature_substitutes_map, lookup_indexes); + unsigned count = varRecords.len; + for (unsigned int i = 0; i < count; i++) + { + if (feature_record_cond_idx_map && + !feature_record_cond_idx_map->has (i)) + continue; + varRecords[i].collect_lookups (this, feature_indexes, lookup_indexes); + } } void closure_features (const hb_map_t *lookup_indexes, const hb_hashmap_t> *feature_record_cond_idx_map, hb_set_t *feature_indexes /* OUT */) const @@ -3830,17 +3907,25 @@ continue; l->cur_feature_var_record_idx = i; subset_record_array (l, &(out->varRecords), this) (varRecords[i]); } + + if (out->varRecords.len && !l->catch_all_record_feature_idxes->is_empty ()) + { + bool insert_catch_all_record = true; + subset_record_array (l, &(out->varRecords), this, insert_catch_all_record) (varRecords[0]); + } + return_trace (bool (out->varRecords)); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && varRecords.sanitize (c, this)); } protected: diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh --- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh @@ -2049,10 +2049,11 @@ public: bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_range (inputZ.arrayZ, inputZ.item_size * (inputCount ? inputCount - 1 : 0) + LookupRecord::static_size * lookupCount)); } @@ -2824,10 +2825,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); unsigned int count = glyphCount; if (unlikely (!count)) return_trace (false); /* We want to access coverageZ[0] freely. */ if (unlikely (!c->check_array (coverageZ.arrayZ, count))) return_trace (false); for (unsigned int i = 0; i < count; i++) if (unlikely (!coverageZ[i].sanitize (c, this))) return_trace (false); @@ -3217,14 +3219,17 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); /* Hyper-optimized sanitized because this is really hot. */ if (unlikely (!backtrack.len.sanitize (c))) return_trace (false); + hb_barrier (); const auto &input = StructAfter (backtrack); if (unlikely (!input.lenP1.sanitize (c))) return_trace (false); + hb_barrier (); const auto &lookahead = StructAfter (input); if (unlikely (!lookahead.len.sanitize (c))) return_trace (false); + hb_barrier (); const auto &lookup = StructAfter (lookahead); return_trace (likely (lookup.sanitize (c))); } protected: @@ -4119,15 +4124,18 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!backtrack.sanitize (c, this))) return_trace (false); + hb_barrier (); const auto &input = StructAfter (backtrack); if (unlikely (!input.sanitize (c, this))) return_trace (false); + hb_barrier (); if (unlikely (!input.len)) return_trace (false); /* To be consistent with Context. */ const auto &lookahead = StructAfter (input); if (unlikely (!lookahead.sanitize (c, this))) return_trace (false); + hb_barrier (); const auto &lookup = StructAfter (lookahead); return_trace (likely (lookup.sanitize (c))); } protected: @@ -4207,10 +4215,11 @@ /* This is called from may_dispatch() above with hb_sanitize_context_t. */ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && extensionLookupType != T::SubTable::Extension); } bool subset (hb_subset_context_t *c) const { @@ -4470,17 +4479,10 @@ { auto snapshot = c->subset_context->serializer->snapshot (); if (!c->subset_context->serializer->extend_min (&out->featureVars)) return_trace (false); - // TODO(qxliu76): the current implementation doesn't correctly handle feature variations - // that are dropped by instancing when the associated conditions don't trigger. - // Since partial instancing isn't yet supported this isn't an issue yet but will - // need to be fixed for partial instancing. - - - // if all axes are pinned all feature vars are dropped. bool ret = !c->subset_context->plan->all_axes_pinned && out->featureVars.serialize_subset (c->subset_context, featureVars, this, c); if (!ret && version.major == 1) { @@ -4511,10 +4513,11 @@ template bool sanitize (hb_sanitize_context_t *c) const { 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 case 2: return_trace (u.version2.sanitize (c)); #endif @@ -4636,15 +4639,15 @@ #endif return get_feature (feature_index); } void feature_variation_collect_lookups (const hb_set_t *feature_indexes, - const hb_hashmap_t *feature_substitutes_map, + const hb_hashmap_t> *feature_record_cond_idx_map, hb_set_t *lookup_indexes /* OUT */) const { #ifndef HB_NO_VAR - get_feature_variations ().collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes); + get_feature_variations ().collect_lookups (feature_indexes, feature_record_cond_idx_map, lookup_indexes); #endif } #ifndef HB_NO_VAR void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const diff --git a/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh b/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh --- a/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh @@ -212,10 +212,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && scriptList.sanitize (c, this)); } protected: diff --git a/gfx/harfbuzz/src/hb-ot-math-table.hh b/gfx/harfbuzz/src/hb-ot-math-table.hh --- a/gfx/harfbuzz/src/hb-ot-math-table.hh +++ b/gfx/harfbuzz/src/hb-ot-math-table.hh @@ -331,10 +331,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) && sanitize_math_value_records (c)); } hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const @@ -982,10 +983,11 @@ { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && vertGlyphCoverage.sanitize (c, this) && horizGlyphCoverage.sanitize (c, this) && + hb_barrier () && c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) && sanitize_offsets (c)); } hb_position_t get_min_connector_overlap (hb_direction_t direction, @@ -1101,10 +1103,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && likely (version.major == 1) && + hb_barrier () && mathConstants.sanitize (c, this) && mathGlyphInfo.sanitize (c, this) && mathVariants.sanitize (c, this)); } diff --git a/gfx/harfbuzz/src/hb-ot-maxp-table.hh b/gfx/harfbuzz/src/hb-ot-maxp-table.hh --- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh +++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh @@ -83,11 +83,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); - + hb_barrier (); if (version.major == 1) { const maxpV1Tail &v1 = StructAfter (*this); return_trace (v1.sanitize (c)); } @@ -101,10 +101,11 @@ if (unlikely (!maxp_prime)) return_trace (false); maxp_prime->numGlyphs = hb_min (c->plan->num_output_glyphs (), 0xFFFFu); if (maxp_prime->version.major == 1) { + hb_barrier (); const maxpV1Tail *src_v1 = &StructAfter (*this); maxpV1Tail *dest_v1 = c->serializer->embed (src_v1); if (unlikely (!dest_v1)) return_trace (false); if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) diff --git a/gfx/harfbuzz/src/hb-ot-meta-table.hh b/gfx/harfbuzz/src/hb-ot-meta-table.hh --- a/gfx/harfbuzz/src/hb-ot-meta-table.hh +++ b/gfx/harfbuzz/src/hb-ot-meta-table.hh @@ -49,10 +49,11 @@ bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && dataZ.sanitize (c, base, dataLength))); } protected: Tag tag; /* A tag indicating the type of metadata. */ @@ -99,10 +100,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && version == 1 && dataMaps.sanitize (c, this))); } protected: diff --git a/gfx/harfbuzz/src/hb-ot-os2-table.hh b/gfx/harfbuzz/src/hb-ot-os2-table.hh --- a/gfx/harfbuzz/src/hb-ot-os2-table.hh +++ b/gfx/harfbuzz/src/hb-ot-os2-table.hh @@ -207,10 +207,27 @@ float ret = a + (width - va) / (vb - va); if (a <= 6) ret += 1.0f; return ret; } + static unsigned calc_avg_char_width (const hb_hashmap_t>& hmtx_map) + { + unsigned num = 0; + unsigned total_width = 0; + for (const auto& _ : hmtx_map.values_ref ()) + { + unsigned width = _.first; + if (width) + { + total_width += width; + num++; + } + } + + return num ? (unsigned) roundf (total_width / num) : 0; + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); OS2 *os2_prime = c->serializer->embed (this); if (unlikely (!os2_prime)) return_trace (false); @@ -237,14 +254,20 @@ HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_SIZE, yStrikeoutSize); HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, yStrikeoutPosition); if (os2_prime->version >= 2) { + hb_barrier (); auto *table = & const_cast (os2_prime->v2 ()); HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_X_HEIGHT, sxHeight); HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_CAP_HEIGHT, sCapHeight); } + + unsigned avg_char_width = calc_avg_char_width (c->plan->hmtx_map); + if (!c->serializer->check_assign (os2_prime->xAvgCharWidth, avg_char_width, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); } #endif Triple *axis_range; if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t'), &axis_range)) @@ -332,10 +355,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); if (unlikely (version >= 1 && !v1X.sanitize (c))) return_trace (false); if (unlikely (version >= 2 && !v2X.sanitize (c))) return_trace (false); if (unlikely (version >= 5 && !v5X.sanitize (c))) return_trace (false); return_trace (true); } diff --git a/gfx/harfbuzz/src/hb-ot-post-table.hh b/gfx/harfbuzz/src/hb-ot-post-table.hh --- a/gfx/harfbuzz/src/hb-ot-post-table.hh +++ b/gfx/harfbuzz/src/hb-ot-post-table.hh @@ -120,11 +120,14 @@ if (post_prime->italicAngle.to_float () != italic_angle) post_prime->italicAngle.set_float (italic_angle); } if (glyph_names && version.major == 2) + { + hb_barrier (); return_trace (v2X.subset (c)); + } return_trace (true); } struct accelerator_t @@ -136,10 +139,11 @@ table = hb_sanitize_context_t ().reference_table (face); unsigned int table_length = table.get_length (); version = table->version.to_int (); if (version != 0x00020000) return; + hb_barrier (); const postV2Tail &v2 = table->v2X; glyphNameIndex = &v2.glyphNameIndex; pool = &StructAfter (v2.glyphNameIndex); @@ -215,14 +219,20 @@ protected: unsigned int get_glyph_count () const { if (version == 0x00010000) + { + hb_barrier (); return format1_names_length; + } if (version == 0x00020000) + { + hb_barrier (); return glyphNameIndex->len; + } return 0; } static int cmp_gids (const void *pa, const void *pb, void *arg) @@ -243,17 +253,22 @@ hb_bytes_t find_glyph_name (hb_codepoint_t glyph) const { if (version == 0x00010000) { + hb_barrier (); if (glyph >= format1_names_length) return hb_bytes_t (); return format1_names (glyph); } - if (version != 0x00020000 || glyph >= glyphNameIndex->len) + if (version != 0x00020000) + return hb_bytes_t (); + hb_barrier (); + + if (glyph >= glyphNameIndex->len) return hb_bytes_t (); unsigned int index = glyphNameIndex->arrayZ[glyph]; if (index < format1_names_length) return format1_names (index); @@ -282,10 +297,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && (version.to_int () == 0x00010000 || (version.to_int () == 0x00020000 && v2X.sanitize (c)) || version.to_int () == 0x00030000)); } diff --git a/gfx/harfbuzz/src/hb-ot-stat-table.hh b/gfx/harfbuzz/src/hb-ot-stat-table.hh --- a/gfx/harfbuzz/src/hb-ot-stat-table.hh +++ b/gfx/harfbuzz/src/hb-ot-stat-table.hh @@ -325,10 +325,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && axisValues.sanitize (c, axisCount))); } protected: HBUINT16 format; /* Format identifier — set to 4. */ @@ -414,10 +415,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); switch (u.format) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); @@ -558,10 +560,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && + hb_barrier () && version.major == 1 && version.minor > 0 && designAxesOffset.sanitize (c, this, designAxisCount) && offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets)))); } diff --git a/gfx/harfbuzz/src/hb-ot-var-avar-table.hh b/gfx/harfbuzz/src/hb-ot-var-avar-table.hh --- a/gfx/harfbuzz/src/hb-ot-var-avar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-avar-table.hh @@ -271,10 +271,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!(version.sanitize (c) && + hb_barrier () && (version.major == 1 #ifndef HB_NO_AVAR2 || version.major == 2 #endif ) && @@ -291,10 +292,11 @@ } #ifndef HB_NO_AVAR2 if (version.major < 2) return_trace (true); + hb_barrier (); const auto &v2 = * (const avarV2Tail *) map; if (unlikely (!v2.sanitize (c, this))) return_trace (false); #endif @@ -314,10 +316,11 @@ } #ifndef HB_NO_AVAR2 if (version.major < 2) return; + hb_barrier (); for (; count < axisCount; count++) map = &StructAfter (*map); const auto &v2 = * (const avarV2Tail *) map; diff --git a/gfx/harfbuzz/src/hb-ot-var-common.hh b/gfx/harfbuzz/src/hb-ot-var-common.hh --- a/gfx/harfbuzz/src/hb-ot-var-common.hh +++ b/gfx/harfbuzz/src/hb-ot-var-common.hh @@ -117,10 +117,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_range (mapDataZ.arrayZ, mapCount, get_width ())); } @@ -189,10 +190,11 @@ bool sanitize (hb_sanitize_context_t *c) const { 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 1: return_trace (u.format1.sanitize (c)); default:return_trace (true); } @@ -432,10 +434,12 @@ DELTA_RUN_COUNT_MASK = 0x3F }; struct tuple_delta_t { + static constexpr bool realloc_move = true; // Watch out when adding new members! + public: hb_hashmap_t axis_tuples; /* indices_length = point_count, indice[i] = 1 means point i is referenced */ hb_vector_t indices; @@ -512,18 +516,23 @@ { if (scalar == 1.0f) return *this; unsigned num = indices.length; - for (unsigned i = 0; i < num; i++) - { - if (!indices.arrayZ[i]) continue; - - deltas_x[i] *= scalar; - if (deltas_y) - deltas_y[i] *= scalar; - } + if (deltas_y) + for (unsigned i = 0; i < num; i++) + { + if (!indices.arrayZ[i]) continue; + deltas_x[i] *= scalar; + deltas_y[i] *= scalar; + } + else + for (unsigned i = 0; i < num; i++) + { + if (!indices.arrayZ[i]) continue; + deltas_x[i] *= scalar; + } return *this; } hb_vector_t change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit, TripleDistances axis_triple_distances) const @@ -765,11 +774,11 @@ { unsigned num_deltas = deltas.length; unsigned encoded_len = 0; while (i < num_deltas) { - int val = deltas[i]; + int val = deltas.arrayZ[i]; if (val == 0) encoded_len += encode_delta_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), deltas); else if (val >= -128 && val <= 127) encoded_len += encode_delta_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), deltas); else @@ -784,11 +793,11 @@ { unsigned num_deltas = deltas.length; unsigned run_length = 0; auto it = encoded_bytes.iter (); unsigned encoded_len = 0; - while (i < num_deltas && deltas[i] == 0) + while (i < num_deltas && deltas.arrayZ[i] == 0) { i++; run_length++; } @@ -813,17 +822,17 @@ { unsigned start = i; unsigned num_deltas = deltas.length; while (i < num_deltas) { - int val = deltas[i]; + int val = deltas.arrayZ[i]; if (val > 127 || val < -128) break; /* from fonttools: if there're 2 or more zeros in a sequence, * it is better to start a new run to save bytes. */ - if (val == 0 && i + 1 < num_deltas && deltas[i+1] == 0) + if (val == 0 && i + 1 < num_deltas && deltas.arrayZ[i+1] == 0) break; i++; } unsigned run_length = i - start; @@ -836,11 +845,11 @@ *it++ = 63; encoded_len++; for (unsigned j = 0; j < 64; j++) { - *it++ = static_cast (deltas[start + j]); + *it++ = static_cast (deltas.arrayZ[start + j]); encoded_len++; } start += 64; run_length -= 64; @@ -851,11 +860,11 @@ *it++ = run_length - 1; encoded_len++; while (start < i) { - *it++ = static_cast (deltas[start++]); + *it++ = static_cast (deltas.arrayZ[start++]); encoded_len++; } } return encoded_len; @@ -867,21 +876,21 @@ { unsigned start = i; unsigned num_deltas = deltas.length; while (i < num_deltas) { - int val = deltas[i]; - + int val = deltas.arrayZ[i]; + /* start a new run for a single zero value*/ if (val == 0) break; /* from fonttools: continue word-encoded run if there's only one * single value in the range [-128, 127] because it is more compact. * Only start a new run when there're 2 continuous such values. */ if (val >= -128 && val <= 127 && i + 1 < num_deltas && - deltas[i+1] >= -128 && deltas[i+1] <= 127) + deltas.arrayZ[i+1] >= -128 && deltas.arrayZ[i+1] <= 127) break; i++; } @@ -893,11 +902,11 @@ *it++ = (DELTAS_ARE_WORDS | 63); encoded_len++; for (unsigned j = 0; j < 64; j++) { - int16_t delta_val = deltas[start + j]; + int16_t delta_val = deltas.arrayZ[start + j]; *it++ = static_cast (delta_val >> 8); *it++ = static_cast (delta_val & 0xFF); encoded_len += 2; } @@ -910,11 +919,11 @@ { *it++ = (DELTAS_ARE_WORDS | (run_length - 1)); encoded_len++; while (start < i) { - int16_t delta_val = deltas[start++]; + int16_t delta_val = deltas.arrayZ[start++]; *it++ = static_cast (delta_val >> 8); *it++ = static_cast (delta_val & 0xFF); encoded_len += 2; } @@ -1173,20 +1182,22 @@ } bool create_from_item_var_data (const VarData &var_data, const hb_vector_t>& regions, const hb_map_t& axes_old_index_tag_map, + unsigned& item_count, const hb_inc_bimap_t* inner_map = nullptr) { /* NULL offset, to keep original varidx valid, just return */ if (&var_data == &Null (VarData)) return true; unsigned num_regions = var_data.get_region_index_count (); if (!tuple_vars.alloc (num_regions)) return false; - unsigned item_count = inner_map ? inner_map->get_population () : var_data.get_item_count (); + item_count = inner_map ? inner_map->get_population () : var_data.get_item_count (); + if (!item_count) return true; unsigned row_size = var_data.get_row_size (); const HBUINT8 *delta_bytes = var_data.get_delta_bytes (); for (unsigned r = 0; r < num_regions; r++) { @@ -1773,10 +1784,18 @@ private: /* each subtable is decompiled into a tuple_variations_t, in which all tuples * have the same num of deltas (rows) */ hb_vector_t vars; + /* num of retained rows for each subtable, there're 2 cases when var_data is empty: + * 1. retained item_count is zero + * 2. regions is empty and item_count is non-zero. + * when converting to tuples, both will be dropped because the tuple is empty, + * however, we need to retain 2. as all-zero rows to keep original varidx + * valid, so we need a way to remember the num of rows for each subtable */ + hb_vector_t var_data_num_rows; + /* original region list, decompiled from item varstore, used when rebuilding * region list after instantiation */ hb_vector_t> orig_region_list; /* region list: vector of Regions, maintain the original order for the regions @@ -1834,26 +1853,30 @@ if (!regionList.get_var_regions (axes_old_index_tag_map, orig_region_list)) return false; unsigned num_var_data = varStore.get_sub_table_count (); if (inner_maps && inner_maps.length != num_var_data) return false; - if (!vars.alloc (num_var_data)) return false; + if (!vars.alloc (num_var_data) || + !var_data_num_rows.alloc (num_var_data)) return false; for (unsigned i = 0; i < num_var_data; i++) { if (inner_maps && !inner_maps.arrayZ[i].get_population ()) continue; tuple_variations_t var_data_tuples; + unsigned item_count = 0; if (!var_data_tuples.create_from_item_var_data (varStore.get_sub_table (i), orig_region_list, axes_old_index_tag_map, + item_count, inner_maps ? &(inner_maps.arrayZ[i]) : nullptr)) return false; + var_data_num_rows.push (item_count); vars.push (std::move (var_data_tuples)); } - return !vars.in_error (); + return !vars.in_error () && !var_data_num_rows.in_error () && vars.length == var_data_num_rows.length; } bool instantiate_tuple_vars (const hb_hashmap_t& normalized_axes_location, const hb_hashmap_t& axes_triple_distances) { @@ -1971,16 +1994,12 @@ { if (!region_list) return false; unsigned num_cols = region_list.length; /* pre-alloc a 2D vector for all sub_table's VarData rows */ unsigned total_rows = 0; - for (unsigned major = 0; major < vars.length; major++) - { - const tuple_variations_t& tuples = vars[major]; - /* all tuples in each sub_table should have same num of deltas(num rows) */ - total_rows += tuples.tuple_vars[0].deltas_x.length; - } + for (unsigned major = 0; major < var_data_num_rows.length; major++) + total_rows += var_data_num_rows[major]; if (!delta_rows.resize (total_rows)) return false; /* init all rows to [0]*num_cols */ for (unsigned i = 0; i < total_rows; i++) if (!(delta_rows[i].resize (num_cols))) return false; @@ -1996,11 +2015,11 @@ for (unsigned major = 0; major < vars.length; major++) { /* deltas are stored in tuples(column based), convert them back into items * (row based) delta */ const tuple_variations_t& tuples = vars[major]; - unsigned num_rows = tuples.tuple_vars[0].deltas_x.length; + unsigned num_rows = var_data_num_rows[major]; for (const tuple_delta_t& tuple: tuples.tuple_vars) { if (tuple.deltas_x.length != num_rows) return false; diff --git a/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh --- a/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh @@ -43,11 +43,12 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && - version.sanitize (c) && likely (version.major == 1) && + hb_barrier () && + likely (version.major == 1) && tupleVariationData.sanitize (c)); } const TupleVariationData* get_tuple_var_data (void) const { return &tupleVariationData; } diff --git a/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh --- a/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh @@ -129,10 +129,11 @@ bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && c->check_array (coordinatesZ.arrayZ, axis_count)); } protected: NameID subfamilyNameID;/* The name ID for entries in the 'name' table @@ -275,12 +276,14 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && c->check_struct (this) && + hb_barrier () && axisSize == 20 && /* Assumed in our code. */ instanceSize >= axisCount * 4 + 4 && get_axes ().sanitize (c) && c->check_range (&StructAfter (get_axes ()), instanceCount, instanceSize)); diff --git a/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh --- a/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -294,11 +294,13 @@ static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && (version.major == 1) && + return_trace (c->check_struct (this) && + hb_barrier () && + (version.major == 1) && sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) && (is_long_offset () ? c->check_array (get_long_offset_array (), c->get_num_glyphs () + 1) : c->check_array (get_short_offset_array (), c->get_num_glyphs () + 1))); } @@ -424,11 +426,14 @@ { hb_codepoint_t old_gid = _.second; subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; } - bool long_offset = subset_data_size & ~0xFFFFu; + bool long_offset = (subset_data_size & ~0xFFFFu); + #ifdef HB_EXPERIMENTAL_API + long_offset = long_offset || (c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); +#endif out->flags = long_offset ? 1 : 0; HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset ? 4 : 2) * (num_glyphs + 1), false); if (!subset_offsets) return_trace (false); @@ -442,10 +447,12 @@ if (!tuples) return_trace (false); out->sharedTuples = (char *) tuples - (char *) out; hb_memcpy (tuples, this+sharedTuples, shared_tuple_size); } + /* This ordering relative to the shared tuples array, which puts the glyphVariationData + last in the table, is required when HB_SUBSET_FLAGS_IFTB_REQUIREMENTS is set */ char *subset_data = c->serializer->allocate_size (subset_data_size, false); if (!subset_data) return_trace (false); out->dataZ = subset_data - (char *) out; diff --git a/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh --- a/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh @@ -286,10 +286,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && varStore.sanitize (c, this) && advMap.sanitize (c, this) && lsbMap.sanitize (c, this) && rsbMap.sanitize (c, this)); diff --git a/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh --- a/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh +++ b/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh @@ -75,12 +75,14 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && + hb_barrier () && likely (version.major == 1) && c->check_struct (this) && + hb_barrier () && valueRecordSize >= VariationValueRecord::static_size && varStore.sanitize (c, this) && c->check_range (valuesZ.arrayZ, valueRecordCount, valueRecordSize)); diff --git a/gfx/harfbuzz/src/hb-ot-vorg-table.hh b/gfx/harfbuzz/src/hb-ot-vorg-table.hh --- a/gfx/harfbuzz/src/hb-ot-vorg-table.hh +++ b/gfx/harfbuzz/src/hb-ot-vorg-table.hh @@ -115,10 +115,11 @@ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && + hb_barrier () && version.major == 1 && vertYOrigins.sanitize (c)); } protected: diff --git a/gfx/harfbuzz/src/hb-priority-queue.hh b/gfx/harfbuzz/src/hb-priority-queue.hh --- a/gfx/harfbuzz/src/hb-priority-queue.hh +++ b/gfx/harfbuzz/src/hb-priority-queue.hh @@ -53,10 +53,13 @@ void reset () { heap.resize (0); } bool in_error () const { return heap.in_error (); } + bool alloc (unsigned size) + { return heap.alloc (size); } + #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE #endif void insert (K priority, unsigned value) { diff --git a/gfx/harfbuzz/src/hb-sanitize.hh b/gfx/harfbuzz/src/hb-sanitize.hh --- a/gfx/harfbuzz/src/hb-sanitize.hh +++ b/gfx/harfbuzz/src/hb-sanitize.hh @@ -132,11 +132,14 @@ lazy_some_gpos (false) {} const char *get_name () { return "SANITIZE"; } template bool may_dispatch (const T *obj HB_UNUSED, const F *format) - { return format->sanitize (this); } + { + return format->sanitize (this) && + hb_barrier (); + } static return_t default_return_value () { return true; } static return_t no_dispatch_return_value () { return false; } bool stop_sublookup_iteration (const return_t r) const { return !r; } bool visit_subtables (unsigned count) diff --git a/gfx/harfbuzz/src/hb-set.hh b/gfx/harfbuzz/src/hb-set.hh --- a/gfx/harfbuzz/src/hb-set.hh +++ b/gfx/harfbuzz/src/hb-set.hh @@ -33,10 +33,12 @@ template struct hb_sparseset_t { + static constexpr bool realloc_move = true; + hb_object_header_t header; impl_t s; hb_sparseset_t () { init (); } ~hb_sparseset_t () { fini (); } diff --git a/gfx/harfbuzz/src/hb-subset-cff1.cc b/gfx/harfbuzz/src/hb-subset-cff1.cc --- a/gfx/harfbuzz/src/hb-subset-cff1.cc +++ b/gfx/harfbuzz/src/hb-subset-cff1.cc @@ -618,10 +618,16 @@ num_glyphs = plan->num_output_glyphs (); orig_fdcount = acc.fdCount; drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; + #ifdef HB_EXPERIMENTAL_API + min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0; + #else + min_charstrings_off_size = 0; + #endif + subset_charset = !acc.is_predef_charset (); if (!subset_charset) /* check whether the subset renumbers any glyph IDs */ for (const auto &_ : plan->new_to_old_gid_list) { @@ -776,17 +782,47 @@ remap_sid_t sidmap; unsigned int topDictModSIDs[name_dict_values_t::ValCount]; bool desubroutinize = false; + + unsigned min_charstrings_off_size = 0; }; } // namespace OT +static bool _serialize_cff1_charstrings (hb_serialize_context_t *c, + struct OT::cff1_subset_plan &plan, + const OT::cff1::accelerator_subset_t &acc) +{ + c->push (); + + unsigned data_size = 0; + unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + + auto *cs = c->start_embed (); + if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) { + c->pop_discard (); + return false; + } + + plan.info.char_strings_link = c->pop_pack (false); + return true; +} + bool OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c, struct OT::cff1_subset_plan &plan) const { + /* push charstrings onto the object stack first which will ensure it packs as the last + object in the table. Keeping the chastrings last satisfies the requirements for patching + via IFTB. If this ordering needs to be changed in the future, charstrings should be left + at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */ + if (!_serialize_cff1_charstrings(c, plan, *this)) + return false; + /* private dicts & local subrs */ for (int i = (int) privateDicts.length; --i >= 0 ;) { if (plan.fdmap.has (i)) { @@ -821,29 +857,10 @@ } if (!is_CID ()) plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo; - /* CharStrings */ - { - c->push (); - - unsigned data_size = 0; - unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size); - if (unlikely (!c->start_zerocopy (total_size))) - return false; - - auto *cs = c->start_embed (); - if (likely (cs->serialize (c, plan.subset_charstrings, &data_size))) - plan.info.char_strings_link = c->pop_pack (false); - else - { - c->pop_discard (); - return false; - } - } - /* FDArray (FD Index) */ if (fdArray != &Null (CFF1FDArray)) { auto *fda = c->push (); cff1_font_dict_op_serializer_t fontSzr; diff --git a/gfx/harfbuzz/src/hb-subset-cff2.cc b/gfx/harfbuzz/src/hb-subset-cff2.cc --- a/gfx/harfbuzz/src/hb-subset-cff2.cc +++ b/gfx/harfbuzz/src/hb-subset-cff2.cc @@ -437,10 +437,16 @@ drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; pinned = (bool) plan->normalized_coords; desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE || pinned; // For instancing we need this path + #ifdef HB_EXPERIMENTAL_API + min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0; + #else + min_charstrings_off_size = 0; + #endif + if (desubroutinize) { /* Flatten global & local subrs */ subr_flattener_t, cff2_cs_opset_flatten_t> flattener(acc, plan); @@ -508,18 +514,49 @@ str_buff_vec_t subset_globalsubrs; hb_vector_t subset_localsubrs; bool drop_hints = false; bool desubroutinize = false; + + unsigned min_charstrings_off_size = 0; }; } // namespace OT +static bool _serialize_cff2_charstrings (hb_serialize_context_t *c, + cff2_subset_plan &plan, + const OT::cff2::accelerator_subset_t &acc) +{ + c->push (); + + unsigned data_size = 0; + unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + + auto *cs = c->start_embed (); + if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) + { + c->pop_discard (); + return false; + } + + plan.info.char_strings_link = c->pop_pack (false); + return true; +} + bool OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c, struct cff2_subset_plan &plan, hb_array_t normalized_coords) const { + /* push charstrings onto the object stack first which will ensure it packs as the last + object in the table. Keeping the chastrings last satisfies the requirements for patching + via IFTB. If this ordering needs to be changed in the future, charstrings should be left + at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */ + if (!_serialize_cff2_charstrings(c, plan, *this)) + return false; + /* private dicts & local subrs */ hb_vector_t private_dict_infos; if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; for (int i = (int)privateDicts.length; --i >= 0 ;) @@ -554,29 +591,10 @@ return false; } } } - /* CharStrings */ - { - c->push (); - - unsigned data_size = 0; - unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size); - if (unlikely (!c->start_zerocopy (total_size))) - return false; - - auto *cs = c->start_embed (); - if (likely (cs->serialize (c, plan.subset_charstrings, &data_size))) - plan.info.char_strings_link = c->pop_pack (false); - else - { - c->pop_discard (); - return false; - } - } - /* FDSelect */ if (fdSelect != &Null (CFF2FDSelect)) { c->push (); if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect, diff --git a/gfx/harfbuzz/src/hb-subset-input.cc b/gfx/harfbuzz/src/hb-subset-input.cc --- a/gfx/harfbuzz/src/hb-subset-input.cc +++ b/gfx/harfbuzz/src/hb-subset-input.cc @@ -121,10 +121,16 @@ HB_TAG ('r', 'a', 'n', 'd'), //justify HB_TAG ('j', 'a', 'l', 't'), // HarfBuzz doesn't use; others might + //East Asian spacing + HB_TAG ('c', 'h', 'w', 's'), + HB_TAG ('v', 'c', 'h', 'w'), + HB_TAG ('h', 'a', 'l', 't'), + HB_TAG ('v', 'h', 'a', 'l'), + //private HB_TAG ('H', 'a', 'r', 'f'), HB_TAG ('H', 'A', 'R', 'F'), HB_TAG ('B', 'u', 'z', 'z'), HB_TAG ('B', 'U', 'Z', 'Z'), diff --git a/gfx/harfbuzz/src/hb-subset-instancer-solver.cc b/gfx/harfbuzz/src/hb-subset-instancer-solver.cc --- a/gfx/harfbuzz/src/hb-subset-instancer-solver.cc +++ b/gfx/harfbuzz/src/hb-subset-instancer-solver.cc @@ -166,16 +166,18 @@ * | | | * axisDef | axisMax * | * crossing */ - if (gain > outGain) + if (gain >= outGain) { + // Note that this is the branch taken if both gain and outGain are 0. + // Crossing point on the axis. float crossing = peak + (1 - gain) * (upper - peak); - Triple loc{axisDef, peak, crossing}; + Triple loc{hb_max (lower, axisDef), peak, crossing}; float scalar = 1.f; // The part before the crossing point. out.push (hb_pair (scalar - gain, loc)); @@ -251,11 +253,11 @@ * axisMin lower| | | upper * | | newUpper * axisDef axisMax */ float newUpper = peak + (1 - gain) * (upper - peak); - assert (axisMax <= newUpper); // Because outGain >= gain + assert (axisMax <= newUpper); // Because outGain > gain if (newUpper <= axisDef + (axisMax - axisDef) * 2) { upper = newUpper; if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper) { diff --git a/gfx/harfbuzz/src/hb-subset-plan-member-list.hh b/gfx/harfbuzz/src/hb-subset-plan-member-list.hh --- a/gfx/harfbuzz/src/hb-subset-plan-member-list.hh +++ b/gfx/harfbuzz/src/hb-subset-plan-member-list.hh @@ -68,10 +68,13 @@ //active lookups we'd like to retain HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_lookups) HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_lookups) +//use_mark_sets mapping: old->new +HB_SUBSET_PLAN_MEMBER (hb_map_t, used_mark_sets_map) + //active langsys we'd like to retain HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gsub_langsys) HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gpos_langsys) //active features after removing redundant langsys and prune_features @@ -85,10 +88,19 @@ //feature index-> address of substituation feature table mapping with //variations HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(), gsub_feature_substitutes_map) HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(), gpos_feature_substitutes_map) +// old feature_indexes set, used to reinstate the old features +HB_SUBSET_PLAN_MEMBER (hb_set_t, gsub_old_features) +HB_SUBSET_PLAN_MEMBER (hb_set_t, gpos_old_features) + +//feature_index->pair of (address of old feature, feature tag), used for inserting a catch all record +//if necessary +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E()>), gsub_old_feature_idx_tag_map) +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E()>), gpos_old_feature_idx_tag_map) + //active layers/palettes we'd like to retain HB_SUBSET_PLAN_MEMBER (hb_map_t, colrv1_layers) HB_SUBSET_PLAN_MEMBER (hb_map_t, colr_palettes) //Old layout item variation index -> (New varidx, delta) mapping diff --git a/gfx/harfbuzz/src/hb-subset-plan.hh b/gfx/harfbuzz/src/hb-subset-plan.hh --- a/gfx/harfbuzz/src/hb-subset-plan.hh +++ b/gfx/harfbuzz/src/hb-subset-plan.hh @@ -145,10 +145,13 @@ // whether to insert a catch-all FeatureVariationRecord bool gsub_insert_catch_all_feature_variation_rec; bool gpos_insert_catch_all_feature_variation_rec; + // whether GDEF VarStore is retained + mutable bool has_gdef_varstore; + #define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name; #include "hb-subset-plan-member-list.hh" #undef HB_SUBSET_PLAN_MEMBER //recalculated head/maxp table info after instancing diff --git a/gfx/harfbuzz/src/hb-subset-plan.cc b/gfx/harfbuzz/src/hb-subset-plan.cc --- a/gfx/harfbuzz/src/hb-subset-plan.cc +++ b/gfx/harfbuzz/src/hb-subset-plan.cc @@ -148,11 +148,12 @@ const T& table, hb_set_t *lookup_indices, /* OUT */ hb_set_t *feature_indices, /* OUT */ hb_hashmap_t> *feature_record_cond_idx_map, /* OUT */ hb_hashmap_t *feature_substitutes_map, /* OUT */ - bool& insert_catch_all_feature_variation_record) + hb_set_t& catch_all_record_feature_idxes, /* OUT */ + hb_hashmap_t>& catch_all_record_idx_feature_map /* OUT */) { unsigned num_features = table.get_feature_count (); hb_vector_t features; if (!plan->check_success (features.resize (num_features))) return; table.get_feature_tags (0, &num_features, features.arrayZ); @@ -184,11 +185,11 @@ { &plan->axes_old_index_tag_map, &plan->axes_location, feature_record_cond_idx_map, feature_substitutes_map, - insert_catch_all_feature_variation_record, + catch_all_record_feature_idxes, feature_indices, false, false, false, 0, @@ -206,21 +207,29 @@ f = *p; f->add_lookup_indexes_to (lookup_indices); } +#ifndef HB_NO_VAR + if (catch_all_record_feature_idxes) + { + for (unsigned feature_index : catch_all_record_feature_idxes) + { + const OT::Feature& f = table.get_feature (feature_index); + f.add_lookup_indexes_to (lookup_indices); + const void *tag = reinterpret_cast (&(table.get_feature_list ().get_tag (feature_index))); + catch_all_record_idx_feature_map.set (feature_index, hb_pair (&f, tag)); + } + } + // If all axes are pinned then all feature variations will be dropped so there's no need // to collect lookups from them. if (!plan->all_axes_pinned) - { - // TODO(qxliu76): this collection doesn't work correctly for feature variations that are dropped - // but not applied. The collection will collect and retain the lookup indices - // associated with those dropped but not activated rules. Since partial instancing - // isn't yet supported this isn't an issue yet but will need to be fixed for - // partial instancing. - table.feature_variation_collect_lookups (feature_indices, feature_substitutes_map, lookup_indices); - } + table.feature_variation_collect_lookups (feature_indices, + plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map, + lookup_indices); +#endif } static inline void _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g, @@ -300,22 +309,24 @@ hb_map_t *lookups, hb_map_t *features, script_langsys_map *langsys_map, hb_hashmap_t> *feature_record_cond_idx_map, hb_hashmap_t *feature_substitutes_map, - bool& insert_catch_all_feature_variation_record) + hb_set_t &catch_all_record_feature_idxes, + hb_hashmap_t>& catch_all_record_idx_feature_map) { hb_blob_ptr_t table = plan->source_table (); hb_tag_t table_tag = table->tableTag; hb_set_t lookup_indices, feature_indices; _collect_layout_indices (plan, *table, &lookup_indices, &feature_indices, feature_record_cond_idx_map, feature_substitutes_map, - insert_catch_all_feature_variation_record); + catch_all_record_feature_idxes, + catch_all_record_idx_feature_map); if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE)) hb_ot_layout_lookups_substitute_closure (plan->source, &lookup_indices, gids_to_retain); @@ -463,10 +474,28 @@ if (math->has_data ()) math->closure_glyphs (glyphset); math.destroy (); } +static inline void +_remap_used_mark_sets (hb_subset_plan_t *plan, + hb_map_t& used_mark_sets_map) +{ + hb_blob_ptr_t gdef = plan->source_table (); + + if (!gdef->has_data () || !gdef->has_mark_glyph_sets ()) + { + gdef.destroy (); + return; + } + + hb_set_t used_mark_sets; + gdef->get_mark_glyph_sets ().collect_used_mark_sets (plan->_glyphset_gsub, used_mark_sets); + gdef.destroy (); + + _remap_indexes (&used_mark_sets, &used_mark_sets_map); +} static inline void _remove_invalid_gids (hb_set_t *glyphs, unsigned int num_glyphs) { @@ -576,18 +605,22 @@ plan->unicode_to_new_gid_list.qsort (); } else { plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ()); - for (hb_codepoint_t cp : *cmap_unicodes) + hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID; + for (; cmap_unicodes->next_range (&first, &last); ) { - hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; - if (!unicodes->has (cp) && !glyphs->has (gid)) - continue; + for (unsigned cp = first; cp <= last; cp++) + { + hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; + if (!unicodes->has (cp) && !glyphs->has (gid)) + continue; - plan->codepoint_to_glyph->set (cp, gid); - plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } } } /* Add gids which where requested, but not mapped in cmap */ unsigned num_glyphs = plan->source->get_num_glyphs (); @@ -712,22 +745,24 @@ &plan->gsub_lookups, &plan->gsub_features, &plan->gsub_langsys, &plan->gsub_feature_record_cond_idx_map, &plan->gsub_feature_substitutes_map, - plan->gsub_insert_catch_all_feature_variation_rec); + plan->gsub_old_features, + plan->gsub_old_feature_idx_tag_map); if (!drop_tables->has (HB_OT_TAG_GPOS)) _closure_glyphs_lookups_features ( plan, &plan->_glyphset_gsub, &plan->gpos_lookups, &plan->gpos_features, &plan->gpos_langsys, &plan->gpos_feature_record_cond_idx_map, &plan->gpos_feature_substitutes_map, - plan->gpos_insert_catch_all_feature_variation_rec); + plan->gpos_old_features, + plan->gpos_old_feature_idx_tag_map); #endif _remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ()); plan->_glyphset_mathed = plan->_glyphset_gsub; if (!drop_tables->has (HB_OT_TAG_MATH)) @@ -812,16 +847,16 @@ return false; } if (retain_gids) { - DEBUG_MSG (SUBSET, nullptr, + DEBUG_MSG (SUBSET, nullptr, "HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if " "a custom glyph mapping has been provided."); return false; } - + hb_codepoint_t max_glyph = 0; hb_set_t remaining; for (auto old_gid : all_gids_to_retain->iter ()) { if (old_gid == 0) { @@ -869,13 +904,15 @@ hb_set_previous (all_gids_to_retain, &max_glyph); *num_glyphs = max_glyph + 1; } + reverse_glyph_map->alloc (reverse_glyph_map->get_population () + new_to_old_gid_list->length); + hb_iter (new_to_old_gid_list) | hb_sink (reverse_glyph_map) ; + glyph_map->alloc (glyph_map->get_population () + new_to_old_gid_list->length); + hb_iter (new_to_old_gid_list) | hb_map (&hb_codepoint_pair_t::reverse) | hb_sink (glyph_map) ; @@ -967,11 +1004,11 @@ hb_glyph_extents_t extents = {0x7FFF, -0x7FFF}; OT::hmtx_accelerator_t _hmtx (plan->source); float *hvar_store_cache = nullptr; if (_hmtx.has_data () && _hmtx.var_table.get_length ()) hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache (); - + OT::vmtx_accelerator_t _vmtx (plan->source); float *vvar_store_cache = nullptr; if (_vmtx.has_data () && _vmtx.var_table.get_length ()) vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache (); @@ -1091,10 +1128,11 @@ gdef_varstore_inner_maps.init (); user_axes_location = input->axes_location; all_axes_pinned = false; pinned_at_default = true; + has_gdef_varstore = false; #ifdef HB_EXPERIMENTAL_API for (auto _ : input->name_table_overrides) { hb_bytes_t name_bytes = _.second; @@ -1110,10 +1148,14 @@ void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key()); attach_accelerator_data = input->attach_accelerator_data; force_long_loca = input->force_long_loca; +#ifdef HB_EXPERIMENTAL_API + force_long_loca = force_long_loca || (flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); +#endif + if (accel) accelerator = (hb_subset_accelerator_t*) accel; if (unlikely (in_error ())) return; @@ -1158,10 +1200,13 @@ v = 0xFFFFFFFF; bounds_height_vec.resize (_num_output_glyphs, false); for (auto &v : bounds_height_vec) v = 0xFFFFFFFF; + if (!drop_tables.has (HB_OT_TAG_GDEF)) + _remap_used_mark_sets (this, used_mark_sets_map); + if (unlikely (in_error ())) return; #ifndef HB_NO_VAR _update_instance_metrics_map_from_cff2 (this); diff --git a/gfx/harfbuzz/src/hb-subset.h b/gfx/harfbuzz/src/hb-subset.h --- a/gfx/harfbuzz/src/hb-subset.h +++ b/gfx/harfbuzz/src/hb-subset.h @@ -71,10 +71,13 @@ * in the final subset. * @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in * OS/2 will not be recalculated. * @HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE: If set don't perform glyph closure on layout * substitution rules (GSUB). Since: 7.2.0. + * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset + * to allow it to be used with incremental font transfer IFTB patches. Primarily, + * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL * * List of boolean properties that can be configured on the subset input. * * Since: 2.9.0 **/ @@ -88,10 +91,13 @@ HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED = 0x00000020u, HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u, HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u, HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u, HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u, +#ifdef HB_EXPERIMENTAL_API + HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00000400u, +#endif } hb_subset_flags_t; /** * hb_subset_sets_t: * @HB_SUBSET_SETS_GLYPH_INDEX: the set of glyph indexes to retain in the subset. diff --git a/gfx/harfbuzz/src/hb-subset.cc b/gfx/harfbuzz/src/hb-subset.cc --- a/gfx/harfbuzz/src/hb-subset.cc +++ b/gfx/harfbuzz/src/hb-subset.cc @@ -458,13 +458,14 @@ switch (tag) { case HB_OT_TAG_hmtx: case HB_OT_TAG_vmtx: case HB_OT_TAG_maxp: + case HB_OT_TAG_OS2: return !plan->normalized_coords || !pending_subset_tags.has (HB_OT_TAG_glyf); case HB_OT_TAG_GPOS: - return !plan->normalized_coords || plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); + return plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); default: return true; } } diff --git a/gfx/harfbuzz/src/hb-vector.hh b/gfx/harfbuzz/src/hb-vector.hh --- a/gfx/harfbuzz/src/hb-vector.hh +++ b/gfx/harfbuzz/src/hb-vector.hh @@ -35,10 +35,12 @@ template struct hb_vector_t { + static constexpr bool realloc_move = true; + typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); using array_t = typename std::conditional, hb_array_t>::type; using c_array_t = typename std::conditional, hb_array_t>::type; @@ -266,14 +268,13 @@ } hb_free (arrayZ); } return new_array; } - /* Specialization for hb_vector_t> to speed up. */ + /* Specialization for types that can be moved using realloc(). */ template ) || - hb_is_same (T, hb_array_t ))> + hb_enable_if (T::realloc_move)> Type * realloc_vector (unsigned new_allocated, hb_priority<1>) { if (!new_allocated) { diff --git a/gfx/harfbuzz/src/hb-version.h b/gfx/harfbuzz/src/hb-version.h --- a/gfx/harfbuzz/src/hb-version.h +++ b/gfx/harfbuzz/src/hb-version.h @@ -45,24 +45,24 @@ /** * HB_VERSION_MINOR: * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 2 +#define HB_VERSION_MINOR 3 /** * HB_VERSION_MICRO: * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 2 +#define HB_VERSION_MICRO 0 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "8.2.2" +#define HB_VERSION_STRING "8.3.0" /** * HB_VERSION_ATLEAST: * @major: the major component of the version number * @minor: the minor component of the version number diff --git a/gfx/harfbuzz/src/meson.build b/gfx/harfbuzz/src/meson.build --- a/gfx/harfbuzz/src/meson.build +++ b/gfx/harfbuzz/src/meson.build @@ -683,33 +683,19 @@ # TODO: MSVC gives the following, # error LNK2019: unresolved external symbol "unsigned __int64 const * const _hb_NullPool" if cpp.get_define('_MSC_FULL_VER') == '' noinst_programs = { 'main': 'main.cc', - 'test-algs': ['test-algs.cc', 'hb-static.cc'], 'test-basics': 'test.cc', - 'test-bimap': ['test-bimap.cc', 'hb-static.cc'], 'test-buffer-serialize': 'test-buffer-serialize.cc', - 'test-classdef-graph': ['graph/test-classdef-graph.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], - 'test-instancer-solver': ['test-subset-instancer-solver.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], - 'test-iter': ['test-iter.cc', 'hb-static.cc'], - 'test-map': ['test-map.cc', 'hb-static.cc'], - 'test-multimap': ['test-multimap.cc', 'hb-static.cc'], 'test-ot-meta': 'test-ot-meta.cc', 'test-ot-name': 'test-ot-name.cc', 'test-ot-glyphname': 'test-ot-glyphname.cc', 'test-ot-gpos-size-params': 'test-gpos-size-params.cc', 'test-ot-gsub-get-alternates': 'test-gsub-get-alternates.cc', 'test-ot-gsub-would-substitute': 'test-gsub-would-substitute.cc', - 'test-priority-queue': ['test-priority-queue.cc', 'hb-static.cc'], - 'test-repacker': ['test-repacker.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], - 'test-serialize': ['test-serialize.cc', 'hb-static.cc'], - 'test-set': ['test-set.cc', 'hb-static.cc'], - 'test-tuple-varstore': ['test-tuple-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], - 'test-item-varstore': ['test-item-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], 'test-use-table': 'test-use-table.cc', - 'test-vector': ['test-vector.cc', 'hb-static.cc'], } foreach name, source : noinst_programs executable(name, source, include_directories: incconfig, cpp_args: cpp_args + ['-UNDEBUG'], @@ -718,14 +704,28 @@ ) endforeach endif compiled_tests = { + 'test-algs': ['test-algs.cc', 'hb-static.cc'], 'test-array': ['test-array.cc'], + 'test-bimap': ['test-bimap.cc', 'hb-static.cc'], + 'test-classdef-graph': ['graph/test-classdef-graph.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], + 'test-iter': ['test-iter.cc', 'hb-static.cc'], 'test-machinery': ['test-machinery.cc', 'hb-static.cc'], + 'test-map': ['test-map.cc', 'hb-static.cc'], + 'test-multimap': ['test-multimap.cc', 'hb-static.cc'], 'test-number': ['test-number.cc', 'hb-number.cc'], 'test-ot-tag': ['hb-ot-tag.cc'], + 'test-set': ['test-set.cc', 'hb-static.cc'], + 'test-serialize': ['test-serialize.cc', 'hb-static.cc'], + 'test-vector': ['test-vector.cc', 'hb-static.cc'], + 'test-repacker': ['test-repacker.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], + 'test-instancer-solver': ['test-subset-instancer-solver.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], + 'test-priority-queue': ['test-priority-queue.cc', 'hb-static.cc'], + 'test-tuple-varstore': ['test-tuple-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], + 'test-item-varstore': ['test-item-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], 'test-unicode-ranges': ['test-unicode-ranges.cc'], } foreach name, source : compiled_tests if cpp.get_argument_syntax() == 'msvc' and source.contains('hb-static.cc') # TODO: MSVC doesn't like tests having hb-static.cc, fix them @@ -866,11 +866,11 @@ if get_option('default_library') == 'static' cmake_config.set('HB_LIB_PREFIX', '${CMAKE_STATIC_LIBRARY_PREFIX}') cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_STATIC_LIBRARY_SUFFIX}') elif host_machine.system() == 'darwin' cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}') - cmake_config.set('HB_LIB_SUFFIX', '.@0@.${CMAKE_SHARED_LIBRARY_SUFFIX}'.format(hb_so_version)) + cmake_config.set('HB_LIB_SUFFIX', '.@0@${CMAKE_SHARED_LIBRARY_SUFFIX}'.format(hb_so_version)) elif host_machine.system() == 'windows' cmake_config.set('HB_LIB_PREFIX', '${CMAKE_IMPORT_LIBRARY_PREFIX}') cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_IMPORT_LIBRARY_SUFFIX}') else cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}') diff --git a/gfx/harfbuzz/src/relative_to.py b/gfx/harfbuzz/src/relative_to.py --- a/gfx/harfbuzz/src/relative_to.py +++ b/gfx/harfbuzz/src/relative_to.py @@ -1,6 +1,6 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 import sys from os import path print(path.relpath(sys.argv[1], sys.argv[2]))