Coverage Report

Created: 2026-11-12 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-vector-draw.cc
Line
Count
Source
1
/*
2
 * Copyright © 2026  Behdad Esfahbod
3
 *
4
 *  This is part of HarfBuzz, a text shaping library.
5
 *
6
 * Permission is hereby granted, without written agreement and without
7
 * license or royalty fees, to use, copy, modify, and distribute this
8
 * software and its documentation for any purpose, provided that the
9
 * above copyright notice and the following two paragraphs appear in
10
 * all copies of this software.
11
 *
12
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16
 * DAMAGE.
17
 *
18
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23
 *
24
 * Author(s): Behdad Esfahbod
25
 */
26
27
#include "hb.hh"
28
29
#include "hb-vector-draw.hh"
30
#include "hb-vector-path.hh"
31
32
#include <math.h>
33
#include <string.h>
34
35
HB_UNUSED static inline bool
36
hb_vector_svg_buffer_contains (const hb_vector_buf_t &buf, const char *needle)
37
0
{
38
0
  unsigned nlen = (unsigned) strlen (needle);
39
0
  if (!nlen || buf.length < nlen)
40
0
    return false;
41
0
42
0
  for (unsigned i = 0; i + nlen <= buf.length; i++)
43
0
    if (buf.arrayZ[i] == needle[0] &&
44
0
        !memcmp (buf.arrayZ + i, needle, nlen))
45
0
      return true;
46
0
  return false;
47
0
}
48
49
/* ---- SVG draw callbacks ---- */
50
51
static void hb_vector_draw_svg_move_to (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, float x, float y, void *)
52
2.36M
{
53
2.36M
  auto *d = (hb_vector_draw_t *) dd;
54
2.36M
  if (unlikely (!d->begin_path_command ())) return;
55
2.20M
  d->path.append_c ('M'); d->append_xy_svg (x, y);
56
2.20M
  d->end_path_command ();
57
2.20M
}
58
59
static void hb_vector_draw_svg_line_to (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, float x, float y, void *)
60
22.7M
{
61
22.7M
  auto *d = (hb_vector_draw_t *) dd;
62
22.7M
  if (unlikely (!d->begin_path_command ())) return;
63
21.7M
  d->path.append_c ('L'); d->append_xy_svg (x, y);
64
21.7M
  d->end_path_command ();
65
21.7M
}
66
67
static void hb_vector_draw_svg_quadratic_to (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, float cx, float cy, float x, float y, void *)
68
25.1M
{
69
25.1M
  auto *d = (hb_vector_draw_t *) dd;
70
25.1M
  if (unlikely (!d->begin_path_command ())) return;
71
25.1M
  d->path.append_c ('Q'); d->append_xy_svg (cx, cy); d->path.append_c (' '); d->append_xy_svg (x, y);
72
25.1M
  d->end_path_command ();
73
25.1M
}
74
75
static void hb_vector_draw_svg_cubic_to (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, float c1x, float c1y, float c2x, float c2y, float x, float y, void *)
76
1.20M
{
77
1.20M
  auto *d = (hb_vector_draw_t *) dd;
78
1.20M
  if (unlikely (!d->begin_path_command ())) return;
79
840k
  d->path.append_c ('C'); d->append_xy_svg (c1x, c1y); d->path.append_c (' '); d->append_xy_svg (c2x, c2y); d->path.append_c (' '); d->append_xy_svg (x, y);
80
840k
  d->end_path_command ();
81
840k
}
82
83
static void hb_vector_draw_svg_close_path (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, void *)
84
2.36M
{
85
2.36M
  auto *d = (hb_vector_draw_t *) dd;
86
2.36M
  if (unlikely (!d->begin_path_command ())) return;
87
2.20M
  d->path.append_c ('Z');
88
2.20M
  d->end_path_command ();
89
2.20M
}
90
91
92
/* ---- PDF draw callbacks ---- */
93
94
static void hb_vector_draw_pdf_move_to (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, float x, float y, void *)
95
1.89M
{
96
1.89M
  auto *d = (hb_vector_draw_t *) dd;
97
1.89M
  if (unlikely (!d->begin_path_command ())) return;
98
1.62M
  d->append_xy_pdf (x, y); d->path.append_str (" m\n");
99
1.62M
  d->end_path_command ();
100
1.62M
}
101
102
static void hb_vector_draw_pdf_line_to (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, float x, float y, void *)
103
17.9M
{
104
17.9M
  auto *d = (hb_vector_draw_t *) dd;
105
17.9M
  if (unlikely (!d->begin_path_command ())) return;
106
16.7M
  d->append_xy_pdf (x, y); d->path.append_str (" l\n");
107
16.7M
  d->end_path_command ();
108
16.7M
}
109
110
/* No quadratic_to — null fallback auto-promotes to cubic. */
111
112
static void hb_vector_draw_pdf_cubic_to (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, float c1x, float c1y, float c2x, float c2y, float x, float y, void *)
113
19.6M
{
114
19.6M
  auto *d = (hb_vector_draw_t *) dd;
115
19.6M
  if (unlikely (!d->begin_path_command ())) return;
116
18.1M
  d->append_xy_pdf (c1x, c1y); d->path.append_c (' '); d->append_xy_pdf (c2x, c2y); d->path.append_c (' '); d->append_xy_pdf (x, y); d->path.append_str (" c\n");
117
18.1M
  d->end_path_command ();
118
18.1M
}
119
120
static void hb_vector_draw_pdf_close_path (hb_draw_funcs_t *, void *dd, hb_draw_state_t *, void *)
121
1.89M
{
122
1.89M
  auto *d = (hb_vector_draw_t *) dd;
123
1.89M
  if (unlikely (!d->begin_path_command ())) return;
124
1.61M
  d->path.append_str ("h\n");
125
1.61M
  d->end_path_command ();
126
1.61M
}
127
128
static hb_bool_t
129
hb_vector_draw_set_budget (hb_draw_funcs_t *, void *draw_data,
130
         int64_t budget, void *)
