Coverage Report

Created: 2026-04-09 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/pcl/pcl/pctext.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
/* pctext.c -  PCL5 text printing commands */
18
19
#include "math_.h"
20
#include "gx.h"
21
#include "gsimage.h"
22
#include "plvalue.h"
23
#include "plvocab.h"
24
#include "pcommand.h"
25
#include "pcstate.h"
26
#include "pcdraw.h"
27
#include "pcfont.h"
28
#include "pcursor.h"
29
#include "pcpage.h"
30
#include "pcfrgrnd.h"
31
#include "gdebug.h"
32
#include "gscoord.h"
33
#include "gsline.h"
34
#include "gspaint.h"
35
#include "gspath.h"
36
#include "gspath2.h"
37
#include "gsrop.h"
38
#include "gsstate.h"
39
#include "gxchar.h"
40
#include "gxfont.h"             /* for setting next_char proc */
41
#include "gxstate.h"
42
43
#include "gxdevsop.h"       /* For special ops */
44
#include "gsdevice.h"       /* for gs_currentdevice */
45
46
/* pseudo-"dots" (actually 1/300" units) used in underline only */
47
0
#define dots(n)     ((float)(7200 / 300 * n))
48
49
/*
50
 * Install a font in the graphic state.
51
 */
52
static void
53
set_gs_font(pcl_state_t * pcs)
54
11.6M
{
55
11.6M
    gs_font *pfont = (gs_font *) pcs->font->pfont;
56
57
11.6M
    gs_setfont(pcs->pgs, pfont);
58
    /* font scaling is reflected directly in the ctm */
59
11.6M
    pfont->FontMatrix = pfont->orig_FontMatrix;
60
11.6M
}
61
62
bool
63
pcl_downloaded_and_bound(const pl_font_t * plfont)
64
34.7M
{
65
34.7M
    return (plfont->storage != pcds_internal && pl_font_is_bound(plfont));
66
34.7M
}
67
68
/*
69
 * Check if a character code is considered "printable" by given symbol set.
70
 */
71
bool
72
char_is_printable(const pl_font_t *font, const pl_symbol_map_t *map, gs_char chr, bool is_stick, bool literal)
73
18.4M
{
74
18.4M
    bool printable = false;
75
76
18.4M
    if (literal) {              /* transparent data */
77
1.49M
        printable = true;
78
16.9M
    } else {
79
16.9M
        if (is_stick) {
80
164k
            printable = (chr >= ' ') && (chr <= 0xff);
81
16.8M
        } else {
82
16.8M
            int map_type = 0;
83
16.8M
            if (map == 0 || pcl_downloaded_and_bound(font)) {
84
                /* PCL TRM 11-18 */
85
0
                if (font)
86
0
                {
87
0
                    map_type = font->font_type;
88
0
                }
89
16.8M
            } else {
90
                /* PCL TRM 10-7
91
                 * symbol map type overrides, font map type
92
                 */
93
16.8M
                map_type = map->type;
94
16.8M
            }
95
96
            /* We do not treat map type as defined in the
97
            specification. Instead the default is to use the behavior we have
98
            observed on several HP devices: Map type 0 is treated as map type
99
            1. */
100
16.8M
            if (map_type == 0) {
101
0
                map_type = 1;
102
0
            }
103
104
16.8M
            if (map_type == 1) {
105
2.68M
                chr &= 0x7f;
106
2.68M
                printable = (chr >= ' ');   /* 0-31 and 128-159 are not printable */
107
14.1M
            } else if (map_type >= 2) {
108
                /* 2 is correct but will force all types above 2 here */
109
14.1M
                if ((chr == 0) || (chr == '\033') ||
110
12.7M
                    ((chr >= '\007') && (chr <= '\017')))
111
1.61M
                    printable = false;
112
12.5M
                else
113
12.5M
                    printable = true;
114
14.1M
            }
115
16.8M
        }
116
16.9M
    }
117
18.4M
    return printable;
118
18.4M
}
119
120
static bool
121
substituting_allowed(pcl_state_t * pcs, gs_char mapped_chr)
122
105k
{
123
105k
    gs_char remapped_chr;       /* NB wrong type */
124
125
105k
    if (
126
           /* msl not yet supported. */
127
105k
           (pcs->map && pcs->map->format == 1) ||
128
           /* by experiment HP does not support substitution with bitmap fonts */
129
105k
           (pcs->font->scaling_technology == plfst_bitmap) ||
130
           /* the font must be downloaded */
131
105k
           (!(pcs->font->storage & pcds_downloaded)))
132
105k
        return false;
133
134
    /* mapped chr is something of a misnomer, if the font is bound and
135
       downloaded it has been identity mappped. */
136
137
0
    remapped_chr = pl_map_symbol(pcs->map, mapped_chr, false,   /* storage not internal */
138
0
                                 false, /* unicode not msl */
139
0
                                 pcs->memory);
140
141
    /* now we can assume the characters are unicode */
142
0
    if (
143
           /* arrows */
144
0
           ((remapped_chr >= 0x2190) && (remapped_chr <= 0x21FF)) ||
145
           /* coptic */
146
0
           ((remapped_chr >= 0x0370) && (remapped_chr <= 0x03FF)) ||
147
           /* math operators */
148
0
           ((remapped_chr >= 0x2200) && (remapped_chr <= 0x22FF)) ||
149
           /* box drawing characters */
150
0
           ((remapped_chr >= 0x2500) && (remapped_chr <= 0x257F)) ||
151
           /* block elements (contiguous with box drawing) */
152
0
           ((remapped_chr >= 0x2580) && (remapped_chr <= 0x259F)) ||
153
           /* Geometric shapes (contiguos with block elements) */
154
0
           ((remapped_chr >= 0x25A0) && (remapped_chr <= 0x25FF)) ||
155
           /* miscellaneous symbols */
156
0
           ((remapped_chr >= 0x2600) && (remapped_chr <= 0x26FF)) ||
157
           /* miscellaneous technical */
158
0
           ((remapped_chr >= 0x2300) && (remapped_chr <= 0x23FF)) ||
159
           /* general punctuation */
160
0
           ((remapped_chr >= 0x2000) && (remapped_chr <= 0x206F)) ||
161
           /* vertical line,  less than, greater than, low line, or micro sign character,  */
162
0
           ((remapped_chr == 0x007C) || (remapped_chr == 0x003C) ||
163
0
            (remapped_chr == 0x003E) || (remapped_chr == 0x005F) ||
164
0
            (remapped_chr == 0x00B5))
165
0
        )
166
0
        return true;
167
0
    return false;
168
0
}
169
170
/*
171
 * Retrieve the next character identifier from a string.
172
 *
173
 * Both the string pointer and the length are modified.
174
 *
175
 * The final operand is true if the text was provided via the literal
176
 * (transparent) text command: ESC & p <nbytes> X. This distinction is
177
 * important for characters that are not considered printable by the
178
 * current symbol set. Normally, such characters are ignored. But if they
179
 * resulted from the literal (transparent) text command, they are handled as
180
 * spaces. Characters that are mapped by the symbol set but are not in a font
181
 * are always dealt with as space characters.
182
 *
183
 * The special handling provided for the character code 32 below is not,
184
 * in fact, correct. PCL fonts may map non-space characters to code 32, and
185
 * if this is done no special handling is provided for this code; in PCL,
186
 * a space character is a character not present in the font. Unfortunately,
187
 * some of the resident fonts used have explicit space characters, and to
188
 * handle the hmi properly when these fonts are used, this code must handle
189
 * fonts that have actual characters at code 32 improperly.
190
 *
191
 * Returns 0 on success, 2 if the string is exhausted. Note that it is not an
192
 * error for the string to end in the middle of a 2-byte sequence.
193
 */
