Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsttf.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* XPS interpreter - truetype font support */
18
19
#include "ghostxps.h"
20
21
#include <gxfont.h>
22
#include "xpsfapi.h"
23
24
/*
25
 * Some extra TTF parsing magic that isn't covered by the graphics library.
26
 */
27
28
static inline int u16(const byte *p)
29
0
{
30
0
        return (p[0] << 8) | p[1];
31
0
}
32
33
static inline int u32(const byte *p)
34
0
{
35
0
        return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
36
0
}
37
38
static const char *pl_mac_names[258] = {
39
    ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl",
40
    "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft",
41
    "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash",
42
    "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
43
    "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at",
44
    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
45
    "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
46
    "backslash", "bracketright", "asciicircum", "underscore", "grave", "a",
47
    "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
48
    "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar",
49
    "braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute",
50
    "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex",
51
    "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave",
52
    "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis",
53
    "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde",
54
    "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent",
55
    "sterling", "section", "bullet", "paragraph", "germandbls", "registered",
56
    "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash",
57
    "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu",
58
    "partialdiff", "summation", "product", "pi", "integral", "ordfeminine",
59
    "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown",
60
    "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft",
61
    "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde",
62
    "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright",
63
    "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis",
64
    "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl",
65
    "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase",
66
    "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis",
67
    "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute",
68
    "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave",
69
    "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring",
70
    "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron",
71
    "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute",
72
    "yacute", "Thorn", "thorn", "minus", "multiply", "onesuperior",
73
    "twosuperior", "threesuperior", "onehalf", "onequarter", "threequarters",
74
    "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla", "scedilla",
75
    "Cacute", "cacute", "Ccaron", "ccaron", "dcroat"
76
};
77
78
/*
79
 * A bunch of callback functions that the ghostscript
80
 * font machinery will call. The most important one
81
 * is the build_char function. These are specific to
82
 * truetype (loca/glyf) flavored opentypes.
83
 */