131
0
{
132
0
  auto *draw = (hb_vector_draw_t *) draw_data;
133
0
  draw->budget = budget;
134
0
  draw->recharge_budget ();
135
0
  return true;
136
0
}
137
138
static int64_t
139
hb_vector_draw_get_budget (hb_draw_funcs_t *, void *draw_data, void *)
140
0
{
141
0
  return ((hb_vector_draw_t *) draw_data)->budget;
142
0
}
143
144
static int64_t *
145
hb_vector_draw_get_budget_remaining (hb_draw_funcs_t *, void *draw_data, void *)
146
25.6k
{
147
25.6k
  return &((hb_vector_draw_t *) draw_data)->budget_remaining;
148
25.6k
}
149
150
151
/* ---- Lazy loaders ---- */
152
153
static inline void free_static_vector_draw_svg_funcs ();
154
static struct hb_vector_draw_svg_funcs_lazy_loader_t
155
  : hb_draw_funcs_lazy_loader_t<hb_vector_draw_svg_funcs_lazy_loader_t>
156
{
157
  static hb_draw_funcs_t *create ()
158
1
  {
159
1
    hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
160
1
    hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) hb_vector_draw_svg_move_to, nullptr, nullptr);
161
1
    hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) hb_vector_draw_svg_line_to, nullptr, nullptr);
162
1
    hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) hb_vector_draw_svg_quadratic_to, nullptr, nullptr);
163
1
    hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) hb_vector_draw_svg_cubic_to, nullptr, nullptr);
164
1
    hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) hb_vector_draw_svg_close_path, nullptr, nullptr);
165
1
    hb_draw_funcs_set_set_budget_func (funcs, hb_vector_draw_set_budget, nullptr, nullptr);
166
1
    hb_draw_funcs_set_get_budget_func (funcs, hb_vector_draw_get_budget, nullptr, nullptr);
167
1
    hb_draw_funcs_set_get_budget_remaining_func (funcs, hb_vector_draw_get_budget_remaining, nullptr, nullptr);
168
1
    hb_draw_funcs_make_immutable (funcs);
169
1
    hb_atexit (free_static_vector_draw_svg_funcs);
170
1
    return funcs;
171
1
  }
172
} static_vector_draw_svg_funcs;
173
1
static inline void free_static_vector_draw_svg_funcs () { static_vector_draw_svg_funcs.free_instance (); }
174
175
static inline void free_static_vector_draw_pdf_funcs ();
176
static struct hb_vector_draw_pdf_funcs_lazy_loader_t
177
  : hb_draw_funcs_lazy_loader_t<hb_vector_draw_pdf_funcs_lazy_loader_t>
178
{
179
  static hb_draw_funcs_t *create ()
180
1
  {
181
1
    hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
182
1
    hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) hb_vector_draw_pdf_move_to, nullptr, nullptr);
183
1
    hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) hb_vector_draw_pdf_line_to, nullptr, nullptr);
184
    /* No quadratic_to: null fallback auto-promotes to cubic. */
185
1
    hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) hb_vector_draw_pdf_cubic_to, nullptr, nullptr);
186
1
    hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) hb_vector_draw_pdf_close_path, nullptr, nullptr);
187
1
    hb_draw_funcs_set_set_budget_func (funcs, hb_vector_draw_set_budget, nullptr, nullptr);
188
1
    hb_draw_funcs_set_get_budget_func (funcs, hb_vector_draw_get_budget, nullptr, nullptr);
189
1
    hb_draw_funcs_set_get_budget_remaining_func (funcs, hb_vector_draw_get_budget_remaining, nullptr, nullptr);
190
1
    hb_draw_funcs_make_immutable (funcs);
191
1
    hb_atexit (free_static_vector_draw_pdf_funcs);
192
1
    return funcs;
193
1
  }
194
} static_vector_draw_pdf_funcs;
195
1
static inline void free_static_vector_draw_pdf_funcs () { static_vector_draw_pdf_funcs.free_instance (); }
196
197
198
199
/**
200
 * hb_vector_draw_create_or_fail:
201
 * @format: output format.
202
 *
203
 * Creates a new draw context for vector output.
204
 *
205
 * Return value: (nullable): a newly allocated #hb_vector_draw_t, or `NULL` on failure.
206
 *
207
 * Since: 13.0.0
208
 */
