Coverage Report

Created: 2026-08-08 08:00

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
12.7M
{
55
12.7M
    gs_font *pfont = (gs_font *) pcs->font->pfont;
56
57
12.7M
    gs_setfont(pcs->pgs, pfont);
58
    /* font scaling is reflected directly in the ctm */
59
12.7M
    pfont->FontMatrix = pfont->orig_FontMatrix;
60
12.7M
}
61
62
bool
63
pcl_downloaded_and_bound(const pl_font_t * plfont)
64
60.2M
{
65
60.2M
    return (plfont->storage != pcds_internal && pl_font_is_bound(plfont));
66
60.2M
}
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
31.4M
{
74
31.4M
    bool printable = false;
75
76
31.4M
    if (literal) {              /* transparent data */
77
1.26M
        printable = true;
78
30.1M
    } else {
79
30.1M
        if (is_stick) {
80
399k
            printable = (chr >= ' ') && (chr <= 0xff);
81
29.7M
        } else {
82
29.7M
            int map_type = 0;
83
29.7M
            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
29.7M
            } else {
90
                /* PCL TRM 10-7
91
                 * symbol map type overrides, font map type
92
                 */
93
29.7M
                map_type = map->type;
94
29.7M
            }
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
29.7M
            if (map_type == 0) {
101
47.5k
                map_type = 1;
102
47.5k
            }
103
104
29.7M
            if (map_type == 1) {
105
8.13M
                chr &= 0x7f;
106
8.13M
                printable = (chr >= ' ');   /* 0-31 and 128-159 are not printable */
107
21.6M
            } else if (map_type >= 2) {
108
                /* 2 is correct but will force all types above 2 here */
109
21.6M
                if ((chr == 0) || (chr == '\033') ||
110
19.8M
                    ((chr >= '\007') && (chr <= '\017')))
111
2.18M
                    printable = false;
112
19.4M
                else
113
19.4M
                    printable = true;
114
21.6M
            }
115
29.7M
        }
116
30.1M
    }
117
31.4M
    return printable;
