[subset] subset MATH table
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index fcaf76c..87ac878 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh
@@ -346,6 +346,43 @@ } HB_FUNCOBJ (subset_record_array); + +template<typename OutputArray> +struct serialize_math_record_array_t +{ + serialize_math_record_array_t (hb_serialize_context_t *serialize_context_, + OutputArray& out_, + const void *base_) : serialize_context (serialize_context_), + out (out_), base (base_) {} + + template <typename T> + bool operator () (T&& record) + { + if (!serialize_context->copy (record, base)) return false; + out.len++; + return true; + } + + private: + hb_serialize_context_t *serialize_context; + OutputArray &out; + const void *base; +}; + +/* + * Helper to serialize an array of MATH records. + */ +struct +{ + template<typename OutputArray> + serialize_math_record_array_t<OutputArray> + operator () (hb_serialize_context_t *serialize_context, OutputArray& out, + const void *base) const + { return serialize_math_record_array_t<OutputArray> (serialize_context, out, base); } + +} +HB_FUNCOBJ (serialize_math_record_array); + /* * * OpenType Layout Common Table Formats
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh index 2cfc5ba..f9c5a83 100644 --- a/src/hb-ot-math-table.hh +++ b/src/hb-ot-math-table.hh
@@ -41,6 +41,16 @@ hb_position_t get_y_value (hb_font_t *font, const void *base) const { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); } + MathValueRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->deviceTable.serialize_copy (c, deviceTable, base, 0, hb_serialize_context_t::Head); + + return_trace (out); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -59,6 +69,29 @@ struct MathConstants { + MathConstants* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!out)) return_trace (nullptr); + + HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2); + if (unlikely (!p)) return_trace (nullptr); + memcpy (p, percentScaleDown, HBINT16::static_size * 2); + + HBUINT16 *m = c->allocate_size<HBUINT16> (HBUINT16::static_size * 2); + if (unlikely (!m)) return_trace (nullptr); + memcpy (m, minHeight, HBUINT16::static_size * 2); + + unsigned count = ARRAY_LENGTH (mathValueRecords); + for (unsigned i = 0; i < count; i++) + if (!c->copy (mathValueRecords[i], this)) + return_trace (nullptr); + + if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr); + return_trace (out); + } + bool sanitize_math_value_records (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -165,6 +198,28 @@ struct MathItalicsCorrectionInfo { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->_glyphset_mathed; + 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); + + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + hb_zip (this+coverage, italicsCorrection) + | hb_filter (glyphset, hb_first) + | hb_filter (serialize_math_record_array (c->serializer, out->italicsCorrection, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -196,6 +251,28 @@ struct MathTopAccentAttachment { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->_glyphset_mathed; + 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); + + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + hb_zip (this+topAccentCoverage, topAccentAttachment) + | hb_filter (glyphset, hb_first) + | hb_filter (serialize_math_record_array (c->serializer, out->topAccentAttachment, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -229,6 +306,22 @@ struct MathKern { + MathKern* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!out)) return_trace (nullptr); + + if (unlikely (!c->embed (heightCount))) return_trace (nullptr); + + unsigned count = 2 * heightCount + 1; + for (unsigned i = 0; i < count; i++) + if (!c->copy (mathValueRecordsZ.arrayZ[i], this)) + return_trace (nullptr); + + return_trace (out); + } + bool sanitize_math_value_records (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -295,6 +388,19 @@ struct MathKernInfoRecord { + MathKernInfoRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + unsigned count = ARRAY_LENGTH (mathKern); + for (unsigned i = 0; i < count; i++) + out->mathKern[i].serialize_copy (c, mathKern[i], base, 0, hb_serialize_context_t::Head); + + return_trace (out); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -328,6 +434,28 @@ struct MathKernInfo { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->_glyphset_mathed; + 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); + + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + hb_zip (this+mathKernCoverage, mathKernInfoRecords) + | hb_filter (glyphset, hb_first) + | hb_filter (serialize_math_record_array (c->serializer, out->mathKernInfoRecords, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -365,6 +493,30 @@ struct MathGlyphInfo { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this); + out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this); + + const hb_set_t &glyphset = *c->plan->_glyphset_mathed; + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+extendedShapeCoverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + out->extendedShapeCoverage.serialize_serialize (c->serializer, it); + + out->mathKernInfo.serialize_subset (c, mathKernInfo, this); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -420,6 +572,16 @@ { friend struct MathGlyphConstruction; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + const hb_map_t& glyph_map = *c->plan->glyph_map; + return_trace (c->serializer->check_assign (out->variantGlyph, glyph_map.get (variantGlyph), HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -453,6 +615,16 @@ struct MathGlyphPartRecord { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + const hb_map_t& glyph_map = *c->plan->glyph_map; + return_trace (c->serializer->check_assign (out->glyph, glyph_map.get (glyph), HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -503,6 +675,20 @@ struct MathGlyphAssembly { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + if (!c->serializer->copy (italicsCorrection, this)) return_trace (false); + if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false); + + for (const auto& record : partRecords.iter ()) + if (!record.subset (c)) return_trace (false); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -555,6 +741,22 @@ struct MathGlyphConstruction { + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->glyphAssembly.serialize_subset (c, glyphAssembly, this); + + if (!c->serializer->check_assign (out->mathGlyphVariantRecord.len, mathGlyphVariantRecord.len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + for (const auto& record : mathGlyphVariantRecord.iter ()) + if (!record.subset (c)) return_trace (false); + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -630,6 +832,60 @@ ; } } + + void collect_coverage_and_indices (hb_sorted_vector_t<hb_codepoint_t>& new_coverage, + const Offset16To<Coverage>& coverage, + unsigned i, + hb_set_t& indices, + const hb_set_t& glyphset, + const hb_map_t& glyph_map) const + { + for (const auto _ : (this+coverage).iter ()) + { + if (glyphset.has (_)) + { + unsigned new_gid = glyph_map.get (_); + new_coverage.push (new_gid); + indices.add (i); + } + i++; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->_glyphset_mathed; + 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); + if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage; + hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage; + hb_set_t indices; + collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, indices, glyphset, glyph_map); + collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, indices, glyphset, glyph_map); + + if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + for (unsigned i : indices.iter ()) + { + auto *o = c->serializer->embed (glyphConstruction[i]); + if (!o) return_trace (false); + o->serialize_subset (c, glyphConstruction[i], this); + } + + out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ()); + out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ()); + return_trace (true); + } + bool sanitize_offsets (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -747,6 +1003,18 @@ } } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + out->mathConstants.serialize_copy (c->serializer, mathConstants, this, 0, hb_serialize_context_t::Head); + out->mathGlyphInfo.serialize_subset (c, mathGlyphInfo, this); + out->mathVariants.serialize_subset (c, mathVariants, this); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this);
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 677df35..1bdf133 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc
@@ -38,6 +38,7 @@ #include "hb-ot-color-colrv1-closure.hh" #include "hb-ot-var-fvar-table.hh" #include "hb-ot-stat-table.hh" +#include "hb-ot-math-table.hh" typedef hb_hashmap_t<unsigned, hb_set_t *, (unsigned)-1, nullptr> script_langsys_map; @@ -222,6 +223,17 @@ } static inline void +_math_closure (hb_face_t *face, + hb_set_t *glyphset) +{ + hb_blob_ptr_t<OT::MATH> math = hb_sanitize_context_t ().reference_table<OT::MATH> (face); + if (math->has_data ()) + math->closure_glyphs (glyphset); + math.destroy (); +} + + +static inline void _remove_invalid_gids (hb_set_t *glyphs, unsigned int num_glyphs) { @@ -334,8 +346,12 @@ #endif _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ()); + hb_set_set (plan->_glyphset_mathed, plan->_glyphset_gsub); + _math_closure (plan->source, plan->_glyphset_mathed); + _remove_invalid_gids (plan->_glyphset_mathed, plan->source->get_num_glyphs ()); + // Collect all glyphs referenced by COLRv0 - hb_set_t* cur_glyphset = plan->_glyphset_gsub; + hb_set_t* cur_glyphset = plan->_glyphset_mathed; hb_set_t glyphset_colrv0; if (colr.is_valid ()) { @@ -468,6 +484,7 @@ plan->_glyphset = hb_set_create (); plan->_glyphset_gsub = hb_set_create (); + plan->_glyphset_mathed = hb_set_create (); plan->codepoint_to_glyph = hb_map_create (); plan->glyph_map = hb_map_create (); plan->reverse_glyph_map = hb_map_create (); @@ -535,6 +552,7 @@ hb_map_destroy (plan->reverse_glyph_map); hb_set_destroy (plan->_glyphset); hb_set_destroy (plan->_glyphset_gsub); + hb_set_destroy (plan->_glyphset_mathed); hb_map_destroy (plan->gsub_lookups); hb_map_destroy (plan->gpos_lookups); hb_map_destroy (plan->gsub_features);
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index 92a4e27..c30feeb 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh
@@ -77,6 +77,7 @@ unsigned int _num_output_glyphs; hb_set_t *_glyphset; hb_set_t *_glyphset_gsub; + hb_set_t *_glyphset_mathed; //active lookups we'd like to retain hb_map_t *gsub_lookups;
diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 34f92e0..5bf1c17 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc
@@ -52,6 +52,7 @@ #include "hb-ot-layout-gpos-table.hh" #include "hb-ot-var-gvar-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-math-table.hh" #include "hb-repacker.hh" /** @@ -305,6 +306,7 @@ case HB_OT_TAG_CPAL: return _subset<const OT::CPAL> (plan); case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan); case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ + case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan); #ifndef HB_NO_SUBSET_CFF case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan);
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am index 3f521fb..79dfd31 100644 --- a/test/subset/data/Makefile.am +++ b/test/subset/data/Makefile.am
@@ -44,6 +44,7 @@ expected/cbdt \ expected/variable \ expected/glyph_names \ + expected/math \ fonts \ profiles \ $(NULL)
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources index ad94589..d9cd4e4 100644 --- a/test/subset/data/Makefile.sources +++ b/test/subset/data/Makefile.sources
@@ -36,6 +36,7 @@ tests/sbix.tests \ tests/variable.tests \ tests/glyph_names.tests \ + tests/math.tests \ $(NULL) # TODO: re-enable once colrv1 subsetting is stabilized.
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.default.2f,7c,305.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.default.2f,7c,305.ttf new file mode 100644 index 0000000..e662f4c --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.default.2f,7c,305.ttf Binary files differ
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.default.retain-all-codepoint.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.default.retain-all-codepoint.ttf new file mode 100644 index 0000000..ee5d11c --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.default.retain-all-codepoint.ttf Binary files differ
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.glyph-names.2f,7c,305.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.glyph-names.2f,7c,305.ttf new file mode 100644 index 0000000..b757c5c --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.glyph-names.2f,7c,305.ttf Binary files differ
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.glyph-names.retain-all-codepoint.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.glyph-names.retain-all-codepoint.ttf new file mode 100644 index 0000000..ca9c66b --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.glyph-names.retain-all-codepoint.ttf Binary files differ
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.notdef-outline.2f,7c,305.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.notdef-outline.2f,7c,305.ttf new file mode 100644 index 0000000..a93fdbb --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.notdef-outline.2f,7c,305.ttf Binary files differ
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.notdef-outline.retain-all-codepoint.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.notdef-outline.retain-all-codepoint.ttf new file mode 100644 index 0000000..4c10ec7 --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.notdef-outline.retain-all-codepoint.ttf Binary files differ
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.retain-gids.2f,7c,305.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.retain-gids.2f,7c,305.ttf new file mode 100644 index 0000000..77a8636 --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.retain-gids.2f,7c,305.ttf Binary files differ
diff --git a/test/subset/data/expected/math/STIXTwoMath-Regular.retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/math/STIXTwoMath-Regular.retain-gids.retain-all-codepoint.ttf new file mode 100644 index 0000000..6da91af --- /dev/null +++ b/test/subset/data/expected/math/STIXTwoMath-Regular.retain-gids.retain-all-codepoint.ttf Binary files differ
diff --git a/test/subset/data/fonts/STIXTwoMath-Regular.ttf b/test/subset/data/fonts/STIXTwoMath-Regular.ttf new file mode 100644 index 0000000..351bfab --- /dev/null +++ b/test/subset/data/fonts/STIXTwoMath-Regular.ttf Binary files differ
diff --git a/test/subset/data/tests/math.tests b/test/subset/data/tests/math.tests new file mode 100644 index 0000000..91a2405 --- /dev/null +++ b/test/subset/data/tests/math.tests
@@ -0,0 +1,12 @@ +FONTS: +STIXTwoMath-Regular.ttf + +PROFILES: +default.txt +retain-gids.txt +glyph-names.txt +notdef-outline.txt + +SUBSETS: +U+2f,U+7c,U+305 +*
diff --git a/test/subset/meson.build b/test/subset/meson.build index 083ab09..b1dd65f 100644 --- a/test/subset/meson.build +++ b/test/subset/meson.build
@@ -32,6 +32,7 @@ 'cmap14', 'sbix', 'colr', + 'math', # TODO: re-enable once colrv1 subsetting is stabilized. # 'colrv1.notoemoji', # 'colrv1',