tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

glyf.hh (17527B)


      1 #ifndef OT_GLYF_GLYF_HH
      2 #define OT_GLYF_GLYF_HH
      3 
      4 
      5 #include "../../hb-open-type.hh"
      6 #include "../../hb-ot-head-table.hh"
      7 #include "../../hb-ot-hmtx-table.hh"
      8 #include "../../hb-ot-var-gvar-table.hh"
      9 #include "../../hb-draw.hh"
     10 #include "../../hb-paint.hh"
     11 
     12 #include "glyf-helpers.hh"
     13 #include "Glyph.hh"
     14 #include "SubsetGlyph.hh"
     15 #include "loca.hh"
     16 #include "path-builder.hh"
     17 
     18 
     19 namespace OT {
     20 
     21 
     22 /*
     23 * glyf -- TrueType Glyph Data
     24 * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
     25 */
     26 #define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
     27 
     28 struct glyf
     29 {
     30  friend struct glyf_accelerator_t;
     31 
     32  static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
     33 
     34  static bool has_valid_glyf_format(const hb_face_t* face)
     35  {
     36    const OT::head &head = *face->table.head;
     37    return head.indexToLocFormat <= 1 && head.glyphDataFormat <= 1;
     38  }
     39 
     40  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
     41  {
     42    TRACE_SANITIZE (this);
     43    /* Runtime checks as eager sanitizing each glyph is costy */
     44    return_trace (true);
     45  }
     46 
     47  /* requires source of SubsetGlyph complains the identifier isn't declared */
     48  template <typename Iterator>
     49  bool serialize (hb_serialize_context_t *c,
     50 	  Iterator it,
     51                  bool use_short_loca,
     52 	  const hb_subset_plan_t *plan)
     53  {
     54    TRACE_SERIALIZE (this);
     55 
     56    unsigned init_len = c->length ();
     57    for (auto &_ : it)
     58      if (unlikely (!_.serialize (c, use_short_loca, plan)))
     59        return false;
     60 
     61    /* As a special case when all glyph in the font are empty, add a zero byte
     62     * to the table, so that OTS doesn’t reject it, and to make the table work
     63     * on Windows as well.
     64     * See https://github.com/khaledhosny/ots/issues/52 */
     65    if (init_len == c->length ())
     66    {
     67      HBUINT8 empty_byte;
     68      empty_byte = 0;
     69      c->copy (empty_byte);
     70    }
     71    return_trace (true);
     72  }
     73 
     74  /* Byte region(s) per glyph to output
     75     unpadded, hints removed if so requested
     76     If we fail to process a glyph we produce an empty (0-length) glyph */
     77  bool subset (hb_subset_context_t *c) const
     78  {
     79    TRACE_SUBSET (this);
     80 
     81    if (!has_valid_glyf_format (c->plan->source)) {
     82      // glyf format is unknown don't attempt to subset it.
     83      DEBUG_MSG (SUBSET, nullptr,
     84                 "unkown glyf format, dropping from subset.");
     85      return_trace (false);
     86    }
     87 
     88    hb_font_t *font = nullptr;
     89    if (c->plan->normalized_coords)
     90    {
     91      font = _create_font_for_instancing (c->plan);
     92      if (unlikely (!font))
     93 return_trace (false);
     94    }
     95 
     96    hb_vector_t<unsigned> padded_offsets;
     97    if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
     98      return_trace (false);
     99 
    100    hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
    101    if (!_populate_subset_glyphs (c->plan, font, glyphs))
    102    {
    103      hb_font_destroy (font);
    104      return_trace (false);
    105    }
    106 
    107    if (font)
    108      hb_font_destroy (font);
    109 
    110    unsigned max_offset = 0;
    111    for (auto &g : glyphs)
    112    {
    113      unsigned size = g.padded_size ();
    114      padded_offsets.push (size);
    115      max_offset += size;
    116    }
    117 
    118    bool use_short_loca = false;
    119    if (likely (!c->plan->force_long_loca))
    120      use_short_loca = max_offset < 0x1FFFF;
    121 
    122    if (!use_short_loca)
    123    {
    124      padded_offsets.resize (0);
    125      for (auto &g : glyphs)
    126 padded_offsets.push (g.length ());
    127    }
    128 
    129    auto *glyf_prime = c->serializer->start_embed <glyf> ();
    130    bool result = glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
    131    if (c->plan->normalized_coords && !c->plan->pinned_at_default)
    132      _free_compiled_subset_glyphs (glyphs);
    133 
    134    if (unlikely (!c->serializer->check_success (glyf_impl::_add_loca_and_head (c,
    135 					 padded_offsets.iter (),
    136 					 use_short_loca))))
    137      return_trace (false);
    138 
    139    return result;
    140  }
    141 
    142  bool
    143  _populate_subset_glyphs (const hb_subset_plan_t   *plan,
    144 		   hb_font_t                *font,
    145 		   hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const;
    146 
    147  hb_font_t *
    148  _create_font_for_instancing (const hb_subset_plan_t *plan) const;
    149 
    150  void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> &glyphs) const
    151  {
    152    for (auto &g : glyphs)
    153      g.free_compiled_bytes ();
    154  }
    155 
    156  protected:
    157  UnsizedArrayOf<HBUINT8>
    158 	dataZ;	/* Glyphs data. */
    159  public:
    160  DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
    161 		 * check the size externally, allow Null() object of it by
    162 		 * defining it _MIN instead. */
    163 };
    164 
    165 struct glyf_accelerator_t
    166 {
    167  glyf_accelerator_t (hb_face_t *face)
    168  {
    169    short_offset = false;
    170    num_glyphs = 0;
    171    loca_table = nullptr;
    172    glyf_table = nullptr;
    173 #ifndef HB_NO_VAR
    174    gvar = nullptr;
    175 #ifndef HB_NO_BEYOND_64K
    176    GVAR = nullptr;
    177 #endif
    178 #endif
    179    hmtx = nullptr;
    180 #ifndef HB_NO_VERTICAL
    181    vmtx = nullptr;
    182 #endif
    183    const OT::head &head = *face->table.head;
    184    if (!glyf::has_valid_glyf_format (face))
    185      /* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
    186      return;
    187    short_offset = 0 == head.indexToLocFormat;
    188 
    189    loca_table = face->table.loca.get_blob (); // Needs no destruct!
    190    glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
    191 #ifndef HB_NO_VAR
    192    gvar = face->table.gvar;
    193 #ifndef HB_NO_BEYOND_64K
    194    GVAR = face->table.GVAR;
    195 #endif
    196 #endif
    197    hmtx = face->table.hmtx;
    198 #ifndef HB_NO_VERTICAL
    199    vmtx = face->table.vmtx;
    200 #endif
    201 
    202    num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
    203    num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
    204  }
    205  ~glyf_accelerator_t ()
    206  {
    207    auto *scratch = cached_scratch.get_relaxed ();
    208    if (scratch)
    209    {
    210      scratch->~hb_glyf_scratch_t ();
    211      hb_free (scratch);
    212    }
    213 
    214    glyf_table.destroy ();
    215  }
    216 
    217  bool has_data () const { return num_glyphs; }
    218 
    219  protected:
    220  template<typename T>
    221  bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
    222 	   hb_array_t<const int> coords,
    223 	   hb_glyf_scratch_t &scratch,
    224 	   hb_scalar_cache_t *gvar_cache = nullptr) const
    225  {
    226    if (gid >= num_glyphs) return false;
    227 
    228    auto &all_points = scratch.all_points;
    229    all_points.resize (0);
    230 
    231    bool phantom_only = !consumer.is_consuming_contour_points ();
    232    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords, gvar_cache)))
    233      return false;
    234 
    235    unsigned count = all_points.length;
    236    assert (count >= glyf_impl::PHANTOM_COUNT);
    237    count -= glyf_impl::PHANTOM_COUNT;
    238 
    239    if (consumer.is_consuming_contour_points ())
    240    {
    241      auto *points = all_points.arrayZ;
    242 
    243      if (false)
    244      {
    245 /* Our path-builder was designed to work with this simple loop.
    246  * But FreeType and CoreText do it differently, so we match those
    247  * with the other, more complicated, code branch below. */
    248 for (unsigned i = 0; i < count; i++)
    249 {
    250   consumer.consume_point (points[i]);
    251   if (points[i].is_end_point)
    252     consumer.contour_end ();
    253 }
    254      }
    255      else
    256      {
    257 for (unsigned i = 0; i < count; i++)
    258 {
    259   // Start of a contour.
    260   if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
    261   {
    262     // First point is on-curve. Draw the contour.
    263     for (; i < count; i++)
    264     {
    265       consumer.consume_point (points[i]);
    266       if (points[i].is_end_point)
    267       {
    268 	consumer.contour_end ();
    269 	break;
    270       }
    271     }
    272   }
    273   else
    274   {
    275     unsigned start = i;
    276 
    277     // Find end of the contour.
    278     for (; i < count; i++)
    279       if (points[i].is_end_point)
    280 	break;
    281 
    282     unsigned end = i;
    283 
    284     // Enough to start from the end. Our path-builder takes care of the rest.
    285     if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
    286       consumer.consume_point (points[end]);
    287 
    288     for (i = start; i < end; i++)
    289       consumer.consume_point (points[i]);
    290 
    291     consumer.contour_end ();
    292   }
    293 }
    294      }
    295 
    296      consumer.points_end ();
    297    }
    298 
    299    /* Where to write phantoms, nullptr if not requested */
    300    contour_point_t *phantoms = consumer.get_phantoms_sink ();
    301    if (phantoms)
    302      for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
    303 phantoms[i] = all_points.arrayZ[count + i];
    304 
    305    return true;
    306  }
    307 
    308  public:
    309 
    310 #ifndef HB_NO_VAR
    311  struct points_aggregator_t
    312  {
    313    hb_font_t *font;
    314    hb_glyph_extents_t *extents;
    315    contour_point_t *phantoms;
    316    bool scaled;
    317 
    318    struct contour_bounds_t
    319    {
    320      contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
    321 
    322      void add (const contour_point_t &p)
    323      {
    324 min_x = hb_min (min_x, p.x);
    325 min_y = hb_min (min_y, p.y);
    326 max_x = hb_max (max_x, p.x);
    327 max_y = hb_max (max_y, p.y);
    328      }
    329 
    330      bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
    331 
    332      void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled)
    333      {
    334 if (unlikely (empty ()))
    335 {
    336   extents->width = 0;
    337   extents->x_bearing = 0;
    338   extents->height = 0;
    339   extents->y_bearing = 0;
    340   return;
    341 }
    342 {
    343   extents->x_bearing = roundf (min_x);
    344   extents->width = roundf (max_x - extents->x_bearing);
    345   extents->y_bearing = roundf (max_y);
    346   extents->height = roundf (min_y - extents->y_bearing);
    347 
    348   if (scaled)
    349     font->scale_glyph_extents (extents);
    350 }
    351      }
    352 
    353      protected:
    354      float min_x, min_y, max_x, max_y;
    355    } bounds;
    356 
    357    points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_)
    358    {
    359      font = font_;
    360      extents = extents_;
    361      phantoms = phantoms_;
    362      scaled = scaled_;
    363      if (extents) bounds = contour_bounds_t ();
    364    }
    365 
    366    HB_ALWAYS_INLINE
    367    void consume_point (const contour_point_t &point) { bounds.add (point); }
    368    void contour_end () {}
    369    void points_end () { bounds.get_extents (font, extents, scaled); }
    370 
    371    bool is_consuming_contour_points () { return extents; }
    372    contour_point_t *get_phantoms_sink () { return phantoms; }
    373  };
    374 
    375 #ifndef HB_NO_VAR
    376  unsigned
    377  get_advance_with_var_unscaled (hb_codepoint_t gid,
    378 			 hb_font_t *font,
    379 			 bool is_vertical,
    380 			  hb_glyf_scratch_t &scratch,
    381 			 hb_scalar_cache_t *gvar_cache = nullptr) const
    382  {
    383    if (unlikely (gid >= num_glyphs)) return 0;
    384 
    385    bool success = false;
    386 
    387    contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
    388    success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
    389 		  hb_array (font->coords,
    390 			    font->has_nonzero_coords ? font->num_coords : 0),
    391 		  scratch, gvar_cache);
    392    if (unlikely (!success))
    393    {
    394      unsigned upem = font->face->get_upem ();
    395      return is_vertical ? upem : upem / 2;
    396    }
    397 
    398    float result = is_vertical
    399 	 ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
    400 	 : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x;
    401    return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
    402  }
    403 
    404  float
    405  get_v_origin_with_var_unscaled (hb_codepoint_t gid,
    406 			  hb_font_t *font,
    407 			  hb_glyf_scratch_t &scratch,
    408 			  hb_scalar_cache_t *gvar_cache = nullptr) const
    409  {
    410    if (unlikely (gid >= num_glyphs)) return 0;
    411 
    412    bool success = false;
    413 
    414    contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
    415    success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
    416 		  hb_array (font->coords,
    417 			    font->has_nonzero_coords ? font->num_coords : 0),
    418 		  scratch, gvar_cache);
    419    if (unlikely (!success))
    420    {
    421      return font->face->get_upem ();
    422    }
    423 
    424    return phantoms[glyf_impl::PHANTOM_TOP].y;
    425  }
    426 #endif
    427 #endif
    428 
    429  public:
    430 
    431  bool get_extents (hb_font_t *font,
    432 	    hb_codepoint_t gid,
    433 	    hb_glyph_extents_t *extents) const
    434  { return get_extents_at (font, gid, extents, hb_array (font->coords,
    435 						 font->has_nonzero_coords ? font->num_coords : 0)); }
    436 
    437  bool get_extents_at (hb_font_t *font,
    438 	       hb_codepoint_t gid,
    439 	       hb_glyph_extents_t *extents,
    440 	       hb_array_t<const int> coords) const
    441  {
    442    if (unlikely (gid >= num_glyphs)) return false;
    443 
    444 #ifndef HB_NO_VAR
    445    if (coords)
    446    {
    447      hb_glyf_scratch_t *scratch = acquire_scratch ();
    448      if (unlikely (!scratch)) return false;
    449      bool ret = get_points (font,
    450 		     gid,
    451 		     points_aggregator_t (font, extents, nullptr, true),
    452 		     coords,
    453 		     *scratch);
    454      release_scratch (scratch);
    455      return ret;
    456    }
    457 #endif
    458    return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
    459  }
    460 
    461  const glyf_impl::Glyph
    462  glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
    463  {
    464    if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph ();
    465 
    466    unsigned int start_offset, end_offset;
    467 
    468    if (short_offset)
    469    {
    470      const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
    471      start_offset = 2 * offsets[gid];
    472      end_offset   = 2 * offsets[gid + 1];
    473    }
    474    else
    475    {
    476      const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
    477      start_offset = offsets[gid];
    478      end_offset   = offsets[gid + 1];
    479    }
    480 
    481    if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
    482      return glyf_impl::Glyph ();
    483 
    484    glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
    485 		     end_offset - start_offset), gid);
    486    return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph;
    487  }
    488 
    489  bool
    490  get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_scalar_cache_t *gvar_cache = nullptr) const
    491  {
    492    if (!has_data ()) return false;
    493 
    494    hb_glyf_scratch_t *scratch = acquire_scratch ();
    495    if (unlikely (!scratch)) return true;
    496 
    497    bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
    498 		   hb_array (font->coords,
    499 			     font->has_nonzero_coords ? font->num_coords : 0),
    500 		   *scratch,
    501 		    gvar_cache);
    502 
    503    release_scratch (scratch);
    504 
    505    return ret;
    506  }
    507 
    508  bool
    509  get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
    510        hb_array_t<const int> coords,
    511        hb_glyf_scratch_t &scratch,
    512        hb_scalar_cache_t *gvar_cache = nullptr) const
    513  {
    514    if (!has_data ()) return false;
    515    return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
    516 	       coords,
    517 	       scratch,
    518 	       gvar_cache);
    519  }
    520 
    521 
    522  hb_glyf_scratch_t *acquire_scratch () const
    523  {
    524    if (!has_data ()) return nullptr;
    525    hb_glyf_scratch_t *scratch = cached_scratch.get_acquire ();
    526    if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
    527    {
    528      scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
    529      if (unlikely (!scratch))
    530 return nullptr;
    531    }
    532    return scratch;
    533  }
    534  void release_scratch (hb_glyf_scratch_t *scratch) const
    535  {
    536    if (!scratch)
    537      return;
    538    if (!cached_scratch.cmpexch (nullptr, scratch))
    539    {
    540      scratch->~hb_glyf_scratch_t ();
    541      hb_free (scratch);
    542    }
    543  }
    544 
    545 #ifndef HB_NO_VAR
    546  const gvar_accelerator_t *gvar;
    547 #ifndef HB_NO_BEYOND_64K
    548  const GVAR_accelerator_t *GVAR;
    549 #endif
    550 #endif
    551  const hmtx_accelerator_t *hmtx;
    552 #ifndef HB_NO_VERTICAL
    553  const vmtx_accelerator_t *vmtx;
    554 #endif
    555 
    556  private:
    557  bool short_offset;
    558  unsigned int num_glyphs;
    559  hb_blob_ptr_t<loca> loca_table;
    560  hb_blob_ptr_t<glyf> glyf_table;
    561  mutable hb_atomic_t<hb_glyf_scratch_t *> cached_scratch;
    562 };
    563 
    564 
    565 inline bool
    566 glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
    567 		       hb_font_t *font,
    568 		       hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
    569 {
    570  OT::glyf_accelerator_t glyf (plan->source);
    571  if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
    572 
    573  for (const auto &pair : plan->new_to_old_gid_list)
    574  {
    575    hb_codepoint_t new_gid = pair.first;
    576    hb_codepoint_t old_gid = pair.second;
    577    glyf_impl::SubsetGlyph *p = glyphs.push ();
    578    glyf_impl::SubsetGlyph& subset_glyph = *p;
    579    subset_glyph.old_gid = old_gid;
    580 
    581    if (unlikely (old_gid == 0 && new_gid == 0 &&
    582                  !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
    583                  !plan->normalized_coords)
    584      subset_glyph.source_glyph = glyf_impl::Glyph ();
    585    else
    586    {
    587      /* If plan has an accelerator, the preprocessing step already trimmed glyphs.
    588       * Don't trim them again! */
    589      subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator);
    590    }
    591 
    592    if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
    593      subset_glyph.drop_hints_bytes ();
    594    else
    595      subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
    596 
    597    if (font)
    598    {
    599      if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf)))
    600      {
    601        // when pinned at default, only bounds are updated, thus no need to free
    602        if (!plan->pinned_at_default)
    603          _free_compiled_subset_glyphs (glyphs);
    604        return false;
    605      }
    606    }
    607  }
    608  return true;
    609 }
    610 
    611 inline hb_font_t *
    612 glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
    613 {
    614  hb_font_t *font = hb_font_create (plan->source);
    615  if (unlikely (font == hb_font_get_empty ())) return nullptr;
    616 
    617  hb_vector_t<hb_variation_t> vars;
    618  if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true)))
    619  {
    620    hb_font_destroy (font);
    621    return nullptr;
    622  }
    623 
    624  for (auto _ : plan->user_axes_location)
    625  {
    626    hb_variation_t var;
    627    var.tag = _.first;
    628    var.value = _.second.middle;
    629    vars.push (var);
    630  }
    631 
    632 #ifndef HB_NO_VAR
    633  hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
    634 #endif
    635  return font;
    636 }
    637 
    638 
    639 } /* namespace OT */
    640 
    641 
    642 #endif /* OT_GLYF_GLYF_HH */