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