Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/psi/zfcmap.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
/* CMap creation operator */
18
#include "memory_.h"
19
#include "ghost.h"
20
#include "oper.h"
21
#include "gsmatrix.h"   /* for gxfont.h */
22
#include "gsstruct.h"
23
#include "gsutil.h"   /* for bytes_compare */
24
#include "gxfcmap1.h"
25
#include "gxfont.h"
26
#include "ialloc.h"
27
#include "icid.h"
28
#include "iddict.h"
29
#include "idparam.h"
30
#include "ifont.h"    /* for zfont_mark_glyph_name */
31
#include "iname.h"
32
#include "store.h"
33
34
/* Exported for zfont0.c */
35
int ztype0_get_cmap(const gs_cmap_t ** ppcmap, const ref * pfdepvector,
36
                    const ref * op, gs_memory_t *imem);
37
38
/*
39
 * Define whether to check the compatibility of CIDSystemInfo between the
40
 * CMap and the composite font.  PLRM2 says compatibility is required, but
41
 * PLRM3 says the interpreter doesn't check it.
42
 */
43
/*#define CHECK_CID_SYSTEM_INFO_COMPATIBILITY*/
44
45
/* ---------------- Internal procedures ---------------- */
46
47
/* Free a code map in case of VMerror. */
48
static void
49
free_code_map(gx_code_map_t * pcmap, gs_memory_t * mem)
50
0
{
51
0
    if (pcmap->lookup) {
52
0
        int i;
53
54
0
        for (i = 0; i < pcmap->num_lookup; ++i) {
55
0
            gx_cmap_lookup_range_t *pclr = &pcmap->lookup[i];
56
57
0
            if (pclr->value_type == CODE_VALUE_GLYPH)
58
0
                gs_free_string(mem, pclr->values.data, pclr->values.size,
59
0
                               "free_code_map(values)");
60
0
        }
61
0
        gs_free_object(mem, pcmap->lookup, "free_code_map(map)");
62
0
    }
63
0
}
64
65
/* Convert code ranges to internal form. */
66
static int
67
acquire_code_ranges(gs_cmap_adobe1_t *cmap, const ref *pref, gs_memory_t *mem)
68
0
{
69
0
    uint num_ranges = 0;
70
0
    gx_code_space_range_t *ranges;
71
0
    uint i, j, elem_sz;
72
0
    ref elem;
73
0
    int code;
74
75
0
    if (!r_is_array(pref))
76
0
        return_error(gs_error_rangecheck);
77
0
    for (i=0; i < r_size(pref); i++) {
78
0
        code = array_get_with_type(mem, pref, i, &elem, t_array);
79
0
        if (code < 0)
80
0
            return code;
81
0
        elem_sz = r_size(&elem);
82
0
        if (elem_sz & 1 || elem_sz > max_uint - num_ranges)
83
0
            return_error(gs_error_rangecheck);
84
0
        num_ranges += elem_sz;
85
0
    }
86
0
    if (num_ranges == 0)
87
0
        return_error(gs_error_rangecheck);
88
0
    num_ranges >>= 1;
89
90
0
    ranges = (gx_code_space_range_t *)
91
0
        gs_alloc_byte_array(mem, num_ranges, sizeof(gx_code_space_range_t),
92
0
                            "acquire_code_ranges");
93
0
    if (ranges == 0)
94
0
        return_error(gs_error_VMerror);
95
0
    cmap->code_space.ranges = ranges;
96
0
    cmap->code_space.num_ranges = num_ranges;
97
98
0
    for (i = 0; i < r_size(pref); i++) {
99
0
        code = array_get_with_type(mem, pref, i, &elem, t_array);
100
0
        if (code < 0) {
101
0
            gs_free_object(mem, ranges, "acquire_code_ranges");
102
0
            return code;
103
0
        }
104
0
        elem_sz = r_size(&elem);
105
0
        for (j = 0; j < elem_sz; j += 2) {
106
0
        ref rfirst, rlast;
107
0
        int size;
108
109
0
            array_get(mem, &elem, j, &rfirst);
110
0
            array_get(mem, &elem, j + 1, &rlast);
111
0
        if (!r_has_type(&rfirst, t_string) ||
112
0
            !r_has_type(&rlast, t_string) ||
113
0
            (size = r_size(&rfirst)) == 0 || size > MAX_CMAP_CODE_SIZE ||
114
0
            r_size(&rlast) != size ||
115
0
            memcmp(rfirst.value.bytes, rlast.value.bytes, size) > 0)
116
0
            return_error(gs_error_rangecheck);
117
0
        memcpy(ranges->first, rfirst.value.bytes, size);
118
0
        memcpy(ranges->last, rlast.value.bytes, size);
119
0
        ranges->size = size;
120
0
            ++ranges;
121
0
        }
122
0
    }
123
0
    return 0;
124
0
}
125
126
/* Convert a code map to internal form. */
127
static int
128
acquire_code_map(gx_code_map_t *pcmap, const ref *pref, gs_cmap_adobe1_t *root,
129
                 gs_memory_t *mem)
