diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/harfbuzz/src/hb-ot-hmtx-table.hh | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh new file mode 100644 index 0000000000..96a394ba42 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh @@ -0,0 +1,409 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod, Roderick Sheeter + */ + +#ifndef HB_OT_HMTX_TABLE_HH +#define HB_OT_HMTX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-maxp-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-var-hvar-table.hh" +#include "hb-ot-metrics.hh" + +/* + * hmtx -- Horizontal Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx + * vmtx -- Vertical Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx + */ +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') +#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') + + +HB_INTERNAL bool +_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb); + +HB_INTERNAL unsigned +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + + +namespace OT { + + +struct LongMetric +{ + UFWORD advance; /* Advance width/height. */ + FWORD sb; /* Leading (left/top) side bearing. */ + public: + DEFINE_SIZE_STATIC (4); +}; + + +template <typename T/*Data table type*/, typename H/*Header table type*/, typename V/*Var table type*/> +struct hmtxvmtx +{ + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return_trace (true); + } + + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>>* get_mtx_map (const hb_subset_plan_t *plan) const + { return T::is_horizontal ? plan->hmtx_map : plan->vmtx_map; } + + bool subset_update_header (hb_subset_plan_t *plan, + unsigned int num_hmetrics) const + { + hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (plan->source, H::tableTag); + hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); + hb_blob_destroy (src_blob); + + if (unlikely (!dest_blob)) { + return false; + } + + unsigned int length; + H *table = (H *) hb_blob_get_data (dest_blob, &length); + table->numberOfLongMetrics = num_hmetrics; + + bool result = plan->add_table (H::tableTag, dest_blob); + hb_blob_destroy (dest_blob); + + return result; + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned num_long_metrics) + { + unsigned idx = 0; + for (auto _ : it) + { + if (idx < num_long_metrics) + { + LongMetric lm; + lm.advance = _.first; + lm.sb = _.second; + if (unlikely (!c->embed<LongMetric> (&lm))) return; + } + else + { + FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size); + if (unlikely (!sb)) return; + *sb = _.second; + } + idx++; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + T *table_prime = c->serializer->start_embed <T> (); + if (unlikely (!table_prime)) return_trace (false); + + accelerator_t _mtx (c->plan->source); + unsigned num_long_metrics; + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *mtx_map = get_mtx_map (c->plan); + { + /* Determine num_long_metrics to encode. */ + auto& plan = c->plan; + + num_long_metrics = plan->num_output_glyphs (); + unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx); + while (num_long_metrics > 1 && + last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx)) + { + num_long_metrics--; + } + } + + auto it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map ([c, &_mtx, mtx_map] (unsigned _) + { + if (!mtx_map->has (_)) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (_, &old_gid)) + return hb_pair (0u, 0); + int lsb = 0; + (void) _mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb); + return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); + } + return mtx_map->get (_); + }) + ; + + table_prime->serialize (c->serializer, it, num_long_metrics); + + if (unlikely (c->serializer->in_error ())) + return_trace (false); + + // Amend header num hmetrics + if (unlikely (!subset_update_header (c->plan, num_long_metrics))) + return_trace (false); + + return_trace (true); + } + + struct accelerator_t + { + friend struct hmtxvmtx; + + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag); + var_table = hb_sanitize_context_t ().reference_table<V> (face, T::variationsTag); + + default_advance = T::is_horizontal ? hb_face_get_upem (face) / 2 : hb_face_get_upem (face); + + /* Populate count variables and sort them out as we go */ + + unsigned int len = table.get_length (); + if (len & 1) + len--; + + num_long_metrics = T::is_horizontal ? + face->table.hhea->numberOfLongMetrics : +#ifndef HB_NO_VERTICAL + face->table.vhea->numberOfLongMetrics +#else + 0 +#endif + ; + if (unlikely (num_long_metrics * 4 > len)) + num_long_metrics = len / 4; + len -= num_long_metrics * 4; + + num_bearings = face->table.maxp->get_num_glyphs (); + + if (unlikely (num_bearings < num_long_metrics)) + num_bearings = num_long_metrics; + if (unlikely ((num_bearings - num_long_metrics) * 2 > len)) + num_bearings = num_long_metrics + len / 2; + len -= (num_bearings - num_long_metrics) * 2; + + /* We MUST set num_bearings to zero if num_long_metrics is zero. + * Our get_advance() depends on that. */ + if (unlikely (!num_long_metrics)) + num_bearings = num_long_metrics = 0; + + num_advances = num_bearings + len / 2; + num_glyphs = face->get_num_glyphs (); + if (num_glyphs < num_advances) + num_glyphs = num_advances; + } + ~accelerator_t () + { + table.destroy (); + var_table.destroy (); + } + + bool has_data () const { return (bool) num_bearings; } + + bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, + int *lsb) const + { + if (glyph < num_long_metrics) + { + *lsb = table->longMetricZ[glyph].sb; + return true; + } + + if (unlikely (glyph >= num_bearings)) + return false; + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + *lsb = bearings[glyph - num_long_metrics]; + return true; + } + + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, + hb_codepoint_t glyph, + int *lsb) const + { + if (!font->num_coords) + return get_leading_bearing_without_var_unscaled (glyph, lsb); + +#ifndef HB_NO_VAR + float delta; + if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) && + get_leading_bearing_without_var_unscaled (glyph, lsb)) + { + *lsb += roundf (delta); + return true; + } + + return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx, lsb); +#else + return false; +#endif + } + + unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const + { + /* OpenType case. */ + if (glyph < num_bearings) + return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance; + + /* If num_advances is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, there's a + * well-defined answer. */ + if (unlikely (!num_advances)) + return default_advance; + +#ifdef HB_NO_BEYOND_64K + return 0; +#endif + + if (unlikely (glyph >= num_glyphs)) + return 0; + + /* num_bearings <= glyph < num_glyphs; + * num_bearings <= num_advances */ + + /* TODO Optimize */ + + if (num_bearings == num_advances) + return get_advance_without_var_unscaled (num_bearings - 1); + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics]; + + return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)]; + } + + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + VariationStore::cache_t *store_cache = nullptr) const + { + unsigned int advance = get_advance_without_var_unscaled (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_bearings) || !font->num_coords) + return advance; + + if (var_table.get_length ()) + return advance + roundf (var_table->get_advance_delta_unscaled (glyph, + font->coords, font->num_coords, + store_cache)); // TODO Optimize?! + + return _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return advance; +#endif + } + + protected: + // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs + unsigned num_long_metrics; + unsigned num_bearings; + unsigned num_advances; + unsigned num_glyphs; + + unsigned int default_advance; + + public: + hb_blob_ptr_t<hmtxvmtx> table; + hb_blob_ptr_t<V> var_table; + }; + + /* get advance: when no variations, call get_advance_without_var_unscaled. + * when there're variations, get advance value from mtx_map in subset_plan*/ + unsigned get_new_gid_advance_unscaled (const hb_subset_plan_t *plan, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *mtx_map, + unsigned new_gid, + const accelerator_t &_mtx) const + { + if (mtx_map->is_empty () || + (new_gid == 0 && !mtx_map->has (new_gid))) + { + hb_codepoint_t old_gid = 0; + return plan->old_gid_for_new_gid (new_gid, &old_gid) ? + _mtx.get_advance_without_var_unscaled (old_gid) : 0; + } + else + { return mtx_map->get (new_gid).first; } + } + + protected: + UnsizedArrayOf<LongMetric> + longMetricZ; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ +/*UnsizedArrayOf<FWORD> leadingBearingX;*/ + /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ +/*UnsizedArrayOf<UFWORD>advancesX;*/ + /* TODO Document. */ + public: + DEFINE_SIZE_ARRAY (0, longMetricZ); +}; + +struct hmtx : hmtxvmtx<hmtx, hhea, HVAR> { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; + static constexpr bool is_horizontal = true; +}; +struct vmtx : hmtxvmtx<vmtx, vhea, VVAR> { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; + static constexpr bool is_horizontal = false; +}; + +struct hmtx_accelerator_t : hmtx::accelerator_t { + hmtx_accelerator_t (hb_face_t *face) : hmtx::accelerator_t (face) {} +}; +struct vmtx_accelerator_t : vmtx::accelerator_t { + vmtx_accelerator_t (hb_face_t *face) : vmtx::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HMTX_TABLE_HH */ |