File Coverage

src/pdfmake_ttf.c
Criterion Covered Total %
statement 0 168 0.0
branch 0 124 0.0
condition n/a
subroutine n/a
pod n/a
total 0 292 0.0


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_ttf.c - TrueType font parser implementation
3             *
4             * Parses TrueType/OpenType fonts, extracts metrics, provides cmap lookup,
5             * and supports subsetting for PDF embedding.
6             *
7             * Reference: OpenType specification (Microsoft), TrueType reference (Apple)
8             */
9              
10             #include "pdfmake_font.h"
11             #include "pdfmake_arena.h"
12             #include "pdfmake_internal.h" /* pdfmake_read_be16 / be32 / sbe16 */
13             #include
14             #include
15              
16             /*============================================================================
17             * TTF table tags
18             *==========================================================================*/
19              
20             #define TAG(a,b,c,d) (((uint32_t)(a)<<24)|((uint32_t)(b)<<16)|((uint32_t)(c)<<8)|(d))
21              
22             #define TAG_CMAP TAG('c','m','a','p')
23             #define TAG_GLYF TAG('g','l','y','f')
24             #define TAG_HEAD TAG('h','e','a','d')
25             #define TAG_HHEA TAG('h','h','e','a')
26             #define TAG_HMTX TAG('h','m','t','x')
27             #define TAG_LOCA TAG('l','o','c','a')
28             #define TAG_MAXP TAG('m','a','x','p')
29             #define TAG_NAME TAG('n','a','m','e')
30             #define TAG_OS2 TAG('O','S','/','2')
31             #define TAG_POST TAG('p','o','s','t')
32             #define TAG_CVT TAG('c','v','t',' ')
33             #define TAG_FPGM TAG('f','p','g','m')
34             #define TAG_PREP TAG('p','r','e','p')
35              
36             /*============================================================================
37             * TTF parsing - table directory
38             *==========================================================================*/
39              
40 0           static int ttf_locate_table(const uint8_t *data, size_t data_len,
41             uint16_t num_tables, uint32_t tag,
42             uint32_t *offset, uint32_t *length) {
43             const uint8_t *p;
44             uint16_t i;
45             (void)data_len;
46 0           p = data + 12; /* Skip offset table header */
47            
48 0 0         for (i = 0; i < num_tables; i++) {
49 0           uint32_t t = pdfmake_read_be32(p);
50 0 0         if (t == tag) {
51 0           *offset = pdfmake_read_be32(p + 8);
52 0           *length = pdfmake_read_be32(p + 12);
53 0           return 1;
54             }
55 0           p += 16;
56             }
57 0           return 0;
58             }
59              
60             /*============================================================================
61             * TTF parsing main entry
62             *==========================================================================*/
63              
64 0           pdfmake_ttf_t *pdfmake_ttf_parse(pdfmake_arena_t *arena,
65             const uint8_t *data, size_t len) {
66             uint32_t sfnt_version;
67             uint16_t num_tables;
68             pdfmake_ttf_t *ttf;
69             size_t bitmap_size;
70              
71 0 0         if (!arena || !data || len < 12) return NULL;
    0          
    0          
72            
73 0           sfnt_version = pdfmake_read_be32(data);
74            
75             /* Check for valid TrueType signature */
76 0 0         if (sfnt_version != 0x00010000 && sfnt_version != TAG('t','r','u','e') &&
    0          
    0          
77             sfnt_version != TAG('O','T','T','O')) {
78 0           return NULL;
79             }
80            
81 0           num_tables = pdfmake_read_be16(data + 4);
82 0 0         if (len < 12 + num_tables * 16) return NULL;
83            
84 0           ttf = pdfmake_arena_alloc(arena, sizeof(pdfmake_ttf_t));
85 0 0         if (!ttf) return NULL;
86 0           memset(ttf, 0, sizeof(pdfmake_ttf_t));
87            
88 0           ttf->data = data;
89 0           ttf->data_len = len;
90            
91             /* Locate tables */
92             #define LOCATE(member, tag_val) \
93             ttf_locate_table(data, len, num_tables, tag_val, &ttf->member.offset, &ttf->member.length)
94            
95 0           LOCATE(head, TAG_HEAD);
96 0           LOCATE(hhea, TAG_HHEA);
97 0           LOCATE(hmtx, TAG_HMTX);
98 0           LOCATE(maxp, TAG_MAXP);
99 0           LOCATE(cmap, TAG_CMAP);
100 0           LOCATE(glyf, TAG_GLYF);
101 0           LOCATE(loca, TAG_LOCA);
102 0           LOCATE(name, TAG_NAME);
103 0           LOCATE(post, TAG_POST);
104 0           LOCATE(os2, TAG_OS2);
105 0           LOCATE(cvt, TAG_CVT);
106 0           LOCATE(fpgm, TAG_FPGM);
107 0           LOCATE(prep, TAG_PREP);
108            
109             #undef LOCATE
110            
111             /* Parse head */
112 0 0         if (ttf->head.length >= 54) {
113 0           const uint8_t *p = data + ttf->head.offset;
114 0           ttf->units_per_em = pdfmake_read_be16(p + 18);
115 0           ttf->x_min = pdfmake_read_sbe16(p + 36);
116 0           ttf->y_min = pdfmake_read_sbe16(p + 38);
117 0           ttf->x_max = pdfmake_read_sbe16(p + 40);
118 0           ttf->y_max = pdfmake_read_sbe16(p + 42);
119 0           ttf->mac_style = pdfmake_read_sbe16(p + 44);
120 0           ttf->index_to_loc_format = pdfmake_read_sbe16(p + 50);
121             }
122            
123             /* Parse maxp */
124 0 0         if (ttf->maxp.length >= 6) {
125 0           ttf->num_glyphs = pdfmake_read_be16(data + ttf->maxp.offset + 4);
126             }
127            
128             /* Parse hhea */
129 0 0         if (ttf->hhea.length >= 36) {
130 0           const uint8_t *p = data + ttf->hhea.offset;
131 0           ttf->ascender = pdfmake_read_sbe16(p + 4);
132 0           ttf->descender = pdfmake_read_sbe16(p + 6);
133 0           ttf->line_gap = pdfmake_read_sbe16(p + 8);
134 0           ttf->num_h_metrics = pdfmake_read_be16(p + 34);
135             }
136            
137             /* Parse OS/2 if present */
138 0 0         if (ttf->os2.length >= 78) {
139 0           const uint8_t *p = data + ttf->os2.offset;
140 0           ttf->has_os2 = 1;
141 0           ttf->us_weight_class = pdfmake_read_be16(p + 4);
142 0           ttf->us_width_class = pdfmake_read_be16(p + 6);
143 0           ttf->fs_selection = pdfmake_read_be16(p + 62);
144            
145 0 0         if (ttf->os2.length >= 72) {
146 0           ttf->s_typo_ascender = pdfmake_read_sbe16(p + 68);
147 0           ttf->s_typo_descender = pdfmake_read_sbe16(p + 70);
148 0           ttf->s_typo_line_gap = pdfmake_read_sbe16(p + 72);
149             }
150 0 0         if (ttf->os2.length >= 90) {
151 0           ttf->s_x_height = pdfmake_read_sbe16(p + 86);
152 0           ttf->s_cap_height = pdfmake_read_sbe16(p + 88);
153             }
154             }
155            
156             /* Find best cmap subtable */
157 0 0         if (ttf->cmap.length >= 4) {
158 0           const uint8_t *p = data + ttf->cmap.offset;
159 0           uint16_t num_subtables = pdfmake_read_be16(p + 2);
160             uint16_t i;
161            
162 0           p += 4;
163 0 0         for (i = 0; i < num_subtables && ttf->cmap.offset + 4 + i*8 + 8 <= len; i++) {
    0          
164 0           uint16_t platform = pdfmake_read_be16(p);
165 0           uint16_t encoding = pdfmake_read_be16(p + 2);
166 0           uint32_t subtable_offset = pdfmake_read_be32(p + 4);
167            
168 0 0         if (ttf->cmap.offset + subtable_offset + 2 <= len) {
169 0           uint16_t format = pdfmake_read_be16(data + ttf->cmap.offset + subtable_offset);
170            
171             /* Prefer format 12 (full Unicode) then format 4 (BMP) */
172 0 0         if (format == 12 && ((platform == 3 && encoding == 10) ||
    0          
    0          
    0          
173 0 0         (platform == 0 && encoding >= 3))) {
174 0           ttf->cmap_format = 12;
175 0           ttf->cmap_offset = ttf->cmap.offset + subtable_offset;
176 0 0         } else if (format == 4 && ttf->cmap_format != 12 &&
    0          
    0          
177 0 0         ((platform == 3 && encoding == 1) ||
    0          
178 0 0         (platform == 0 && encoding <= 3))) {
179 0           ttf->cmap_format = 4;
180 0           ttf->cmap_offset = ttf->cmap.offset + subtable_offset;
181             }
182             }
183 0           p += 8;
184             }
185             }
186            
187             /* Allocate used glyph bitmap */
188 0           bitmap_size = (ttf->num_glyphs + 7) / 8;
189 0           ttf->used_glyphs = pdfmake_arena_alloc(arena, bitmap_size);
190 0 0         if (ttf->used_glyphs) {
191 0           memset(ttf->used_glyphs, 0, bitmap_size);
192             }
193 0           ttf->used_count = 0;
194            
195 0           return ttf;
196             }
197              
198             /*============================================================================
199             * cmap format 4 decoder (BMP)
200             *==========================================================================*/
201              
202 0           static uint16_t cmap_format4_lookup(const pdfmake_ttf_t *ttf, uint32_t codepoint) {
203             const uint8_t *p;
204             uint16_t length, seg_count_x2, seg_count;
205             const uint8_t *end_codes, *start_codes, *id_deltas, *id_range_offsets;
206             uint16_t lo, hi;
207             uint16_t end_code, start_code;
208             int16_t id_delta;
209             uint16_t id_range_offset;
210             uint16_t glyph_id;
211 0 0         if (codepoint > 0xFFFF) return 0; /* Format 4 only handles BMP */
212            
213 0           p = ttf->data + ttf->cmap_offset;
214            
215 0           length = pdfmake_read_be16(p + 2);
216 0           seg_count_x2 = pdfmake_read_be16(p + 6);
217 0           seg_count = seg_count_x2 / 2;
218            
219 0 0         if (ttf->cmap_offset + length > ttf->data_len) return 0;
220            
221 0           end_codes = p + 14;
222 0           start_codes = end_codes + seg_count_x2 + 2;
223 0           id_deltas = start_codes + seg_count_x2;
224 0           id_range_offsets = id_deltas + seg_count_x2;
225            
226             /* Binary search for segment */
227 0           lo = 0; hi = seg_count;
228 0 0         while (lo < hi) {
229 0           uint16_t mid = lo + (hi - lo) / 2;
230 0           uint16_t mid_end = pdfmake_read_be16(end_codes + mid * 2);
231 0 0         if (mid_end < codepoint) {
232 0           lo = mid + 1;
233             } else {
234 0           hi = mid;
235             }
236             }
237            
238 0 0         if (lo >= seg_count) return 0;
239            
240 0           end_code = pdfmake_read_be16(end_codes + lo * 2);
241 0           start_code = pdfmake_read_be16(start_codes + lo * 2);
242            
243 0 0         if (codepoint < start_code || codepoint > end_code) return 0;
    0          
244            
245 0           id_delta = pdfmake_read_sbe16(id_deltas + lo * 2);
246 0           id_range_offset = pdfmake_read_be16(id_range_offsets + lo * 2);
247            
248 0 0         if (id_range_offset == 0) {
249 0           glyph_id = (uint16_t)((codepoint + id_delta) & 0xFFFF);
250             } else {
251 0           const uint8_t *glyph_addr = id_range_offsets + lo * 2 + id_range_offset +
252 0           (codepoint - start_code) * 2;
253 0 0         if (glyph_addr >= ttf->data + ttf->data_len - 1) return 0;
254 0           glyph_id = pdfmake_read_be16(glyph_addr);
255 0 0         if (glyph_id != 0) {
256 0           glyph_id = (uint16_t)((glyph_id + id_delta) & 0xFFFF);
257             }
258             }
259            
260 0           return glyph_id;
261             }
262              
263             /*============================================================================
264             * cmap format 12 decoder (full Unicode)
265             *==========================================================================*/
266              
267 0           static uint16_t cmap_format12_lookup(const pdfmake_ttf_t *ttf, uint32_t codepoint) {
268 0           const uint8_t *p = ttf->data + ttf->cmap_offset;
269             const uint8_t *groups;
270             uint32_t length, num_groups;
271             uint32_t lo, hi;
272             uint32_t start_code, end_code, start_glyph;
273            
274 0           length = pdfmake_read_be32(p + 4);
275 0           num_groups = pdfmake_read_be32(p + 12);
276            
277 0 0         if (ttf->cmap_offset + length > ttf->data_len) return 0;
278 0 0         if (length < 16 + num_groups * 12) return 0;
279            
280 0           groups = p + 16;
281            
282             /* Binary search */
283 0           lo = 0; hi = num_groups;
284 0 0         while (lo < hi) {
285 0           uint32_t mid = lo + (hi - lo) / 2;
286 0           uint32_t mid_end = pdfmake_read_be32(groups + mid * 12 + 4);
287 0 0         if (mid_end < codepoint) {
288 0           lo = mid + 1;
289             } else {
290 0           hi = mid;
291             }
292             }
293            
294 0 0         if (lo >= num_groups) return 0;
295            
296 0           start_code = pdfmake_read_be32(groups + lo * 12);
297 0           end_code = pdfmake_read_be32(groups + lo * 12 + 4);
298 0           start_glyph = pdfmake_read_be32(groups + lo * 12 + 8);
299            
300 0 0         if (codepoint < start_code || codepoint > end_code) return 0;
    0          
301            
302 0           return (uint16_t)(start_glyph + (codepoint - start_code));
303             }
304              
305             /*============================================================================
306             * cmap lookup (public API)
307             *==========================================================================*/
308              
309 0           uint16_t pdfmake_ttf_cmap_lookup(const pdfmake_ttf_t *ttf, uint32_t codepoint) {
310 0 0         if (!ttf || ttf->cmap_offset == 0) return 0;
    0          
311            
312 0 0         if (ttf->cmap_format == 12) {
313 0           return cmap_format12_lookup(ttf, codepoint);
314 0 0         } else if (ttf->cmap_format == 4) {
315 0           return cmap_format4_lookup(ttf, codepoint);
316             }
317            
318 0           return 0;
319             }
320              
321             /*============================================================================
322             * Glyph advance width lookup
323             *==========================================================================*/
324              
325 0           uint16_t pdfmake_ttf_glyph_advance(const pdfmake_ttf_t *ttf, uint16_t glyph_id) {
326             const uint8_t *hmtx;
327             uint16_t advance;
328 0 0         if (!ttf || glyph_id >= ttf->num_glyphs) return 0;
    0          
329 0 0         if (ttf->hmtx.offset == 0) return 0;
330            
331 0           hmtx = ttf->data + ttf->hmtx.offset;
332            
333 0 0         if (glyph_id < ttf->num_h_metrics) {
334 0           advance = pdfmake_read_be16(hmtx + glyph_id * 4);
335             } else {
336 0           advance = pdfmake_read_be16(hmtx + (ttf->num_h_metrics - 1) * 4);
337             }
338            
339             /* Convert from font units to 1/1000 em */
340 0 0         if (ttf->units_per_em == 0) return advance;
341 0           return (advance * 1000) / ttf->units_per_em;
342             }
343              
344             /*============================================================================
345             * Mark glyph as used (for subsetting)
346             *==========================================================================*/
347              
348 0           void pdfmake_ttf_mark_glyph(pdfmake_ttf_t *ttf, uint16_t glyph_id) {
349             size_t byte;
350             uint8_t bit;
351 0 0         if (!ttf || !ttf->used_glyphs) return;
    0          
352 0 0         if (glyph_id >= ttf->num_glyphs) return;
353            
354 0           byte = glyph_id / 8;
355 0           bit = 1 << (glyph_id % 8);
356            
357 0 0         if (!(ttf->used_glyphs[byte] & bit)) {
358 0           ttf->used_glyphs[byte] |= bit;
359 0           ttf->used_count++;
360             }
361             }