130
0
{
131
0
    uint num_lookup = 0;
132
0
    gx_cmap_lookup_range_t *pclr;
133
0
    long i;
134
0
    ref elem;
135
0
    uint elem_sz;
136
0
    int code;
137
138
0
    if (!r_is_array(pref))
139
0
        return_error(gs_error_rangecheck);
140
0
    for (i=0; i < r_size(pref); i++) {
141
0
        int code = array_get_with_type(mem, pref, i, &elem, t_array);
142
0
        if (code < 0)
143
0
            return code;
144
0
        elem_sz = r_size(&elem);
145
0
        if (elem_sz % 5 != 0 || elem_sz > max_uint - num_lookup)
146
0
            return_error(gs_error_rangecheck);
147
0
        num_lookup += elem_sz;
148
0
    }
149
0
    num_lookup /= 5;
150
0
    pclr = gs_alloc_struct_array(mem, num_lookup, gx_cmap_lookup_range_t,
151
0
                                 &st_cmap_lookup_range_element,
152
0
                                 "acquire_code_map(lookup ranges)");
153
0
    if (pclr == 0)
154
0
        return_error(gs_error_VMerror);
155
0
    memset(pclr, 0, sizeof(*pclr) * num_lookup);
156
0
    pcmap->lookup = pclr;
157
0
    pcmap->num_lookup = num_lookup;
158
159
0
    for (i = 0; i < r_size(pref); i++) {
160
0
        uint j;
161
0
        code = array_get_with_type(mem, pref, i, &elem, t_array);
162
0
        if (code < 0) {
163
            /* Free the lookup range, but leave it to gc to clean up the rest */
164
0
            gs_free_object(mem, pclr, "acquire_code_map");
165
0
            return code;
166
0
        }
167
0
        elem_sz = r_size(&elem);
168
0
        for (j = 0; j < elem_sz; j += 5) {
169
0
        ref rprefix, rmisc, rkeys, rvalues, rfxs;
170
171
0
            array_get(mem, &elem, j, &rprefix);
172
0
            array_get(mem, &elem, j + 1, &rmisc);
173
0
            array_get(mem, &elem, j + 2, &rkeys);
174
0
            array_get(mem, &elem, j + 3, &rvalues);
175
0
            array_get(mem, &elem, j + 4, &rfxs);
176
177
0
        if (!r_has_type(&rprefix, t_string) ||
178
0
            !r_has_type(&rmisc, t_string) ||
179
0
            !r_has_type(&rkeys, t_string) ||
180
0
            !(r_has_type(&rvalues, t_string) || r_is_array(&rvalues)) ||
181
0
            !r_has_type(&rfxs, t_integer)
182
0
            )
183
0
            return_error(gs_error_typecheck);
184
0
        if (r_size(&rmisc) != 4 ||
185
0
            r_size(&rprefix) > 4 ||
186
0
            rmisc.value.bytes[0] > MAX_CMAP_CODE_SIZE - r_size(&rprefix) ||
187
0
            rmisc.value.bytes[1] > 1 ||
188
0
            rmisc.value.bytes[2] > CODE_VALUE_MAX ||
189
0
            rmisc.value.bytes[3] == 0)
190
0
            return_error(gs_error_rangecheck);
191
0
        pclr->cmap = root;
192
0
        pclr->key_size = rmisc.value.bytes[0];
193
0
        pclr->key_prefix_size = r_size(&rprefix);
194
0
        memcpy(pclr->key_prefix, rprefix.value.bytes, pclr->key_prefix_size);
195
0
        pclr->key_is_range = rmisc.value.bytes[1];
196
0
        if (pclr->key_size == 0) {
197
            /* This is a single entry consisting only of the prefix. */
198
0
            if (r_size(&rkeys) != 0)
199
0
                return_error(gs_error_rangecheck);
200
0
            pclr->num_entries = 1;
201
0
        } else {
202
0
            int step = pclr->key_size * (pclr->key_is_range ? 2 : 1);
203
204
0
            if (r_size(&rkeys) % step != 0)
205
0
                return_error(gs_error_rangecheck);
206
0
            pclr->num_entries = r_size(&rkeys) / step;
207
0
        }
208
0
        pclr->keys.data = rkeys.value.bytes,
209
0
            pclr->keys.size = r_size(&rkeys);
210
0
        pclr->value_type = rmisc.value.bytes[2];
211
0
        pclr->value_size = rmisc.value.bytes[3];
212
0
        if (r_has_type(&rvalues, t_string)) {
213
0
            if (pclr->value_type == CODE_VALUE_GLYPH)
214
0
                return_error(gs_error_rangecheck);
215
0
            if (r_size(&rvalues) % pclr->num_entries != 0 ||
216
0
                r_size(&rvalues) / pclr->num_entries != pclr->value_size)
217
0
                return_error(gs_error_rangecheck);
218
0
            pclr->values.data = rvalues.value.bytes,
219
0
                pclr->values.size = r_size(&rvalues);
220
0
        } else {
221
0
            uint values_size = pclr->num_entries * pclr->value_size;
222
0
            long k;
223
0
            byte *pvalue;
224
225
0
            if (pclr->value_type != CODE_VALUE_GLYPH ||
226
0
                r_size(&rvalues) != pclr->num_entries ||
227
0
                pclr->value_size > sizeof(gs_glyph))
228
0
                return_error(gs_error_rangecheck);
229
0
            pclr->values.data = gs_alloc_string(mem, values_size,
230
0
                                                "acquire_code_map(values)");
231
0
            if (pclr->values.data == 0)
232
0
                return_error(gs_error_VMerror);
233
0
            pclr->values.size = values_size;
234
0
            pvalue = pclr->values.data;
235
0
            for (k = 0; k < pclr->num_entries; ++k) {
236
0
                ref rvalue;
237
0
                gs_glyph value;
238
0
                int i;
239
240
0
                array_get(mem, &rvalues, k, &rvalue);
241
0
                if (!r_has_type(&rvalue, t_name))
242
0
                    return_error(gs_error_rangecheck);
243
0
                value = name_index(mem, &rvalue);
244
                /*
245
                 * We need a special check here because some CPUs cannot
246
                 * shift by the full size of an int or long.
247
                 */
248
0
                if (pclr->value_size < sizeof(value) &&
249
0
                    (value >> (pclr->value_size * 8)) != 0
250
0
                    )
251
0
                    return_error(gs_error_rangecheck);
252
0
                for (i = pclr->value_size; --i >= 0; )
253
0
                    *pvalue++ = (byte)(value >> (i * 8));
254
0
            }
255
0
        }
256
0
        check_int_leu_only(rfxs, 0xff);
257
0
        pclr->font_index = (int)rfxs.value.intval;
258
0
            ++pclr;
259
0
        }
260
0
    }
261
0
    return 0;
262
0
}
263
264
/*
265
 * Acquire the CIDSystemInfo array from a dictionary.  If missing, fabricate
266
 * a 0-element array and return 1.
267
 */