209
hb_vector_draw_t *
210
hb_vector_draw_create_or_fail (hb_vector_format_t format)
211
32.1k
{
212
32.1k
  switch (format)
213
32.1k
  {
214
16.0k
    case HB_VECTOR_FORMAT_SVG:
215
32.1k
    case HB_VECTOR_FORMAT_PDF:
216
32.1k
      break;
217
0
    case HB_VECTOR_FORMAT_INVALID: default:
218
0
      return nullptr;
219
32.1k
  }
220
221
32.1k
  hb_vector_draw_t *draw = hb_object_create<hb_vector_draw_t> ();
222
32.1k
  if (unlikely (!draw))
223
1.35k
    return nullptr;
224
30.8k
  draw->format = format;
225
30.8k
  draw->defs.alloc (2048);
226
30.8k
  draw->body.alloc (8192);
227
30.8k
  draw->path.alloc (2048);
228
30.8k
  return draw;
229
32.1k
}
230
231
/**
232
 * hb_vector_draw_reference:
233
 * @draw: a draw context.
234
 *
235
 * Increases the reference count of @draw.
236
 *
237
 * Return value: (transfer full): referenced @draw.
238
 *
239
 * Since: 13.0.0
240
 */
241
hb_vector_draw_t *
242
hb_vector_draw_reference (hb_vector_draw_t *draw)
243
0
{
244
0
  return hb_object_reference (draw);
245
0
}
246
247
/**
248
 * hb_vector_draw_destroy:
249
 * @draw: a draw context.
250
 *
251
 * Decreases the reference count of @draw and destroys it when it reaches zero.
252
 *
253
 * Since: 13.0.0
254
 */
255
void
256
hb_vector_draw_destroy (hb_vector_draw_t *draw)
257
32.1k
{
258
32.1k
  if (!hb_object_should_destroy (draw))
259
1.35k
    return;
260
261
30.8k
  hb_blob_destroy (draw->recycled_blob);
262
30.8k
  hb_object_actually_destroy (draw);
263
30.8k
  hb_free (draw);
264
30.8k
}
265
266
/**
267
 * hb_vector_draw_set_user_data:
268
 * @draw: a draw context.
269
 * @key: user-data key.
270
 * @data: user-data value.
271
 * @destroy: (nullable): destroy callback for @data.
272
 * @replace: whether to replace an existing value for @key.
273
 *
274
 * Attaches user data to @draw.
275
 *
276
 * Return value: `true` on success, `false` otherwise.
277
 *
278
 * Since: 13.0.0
279
 */
280
hb_bool_t
281
hb_vector_draw_set_user_data (hb_vector_draw_t   *draw,
282
                              hb_user_data_key_t *key,
283
                              void               *data,
284
                              hb_destroy_func_t   destroy,
285
                              hb_bool_t           replace)
286
0
{
287
0
  return hb_object_set_user_data (draw, key, data, destroy, replace);
288
0
}
289
290
/**
291
 * hb_vector_draw_get_user_data:
292
 * @draw: a draw context.
293
 * @key: user-data key.
294
 *
295
 * Gets previously attached user data from @draw.
296
 *
297
 * Return value: (nullable): user-data value associated with @key.
298
 *
299
 * Since: 13.0.0
300
 */
301
void *
302
hb_vector_draw_get_user_data (const hb_vector_draw_t   *draw,
303
                              hb_user_data_key_t *key)
304
0
{
305
0
  return hb_object_get_user_data (draw, key);
306
0
}
307
308
/**
309
 * hb_vector_draw_set_transform:
310
 * @draw: a draw context.
311
 * @xx: transform xx component.
312
 * @yx: transform yx component.
313
 * @xy: transform xy component.
314
 * @yy: transform yy component.
315
 * @dx: transform x translation.
316
 * @dy: transform y translation.
317
 *
318
 * Sets the affine transform used when drawing glyphs.
319
 *
320
 * Since: 13.0.0
321
 */
322
void
323
hb_vector_draw_set_transform (hb_vector_draw_t *draw,
324
                              float xx, float yx,
325
                              float xy, float yy,
326
                              float dx, float dy)
327
25.6k
{
328
25.6k
  draw->transform = {xx, yx, xy, yy, dx, dy};
329
25.6k
}
330
331
/**
332
 * hb_vector_draw_get_transform:
333
 * @draw: a draw context.
334
 * @xx: (out) (nullable): transform xx component.
335
 * @yx: (out) (nullable): transform yx component.
336
 * @xy: (out) (nullable): transform xy component.
337
 * @yy: (out) (nullable): transform yy component.
338
 * @dx: (out) (nullable): transform x translation.
339
 * @dy: (out) (nullable): transform y translation.
340
 *
341
 * Gets the affine transform used when drawing glyphs.
342
 *
343
 * Since: 13.0.0
344
 */
345
void
346
hb_vector_draw_get_transform (const hb_vector_draw_t *draw,
347
                              float *xx, float *yx,
348
                              float *xy, float *yy,
349
                              float *dx, float *dy)
350
0
{
351
0
  if (xx) *xx = draw->transform.xx;
352
0
  if (yx) *yx = draw->transform.yx;
353
0
  if (xy) *xy = draw->transform.xy;
354
0
  if (yy) *yy = draw->transform.yy;
355
0
  if (dx) *dx = draw->transform.x0;
356
0
  if (dy) *dy = draw->transform.y0;
357
0
}
358
359
/**
360
 * hb_vector_draw_set_scale_factor:
361
 * @draw: a draw context.
362
 * @x_scale_factor: x scale factor.
363
 * @y_scale_factor: y scale factor.
364
 *
365
 * Sets additional output scaling factors.
366
 *
367
 * Since: 13.0.0
368
 */