194
static int
195
get_next_char(pcl_state_t * pcs,
196
              const byte ** ppb,
197
              uint * plen,
198
              gs_char * pchr,
199
              gs_char * porig_char,
200
              bool * pis_space,
201
              bool * pprint_undefined,
202
              bool literal, gs_point * pwidth, bool * unstyled_substitution)
203
21.6M
{
204
21.6M
    const byte *pb = *ppb;
205
21.6M
    int len = *plen;
206
21.6M
    pl_font_t *plfont = pcs->font;
207
21.6M
    bool substituting = false;
208
21.6M
    int code = 0;
209
21.6M
    gs_char chr;
210
21.6M
    gs_char mapped_chr;         /* NB wrong type */
211
21.6M
    bool db;
212
213
21.6M
    if (len <= 0)
214
3.66M
        return 2;
215
18.0M
    *pis_space = false;
216
18.0M
    *unstyled_substitution = false;
217
18.0M
    chr = pcl_char_get_char(pcs->text_parsing_method, &pb, len);
218
    /* invalid char: pb has not been incremented */
219
18.0M
    if (pb == *ppb) {
220
0
        pb++;
221
0
    }
222
18.0M
    len -= (pb - *ppb);
223
18.0M
    *ppb = pb;
224
18.0M
    *plen = len;
225
18.0M
    *porig_char = chr;
226
    /* check if the code is considered "printable" in the current symbol set */
227
18.0M
    if (!char_is_printable(pcs->font, pcs->map, chr, false, literal)) {
228
1.70M
        *pis_space = literal;
229
1.70M
        *pchr = 0xffff;
230
1.70M
        return 0;
231
1.70M
    }
232
233
    /* map the symbol.  If the font is downloaded and bound there is
234
       no symbol set.  We do use the symbol set for internal bound
235
       fonts. NB WE AREN'T HAPPY WITH THIS LABEL & GOTO. */
236
16.2M
  r:db = pcl_downloaded_and_bound(plfont);
237
16.2M
    mapped_chr = pl_map_symbol((db ? NULL : pcs->map), chr,
238
16.2M
                               plfont->storage == pcds_internal,
239
16.2M
                               plfont->font_type == plft_MSL,
240
16.2M
                               pcs->memory);
241
16.2M
    *pchr = mapped_chr;
242
16.2M
    if (mapped_chr == 0xffff) {
243
39.0k
        if ((plfont->storage != pcds_internal) &&
244
0
            (pl_font_char_width
245
0
             (plfont, (void *)(pcs->pgs), mapped_chr, pwidth) == 0)) {
246
0
            *pprint_undefined = true;
247
0
            return 0;
248
0
        }
249
39.0k
        *pis_space = true;
250
39.0k
        return 0;
251
39.0k
    }
252
253
    /* NB we assume all internal fonts use unicode */
254
16.2M
    if (plfont->storage == pcds_internal && mapped_chr == 0x0020
255
77.9k
        && !substituting) {
256
77.9k
        *pis_space = true;
257
77.9k
        *pchr = 0xffff;
258
77.9k
        return 0;
259
77.9k
    }
260
261
    /* For internal fonts we simulate the font missing the space
262
       character here.  The character complement is checked to see if
263
       the the font is Western Latin and Unicode 0020.  We could
264
       also check for an MSL space here but we know the internal
265
       reportoire will never contain an MSL font that requires
266
       simulating a missing space character. */
267
16.1M
    if (plfont->storage == pcds_internal &&
268
16.1M
        chr == 0x0020 &&
269
0
        plfont->character_complement[5] == 0x3f &&
270
0
        pl_complement_to_vocab(plfont->character_complement) ==
271
0
        plgv_Unicode) {
272
0
        *pis_space = true;
273
0
        *pchr = 0xffff;
274
0
        return 0;
275
0
    }
276
277
    /* check if the character is in the font and get the character
278
       width at the same time */
279
16.1M
    if (*pis_space == false)
280
16.1M
        if (pl_font_char_width(plfont, (void *)(pcs->pgs), mapped_chr, pwidth)
281
16.1M
            == 0)
282
16.0M
            return 0;
283
284
    /*
285
     * Try an unstyled substitution
286
     */
287
105k
    if (!substituting && substituting_allowed(pcs, db ? mapped_chr : chr)) {
288
0
        pcl_decache_font(pcs, -1, true);
289
0
        code = pcl_recompute_font(pcs, true);
290
0
        if (code < 0)
291
0
            return code;
292
0
        substituting = true;
293
0
        *unstyled_substitution = true;
294
0
        plfont = pcs->font;
295
0
        set_gs_font(pcs);
296
0
        goto r;
297
0
    }
298
299
    /* we substituted and didn't find the character in the font.
300
       Restore the old font */
301
105k
    if (substituting) {
302
0
        pcl_decache_font(pcs, -1, true);
303
0
        code = pcl_recompute_font(pcs, false);
304
0
        if (code < 0)
305
0
            return code;
306
0
        set_gs_font(pcs);
307
0
    }
308
309
    /*
310
     * If we get to this point deem the character an undefined
311
     * character - a space in pcl.
312
     */
313
105k
    *pis_space = true;
314
105k
    *pchr = 0xffff;
315
105k
    return 0;
316
105k
}
317
/*
318
 * return length of multibyte sequence from starting byte
319
 * replacement of macro pcl_char_is_2_byte, UTF-8 sequence length may be up to 6 bytes
320
 *
321
 * Returns 0 for invalid byte, byte length > 0 of multibyte character sequence
322
 */