118
31.4M
}
119
120
static bool
121
substituting_allowed(pcl_state_t * pcs, gs_char mapped_chr)
122
397k
{
123
397k
    gs_char remapped_chr;       /* NB wrong type */
124
125
397k
    if (
126
           /* msl not yet supported. */
127
397k
           (pcs->map && pcs->map->format == 1) ||
128
           /* by experiment HP does not support substitution with bitmap fonts */
129
397k
           (pcs->font->scaling_technology == plfst_bitmap) ||
130
           /* the font must be downloaded */
131
397k
           (!(pcs->font->storage & pcds_downloaded)))
132
397k
        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
35.6M
{
204
35.6M
    const byte *pb = *ppb;
205
35.6M
    int len = *plen;
206
35.6M
    pl_font_t *plfont = pcs->font;
207
35.6M
    bool substituting = false;
208
35.6M
    int code = 0;
209
35.6M
    gs_char chr;
210
35.6M
    gs_char mapped_chr;         /* NB wrong type */
211
35.6M
    bool db;
212
213
35.6M
    if (len <= 0)
214
4.88M
        return 2;
215
30.7M
    *pis_space = false;
216
30.7M
    *unstyled_substitution = false;
217
30.7M
    chr = pcl_char_get_char(pcs->text_parsing_method, &pb, len);
218
    /* invalid char: pb has not been incremented */
219
30.7M
    if (pb == *ppb) {
220
12
        pb++;
221
12
    }
222
30.7M
    len -= (pb - *ppb);
223
30.7M
    *ppb = pb;
224
30.7M
    *plen = len;
225
30.7M
    *porig_char = chr;
226
    /* check if the code is considered "printable" in the current symbol set */
227
30.7M
    if (!char_is_printable(pcs->font, pcs->map, chr, false, literal)) {
228
2.37M
        *pis_space = literal;
229
2.37M
        *pchr = 0xffff;
230
2.37M
        return 0;
231
2.37M
    }
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
28.3M
  r:db = pcl_downloaded_and_bound(plfont);
237
28.3M
    mapped_chr = pl_map_symbol((db ? NULL : pcs->map), chr,
238
28.3M
                               plfont->storage == pcds_internal,
239
28.3M
                               plfont->font_type == plft_MSL,
240
28.3M
                               pcs->memory);
241
28.3M
    *pchr = mapped_chr;
242
28.3M
    if (mapped_chr == 0xffff) {
243
140k
        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
140k
        *pis_space = true;
250
140k
        return 0;
251
140k
    }
252
253
    /* NB we assume all internal fonts use unicode */
254
28.2M
    if (plfont->storage == pcds_internal && mapped_chr == 0x0020
255
1.01M
        && !substituting) {
256
1.01M
        *pis_space = true;
257
1.01M
        *pchr = 0xffff;
258
1.01M
        return 0;
259
1.01M
    }
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
27.1M
    if (plfont->storage == pcds_internal &&
268
27.1M
        chr == 0x0020 &&
269
233
        plfont->character_complement[5] == 0x3f &&
270
233
        pl_complement_to_vocab(plfont->character_complement) ==
271
233
        plgv_Unicode) {
272
233
        *pis_space = true;
273
233
        *pchr = 0xffff;
274
233
        return 0;
275
233
    }
276
277
    /* check if the character is in the font and get the character
278
       width at the same time */
279
27.1M
    if (*pis_space == false)
280
27.1M
        if (pl_font_char_width(plfont, (void *)(pcs->pgs), mapped_chr, pwidth)
281
27.1M
            == 0)
282
26.7M
            return 0;
283
284
    /*
285
     * Try an unstyled substitution
286
     */
287
397k
    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
397k
    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
397k
    *pis_space = true;
314
397k
    *pchr = 0xffff;
315
397k
    return 0;
316
397k
}
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
42.6M
{
326
327
42.6M
    int bytelen = 1;
328
329
42.6M
    switch (tpm) {
330
40.2M
        default:
331
            /* byte length defaults to 1 */
332
40.2M
            break;
333
334
40.2M
        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
2.44M
        case tpm_83_utf8:
350
2.44M
        case tpm_1008_utf8:
351
2.44M
            if (ch < 0x80) {
352
                /* 0xxxxxxx */
353
1.62M
                bytelen = 1;
354
1.62M
                break;
355
1.62M
            }
356
821k
            if (ch < 0xc2) {
357
12
                bytelen = 0;    /* illegal */
358
12
                break;
359
12
            }
360
821k
            if (ch < 0xe0) {
361
                /* 110XXXXx 10xxxxxx */
362
289k
                bytelen = 2;
363
289k
                break;
364
289k
            }
365
531k
            if (ch < 0xf0) {
366
                /* 1110XXXX 10Xxxxxx 10xxxxxx */
367
530k
                bytelen = 3;
368
530k
                break;
369
530k
            }
370
1.26k
            if (ch < 0xf8) {
371
                /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
372
1.24k
                bytelen = 4;
373
1.24k
                break;
374
1.24k
            }
375
14
            if (ch < 0xfc) {
376
                /* 111110XX 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
377
1
                bytelen = 5;
378
1
                break;
379
1
            }
380
13
            if (ch < 0xfe) {
381
                /* 1111110X 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
382
1
                bytelen = 6;
383
1
                break;
384
1
            }
385
12
            bytelen = 0;        /* illegal */
386
12
            break;
387
42.6M
    }
388
42.6M
    return bytelen;
389
42.6M
}
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
30.7M
{
401
30.7M
    gs_char chr;
402
30.7M
    const byte *src = *psrc;
403
30.7M
    int bytelen = pcl_char_bytelen(src[0], tpm);
404
405
30.7M
    if (bytelen == 0 || bytelen > src_len) {
406
12
        return INVALID_UC;
407
12
    }
408
30.7M
    switch (tpm) {
409
29.5M
        default:
410
29.5M
            chr = src[0];
411
29.5M
            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
1.15M
        case tpm_83_utf8:
429
1.15M
        case tpm_1008_utf8:
430
1.15M
            if (src[0] < 0x80) {
431
                /* 0xxxxxxx */
432
741k
                chr = src[0];
433
741k
                break;
434
741k
            }
435
410k
            if (src[0] < 0xc2) {
436
0
                chr = INVALID_UC;
437
0
                break;
438
0
            }
439
410k
            if (src[0] < 0xe0) {
440
                /* 110XXXXx 10xxxxxx */
441
144k
                chr = (src[0] & 0x1f);
442
144k
                chr = (chr << 6) | (src[1] & 0x3f);
443
144k
                break;
444
144k
            }
445
265k
            if (src[0] < 0xf0) {
446
                /* 1110XXXX 10Xxxxxx 10xxxxxx */
447
265k
                chr = (src[0] & 0x0f);
448
265k
                chr = (chr << 6) | (src[1] & 0x3f);
449
265k
                chr = (chr << 6) | (src[2] & 0x3f);
450
265k
                break;
451
265k
            }
452
623
            if (src[0] < 0xf8) {
453
                /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
454
                /* chr is 16 bit: overflow */
455
623
                chr = INVALID_UC;
456
623
                break;
457
623
            }
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
30.7M
    }
473
30.7M
    *psrc += bytelen;
474
30.7M
    return chr;
475
30.7M
}
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
21.2M
{
484
21.2M
    int code = 0;
485
21.2M
    gs_text_enum_t *penum;
486
21.2M
    pl_font_t *plfont = pcs->font;
487
21.2M
    gs_font *pfont = plfont->pfont;
488
21.2M
    gs_text_params_t text;
489
490
    /* set vertical writing if -1 which requires double bytes or 1 */
491
21.2M
    if ((pcs->text_path == -1 && ((pbuff[0] & 0xff00) != 0)) ||
492
21.2M
        (pcs->text_path == 1))
493
0
        pfont->WMode = 1;
494
21.2M
    else
495
21.2M
        pfont->WMode = 0;
496
21.2M
    text.operation = TEXT_FROM_CHARS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH;
497
21.2M
    text.data.chars = pbuff;
498
21.2M
    text.size = 1;
499
21.2M
    code = gs_text_begin(pcs->pgs, &text, pcs->memory, &penum);
500
21.2M
    if (code >= 0) {
501
21.2M
        code = gs_text_process(penum);
502
21.2M
        gs_text_release(pcs->pgs, penum, "show_char_foreground");
503
21.2M
    }
504
21.2M
    return code;
505
21.2M
}
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
28.2k
{
604
28.2k
    gs_gstate *pgs = pcs->pgs;
605
28.2k
    gs_rop3_t rop = (gs_rop3_t) (pcs->logical_op);
606
28.2k
    const pl_font_t *plfont = pcs->font;
607
28.2k
    gs_font *pfont = plfont->pfont;
608
28.2k
    gs_point pt;
609
28.2k
    int code = 0, code2;
610
611
    /* save the graphic state and set the background raster operation */
612
28.2k
    code = pcl_gsave(pcs);
613
28.2k
    if (code < 0)
614
0
        return code;
615
28.2k
    if (pcs->pattern_transparent) {
616
28.1k
        code = pcl_set_drawing_color(pcs, pcl_pattern_solid_white, 0, false);
617
28.1k
        if (code < 0) {
618
0
            (void)pcl_grestore(pcs);
619
0
            return code;
620
0
        }
621
28.1k
    }
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
28.2k
    code = check_rasterops(pcs, (gs_rop3_t) rop3_know_S_1((int)rop));
627
28.2k
    if (code < 0) {
628
0
        (void)pcl_grestore(pcs);
629
0
        return 0;
630
0
    }
631
28.2k
    if (((code = gs_setrasterop(pgs, (gs_rop3_t) rop3_know_S_1((int)rop))) < 0) ||
632
28.2k
        ((code = gs_currentpoint(pgs, &pt)) < 0)) {
633
0
        (void)pcl_grestore(pcs);
634
0
        return code;
635
0
    }
636
637
28.2k
    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
28.2k
    } else {
682
28.2k
        gs_text_params_t text;
683
28.2k
        gs_rect bbox;
684
28.2k
        gs_text_enum_t *penum = NULL;
685
686
        /* clear the path; start the new one from the current point */
687
28.2k
        if (((code = gs_newpath(pgs)) < 0) ||
688
28.2k
            ((code = gs_moveto(pgs, pt.x, pt.y)) < 0)) {
689
0
            (void)pcl_grestore(pcs);
690
0
            return code;
691
0
        }
692
28.2k
        text.data.chars = pbuff;
693
28.2k
        text.size = 1;
694
28.2k
        text.operation =
695
28.2k
            TEXT_FROM_CHARS | TEXT_DO_TRUE_CHARPATH | TEXT_RETURN_WIDTH;
696
28.2k
        code = gs_text_begin(pgs, &text, pcs->memory, &penum);
697
28.2k
        if (code >= 0)
698
28.2k
            code = gs_text_process(penum);
699
28.2k
        if (code >= 0) {
700
            /* append the characters bounding box and use eofill */
701
28.2k
            if ((code = gs_pathbbox(pgs, &bbox)) >= 0 &&
702
28.2k
                (code = gs_rectappend(pgs, &bbox, 1)) >= 0 &&
703
28.2k
                (code = gs_eofill(pgs)) >= 0)
704
28.2k
            {
705
                /* fall through */
706
28.2k
            }
707
28.2k
        }
708
28.2k
        gs_text_release(pgs, penum, "show_char_background");
709
28.2k
    }
710
711
28.2k
    code2 = pcl_grestore(pcs);
712
28.2k
    if (code >= 0)
713
28.2k
        code = code2;
714
28.2k
    return code;
715
28.2k
}
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
12.7M
{
723
12.7M
    int code;
724
12.7M
    code = pcl_set_drawing_color(pcs,
725
12.7M
                                 pcs->pattern_type,
726
12.7M
                                 pcs->current_pattern_id, false);
727
12.7M
    if (code >= 0)
728
12.7M
        code = pcl_set_graphics_state(pcs);
729
12.7M
    if (code < 0)
730
0
        return code;
731
12.7M
    set_gs_font(pcs);
732
12.7M
    return gs_scale(pcs->pgs, scale->x, scale->y);
733
12.7M
}
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
30.7M
{
743
30.7M
    int code = 0;
744
30.7M
    pcl_font_selection_t *pfp = &(pcs->font_selection[pcs->font_selected]);
745
30.7M
    double width;
746
747
30.7M
    if (chr != 0xffff || print_undefined) {
748
26.7M
        if (!pfp->params.proportional_spacing || is_space)
749
21.4M
        {
750
21.4M
            code = pcl_update_hmi_cp(pcs);
751
21.4M
            if (code < 0)
752
0
                return code;
753
754
21.4M
            width = pcs->hmi_cp;
755
21.4M
        }
756
5.38M
        else {
757
5.38M
            if (pcs->font->scaling_technology == plfst_TrueType ||
758
5.38M
                pcs->font->scaling_technology == plfst_MicroType) {
759
5.38M
                double tmp;
760
761
5.38M
                tmp = pscale->x / (double) pcs->uom_cp + 0.5;
762
5.38M
                tmp -= fmod(tmp, (double) 1.0);
763
5.38M
                tmp *= (double) pcs->uom_cp;
764
5.38M
                width = advance_vector->x * tmp;
765
766
5.38M
            } else
767
0
                width = advance_vector->x * pscale->x;
768
5.38M
            width += (double) pcs->uom_cp / 2.0;
769
5.38M
            width -= fmod(width, (double) pcs->uom_cp);
770
5.38M
        }
771
26.7M
    } else if (is_space) {
772
1.55M
        code = pcl_update_hmi_cp(pcs);
773
1.55M
        if (code < 0)
774
0
            return code;
775
1.55M
        width = pcs->hmi_cp;
776
1.55M
    }
777
2.37M
    else
778
2.37M
        width = 0.0;
779
    /* round to nearest integral pcl units */
780
30.7M
    *output_width = width;
781
30.7M
    return code;
782
30.7M
}
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
12.7M
{
844
12.7M
    gs_gstate *pgs = pcs->pgs;
845
12.7M
    gs_char buff[1];
846
12.7M
    double rmargin = pcs->margins.right;
847
12.7M
    double page_size = pcs->xfm_state.pd_size.x;
848
12.7M
    bool source_opaque = !pcs->source_transparent;
849
12.7M
    bool invisible_pattern = is_invisible_pattern(pcs);
850
12.7M
    bool wrap = pcs->end_of_line_wrap;
851
12.7M
    bool is_space = false;
852
12.7M
    bool print_undefined = false;
853
12.7M
    bool use_rmargin = (pcs->cap.x <= rmargin);
854
12.7M
    gs_char chr, orig_chr;
855
12.7M
    int code = 0;
856
12.7M
    double width;
857
12.7M
    gs_point advance_vector;
858
12.7M
    bool unstyled_substitution;
859
860
12.7M
    width = pcs->last_width;
861
862
35.6M
    while (get_next_char(pcs, &str, &size, &chr,
863
35.6M
                         &orig_chr, &is_space, &print_undefined, literal,
864
35.6M
                         &advance_vector, &unstyled_substitution) == 0) {
865
30.7M
        double tmp_x;
866
867
        /* check if a character was found */
868
30.7M
        buff[0] = chr;
869
870
        /* round width to integral pcl current units */
871
30.7M
        code =
872
30.7M
            (pcl_get_width
873
30.7M
             (pcs, &advance_vector, pscale, chr, is_space, print_undefined, &width));
874
30.7M
        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
30.7M
        if (!pcs->last_was_BS) {
895
30.6M
            if (wrap) {
896
1.15M
                if ((use_rmargin && (pcs->cap.x + width > rmargin)) ||
897
1.14M
                    (pcs->cap.x + width > page_size)) {
898
8.93k
                    code = pcl_do_CR(pcs);
899
8.93k
                    if (code < 0)
900
0
                        return code;
901
8.93k
                    code = pcl_do_LF(pcs);
902
8.93k
                    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
8.93k
                    if (pcl_page_marked(pcs) == false) {
908
1.05k
                        code = pcl_set_gstate_for_font(pcs, pscale);
909
1.05k
                        if (code < 0)
910
0
                            return code;
911
1.05k
                    }
912
8.93k
                    use_rmargin = true;
913
8.93k
                }
914
29.4M
            } else {
915
29.4M
                if (use_rmargin && (pcs->cap.x == rmargin))
916
7.80M
                    break;
917
21.6M
                else if (pcs->cap.x >= (coord)page_size) {
918
184
                    pcs->cap.x = (coord)page_size;
919
184
                    break;
920
184
                }
921
29.4M
            }
922
30.6M
        }
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
22.9M
        tmp_x = pcs->cap.x;
931
22.9M
        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
80.1k
            if (pcs->last_width > pcs->xfm_state.pd_size.x)
940
0
                tmp_x = 0;
941
80.1k
            else
942
80.1k
                tmp_x += (pcs->last_width - width) / 2;
943
80.1k
        }
944
22.9M
        code = gs_moveto(pgs, tmp_x / pscale->x, pcs->cap.y / pscale->y);
945
22.9M
        if (code < 0)
946
0
            return code;
947
948
22.9M
        if (chr != 0xffff || print_undefined) {
949
            /* if source is opaque, show and opaque background */
950
      /* retrieve the current cursor position: leftside of character */
951
21.2M
            gs_fixed_point pt;
952
21.2M
            code = gx_path_current_point(gx_current_path(pcs->pgs), &pt);
953
21.2M
            if (source_opaque)
954
28.2k
                code = show_char_background(pcs, buff);
955
21.2M
            if (code < 0)
956
0
                break;
957
958
21.2M
            if (invisible_pattern)
959
0
                code = show_char_invisible_foreground(pcs, buff);
960
21.2M
            else
961
21.2M
                code = show_char_foreground(pcs, buff);
962
963
21.2M
            if (code < 0)
964
0
                break;
965
966
21.2M
            if ((code = pcl_mark_page_for_character(pcs, &pt)) < 0)
967
0
                return code;
968
21.2M
        }
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
22.9M
        if (pcs->last_was_BS) {
975
80.1k
            pcs->cap.x += (coord)pcs->last_width;
976
80.1k
            pcs->last_was_BS = false;
977
80.1k
        } else
978
22.8M
            pcs->cap.x += (coord)width;
979
980
        /* check for going beyond the margin if not wrapping */
981
22.9M
        if (!wrap) {
982
21.7M
            if (use_rmargin && (pcs->cap.x > rmargin)) {
983
2.46k
                pcs->cap.x = (coord)rmargin;
984
2.46k
                break;
985
21.7M
            } else if (pcs->cap.x >= (coord)page_size) {
986
79.9k
                pcs->cap.x = (coord)page_size;
987
79.9k
                break;
988
79.9k
            }
989
21.7M
        }
990
22.8M
        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
22.8M
    }
998
999
    /* record the last width */
1000
12.7M
    pcs->last_width = width;
1001
1002
12.7M
    return code;
1003
12.7M
}
1004
1005
void
1006
pcl_font_scale(pcl_state_t * pcs, gs_point * pscale)
1007
12.7M
{
1008
    /* set up the font transformation */
1009
12.7M
    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
12.7M
    } 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