84
85
static unsigned int
86
xps_true_get_glyph_index(gs_font_type42 *pfont42, gs_glyph glyph)
87
0
{
88
    /* identity */
89
0
    return glyph;
90
0
}
91
92
static int
93
xps_true_callback_string_proc(gs_font_type42 *p42, ulong offset, uint length, const byte **pdata)
94
459
{
95
459
    xps_font_t *font = p42->client_data;
96
459
    if (offset + length > font->length)
97
0
    {
98
0
        *pdata = NULL;
99
0
        return gs_throw2(-1, "font data access out of bounds (offset=%lu size=%u)", offset, length);
100
0
    }
101
459
    *pdata = font->data + offset;
102
459
    return 0;
103
459
}
104
105
static gs_glyph
106
xps_true_callback_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t spc)
107
0
{
108
0
    xps_font_t *font = pfont->client_data;
109
0
    int value;
110
111
0
    value = xps_encode_font_char(font, chr);
112
0
    if (value == 0)
113
0
        return GS_NO_GLYPH;
114
0
    return value;
115
0
}
116
117
static int
118
xps_true_callback_decode_glyph(gs_font *pfont, gs_glyph glyph, int ch, ushort *unicode_return, unsigned int length)
119
0
{
120
0
    xps_font_t *font = pfont->client_data;
121
0
    char *ur = (char *)unicode_return;
122
0
    int u;
123
124
0
    if (length == 0)
125
0
        return 2;
126
0
    u = xps_decode_font_char(font, glyph);
127
    /* Unfortunate assumptions in the pdfwrite code mea that we have to return the
128
     * value as a big-endian short, no matter what platform we are on
129
     */
130
0
    ur[1] = u & 0xff;
131
0
    ur[0] = u >> 8;
132
0
    return 2;
133
0
}
134
135
static int
136
xps_true_callback_glyph_name(gs_font *pfont, gs_glyph glyph, gs_const_string *pstr)
137
0
{
138
    /* This function is copied verbatim from plfont.c */
139
140
0
    int table_length;
141
0
    int table_offset;
142
143
0
    ulong format;
144
0
    int numGlyphs;
145
0
    uint glyph_name_index;
146
0
    const byte *postp, *indexp; /* post table pointer */
147
0
    xps_font_t *font = pfont->client_data;
148
149
0
    if (glyph >= GS_MIN_GLYPH_INDEX) {
150
0
        glyph -= GS_MIN_GLYPH_INDEX;
151
0
    }
152
153
    /* guess if the font type is not truetype */
154
0
    if ( pfont->FontType != ft_TrueType )
155
0
    {
156
0
        glyph -= 29;
157
0
        if (glyph < 258 )
158
0
        {
159
0
            pstr->data = (byte*) pl_mac_names[glyph];
160
0
            pstr->size = strlen((char*)pstr->data);
161
0
            return 0;
162
0
        }
163
0
        else
164
0
        {
165
0
            return gs_throw1(-1, "glyph index %lu out of range", (ulong)glyph);
166
0
        }
167
0
    }
168
169
0
    table_offset = xps_find_sfnt_table((xps_font_t*)pfont->client_data, "post", &table_length);
170
171
    /* no post table */
172
0
    if (table_offset < 0)
173
0
        return gs_throw(-1, "no post table");
174
175
    /* this shoudn't happen but... */
176
0
    if ( table_length == 0 )
177
0
        return gs_throw(-1, "zero-size post table");
178
179
0
    ((gs_font_type42 *)pfont)->data.string_proc((gs_font_type42 *)pfont,
180
0
                                                table_offset, table_length, &postp);
181
0
    format = u32(postp);
182
183
    /* Format 1.0 (mac encoding) is a simple table see the TT spec.
184
     * We don't implement this because we don't see it in practice.
185
     * Format 2.5 is deprecated.
186
     * Format 3.0 means that there is no post data in the font file.
187
     * We see this a lot but can't do much about it.
188
     * The only format we support is 2.0.
189
     */
190
0
    if ( format != 0x20000 )
191
0
    {
192
        /* Invent a name if we don't know the table format. */
193
0
        char buf[32];
194
0
        gs_snprintf(buf, sizeof(buf), "glyph%d", (int)glyph);
195
196
        /* Ugly hackery. see comment below, after 'not mac' this ends up as a memory leak.
197
         * The PostScript interpreter adds the strings it creates to the PostScript name table
198
         * which is cleared and freed at EOJ. Presumably because these functions were
199
         * written with PostScript in mind, there is no provision for there not to be a
200
         * persistent copy of the name data, so we have to make one, which means it leaks.
201
         */
202
0
        pstr->size = strlen(buf);
203
0
        pstr->data = gs_alloc_bytes(pfont->memory, (size_t)pstr->size + 1, "glyph to name");
204
0
        if ( pstr->data == 0 )
205
0
            return -1;
206
207
0
        memset((byte *)pstr->data, 0x00, pstr->size + 1);
208
0
        memcpy((byte *)pstr->data, buf, pstr->size);
209
0
        return 0;
210
0
    }
211
212
    /* skip over the post header */
213
0
    numGlyphs = (int)u16(postp + 32);
214
0
    if ((int)glyph > numGlyphs - 1)
215
0
    {
216
0
        return gs_throw1(-1, "glyph index %lu out of range", (ulong)glyph);
217
0
    }
218
219
    /* glyph name index starts at post + 34 each entry is 2 bytes */
220
0
    indexp = postp + 34 + (glyph * 2);
221
0
    if (indexp > (postp + table_length - 2))
222
0
        return gs_throw(-1, "post table format error");
223
224
0
    glyph_name_index = u16(indexp);
225
226
    /* this shouldn't happen */
227
0
    if ( glyph_name_index > 0x7fff )
228
0
        return gs_throw(-1, "post table format error");
229
230
    /* mac easy */
231
0
    if ( glyph_name_index < 258 )
232
0
    {
233
        /* dmprintf2(pfont->memory, "glyph name (mac) %d = %s\n", glyph, pl_mac_names[glyph_name_index]); */
234
0
        pstr->data = (byte*) pl_mac_names[glyph_name_index];
235
0
        pstr->size = strlen((char*)pstr->data);
236
0
        return 0;
237
0
    }
238
239
    /* not mac */
240
0
    else
241
0
    {
242
0
        byte *mydata;
243
244
        /* and here's the tricky part */
245
0
        const byte *pascal_stringp = postp + 34 + (numGlyphs * 2);
246
247
        /* 0 - 257 lives in the mac table above */
248
0
        glyph_name_index -= 258;
249
250
        /* The string we want is the index'th pascal string,
251
         * so we "hop" to each length byte "index" times. */
252
0
        while (glyph_name_index > 0)
253
0
        {
254
0
            pascal_stringp += ((int)(*pascal_stringp)+1);
255
0
            glyph_name_index--;
256
0
            if (pascal_stringp >= postp + table_length)
257
0
                return gs_throw(-1, "data out of range");
258
0
        }
259
260
        /* length byte */
261
0
        pstr->size = (int)(*pascal_stringp);
262
263
        /* + 1 is for the length byte */
264
0
        pstr->data = pascal_stringp + 1;
265
266
        /* sanity check */
267
0
        if ( pstr->data + pstr->size > postp + table_length || pstr->data - 1 < postp)
268
0
            return gs_throw(-1, "data out of range");
269
270
        /* sigh - we have to allocate a copy of the data - by the
271
           time a high level device makes use of it the font data
272
           may be freed.  Track the allocated memory in our
273
           font 'wrapper' so we can free it when we free tha font wrapper.
274
         */
275
0
        mydata = gs_alloc_bytes(pfont->memory, (size_t)pstr->size + 1, "glyph to name");
276
0
        if ( mydata == 0 )
277
0
            return -1;
278
0
        memcpy(mydata, pascal_stringp + 1, pstr->size);
279
0
        pstr->data = mydata;
280
281
0
        mydata[pstr->size] = 0;
282
283
0
        if (font->names == NULL) {
284
0
            font->names = (char **)gs_alloc_bytes(pfont->memory, (size_t)256 * sizeof (char *), "names storage");
285
0
            if (font->names == NULL) {
286
0
                gs_free_object(pfont->memory, (byte *)pstr->data, "free string on error");
287
0
                pstr->data = NULL;
288
0
                pstr->size = 0;
289
0
                return -1;
290
0
            }
291
0
            font->max_name_index = 255;
292
0
            font->next_name_index = 0;
293
0
            memset(font->names, 0x00, 256 * sizeof (char *));
294
0
        }
295
0
        if (font->next_name_index > font->max_name_index) {
296
0
            char **temp = NULL;
297
0
            temp = (char **)gs_alloc_bytes(pfont->memory, ((size_t)font->max_name_index + 256) * sizeof (char *), "names storage");
298
0
            if (temp == NULL) {
299
0
                gs_free_object(pfont->memory, (byte *)pstr->data, "free string on error");
300
0
                pstr->data = NULL;
301
0
                pstr->size = 0;
302
0
                return -1;
303
0
            }
304
0
            memset(temp, 0x00, (font->max_name_index + 256) * sizeof (char *));
305
0
            memcpy(temp, font->names, font->max_name_index * sizeof(char *));
306
0
            gs_free_object(pfont->memory, (void *)font->names, "realloc names storage");
307
0
            font->names = temp;
308
0
            font->max_name_index += 256;
309
0
        }
310
0
        font->names[font->next_name_index++] = (char *)pstr->data;
311
0
        return 0;
312
0
    }
313
0
}
314
315
static int
316
xps_true_callback_build_char(gs_show_enum *penum, gs_gstate *pgs, gs_font *pfont,
317
        gs_char chr, gs_glyph glyph)
