Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/psi/zcharout.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
/* Common code for outline (Type 1 / 4 / 42) fonts */
18
#include "memory_.h"
19
#include "ghost.h"
20
#include "oper.h"
21
#include "gscrypt1.h"
22
#include "gstext.h"
23
#include "gxdevice.h"   /* for gxfont.h */
24
#include "gxfont.h"
25
#include "gxfont1.h"
26
#include "dstack.h"   /* only for systemdict */
27
#include "estack.h"
28
#include "ichar.h"
29
#include "icharout.h"
30
#include "idict.h"
31
#include "ifont.h"
32
#include "igstate.h"
33
#include "iname.h"
34
#include "store.h"
35
36
/*
37
 * Execute an outline defined by a PostScript procedure.
38
 * The top elements of the stack are:
39
 *      <font> <code|name> <name> <outline_id>
40
 */
41
int
42
zchar_exec_char_proc(i_ctx_t *i_ctx_p)
43
0
{
44
0
    os_ptr op = osp;
45
        /*
46
         * The definition is a PostScript procedure.  Execute
47
         *      <code|name> proc
48
         * within a systemdict begin/end and a font begin/end.
49
         */
50
0
    es_ptr ep;
51
52
0
    check_estack(5);
53
0
    ep = esp += 5;
54
0
    make_op_estack(ep - 4, zend);
55
0
    make_op_estack(ep - 3, zend);
56
0
    ref_assign(ep - 2, op);
57
0
    make_op_estack(ep - 1, zbegin);
58
0
    make_op_estack(ep, zbegin);
59
0
    ref_assign(op - 1, systemdict);
60
0
    {
61
0
        ref rfont;
62
63
0
        ref_assign(&rfont, op - 3);
64
0
        ref_assign(op - 3, op - 2);
65
0
        ref_assign(op - 2, &rfont);
66
0
    }
67
0
    pop(1);
68
0
    return o_push_estack;
69
0
}
70
71
/*
72
 * Get the metrics for a character from the Metrics dictionary of a base
73
 * font.  If present, store the l.s.b. in psbw[0,1] and the width in
74
 * psbw[2,3].
75
 */
76
int       /*metrics_present*/
77
zchar_get_metrics(const gs_font_base * pbfont, const ref * pcnref,
78
                  double psbw[4])
79
3.81M
{
80
3.81M
    const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
81
3.81M
    ref *pmdict;
82
83
3.81M
    if (dict_find_string(pfdict, "Metrics", &pmdict) > 0) {
84
0
        ref *pmvalue;
85
86
0
        check_type_only(*pmdict, t_dictionary);
87
0
        check_dict_read(*pmdict);
88
0
        if (dict_find(pmdict, pcnref, &pmvalue) > 0) {
89
0
            if (num_params(pmvalue, 1, psbw + 2) >= 0) { /* <wx> only */
90
0
                psbw[3] = 0;
91
0
                return metricsWidthOnly;
92
0
            } else {
93
0
                int code;
94
95
0
                check_read_type_only(*pmvalue, t_array);
96
0
                switch (r_size(pmvalue)) {
97
0
                    case 2: /* [<sbx> <wx>] */
98
0
                        code = num_params(pmvalue->value.refs + 1,
99
0
                                          2, psbw);
100
0
                        psbw[2] = psbw[1];
101
0
                        psbw[1] = psbw[3] = 0;
102
0
                        break;
103
0
                    case 4: /* [<sbx> <sby> <wx> <wy>] */
104
0
                        code = num_params(pmvalue->value.refs + 3,
105
0
                                          4, psbw);
106
0
                        break;
107
0
                    default:
108
0
                        return_error(gs_error_rangecheck);
109
0
                }
110
0
                if (code < 0)
111
0
                    return code;
112
0
                return metricsSideBearingAndWidth;
113
0
            }
114
0
        }
115
0
    }
116
3.81M
    return metricsNone;
117
3.81M
}
118
119
/* Get the vertical metrics for a character from Metrics2, if present. */
120
int
121
zchar_get_metrics2(const gs_font_base * pbfont, const ref * pcnref,
122
                   double pwv[4])