12.7M
        pcl_font_selection_t *pfp = &pcs->font_selection[pcs->font_selected];
1020
1021
        /* AGFA madness - 72.307 points per inch for intellifonts */
1022
12.7M
        double ppi =
1023
12.7M
            (pfp->font->scaling_technology ==
1024
12.7M
             plfst_Intellifont) ? 72.307 : 72.0;
1025
12.7M
        if (pfp->font->params.proportional_spacing) {
1026
252k
            pscale->x = pscale->y = pfp->params.height_4ths
1027
252k
                * 0.25 * 7200.0 / ppi;
1028
12.5M
        } else {
1029
12.5M
            pscale->x = pscale->y = pl_fp_pitch_cp(&pfp->params)
1030
12.5M
                * (1000.0 / pl_fp_pitch_cp(&pfp->font->params))
1031
12.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
12.5M
            if ((pfp->font->params.typeface_family == 0) &&
1037
102k
                (pfp->font->storage == pcds_internal)) {
1038
102k
                pscale->x = pscale->y = 850.0;
1039
102k
            }
1040
1041
12.5M
        }
1042
        /*
1043
         * Scalable fonts use an upright coordinate system,
1044
         * the opposite from the usual PCL system.
1045
         */
1046
12.7M
        pscale->y = -pscale->y;