369
void
370
hb_vector_draw_set_scale_factor (hb_vector_draw_t *draw,
371
                                 float x_scale_factor,
372
                                 float y_scale_factor)
373
0
{
374
0
  draw->x_scale_factor = x_scale_factor > 0.f ? x_scale_factor : 1.f;
375
0
  draw->y_scale_factor = y_scale_factor > 0.f ? y_scale_factor : 1.f;
376
0
}
377
378
/**
379
 * hb_vector_draw_get_scale_factor:
380
 * @draw: a draw context.
381
 * @x_scale_factor: (out) (nullable): x scale factor.
382
 * @y_scale_factor: (out) (nullable): y scale factor.
383
 *
384
 * Gets additional output scaling factors.
385
 *
386
 * Since: 13.0.0
387
 */
388
void
389
hb_vector_draw_get_scale_factor (const hb_vector_draw_t *draw,
390
                                 float *x_scale_factor,
391
                                 float *y_scale_factor)
392
0
{
393
0
  if (x_scale_factor) *x_scale_factor = draw->x_scale_factor;
394
0
  if (y_scale_factor) *y_scale_factor = draw->y_scale_factor;
395
0
}
396
397
/**
398
 * hb_vector_draw_set_extents:
399
 * @draw: a draw context.
400
 * @extents: (nullable): output extents to set or expand.
401
 *
402
 * Sets or expands output extents on @draw. Passing `NULL` clears extents.
403
 *
404
 * Since: 13.0.0
405
 */
406
void
407
hb_vector_draw_set_extents (hb_vector_draw_t *draw,
408
                            const hb_vector_extents_t *extents)
409
0
{
410
0
  if (!extents)
411
0
  {
412
0
    draw->extents = {0, 0, 0, 0};
413
0
    draw->has_extents = false;
414
0
    return;
415
0
  }
416
417
0
  if (extents->width == 0.f || extents->height == 0.f)
418
0
    return;
419
420
  /* Caller-supplied extents are in input-space; divide by
421
   * scale_factor so they end up in output-space, matching
422
   * the per-glyph extents accumulated via
423
   * hb_vector_set_glyph_extents_common (which applies the
424
   * same divide).  Normalize so origin is the min corner and
425
   * width/height are positive — callers may pass a
426
   * glyph-extents-style box with negative height. */
427
0
  float x0 = extents->x / draw->x_scale_factor;
428
0
  float y0 = extents->y / draw->y_scale_factor;
429
0
  float x1 = x0 + extents->width  / draw->x_scale_factor;
430
0
  float y1 = y0 + extents->height / draw->y_scale_factor;
431
0
  hb_vector_extents_t e = {
432
0
    hb_min (x0, x1), hb_min (y0, y1),
433
0
    fabsf (x1 - x0), fabsf (y1 - y0),
434
0
  };
435
436
0
  if (draw->has_extents)
437
0
  {
438
0
    float x0 = hb_min (draw->extents.x, e.x);
439
0
    float y0 = hb_min (draw->extents.y, e.y);
440
0
    float x1 = hb_max (draw->extents.x + draw->extents.width,
441
0
                       e.x + e.width);
442
0
    float y1 = hb_max (draw->extents.y + draw->extents.height,
443
0
                       e.y + e.height);
444
0
    draw->extents = {x0, y0, x1 - x0, y1 - y0};
445
0
  }
446
0
  else
447
0
  {
448
0
    draw->extents = e;
449
0
    draw->has_extents = true;
450
0
  }
451
0
}
452
453
/**
454
 * hb_vector_draw_get_extents:
455
 * @draw: a draw context.
456
 * @extents: (out) (nullable): where to store current output extents.
457
 *
458
 * Gets current output extents from @draw.
459
 *
460
 * Return value: `true` if extents are set, `false` otherwise.
461
 *
462
 * Since: 13.0.0
463
 */
464
hb_bool_t
465
hb_vector_draw_get_extents (const hb_vector_draw_t *draw,
466
                            hb_vector_extents_t *extents)
467
0
{
468
0
  if (!draw->has_extents)
469
0
    return false;
470
471
0
  if (extents)
472
0
    *extents = draw->extents;
473
0
  return true;
474
0
}
475
476
/**
477
 * hb_vector_draw_set_glyph_extents:
478
 * @draw: a draw context.
479
 * @glyph_extents: glyph extents in font units.
480
 *
481
 * Expands @draw extents using @glyph_extents under the current transform.
482
 *
483
 * Return value: `true` on success, `false` otherwise.
484
 *
485
 * Since: 13.0.0
486
 */
487
hb_bool_t
488
hb_vector_draw_set_glyph_extents (hb_vector_draw_t *draw,
489
                                  const hb_glyph_extents_t *glyph_extents)
