Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/zcharout.c
Line
Count
Source (jump to first uncovered line)
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
/* 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
5.21M
{
80
5.21M
    const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
81
5.21M
    ref *pmdict;
82
83
5.21M
    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
5.21M
    return metricsNone;
117
5.21M
}
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.50M
{
124
1.50M
    const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
125
1.50M
    ref *pmdict;
126
127
1.50M
    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.50M
    return metricsNone;
142
1.50M
}
143
144
/*
145
 * Get CDevProc.
146
 */
147
bool
148
zchar_get_CDevProc(const gs_font_base * pbfont, ref **ppcdevproc)
149
4.95M
{
150
4.95M
    const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
151
152
4.95M
    return dict_find_string(pfdict, "CDevProc", ppcdevproc) > 0;
153
4.95M
}
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.50M
{
168
1.50M
    os_ptr op = osp;
169
1.50M
    ref *pcdevproc, *valueref;
170
1.50M
    int have_cdevproc;
171
1.50M
    ref rpop;
172
1.50M
    ref cid, *cidptr;
173
1.50M
    bool metrics2;
174
1.50M
    bool metrics2_use_default = false;
175
1.50M
    double w2[10];
176
1.50M
    gs_text_enum_t *penum = op_show_find(i_ctx_p);
177
178
1.50M
    if (penum == NULL)
179
0
        return_error(gs_error_invalidaccess);
180
181
1.50M
    w2[0] = pwidth[0], w2[1] = pwidth[1];
182
183
    /* Adjust the bounding box for stroking if needed. */
184
185
1.50M
    w2[2] = pbbox->p.x, w2[3] = pbbox->p.y;
186
1.50M
    w2[4] = pbbox->q.x, w2[5] = pbbox->q.y;
187
1.50M
    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.50M
    {
198
1.50M
        int code = zchar_get_metrics2(pbfont, pcnref, w2 + 6);
199
200
1.50M
        if (code < 0)
201
0
            return code;
202
1.50M
        metrics2 = code > 0;
203
1.50M
    }
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.50M
    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.50M
    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.50M
    cidptr = (ref *)pcnref;
227
1.50M
    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.50M
    if (have_cdevproc || zchar_show_width_only(penum)) {
241
680
        int i;
242
680
        op_proc_t zsetc;
243
680
        int nparams;
244
245
680
        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
680
        } else {
260
680
            make_oper(&rpop, 0, zpop);
261
680
            pcdevproc = &rpop;
262
680
            if (metrics2)
263
0
                zsetc = zsetcachedevice2, nparams = 10;
264
680
            else
265
680
                zsetc = zsetcachedevice, nparams = 6;
266
680
        }
267
680
        check_estack(3);
268
        /* Push the l.s.b. for .type1addpath if necessary. */
269
680
        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
680
        } else {
274
680
            push(nparams + 1);
275
680
        }
276
4.76k
        for (i = 0; i < nparams; ++i)
277
4.08k
            make_real(op - nparams + i, w2[i]);
278
680
        ref_assign(op, cidptr);
279
680
        push_op_estack(cont);
280
680
        push_op_estack(zsetc);
281
680
        ++esp;
282
680
        ref_assign(esp, pcdevproc);
283
680
        return o_push_estack;
284
1.50M
    } {
285
1.50M
        int code =
286
1.50M
            (metrics2 ? gs_text_setcachedevice2(penum, w2) :
287
1.50M
             gs_text_setcachedevice(penum, w2));
288
289
1.50M
        if (code < 0)
290
0
            return code;
291
1.50M
    }
292
293
    /* No metrics modification, do the stroke or fill now. */
294
295
    /* Push the l.s.b. for .type1addpath if necessary. */
296
1.50M
    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.50M
    *exec_cont = cont;
302
1.50M
    return 0;