123
1.36M
{
124
1.36M
    const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
125
1.36M
    ref *pmdict;
126
127
1.36M
    if (dict_find_string(pfdict, "Metrics2", &pmdict) > 0) {
128
0
        ref *pmvalue;
129
130
0
        check_type_only(*pmdict, t_dictionary);
131
0
        check_dict_read(*pmdict);
132
0
        if (dict_find(pmdict, pcnref, &pmvalue) > 0) {
133
0
            check_read_type_only(*pmvalue, t_array);
134
0
            if (r_size(pmvalue) == 4) {
135
0
                int code = num_params(pmvalue->value.refs + 3, 4, pwv);
136
137
0
                return (code < 0 ? code : metricsSideBearingAndWidth);
138
0
            }
139
0
        }
140
0
    }
141
1.36M
    return metricsNone;
142
1.36M
}
143
144
/*
145
 * Get CDevProc.
146
 */
147
bool
148
zchar_get_CDevProc(const gs_font_base * pbfont, ref **ppcdevproc)
149
3.46M
{
150
3.46M
    const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
151
152
3.46M
    return dict_find_string(pfdict, "CDevProc", ppcdevproc) > 0;
153
3.46M
}
154
155
/*
156
 * Consult Metrics2 and CDevProc, and call setcachedevice[2].  Return
157
 * o_push_estack if we had to call a CDevProc, or if we are skipping the
158
 * rendering process (only getting the metrics).
159
 * Returns exec_cont - a function, which must be called by caller after this function.
160
 */
161
int
162
zchar_set_cache(i_ctx_t *i_ctx_p, const gs_font_base * pbfont,
163
                const ref * pcnref, const double psb[2],
164
                const double pwidth[2], const gs_rect * pbbox,
165
                op_proc_t cont, op_proc_t *exec_cont,
166
                const double Metrics2_sbw_default[4])