490
0
{
491
0
  hb_bool_t has_extents = draw->has_extents;
492
0
  hb_bool_t ret = hb_vector_set_glyph_extents_common (draw->transform,
493
0
               draw->x_scale_factor,
494
0
               draw->y_scale_factor,
495
0
               glyph_extents,
496
0
               &draw->extents,
497
0
               &has_extents);
498
0
  draw->has_extents = has_extents;
499
0
  return ret;
500
0
}
501
502
/**
503
 * hb_vector_draw_get_format:
504
 * @draw: a vector draw context.
505
 *
506
 * Gets the output format @draw was created with.
507
 *
508
 * Return value: the output format.
509
 *
510
 * Since: 14.2.0
511
 */
512
hb_vector_format_t
513
hb_vector_draw_get_format (const hb_vector_draw_t *draw)
514
0
{
515
0
  return draw->format;
516
0
}
517
518
/**
519
 * hb_vector_draw_get_funcs:
520
 * @draw: a vector draw context.
521
 *
522
 * Gets draw callbacks for feeding outline data into @draw.
523
 * Pass @draw as the @draw_data argument when calling them.
524
 *
525
 * Return value: (transfer none): immutable #hb_draw_funcs_t.
526
 *
527
 * Since: 14.2.0
528
 */
529
hb_draw_funcs_t *
530
hb_vector_draw_get_funcs (const hb_vector_draw_t *draw)
531
25.6k
{
532
25.6k
  switch (draw ? draw->format : HB_VECTOR_FORMAT_INVALID)
533
25.6k
  {
534
12.7k
    case HB_VECTOR_FORMAT_SVG: return static_vector_draw_svg_funcs.get_unconst ();
535
12.9k
    case HB_VECTOR_FORMAT_PDF: return static_vector_draw_pdf_funcs.get_unconst ();
536
0
    case HB_VECTOR_FORMAT_INVALID: default: return nullptr;
537
25.6k
  }
538
25.6k
}
539
540
/**
541
 * hb_vector_draw_new_path:
542
 * @draw: a draw context.
543
 *
544
 * Flushes any pending path and starts a new one.  Call this
545
 * between glyphs to separate their outlines so fill rules
546
 * don't interact across glyphs.
547
 *
548
 * Since: 14.2.0
549
 */
550
void
551
hb_vector_draw_new_path (hb_vector_draw_t *draw)
552
0
{
553
0
  draw->new_path ();
554
0
}
555
556
/**
557
 * hb_vector_draw_glyph_or_fail:
558
 * @draw: a draw context.
559
 * @font: font object.
560
 * @glyph: glyph ID.
561
 * @extents_mode: extents update mode.
562
 *
563
 * Convenience to draw one glyph into @draw.  Equivalent to:
564
 *
565
 * |[<!-- language="plain" -->
566
 * // extend extents if requested
567
 * hb_vector_draw_new_path (draw);
568
 * hb_font_draw_glyph_or_fail (font, glyph,
569
 *   hb_vector_draw_get_funcs (draw), draw);
570
 * ]|
571
 *
572
 * Return value: `true` if glyph data was emitted, `false` otherwise.
573
 *
574
 * Since: 14.2.0
575
 */
576
hb_bool_t
577
hb_vector_draw_glyph_or_fail (hb_vector_draw_t *draw,
578
                      hb_font_t *font,
579
                      hb_codepoint_t glyph,
580
                      hb_vector_extents_mode_t extents_mode)
581
25.6k
{
582
583
25.6k
  if (extents_mode == HB_VECTOR_EXTENTS_MODE_EXPAND)
584
25.6k
  {
585
25.6k
    hb_glyph_extents_t ge;
586
25.6k
    if (hb_font_get_glyph_extents (font, glyph, &ge))
587
14.7k
    {
588
14.7k
      hb_bool_t has_extents = draw->has_extents;
589
14.7k
      hb_vector_set_glyph_extents_common (draw->transform,
590
14.7k
            draw->x_scale_factor,
591
14.7k
            draw->y_scale_factor,
592
14.7k
            &ge,
593
14.7k
            &draw->extents,
594
14.7k
            &has_extents);
595
14.7k
      draw->has_extents = has_extents;
596
14.7k
    }
597
25.6k
  }
598
599
25.6k
  draw->new_path ();
600
25.6k
  return hb_font_draw_glyph_or_fail (font, glyph, hb_vector_draw_get_funcs (draw), draw);
601
25.6k
}
602
603
/**
604
 * hb_vector_draw_glyph:
605
 * @draw: a draw context.
606
 * @font: font object.
607
 * @glyph: glyph ID.
608
 * @extents_mode: extents update mode.
609
 *
610
 * Draws one glyph into @draw.  Equivalent to
611
 * hb_vector_draw_glyph_or_fail() with the return value ignored.
612
 *
613
 * Since: 14.2.0
614
 */
615
void
616
hb_vector_draw_glyph (hb_vector_draw_t *draw,
617
                      hb_font_t *font,
618
                      hb_codepoint_t glyph,
619
                      hb_vector_extents_mode_t extents_mode)
620
25.6k
{
621
25.6k
  hb_vector_draw_glyph_or_fail (draw, font, glyph, extents_mode);
622
25.6k
}
623
624
625
/**
626
 * hb_vector_draw_set_precision:
627
 * @draw: a draw context.
628
 * @precision: decimal precision.
629
 *
630
 * Sets numeric output precision for draw output.
631
 *
632
 * Since: 14.2.0
633
 */