318
0
{
319
0
    gs_font_type42 *p42 = (gs_font_type42*)pfont;
320
0
    const gs_rect *pbbox;
321
0
    float sbw[4], w2[6];
322
0
    gs_fixed_point saved_adjust;
323
0
    int code;
324
325
    /* dmprintf1(pfont->memory, "build char ttf %d\n", glyph); */
326
327
0
    code = gs_type42_get_metrics(p42, glyph, sbw);
328
0
    if (code < 0)
329
0
        return code;
330
331
0
    w2[0] = sbw[2];
332
0
    w2[1] = sbw[3];
333
334
0
    pbbox =  &p42->FontBBox;
335
0
    w2[2] = pbbox->p.x;
336
0
    w2[3] = pbbox->p.y;
337
0
    w2[4] = pbbox->q.x;
338
0
    w2[5] = pbbox->q.y;
339
340
    /* Expand the bbox when stroking */
341
0
    if ( pfont->PaintType )
342
0
    {
343
0
        float expand = max(1.415, gs_currentmiterlimit(pgs)) * gs_currentlinewidth(pgs) / 2;
344
0
        w2[2] -= expand, w2[3] -= expand;
345
0
        w2[4] += expand, w2[5] += expand;
346
0
    }
347
348
0
    if ( (code = gs_moveto(pgs, 0.0, 0.0)) < 0 )
349
0
        return code;
350
351
0
    if ( (code = gs_setcachedevice(penum, pgs, w2)) < 0 )
352
0
        return code;
353
354
0
    code = gs_type42_append(glyph, pgs,
355
0
            gx_current_path(pgs),
356
0
            (gs_text_enum_t*)penum, (gs_font*)p42,
357
0
            gs_show_in_charpath(penum) != cpm_show);
358
0
    if (code < 0)
359
0
        return code;
360
361
    /* Indicate that dropout prevention should be enabled by setting
362
        fill_adjust to the special value -1. */
363
0
    saved_adjust = pgs->fill_adjust;
364
0
    pgs->fill_adjust.x = -1;
365
0
    pgs->fill_adjust.y = -1;
366
367
0
    code = (pfont->PaintType ? gs_stroke(pgs) : gs_fill(pgs));
368
0
    if (code < 0)
369
0
        return code;
370
371
0
    pgs->fill_adjust = saved_adjust;
372
373
0
    return 0;
374
0
}
375
376
/*
377
 * Initialize the ghostscript font machinery for a truetype
378
 * (type42 in postscript terminology) font.
379
 */