268
static int
269
acquire_cid_system_info(ref *psia, const ref *op)
270
0
{
271
0
    ref *prcidsi;
272
273
0
    if (dict_find_string(op, "CIDSystemInfo", &prcidsi) <= 0) {
274
0
        make_empty_array(psia, a_readonly);
275
0
        return 1;
276
0
    }
277
0
    if (r_has_type(prcidsi, t_dictionary)) {
278
0
        make_array(psia, a_readonly, 1, prcidsi);
279
0
        return 0;
280
0
    }
281
0
    if (!r_is_array(prcidsi))
282
0
        return_error(gs_error_typecheck);
283
0
    *psia = *prcidsi;
284
0
    return 0;
285
0
}
286
287
/*
288
 * Get one element of a CIDSystemInfo array.  If the element is missing or
289
 * null, return 1.
290
 */
291
static int
292
get_cid_system_info(const gs_memory_t *mem, gs_cid_system_info_t *pcidsi, const ref *psia, uint index)
293
0
{
294
0
    ref rcidsi;
295
0
    int code = array_get(mem, psia, (long)index, &rcidsi);
296
297
0
    if (code < 0 || r_has_type(&rcidsi, t_null)) {
298
0
        cid_system_info_set_null(pcidsi);
299
0
        return 1;
300
0
    }
301
0
    return cid_system_info_param(pcidsi, &rcidsi);
302
0
}
303
304
#ifdef CHECK_CID_SYSTEM_INFO_COMPATIBILITY
305
306
/* Check compatibility of CIDSystemInfo. */
307
static bool
308
bytes_eq(const gs_const_string *pcs1, const gs_const_string *pcs2)
309
{
310
    return !bytes_compare(pcs1->data, pcs1->size,
311
                          pcs2->data, pcs2->size);
312
}
313
static bool
314
cid_system_info_compatible(const gs_cid_system_info_t * psi1,
315
                           const gs_cid_system_info_t * psi2)