634
void
635
hb_vector_draw_set_precision (hb_vector_draw_t *draw,
636
                             unsigned precision)
637
29.2k
{
638
29.2k
  draw->set_precision (precision);
639
29.2k
}
640
641
/**
642
 * hb_vector_draw_get_precision:
643
 * @draw: a draw context.
644
 *
645
 * Returns the numeric output precision previously set on @draw,
646
 * or the default if none was set.
647
 *
648
 * Return value: the precision.
649
 *
650
 * Since: 14.2.0
651
 */
652
unsigned
653
hb_vector_draw_get_precision (const hb_vector_draw_t *draw)
654
0
{
655
0
  return draw->get_precision ();
656
0
}
657
658
/**
659
 * hb_vector_draw_set_foreground:
660
 * @draw: a draw context.
661
 * @foreground: foreground fill color.
662
 *
663
 * Sets the fill color for drawn glyph outlines.
664
 * Default is opaque black.
665
 *
666
 * Since: 14.2.0
667
 */
668
void
669
hb_vector_draw_set_foreground (hb_vector_draw_t *draw,
670
                               hb_color_t foreground)
671
0
{
672
0
  draw->flush_path ();
673
0
  draw->foreground = foreground;
674
0
}
675
676
/**
677
 * hb_vector_draw_get_foreground:
678
 * @draw: a draw context.
679
 *
680
 * Returns the foreground fill color.
681
 *
682
 * Return value: the foreground color.
683
 *
684
 * Since: 14.2.0
685
 */
686
hb_color_t
687
hb_vector_draw_get_foreground (const hb_vector_draw_t *draw)
688
0
{
689
0
  return draw->foreground;
690
0
}
691
692
/**
693
 * hb_vector_draw_set_background:
694
 * @draw: a draw context.
695
 * @background: background color.
696
 *
697
 * Sets the background color.  If non-transparent, a filled
698
 * rectangle covering the extents is emitted behind all content.
699
 * Default is transparent (no background).
700
 *
701
 * Since: 14.2.0
702
 */
703
void
704
hb_vector_draw_set_background (hb_vector_draw_t *draw,
705
                               hb_color_t background)
706
0
{
707
0
  draw->background = background;
708
0
}
709
710
/**
711
 * hb_vector_draw_get_background:
712
 * @draw: a draw context.
713
 *
714
 * Returns the background color.
715
 *
716
 * Return value: the background color.
717
 *
718
 * Since: 14.2.0
719
 */