380
381
int
382
xps_init_truetype_font(xps_context_t *ctx, xps_font_t *font)
383
2
{
384
2
    int code = 0;
385
386
2
    font->font = (void*) gs_alloc_struct(ctx->memory, gs_font_type42, &st_gs_font_type42, "xps_font type42");
387
2
    if (!font->font)
388
0
        return gs_throw(gs_error_VMerror, "out of memory");
389
390
    /* no shortage of things to initialize */
391
2
    {
392
2
        gs_font_type42 *p42 = (gs_font_type42*) font->font;
393
394
        /* Common to all fonts: */
395
396
2
        p42->next = 0;
397
2
        p42->prev = 0;
398
2
        p42->memory = ctx->memory;
399
400
2
        p42->dir = ctx->fontdir; /* NB also set by gs_definefont later */
401
2
        p42->base = font->font; /* NB also set by gs_definefont later */
402
2
        p42->is_resource = false;
403
2
        gs_notify_init(&p42->notify_list, gs_memory_stable(ctx->memory));
404
2
        p42->id = gs_next_ids(ctx->memory, 1);
405
406
2
        p42->client_data = font; /* that's us */
407
408
        /* this is overwritten in grid_fit() */
409
2
        gs_make_identity(&p42->FontMatrix);
410
2
        gs_make_identity(&p42->orig_FontMatrix); /* NB ... original or zeroes? */
411
412
2
        p42->FontType = ft_TrueType;
413
2
        p42->BitmapWidths = false;
414
2
        p42->ExactSize = fbit_use_outlines;
415
2
        p42->InBetweenSize = fbit_use_outlines;
416
2
        p42->TransformedChar = fbit_use_outlines;
417
2
        p42->WMode = 0;
418
2
        p42->PaintType = 0;
419
2
        p42->StrokeWidth = 0;
420
2
        p42->is_cached = 0;
421
422
2
        p42->procs.define_font = gs_no_define_font;
423
2
        p42->procs.make_font = gs_no_make_font;
424
2
        p42->procs.font_info = gs_type42_font_info;
425
2
        p42->procs.same_font = gs_default_same_font;
426
2
        p42->procs.encode_char = xps_true_callback_encode_char;
427
2
        p42->procs.decode_glyph = xps_true_callback_decode_glyph;
428
2
        p42->procs.enumerate_glyph = gs_type42_enumerate_glyph;
429
2
        p42->procs.glyph_info = gs_type42_glyph_info;
430
2
        p42->procs.glyph_outline = gs_type42_glyph_outline;
431
2
        p42->procs.glyph_name = xps_true_callback_glyph_name;
432
2
        p42->procs.init_fstack = gs_default_init_fstack;
433
2
        p42->procs.next_char_glyph = gs_default_next_char_glyph;
434
2
        p42->procs.build_char = xps_true_callback_build_char;
435
436
2
        memset(p42->font_name.chars, 0, sizeof(p42->font_name.chars));
437
2
        xps_load_sfnt_name(font, (char*)p42->font_name.chars, sizeof(p42->font_name.chars));
438
2
        p42->font_name.size = strlen((char*)p42->font_name.chars);
439
440
2
        memset(p42->key_name.chars, 0, sizeof(p42->key_name.chars));
441
2
        strcpy((char*)p42->key_name.chars, (char*)p42->font_name.chars);
442
2
        p42->key_name.size = strlen((char*)p42->key_name.chars);
443
444
        /* Base font specific: */
445
446
2
        p42->FontBBox.p.x = 0;
447
2
        p42->FontBBox.p.y = 0;
448
2
        p42->FontBBox.q.x = 0;
449
2
        p42->FontBBox.q.y = 0;
450
451
2
        uid_set_UniqueID(&p42->UID, p42->id);
452
453
2
        p42->encoding_index = ENCODING_INDEX_UNKNOWN;
454
2
        p42->nearest_encoding_index = ENCODING_INDEX_ISOLATIN1;
455
456
2
        p42->FAPI = 0;
457
2
        p42->FAPI_font_data = 0;
458
459
        /* Type 42 specific: */
460
461
2
        p42->data.string_proc = xps_true_callback_string_proc;
462
2
        p42->data.proc_data = font;
463
464
2
        code = gs_type42_font_init(p42, font->subfontid);
465
2
        if (code < 0)
466
0
            return code;
467
2
        p42->data.get_glyph_index = xps_true_get_glyph_index;
468
2
    }
469
470
2
    if ((code = gs_definefont(ctx->fontdir, font->font)) < 0) {
471
0
        return(code);
472
0
    }
473
474
2
    code = xps_fapi_passfont (font->font, NULL, NULL, font->data, font->length);
475
2
    return code;
476
2
}