316
{
317
    return bytes_eq(&psi1->Registry, &psi2->Registry) &&
318
        bytes_eq(&psi1->Ordering, &psi2->Ordering);
319
}
320
321
#endif /* CHECK_CID_SYSTEM_INFO_COMPATIBILITY */
322
323
/* ---------------- (Semi-)public procedures ---------------- */
324
325
extern_st(st_cmap_tt_16bit_format4);
326
extern_st(st_cmap_identity);
327
extern_st(st_cmap_ToUnicode);
328
329
/* Get the CodeMap from a Type 0 font, and check the CIDSystemInfo of */
330
/* its subsidiary fonts. */
331
int
332
ztype0_get_cmap(const gs_cmap_t **ppcmap, const ref *pfdepvector,
333
                const ref *op, gs_memory_t *imem)
334
0
{
335
0
    ref *prcmap;
336
0
    ref *pcodemap;
337
0
    const gs_cmap_t *pcmap;
338
0
    int code;
339
0
    uint num_fonts;
340
0
    uint i;
341
342
0
    if (dict_find_string(op, "CMap", &prcmap) <= 0 ||
343
0
        !r_has_type(prcmap, t_dictionary) ||
344
0
        dict_find_string(prcmap, "CodeMap", &pcodemap) <= 0 ||
345
0
        !r_is_struct(pcodemap) || (!r_has_stype(pcodemap, imem, st_cmap_tt_16bit_format4) &&
346
0
        !r_has_stype(pcodemap, imem, st_cmap_identity) && !r_has_stype(pcodemap, imem, st_cmap_ToUnicode) &&
347
0
        !r_has_stype(pcodemap, imem, st_cmap_adobe1))
348
0
        )
349
0
        return_error(gs_error_invalidfont);
350
0
    pcmap = r_ptr(pcodemap, gs_cmap_t);
351
0
    num_fonts = r_size(pfdepvector);
352
0
    for (i = 0; i < num_fonts; ++i) {
353
0
        ref rfdep, rfsi;
354
355
0
        array_get(imem, pfdepvector, (long)i, &rfdep);
356
0
        code = acquire_cid_system_info(&rfsi, &rfdep);
357
0
        if (code < 0)
358
0
            return code;
359
0
        if (code == 0) {
360
0
            if (r_size(&rfsi) != 1)
361
0
                return_error(gs_error_rangecheck);
362
#ifdef CHECK_CID_SYSTEM_INFO_COMPATIBILITY
363
            {
364
                gs_cid_system_info_t cidsi;
365
366
                get_cid_system_info(&cidsi, &rfsi, 0);
367
                if (!cid_system_info_is_null(&cidsi) &&
368
                    !cid_system_info_compatible(&cidsi,
369
                                                pcmap->CIDSystemInfo + i))
370
                    return_error(gs_error_rangecheck);
371
            }
372
#endif
373
0
        }
374
0
    }
375
0
    *ppcmap = pcmap;
376
0
    return 0;
377
0
}
378
379
/* ---------------- Operators ---------------- */
380
381
/* <CMap> .buildcmap <CMap> */
382
/*
383
 * Create the internal form of a CMap.  The initial CMap must be read-write
384
 * and have an entry with key = CodeMap and value = null; the result is
385
 * read-only and has a real CodeMap.
386
 *
387
 * This operator reads the CMapType, CMapName, CIDSystemInfo, CMapVersion,
388
 * UIDOffset, XUID, WMode, and .CodeMapData elements of the CMap dictionary.
389
 * For details, see lib/gs_cmap.ps and the Adobe documentation.
390
 */
