[subset] subset MATH table

This commit is contained in:
Qunxin Liu 2021-09-20 14:42:51 -07:00 committed by Behdad Esfahbod
parent c2cc566c9d
commit ca7b9daef0
18 changed files with 342 additions and 1 deletions

View File

@ -346,6 +346,43 @@ struct
} }
HB_FUNCOBJ (subset_record_array); 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 * OpenType Layout Common Table Formats

View File

@ -41,6 +41,16 @@ struct MathValueRecord
hb_position_t get_y_value (hb_font_t *font, const void *base) const 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); } { 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 bool sanitize (hb_sanitize_context_t *c, const void *base) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -59,6 +69,29 @@ struct MathValueRecord
struct MathConstants 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 bool sanitize_math_value_records (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -165,6 +198,28 @@ struct MathConstants
struct MathItalicsCorrectionInfo 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -196,6 +251,28 @@ struct MathItalicsCorrectionInfo
struct MathTopAccentAttachment 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -229,6 +306,22 @@ struct MathTopAccentAttachment
struct MathKern 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 bool sanitize_math_value_records (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -295,6 +388,19 @@ struct MathKern
struct MathKernInfoRecord 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 bool sanitize (hb_sanitize_context_t *c, const void *base) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -328,6 +434,28 @@ struct MathKernInfoRecord
struct MathKernInfo 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -365,6 +493,30 @@ struct MathKernInfo
struct MathGlyphInfo 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -420,6 +572,16 @@ struct MathGlyphVariantRecord
{ {
friend struct MathGlyphConstruction; 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -453,6 +615,16 @@ struct PartFlags : HBUINT16
struct MathGlyphPartRecord 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -503,6 +675,20 @@ struct MathGlyphPartRecord
struct MathGlyphAssembly 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -555,6 +741,22 @@ struct MathGlyphAssembly
struct MathGlyphConstruction 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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -630,6 +832,60 @@ struct MathVariants
; ;
} }
} }
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 bool sanitize_offsets (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -747,6 +1003,18 @@ struct MATH
} }
} }
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 bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);

View File

@ -38,6 +38,7 @@
#include "hb-ot-color-colrv1-closure.hh" #include "hb-ot-color-colrv1-closure.hh"
#include "hb-ot-var-fvar-table.hh" #include "hb-ot-var-fvar-table.hh"
#include "hb-ot-stat-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; typedef hb_hashmap_t<unsigned, hb_set_t *, (unsigned)-1, nullptr> script_langsys_map;
@ -221,6 +222,17 @@ _cmap_closure (hb_face_t *face,
cmap.fini (); cmap.fini ();
} }
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 static inline void
_remove_invalid_gids (hb_set_t *glyphs, _remove_invalid_gids (hb_set_t *glyphs,
unsigned int num_glyphs) unsigned int num_glyphs)
@ -334,8 +346,12 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
#endif #endif
_remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ()); _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 // 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; hb_set_t glyphset_colrv0;
if (colr.is_valid ()) if (colr.is_valid ())
{ {
@ -468,6 +484,7 @@ hb_subset_plan_create (hb_face_t *face,
plan->_glyphset = hb_set_create (); plan->_glyphset = hb_set_create ();
plan->_glyphset_gsub = hb_set_create (); plan->_glyphset_gsub = hb_set_create ();
plan->_glyphset_mathed = hb_set_create ();
plan->codepoint_to_glyph = hb_map_create (); plan->codepoint_to_glyph = hb_map_create ();
plan->glyph_map = hb_map_create (); plan->glyph_map = hb_map_create ();
plan->reverse_glyph_map = hb_map_create (); plan->reverse_glyph_map = hb_map_create ();
@ -535,6 +552,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
hb_map_destroy (plan->reverse_glyph_map); hb_map_destroy (plan->reverse_glyph_map);
hb_set_destroy (plan->_glyphset); hb_set_destroy (plan->_glyphset);
hb_set_destroy (plan->_glyphset_gsub); hb_set_destroy (plan->_glyphset_gsub);
hb_set_destroy (plan->_glyphset_mathed);
hb_map_destroy (plan->gsub_lookups); hb_map_destroy (plan->gsub_lookups);
hb_map_destroy (plan->gpos_lookups); hb_map_destroy (plan->gpos_lookups);
hb_map_destroy (plan->gsub_features); hb_map_destroy (plan->gsub_features);

View File

@ -77,6 +77,7 @@ struct hb_subset_plan_t
unsigned int _num_output_glyphs; unsigned int _num_output_glyphs;
hb_set_t *_glyphset; hb_set_t *_glyphset;
hb_set_t *_glyphset_gsub; hb_set_t *_glyphset_gsub;
hb_set_t *_glyphset_mathed;
//active lookups we'd like to retain //active lookups we'd like to retain
hb_map_t *gsub_lookups; hb_map_t *gsub_lookups;

View File

@ -52,6 +52,7 @@
#include "hb-ot-layout-gpos-table.hh" #include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-var-gvar-table.hh" #include "hb-ot-var-gvar-table.hh"
#include "hb-ot-var-hvar-table.hh" #include "hb-ot-var-hvar-table.hh"
#include "hb-ot-math-table.hh"
#include "hb-repacker.hh" #include "hb-repacker.hh"
/** /**
@ -305,6 +306,7 @@ _subset_table (hb_subset_plan_t *plan, hb_tag_t tag)
case HB_OT_TAG_CPAL: return _subset<const OT::CPAL> (plan); 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_CBLC: return _subset<const OT::CBLC> (plan);
case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ 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 #ifndef HB_NO_SUBSET_CFF
case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan); case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan);

View File

@ -44,6 +44,7 @@ EXTRA_DIST += \
expected/cbdt \ expected/cbdt \
expected/variable \ expected/variable \
expected/glyph_names \ expected/glyph_names \
expected/math \
fonts \ fonts \
profiles \ profiles \
$(NULL) $(NULL)

View File

@ -36,6 +36,7 @@ TESTS = \
tests/sbix.tests \ tests/sbix.tests \
tests/variable.tests \ tests/variable.tests \
tests/glyph_names.tests \ tests/glyph_names.tests \
tests/math.tests \
$(NULL) $(NULL)
# TODO: re-enable once colrv1 subsetting is stabilized. # TODO: re-enable once colrv1 subsetting is stabilized.

Binary file not shown.

View File

@ -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
*

View File

@ -32,6 +32,7 @@ tests = [
'cmap14', 'cmap14',
'sbix', 'sbix',
'colr', 'colr',
'math',
# TODO: re-enable once colrv1 subsetting is stabilized. # TODO: re-enable once colrv1 subsetting is stabilized.
# 'colrv1.notoemoji', # 'colrv1.notoemoji',
# 'colrv1', # 'colrv1',