File Coverage

src/pdfmake_font_widths.c
Criterion Covered Total %
statement 69 281 24.5
branch 48 260 18.4
condition n/a
subroutine n/a
pod n/a
total 117 541 21.6


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_font_widths.c — Glyph-advance + descriptor metrics resolution.
3             *
4             * PDF spec references:
5             * §9.2.4 Simple font /FirstChar, /LastChar, /Widths
6             * §9.7.4.3 CID /W array (formats 1 and 2), /DW default width
7             * §9.8.1 Font descriptor /Ascent, /Descent, /CapHeight, /XHeight
8             */
9              
10             #include "pdfmake_font_widths.h"
11             #include "pdfmake_reader.h"
12             #include "pdfmake_parser.h"
13             #include "pdfmake_font.h"
14             #include "pdfmake_arena.h"
15             #include "pdfmake_buf.h"
16             #include
17             #include
18             #include
19              
20             /*============================================================================
21             * Helpers
22             *==========================================================================*/
23              
24 2518           static int16_t int_of(const pdfmake_obj_t *o, int16_t fallback) {
25 2518 50         if (!o) return fallback;
26 2518 50         if (o->kind == PDFMAKE_INT) return (int16_t)o->as.i;
27 0 0         if (o->kind == PDFMAKE_REAL) return (int16_t)o->as.r;
28 0           return fallback;
29             }
30              
31 90           static int32_t int32_of(const pdfmake_obj_t *o, int32_t fallback) {
32 90 50         if (!o) return fallback;
33 90 50         if (o->kind == PDFMAKE_INT) return (int32_t)o->as.i;
34 0 0         if (o->kind == PDFMAKE_REAL) return (int32_t)o->as.r;
35 0           return fallback;
36             }
37              
38             /*============================================================================
39             * Descriptor metrics
40             *==========================================================================*/
41              
42 0           static void fill_descriptor_metrics(pdfmake_arena_t *arena,
43             pdfmake_obj_t *descriptor,
44             pdfmake_font_widths_t *out)
45             {
46             uint32_t ascent_k;
47             uint32_t descent_k;
48             uint32_t cap_k;
49             uint32_t xh_k;
50             uint32_t mw_k;
51             pdfmake_obj_t *mw;
52 0 0         if (!descriptor || descriptor->kind != PDFMAKE_DICT) return;
    0          
53              
54 0           ascent_k = pdfmake_arena_intern_name(arena, "Ascent", 6);
55 0           descent_k = pdfmake_arena_intern_name(arena, "Descent", 7);
56 0           cap_k = pdfmake_arena_intern_name(arena, "CapHeight", 9);
57 0           xh_k = pdfmake_arena_intern_name(arena, "XHeight", 7);
58 0           mw_k = pdfmake_arena_intern_name(arena, "MissingWidth", 12);
59              
60 0           out->ascent = int_of(pdfmake_dict_get(descriptor, ascent_k), out->ascent);
61 0           out->descent = int_of(pdfmake_dict_get(descriptor, descent_k), out->descent);
62 0           out->cap_height = int_of(pdfmake_dict_get(descriptor, cap_k), out->cap_height);
63 0           out->x_height = int_of(pdfmake_dict_get(descriptor, xh_k), out->x_height);
64              
65 0           mw = pdfmake_dict_get(descriptor, mw_k);
66 0 0         if (mw) out->default_width = int_of(mw, out->default_width);
67             }
68              
69             /*============================================================================
70             * Simple font /Widths
71             *==========================================================================*/
72              
73 46           void pdfmake_font_widths_init(pdfmake_font_widths_t *w) {
74 46           memset(w, 0, sizeof(*w));
75 46           }
76              
77 45           int pdfmake_font_widths_from_simple(
78             pdfmake_arena_t *arena,
79             pdfmake_obj_t *font_dict,
80             pdfmake_font_widths_t *out)
81             {
82             uint32_t first_k;
83             uint32_t last_k;
84             uint32_t widths_k;
85             uint32_t fd_k;
86             pdfmake_obj_t *fd;
87             pdfmake_obj_t *first;
88             pdfmake_obj_t *last;
89             pdfmake_obj_t *widths;
90             int32_t fc;
91             int32_t lc;
92             size_t n;
93             size_t have;
94             int16_t *table;
95             size_t i;
96              
97 45           pdfmake_font_widths_init(out);
98 45 50         if (!font_dict || font_dict->kind != PDFMAKE_DICT || !arena) return -1;
    50          
    50          
99              
100 45           first_k = pdfmake_arena_intern_name(arena, "FirstChar", 9);
101 45           last_k = pdfmake_arena_intern_name(arena, "LastChar", 8);
102 45           widths_k = pdfmake_arena_intern_name(arena, "Widths", 6);
103 45           fd_k = pdfmake_arena_intern_name(arena, "FontDescriptor", 14);
104              
105             /* Always pull descriptor metrics even if /Widths is absent */
106 45           fd = pdfmake_dict_get(font_dict, fd_k);
107 45 50         if (fd && fd->kind == PDFMAKE_DICT) {
    50          
108 0           fill_descriptor_metrics(arena, fd, out);
109             }
110              
111 45           first = pdfmake_dict_get(font_dict, first_k);
112 45           last = pdfmake_dict_get(font_dict, last_k);
113 45           widths = pdfmake_dict_get(font_dict, widths_k);
114              
115 45 50         if (!first || !last || !widths) return -1;
    50          
    50          
116 45 50         if (widths->kind != PDFMAKE_ARRAY) return -1;
117              
118 45           fc = int32_of(first, -1);
119 45           lc = int32_of(last, -1);
120 45 50         if (fc < 0 || lc < fc || fc > 0xFFFF || lc > 0xFFFF) return -1;
    50          
    50          
    50          
121              
122 45           n = (size_t)(lc - fc + 1);
123 45           have = pdfmake_array_len(widths);
124 45 50         if (have < n) n = have;
125              
126 45           table = pdfmake_arena_alloc(arena, n * sizeof(int16_t));
127 45 50         if (!table) return -1;
128              
129 2563 100         for (i = 0; i < n; i++) {
130 2518           pdfmake_obj_t *w = pdfmake_array_get(widths, i);
131 2518           table[i] = int_of(w, 0);
132             }
133              
134 45           out->table = table;
135 45           out->first_char = (uint16_t)fc;
136 45           out->last_char = (uint16_t)(fc + n - 1);
137 45           return 0;
138             }
139              
140             /*============================================================================
141             * CID font /W array
142             *
143             * Two entry formats, mixed freely in one array:
144             * 1. first [ w1 w2 w3 ... ] codes first..first+n-1 have widths wi
145             * 2. first last w codes first..last all have width w
146             *==========================================================================*/
147              
148 0           static int add_range(pdfmake_arena_t *arena,
149             pdfmake_width_range_t **list, size_t *count, size_t *cap,
150             uint32_t lo, uint32_t hi, int16_t w)
151             {
152             pdfmake_width_range_t *r;
153 0 0         if (*count >= *cap) {
154 0 0         size_t new_cap = *cap ? *cap * 2 : 16;
155 0           pdfmake_width_range_t *n = pdfmake_arena_alloc(
156             arena, new_cap * sizeof(pdfmake_width_range_t));
157 0 0         if (!n) return -1;
158 0 0         if (*list && *count > 0) {
    0          
159 0           memcpy(n, *list, *count * sizeof(pdfmake_width_range_t));
160             }
161 0           *list = n;
162 0           *cap = new_cap;
163             }
164 0           r = &(*list)[(*count)++];
165 0           r->lo = lo;
166 0           r->hi = hi;
167 0           r->width = w;
168 0           return 0;
169             }
170              
171 0           static int range_cmp(const void *a, const void *b) {
172 0           const pdfmake_width_range_t *ra = a;
173 0           const pdfmake_width_range_t *rb = b;
174 0 0         if (ra->lo < rb->lo) return -1;
175 0 0         if (ra->lo > rb->lo) return 1;
176 0           return 0;
177             }
178              
179 0           static int parse_w_array(pdfmake_arena_t *arena,
180             pdfmake_obj_t *w_arr,
181             pdfmake_font_widths_t *out)
182             {
183             pdfmake_width_range_t *list;
184             size_t count, cap;
185             size_t n;
186             size_t i;
187 0 0         if (!w_arr || w_arr->kind != PDFMAKE_ARRAY) return -1;
    0          
188              
189 0           list = NULL;
190 0           count = 0; cap = 0;
191 0           n = pdfmake_array_len(w_arr);
192              
193 0           i = 0;
194 0 0         while (i < n) {
195 0           pdfmake_obj_t *first = pdfmake_array_get(w_arr, i++);
196 0           int32_t fc = int32_of(first, -1);
197             pdfmake_obj_t *next;
198 0 0         if (fc < 0 || i >= n) break;
    0          
199              
200 0           next = pdfmake_array_get(w_arr, i);
201 0 0         if (!next) break;
202              
203 0 0         if (next->kind == PDFMAKE_ARRAY) {
204             /* Format 1: first [ w w w ... ] */
205 0           size_t m = pdfmake_array_len(next);
206             size_t j;
207             /* Expand as consecutive single-code ranges for uniform lookup.
208             * Could compress runs of equal widths, but not worth the complexity. */
209 0 0         for (j = 0; j < m; j++) {
210 0           int16_t w = int_of(pdfmake_array_get(next, j), 0);
211 0 0         if (add_range(arena, &list, &count, &cap,
212             (uint32_t)(fc + j), (uint32_t)(fc + j), w) < 0) {
213 0           return -1;
214             }
215             }
216 0           i++;
217             } else {
218             /* Format 2: first last w */
219 0           int32_t lc = int32_of(next, fc);
220             int16_t w;
221 0 0         if (lc < fc) lc = fc;
222 0 0         if (i + 1 >= n) break;
223 0           w = int_of(pdfmake_array_get(w_arr, i + 1), 0);
224 0 0         if (add_range(arena, &list, &count, &cap,
225             (uint32_t)fc, (uint32_t)lc, w) < 0) {
226 0           return -1;
227             }
228 0           i += 2;
229             }
230             }
231              
232 0 0         if (count > 1) qsort(list, count, sizeof(*list), range_cmp);
233 0           out->ranges = list;
234 0           out->range_count = count;
235 0           return 0;
236             }
237              
238 1           int pdfmake_font_widths_from_cid(
239             pdfmake_arena_t *arena,
240             pdfmake_obj_t *font_dict,
241             pdfmake_font_widths_t *out)
242             {
243             uint32_t desc_k;
244             pdfmake_obj_t *desc_arr;
245 1           pdfmake_obj_t *cidfont = NULL;
246             uint32_t w_k;
247             uint32_t dw_k;
248             uint32_t fd_k;
249             pdfmake_obj_t *dw;
250             pdfmake_obj_t *fd;
251             pdfmake_obj_t *w_arr;
252              
253 1           pdfmake_font_widths_init(out);
254 1           out->default_width = 1000; /* Per §9.7.4.3: /DW defaults to 1000 */
255              
256 1 50         if (!font_dict || font_dict->kind != PDFMAKE_DICT || !arena) return -1;
    50          
    50          
257              
258             /* For Type0 fonts, widths live on /DescendantFonts[0] */
259 1           desc_k = pdfmake_arena_intern_name(arena, "DescendantFonts", 15);
260 1           desc_arr = pdfmake_dict_get(font_dict, desc_k);
261 1 50         if (desc_arr && desc_arr->kind == PDFMAKE_ARRAY && pdfmake_array_len(desc_arr) > 0) {
    50          
    50          
262 1           cidfont = pdfmake_array_get(desc_arr, 0);
263             }
264 1 50         if (!cidfont) cidfont = font_dict; /* Might already be the CIDFont */
265 1 50         if (cidfont->kind != PDFMAKE_DICT) return -1;
266              
267 0           w_k = pdfmake_arena_intern_name(arena, "W", 1);
268 0           dw_k = pdfmake_arena_intern_name(arena, "DW", 2);
269 0           fd_k = pdfmake_arena_intern_name(arena, "FontDescriptor", 14);
270              
271 0           dw = pdfmake_dict_get(cidfont, dw_k);
272 0 0         if (dw) out->default_width = int_of(dw, out->default_width);
273              
274 0           fd = pdfmake_dict_get(cidfont, fd_k);
275 0 0         if (fd && fd->kind == PDFMAKE_DICT) {
    0          
276 0           fill_descriptor_metrics(arena, fd, out);
277             }
278              
279 0           w_arr = pdfmake_dict_get(cidfont, w_k);
280 0 0         if (w_arr) parse_w_array(arena, w_arr, out);
281              
282 0           return 0;
283             }
284              
285             /*============================================================================
286             * Lookup
287             *==========================================================================*/
288              
289 12698           int16_t pdfmake_font_widths_lookup(const pdfmake_font_widths_t *w,
290             uint32_t code)
291             {
292 12698 50         if (!w) return 0;
293              
294             /* Simple font table */
295 12698 100         if (w->table && code >= w->first_char && code <= w->last_char) {
    50          
    50          
296 12692           int16_t v = w->table[code - w->first_char];
297 12692 50         if (v != 0) return v;
298             }
299              
300             /* CID range list — binary search */
301 6 50         if (w->ranges && w->range_count > 0) {
    0          
302 0           size_t lo = 0, hi = w->range_count;
303             /* Find last range with range.lo <= code */
304 0 0         while (lo < hi) {
305 0           size_t mid = (lo + hi) / 2;
306 0 0         if (w->ranges[mid].lo <= code) lo = mid + 1;
307 0           else hi = mid;
308             }
309 0 0         if (lo > 0) {
310 0           const pdfmake_width_range_t *r = &w->ranges[lo - 1];
311 0 0         if (code >= r->lo && code <= r->hi) return r->width;
    0          
312             }
313             }
314              
315 6           return w->default_width;
316             }
317              
318             /*============================================================================
319             * Phase 6: Enhance widths from embedded /FontFile2 TTF data.
320             *==========================================================================*/
321              
322             /* Resolve /FontFile2 (or /FontFile3) stream from a font descriptor.
323             * Returns the decoded stream bytes (owned by `out_buf`) on success. */
324 0           static int fetch_font_file_stream(
325             struct pdfmake_reader *reader,
326             pdfmake_arena_t *arena,
327             pdfmake_obj_t *descriptor,
328             pdfmake_buf_t *out_buf)
329             {
330             uint32_t ff2_k;
331             uint32_t ff3_k;
332             pdfmake_obj_t *ref;
333 0 0         if (!descriptor || descriptor->kind != PDFMAKE_DICT) return -1;
    0          
334              
335 0           ff2_k = pdfmake_arena_intern_name(arena, "FontFile2", 9);
336 0           ff3_k = pdfmake_arena_intern_name(arena, "FontFile3", 9);
337 0           ref = pdfmake_dict_get(descriptor, ff2_k);
338 0 0         if (!ref) ref = pdfmake_dict_get(descriptor, ff3_k);
339 0 0         if (!ref || ref->kind != PDFMAKE_REF) return -1;
    0          
340              
341 0           return pdfmake_reader_resolve_stream(reader, ref->as.ref.num,
342 0           ref->as.ref.gen, out_buf) == PDFMAKE_OK
343 0 0         ? 0 : -1;
344             }
345              
346             /* Parse /CIDToGIDMap. For /Identity (or missing), returns NULL and sets
347             * is_identity=1. For a stream, returns a uint16_t[num_cids] array in arena. */
348 0           static uint16_t *resolve_cid_to_gid_map(
349             pdfmake_arena_t *arena,
350             struct pdfmake_reader *reader,
351             pdfmake_obj_t *cidfont_dict,
352             size_t *out_count,
353             int *out_is_identity)
354             {
355             uint32_t k;
356             pdfmake_obj_t *m;
357             pdfmake_buf_t buf;
358             size_t count;
359             uint16_t *map;
360             const uint8_t *d;
361             size_t i;
362              
363 0           *out_count = 0;
364 0           *out_is_identity = 0;
365              
366 0           k = pdfmake_arena_intern_name(arena, "CIDToGIDMap", 11);
367 0           m = pdfmake_dict_get(cidfont_dict, k);
368 0 0         if (!m) { *out_is_identity = 1; return NULL; }
369              
370 0 0         if (m->kind == PDFMAKE_NAME) {
371             /* /Identity is the only named form */
372 0           *out_is_identity = 1;
373 0           return NULL;
374             }
375              
376 0 0         if (m->kind != PDFMAKE_REF) return NULL;
377              
378 0 0         if (pdfmake_buf_init(&buf) != PDFMAKE_OK) return NULL;
379 0 0         if (pdfmake_reader_resolve_stream(reader, m->as.ref.num, m->as.ref.gen, &buf)
380             != PDFMAKE_OK) {
381 0           pdfmake_buf_free(&buf);
382 0           return NULL;
383             }
384              
385             /* Stream is a sequence of big-endian 2-byte GIDs indexed by CID */
386 0           count = pdfmake_buf_len(&buf) / 2;
387 0 0         if (count == 0) { pdfmake_buf_free(&buf); return NULL; }
388              
389 0           map = pdfmake_arena_alloc(arena, count * sizeof(uint16_t));
390 0 0         if (!map) { pdfmake_buf_free(&buf); return NULL; }
391              
392 0           d = pdfmake_buf_data(&buf);
393 0 0         for (i = 0; i < count; i++) {
394 0           map[i] = ((uint16_t)d[i * 2] << 8) | d[i * 2 + 1];
395             }
396 0           pdfmake_buf_free(&buf);
397 0           *out_count = count;
398 0           return map;
399             }
400              
401 46           int pdfmake_font_widths_enhance_with_ttf(
402             pdfmake_arena_t *arena,
403             struct pdfmake_reader *reader,
404             pdfmake_obj_t *font_dict,
405             int is_cid,
406             const uint32_t *byte_to_unicode,
407             pdfmake_font_widths_t *out)
408             {
409             pdfmake_obj_t *descriptor_owner;
410             uint32_t fd_k;
411             pdfmake_obj_t *descriptor;
412             pdfmake_buf_t ttf_buf;
413             pdfmake_ttf_t *ttf;
414              
415 46 50         if (!arena || !reader || !font_dict || !out) return -1;
    50          
    50          
    50          
416              
417             /* Find the font dict that owns the descriptor. For CID fonts, that's the
418             * descendant CIDFont, not the Type0 wrapper. */
419 46           descriptor_owner = font_dict;
420 46 100         if (is_cid) {
421 1           uint32_t df_k = pdfmake_arena_intern_name(arena, "DescendantFonts", 15);
422 1           pdfmake_obj_t *df_arr = pdfmake_dict_get(font_dict, df_k);
423 2 50         if (df_arr && df_arr->kind == PDFMAKE_ARRAY &&
424 1           pdfmake_array_len(df_arr) > 0) {
425 1           pdfmake_obj_t *cidfont = pdfmake_array_get(df_arr, 0);
426 1 50         if (cidfont && cidfont->kind == PDFMAKE_DICT) {
    50          
427 0           descriptor_owner = cidfont;
428             }
429             }
430             }
431              
432 46           fd_k = pdfmake_arena_intern_name(arena, "FontDescriptor", 14);
433 46           descriptor = pdfmake_dict_get(descriptor_owner, fd_k);
434 46 100         if (!descriptor || descriptor->kind != PDFMAKE_DICT) return -1;
    50          
435              
436 0 0         if (pdfmake_buf_init(&ttf_buf) != PDFMAKE_OK) return -1;
437 0 0         if (fetch_font_file_stream(reader, arena, descriptor, &ttf_buf) != 0) {
438 0           pdfmake_buf_free(&ttf_buf);
439 0           return -1;
440             }
441              
442 0           ttf = pdfmake_ttf_parse(arena,
443             pdfmake_buf_data(&ttf_buf),
444             pdfmake_buf_len(&ttf_buf));
445             /* Note: pdfmake_ttf_parse may reference the input buffer internally.
446             * We must keep ttf_buf alive for the lifetime of the TTF object. */
447 0 0         if (!ttf) {
448 0           pdfmake_buf_free(&ttf_buf);
449 0           return -1;
450             }
451              
452             /* Pull better ascent/descent from OS/2 if available */
453 0 0         if (ttf->has_os2 && ttf->units_per_em) {
    0          
454 0           out->ascent = (int16_t)((int32_t)ttf->s_typo_ascender * 1000 / ttf->units_per_em);
455 0           out->descent = (int16_t)((int32_t)ttf->s_typo_descender * 1000 / ttf->units_per_em);
456 0 0         if (ttf->s_cap_height)
457 0           out->cap_height = (int16_t)((int32_t)ttf->s_cap_height * 1000 / ttf->units_per_em);
458 0 0         if (ttf->s_x_height)
459 0           out->x_height = (int16_t)((int32_t)ttf->s_x_height * 1000 / ttf->units_per_em);
460 0 0         } else if (ttf->units_per_em) {
461 0           out->ascent = (int16_t)((int32_t)ttf->ascender * 1000 / ttf->units_per_em);
462 0           out->descent = (int16_t)((int32_t)ttf->descender * 1000 / ttf->units_per_em);
463             }
464              
465 0 0         if (is_cid) {
466             /* Build a range list: CID -> advance via /CIDToGIDMap + hmtx.
467             * Only populate CIDs that the map actually covers. */
468 0           size_t cid_count = 0;
469 0           int is_identity = 0;
470             uint16_t *cidmap;
471             size_t new_cap;
472             pdfmake_width_range_t *merged;
473             size_t out_count;
474             size_t cid;
475              
476 0           cidmap = resolve_cid_to_gid_map(arena, reader, descriptor_owner,
477             &cid_count, &is_identity);
478              
479             /* For Identity mapping, treat GID == CID up to num_glyphs. */
480 0 0         if (is_identity) {
481 0           cid_count = ttf->num_glyphs;
482             }
483 0 0         if (!is_identity && !cidmap) {
    0          
484 0           pdfmake_buf_free(&ttf_buf);
485 0           return 0; /* metrics updated, widths unchanged */
486             }
487              
488             /* Append TTF-derived widths to the existing range list, where they
489             * fill gaps. For simplicity, emit one range per CID — the lookup
490             * code binary-searches so O(log n) either way.
491             *
492             * Skip CIDs already covered by the PDF's /W array (PDF values win). */
493 0           new_cap = out->range_count + cid_count;
494 0           merged = pdfmake_arena_alloc(
495             arena, new_cap * sizeof(*merged));
496 0 0         if (!merged) { pdfmake_buf_free(&ttf_buf); return 0; }
497              
498 0 0         if (out->ranges && out->range_count)
    0          
499 0           memcpy(merged, out->ranges, out->range_count * sizeof(*merged));
500 0           out_count = out->range_count;
501              
502 0 0         for (cid = 0; cid < cid_count; cid++) {
503 0 0         uint16_t gid = is_identity ? (uint16_t)cid : cidmap[cid];
504             uint16_t w;
505             int covered;
506             size_t j;
507 0 0         if (gid == 0) continue;
508 0           w = pdfmake_ttf_glyph_advance(ttf, gid);
509 0 0         if (w == 0) continue;
510              
511             /* Check if CID is already in existing ranges */
512 0           covered = 0;
513 0 0         for (j = 0; j < out->range_count; j++) {
514 0 0         if (cid >= out->ranges[j].lo && cid <= out->ranges[j].hi) {
    0          
515 0           covered = 1;
516 0           break;
517             }
518             }
519 0 0         if (covered) continue;
520              
521 0           merged[out_count].lo = (uint32_t)cid;
522 0           merged[out_count].hi = (uint32_t)cid;
523 0           merged[out_count].width = (int16_t)w;
524 0           out_count++;
525             }
526              
527             /* Sort merged ranges for binary search */
528 0 0         if (out_count > 1) {
529 0           qsort(merged, out_count, sizeof(*merged), range_cmp);
530             }
531 0           out->ranges = merged;
532 0           out->range_count = out_count;
533             } else {
534             /* Simple font: populate a 256-entry table indexed by byte code.
535             * charcode -> Unicode (from /Encoding) -> GID (from TTF cmap)
536             * -> advance (from hmtx)
537             *
538             * Only overwrite entries that are currently 0 (unset by /Widths) —
539             * PDF's declared /Widths array takes precedence. */
540             int16_t *table;
541             uint16_t first;
542             uint16_t last;
543             size_t tbl_len;
544             int code;
545 0 0         if (!byte_to_unicode) {
546 0           pdfmake_buf_free(&ttf_buf);
547 0           return 0;
548             }
549              
550 0           table = out->table;
551 0           first = out->first_char;
552 0           last = out->last_char;
553 0 0         tbl_len = table ? (size_t)(last - first + 1) : 0;
554              
555             /* If no existing table, allocate full 256-entry one */
556 0 0         if (!table) {
557 0           table = pdfmake_arena_alloc(arena, 256 * sizeof(int16_t));
558 0 0         if (!table) { pdfmake_buf_free(&ttf_buf); return 0; }
559 0           memset(table, 0, 256 * sizeof(int16_t));
560 0           first = 0;
561 0           last = 255;
562 0           tbl_len = 256;
563 0 0         } else if (first > 0 || last < 255) {
    0          
564             /* Expand to full 256-entry so we can cover the full byte range */
565 0           int16_t *expanded = pdfmake_arena_alloc(arena, 256 * sizeof(int16_t));
566             size_t i;
567 0 0         if (!expanded) { pdfmake_buf_free(&ttf_buf); return 0; }
568 0           memset(expanded, 0, 256 * sizeof(int16_t));
569 0 0         for (i = 0; i < tbl_len; i++)
570 0           expanded[first + i] = table[i];
571 0           table = expanded;
572 0           first = 0;
573 0           last = 255;
574 0           tbl_len = 256;
575             }
576              
577             /* Fill gaps from TTF */
578 0 0         for (code = 0; code < 256; code++) {
579             uint32_t uni;
580             uint16_t gid;
581             uint16_t w;
582 0 0         if (table[code] != 0) continue; /* honour PDF /Widths */
583 0           uni = byte_to_unicode[code];
584 0 0         if (uni == 0) continue;
585 0           gid = pdfmake_ttf_cmap_lookup(ttf, uni);
586 0 0         if (gid == 0) continue;
587 0           w = pdfmake_ttf_glyph_advance(ttf, gid);
588 0 0         if (w == 0) continue;
589 0           table[code] = (int16_t)w;
590             }
591              
592 0           out->table = table;
593 0           out->first_char = first;
594 0           out->last_char = last;
595             }
596              
597             /* NB: ttf_buf backs the ttf->data pointer. Keeping it leaked-into-arena
598             * isn't possible (buf uses malloc), but the TTF parse copied nothing.
599             * For the simple font path we've already consumed all data via lookups,
600             * so freeing here is safe.
601             *
602             * Actually: pdfmake_ttf_cmap_lookup walks ttf->data, so for the CID path
603             * we might have read partial data if lookups happen after this function
604             * returns. We therefore arena-copy the buffer first.
605             *
606             * Currently all TTF reads happen inside this function, so freeing is
607             * safe. If that changes, hoist the memcpy-to-arena into parse above. */
608 0           pdfmake_buf_free(&ttf_buf);
609 0           return 0;
610             }