323
int
324
pcl_char_bytelen(byte ch, pcl_text_parsing_method_t tpm)
325
28.9M
{
326
327
28.9M
    int bytelen = 1;
328
329
28.9M
    switch (tpm) {
330
27.8M
        default:
331
            /* byte length defaults to 1 */
332
27.8M
            break;
333
334
27.8M
        case tpm_21_DBCS7:
335
            /* 0x21-0xff are double-byte */
336
0
            bytelen = (ch < 0x21) ? 1 : 2;
337
0
            break;
338
339
0
        case tpm_31_sjis:
340
            /* 0x81-0x9f, 0xe0-0xfc are double-byte */
341
0
            bytelen = (ch < 0x81 || (ch > 0x9f && ch < 0xe0)
342
0
                       || ch > 0xfc) ? 1 : 2;
343
0
            break;
344
345
0
        case tpm_38_DBCS8:
346
            /* 0x80-0xff are double-byte */
347
0
            bytelen = (ch < 0x80) ? 1 : 2;
348
0
            break;
349
1.07M
        case tpm_83_utf8:
350
1.07M
        case tpm_1008_utf8:
351
1.07M
            if (ch < 0x80) {
352
                /* 0xxxxxxx */
353
910k
                bytelen = 1;
354
910k
                break;
355
910k
            }
356
168k
            if (ch < 0xc2) {
357
0
                bytelen = 0;    /* illegal */
358
0
                break;
359
0
            }
360
168k
            if (ch < 0xe0) {
361
                /* 110XXXXx 10xxxxxx */
362
93.9k
                bytelen = 2;
363
93.9k
                break;
364
93.9k
            }
365
74.3k
            if (ch < 0xf0) {
366
                /* 1110XXXX 10Xxxxxx 10xxxxxx */
367
74.3k
                bytelen = 3;
368
74.3k
                break;
369
74.3k
            }
370
14
            if (ch < 0xf8) {
371
                /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
372
14
                bytelen = 4;
373
14
                break;
374
14
            }
375
0
            if (ch < 0xfc) {
376
                /* 111110XX 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
377
0
                bytelen = 5;
378
0
                break;
379
0
            }
380
0
            if (ch < 0xfe) {
381
                /* 1111110X 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
382
0
                bytelen = 6;
383
0
                break;
384
0
            }
385
0
            bytelen = 0;        /* illegal */
386
0
            break;
387
28.9M
    }
388
28.9M
    return bytelen;
389
28.9M
}
390
/*
391
 * convert multibyte sequence to unicode (16-bit)
392
 * Both the string pointer and the length are modified.
393
 *
394
 * Returns 0 for invalid byte, byte length > 0 of multibyte character sequence
395
 */
396
gs_char
397
pcl_char_get_char(pcl_text_parsing_method_t tpm, const byte ** psrc,
398
                  int src_len)
399
/* src_len minimum 1 */
400
18.0M
{
401
18.0M
    gs_char chr;
402
18.0M
    const byte *src = *psrc;
403
18.0M
    int bytelen = pcl_char_bytelen(src[0], tpm);
404
405
18.0M
    if (bytelen == 0 || bytelen > src_len) {
406
0
        return INVALID_UC;
407
0
    }
408
18.0M
    switch (tpm) {
409
17.4M
        default:
410
17.4M
            chr = src[0];
411
17.4M
            break;
412
413
0
        case tpm_21_DBCS7:
414
            /* 0x21-0xff are double-byte */
415
0
            chr = (src[0] < 0x21) ? src[0] : (src[0] << 8 | src[1]);
416
0
            break;
417
418
0
        case tpm_31_sjis:
419
            /* 0x81-0x9f, 0xe0-0xfc are double-byte */
420
0
            chr = (src[0] < 0x81 || (src[0] > 0x9f && src[0] < 0xe0)
421
0
                   || src[0] > 0xfc) ? src[0] : (src[0] << 8 | src[1]);
422
0
            break;
423
424
0
        case tpm_38_DBCS8:
425
            /* 0x80-0xff are double-byte */
426
0
            chr = (src[0] < 0x80) ? src[0] : (src[0] << 8 | src[1]);
427
0
            break;
428
533k
        case tpm_83_utf8:
429
533k
        case tpm_1008_utf8:
430
533k
            if (src[0] < 0x80) {
431
                /* 0xxxxxxx */
432
449k
                chr = src[0];
433
449k
                break;
434
449k
            }
435
84.1k
            if (src[0] < 0xc2) {
436
0
                chr = INVALID_UC;
437
0
                break;
438
0
            }
439
84.1k
            if (src[0] < 0xe0) {
440
                /* 110XXXXx 10xxxxxx */
441
46.9k
                chr = (src[0] & 0x1f);
442
46.9k
                chr = (chr << 6) | (src[1] & 0x3f);
443
46.9k
                break;
444
46.9k
            }
445
37.1k
            if (src[0] < 0xf0) {
446
                /* 1110XXXX 10Xxxxxx 10xxxxxx */
447
37.1k
                chr = (src[0] & 0x0f);
448
37.1k
                chr = (chr << 6) | (src[1] & 0x3f);
449
37.1k
                chr = (chr << 6) | (src[2] & 0x3f);
450
37.1k
                break;
451
37.1k
            }
452
7
            if (src[0] < 0xf8) {
453
                /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
454
                /* chr is 16 bit: overflow */
455
7
                chr = INVALID_UC;
456
7
                break;
457
7
            }
458
0
            if (src[0] < 0xfc) {
459
                /* 111110XX 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
460
                /* chr is 16 bit: overflow */
461
0
                chr = INVALID_UC;
462
0
                break;
463
0
            }
464
0
            if (src[0] < 0xfe) {
465
                /* 1111110X 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
466
                /* chr is 16 bit: overflow */
467
0
                chr = INVALID_UC;
468
0
                break;
469
0
            }
470
0
            chr = INVALID_UC;
471
0
            break;
472
18.0M
    }
473
18.0M
    *psrc += bytelen;
474
18.0M
    return chr;
475
18.0M
}
476
477
/*
478
 * Draw the foreground of a character. For transparent text this is the only
479
 * part that must be drawn.
480
 */
481
static int
482
show_char_foreground(const pcl_state_t * pcs, const gs_char * pbuff)
483
9.37M
{
484
9.37M
    int code = 0;
485
9.37M
    gs_text_enum_t *penum;
486
9.37M
    pl_font_t *plfont = pcs->font;
487
9.37M
    gs_font *pfont = plfont->pfont;
488
9.37M
    gs_text_params_t text;
489
490
    /* set vertical writing if -1 which requires double bytes or 1 */
491
9.37M
    if ((pcs->text_path == -1 && ((pbuff[0] & 0xff00) != 0)) ||
492
9.37M
        (pcs->text_path == 1))
493
0
        pfont->WMode = 1;
494
9.37M
    else
495
9.37M
        pfont->WMode = 0;
496
9.37M
    text.operation = TEXT_FROM_CHARS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
497
9.37M
    text.data.chars = pbuff;
498
9.37M
    text.size = 1;
499
9.37M
    code = gs_text_begin(pcs->pgs, &text, pcs->memory, &penum);
500
9.37M
    if (code >= 0) {
501
9.37M
        code = gs_text_process(penum);
502
9.37M
        gs_text_release(pcs->pgs, penum, "show_char_foreground");
503
9.37M
    }
504
9.37M
    return code;
505
9.37M
}
506
507
static int
508
show_char_invisible_foreground(const pcl_state_t * pcs, const gs_char * pbuff)
509
0
{
510
511
0
    gs_c_param_list list;
512
0
    dev_param_req_t request;
513
0
    gs_param_name ParamName = "PreserveTrMode";
514
0
    gs_param_typed_value Param;
515
0
    char *data;
516
0
    gs_gstate *pgs = pcs->pgs;
517
0
    uint saved_mode = gs_currenttextrenderingmode(pgs);
518
0
    int code = 0;
519
520
    /* Interrogate the device to see if it supports Text Rendering Mode
521
     * If it does we can mimic the 'invisible text' by using mode 3. If it
522
     * doesn't then we drop the text.
523
     */
524
0
    data = (char *)gs_alloc_bytes(pcs->memory, 15, "temporary special_op string");
525
0
    if (data == NULL)
526
0
        return_error(gs_error_VMerror);
527
0
    memset(data, 0x00, 15);
528
0
    memcpy(data, "PreserveTrMode", 15);
529
0
    gs_c_param_list_write(&list, pcs->memory);
530
    /* Make a null object so that the param list won't check for requests */
531
0
    Param.type = gs_param_type_null;
532
0
    list.procs->xmit_typed((gs_param_list *)&list, ParamName, &Param);
533
    /* Stuff the data into a structure for passing to the spec_op */
534
0
    request.Param = data;
535
0
    request.list = &list;
536
537
0
    code = dev_proc(gs_currentdevice(pgs), dev_spec_op)(gs_currentdevice(pgs), gxdso_get_dev_param,
538
0
                                                        &request, sizeof(dev_param_req_t));
539
540
0
    if (code != gs_error_undefined) {
541
        /* The parameter is present in the device, now we need to see its value */
542
0
        gs_c_param_list_read(&list);
543
0
        list.procs->xmit_typed((gs_param_list *)&list, ParamName, &Param);
544
545
0
        if (Param.type != gs_param_type_bool) {
546
            /* This really shoudn't happen, but its best to be sure */
547
0
            gs_free_object(pcs->memory, data,"temporary special_op string");
548
0
            gs_c_param_list_release(&list);
549
0
            return gs_error_typecheck;
550
0
        }
551
552
0
        if (Param.value.b) {
553
            /* Its true, so we can set the Tr mode to 3, draw the text
554
               and then reset the Tr mode */
555
0
            gs_settextrenderingmode(pgs, 3);
556
0
            code = show_char_foreground(pcs, pbuff);
557
0
            gs_settextrenderingmode(pgs, saved_mode);
558
0
        }
559
0
    } else {
560
0
        code = 0;
561
0
    }
562
0
    gs_free_object(pcs->memory, data,"temporary special_op string");
563
0
    gs_c_param_list_release(&list);
564
0
    return code;
565
0
}
566
567
568
569
/*
570
 * draw the opaque background of a character.
571
 *
572
 * In the graphic library, characters are masks, hence they are always
573
 * transparent. Not so in PCL, where characters may be either opaque or
574
 * transparent.
575
 *
576
 * To deal with this dichotomy, opaque characters are rendered as a pair of
577
 * masks. One is the normal character mask; the other is the bounding box of
578
 * the character less the character itself.
579
 *
580
 * The manner in which the second mask is formed varies based on the font type.
581
 * For bitmap fonts, the inverse mask is formed as an imagemask object, with
582
 * inverted polarity. For scalable fonts (which have only provided a path),
583
 * the inverse is formed by adding the bounding box rectangle as a path to
584
 * the character path, and using eofill on the resultant path.
585
 *
586
 * Special handling is required to achieve the desired raster operation on the
587
 * "background" mask. From the point of view of the graphic library, the
588
 * background mask is a normal mask, and hence would utiltise the S = 0
589
 * portion of the current logical operation (recall that rop's are expressed
590
 * in an additive sense). The desired effect is, however, the S = 1 portion
591
 * of the current rop, so the current rop must be inverted in the sense of the
592
 * source to achive the desired result. In principle, the S = 1 porition of
593
 * the background rop should be set to the no-op rop, but this is not necessary
594
 * as the source is a mask.
595
 *
596
 * An additional complication arises from the specification provided by HP for
597
 * handling the source opaque, pattern transparent situation. In this case,
598
 * the pattern affects only for the foreground pixels of the source; the back-
599
 * ground must be rendered as a solid, opaque white.
600
 */
601
static int
602
show_char_background(pcl_state_t * pcs, const gs_char * pbuff)
603
14.9k
{
604
14.9k
    gs_gstate *pgs = pcs->pgs;
605
14.9k
    gs_rop3_t rop = (gs_rop3_t) (pcs->logical_op);
606
14.9k
    const pl_font_t *plfont = pcs->font;
607
14.9k
    gs_font *pfont = plfont->pfont;
608
14.9k
    gs_point pt;
609
14.9k
    int code = 0, code2;
610
611
    /* save the graphic state and set the background raster operation */
612
14.9k
    code = pcl_gsave(pcs);
613
14.9k
    if (code < 0)
614
0
        return code;
615
14.9k
    if (pcs->pattern_transparent) {
616
14.9k
        code = pcl_set_drawing_color(pcs, pcl_pattern_solid_white, 0, false);
617
14.9k
        if (code < 0) {
618
0
            (void)pcl_grestore(pcs);
619
0
            return code;
620
0
        }
621
14.9k
    }
622
    /* In this case, instead of just ignoring the unsupported rasterop we
623
     * abort the operation. Otherwise we end up with lots of black rectangles
624
     * over the text. Dropping the background works 'better'.
625
     */
626
14.9k
    code = check_rasterops(pcs, (gs_rop3_t) rop3_know_S_1((int)rop));
627
14.9k
    if (code < 0) {
628
0
        (void)pcl_grestore(pcs);
629
0
        return 0;
630
0
    }
631
14.9k
    if (((code = gs_setrasterop(pgs, (gs_rop3_t) rop3_know_S_1((int)rop))) < 0) ||
632
14.9k
        ((code = gs_currentpoint(pgs, &pt)) < 0)) {
633
0
        (void)pcl_grestore(pcs);
634
0
        return code;
635
0
    }
636
637
14.9k
    if (plfont->scaling_technology == plfst_bitmap) {
638
0
        gs_char chr = pbuff[0];
639
0
        gs_glyph glyph = pfont->procs.encode_char(pfont, chr, GS_NO_GLYPH);
640
0
        const byte *cdata = pl_font_lookup_glyph(plfont, glyph)->data;
641
0
        int nbytes;
642
0
        uint used;
643
0
        gs_image_enum *pen = NULL;
644
0
        gs_image1_t mask;
645
646
        /* empty characters have no background */
647
0
        if (cdata == 0) {
648
0
            return pcl_grestore(pcs);
649
0
        }
650
651
        /* allocate the image enumerator */
652
0
        pen =
653
0
            gs_image_enum_alloc(gs_gstate_memory(pgs),
654
0
                                "bitmap font background");
655
0
        if (pen == 0) {
656
0
            (void)pcl_grestore(pcs);
657
0
            return e_Memory;
658
0
        }
659
660
        /* translate the origin to the ul corner of the image */
661
0
        pt.x += (float)pl_get_int16(cdata + 6);
662
0
        pt.y -= (float)pl_get_int16(cdata + 8);
663
0
        gs_translate(pgs, pt.x, pt.y);
664
665
        /* set up and render the image mask */
666
0
        gs_image_t_init_mask(&mask, false);
667
0
        mask.adjust = false;
668
0
        mask.Width = pl_get_uint16(cdata + 10);
669
0
        mask.Height = pl_get_uint16(cdata + 12);
670
0
        nbytes = ((mask.Width + 7) / 8) * mask.Height;
671
0
        code = gs_image_init(pen, &mask, false, false, pgs);
672
0
        if (code >= 0)
673
0
            code = gs_image_next(pen, cdata + 16, nbytes, &used);
674
675
        /* clean up */
676
0
        code2 = gs_image_cleanup(pen, pgs);
677
0
        if (code >= 0)
678
0
            code = code2;
679
0
        gs_free_object(gs_gstate_memory(pgs), pen, "bitmap font background");
680
681
14.9k
    } else {
682
14.9k
        gs_text_params_t text;
683
14.9k
        gs_rect bbox;
684
14.9k
        gs_text_enum_t *penum = NULL;
685
686
        /* clear the path; start the new one from the current point */
687
14.9k
        if (((code = gs_newpath(pgs)) < 0) ||
688
14.9k
            ((code = gs_moveto(pgs, pt.x, pt.y)) < 0)) {
689
0
            (void)pcl_grestore(pcs);
690
0
            return code;
691
0
        }
692
14.9k
        text.data.chars = pbuff;
693
14.9k
        text.size = 1;
694
14.9k
        text.operation =
695
14.9k
            TEXT_FROM_CHARS | TEXT_DO_TRUE_CHARPATH | TEXT_RETURN_WIDTH;
696
14.9k
        code = gs_text_begin(pgs, &text, pcs->memory, &penum);
697
14.9k
        if (code >= 0)
698
14.9k
            code = gs_text_process(penum);
699
14.9k
        if (code >= 0) {
700
            /* append the characters bounding box and use eofill */
701
14.9k
            if ((code = gs_pathbbox(pgs, &bbox)) >= 0 &&
702
14.9k
                (code = gs_rectappend(pgs, &bbox, 1)) >= 0 &&
703
14.9k
                (code = gs_eofill(pgs)) >= 0)
704
14.9k
            {
705
                /* fall through */
706
14.9k
            }
707
14.9k
        }
708
14.9k
        gs_text_release(pgs, penum, "show_char_background");
709
14.9k
    }
710
711
14.9k
    code2 = pcl_grestore(pcs);
712
14.9k
    if (code >= 0)
713
14.9k
        code = code2;
714
14.9k
    return code;
715
14.9k
}
716
717
/*
718
 * Set color and ctm for a font
719
 */
720
static int
721
pcl_set_gstate_for_font(pcl_state_t *pcs, const gs_point *scale)
722
11.6M
{
723
11.6M
    int code;
724
11.6M
    code = pcl_set_drawing_color(pcs,
725
11.6M
                                 pcs->pattern_type,
726
11.6M
                                 pcs->current_pattern_id, false);
727
11.6M
    if (code >= 0)
728
11.6M
        code = pcl_set_graphics_state(pcs);
729
11.6M
    if (code < 0)
730
0
        return code;
731
11.6M
    set_gs_font(pcs);
732
11.6M
    return gs_scale(pcs->pgs, scale->x, scale->y);
733
11.6M
}
734
735
/*
736
 * get the advance width.
737
 */
738
static int
739
pcl_get_width(pcl_state_t * pcs, gs_point * advance_vector,
740
              const gs_point * pscale, gs_char chr, bool is_space,
741
              bool print_undefined, double *output_width)
742
18.0M
{
743
18.0M
    int code = 0;
744
18.0M
    pcl_font_selection_t *pfp = &(pcs->font_selection[pcs->font_selected]);
745
18.0M
    double width;
746
747
18.0M
    if (chr != 0xffff || print_undefined) {
748
16.0M
        if (!pfp->params.proportional_spacing || is_space)
749
14.5M
        {
750
14.5M
            code = pcl_update_hmi_cp(pcs);
751
14.5M
            if (code < 0)
752
0
                return code;
753
754
14.5M
            width = pcs->hmi_cp;
755
14.5M
        }
756
1.54M
        else {
757
1.54M
            if (pcs->font->scaling_technology == plfst_TrueType ||
758
1.54M
                pcs->font->scaling_technology == plfst_MicroType) {
759
1.54M
                double tmp;
760
761
1.54M
                tmp = pscale->x / (double) pcs->uom_cp + 0.5;
762
1.54M
                tmp -= fmod(tmp, (double) 1.0);
763
1.54M
                tmp *= (double) pcs->uom_cp;
764
1.54M
                width = advance_vector->x * tmp;
765
766
1.54M
            } else
767
0
                width = advance_vector->x * pscale->x;
768
1.54M
            width += (double) pcs->uom_cp / 2.0;
769
1.54M
            width -= fmod(width, (double) pcs->uom_cp);
770
1.54M
        }
771
16.0M
    } else if (is_space) {
772
222k
        code = pcl_update_hmi_cp(pcs);
773
222k
        if (code < 0)
774
0
            return code;
775
222k
        width = pcs->hmi_cp;
776
222k
    }
777
1.70M
    else
778
1.70M
        width = 0.0;
779
    /* round to nearest integral pcl units */
780
18.0M
    *output_width = width;
781
18.0M
    return code;
782
18.0M
}
783
784
/*
785
 * Show a string of characters.  Provide a general purpose function
786
 * that can be used in all cases (pcl_show_chars_slow) and a faster
787
 * function (pcl_show_chars_fast) that can be used for most
788
 * circumstances.  The latter algorithm can print strings of
789
 * characters the slow algorithm only prints one character at a time.
790
 *
791
 * As is the case for other parts of this code, this code is made more complex
792
 * by the PostScript-centric nature of the the graphics library, and by a
793
 * long standing flaw in the PostScript view of fonts. Specifically, the
794
 * initial introduction of Encoding arrays into PostScript fonts, followed by
795
 * composite font mechanism, very much confused the concepts of font and text
796
 * parsing method.
797
 *
798
 * A font is an object which accepts a character identifier and returns a
799
 * "rendering" of that character (which may be a bitmap, may be a path, may
800
 * be an advance vector, or may be some combination of the above); it may also
801
 * in some cases apply this rendering to the graphic state (which may include
802
 * modifying the output). Whether or not a font caches or expects its client
803
 * to handle caching is a separate issue; there are good areguments for either
804
 * approach.
805
 *
806
 * A text parsing method is an object that accepts a character string and
807
 * returns one or more character identifiers. A text parsing method is, in
808
 * principle, completely independent of a font, though for historical reasons
809
 * the two concepts are often linked at the application level.
810
 *
811
 * Because of the PostScript origins of the graphic library, its font interface
812
 * handles both text parsing and rendering. To achieve flexibility, the client
813
 * may provide a "get next character" procedure for the graphic library font
814
 * machinery to work with, but this flexibility is not sufficient for PCL, as
815
 * the latter potentially needs to perform additional operations on each
816
 * character. Hence, PCL will not ask the font machiner to render more than
817
 * one character at a time.
818
 *
819
 * Complicating this picture is the nature of memory management in the graphic
820
 * library. The show class operators in PostScript generally take a string as
821
 * an operand. PostScript strings have the "getinterval" property: one string
822
 * may be part of another string. Hence strings cannot have headers. In a
823
 * relocating memory systems, this implies that strings must be dealt with
824
 * separately from other objects: they must be allocated as strings. In the
825
 * case of PCL, this is not necessarily the case (see, for example, the case
826
 * of transparent mode, below).
827
 *
828
 * The original implementation of this routine ignored this distinction and
829
 * could, in principle, have failed if re-location was enabled. It was also
830
 * rather hard to read, because it parsed the input string (at least) twice:
831
 * once the find the character so that PCL-specific actions could be taken,
832
 * then again via the font machiner.
833
 *
834
 *
835
 * The final operand is true if the text was provided via the literal
836
 * (transparent) text command: ESC & p <nbytes> X. This distinction is
837
 * important for characters that are not mapped by the current symbol set.  */
838
839
static int
840
pcl_show_chars_slow(pcl_state_t * pcs,
841
                    const gs_point * pscale,
842
                    const byte * str, uint size, bool literal)
843
11.6M
{
844
11.6M
    gs_gstate *pgs = pcs->pgs;
845
11.6M
    gs_char buff[1];
846
11.6M
    double rmargin = pcs->margins.right;
847
11.6M
    double page_size = pcs->xfm_state.pd_size.x;
848
11.6M
    bool source_opaque = !pcs->source_transparent;
849
11.6M
    bool invisible_pattern = is_invisible_pattern(pcs);
850
11.6M
    bool wrap = pcs->end_of_line_wrap;
851
11.6M
    bool is_space = false;
852
11.6M
    bool print_undefined = false;
853
11.6M
    bool use_rmargin = (pcs->cap.x <= rmargin);
854
11.6M
    gs_char chr, orig_chr;
855
11.6M
    int code = 0;
856
11.6M
    double width;
857
11.6M
    gs_point advance_vector;
858
11.6M
    bool unstyled_substitution;
859
860
11.6M
    width = pcs->last_width;
861
862
21.6M
    while (get_next_char(pcs, &str, &size, &chr,
863
21.6M
                         &orig_chr, &is_space, &print_undefined, literal,
864
21.6M
                         &advance_vector, &unstyled_substitution) == 0) {
865
18.0M
        double tmp_x;
866
867
        /* check if a character was found */
868
18.0M
        buff[0] = chr;
869
870
        /* round width to integral pcl current units */
871
18.0M
        code =
872
18.0M
            (pcl_get_width
873
18.0M
             (pcs, &advance_vector, pscale, chr, is_space, print_undefined, &width));
874
18.0M
        if (code < 0)
875
0
            return code;
876
877
        /*
878
         * Check for transitions of the left margin; this check is
879
         * disabled if the immediately preceding character was a back-space.
880
         * A move beyond the "right" logical page edge is also considered
881
         * a margin transition.
882
         *
883
         * A little-known feature of PCL is the effect of the line-wrap
884
         * command on the interpretation of the right margin. If line
885
         * wrap is in effect, a transition of the left margin will cause
886
         * a <cr><lf> operation BEFORE the current character is printed. If
887
         * line-wrap is not in effect, a transition of the right margin will
888
         * stop printing AFTER the current character is printed.
889
         *
890
         * A special case occurs in the non-wrap situation when the current
891
         * position exactly equals the current margin. In that case, no
892
         * character is printed.
893
         */
894
18.0M
        if (!pcs->last_was_BS) {
895
17.9M
            if (wrap) {
896
533k
                if ((use_rmargin && (pcs->cap.x + width > rmargin)) ||
897
529k
                    (pcs->cap.x + width > page_size)) {
898
4.18k
                    code = pcl_do_CR(pcs);
899
4.18k
                    if (code < 0)
900
0
                        return code;
901
4.18k
                    code = pcl_do_LF(pcs);
902
4.18k
                    if (code < 0)
903
0
                        return code;
904
                    /* A LF can cause a page feed which in turn can
905
                     * change the CTM, reapply the current font
906
                     * scaling */
907
4.18k
                    if (pcl_page_marked(pcs) == false) {
908
55
                        code = pcl_set_gstate_for_font(pcs, pscale);
909
55
                        if (code < 0)
910
0
                            return code;
911
55
                    }
912
4.18k
                    use_rmargin = true;
913
4.18k
                }
914
17.3M
            } else {
915
17.3M
                if (use_rmargin && (pcs->cap.x == rmargin))
916
7.86M
                    break;
917
9.52M
                else if (pcs->cap.x >= (coord)page_size) {
918
353
                    pcs->cap.x = (coord)page_size;
919
353
                    break;
920
353
                }
921
17.3M
            }
922
17.9M
        }
923
924
        /*
925
         * If the immediately preceding character was a BS, the code will
926
         * center the current character on top of the preceding one. After
927
         * the character is printed, the current point is returned to the
928
         * prior point.
929
         */
930
10.1M
        tmp_x = pcs->cap.x;
931
10.1M
        if (pcs->last_was_BS) {
932
            /* hack alert.  It seems if the last width is large, we
933
               use the horizontal dimension of the page as a guess, the
934
               centering is replaced by returning to the zero
935
               coordinate.  It would take quite a bit of time to
936
               investigate what the hp is doing in this pathological
937
               case, so we have not done a detailed analysis.  This
938
               solution prints the tests we have correctly. */
939
79.5k
            if (pcs->last_width > pcs->xfm_state.pd_size.x)
940
0
                tmp_x = 0;
941
79.5k
            else
942
79.5k
                tmp_x += (pcs->last_width - width) / 2;
943
79.5k
        }
944
10.1M
        code = gs_moveto(pgs, tmp_x / pscale->x, pcs->cap.y / pscale->y);
945
10.1M
        if (code < 0)
946
0
            return code;
947
948
10.1M
        if (chr != 0xffff || print_undefined) {
949
            /* if source is opaque, show and opaque background */
950
      /* retrieve the current cursor position: leftside of character */
951
9.37M
            gs_fixed_point pt;
952
9.37M
            code = gx_path_current_point(gx_current_path(pcs->pgs), &pt);
953
9.37M
            if (source_opaque)
954
14.9k
                code = show_char_background(pcs, buff);
955
9.37M
            if (code < 0)
956
0
                break;
957
958
9.37M
            if (invisible_pattern)
959
0
                code = show_char_invisible_foreground(pcs, buff);
960
9.37M
            else
961
9.37M
                code = show_char_foreground(pcs, buff);
962
963
9.37M
            if (code < 0)
964
0
                break;
965
966
9.37M
            if ((code = pcl_mark_page_for_character(pcs, &pt)) < 0)
967
0
                return code;
968
9.37M
        }
969
970
        /*
971
         * Check again for the first character following a back-space. if
972
         * this is the case, go back to the original position.
973
         */
974
10.1M
        if (pcs->last_was_BS) {
975
79.5k
            pcs->cap.x += (coord)pcs->last_width;
976
79.5k
            pcs->last_was_BS = false;
977
79.5k
        } else
978
10.0M
            pcs->cap.x += (coord)width;
979
980
        /* check for going beyond the margin if not wrapping */
981
10.1M
        if (!wrap) {
982
9.60M
            if (use_rmargin && (pcs->cap.x > rmargin)) {
983
2.07k
                pcs->cap.x = (coord)rmargin;
984
2.07k
                break;
985
9.60M
            } else if (pcs->cap.x >= (coord)page_size) {
986
91.2k
                pcs->cap.x = (coord)page_size;
987
91.2k
                break;
988
91.2k
            }
989
9.60M
        }
990
10.0M
        if (unstyled_substitution) {
991
0
            pcl_decache_font(pcs, -1, true);
992
0
            code = pcl_recompute_font(pcs, false);
993
0
            if (code < 0)
994
0
                return code;
995
0
            set_gs_font(pcs);
996
0
        }
997
10.0M
    }
998
999
    /* record the last width */
1000
11.6M
    pcs->last_width = width;
1001
1002
11.6M
    return code;
1003
11.6M
}
1004
1005
void
1006
pcl_font_scale(pcl_state_t * pcs, gs_point * pscale)
1007
11.6M
{
1008
    /* set up the font transformation */
1009
11.6M
    if (pcs->font->scaling_technology == plfst_bitmap) {
1010
0
        pscale->x = (double) pcl_coord_scale / pcs->font->resolution.x;
1011
0
        pscale->y = (double) pcl_coord_scale / pcs->font->resolution.y;
1012
11.6M
    } else {
1013
        /*
1014
         * Outline fonts are 1-point; the font height is given in
1015
         * (quarter-)points.  However, if the font is fixed-width,
1016
         * it must be scaled by pitch, not by height, relative to
1017
         * the nominal pitch of the outline.
1018
         */
1019
11.6M
        pcl_font_selection_t *pfp = &pcs->font_selection[pcs->font_selected];
1020
1021
        /* AGFA madness - 72.307 points per inch for intellifonts */
1022
11.6M
        double ppi =
1023
11.6M
            (pfp->font->scaling_technology ==
1024
11.6M
             plfst_Intellifont) ? 72.307 : 72.0;
1025
11.6M
        if (pfp->font->params.proportional_spacing) {
1026
79.9k
            pscale->x = pscale->y = pfp->params.height_4ths
1027
79.9k
                * 0.25 * 7200.0 / ppi;
1028
11.5M
        } else {
1029
11.5M
            pscale->x = pscale->y = pl_fp_pitch_cp(&pfp->params)
1030
11.5M
                * (1000.0 / pl_fp_pitch_cp(&pfp->font->params))
1031
11.5M
                * (7200.0 / (100.0 * ppi));
1032
1033
            /* hack for our internal scalable lineprinter font.  If a
1034
               real lineprinter bitmap font is available it will be
1035
               handled by the bitmap scaling case above */
1036
11.5M
            if ((pfp->font->params.typeface_family == 0) &&
1037
70.6k
                (pfp->font->storage == pcds_internal)) {
1038
70.6k
                pscale->x = pscale->y = 850.0;
1039
70.6k
            }
1040
1041
11.5M
        }
1042
        /*
1043
         * Scalable fonts use an upright coordinate system,
1044
         * the opposite from the usual PCL system.
1045
         */
1046
11.6M
        pscale->y = -pscale->y;
1047
11.6M
    }
1048
11.6M
}
1049
1050
/*
1051
 * Set up to handle a string of text.
1052
 *
1053
 * The final operand is true if the text was provided via the literal
1054
 * (transparent) text command: ESC & p <nbytes> X. This distinction is
1055
 * important for characters that are not mapped by the current symbol set.
1056
 */
1057
int
1058
pcl_text(const byte * str, uint size, pcl_state_t * pcs, bool literal)
1059
11.6M
{
1060
11.6M
    gs_point scale;
1061
11.6M
    int code;
1062
1063
    /* rtl files can have text in them - we don't print any characters
1064
       in rtl */
1065
11.6M
    if (pcs->personality == rtl)
1066
0
        return 0;
1067
    /* set up the current font and HMI */
1068
11.6M
    if ((pcs->font == 0) || pcs->font_selection[pcs->font_selected].font == 0) {
1069
312k
        code = pcl_recompute_font(pcs, false);
1070
312k
        if (code < 0)
1071
0
            return gs_rethrow_code(code);
1072
312k
    }
1073
1074
11.6M
    pcl_font_scale(pcs, &scale);
1075
11.6M
    code = pcl_set_gstate_for_font(pcs, &scale);
1076
11.6M
    if (code < 0)
1077
0
        return code;
1078
1079
    /*
1080
     * If floating underline is on, since we're about to print a real
1081
     * character, track the best-underline position.
1082
     * XXX Until we have the font's design value for underline position,
1083
     * use 0.2 em.  This is enough to almost clear descenders in typical
1084
     * fonts; it's also large enough for us to check that the mechanism
1085
     * works.
1086
     */
1087
11.6M
    if (pcs->underline_enabled && pcs->underline_floating) {
1088
0
        float yu = fabs(scale.y) / 5.0;
1089
1090
0
        if (yu > pcs->underline_position)
1091
0
            pcs->underline_position = yu;
1092
0
    }
1093
1094
    /* it is not clear if vertical substitutes are allowed in mode -1 */
1095
11.6M
    if (pcs->text_path != 0)
1096
0
        pcs->font->allow_vertical_substitutes = true;
1097
11.6M
    else
1098
11.6M
        pcs->font->allow_vertical_substitutes = false;
1099
1100
    /* Print the characters. */
1101
11.6M
    code = pcl_show_chars_slow(pcs, &scale, str, size, literal);
1102
11.6M
    if (code > 0)               /* shouldn't happen */
1103
0
        code = gs_note_error(gs_error_invalidfont);
1104
11.6M
    return code;
1105
11.6M
}
1106
1107
/*
1108
 * Individual non-command/control characters
1109
 */
1110
int
1111
pcl_plain_char(pcl_args_t * pargs, pcl_state_t * pcs)
1112
1.28M
{
1113
1.28M
    return pcl_text((const byte *)&(pargs->command), 1, pcs,
1114
1.28M
                    pcs->display_functions);
1115
1.28M
}
1116
1117
/*
1118
 * Do any underlining just before a break in motion (vertical motion or
1119
 * negative horizontal motion)...
1120
 */
1121
int pcl_break_underline(pcl_state_t * pcs)
1122
1.15M
{
1123
1.15M
    int code = 0;
1124
1125
1.15M
    if (pcs->underline_enabled)
1126
0
        code = pcl_do_underline(pcs);
1127
1128
1.15M
    return code;
1129
1.15M
}
1130
1131
/*
1132
 * draw underline up to current point, adjust status
1133
 */
1134
int
1135
pcl_do_underline(pcl_state_t * pcs)
1136
0
{
1137
0
    int code = 0;
1138
1139
0
    if (pcs->underline_start.x != pcs->cap.x) {
1140
0
        gs_gstate *pgs = pcs->pgs;
1141
0
        float y = pcs->underline_start.y + pcs->underline_position;
1142
1143
        /* save the graphics state */
1144
0
        code = pcl_gsave(pcs);
1145
0
        if (code < 0)
1146
0
            return code;
1147
1148
0
        code = pcl_set_drawing_color(pcs,
1149
0
                                     pcs->pattern_type,
1150
0
                                     pcs->current_pattern_id, false);
1151
0
        if (code >= 0)
1152
0
            code = pcl_set_graphics_state(pcs);
1153
0
        if (code < 0) {
1154
0
            (void)pcl_grestore(pcs);
1155
0
            return code;
1156
0
        }
1157
1158
        /*
1159
         * TRM says (8-34) that underline is 3 dots.  In a victory for
1160
         * common sense, it's not.  Rather, it's 0.01" (which *is* 3 dots
1161
         * at 300 dpi only)
1162
         */
1163
0
        gs_setlinewidth(pgs, dots(3));
1164
0
        if ((gs_moveto(pgs, pcs->underline_start.x, y) < 0) ||
1165
0
            (gs_lineto(pgs, pcs->cap.x, y) < 0) ||
1166
0
            (gs_stroke(pgs) < 0)) {
1167
0
            (void)pcl_grestore(pcs);
1168
0
            return code;
1169
0
        }
1170
1171
0
        code = pcl_grestore(pcs);
1172
0
        if (code < 0)
1173
0
            return code;
1174
0
    }
1175
1176
    /*
1177
     * Fixed underline is 5 "dots" (actually 5/300") down.  Floating
1178
     * will be determined on the fly.
1179
     */
1180
0
    pcs->underline_start = pcs->cap;
1181
0
    pcs->underline_position = pcs->underline_floating ? 0.0 : dots(5);
1182
0
    return code;
1183
0
}
1184
1185
/* ------ Commands ------ */
1186
1187
/*
1188
 * ESC & p <count> X
1189
 *
1190
 * Unparsed text command
1191
 */
1192
static int
1193
pcl_transparent_mode(pcl_args_t * pargs, pcl_state_t * pcs)
1194
1.74k
{
1195
1196
#ifdef DEBUG
1197
    if (gs_debug_c('i')) {
1198
        pcl_debug_dump_data(pcs->memory, arg_data(pargs), uint_arg(pargs));
1199
    }
1200
#endif
1201
1202
1.74k
    return pcl_text(arg_data(pargs), uint_arg(pargs), pcs, true);
1203
1.74k
}
1204
1205
/*
1206
 * ESC & d <0|3> D
1207
 *
1208
 * Enable floating or fixed-depth underlining.
1209
 *
1210
 * NB: If underlining is already enabled, this command is ignored. Underlining
1211
 *     must be specifically disabled to switch from fixed to floating.
1212
 */
1213
static int
1214
pcl_enable_underline(pcl_args_t * pargs, pcl_state_t * pcs)
1215
0
{
1216
0
    int type = int_arg(pargs);
1217
1218
    /* ignore command if underlining is already enabled */
1219
0
    if (pcs->underline_enabled)
1220
0
        return 0;
1221
1222
0
    if ((type == 0) || (type == 1)) {
1223
0
        pcs->underline_floating = false;
1224
0
        pcs->underline_position = dots(5);
1225
0
    } else if (type == 3) {
1226
0
        pcs->underline_floating = true;
1227
0
        pcs->underline_position = 0.0;
1228
0
    } else
1229
0
        return 0;
1230
1231
0
    pcs->underline_enabled = true;
1232
0
    pcs->underline_start = pcs->cap;
1233
0
    return 0;
1234
0
}
1235
1236
/*
1237
 * ESC & d @
1238
 *
1239
 * Disable underlining
1240
 */
1241
static int
1242
pcl_disable_underline(pcl_args_t * pargs, pcl_state_t * pcs)
1243
240
{
1244
240
    int code = 0;
1245
1246
    /* apparently disabling underlining has the side effect of
1247
       flushing any pending underlines.  This side effect is not
1248
       documented */
1249
240
    if (pcs->underline_enabled == true) {
1250
0
        code = pcl_do_underline(pcs);
1251
0
        pcs->underline_enabled = false;
1252
0
    }
1253
240
    return code;
1254
240
}
1255
1256
/* (From PCL5 Comparison Guide, p. 1-56) */
1257
1258
/*
1259
 * ESC & t <method> P
1260
 *
1261
 * Select the text parsing method.
1262
 */
1263
static int
1264
pcl_text_parsing_method(pcl_args_t * pargs, pcl_state_t * pcs)
1265
519
{
1266
519
    switch (int_arg(pargs)) {
1267
1268
14
        case 0:
1269
14
        case 1:
1270
14
            pcs->text_parsing_method = tpm_0_SBCS;
1271
14
            break;
1272
1273
0
        case 21:
1274
0
            pcs->text_parsing_method = tpm_21_DBCS7;
1275
0
            break;
1276
1277
0
        case 31:
1278
0
            pcs->text_parsing_method = tpm_31_sjis;
1279
0
            break;
1280
1281
0
        case 38:
1282
0
            pcs->text_parsing_method = tpm_38_DBCS8;
1283
0
            break;
1284
1285
504
        case 83:
1286
504
            pcs->text_parsing_method = tpm_83_utf8;
1287
504
            break;
1288
1289
0
        case 1008:
1290
0
            pcs->text_parsing_method = tpm_1008_utf8;
1291
0
            break;
1292
1293
1
        default:
1294
1
            return e_Range;
1295
519
    }
1296
1297
518
    return 0;
1298
519
}
1299
1300
/* (From PCL5 Comparison Guide, p. 1-57) */
1301
1302
/*
1303
 * ESC & c <direction> T
1304
 *
1305
 * Set the text path direction - not yet implemented.
1306
 */
1307
static int
1308
pcl_text_path_direction(pcl_args_t * pargs, pcl_state_t * pcs)
1309
0
{
1310
0
    int direction = int_arg(pargs);
1311
1312
0
    switch (direction) {
1313
1314
0
        case 0:
1315
0
        case 1:
1316
0
        case -1:
1317
0
            break;
1318
1319
0
        default:
1320
0
            return e_Range;
1321
0
    }
1322
1323
0
    pcs->text_path = direction;
1324
0
    return 0;
1325
0
}
1326
1327
/* ------ Initialization ------ */
1328
static int
1329
pctext_do_registration(pcl_parser_state_t * pcl_parser_state,
1330
                       gs_memory_t * mem)
1331
8.97k
{
1332
    /* Register commands */
1333
8.97k
    DEFINE_CONTROL(0, "(plain char)", pcl_plain_char);
1334
1335
8.97k
    DEFINE_CLASS('&') {
1336
8.97k
        'p', 'X',
1337
8.97k
            PCL_COMMAND("Transparent Mode", pcl_transparent_mode, pca_bytes)
1338
8.97k
    }, {
1339
8.97k
        'd', 'D',
1340
8.97k
            PCL_COMMAND("Enable Underline",
1341
8.97k
                        pcl_enable_underline, pca_neg_ignore | pca_big_ignore)
1342
8.97k
    }, {
1343
8.97k
        'd', '@',
1344
8.97k
            PCL_COMMAND("Disable Underline",
1345
8.97k
                        pcl_disable_underline,
1346
8.97k
                        pca_neg_ignore | pca_big_ignore)
1347
8.97k
    }, END_CLASS DEFINE_CLASS('&') {
1348
8.97k
        't', 'P',
1349
8.97k
            PCL_COMMAND("Text Parsing Method",
1350
8.97k
                        pcl_text_parsing_method,
1351
8.97k
                        pca_neg_error | pca_big_error)
1352
8.97k
    }, {
1353
8.97k
        'c', 'T',
1354
8.97k
            PCL_COMMAND("Text Path Direction",
1355
8.97k
                        pcl_text_path_direction, pca_neg_ok | pca_big_error)
1356
8.97k
    }, END_CLASS DEFINE_CONTROL(1, "(plain char)", pcl_plain_char);     /* default "command" */
1357
1358
8.97k
    return 0;
1359
8.97k
}
1360
1361
static int
1362
pctext_do_reset(pcl_state_t * pcs, pcl_reset_type_t type)
1363
35.3k
{
1364
35.3k
    static const uint mask = (pcl_reset_initial
1365
35.3k
                              | pcl_reset_printer | pcl_reset_overlay);
1366
1367
35.3k
    if ((type & mask) != 0) {
1368
35.3k
        pcs->underline_enabled = false;
1369
35.3k
        pcs->last_was_BS = false;
1370
35.3k
        pcs->last_width = inch2coord(1.0 / 10.0);
1371
35.3k
        pcs->text_parsing_method = tpm_0_SBCS;
1372
35.3k
        pcs->text_path = 0;
1373
35.3k
    }
1374
35.3k
    return 0;
1375
35.3k
}
1376
1377
const pcl_init_t pctext_init = { pctext_do_registration, pctext_do_reset, 0 };