comparison mupdf-source/thirdparty/harfbuzz/src/OT/glyf/glyf.hh @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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
33 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>
42 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 */
71 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
127 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 {
144 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 }
176 ~glyf_accelerator_t ()
177 {
178 glyf_table.destroy ();
179 }
180
181 bool has_data () const { return num_glyphs; }
182
183 protected:
184 template<typename T>
185 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 {
228 contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
229
230 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
238 bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
239
240 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
270 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
279 void consume_point (const contour_point_t &point) { bounds.add (point); }
280 void points_end () { bounds.get_extents (font, extents, scaled); }
281
282 bool is_consuming_contour_points () { return extents; }
283 contour_point_t *get_phantoms_sink () { return phantoms; }
284 };
285
286 public:
287 unsigned
288 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
311 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:
329 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
341 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
369 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
389 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 *
421 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 */