Coverage Report

Created: 2026-02-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/devices/vector/gdevpsfm.c
Line
Count
Source
1
/* Copyright (C) 2001-2023 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
/* Write a CMap */
18
#include "gx.h"
19
#include "gserrors.h"
20
#include "gxfcmap.h"
21
#include "stream.h"
22
#include "spprint.h"
23
#include "spsdf.h"
24
#include "gdevpsf.h"
25
26
/* ---------------- Utilities ---------------- */
27
28
typedef struct cmap_operators_s {
29
    const char *beginchar;
30
    const char *endchar;
31
    const char *beginrange;
32
    const char *endrange;
33
} cmap_operators_t;
34
static const cmap_operators_t
35
  cmap_cid_operators = {
36
    "begincidchar\n", "endcidchar\n",
37
    "begincidrange\n", "endcidrange\n"
38
  },
39
  cmap_notdef_operators = {
40
    "beginnotdefchar\n", "endnotdefchar\n",
41
    "beginnotdefrange\n", "endnotdefrange\n"
42
  };
43
44
/* Write a gs_string with a prefix. */
45
static void
46
pput_string_entry(stream *s, const char *prefix, const gs_const_string *pstr)
47
236
{
48
236
    stream_puts(s, prefix);
49
236
    stream_write(s, pstr->data, pstr->size);
50
236
}
51
52
/* Write a hex string. */
53
static void
54
pput_hex(stream *s, const byte *pcid, int size)
55
231k
{
56
231k
    int i;
57
231k
    static const char *const hex_digits = "0123456789abcdef";
58
59
569k
    for (i = 0; i < size; ++i) {
60
337k
        stream_putc(s, hex_digits[pcid[i] >> 4]);
61
337k
        stream_putc(s, hex_digits[pcid[i] & 0xf]);
62
337k
    }
63
231k
}
64
65
/* Write a list of code space ranges. */
66
static void
67
cmap_put_ranges(stream *s, const gx_code_space_range_t *pcsr, int count)
68
3.99k
{
69
3.99k
    int i;
70
71
3.99k
    pprintd1(s, "%d begincodespacerange\n", count);
72
7.99k
    for (i = 0; i < count; ++i, ++pcsr) {
73
4.00k
        stream_puts(s, "<");
74
4.00k
        pput_hex(s, pcsr->first, pcsr->size);
75
4.00k
        stream_puts(s, "><");
76
4.00k
        pput_hex(s, pcsr->last, pcsr->size);
77
4.00k
        stream_puts(s, ">\n");
78
4.00k
    }
79
3.99k
    stream_puts(s, "endcodespacerange\n");
80
3.99k
}
81
82
/* Write one CIDSystemInfo dictionary. */
83
static void
84
cmap_put_system_info(stream *s, const gs_cid_system_info_t *pcidsi)
85
59
{
86
59
    if (cid_system_info_is_null(pcidsi)) {
87
3
        stream_puts(s, " null ");
88
56
    } else {
89
56
        stream_puts(s, " 3 dict dup begin\n");
90
56
        stream_puts(s, "/Registry ");
91
56
        s_write_ps_string(s, pcidsi->Registry.data, pcidsi->Registry.size, 0);
92
56
        stream_puts(s, " def\n/Ordering ");
93
56
        s_write_ps_string(s, pcidsi->Ordering.data, pcidsi->Ordering.size, 0);
94
56
        pprintd1(s, " def\n/Supplement %d def\nend ", pcidsi->Supplement);
95
56
    }
96
59
}
97
98
/* Write one code map. */
99
static int
100
cmap_put_code_map(gs_memory_t *mem,
101
                  stream *s, int which, const gs_cmap_t *pcmap,
102
                  const cmap_operators_t *pcmo,
103
                  psf_put_name_chars_proc_t put_name_chars,
104
                  int font_index_only)
