• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 #include "glyf-helpers.hh"
12 #include "Glyph.hh"
13 #include "SubsetGlyph.hh"
14 #include "loca.hh"
15 #include "path-builder.hh"
16 
17 
18 namespace OT {
19 
20 
21 /*
22  * glyf -- TrueType Glyph Data
23  * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
24  */
25 #define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
26 
27 struct glyf
28 {
29   friend struct glyf_accelerator_t;
30 
31   static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
32 
sanitizeOT::glyf33   bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
34   {
35     TRACE_SANITIZE (this);
36     /* Runtime checks as eager sanitizing each glyph is costy */
37     return_trace (true);
38   }
39 
40   /* requires source of SubsetGlyph complains the identifier isn't declared */
41   template <typename Iterator>
serializeOT::glyf42   bool serialize (hb_serialize_context_t *c,
43 		  Iterator it,
44                   bool use_short_loca,
45 		  const hb_subset_plan_t *plan,
46 		  hb_font_t *font)
47   {
48     TRACE_SERIALIZE (this);
49 
50     unsigned init_len = c->length ();
51     for (auto &_ : it)
52       if (unlikely (!_.serialize (c, use_short_loca, plan, font)))
53         return false;
54 
55     /* As a special case when all glyph in the font are empty, add a zero byte
56      * to the table, so that OTS doesn’t reject it, and to make the table work
57      * on Windows as well.
58      * See https://github.com/khaledhosny/ots/issues/52 */
59     if (init_len == c->length ())
60     {
61       HBUINT8 empty_byte;
62       empty_byte = 0;
63       c->copy (empty_byte);
64     }
65     return_trace (true);
66   }
67 
68   /* Byte region(s) per glyph to output
69      unpadded, hints removed if so requested
70      If we fail to process a glyph we produce an empty (0-length) glyph */
subsetOT::glyf71   bool subset (hb_subset_context_t *c) const
72   {
73     TRACE_SUBSET (this);
74 
75     glyf *glyf_prime = c->serializer->start_embed <glyf> ();
76     if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
77 
78     hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
79     _populate_subset_glyphs (c->plan, glyphs);
80 
81     hb_font_t *font = nullptr;
82     if (!c->plan->pinned_at_default)
83     {
84       font = _create_font_for_instancing (c->plan);
85       if (unlikely (!font)) return false;
86     }
87 
88     auto padded_offsets =
89     + hb_iter (glyphs)
90     | hb_map (&glyf_impl::SubsetGlyph::padded_size)
91     ;
92 
93     bool use_short_loca = false;
94     if (likely (!c->plan->force_long_loca))
95     {
96       unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0);
97       use_short_loca = max_offset < 0x1FFFF;
98     }
99 
100     glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan, font);
101     if (!use_short_loca) {
102       padded_offsets =
103           + hb_iter (glyphs)
104           | hb_map (&glyf_impl::SubsetGlyph::length)
105           ;
106     }
107 
108     if (font)
109     {
110       _free_compiled_subset_glyphs (&glyphs);
111       hb_font_destroy (font);
112     }
113 
114     if (unlikely (c->serializer->in_error ())) return_trace (false);
115     return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan,
116 									       padded_offsets,
117 									       use_short_loca)));
118   }
119 
120   void
121   _populate_subset_glyphs (const hb_subset_plan_t   *plan,
122 			   hb_vector_t<glyf_impl::SubsetGlyph> &glyphs /* OUT */) const;
123 
124   hb_font_t *
125   _create_font_for_instancing (const hb_subset_plan_t *plan) const;
126 
_free_compiled_subset_glyphsOT::glyf127   void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> *glyphs) const
128   {
129     for (auto _ : *glyphs)
130       _.free_compiled_bytes ();
131   }
132 
133   protected:
134   UnsizedArrayOf<HBUINT8>
135 		dataZ;	/* Glyphs data. */
136   public:
137   DEFINE_SIZE_MIN (0);	/* In reality, this is UNBOUNDED() type; but since we always
138 			 * check the size externally, allow Null() object of it by
139 			 * defining it _MIN instead. */
140 };
141 
142 struct glyf_accelerator_t
143 {
glyf_accelerator_tOT::glyf_accelerator_t144   glyf_accelerator_t (hb_face_t *face)
145   {
146     short_offset = false;
147     num_glyphs = 0;
148     loca_table = nullptr;
149     glyf_table = nullptr;
150 #ifndef HB_NO_VAR
151     gvar = nullptr;
152 #endif
153     hmtx = nullptr;
154 #ifndef HB_NO_VERTICAL
155     vmtx = nullptr;
156 #endif
157     const OT::head &head = *face->table.head;
158     if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0)
159       /* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
160       return;
161     short_offset = 0 == head.indexToLocFormat;
162 
163     loca_table = face->table.loca.get_blob (); // Needs no destruct!
164     glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
165 #ifndef HB_NO_VAR
166     gvar = face->table.gvar;
167 #endif
168     hmtx = face->table.hmtx;
169 #ifndef HB_NO_VERTICAL
170     vmtx = face->table.vmtx;
171 #endif
172 
173     num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
174     num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
175   }
~glyf_accelerator_tOT::glyf_accelerator_t176   ~glyf_accelerator_t ()
177   {
178     glyf_table.destroy ();
179   }
180 
has_dataOT::glyf_accelerator_t181   bool has_data () const { return num_glyphs; }
182 
183   protected:
184   template<typename T>
get_pointsOT::glyf_accelerator_t185   bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const
186   {
187     if (gid >= num_glyphs) return false;
188 
189     /* Making this allocfree is not that easy
190        https://github.com/harfbuzz/harfbuzz/issues/2095
191        mostly because of gvar handling in VF fonts,
192        perhaps a separate path for non-VF fonts can be considered */
193     contour_point_vector_t all_points;
194 
195     bool phantom_only = !consumer.is_consuming_contour_points ();
196     if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, true, true, phantom_only)))
197       return false;
198 
199     if (consumer.is_consuming_contour_points ())
200     {
201       unsigned count = all_points.length;
202       assert (count >= glyf_impl::PHANTOM_COUNT);
203       count -= glyf_impl::PHANTOM_COUNT;
204       for (unsigned point_index = 0; point_index < count; point_index++)
205 	consumer.consume_point (all_points[point_index]);
206       consumer.points_end ();
207     }
208 
209     /* Where to write phantoms, nullptr if not requested */
210     contour_point_t *phantoms = consumer.get_phantoms_sink ();
211     if (phantoms)
212       for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
213 	phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i];
214 
215     return true;
216   }
217 
218 #ifndef HB_NO_VAR
219   struct points_aggregator_t
220   {
221     hb_font_t *font;
222     hb_glyph_extents_t *extents;
223     contour_point_t *phantoms;
224     bool scaled;
225 
226     struct contour_bounds_t
227     {
contour_bounds_tOT::glyf_accelerator_t::points_aggregator_t::contour_bounds_t228       contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
229 
addOT::glyf_accelerator_t::points_aggregator_t::contour_bounds_t230       void add (const contour_point_t &p)
231       {
232 	min_x = hb_min (min_x, p.x);
233 	min_y = hb_min (min_y, p.y);
234 	max_x = hb_max (max_x, p.x);
235 	max_y = hb_max (max_y, p.y);
236       }
237 
emptyOT::glyf_accelerator_t::points_aggregator_t::contour_bounds_t238       bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
239 
get_extentsOT::glyf_accelerator_t::points_aggregator_t::contour_bounds_t240       void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled)
241       {
242 	if (unlikely (empty ()))
243 	{
244 	  extents->width = 0;
245 	  extents->x_bearing = 0;
246 	  extents->height = 0;
247 	  extents->y_bearing = 0;
248 	  return;
249 	}
250 	if (scaled)
251 	{
252 	  extents->x_bearing = font->em_scalef_x (min_x);
253 	  extents->width = font->em_scalef_x (max_x) - extents->x_bearing;
254 	  extents->y_bearing = font->em_scalef_y (max_y);
255 	  extents->height = font->em_scalef_y (min_y) - extents->y_bearing;
256 	}
257 	else
258 	{
259 	  extents->x_bearing = roundf (min_x);
260 	  extents->width = roundf (max_x - extents->x_bearing);
261 	  extents->y_bearing = roundf (max_y);
262 	  extents->height = roundf (min_y - extents->y_bearing);
263 	}
264       }
265 
266       protected:
267       float min_x, min_y, max_x, max_y;
268     } bounds;
269 
points_aggregator_tOT::glyf_accelerator_t::points_aggregator_t270     points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_)
271     {
272       font = font_;
273       extents = extents_;
274       phantoms = phantoms_;
275       scaled = scaled_;
276       if (extents) bounds = contour_bounds_t ();
277     }
278 
consume_pointOT::glyf_accelerator_t::points_aggregator_t279     void consume_point (const contour_point_t &point) { bounds.add (point); }
points_endOT::glyf_accelerator_t::points_aggregator_t280     void points_end () { bounds.get_extents (font, extents, scaled); }
281 
is_consuming_contour_pointsOT::glyf_accelerator_t::points_aggregator_t282     bool is_consuming_contour_points () { return extents; }
get_phantoms_sinkOT::glyf_accelerator_t::points_aggregator_t283     contour_point_t *get_phantoms_sink () { return phantoms; }
284   };
285 
286   public:
287   unsigned
get_advance_with_var_unscaledOT::glyf_accelerator_t288   get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
289   {
290     if (unlikely (gid >= num_glyphs)) return 0;
291 
292     bool success = false;
293 
294     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
295     if (font->num_coords)
296       success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false));
297 
298     if (unlikely (!success))
299       return
300 #ifndef HB_NO_VERTICAL
301 	is_vertical ? vmtx->get_advance_without_var_unscaled (gid) :
302 #endif
303 	hmtx->get_advance_without_var_unscaled (gid);
304 
305     float result = is_vertical
306 		 ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
307 		 : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x;
308     return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
309   }
310 
get_leading_bearing_with_var_unscaledOT::glyf_accelerator_t311   bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const
312   {
313     if (unlikely (gid >= num_glyphs)) return false;
314 
315     hb_glyph_extents_t extents;
316 
317     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
318     if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false))))
319       return false;
320 
321     *lsb = is_vertical
322 	 ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing
323 	 : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x);
324     return true;
325   }
326 #endif
327 
328   public:
get_extentsOT::glyf_accelerator_t329   bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
330   {
331     if (unlikely (gid >= num_glyphs)) return false;
332 
333 #ifndef HB_NO_VAR
334     if (font->num_coords)
335       return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true));
336 #endif
337     return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
338   }
339 
340   const glyf_impl::Glyph
glyph_for_gidOT::glyf_accelerator_t341   glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
342   {
343     if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph ();
344 
345     unsigned int start_offset, end_offset;
346 
347     if (short_offset)
348     {
349       const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
350       start_offset = 2 * offsets[gid];
351       end_offset   = 2 * offsets[gid + 1];
352     }
353     else
354     {
355       const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
356       start_offset = offsets[gid];
357       end_offset   = offsets[gid + 1];
358     }
359 
360     if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
361       return glyf_impl::Glyph ();
362 
363     glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
364 			     end_offset - start_offset), gid);
365     return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph;
366   }
367 
368   bool
get_pathOT::glyf_accelerator_t369   get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
370   { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); }
371 
372 #ifndef HB_NO_VAR
373   const gvar_accelerator_t *gvar;
374 #endif
375   const hmtx_accelerator_t *hmtx;
376 #ifndef HB_NO_VERTICAL
377   const vmtx_accelerator_t *vmtx;
378 #endif
379 
380   private:
381   bool short_offset;
382   unsigned int num_glyphs;
383   hb_blob_ptr_t<loca> loca_table;
384   hb_blob_ptr_t<glyf> glyf_table;
385 };
386 
387 
388 inline void
_populate_subset_glyphs(const hb_subset_plan_t * plan,hb_vector_t<glyf_impl::SubsetGlyph> & glyphs) const389 glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
390 			       hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
391 {
392   OT::glyf_accelerator_t glyf (plan->source);
393   unsigned num_glyphs = plan->num_output_glyphs ();
394   if (!glyphs.resize (num_glyphs)) return;
395 
396   for (auto p : plan->glyph_map->iter ())
397   {
398     unsigned new_gid = p.second;
399     glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid];
400     subset_glyph.old_gid = p.first;
401 
402     if (unlikely (new_gid == 0 &&
403                   !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
404                   plan->pinned_at_default)
405       subset_glyph.source_glyph = glyf_impl::Glyph ();
406     else
407     {
408       /* If plan has an accelerator, the preprocessing step already trimmed glyphs.
409        * Don't trim them again! */
410       subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator);
411     }
412 
413     if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
414       subset_glyph.drop_hints_bytes ();
415     else
416       subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
417   }
418 }
419 
420 inline hb_font_t *
_create_font_for_instancing(const hb_subset_plan_t * plan) const421 glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
422 {
423   hb_font_t *font = hb_font_create (plan->source);
424   if (unlikely (font == hb_font_get_empty ())) return nullptr;
425 
426   hb_vector_t<hb_variation_t> vars;
427   if (unlikely (!vars.alloc (plan->user_axes_location->get_population ())))
428     return nullptr;
429 
430   for (auto _ : *plan->user_axes_location)
431   {
432     hb_variation_t var;
433     var.tag = _.first;
434     var.value = _.second;
435     vars.push (var);
436   }
437 
438 #ifndef HB_NO_VAR
439   hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ());
440 #endif
441   return font;
442 }
443 
444 
445 } /* namespace OT */
446 
447 
448 #endif /* OT_GLYF_GLYF_HH */
449