167
1.36M
{
168
1.36M
    os_ptr op = osp;
169
1.36M
    ref *pcdevproc, *valueref;
170
1.36M
    int have_cdevproc;
171
1.36M
    ref rpop;
172
1.36M
    ref cid, *cidptr;
173
1.36M
    bool metrics2;
174
1.36M
    bool metrics2_use_default = false;
175
1.36M
    double w2[10];
176
1.36M
    gs_text_enum_t *penum = op_show_find(i_ctx_p);
177
178
1.36M
    if (penum == NULL)
179
0
        return_error(gs_error_invalidaccess);
180
181
1.36M
    w2[0] = pwidth[0], w2[1] = pwidth[1];
182
183
    /* Adjust the bounding box for stroking if needed. */
184
185
1.36M
    w2[2] = pbbox->p.x, w2[3] = pbbox->p.y;
186
1.36M
    w2[4] = pbbox->q.x, w2[5] = pbbox->q.y;
187
1.36M
    if (pbfont->PaintType != 0) {
188
0
        double expand = max(1.415, gs_currentmiterlimit(igs)) *
189
0
        gs_currentlinewidth(igs) / 2;
190
191
0
        w2[2] -= expand, w2[3] -= expand;
192
0
        w2[4] += expand, w2[5] += expand;
193
0
    }
194
195
    /* Check for Metrics2. */
196
197
1.36M
    {
198
1.36M
        int code = zchar_get_metrics2(pbfont, pcnref, w2 + 6);
199
200
1.36M
        if (code < 0)
201
0
            return code;
202
1.36M
        metrics2 = code > 0;
203
1.36M
    }
204
205
    /*
206
     * For FontType 9 and 11, if Metrics2 is missing, the caller provides
207
     * default Metrics2 values derived from the FontBBox.
208
     */
209
1.36M
    if (!metrics2 && Metrics2_sbw_default != NULL) {
210
0
        w2[6] = Metrics2_sbw_default[2];
211
0
        w2[7] = Metrics2_sbw_default[3];
212
0
        w2[8] = Metrics2_sbw_default[0];
213
0
        w2[9] = Metrics2_sbw_default[1];
214
0
        metrics2 = true;
215
0
        metrics2_use_default = true;
216
0
    }
217
218
    /* Check for CDevProc or "short-circuiting". */
219
220
1.36M
    have_cdevproc = zchar_get_CDevProc(pbfont, &pcdevproc);
221
222
    /* Obscure test. The CDevProc is supposed to be called with the original CID but what we get passed
223
     * here is the TT GID. So the CDevProc won't do the right thing. We need to extract the CID from the
224
     * enumerator, and use that instead.
225
     */
226
1.36M
    cidptr = (ref *)pcnref;
227
1.36M
    if (pbfont->FontType == ft_CID_TrueType && dict_find_string(&pfont_data(gs_font_parent(pbfont))->dict, "File", &valueref) > 0) {
228
0
        if (pbfont->key_name.size != pbfont->font_name.size ||
229
0
            strncmp((const char *)pbfont->key_name.chars, (const char *)pbfont->font_name.chars, pbfont->key_name.size)) {
230
231
0
            if (penum->returned.current_glyph >= GS_MIN_CID_GLYPH) {
232
0
                make_int(&cid, penum->returned.current_glyph - GS_MIN_CID_GLYPH);
233
0
            }
234
0
            else {
235
0
                make_int(&cid, penum->returned.current_glyph);
236
0
            }
237
0
            cidptr = &cid;
238
0
        }
239
0
    }
240
1.36M
    if (have_cdevproc || zchar_show_width_only(penum)) {
241
3.85k
        int i;
242
3.85k
        op_proc_t zsetc;
243
3.85k
        int nparams;
244
245
3.85k
        if (have_cdevproc) {
246
0
            check_proc_only(*pcdevproc);
247
0
            zsetc = zsetcachedevice2;
248
249
            /* If we have cdevproc and the font type is CID type 0,
250
               we'll throw away Metrics2_sbw_default that is calculated
251
               from FontBBox. */
252
0
            if (!metrics2
253
0
                || (penum->current_font->FontType == ft_CID_encrypted
254
0
                    && metrics2_use_default)) {
255
0
                w2[6] = w2[0], w2[7] = w2[1];
256
0
                w2[8] = w2[9] = 0;
257
0
            }
258
0
            nparams = 10;
259
3.85k
        } else {
260
3.85k
            make_oper(&rpop, 0, zpop);
261
3.85k
            pcdevproc = &rpop;
262
3.85k
            if (metrics2)
263
0
                zsetc = zsetcachedevice2, nparams = 10;
264
3.85k
            else
265
3.85k
                zsetc = zsetcachedevice, nparams = 6;
266
3.85k
        }
267
3.85k
        check_estack(3);
268
        /* Push the l.s.b. for .type1addpath if necessary. */
269
3.85k
        if (psb != 0) {
270
0
            push(nparams + 3);
271
0
            make_real(op - (nparams + 2), psb[0]);
272
0
            make_real(op - (nparams + 1), psb[1]);
273
3.85k
        } else {
274
3.85k
            push(nparams + 1);
275
3.85k
        }
276
26.9k
        for (i = 0; i < nparams; ++i)
277
23.1k
            make_real(op - nparams + i, w2[i]);
278
3.85k
        ref_assign(op, cidptr);
279
3.85k
        push_op_estack(cont);
280
3.85k
        push_op_estack(zsetc);
281
3.85k
        ++esp;
282
3.85k
        ref_assign(esp, pcdevproc);
283
3.85k
        return o_push_estack;
284
1.35M
    } {
285
1.35M
        int code =
286
1.35M
            (metrics2 ? gs_text_setcachedevice2(penum, w2) :
287
1.35M
             gs_text_setcachedevice(penum, w2));
288
289
1.35M
        if (code < 0)
290
0
            return code;
291
1.35M
    }
292
293
    /* No metrics modification, do the stroke or fill now. */
294
295
    /* Push the l.s.b. for .type1addpath if necessary. */
296
1.35M
    if (psb != 0) {
297
0
        push(2);
298
0
        make_real(op - 1, psb[0]);
299
0
        make_real(op, psb[1]);
300
0
    }
301
1.35M
    *exec_cont = cont;
302
1.35M
    return 0;
303
1.35M
}
304
305
/*
306
 * Get the CharString data corresponding to a glyph.  Return typecheck
307
 * if it isn't a string.
308
 */
