comparison mupdf-source/thirdparty/harfbuzz/src/OT/glyf/Glyph.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_GLYPH_HH
2 #define OT_GLYF_GLYPH_HH
3
4
5 #include "../../hb-open-type.hh"
6
7 #include "GlyphHeader.hh"
8 #include "SimpleGlyph.hh"
9 #include "CompositeGlyph.hh"
10 #include "VarCompositeGlyph.hh"
11 #include "coord-setter.hh"
12
13
14 namespace OT {
15
16 struct glyf_accelerator_t;
17
18 namespace glyf_impl {
19
20
21 #ifndef HB_GLYF_MAX_POINTS
22 #define HB_GLYF_MAX_POINTS 10000
23 #endif
24
25
26 enum phantom_point_index_t
27 {
28 PHANTOM_LEFT = 0,
29 PHANTOM_RIGHT = 1,
30 PHANTOM_TOP = 2,
31 PHANTOM_BOTTOM = 3,
32 PHANTOM_COUNT = 4
33 };
34
35 struct Glyph
36 {
37 enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE, VAR_COMPOSITE };
38
39 public:
40 composite_iter_t get_composite_iterator () const
41 {
42 if (type != COMPOSITE) return composite_iter_t ();
43 return CompositeGlyph (*header, bytes).iter ();
44 }
45 var_composite_iter_t get_var_composite_iterator () const
46 {
47 if (type != VAR_COMPOSITE) return var_composite_iter_t ();
48 return VarCompositeGlyph (*header, bytes).iter ();
49 }
50
51 const hb_bytes_t trim_padding () const
52 {
53 switch (type) {
54 case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
55 case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding ();
56 default: return bytes;
57 }
58 }
59
60 void drop_hints ()
61 {
62 switch (type) {
63 case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
64 case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return;
65 default: return;
66 }
67 }
68
69 void set_overlaps_flag ()
70 {
71 switch (type) {
72 case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
73 case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
74 default: return;
75 }
76 }
77
78 void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
79 {
80 switch (type) {
81 case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
82 case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
83 default: return;
84 }
85 }
86
87 void update_mtx (const hb_subset_plan_t *plan,
88 int xMin, int yMax,
89 const contour_point_vector_t &all_points) const
90 {
91 hb_codepoint_t new_gid = 0;
92 if (!plan->new_gid_for_old_gid (gid, &new_gid))
93 return;
94
95 unsigned len = all_points.length;
96 float leftSideX = all_points[len - 4].x;
97 float rightSideX = all_points[len - 3].x;
98 float topSideY = all_points[len - 2].y;
99 float bottomSideY = all_points[len - 1].y;
100
101 int hori_aw = roundf (rightSideX - leftSideX);
102 if (hori_aw < 0) hori_aw = 0;
103 int lsb = roundf (xMin - leftSideX);
104 plan->hmtx_map->set (new_gid, hb_pair (hori_aw, lsb));
105
106 int vert_aw = roundf (topSideY - bottomSideY);
107 if (vert_aw < 0) vert_aw = 0;
108 int tsb = roundf (topSideY - yMax);
109 plan->vmtx_map->set (new_gid, hb_pair (vert_aw, tsb));
110 }
111
112 bool compile_header_bytes (const hb_subset_plan_t *plan,
113 const contour_point_vector_t &all_points,
114 hb_bytes_t &dest_bytes /* OUT */) const
115 {
116 GlyphHeader *glyph_header = nullptr;
117 if (type != EMPTY && all_points.length > 4)
118 {
119 glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
120 if (unlikely (!glyph_header)) return false;
121 }
122
123 float xMin = 0, xMax = 0;
124 float yMin = 0, yMax = 0;
125 if (all_points.length > 4)
126 {
127 xMin = xMax = all_points[0].x;
128 yMin = yMax = all_points[0].y;
129 }
130
131 for (unsigned i = 1; i < all_points.length - 4; i++)
132 {
133 float x = all_points[i].x;
134 float y = all_points[i].y;
135 xMin = hb_min (xMin, x);
136 xMax = hb_max (xMax, x);
137 yMin = hb_min (yMin, y);
138 yMax = hb_max (yMax, y);
139 }
140
141 update_mtx (plan, roundf (xMin), roundf (yMax), all_points);
142
143 /*for empty glyphs: all_points only include phantom points.
144 *just update metrics and then return */
145 if (!glyph_header)
146 return true;
147
148 glyph_header->numberOfContours = header->numberOfContours;
149 glyph_header->xMin = roundf (xMin);
150 glyph_header->yMin = roundf (yMin);
151 glyph_header->xMax = roundf (xMax);
152 glyph_header->yMax = roundf (yMax);
153
154 dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size);
155 return true;
156 }
157
158 bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
159 hb_font_t *font,
160 const glyf_accelerator_t &glyf,
161 hb_bytes_t &dest_start, /* IN/OUT */
162 hb_bytes_t &dest_end /* OUT */)
163 {
164 contour_point_vector_t all_points, deltas;
165 if (!get_points (font, glyf, all_points, &deltas, false, false))
166 return false;
167
168 // .notdef, set type to empty so we only update metrics and don't compile bytes for
169 // it
170 if (gid == 0 &&
171 !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
172 type = EMPTY;
173
174 switch (type) {
175 case COMPOSITE:
176 if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
177 deltas,
178 dest_end))
179 return false;
180 break;
181 case SIMPLE:
182 if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points,
183 plan->flags & HB_SUBSET_FLAGS_NO_HINTING,
184 dest_end))
185 return false;
186 break;
187 default:
188 /* set empty bytes for empty glyph
189 * do not use source glyph's pointers */
190 dest_start = hb_bytes_t ();
191 dest_end = hb_bytes_t ();
192 break;
193 }
194
195 if (!compile_header_bytes (plan, all_points, dest_start))
196 {
197 dest_end.fini ();
198 return false;
199 }
200 return true;
201 }
202
203
204 /* Note: Recursively calls itself.
205 * all_points includes phantom points
206 */
207 template <typename accelerator_t>
208 bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
209 contour_point_vector_t &all_points /* OUT */,
210 contour_point_vector_t *deltas = nullptr, /* OUT */
211 bool shift_points_hori = true,
212 bool use_my_metrics = true,
213 bool phantom_only = false,
214 hb_array_t<int> coords = hb_array_t<int> (),
215 unsigned int depth = 0) const
216 {
217 if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
218
219 if (!coords)
220 coords = hb_array (font->coords, font->num_coords);
221
222 contour_point_vector_t stack_points;
223 bool inplace = type == SIMPLE && all_points.length == 0;
224 /* Load into all_points if it's empty, as an optimization. */
225 contour_point_vector_t &points = inplace ? all_points : stack_points;
226
227 switch (type) {
228 case SIMPLE:
229 if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
230 return false;
231 break;
232 case COMPOSITE:
233 {
234 /* pseudo component points for each component in composite glyph */
235 unsigned num_points = hb_len (CompositeGlyph (*header, bytes).iter ());
236 if (unlikely (!points.resize (num_points))) return false;
237 break;
238 }
239 #ifndef HB_NO_VAR_COMPOSITES
240 case VAR_COMPOSITE:
241 {
242 for (auto &item : get_var_composite_iterator ())
243 if (unlikely (!item.get_points (points))) return false;
244 }
245 #endif
246 default:
247 break;
248 }
249
250 /* Init phantom points */
251 if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
252 hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
253 {
254 int lsb = 0;
255 int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ?
256 (int) header->xMin - lsb : 0;
257 HB_UNUSED int tsb = 0;
258 int v_orig = (int) header->yMax +
259 #ifndef HB_NO_VERTICAL
260 ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb)
261 #else
262 0
263 #endif
264 ;
265 unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid);
266 unsigned v_adv =
267 #ifndef HB_NO_VERTICAL
268 glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
269 #else
270 - font->face->get_upem ()
271 #endif
272 ;
273 phantoms[PHANTOM_LEFT].x = h_delta;
274 phantoms[PHANTOM_RIGHT].x = h_adv + h_delta;
275 phantoms[PHANTOM_TOP].y = v_orig;
276 phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
277 }
278
279 if (deltas != nullptr && depth == 0 && type == COMPOSITE)
280 {
281 if (unlikely (!deltas->resize (points.length))) return false;
282 deltas->copy_vector (points);
283 }
284
285 #ifndef HB_NO_VAR
286 glyf_accelerator.gvar->apply_deltas_to_points (gid,
287 coords,
288 points.as_array ());
289 #endif
290
291 // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
292 // with child glyphs' points
293 if (deltas != nullptr && depth == 0 && type == COMPOSITE)
294 {
295 for (unsigned i = 0 ; i < points.length; i++)
296 {
297 deltas->arrayZ[i].x = points.arrayZ[i].x - deltas->arrayZ[i].x;
298 deltas->arrayZ[i].y = points.arrayZ[i].y - deltas->arrayZ[i].y;
299 }
300 }
301
302 switch (type) {
303 case SIMPLE:
304 if (!inplace)
305 all_points.extend (points.as_array ());
306 break;
307 case COMPOSITE:
308 {
309 contour_point_vector_t comp_points;
310 unsigned int comp_index = 0;
311 for (auto &item : get_composite_iterator ())
312 {
313 comp_points.reset ();
314
315 if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
316 .get_points (font,
317 glyf_accelerator,
318 comp_points,
319 deltas,
320 shift_points_hori,
321 use_my_metrics,
322 phantom_only,
323 coords,
324 depth + 1)))
325 return false;
326
327 /* Copy phantom points from component if USE_MY_METRICS flag set */
328 if (use_my_metrics && item.is_use_my_metrics ())
329 for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
330 phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
331
332 /* Apply component transformation & translation */
333 item.transform_points (comp_points);
334
335 /* Apply translation from gvar */
336 comp_points.translate (points[comp_index]);
337
338 if (item.is_anchored ())
339 {
340 unsigned int p1, p2;
341 item.get_anchor_points (p1, p2);
342 if (likely (p1 < all_points.length && p2 < comp_points.length))
343 {
344 contour_point_t delta;
345 delta.init (all_points[p1].x - comp_points[p2].x,
346 all_points[p1].y - comp_points[p2].y);
347
348 comp_points.translate (delta);
349 }
350 }
351
352 all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT));
353
354 if (all_points.length > HB_GLYF_MAX_POINTS)
355 return false;
356
357 comp_index++;
358 }
359
360 all_points.extend (phantoms);
361 } break;
362 #ifndef HB_NO_VAR_COMPOSITES
363 case VAR_COMPOSITE:
364 {
365 contour_point_vector_t comp_points;
366 hb_array_t<contour_point_t> points_left = points.as_array ();
367 for (auto &item : get_var_composite_iterator ())
368 {
369 hb_array_t<contour_point_t> record_points = points_left.sub_array (0, item.get_num_points ());
370
371 comp_points.reset ();
372
373 coord_setter_t coord_setter (coords);
374 item.set_variations (coord_setter, record_points);
375
376 if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
377 .get_points (font,
378 glyf_accelerator,
379 comp_points,
380 deltas,
381 shift_points_hori,
382 use_my_metrics,
383 phantom_only,
384 coord_setter.get_coords (),
385 depth + 1)))
386 return false;
387
388 /* Apply component transformation */
389 item.transform_points (record_points, comp_points);
390
391 /* Copy phantom points from component if USE_MY_METRICS flag set */
392 if (use_my_metrics && item.is_use_my_metrics ())
393 for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
394 phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
395
396 all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT));
397
398 if (all_points.length > HB_GLYF_MAX_POINTS)
399 return false;
400
401 points_left += item.get_num_points ();
402 }
403 all_points.extend (phantoms);
404 } break;
405 #endif
406 default:
407 all_points.extend (phantoms);
408 break;
409 }
410
411 if (depth == 0 && shift_points_hori) /* Apply at top level */
412 {
413 /* Undocumented rasterizer behavior:
414 * Shift points horizontally by the updated left side bearing
415 */
416 contour_point_t delta;
417 delta.init (-phantoms[PHANTOM_LEFT].x, 0.f);
418 if (delta.x) all_points.translate (delta);
419 }
420
421 return !all_points.in_error ();
422 }
423
424 bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator,
425 hb_glyph_extents_t *extents) const
426 {
427 if (type == EMPTY) return true; /* Empty glyph; zero extents. */
428 return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents);
429 }
430
431 hb_bytes_t get_bytes () const { return bytes; }
432
433 Glyph () : bytes (),
434 header (bytes.as<GlyphHeader> ()),
435 gid (-1),
436 type(EMPTY)
437 {}
438
439 Glyph (hb_bytes_t bytes_,
440 hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_),
441 header (bytes.as<GlyphHeader> ()),
442 gid (gid_)
443 {
444 int num_contours = header->numberOfContours;
445 if (unlikely (num_contours == 0)) type = EMPTY;
446 else if (num_contours > 0) type = SIMPLE;
447 else if (num_contours == -2) type = VAR_COMPOSITE;
448 else type = COMPOSITE; /* negative numbers */
449 }
450
451 protected:
452 hb_bytes_t bytes;
453 const GlyphHeader *header;
454 hb_codepoint_t gid;
455 unsigned type;
456 };
457
458
459 } /* namespace glyf_impl */
460 } /* namespace OT */
461
462
463 #endif /* OT_GLYF_GLYPH_HH */