391
static int
392
zfcmap_glyph_name(const gs_memory_t *mem,
393
                  gs_glyph glyph, gs_const_string *pstr, void *proc_data)
394
0
{
395
0
    ref nref, nsref;
396
397
0
    name_index_ref(mem, (uint)glyph, &nref);
398
0
    name_string_ref(mem, &nref, &nsref);
399
0
    pstr->data = nsref.value.const_bytes;
400
0
    pstr->size = r_size(&nsref);
401
0
    return 0;
402
0
}
403
static int
404
zbuildcmap(i_ctx_t *i_ctx_p)
405
0
{
406
0
    os_ptr op = osp;
407
0
    int code;
408
0
    ref *pcmapname;
409
0
    ref *puidoffset;
410
0
    ref *pcodemapdata;
411
0
    ref *pcodemap;
412
0
    ref rname, rcidsi, rcoderanges, rdefs, rnotdefs;
413
0
    gs_cmap_adobe1_t *pcmap = 0;
414
0
    ref rcmap;
415
0
    uint i;
416
417
0
    check_op(1);
418
0
    check_type(*op, t_dictionary);
419
0
    check_dict_write(*op);
420
0
    if ((code = dict_find_string(op, "CMapName", &pcmapname)) <= 0) {
421
0
        code = gs_note_error(gs_error_rangecheck);
422
0
        goto fail;
423
0
    }
424
0
    if (!r_has_type(pcmapname, t_name)) {
425
0
        code = gs_note_error(gs_error_typecheck);
426
0
        goto fail;
427
0
    }
428
0
    name_string_ref(imemory, pcmapname, &rname);
429
0
    if (dict_find_string(op, ".CodeMapData", &pcodemapdata) <= 0 ||
430
0
        !r_has_type(pcodemapdata, t_array) ||
431
0
        r_size(pcodemapdata) != 3 ||
432
0
        dict_find_string(op, "CodeMap", &pcodemap) <= 0 ||
433
0
        !r_has_type(pcodemap, t_null)
434
0
        ) {
435
0
        code = gs_note_error(gs_error_rangecheck);
436
0
        goto fail;
437
0
    }
438
0
    if ((code = acquire_cid_system_info(&rcidsi, op)) < 0)
439
0
        goto fail;
440
0
    if ((code = gs_cmap_adobe1_alloc(&pcmap, 0, rname.value.const_bytes,
441
0
                                     r_size(&rname), r_size(&rcidsi),
442
0
                                     0, 0, 0, 0, 0, imemory)) < 0)
443
0
        goto fail;
444
0
    if ((code = dict_int_param(op, "CMapType", 0, 1, 0, &pcmap->CMapType)) < 0 ||
445
0
        (code = dict_float_param(op, "CMapVersion", 0.0, &pcmap->CMapVersion)) < 0 ||
446
0
        (code = dict_uid_param(op, &pcmap->uid, 0, imemory, i_ctx_p)) < 0 ||
447
0
        (code = dict_int_param(op, "WMode", 0, 1, 0, &pcmap->WMode)) < 0
448
0
        )
449
0
        goto fail;
450
0
    if (dict_find_string(op, "UIDOffset", &puidoffset) > 0) {
451
0
        if (!r_has_type(puidoffset, t_integer)) {
452
0
            code = gs_note_error(gs_error_typecheck);
453
0
            goto fail;
454
0
        }
455
0
        pcmap->UIDOffset = puidoffset->value.intval; /* long, not int */
456
0
    }
457
0
    for (i = 0; i < r_size(&rcidsi); ++i) {
458
0
        code = get_cid_system_info(imemory, pcmap->CIDSystemInfo + i, &rcidsi, i);
459
0
        if (code < 0)
460
0
            goto fail;
461
0
    }
462
0
    array_get(imemory, pcodemapdata, 0L, &rcoderanges);
463
0
    array_get(imemory, pcodemapdata, 1L, &rdefs);
464
0
    array_get(imemory, pcodemapdata, 2L, &rnotdefs);
465
0
    if ((code = acquire_code_ranges(pcmap, &rcoderanges, imemory)) < 0)
466
0
        goto fail;
467
0
    if ((code = acquire_code_map(&pcmap->def, &rdefs, pcmap, imemory)) < 0)
468
0
        goto fail;
469
0
    if ((code = acquire_code_map(&pcmap->notdef, &rnotdefs, pcmap, imemory)) < 0)
470
0
        goto fail;
471
0
    if (!bytes_compare(pcmap->CIDSystemInfo->Registry.data, pcmap->CIDSystemInfo->Registry.size,
472
0
                    (const byte *)"Artifex", 7) &&
473
0
        !bytes_compare(pcmap->CIDSystemInfo->Ordering.data, pcmap->CIDSystemInfo->Ordering.size,
474
0
                    (const byte *)"Unicode", 7))
475
0
        pcmap->from_Unicode = true;
476
0
    pcmap->mark_glyph = zfont_mark_glyph_name;
477
0
    pcmap->mark_glyph_data = 0;
478
0
    pcmap->glyph_name = zfcmap_glyph_name;
479
0
    pcmap->glyph_name_data = 0;
480
0
    make_istruct_new(&rcmap, a_readonly, pcmap);
481
0
    code = idict_put_string(op, "CodeMap", &rcmap);
482
0
    if (code < 0)
483
0
        goto fail;
484
0
    return zreadonly(i_ctx_p);
485
0
fail:
486
0
    if (pcmap) {
487
0
        free_code_map(&pcmap->notdef, imemory);
488
0
        free_code_map(&pcmap->def, imemory);
489
0
        ifree_object(pcmap->CIDSystemInfo, "zbuildcmap(CIDSystemInfo)");
490
        ifree_object(pcmap, "zbuildcmap(cmap)");
491
0
    }
492
0
    return code;
493
0
}
494
495
/* ------ Initialization procedure ------ */
496
497
const op_def zfcmap_op_defs[] =
498
{
499
    {"1.buildcmap", zbuildcmap},
500
    op_def_end(0)
501
};