1047
12.7M
    }
1048
12.7M
}
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
12.7M
{
1060
12.7M
    gs_point scale;
1061
12.7M
    int code;
1062
1063
    /* rtl files can have text in them - we don't print any characters
1064
       in rtl */
1065
12.7M
    if (pcs->personality == rtl)
1066
0
        return 0;
1067
    /* set up the current font and HMI */
1068
12.7M
    if ((pcs->font == 0) || pcs->font_selection[pcs->font_selected].font == 0) {
1069
749k
        code = pcl_recompute_font(pcs, false);
1070
749k
        if (code < 0)
1071
0
            return gs_rethrow_code(code);
1072
749k
    }
1073
1074
12.7M
    pcl_font_scale(pcs, &scale);
1075
12.7M
    code = pcl_set_gstate_for_font(pcs, &scale);
1076
12.7M
    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
12.7M
    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
12.7M
    if (pcs->text_path != 0)
1096
0
        pcs->font->allow_vertical_substitutes = true;
1097
12.7M
    else
1098
12.7M
        pcs->font->allow_vertical_substitutes = false;
1099
1100
    /* Print the characters. */
1101
12.7M
    code = pcl_show_chars_slow(pcs, &scale, str, size, literal);
1102
12.7M
    if (code > 0)               /* shouldn't happen */
1103
0
        code = gs_note_error(gs_error_invalidfont);
1104
12.7M
    return code;
1105
12.7M
}
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.06M
{
1113
1.06M
    return pcl_text((const byte *)&(pargs->command), 1, pcs,
1114
1.06M
                    pcs->display_functions);
1115
1.06M
}
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
2.50M
{
1123
2.50M
    int code = 0;
1124
1125
2.50M
    if (pcs->underline_enabled)
1126
0
        code = pcl_do_underline(pcs);
1127
1128
2.50M
    return code;
1129
2.50M
}
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.64k
{
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.64k
    return pcl_text(arg_data(pargs), uint_arg(pargs), pcs, true);
1203
1.64k
}
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
274
{
1244
274
    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
274
    if (pcs->underline_enabled == true) {
1250
0
        code = pcl_do_underline(pcs);
1251
0
        pcs->underline_enabled = false;
1252
0
    }
1253
274
    return code;
1254
274
}
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
658
{
1266
658
    switch (int_arg(pargs)) {
1267
1268
17
        case 0:
1269
17
        case 1:
1270
17
            pcs->text_parsing_method = tpm_0_SBCS;
1271
17
            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
637
        case 83:
1286
637
            pcs->text_parsing_method = tpm_83_utf8;
1287
637
            break;
1288
1289
0
        case 1008:
1290
0
            pcs->text_parsing_method = tpm_1008_utf8;
1291
0
            break;
1292
1293
4
        default:
1294
4
            return e_Range;
1295
658
    }
1296
1297
654
    return 0;
1298
658
}
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
3
{
1310
3
    int direction = int_arg(pargs);
1311
1312
3
    switch (direction) {
1313
1314
3
        case 0:
1315
3
        case 1:
1316
3
        case -1:
1317
3
            break;
1318
1319
0
        default:
1320
0
            return e_Range;
1321
3
    }
1322
1323
3
    pcs->text_path = direction;
1324
3
    return 0;
1325
3
}
1326
1327
/* ------ Initialization ------ */
1328
static int
1329
pctext_do_registration(pcl_parser_state_t * pcl_parser_state,
1330
                       gs_memory_t * mem)
