Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsfont.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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 - general font functions */
18
19
#include "ghostxps.h"
20
21
static void xps_load_sfnt_cmap(xps_font_t *font);
22
23
/*
24
 * Big-endian memory accessor functions
25
 */
26
27
static inline int s16(byte *p)
28
264
{
29
264
    return (signed short)( (p[0] << 8) | p[1] );
30
264
}
31
32
static inline int u16(byte *p)
33
736
{
34
736
    return (p[0] << 8) | p[1];
35
736
}
36
37
static inline int u24(byte *p)
38
0
{
39
0
    return (p[0] << 16) | (p[1] << 8) | p[2];
40
0
}
41
42
static inline int u32(byte *p)
43
538
{
44
    /* Casts to avoid UB here. Anything smaller than an int (such as a byte (unsigned char)) is promoted to a signed int before shifting.
45
     * so p[0] << 24 where p[0] = 0x80 is officially UB. To avoid this, cast to uint32_t first. If uint32_t is smaller than an int,
46
     * then p[0] << 24 won't overflow, so no problems. If uint32_t is the same size as int, we get no UB. We then cast the result to
47
     * int32_t (no change), and then to int (to sign extend if required). It would have been much nicer if this function had been written
48
     * to return uint32_t in the first place! */
49
538
    return (int)(int32_t)(((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3]);
50
538
}
51
52
xps_font_t *
53
xps_new_font(xps_context_t *ctx, byte *buf, int buflen, int index)
54
2
{
55
2
    xps_font_t *font;
56
2
    int code;
57
58
2
    font = xps_alloc(ctx, sizeof(xps_font_t));
59
2
    if (!font)
60
0
    {
61
0
        gs_throw(gs_error_VMerror, "out of memory");
62
0
        return NULL;
63
0
    }
64
65
2
    font->data = buf;
66
2
    font->length = buflen;
67
2
    font->font = NULL;
68
69
2
    font->subfontid = index;
70
2
    font->cmaptable = 0;
71
2
    font->cmapsubcount = 0;
72
2
    font->cmapsubtable = 0;
73
2
    font->usepua = 0;
74
75
2
    font->cffdata = 0;
76
2
    font->cffend = 0;
77
2
    font->gsubrs = 0;
78
2
    font->subrs = 0;
79
2
    font->charstrings = 0;
80
2
    font->names = NULL;
81
2
    font->max_name_index = font->next_name_index = 0;
82
83
2
    if (memcmp(font->data, "OTTO", 4) == 0)
84
0
        code = xps_init_postscript_font(ctx, font);
85
2
    else if (memcmp(font->data, "\0\1\0\0", 4) == 0)
86
2
        code = xps_init_truetype_font(ctx, font);
87
0
    else if (memcmp(font->data, "true", 4) == 0)
88
0
        code = xps_init_truetype_font(ctx, font);
89
0
    else if (memcmp(font->data, "ttcf", 4) == 0)
90
0
        code = xps_init_truetype_font(ctx, font);
91
0
    else
92
0
    {
93
0
        xps_free_font(ctx, font);
94
0
        gs_throw(-1, "not an opentype font");
95
0
        return NULL;
96
0
    }
97
98
2
    if (code < 0)
99
0
    {
100
0
        xps_free_font(ctx, font);
101
0
        gs_rethrow(-1, "cannot init font");
102
0
        return NULL;
103
0
    }
104
105
2
    xps_load_sfnt_cmap(font);
106
107
2
    return font;
108
2
}
109
110
void
111
xps_free_font(xps_context_t *ctx, xps_font_t *font)
112
2
{
113
2
    if (font == NULL)
114
0
        return;
115
2
    if (font->names != NULL) {
116
0
        int i = 0;
117
0
        for (i = 0;i < font->next_name_index; i++)
118
0
            gs_free_object(font->font->memory, font->names[i], "freeing names table");
119
0
        gs_free_object(font->font->memory, font->names, "free names table");
120
0
        font->names = NULL;
121
0
        font->max_name_index = font->next_name_index = 0;
122
0
    }
123
2
    if (font->font)
124
2
    {
125
2
        gs_font_finalize(ctx->memory, font->font);
126
2
        gs_free_object(ctx->memory, font->font, "font object");
127
2
    }
128
2
    xps_free(ctx, font->data);
129
2
    xps_free(ctx, font);
130
2
}
131
132
/*
133
 * Find the offset and length of an SFNT table.
134
 * Return -1 if no table by the specified name is found.
135
 */
136
137
int
138
xps_find_sfnt_table(xps_font_t *font, const char *name, int *lengthp)
139
400
{
140
400
    int offset;
141
400
    int ntables;
142
400
    int i;
143
144
400
    if (font->length < 12)
145
0
        return -1;
146
147
400
    if (!memcmp(font->data, "ttcf", 4))
148
0
    {
149
0
        int nfonts = u32(font->data + 8);
150
151
        /* check if the buffer contains enough data to contain nfonts subfonts */
152
0
        int min_len = 12 + nfonts * 4;
153
154
0
        if (nfonts < 0)
155
0
        {
156
0
            gs_warn("nfonts out of bounds");
157
0
            return -1;
158
0
        }
159
160
0
        if (min_len < 0 || font->length < min_len)
161
0
        {
162
0
            gs_warn("font data length too small");
163
0
            return -1;
164
0
        }
165
166
0
        if (font->subfontid < 0 || font->subfontid >= nfonts)
167
0
        {
168
0
            gs_warn("Invalid subfont ID");
169
0
            return -1;
170
0
        }
171
0
        offset = u32(font->data + 12 + font->subfontid * 4);
172
0
        if (offset < 0)
173
0
        {
174
0
            gs_warn("subfont table offset negative");
175
0
            return -1;
176
0
        }
177
0
    }
178
400
    else
179
400
    {
180
400
        offset = 0;
181
400
    }
182
183
400
    if (font->length - 6 < offset)
184
0
    {
185
0
        gs_warn("subfont length insufficient for ntables read");
186
0
        return -1;
187
188
0
    }
189
400
    ntables = u16(font->data + offset + 4);
190
400
    if (font->length < offset + 12 + ntables * 16)
191
0
    {
192
0
        gs_warn("subfont length insufficient for entry reads");
193
0
        return -1;
194
0
    }
195
2.53k
    for (i = 0; i < ntables; i++)
196
2.39k
    {
197
2.39k
        byte *entry = font->data + offset + 12 + i * 16;
198
2.39k
        if (!memcmp(entry, name, 4))
199
268
        {
200
268
            int ofs = u32(entry + 8);
201
268
            int len = u32(entry + 12);
202
268
            if (ofs < 0 || len < 0 || len + ofs < 0 || len + ofs > font->length)
203
0
            {
204
0
                gs_warn("table length invalid");
205
0
                return -1;
206
0
            }
207
268
            if (lengthp)
208
268
                *lengthp = len;
209
268
            return ofs;
210
268
        }
211
2.39k
    }
212
213
132
    return -1;
214
400
}
215
216
/*
217
 * Get the windows truetype font file name - position 4 in the name table.
218
 */
219
void
220
xps_load_sfnt_name(xps_font_t *font, char *namep, const int buflen)
221
2
{
222
2
    byte *namedata;
223
2
    int offset, length;
224
    /*int format;*/
225
2
    int count, stringoffset;
226
2
    int found;
227
2
    int i, k;
228
229
2
    found = 0;
230
2
    strcpy(namep, "Unknown");
231
232
2
    offset = xps_find_sfnt_table(font, "name", &length);
233
2
    if (offset < 0 || length < 6)
234
0
    {
235
0
        gs_warn("cannot find name table");
236
0
        return;
237
0
    }
238
239
    /* validate the offset, and the data for the two
240
     * values we're about to read
241
     */
242
2
    if (offset + 6 > font->length)
243
0
    {
244
0
        gs_warn("name table byte offset invalid");
245
0
        return;
246
0
    }
247
2
    namedata = font->data + offset;
248
249
    /*format = u16(namedata + 0);*/
250
2
    count = u16(namedata + 2);
251
2
    stringoffset = u16(namedata + 4);
252
253
2
    if (stringoffset + offset > font->length
254
2
        || offset + 6 + count * 12 > font->length)
255
1
    {
256
1
        gs_warn("name table invalid");
257
1
        return;
258
1
    }
259
260
1
    if (length < 6 + (count * 12))
261
0
    {
262
0
        gs_warn("name table too short");
263
0
        return;
264
0
    }
265
266
13
    for (i = 0; i < count; i++)
267
12
    {
268
12
        byte *record = namedata + 6 + i * 12;
269
12
        int pid = u16(record + 0);
270
12
        int eid = u16(record + 2);
271
12
        int langid = u16(record + 4);
272
12
        int nameid = u16(record + 6);
273
12
        length = u16(record + 8);
274
12
        offset = u16(record + 10);
275
276
12
        length = length > buflen - 1 ? buflen - 1: length;
277
12
        if (namedata + stringoffset + offset >= font->data + font->length)
278
0
            continue;
279
280
12
        if (namedata + stringoffset + offset + length >= font->data +  font->length)
281
0
            length = (font->data +  font->length) - (namedata + stringoffset + offset);
282
283
        /* Full font name or postscript name */
284
12
        if (nameid == 4 || nameid == 6)
285
4
        {
286
4
            if (pid == 1 && eid == 0 && langid == 0) /* mac roman, english */
287
2
            {
288
2
                if (found < 3)
289
1
                {
290
1
                    memcpy(namep, namedata + stringoffset + offset, length);
291
1
                    namep[length] = 0;
292
1
                    found = 3;
293
1
                }
294
2
            }
295
296
4
            if (pid == 3 && eid == 1 && langid == 0x409) /* windows unicode ucs-2, US */
297
0
            {
298
0
                if (found < 2)
299
0
                {
300
0
                    unsigned char *s = namedata + stringoffset + offset;
301
0
                    int n = length / 2;
302
0
                    for (k = 0; k < n; k ++)
303
0
                    {
304
0
                        int c = u16(s + k * 2);
305
0
                        namep[k] = isprint(c) ? c : '?';
306
0
                    }
307
0
                    namep[k] = 0;
308
0
                    found = 2;
309
0
                }
310
0
            }
311
312
4
            if (pid == 3 && eid == 10 && langid == 0x409) /* windows unicode ucs-4, US */
313
0
            {
314
0
                if (found < 1)
315
0
                {
316
0
                    unsigned char *s = namedata + stringoffset + offset;
317
0
                    int n = length / 4;
318
0
                    for (k = 0; k < n; k ++)
319
0
                    {
320
0
                        int c = u32(s + k * 4);
321
0
                        namep[k] = isprint(c) ? c : '?';
322
0
                    }
323
0
                    namep[k] = 0;
324
0
                    found = 1;
325
0
                }
326
0
            }
327
4
        }
328
12
    }
329
1
}
330
331
/*
332
 * Locate the 'cmap' table and count the number of subtables.
333
 */
334
335
static void
336
xps_load_sfnt_cmap(xps_font_t *font)
337
2
{
338
2
    byte *cmapdata;
339
2
    int offset, length;
340
2
    int nsubtables;
341
342
2
    offset = xps_find_sfnt_table(font, "cmap", &length);
343
2
    if (offset < 0 || length < 4)
344
0
    {
345
0
        gs_warn("cannot find cmap table");
346
0
        return;
347
0
    }
348
349
2
    cmapdata = font->data + offset;
350
2
    if (cmapdata + 4 < font->data + font->length)
351
2
    {
352
2
        nsubtables = u16(cmapdata + 2);
353
2
        if (nsubtables < 0 || length < 4 + nsubtables * 8)
354
0
        {
355
0
            gs_warn("cannot find cmap sub-tables");
356
0
            return;
357
0
        }
358
359
2
        font->cmaptable = offset;
360
2
        font->cmapsubcount = nsubtables;
361
2
        font->cmapsubtable = 0;
362
2
    }
363
2
}
364
365
/*
366
 * Return the number of cmap subtables.
367
 */
368
369
int
370
xps_count_font_encodings(xps_font_t *font)
371
2
{
372
2
    return font->cmapsubcount;
373
2
}
374
375
/*
376
 * Extract PlatformID and EncodingID for a cmap subtable.
377
 */
378
379
void
380
xps_identify_font_encoding(xps_font_t *font, int idx, int *pid, int *eid)
381
28
{
382
28
    byte *cmapdata, *entry;
383
28
    if (idx < 0 || idx >= font->cmapsubcount)
384
0
        return;
385
28
    cmapdata = font->data + font->cmaptable;
386
28
    entry = cmapdata + 4 + idx * 8;
387
28
    *pid = u16(entry + 0);
388
28
    *eid = u16(entry + 2);
389
28
}
390
391
/*
392
 * Select a cmap subtable for use with encoding functions.
393
 */
394
395
int
396
xps_select_font_encoding(xps_font_t *font, int idx)
397
2
{
398
2
    byte *cmapdata, *entry;
399
2
    int pid, eid;
400
2
    if (idx < 0 || idx >= font->cmapsubcount)
401
0
        return 0;
402
2
    cmapdata = font->data + font->cmaptable;
403
2
    entry = cmapdata + 4 + idx * 8;
404
2
    pid = u16(entry + 0);
405
2
    eid = u16(entry + 2);
406
2
    font->cmapsubtable = font->cmaptable + u32(entry + 4);
407
2
    if (font->cmapsubtable < 0 || font->cmapsubtable >= font->length) {
408
0
        font->cmapsubtable = 0;
409
0
        return 0;
410
0
    }
411
2
    font->usepua = (pid == 3 && eid == 0);
412
2
    return 1;
413
2
}
414
415
/*
416
 * Encode a character using the selected cmap subtable.
417
 * TODO: extend this to cover more cmap formats.
418
 */
419
420
static int
421
xps_encode_font_char_imp(xps_font_t *font, int code)
422
0
{
423
0
    byte *table;
424
0
    byte *tablemax;
425
426
    /* no cmap selected: return identity */
427
0
    if (font->cmapsubtable <= 0)
428
0
        return code;
429
430
0
    table = font->data + font->cmapsubtable;
431
0
    tablemax = font->data + font->length;
432
433
0
    switch (u16(table))
434
0
    {
435
0
    case 0: /* Apple standard 1-to-1 mapping. */
436
0
        if (table + code + 6 >= tablemax)
437
0
            return gs_error_invalidfont;
438
0
        return table[code + 6];
439
440
0
    case 4: /* Microsoft/Adobe segmented mapping. */
441
0
        if (table + 14 >= tablemax)
442
0
            return gs_error_invalidfont;
443
0
        {
444
0
            int segCount2 = u16(table + 6);
445
0
            byte *endCount = table + 14;
446
0
            byte *startCount = endCount + segCount2 + 2;
447
0
            byte *idDelta = startCount + segCount2;
448
0
            byte *idRangeOffset = idDelta + segCount2;
449
0
            byte *giddata;
450
0
            int i2;
451
452
0
            if (segCount2 < 3 || segCount2 > 65535 ||
453
0
               idRangeOffset > font->data + font->length)
454
0
               return gs_error_invalidfont;
455
456
0
           for (i2 = 0; i2 < segCount2 - 3; i2 += 2)
457
0
            {
458
0
                int delta, roff;
459
0
                int start = u16(startCount + i2);
460
0
                int glyph;
461
462
0
                if ( code < start )
463
0
                    return 0;
464
0
                if ( code > u16(endCount + i2) )
465
0
                    continue;
466
0
                delta = s16(idDelta + i2);
467
0
                roff = u16(idRangeOffset + i2);
468
0
                if ( roff == 0 )
469
0
                {
470
0
                    return ( code + delta ) & 0xffff; /* mod 65536 */
471
0
                }
472
0
                if ((giddata = (idRangeOffset + i2 + roff + ((code - start) << 1))) >
473
0
                    font->data + font->length) {
474
0
                    return code;
475
0
                }
476
0
                glyph = u16(giddata);
477
0
                return (glyph == 0 ? 0 : glyph + delta);
478
0
            }
479
480
            /*
481
             * The TrueType documentation says that the last range is
482
             * always supposed to end with 0xffff, so this shouldn't
483
             * happen; however, in some real fonts, it does.
484
             */
485
0
            return 0;
486
0
        }
487
488
0
    case 6: /* Single interval lookup. */
489
0
        if (table + 9 >= tablemax)
490
0
            return gs_error_invalidfont;
491
0
        {
492
0
            int firstCode = u16(table + 6);
493
0
            int entryCount = u16(table + 8);
494
0
            if ( code < firstCode || code >= firstCode + entryCount )
495
0
                return 0;
496
0
            if (table + 11 + ((code - firstCode) << 1) >= tablemax)
497
0
                return gs_error_invalidfont;
498
0
            return u16(table + 10 + ((code - firstCode) << 1));
499
0
        }
500
501
0
    case 10: /* Trimmed array (like 6) */
502
0
        if (table + 19 >= tablemax)
503
0
            return gs_error_invalidfont;
504
0
        {
505
0
            int startCharCode = u32(table + 12);
506
0
            int numChars = u32(table + 16);
507
0
            if (startCharCode < 0 || numChars < 0 || code < startCharCode || code >= startCharCode + numChars )
508
0
                return 0;
509
0
            if (table + 20 + (code - startCharCode) * 4 + 3 >= tablemax)
510
0
                return gs_error_invalidfont;
511
0
            return u32(table + 20 + (code - startCharCode) * 4);
512
0
        }
513
514
0
    case 12: /* Segmented coverage. (like 4) */
515
0
        if (table + 15 >= tablemax)
516
0
            return gs_error_invalidfont;
517
0
        {
518
0
            int nGroups = u32(table + 12);
519
0
            byte *group = table + 16;
520
0
            int i;
521
522
0
            if (nGroups < 0 || group + nGroups*12 >= tablemax)
523
0
                return gs_error_invalidfont;
524
0
            for (i = 0; i < nGroups; i++)
525
0
            {
526
0
                int startCharCode = u32(group + 0);
527
0
                int endCharCode = u32(group + 4);
528
0
                int startGlyphID = u32(group + 8);
529
0
                if ( startCharCode < 0 || endCharCode < 0 || startGlyphID < 0 || code < startCharCode )
530
0
                    return 0;
531
0
                if ( code <= endCharCode )
532
0
                    return startGlyphID + (code - startCharCode);
533
0
                group += 12;
534
0
            }
535
536
0
            return 0;
537
0
        }
538
539
0
    case 2: /* High-byte mapping through table. */
540
0
    case 8: /* Mixed 16-bit and 32-bit coverage (like 2) */
541
0
    default:
542
0
        gs_warn1("unknown cmap format: %d\n", u16(table));
543
0
        return 0;
544
0
    }
545
546
0
    return 0;
547
0
}
548
549
/*
550
 * Given a GID, reverse the CMAP subtable lookup to turn it back into a character code
551
 * We need a Unicode return value, so we might need to do some fixed tables for
552
 * certain kinds of CMAP subtables (ie non-Unicode ones). That would be a future enhancement
553
 * if we ever encounter such a beast.
554
 */
555
static int
556
xps_decode_font_char_imp(xps_font_t *font, int code)
557
0
{
558
0
    byte *table, *t;
559
0
    byte *tablemax;
560
561
    /* no cmap selected: return identity */
562
0
    if (font->cmapsubtable <= 0)
563
0
        return code;
564
565
0
    table = font->data + font->cmapsubtable;
566
0
    tablemax = font->data + font->length;
567
0
    if (table >= tablemax)
568
0
        return code;
569
570
0
    switch (u16(table))
571
0
    {
572
0
        case 0: /* Apple standard 1-to-1 mapping. */
573
0
            if (table + 3 >= tablemax)
574
0
                return gs_error_invalidfont;
575
0
            {
576
0
                int i, length = u16(&table[2]) - 6;
577
578
0
                if (length < 0 || length > 256)
579
0
                    return gs_error_invalidfont;
580
581
0
                if (table + 6 + length >= tablemax)
582
0
                    return gs_error_invalidfont;
583
0
                for (i=0;i<length;i++) {
584
0
                    if (table[6 + i] == code)
585
0
                        return i;
586
0
                }
587
0
            }
588
0
            return 0;
589
0
        case 4: /* Microsoft/Adobe segmented mapping. */
590
0
            if (table + 7 >= tablemax)
591
0
                return gs_error_invalidfont;
592
0
            {
593
0
                int segCount2 = u16(table + 6);
594
0
                byte *endCount = table + 14;
595
0
                byte *startCount = endCount + segCount2 + 2;
596
0
                byte *idDelta = startCount + segCount2;
597
0
                byte *idRangeOffset = idDelta + segCount2;
598
0
                byte *giddata;
599
0
                int i2;
600
601
0
                if (segCount2 < 3 || segCount2 > 65535 ||
602
0
                    idRangeOffset > tablemax)
603
0
                    return gs_error_invalidfont;
604
605
0
                for (i2 = 0; i2 < segCount2 - 3; i2 += 2)
606
0
                {
607
0
                    int delta = s16(idDelta + i2), roff = u16(idRangeOffset + i2);
608
0
                    int start = u16(startCount + i2);
609
0
                    int end = u16(endCount + i2);
610
0
                    int glyph, i;
611
612
0
                    if (end < start)
613
0
                        return gs_error_invalidfont;
614
615
0
                    for (i=start;i<=end;i++) {
616
0
                        if (roff == 0) {
617
0
                            glyph = (i + delta) & 0xffff;
618
0
                        } else {
619
0
                            if ((giddata = (idRangeOffset + i2 + roff + ((i - start) << 1))) >
620
0
                                 font->data + font->length) {
621
0
                                return_error(gs_error_invalidfont);
622
0
                            }
623
0
                            glyph = u16(giddata);
624
0
                        }
625
0
                        if (glyph == code) {
626
0
                            return i;
627
0
                        }
628
0
                    }
629
0
                }
630
0
            }
631
0
            return 0;
632
0
        case 6: /* Single interval lookup. */
633
0
            if (table + 9 >= tablemax)
634
0
                return gs_error_invalidfont;
635
0
            {
636
0
                int ch, i, length = u16(&table[8]);
637
0
                int firstCode = u16(&table[6]);
638
639
0
                if (length < 0 || length > 65535)
640
0
                    return gs_error_invalidfont;
641
642
0
                for (i=0;i<length;i++) {
643
0
                    t = &table[10 + (i * 2)];
644
0
                    if (t + 2 > tablemax)
645
0
                        return gs_error_invalidfont;
646
0
                    ch = u16(t);
647
0
                    if (ch == code)
648
0
                        return (firstCode + i);
649
0
                }
650
0
            }
651
0
            return 0;
652
0
        case 10: /* Trimmed array (like 6) */
653
0
            if (table + 23 >= tablemax)
654
0
                return gs_error_invalidfont;
655
0
            {
656
0
                unsigned int ch, i, length = u32(&table[20]);
657
0
                int firstCode = u32(&table[16]);
658
0
                if (length == 0 || firstCode < 0)
659
0
                    return gs_error_invalidfont;
660
0
                for (i=0;i<length;i++) {
661
0
                    t = &table[10 + (i * 2)];
662
0
                    if (t + 2 > tablemax)
663
0
                        return gs_error_invalidfont;
664
0
                    ch = u16(t);
665
0
                    if (ch == code)
666
0
                        return (firstCode + i);
667
0
                }
668
0
            }
669
0
            return 0;
670
0
        case 12: /* Segmented coverage. (like 4) */
671
0
            if (table + 15 >= tablemax)
672
0
                return gs_error_invalidfont;
673
0
            {
674
0
                unsigned int nGroups = u32(&table[12]);
675
0
                int Group;
676
677
0
                if (table + 16 + nGroups * 12 >= tablemax)
678
0
                    return gs_error_invalidfont;
679
0
                for (Group=0;Group<nGroups;Group++)
680
0
                {
681
0
                    int startCharCode = u32(&table[16 + (Group * 12)]);
682
0
                    int endCharCode = u32(&table[16 + (Group * 12) + 4]);
683
0
                    int startGlyphCode = u32(&table[16 + (Group * 12) + 8]);
684
685
0
                    if (startCharCode < 0 || endCharCode < 0 || startGlyphCode < 0)
686
0
                        return gs_error_invalidfont;
687
0
                    if (code >= startGlyphCode && code <= (startGlyphCode + (endCharCode - startCharCode))) {
688
0
                        return startGlyphCode + (code - startCharCode);
689
0
                    }
690
0
                }
691
0
            }
692
0
            return 0;
693
0
        case 2: /* High-byte mapping through table. */
694
0
        case 8: /* Mixed 16-bit and 32-bit coverage (like 2) */
695
0
        default:
696
0
            gs_warn1("unknown cmap format: %d\n", u16(table));
697
0
            return 0;
698
0
    }
699
0
    return 0;
700
0
}
701
702
int
703
xps_decode_font_char(xps_font_t *font, int code)
704
0
{
705
0
    int gid = xps_decode_font_char_imp(font, code);
706
0
    if (gid == 0)
707
0
        return code;
708
0
    return gid;
709
0
}
710
711
int
712
xps_encode_font_char(xps_font_t *font, int code)
713
0
{
714
0
    int gid = xps_encode_font_char_imp(font, code);
715
0
    if (gid == 0 && font->usepua)
716
0
        gid = xps_encode_font_char_imp(font, 0xF000 | code);
717
0
    return gid;
718
0
}
719
720
/*
721
 * Get glyph metrics by parsing TTF tables manually.
722
 * XPS needs more and different metrics than postscript/ghostscript
723
 * use so the native ghostscript functions are not adequate.
724
 */
725
726
void
727
xps_measure_font_glyph(xps_context_t *ctx, xps_font_t *font, int gid, xps_glyph_metrics_t *mtx)
728
66
{
729
730
66
    int head, format, loca, glyf;
731
66
    int ofs, len, glyf_len, loca_len, head_len;
732
66
    int idx, i, n;
733
66
    int hadv, vadv, vorg;
734
66
    int vtop, ymax, desc;
735
66
    int scale;
736
66
    byte *fontmax = font->data + font->length;
737
738
    /* some insane defaults */
739
740
66
    scale = 1000; /* units-per-em */
741
66
    hadv = 500;
742
66
    vadv = -1000;
743
66
    vorg = 1000;
744
745
    /*
746
     * Horizontal metrics are easy.
747
     */
748
749
66
    ofs = xps_find_sfnt_table(font, "hhea", &len);
750
66
    if (ofs < 0 || len < 2 * 18)
751
0
    {
752
0
        gs_warn("hhea table is too short");
753
0
        return;
754
0
    }
755
756
66
    vorg = s16(font->data + ofs + 4); /* ascender is default vorg */
757
66
    desc = s16(font->data + ofs + 6); /* descender */
758
66
    if (desc < 0)
759
66
        desc = -desc;
760
66
    n = u16(font->data + ofs + 17 * 2);
761
762
66
    ofs = xps_find_sfnt_table(font, "hmtx", &len);
763
66
    if (ofs < 0)
764
0
    {
765
0
        gs_warn("cannot find hmtx table");
766
0
        return;
767
0
    }
768
769
66
    idx = gid;
770
66
    if (idx > n - 1)
771
0
        idx = n - 1;
772
773
66
    if (idx * 4 + 2 <= len)
774
66
        hadv = u16(font->data + ofs + idx * 4);
775
66
    vadv = 0;
776
777
    /*
778
     * Vertical metrics are hairy (with missing tables).
779
     */
780
781
66
    head = xps_find_sfnt_table(font, "head", &head_len);
782
66
    if (head > 0 && head_len >= 20)
783
66
    {
784
66
        scale = u16(font->data + head + 18); /* units per em */
785
66
    }
786
787
66
    ofs = xps_find_sfnt_table(font, "OS/2", &len);
788
66
    if (ofs > 0 && len >= 72)
789
66
    {
790
66
        vorg = s16(font->data + ofs + 68); /* sTypoAscender */
791
66
        desc = s16(font->data + ofs + 70); /* sTypoDescender */
792
66
        if (desc < 0)
793
66
            desc = -desc;
794
66
    }
795
796
66
    ofs = xps_find_sfnt_table(font, "vhea", &len);
797
66
    if (ofs > 0 && len >= 2 * 18)
798
0
    {
799
0
        n = u16(font->data + ofs + 17 * 2);
800
801
0
        ofs = xps_find_sfnt_table(font, "vmtx", &len);
802
0
        if (ofs < 0)
803
0
        {
804
0
            gs_warn("cannot find vmtx table");
805
0
            return;
806
0
        }
807
808
0
        idx = gid;
809
0
        if (idx > n - 1)
810
0
            idx = n - 1;
811
812
0
        vadv = u16(font->data + ofs + idx * 4);
813
0
        vtop = u16(font->data + ofs + idx * 4 + 2);
814
815
0
        glyf = xps_find_sfnt_table(font, "glyf", &glyf_len);
816
0
        loca = xps_find_sfnt_table(font, "loca", &loca_len);
817
0
        if (head > 0 && glyf > 0 && loca > 0 && head_len >= 42)
818
0
        {
819
0
            format = u16(font->data + head + 50); /* indexToLocaFormat */
820
821
0
            do /* So we can break out. */
822
0
            {
823
0
                if (format == 0)
824
0
                {
825
0
                    if (loca_len < gid*2 + 2)
826
0
                        break;
827
0
                    ofs = u16(font->data + loca + gid * 2) * 2;
828
0
                }
829
0
                else
830
0
                {
831
0
                    if (loca_len < gid*4 + 4)
832
0
                        break;
833
0
                    ofs = u32(font->data + loca + gid * 4);
834
0
                }
835
0
                if (ofs > 0 && ofs + 8 + 4 <= glyf_len)
836
0
                {
837
0
                    ymax = u16(font->data + glyf + ofs + 8); /* yMax */
838
839
0
                    vorg = ymax + vtop;
840
0
                }
841
0
            }
842
0
            while (0);
843
0
        }
844
0
    }
845
846
66
    ofs = xps_find_sfnt_table(font, "VORG", &len);
847
66
    if (ofs > 0 && len >= 8)
848
0
    {
849
0
        vorg = u16(font->data + ofs + 4);
850
0
        n = u16(font->data + ofs + 6);
851
0
        for (i = 0; i < n; i++)
852
0
        {
853
0
            if (8 + 4 * n >= len)
854
0
                break;
855
0
            if (u16(font->data + ofs + 8 + 4 * i) == gid)
856
0
            {
857
0
                vorg = s16(font->data + ofs + 8 + 4 * i + 2);
858
0
                break;
859
0
            }
860
0
        }
861
0
    }
862
863
66
    if (vadv == 0)
864
66
        vadv = vorg + desc;
865
866
66
    mtx->hadv = hadv / (float) scale;
867
66
    mtx->vadv = vadv / (float) scale;
868
66
    mtx->vorg = vorg / (float) scale;
869
66
}