720
hb_color_t
721
hb_vector_draw_get_background (const hb_vector_draw_t *draw)
722
0
{
723
0
  return draw->background;
724
0
}
725
726
static hb_blob_t *
727
hb_vector_draw_render_pdf (hb_vector_draw_t *draw)
728
12.9k
{
729
12.9k
  draw->flush_path ();
730
12.9k
  if (!draw->has_extents)
731
9.54k
    return nullptr;
732
733
  /* Collect the content stream.  Extents and path coordinates are
734
   * both in Y-up (font) space, same as the PDF page. */
735
3.36k
  float ex = draw->extents.x;
736
3.36k
  float ey = draw->extents.y;
737
3.36k
  float ew = draw->extents.width;
738
3.36k
  float eh = draw->extents.height;
739
740
3.36k
  hb_vector_buf_t stream;
741
3.36k
  stream.alloc (draw->body.length + draw->path.length + 256);
742
743
  /* Background rect. */
744
3.36k
  if (hb_color_get_alpha (draw->background))
745
0
  {
746
0
    float a = hb_color_get_alpha (draw->background) / 255.f;
747
0
    if (a < 1.f - 1.f / 512.f)
748
0
    {
749
0
      stream.append_num (a, 4);
750
0
      stream.append_str (" ca gs\n");
751
0
    }
752
0
    stream.append_num (hb_color_get_red (draw->background) / 255.f, 4);
753
0
    stream.append_c (' ');
754
0
    stream.append_num (hb_color_get_green (draw->background) / 255.f, 4);
755
0
    stream.append_c (' ');
756
0
    stream.append_num (hb_color_get_blue (draw->background) / 255.f, 4);
757
0
    stream.append_str (" rg\n");
758
0
    stream.append_num (ex);
759
0
    stream.append_c (' ');
760
0
    stream.append_num (ey);
761
0
    stream.append_c (' ');
762
0
    stream.append_num (ew);
763
0
    stream.append_c (' ');
764
0
    stream.append_num (eh);
765
0
    stream.append_str (" re f\n");
766
0
  }
767
768
3.36k
  draw->flush_path ();
769
3.36k
  if (draw->body.length)
770
1.28k
    stream.append_len (draw->body.arrayZ, draw->body.length);
771
772
  /* Build PDF objects, tracking byte offsets for xref. */
773
3.36k
  hb_vector_buf_t out;
774
3.36k
  hb_buf_recover_recycled (draw->recycled_blob, &out);
775
3.36k
  out.alloc (stream.length + 512);
776
777
3.36k
  unsigned offsets[5]; /* objects 1-4, plus end */
778
779
3.36k
  out.append_str ("%PDF-1.4\n%\xC0\xC1\xC2\xC3\n");
780
781
  /* Object 1: Catalog */
782
3.36k
  offsets[0] = out.length;
783
3.36k
  out.append_str ("1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
784
785
  /* Object 2: Pages */
786
3.36k
  offsets[1] = out.length;
787
3.36k
  out.append_str ("2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n");
788
789
  /* Object 3: Page */
790
3.36k
  offsets[2] = out.length;
791
3.36k
  out.append_str ("3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [");
792
3.36k
  out.append_num (ex);
793
3.36k
  out.append_c (' ');
794
3.36k
  out.append_num (ey);
795
3.36k
  out.append_c (' ');
796
3.36k
  out.append_num (ex + ew);
797
3.36k
  out.append_c (' ');
798
3.36k
  out.append_num (ey + eh);
799
3.36k
  out.append_str ("] /Contents 4 0 R");
800
3.36k
  if (draw->pdf_extgstate_dict.length)
801
0
  {
802
0
    out.append_str (" /Resources << /ExtGState << ");
803
0
    out.append_len (draw->pdf_extgstate_dict.arrayZ, draw->pdf_extgstate_dict.length);
804
0
    out.append_str (">> >>");
805
0
  }
806
3.36k
  out.append_str (" >>\nendobj\n");
807
808
  /* Object 4: Content stream */
809
3.36k
  offsets[3] = out.length;
810
3.36k
  out.append_str ("4 0 obj\n<< /Length ");
811
3.36k
  out.append_unsigned (stream.length);
812
3.36k
  out.append_str (" >>\nstream\n");
813
3.36k
  out.append_len (stream.arrayZ, stream.length);
814
3.36k
  out.append_str ("endstream\nendobj\n");
815
816
  /* Cross-reference table */
817
3.36k
  unsigned xref_offset = out.length;
818
3.36k
  out.append_str ("xref\n0 5\n");
819
3.36k
  out.append_str ("0000000000 65535 f \n");
820
16.8k
  for (unsigned i = 0; i < 4; i++)
821
13.4k
  {
822
13.4k
    char tmp[21];
823
13.4k
    snprintf (tmp, sizeof (tmp), "%010u 00000 n \n", offsets[i]);
824
13.4k
    out.append_len (tmp, 20);
825
13.4k
  }
826
827
  /* Trailer */
828
3.36k
  out.append_str ("trailer\n<< /Size 5 /Root 1 0 R >>\nstartxref\n");
829
3.36k
  out.append_unsigned (xref_offset);
830
3.36k
  out.append_str ("\n%%EOF\n");
831
832
3.36k
  hb_blob_t *blob = hb_buf_blob_from (&draw->recycled_blob, &out);
833
834
3.36k
  return blob;
835
12.9k
}
836
837
static hb_blob_t *
838
hb_vector_draw_render_svg (hb_vector_draw_t *draw)
839
12.7k
{
840
12.7k
  draw->flush_path ();
841
12.7k
  if (!draw->has_extents)
842
9.54k
    return nullptr;
843
844
3.16k
  hb_vector_buf_t out;
845
3.16k
  hb_buf_recover_recycled (draw->recycled_blob, &out);
846
3.16k
  unsigned estimated = draw->defs.length +
847
3.16k
           (draw->body.length ? draw->body.length : draw->path.length) +
848
3.16k
           256;
849
3.16k
  out.alloc (estimated);
850
  /* Extents are in Y-up (font) space.  SVG is Y-down.
851
   * The global scale(1,-1) wrapper flips content; the
852
   * viewBox maps the flipped range. */
853
3.16k
  float vb_x = draw->extents.x;
854
3.16k
  float vb_y = -(draw->extents.y + draw->extents.height);
855
3.16k
  float vb_w = draw->extents.width;
856
3.16k
  float vb_h = draw->extents.height;
857
3.16k
  out.append_str ("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"");
858
3.16k
  out.append_num (vb_x);
859
3.16k
  out.append_c (' ');
860
3.16k
  out.append_num (vb_y);
861
3.16k
  out.append_c (' ');
862
3.16k
  out.append_num (vb_w);
863
3.16k
  out.append_c (' ');
864
3.16k
  out.append_num (vb_h);
865
3.16k
  out.append_str ("\" width=\"");
866
3.16k
  out.append_num (vb_w);
867
3.16k
  out.append_str ("\" height=\"");
868
3.16k
  out.append_num (vb_h);
869
3.16k
  out.append_str ("\">\n");
870
871
3.16k
  if (draw->defs.length)
872
0
  {
873
0
    out.append_str ("<defs>\n");
874
0
    out.append_len (draw->defs.arrayZ, draw->defs.length);
875
0
    out.append_str ("</defs>\n");
876
0
  }
877
878
  /* Background rect. */
879
3.16k
  if (hb_color_get_alpha (draw->background))
880
0
  {
881
0
    out.append_str ("<rect x=\"");
882
0
    out.append_num (vb_x);
883
0
    out.append_str ("\" y=\"");
884
0
    out.append_num (vb_y);
885
0
    out.append_str ("\" width=\"");
886
0
    out.append_num (vb_w);
887
0
    out.append_str ("\" height=\"");
888
0
    out.append_num (vb_h);
889
0
    out.append_str ("\" fill=\"rgb(");
890
0
    out.append_unsigned (hb_color_get_red (draw->background));
891
0
    out.append_c (',');
892
0
    out.append_unsigned (hb_color_get_green (draw->background));
893
0
    out.append_c (',');
894
0
    out.append_unsigned (hb_color_get_blue (draw->background));
895
0
    out.append_str (")\"");
896
0
    if (hb_color_get_alpha (draw->background) < 255)
897
0
    {
898
0
      out.append_str (" fill-opacity=\"");
899
0
      out.append_num (hb_color_get_alpha (draw->background) / 255.f, 4);
900
0
      out.append_c ('"');
901
0
    }
902
0
    out.append_str ("/>\n");
903
0
  }
904
905
3.16k
  draw->flush_path ();
906
3.16k
  if (draw->body.length)
907
1.16k
  {
908
1.16k
    out.append_str ("<g transform=\"scale(1,-1)\">\n");
909
1.16k
    out.append_len (draw->body.arrayZ, draw->body.length);
910
1.16k
    out.append_str ("</g>\n");
911
1.16k
  }
912
913
3.16k
  out.append_str ("</svg>\n");
914
915
3.16k
  hb_blob_t *blob = hb_buf_blob_from (&draw->recycled_blob, &out);
916
917
3.16k
  return blob;
918
12.7k
}
919
920
/**
921
 * hb_vector_draw_render:
922
 * @draw: a draw context.
923
 *
924
 * Renders accumulated draw content to an output blob.
925
 * Accumulated content and extents are cleared on return, including
926
 * when rendering fails. User configuration is preserved.
927
 *
928
 * Return value: (transfer full) (nullable): output blob, or `NULL` if rendering cannot proceed.
929
 *
930
 * Since: 13.0.0
931
 */
932
hb_blob_t *
933
hb_vector_draw_render (hb_vector_draw_t *draw)
934
25.6k
{
935
25.6k
  HB_SCOPE_GUARD (hb_vector_draw_clear (draw));
936
25.6k
  switch (draw->format)
937
25.6k
  {
938
12.7k
    case HB_VECTOR_FORMAT_SVG:
939
12.7k
      return hb_vector_draw_render_svg (draw);
940
941
12.9k
    case HB_VECTOR_FORMAT_PDF:
942
12.9k
      return hb_vector_draw_render_pdf (draw);
943
944
0
    case HB_VECTOR_FORMAT_INVALID: default:
945
0
      return nullptr;
946
25.6k
  }
947
25.6k
}
948
949
/**
950
 * hb_vector_draw_clear:
951
 * @draw: a draw context.
952
 *
953
 * Discards accumulated draw output so @draw can be reused for
954
 * another render.  User configuration (transform, scale factors,
955
 * precision, foreground, background, and budget) is preserved.  Call
956
 * hb_vector_draw_reset() to also reset user configuration to defaults.
957
 *
958
 * Since: 14.2.0
959
 */
960
void
961
hb_vector_draw_clear (hb_vector_draw_t *draw)
962
25.6k
{
963
25.6k
  draw->extents = {0, 0, 0, 0};
964
25.6k
  draw->has_extents = false;
965
25.6k
  draw->defs.clear ();
966
25.6k
  draw->body.clear ();
967
25.6k
  draw->path.clear ();
968
25.6k
  draw->pdf_extgstate_dict.clear ();
969
25.6k
  draw->pdf_extgstate_count = 0;
970
25.6k
  draw->recharge_budget ();
971
25.6k
}
972
973
/**
974
 * hb_vector_draw_reset:
975
 * @draw: a draw context.
976
 *
977
 * Restores user configuration to defaults and clears accumulated content.
978
 * The output format and attached user data are preserved.
979
 *
980
 * Since: 13.0.0
981
 */
982
void
983
hb_vector_draw_reset (hb_vector_draw_t *draw)
984
0
{
985
0
  draw->transform = {1, 0, 0, 1, 0, 0};
986
0
  draw->x_scale_factor = 1.f;
987
0
  draw->y_scale_factor = 1.f;
988
0
  draw->foreground = HB_COLOR (0, 0, 0, 255);
989
0
  draw->background = HB_COLOR (0, 0, 0, 0);
990
0
  draw->set_precision (2);
991
0
  draw->budget = HB_BUDGET_DEFAULT;
992
0
  hb_vector_draw_clear (draw);
993
0
}
994
995
/**
996
 * hb_vector_draw_recycle_blob:
997
 * @draw: a draw context.
998
 * @blob: (transfer full) (nullable): previously rendered blob to recycle.
999
 *
1000
 * Provides a blob for internal buffer reuse by later render calls.
1001
 * The caller transfers ownership of one reference to @draw and must
1002
 * not destroy that reference after this call. Finish accessing @blob
1003
 * and its data before recycling: later renders may reuse the blob
1004
 * and overwrite its data.
1005
 *
1006
 * Since: 13.0.0
1007
 */
1008
void
1009
hb_vector_draw_recycle_blob (hb_vector_draw_t *draw,
1010
                             hb_blob_t *blob)
1011
5.21k
{
1012
5.21k
  hb_blob_destroy (draw->recycled_blob);
1013
5.21k
  draw->recycled_blob = nullptr;
1014
5.21k
  if (!blob || blob == hb_blob_get_empty ())
1015
0
    return;
1016
5.21k
  draw->recycled_blob = blob;
1017
5.21k
}