105
7.98k
{
106
    /* For simplicity, produce one entry for each lookup range. */
107
7.98k
    gs_cmap_lookups_enum_t lenum;
108
7.98k
    int font_index = (pcmap->num_fonts <= 1 ? 0 : -1);
109
7.98k
    int code;
110
111
7.98k
    for (gs_cmap_lookups_enum_init(pcmap, which, &lenum);
112
14.6k
         (code = gs_cmap_enum_next_lookup(mem, &lenum)) == 0; ) {
113
6.69k
        gs_cmap_lookups_enum_t counter;
114
6.69k
        int num_entries = 0;
115
6.69k
        int gi;
116
117
6.69k
        if (font_index_only >= 0 && lenum.entry.font_index != font_index_only)
118
0
            continue;
119
6.69k
        if (font_index_only < 0 && lenum.entry.font_index != font_index) {
120
0
            pprintd1(s, "%d usefont\n", lenum.entry.font_index);
121
0
            font_index = lenum.entry.font_index;
122
0
        }
123
        /* Count the number of entries in this lookup range. */
124
6.69k
        counter = lenum;
125
82.8k
        while (gs_cmap_enum_next_entry(&counter) == 0)
126
76.2k
            ++num_entries;
127
13.4k
        for (gi = 0; gi < num_entries; gi += 100) {
128
6.71k
            int i = gi, ni = min(i + 100, num_entries);
129
6.71k
            const char *end;
130
131
6.71k
            pprintd1(s, "%d ", ni - i);
132
6.71k
            if (lenum.entry.key_is_range) {
133
4.80k
                if (lenum.entry.value_type == CODE_VALUE_CID || lenum.entry.value_type == CODE_VALUE_NOTDEF) {
134
856
                    stream_puts(s, pcmo->beginrange);
135
856
                    end = pcmo->endrange;
136
3.95k
                } else { /* must be def, not notdef */
137
3.95k
                    stream_puts(s, "beginbfrange\n");
138
3.95k
                    end = "endbfrange\n";
139
3.95k
                }
140
4.80k
            } else {
141
1.90k
                if (lenum.entry.value_type == CODE_VALUE_CID || lenum.entry.value_type == CODE_VALUE_NOTDEF) {
142
1.90k
                    stream_puts(s, pcmo->beginchar);
143
1.90k
                    end = pcmo->endchar;
144
1.90k
                } else { /* must be def, not notdef */
145
0
                    stream_puts(s, "beginbfchar\n");
146
0
                    end = "endbfchar\n";
147
0
                }
148
1.90k
            }
149
82.9k
            for (; i < ni; ++i) {
150
76.2k
                int j;
151
76.2k
                long value;
152
76.2k
                int value_size;
153
154
76.2k
                DISCARD(gs_cmap_enum_next_entry(&lenum)); /* can't fail */
155
76.2k
                value_size = lenum.entry.value.size;
156
226k
                for (j = 0; j <= lenum.entry.key_is_range; ++j) {
157
150k
                    stream_putc(s, '<');
158
150k
                    pput_hex(s, lenum.entry.key[j], lenum.entry.key_size);
159
150k
                    stream_putc(s, '>');
160
150k
                }
161
229k
                for (j = 0, value = 0; j < value_size; ++j)
162
152k
                    value = (value << 8) + lenum.entry.value.data[j];
163
76.2k
                switch (lenum.entry.value_type) {
164
2.75k
                case CODE_VALUE_CID:
165
2.75k
                case CODE_VALUE_NOTDEF:
166
2.75k
                    pprintld1(s, "%ld", value);
167
2.75k
                    break;
168
73.4k
                case CODE_VALUE_CHARS:
169
73.4k
                    stream_putc(s, '<');
170
73.4k
                    pput_hex(s, lenum.entry.value.data, value_size);
171
73.4k
                    stream_putc(s, '>');
172
73.4k
                    break;
173
0
                case CODE_VALUE_GLYPH: {
174
0
                    gs_const_string str;
175
0
                    int code = pcmap->glyph_name(mem, (gs_glyph)value, &str,
176
0
                                                 pcmap->glyph_name_data);
177
178
0
                    if (code < 0) {
179
0
                        if (lenum.entry.value.data && lenum.entry.value.data != lenum.temp_value) {
180
0
                            gs_free_object(mem, (void *)lenum.entry.value.data, "working ToUnicode buffer");
181
0
                        }
182
0
                        return code;
183
0
                    }
184
0
                    stream_putc(s, '/');
185
0
                    code = put_name_chars(s, str.data, str.size);
186
0
                    if (code < 0) {
187
0
                        if (lenum.entry.value.data && lenum.entry.value.data != lenum.temp_value) {
188
0
                            gs_free_object(mem, (void *)lenum.entry.value.data, "working ToUnicode buffer");
189
0
                        }
190
0
                        return code;
191
0
                    }
192
0
                }
193
0
                    break;
194
0
                default:  /* not possible */
195
0
                    if (lenum.entry.value.data && lenum.entry.value.data != lenum.temp_value) {
196
0
                        gs_free_object(mem, (void *)lenum.entry.value.data, "working ToUnicode buffer");
197
0
                    }
198
0
                    return_error(gs_error_unregistered);
199
76.2k
                }
200
76.2k
                stream_putc(s, '\n');
201
76.2k
            }
202
6.71k
            stream_puts(s, end);
203
6.71k
        }
204
6.69k
    }
205
7.98k
    if (lenum.entry.value.data && lenum.entry.value.data != lenum.temp_value) {
206
3.93k
        gs_free_object(mem, (void *)lenum.entry.value.data, "working ToUnicode buffer");
207
3.93k
    }
208
7.98k
    return code;
209
7.98k
}
210
211
/* ---------------- Main program ---------------- */
212
213
/* Write a CMap in its standard (source) format. */
214
int
215
psf_write_cmap(gs_memory_t *mem,
216
               stream *s, const gs_cmap_t *pcmap,
217
               psf_put_name_chars_proc_t put_name_chars,
218
               const gs_const_string *alt_cmap_name, int font_index_only)