309
static bool charstring_is_notdef_proc(const gs_memory_t *mem, const ref *);
310
static int charstring_make_notdef(gs_glyph_data_t *, gs_font *);
311
int
312
zchar_charstring_data(gs_font *font, const ref *pgref, gs_glyph_data_t *pgd)
313
7.27M
{
314
7.27M
    ref *pcstr;
315
7.27M
    ref *cffcstr;
316
7.27M
    ref *pdr = pfont_dict(font);
317
318
7.27M
    if (dict_find(&pfont_data(font)->CharStrings, pgref, &pcstr) <= 0)
319
0
        return_error(gs_error_undefined);
320
321
7.27M
    if (r_has_type(pcstr, t_integer)
322
0
       && dict_find_string(pdr, "CFFCharStrings", &cffcstr) > 0) {
323
0
        ref *pcstr2;
324
325
0
        if (!r_has_type(cffcstr, t_dictionary))
326
0
            return_error(gs_error_typecheck);
327
328
0
        if (dict_find(cffcstr, pcstr, &pcstr2) <= 0) {
329
0
            ref nd;
330
0
            make_int(&nd, 0);
331
0
            if (dict_find(cffcstr, &nd, &pcstr2) <= 0) {
332
0
                return_error(gs_error_undefined);
333
0
            }
334
0
        }
335
0
        pcstr = pcstr2;
336
0
    }
337
338
7.27M
    if (!r_has_type(pcstr, t_string)) {
339
        /*
340
         * The ADOBEPS4 Windows driver replaces the .notdef entry of
341
         * otherwise normal Type 1 fonts with the procedure
342
         *  {pop 0 0 setcharwidth}
343
         * To prevent this from making the font unembeddable in PDF files
344
         * (with our present font-writing code), we recognize this as a
345
         * special case and return a Type 1 CharString consisting of
346
         *  0 0 hsbw endchar
347
         */
348
0
        if (font->FontType == ft_encrypted &&
349
0
            charstring_is_notdef_proc(font->memory, pcstr)
350
0
            )
351
0
            return charstring_make_notdef(pgd, font);
352
0
        else {
353
            /* Bug #703779. It seems that other tools can modify type 1 fonts, using
354
             * a procedure in place of a CharString for the /.notdef. In this case the
355
             * culprit is "Polylogics DIAD White Pages Pagination". Doing this prevents
356
             * pdfwrite from being able to write the font. Obviously we cannot have a
357
             * PostScript procedure in a PDF file. Adobe Acrobat Distiller replaces
358
             * the procedure with a simple 'endchar' CharString, so we now do the
359
             * same. I've chosen to leave the specific ADOBEPS4 test above unchanged, rather
360
             * than roll it in here, because I can't find an example file for it and
361
             * can't be certain that 'pgref' will be a name in that case.
362
             */
363
0
            ref namestr;
364
365
0
            if (r_has_type(pgref, t_name)) {
366
0
                name_string_ref(pgd->memory, pgref, &namestr);
367
0
                if (r_size(&namestr) == 7 && !memcmp(namestr.value.bytes, ".notdef", 7))
368
0
                    return charstring_make_notdef(pgd, font);
369
0
            }
370
0
            return_error(gs_error_typecheck);
371
0
        }
372
0
    }
373
7.27M
    gs_glyph_data_from_string(pgd, pcstr->value.const_bytes, r_size(pcstr),
374
7.27M
                              NULL);
375
7.27M
    return 0;
376
7.27M
}
377
static bool
378
charstring_is_notdef_proc(const gs_memory_t *mem, const ref *pcstr)
379
0
{
380
0
    if (r_is_array(pcstr) && r_size(pcstr) == 4) {
381
0
        ref elts[4];
382
0
        long i;
383
384
0
        for (i = 0; i < 4; ++i)
385
0
            array_get(mem, pcstr, i, &elts[i]);
386
0
        if (r_has_type(&elts[0], t_name) &&
387
0
            r_has_type(&elts[1], t_integer) && elts[1].value.intval == 0 &&
388
0
            r_has_type(&elts[2], t_integer) && elts[2].value.intval == 0 &&
389
0
            r_has_type(&elts[3], t_name)
390
0
            ) {
391
0
            ref nref;
392
393
0
            name_enter_string(mem, "pop", &nref);
394
0
            if (name_eq(&elts[0], &nref)) {
395
0
                name_enter_string(mem, "setcharwidth", &nref);
396
0
                if (name_eq(&elts[3], &nref))
397
0
                    return true;
398
0
            }
399
0
        }
400
0
    }
401
0
    return false;
402
0
}
403
static int
404
charstring_make_notdef(gs_glyph_data_t *pgd, gs_font *font)
405
0
{
406
0
    gs_font_type1 *const pfont = (gs_font_type1 *)font;
407
0
    static const byte char_data[4] = {
408
0
        139,      /* 0 */
409
0
        139,      /* 0 */
410
0
        c1_hsbw,
411
0
        cx_endchar
412
0
    };
413
0
    uint len = max(pfont->data.lenIV, 0) + sizeof(char_data);
414
0
    byte *chars = gs_alloc_string(font->memory, len, "charstring_make_notdef");
415
416
0
    if (chars == 0)
417
0
        return_error(gs_error_VMerror);
418
0
    gs_glyph_data_from_string(pgd, chars, len, font);
419
0
    if (pfont->data.lenIV < 0)
420
0
        memcpy(chars, char_data, sizeof(char_data));
421
0
    else {
422
0
        crypt_state state = crypt_charstring_seed;
423
424
0
        memcpy(chars + pfont->data.lenIV, char_data, sizeof(char_data));
425
0
        gs_type1_encrypt(chars, chars, len, &state);
426
0
    }
427
0
    return 0;
428
0
}
429
430
/*
431
 * Enumerate the next glyph from a directory.  This is essentially a
432
 * wrapper around dict_first/dict_next to implement the enumerate_glyph
433
 * font procedure.
434
 *
435
 * Note that *prdict will be null if the font is a subfont of a
436
 * CIDFontType 0 CIDFont.
437
 */
438
int
439
zchar_enumerate_glyph(const gs_memory_t *mem, const ref *prdict, int *pindex, gs_glyph *pglyph)
440
8.70M
{
441
8.70M
    int index = *pindex - 1;
442
8.70M
    ref elt[2];
443
444
8.70M
    if (!r_has_type(prdict, t_dictionary))
445
0
        return 0;   /* *pindex was 0, is still 0 */
446
8.70M
    if (index < 0)
447
11.0k
        index = dict_first(prdict);
448
8.70M
next:
449
8.70M
    index = dict_next(prdict, index, elt);
450
8.70M
    *pindex = index + 1;
451
8.70M
    if (index >= 0) {
452
8.70M
        switch (r_type(elt)) {
453
0
            case t_integer:
454
0
                *pglyph = GS_MIN_CID_GLYPH + elt[0].value.intval;
455
0
                break;
456
8.70M
            case t_name:
457
8.70M
                *pglyph = name_index(mem, elt);
458
8.70M
                break;
459
0
            default:    /* can't handle it */
460
0
                goto next;
461
8.70M
        }
462
8.70M
    }
463
8.70M
    return 0;
464
8.70M
}