303
1.50M
}
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.29M
{
314
7.29M
    ref *pcstr;
315
7.29M
    ref *cffcstr;
316
7.29M
    ref *pdr = pfont_dict(font);
317
318
7.29M
    if (dict_find(&pfont_data(font)->CharStrings, pgref, &pcstr) <= 0)
319
0
        return_error(gs_error_undefined);
320
321
7.29M
    if (r_has_type(pcstr, t_integer)
322
7.29M
       && dict_find_string(pdr, "CFFCharStrings", &cffcstr) > 0) {
323
0
        ref *pcstr2;
324
0
        if (dict_find(cffcstr, pcstr, &pcstr2) <= 0) {
325
0
            ref nd;
326
0
            make_int(&nd, 0);
327
0
            if (dict_find(cffcstr, &nd, &pcstr2) <= 0) {
328
0
                return_error(gs_error_undefined);
329
0
            }
330
0
        }
331
0
        pcstr = pcstr2;
332
0
    }
333
334
7.29M
    if (!r_has_type(pcstr, t_string)) {
335
        /*
336
         * The ADOBEPS4 Windows driver replaces the .notdef entry of
337
         * otherwise normal Type 1 fonts with the procedure
338
         *  {pop 0 0 setcharwidth}
339
         * To prevent this from making the font unembeddable in PDF files
340
         * (with our present font-writing code), we recognize this as a
341
         * special case and return a Type 1 CharString consisting of
342
         *  0 0 hsbw endchar
343
         */
344
0
        if (font->FontType == ft_encrypted &&
345
0
            charstring_is_notdef_proc(font->memory, pcstr)
346
0
            )
347
0
            return charstring_make_notdef(pgd, font);
348
0
        else {
349
            /* Bug #703779. It seems that other tools can modify type 1 fonts, using
350
             * a procedure in place of a CharString for the /.notdef. In this case the
351
             * culprit is "Polylogics DIAD White Pages Pagination". Doing this prevents
352
             * pdfwrite from being able to write the font. Obviously we cannot have a
353
             * PostScript procedure in a PDF file. Adobe Acrobat Distiller replaces
354
             * the procedure with a simple 'endchar' CharString, so we now do the
355
             * same. I've chosen to leave the specific ADOBEPS4 test above unchanged, rather
356
             * than roll it in here, because I can't find an example file for it and
357
             * can't be certain that 'pgref' will be a name in that case.
358
             */
359
0
            ref namestr;
360
361
0
            if (r_has_type(pgref, t_name)) {
362
0
                name_string_ref(pgd->memory, pgref, &namestr);
363
0
                if (r_size(&namestr) == 7 && !memcmp(namestr.value.bytes, ".notdef", 7))
364
0
                    return charstring_make_notdef(pgd, font);
365
0
            }
366
0
            return_error(gs_error_typecheck);
367
0
        }
368
0
    }
369
7.29M
    gs_glyph_data_from_string(pgd, pcstr->value.const_bytes, r_size(pcstr),
370
7.29M
                              NULL);
371
7.29M
    return 0;
372
7.29M
}
373
static bool
374
charstring_is_notdef_proc(const gs_memory_t *mem, const ref *pcstr)
375
0
{
376
0
    if (r_is_array(pcstr) && r_size(pcstr) == 4) {
377
0
        ref elts[4];
378
0
        long i;
379
380
0
        for (i = 0; i < 4; ++i)
381
0
            array_get(mem, pcstr, i, &elts[i]);
382
0
        if (r_has_type(&elts[0], t_name) &&
383
0
            r_has_type(&elts[1], t_integer) && elts[1].value.intval == 0 &&
384
0
            r_has_type(&elts[2], t_integer) && elts[2].value.intval == 0 &&
385
0
            r_has_type(&elts[3], t_name)
386
0
            ) {
387
0
            ref nref;
388
389
0
            name_enter_string(mem, "pop", &nref);
390
0
            if (name_eq(&elts[0], &nref)) {
391
0
                name_enter_string(mem, "setcharwidth", &nref);
392
0
                if (name_eq(&elts[3], &nref))
393
0
                    return true;
394
0
            }
395
0
        }
396
0
    }
397
0
    return false;
398
0
}
399
static int
400
charstring_make_notdef(gs_glyph_data_t *pgd, gs_font *font)
401
0
{
402
0
    gs_font_type1 *const pfont = (gs_font_type1 *)font;
403
0
    static const byte char_data[4] = {
404
0
        139,      /* 0 */
405
0
        139,      /* 0 */
406
0
        c1_hsbw,
407
0
        cx_endchar
408
0
    };
409
0
    uint len = max(pfont->data.lenIV, 0) + sizeof(char_data);
410
0
    byte *chars = gs_alloc_string(font->memory, len, "charstring_make_notdef");
411
412
0
    if (chars == 0)
413
0
        return_error(gs_error_VMerror);
414
0
    gs_glyph_data_from_string(pgd, chars, len, font);
415
0
    if (pfont->data.lenIV < 0)
416
0
        memcpy(chars, char_data, sizeof(char_data));
417
0
    else {
418
0
        crypt_state state = crypt_charstring_seed;
419
420
0
        memcpy(chars + pfont->data.lenIV, char_data, sizeof(char_data));
421
0
        gs_type1_encrypt(chars, chars, len, &state);
422
0
    }
423
0
    return 0;
424
0
}
425
426
/*
427
 * Enumerate the next glyph from a directory.  This is essentially a
428
 * wrapper around dict_first/dict_next to implement the enumerate_glyph
429
 * font procedure.
430
 *
431
 * Note that *prdict will be null if the font is a subfont of a
432
 * CIDFontType 0 CIDFont.
433
 */
434
int
435
zchar_enumerate_glyph(const gs_memory_t *mem, const ref *prdict, int *pindex, gs_glyph *pglyph)
436
6.79M
{
437
6.79M
    int index = *pindex - 1;
438
6.79M
    ref elt[2];
439
440
6.79M
    if (!r_has_type(prdict, t_dictionary))
441
0
        return 0;   /* *pindex was 0, is still 0 */
442
6.79M
    if (index < 0)
443
8.48k
        index = dict_first(prdict);
444
6.79M
next:
445
6.79M
    index = dict_next(prdict, index, elt);
446
6.79M
    *pindex = index + 1;
447
6.79M
    if (index >= 0) {
448
6.79M
        switch (r_type(elt)) {
449
0
            case t_integer:
450
0
                *pglyph = GS_MIN_CID_GLYPH + elt[0].value.intval;
451
0
                break;
452
6.79M
            case t_name:
453
6.79M
                *pglyph = name_index(mem, elt);
454
6.79M
                break;
455
0
            default:    /* can't handle it */
456
0
                goto next;
457
6.79M
        }
458
6.79M
    }
459
6.79M
    return 0;
460
6.79M
}