219
3.99k
{
220
3.99k
    const gs_const_string *const cmap_name =
221
3.99k
        (alt_cmap_name ? alt_cmap_name : &pcmap->CMapName);
222
3.99k
    const gs_cid_system_info_t *const pcidsi = pcmap->CIDSystemInfo;
223
224
3.99k
    switch (pcmap->CMapType) {
225
3.99k
    case 0: case 1: case 2:
226
3.99k
        break;
227
0
    default:
228
0
        return_error(gs_error_rangecheck);
229
3.99k
    }
230
231
    /* Write the header. */
232
233
3.99k
    if (!pcmap->ToUnicode) {
234
59
        stream_puts(s, "%!PS-Adobe-3.0 Resource-CMap\n");
235
59
        stream_puts(s, "%%DocumentNeededResources: ProcSet (CIDInit)\n");
236
59
        stream_puts(s, "%%IncludeResource: ProcSet (CIDInit)\n");
237
59
        pput_string_entry(s, "%%BeginResource: CMap (", cmap_name);
238
59
        pput_string_entry(s, ")\n%%Title: (", cmap_name);
239
59
        pput_string_entry(s, " ", &pcidsi->Registry);
240
59
        pput_string_entry(s, " ", &pcidsi->Ordering);
241
59
        pprintd1(s, " %d)\n", pcidsi->Supplement);
242
59
        pprintg1(s, "%%%%Version: %g\n", pcmap->CMapVersion);
243
59
    }
244
3.99k
    stream_puts(s, "/CIDInit /ProcSet findresource begin\n");
245
3.99k
    stream_puts(s, "12 dict begin\nbegincmap\n");
246
247
    /* Write the fixed entries. */
248
249
3.99k
    pprintd1(s, "/CMapType %d def\n", pcmap->CMapType);
250
3.99k
    stream_puts(s, "/CMapName/");
251
3.99k
    put_name_chars(s, cmap_name->data, cmap_name->size);
252
3.99k
    stream_puts(s, " def\n");
253
3.99k
    if (!pcmap->ToUnicode) {
254
59
        pprintg1(s, "/CMapVersion %g def\n", pcmap->CMapVersion);
255
59
        stream_puts(s, "/CIDSystemInfo");
256
59
        if (font_index_only >= 0 && font_index_only < pcmap->num_fonts) {
257
59
            cmap_put_system_info(s, pcidsi + font_index_only);
258
59
        } else if (pcmap->num_fonts == 1) {
259
0
            cmap_put_system_info(s, pcidsi);
260
0
        } else {
261
0
            int i;
262
263
0
            pprintd1(s, " %d array\n", pcmap->num_fonts);
264
0
            for (i = 0; i < pcmap->num_fonts; ++i) {
265
0
                pprintd1(s, "dup %d", i);
266
0
                cmap_put_system_info(s, pcidsi + i);
267
0
                stream_puts(s, "put\n");
268
0
            }
269
0
        }
270
59
        stream_puts(s, " def\n");
271
59
        if (uid_is_XUID(&pcmap->uid)) {
272
23
            uint i, n = uid_XUID_size(&pcmap->uid);
273
23
            const long *values = uid_XUID_values(&pcmap->uid);
274
275
23
            stream_puts(s, "/XUID [");
276
109
            for (i = 0; i < n; ++i)
277
86
                pprintld1(s, " %ld", values[i]);
278
23
            stream_puts(s, "] def\n");
279
23
        }
280
59
        pprintld1(s, "/UIDOffset %ld def\n", pcmap->UIDOffset);
281
59
        pprintd1(s, "/WMode %d def\n", pcmap->WMode);
282
59
    }
283
284
    /* Write the code space ranges. */
285
286
3.99k
    {
287
3.99k
        gs_cmap_ranges_enum_t renum;
288
4.00k
#define MAX_RANGES 100
289
3.99k
        gx_code_space_range_t ranges[MAX_RANGES];
290
3.99k
        int code, count = 0;
291
292
3.99k
        for (gs_cmap_ranges_enum_init(pcmap, &renum);
293
7.99k
             (code = gs_cmap_enum_next_range(&renum)) == 0; ) {
294
4.00k
            if (count == MAX_RANGES) {
295
0
                cmap_put_ranges(s, ranges, count);
296
0
                count = 0;
297
0
            }
298
4.00k
            ranges[count++] = renum.range;
299
4.00k
        }
300
3.99k
        if (code < 0)
301
0
            return code;
302
3.99k
        if (count)
303
3.99k
            cmap_put_ranges(s, ranges, count);
304
3.99k
#undef MAX_RANGES
305
3.99k
    }
306
307
    /* Write the code and notdef data. */
308
309
0
    {
310
3.99k
        int code;
311
312
3.99k
        code = cmap_put_code_map(mem, s, 1, pcmap, &cmap_notdef_operators,
313
3.99k
                                 put_name_chars, font_index_only);
314
3.99k
        if (code < 0)
315
0
            return code;
316
3.99k
        code = cmap_put_code_map(mem, s, 0, pcmap, &cmap_cid_operators,
317
3.99k
                                 put_name_chars, font_index_only);
318
3.99k
        if (code < 0)
319
0
            return code;
320
3.99k
    }
321
322
    /* Write the trailer. */
323
324
3.99k
    stream_puts(s, "endcmap\n");
325
3.99k
    stream_puts(s, "CMapName currentdict /CMap defineresource pop\nend end\n");
326
3.99k
    if (!pcmap->ToUnicode) {
327
59
        stream_puts(s, "%%EndResource\n");
328
59
        stream_puts(s, "%%EOF\n");
329
59
    }
330
331
3.99k
    return 0;
332
3.99k
}