1331
16.1k
{
1332
    /* Register commands */
1333
16.1k
    DEFINE_CONTROL(0, "(plain char)", pcl_plain_char);
1334
1335
16.1k
    DEFINE_CLASS('&') {
1336
16.1k
        'p', 'X',
1337
16.1k
            PCL_COMMAND("Transparent Mode", pcl_transparent_mode, pca_bytes)
1338
16.1k
    }, {
1339
16.1k
        'd', 'D',
1340
16.1k
            PCL_COMMAND("Enable Underline",
1341
16.1k
                        pcl_enable_underline, pca_neg_ignore | pca_big_ignore)
1342
16.1k
    }, {
1343
16.1k
        'd', '@',
1344
16.1k
            PCL_COMMAND("Disable Underline",
1345
16.1k
                        pcl_disable_underline,
1346
16.1k
                        pca_neg_ignore | pca_big_ignore)
1347
16.1k
    }, END_CLASS DEFINE_CLASS('&') {
1348
16.1k
        't', 'P',
1349
16.1k
            PCL_COMMAND("Text Parsing Method",
1350
16.1k
                        pcl_text_parsing_method,
1351
16.1k
                        pca_neg_error | pca_big_error)
1352
16.1k
    }, {
1353
16.1k
        'c', 'T',
1354
16.1k
            PCL_COMMAND("Text Path Direction",
1355
16.1k
                        pcl_text_path_direction, pca_neg_ok | pca_big_error)
1356
16.1k
    }, END_CLASS DEFINE_CONTROL(1, "(plain char)", pcl_plain_char);     /* default "command" */
1357
1358
16.1k
    return 0;
1359
16.1k
}
1360
1361
static int
1362
pctext_do_reset(pcl_state_t * pcs, pcl_reset_type_t type)
1363
49.4k
{
1364
49.4k
    static const uint mask = (pcl_reset_initial
1365
49.4k
                              | pcl_reset_printer | pcl_reset_overlay);
1366
1367
49.4k
    if ((type & mask) != 0) {
1368
49.4k
        pcs->underline_enabled = false;
1369
49.4k
        pcs->last_was_BS = false;
1370
49.4k
        pcs->last_width = inch2coord(1.0 / 10.0);
1371
49.4k
        pcs->text_parsing_method = tpm_0_SBCS;
1372
49.4k
        pcs->text_path = 0;
1373
49.4k
    }
1374
49.4k
    return 0;
1375
49.4k
}
1376
1377
const pcl_init_t pctext_init = { pctext_do_registration, pctext_do_reset, 0 };