Coverage Report

Created: 2026-10-28 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/pdf/pdf_text.c
Line
Count
Source
1
/* Copyright (C) 2018-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
/* Text operations for the PDF interpreter */
17
18
#include "pdf_int.h"
19
#include "pdf_array.h"
20
#include "pdf_text.h"
21
#include "pdf_image.h"
22
#include "pdf_colour.h"
23
#include "pdf_stack.h"
24
#include "pdf_font.h"
25
#include "pdf_font_types.h"
26
#include "pdf_gstate.h"
27
#include "pdf_trans.h"
28
#include "pdf_optcontent.h"
29
30
#include "gsstate.h"
31
#include "gsmatrix.h"
32
#include "gdevbbox.h"
33
#include "gspaint.h"        /* For gs_fill() and friends */
34
#include "gscoord.h"        /* For gs_setmatrix() */
35
#include "gxdevsop.h"               /* For special ops */
36
37
static int pdfi_set_TL(pdf_context *ctx, double TL);
38
39
int pdfi_BT(pdf_context *ctx)
40
1.84M
{
41
1.84M
    int code;
42
1.84M
    gs_matrix m;
43
1.84M
    bool illegal_BT = false;
44
45
1.84M
    if (ctx->text.BlockDepth != 0) {
46
39.6k
        illegal_BT = true;
47
39.6k
        if (ctx->text.TextClip) {
48
87
            gx_device *dev = gs_currentdevice_inline(ctx->pgs);
49
50
87
            ctx->text.TextClip = false;
51
87
            (void)dev_proc(dev, dev_spec_op)(dev, gxdso_hilevel_text_clip, (void *)0, 1);
52
87
        }
53
39.6k
        code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, W_PDF_NESTEDTEXTBLOCK, "pdfi_BT", NULL);
54
39.6k
        if (code < 0)
55
0
            return code;
56
39.6k
    }
57
58
1.84M
    gs_make_identity(&m);
59
1.84M
    code = gs_settextmatrix(ctx->pgs, &m);
60
1.84M
    if (code < 0)
61
0
        return code;
62
63
1.84M
    code = gs_settextlinematrix(ctx->pgs, &m);
64
1.84M
    if (code < 0)
65
0
        return code;
66
67
    /* We should not perform the clip (for text rendering modes involving a clip)
68
     * when preserving the text rendering mode.
69
     */
70
1.84M
    if (gs_currenttextrenderingmode(ctx->pgs) >= 4 && ctx->text.BlockDepth == 0 && !ctx->device_state.preserve_tr_mode) {
71
        /* Start a new path (so our clip doesn't include any
72
         * already extant path in the graphics state)
73
         */
74
123
        gs_newpath(ctx->pgs);
75
123
    }
76
77
1.84M
    ctx->text.initial_current_point_valid = ctx->pgs->current_point_valid;
78
1.84M
    if (!ctx->pgs->current_point_valid)
79
1.47M
        code = gs_moveto(ctx->pgs, 0, 0);
80
81
1.84M
    ctx->text.BlockDepth++;
82
83
1.84M
    if (ctx->page.has_transparency && gs_currenttextknockout(ctx->pgs) && !illegal_BT)
84
409k
        gs_begin_transparency_text_group(ctx->pgs);
85
86
1.84M
    return code;
87
1.84M
}
88
89
static int do_ET(pdf_context *ctx)
90
1.86M
{
91
1.86M
    int code = 0;
92
1.86M
    gx_clip_path *copy = NULL;
93
94
    /* If we have reached the end of a text block (or the outermost block
95
     * if we have illegally nested text blocks) and we are using a 'clip'
96
     * text rendering mode, then we need to apply the clip. We also need
97
     * to grestore back one level as we will have pushed a gsave either in
98
     * pdfi_BT or in pdfi_Tr. The extra gsave is so we can accumulate a
99
     * clipping path separately to any path already existing in the
100
     * graphics state.
101
     */
102
103
    /* See the note on text rendering modes with clip in pdfi_BT() above */
104
1.86M
    if (ctx->text.BlockDepth == 0 && gs_currenttextrenderingmode(ctx->pgs) >= 4) {
105
861
        gs_point initial_point;
106
107
861
        if  (!ctx->device_state.preserve_tr_mode) {
108
817
            ctx->text.TextClip = false;
109
            /* Capture the current position */
110
817
            code = gs_currentpoint(ctx->pgs, &initial_point);
111
817
            if (code >= 0 || code == gs_error_nocurrentpoint) {
112
815
                gs_point adjust;
113
815
                bool nocurrentpoint = code >= 0 ? false : true;
114
115
815
                gs_currentfilladjust(ctx->pgs, &adjust);
116
815
                code = gs_setfilladjust(ctx->pgs, (double)0.0, (double)0.0);
117
815
                if (code < 0)
118
0
                    return code;
119
120
815
                code = gs_clip(ctx->pgs);
121
815
                if (code >= 0)
122
815
                    copy = gx_cpath_alloc_shared(ctx->pgs->clip_path, ctx->memory, "save clip path");
123
124
815
                code = gs_setfilladjust(ctx->pgs, adjust.x, adjust.y);
125
815
                if (code < 0)
126
0
                    return code;
127
128
815
                if (copy != NULL)
129
815
                    (void)gx_cpath_assign_free(ctx->pgs->clip_path, copy);
130
131
815
                if (nocurrentpoint == false)
132
815
                    code = gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
133
815
            }
134
817
        }
135
861
    }
136
1.86M
    if (ctx->page.has_transparency && gs_currenttextknockout(ctx->pgs))
137
416k
        gs_end_transparency_text_group(ctx->pgs);
138
139
1.86M
    if (!ctx->text.initial_current_point_valid)
140
965k
        gs_newpath(ctx->pgs);
141
1.86M
    return code;
142
1.86M
}
143
144
int pdfi_ET(pdf_context *ctx)
145
1.81M
{
146
1.81M
    if (ctx->text.BlockDepth == 0) {
147
68.1k
        int code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, W_PDF_ETNOTEXTBLOCK, "pdfi_ET", NULL);
148
68.1k
        return code;
149
68.1k
    }
150
151
1.74M
    ctx->text.BlockDepth--;
152
153
1.74M
    return do_ET(ctx);
154
1.81M
}
155
156
int pdfi_T_star(pdf_context *ctx)
157
160k
{
158
160k
    int code;
159
160k
    gs_matrix m, mat;
160
161
160k
    if (ctx->text.BlockDepth == 0) {
162
35.0k
        int code;
163
35.0k
        if ((code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, W_PDF_TEXTOPNOBT, "pdfi_T_star", NULL)) < 0)
164
0
            return code;
165
35.0k
    }
166
167
160k
    gs_make_identity(&m);
168
160k
    m.ty += ctx->pgs->textleading;
169
170
160k
    code = gs_matrix_multiply(&m, &ctx->pgs->textlinematrix, &mat);
171
160k
    if (code < 0)
172
0
        return code;
173
174
160k
    code = gs_settextmatrix(ctx->pgs, (gs_matrix *)&mat);
175
160k
    if (code < 0)
176
0
        return code;
177
178
160k
    code = gs_settextlinematrix(ctx->pgs, (gs_matrix *)&mat);
179
160k
    return code;
180
160k
}
181
182
static int pdfi_set_Tc(pdf_context *ctx, double Tc)
183
731k
{
184
731k
    return gs_settextspacing(ctx->pgs, Tc);
185
731k
}
186
187
int pdfi_Tc(pdf_context *ctx)
188
745k
{
189
745k
    int code;
190
745k
    double d;
191
192
745k
    code = pdfi_destack_real(ctx, &d);
193
745k
    if (code < 0)
194
13.9k
        return code;
195
196
731k
    return pdfi_set_Tc(ctx, d);
197
745k
}
198
199
int pdfi_Td(pdf_context *ctx)
200
1.28M
{
201
1.28M
    int code;
202
1.28M
    double Txy[2];
203
1.28M
    gs_matrix m, mat;
204
205
1.28M
    code = pdfi_destack_reals(ctx, Txy, 2);
206
1.28M
    if (code < 0)
207
21.8k
        return code;
208
209
1.26M
    gs_make_identity(&m);
210
211
1.26M
    m.tx = Txy[0];
212
1.26M
    m.ty = Txy[1];
213
214
1.26M
    if (ctx->text.BlockDepth == 0) {
215
13.1k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_Td", NULL);
216
217
13.1k
        gs_make_identity(&mat);
218
13.1k
        code = gs_settextmatrix(ctx->pgs, &mat);
219
13.1k
        if (code < 0)
220
0
            return code;
221
222
13.1k
        code = gs_settextlinematrix(ctx->pgs, &mat);
223
13.1k
        if (code < 0)
224
0
            return code;
225
13.1k
    }
226
227
1.26M
    code = gs_matrix_multiply(&m, &ctx->pgs->textlinematrix, &mat);
228
1.26M
    if (code < 0)
229
0
        return code;
230
231
1.26M
    code = gs_settextmatrix(ctx->pgs, (gs_matrix *)&mat);
232
1.26M
    if (code < 0)
233
0
        return code;
234
235
1.26M
    return gs_settextlinematrix(ctx->pgs, (gs_matrix *)&mat);
236
1.26M
}
237
238
int pdfi_TD(pdf_context *ctx)
239
404k
{
240
404k
    int code;
241
404k
    double Txy[2];
242
404k
    gs_matrix m, mat;
243
244
404k
    gs_make_identity(&m);
245
246
404k
    code = pdfi_destack_reals(ctx, Txy, 2);
247
404k
    if (code < 0)
248
2.88k
        return code;
249
250
401k
    m.tx = Txy[0];
251
401k
    m.ty = Txy[1];
252
253
401k
    if (ctx->text.BlockDepth == 0) {
254
1.42k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_TD", NULL);
255
256
1.42k
        gs_make_identity(&mat);
257
1.42k
        code = gs_settextmatrix(ctx->pgs, &mat);
258
1.42k
        if (code < 0)
259
0
            return code;
260
261
1.42k
        code = gs_settextlinematrix(ctx->pgs, &mat);
262
1.42k
        if (code < 0)
263
0
            return code;
264
1.42k
    }
265
266
401k
    code = pdfi_set_TL(ctx, m.ty * 1.0f);
267
401k
    if (code < 0)
268
0
        return code;
269
270
401k
    code = gs_matrix_multiply(&m, &ctx->pgs->textlinematrix, &mat);
271
401k
    if (code < 0)
272
0
        return code;
273
274
401k
    code = gs_settextmatrix(ctx->pgs, (gs_matrix *)&mat);
275
401k
    if (code < 0)
276
0
        return code;
277
278
401k
    return gs_settextlinematrix(ctx->pgs, (gs_matrix *)&mat);
279
401k
}
280
281
/* This routine sets up most of the text params structure. In particular it
282
 * creates and initialises the x and y widths arrays, and for type 3 fonts
283
 * it creates and populates the 'chars' member.
284
 * It also sets the delta_sace member and partially set sup the 'operation'
285
 * bitfield. It does not set any of the TEXT_DO_* fields because we intend
286
 * to use this routine to set up the 'common' parts of the structure and
287
 * then we will twiddle the 'TEXT_DO_*' fields as required for the type of
288
 * operation we are doing (fill, create path for stroke, create path for fill)
289
 */
290
static int pdfi_show_set_params(pdf_context *ctx, pdf_string *s, gs_text_params_t *text)
291
13.4M
{
292
13.4M
    pdf_font *current_font = NULL;
293
13.4M
    gs_matrix mat;
294
13.4M
    float *x_widths = NULL, *y_widths = NULL, width;
295
13.4M
    double Tw = 0, Tc = 0;
296
13.4M
    int i, code;
297
298
13.4M
    text->data.chars = NULL;
299
13.4M
    text->x_widths = NULL;
300
13.4M
    text->y_widths = NULL;
301
302
    /* NOTE: we don't scale the FontMatrix we leave it as the default
303
     * and do all our scaling with the textmatrix/ctm. This saves having
304
     * to create multiple instances of the same font, and simplifies
305
     * composite fonts significantly.
306
     */
307
13.4M
    current_font = pdfi_get_current_pdf_font(ctx);
308
309
13.4M
    if (current_font == NULL)
310
0
        return_error(gs_error_invalidfont);
311
312
    /* Division by PDFfontsize because these are in unscaled font units,
313
       and the font scale is now pickled into the text matrix, so we have to
314
       undo that.
315
     */
316
13.4M
    Tc = gs_currenttextspacing(ctx->pgs) / ctx->pgs->PDFfontsize;
317
318
13.4M
    if (current_font->pdfi_font_type == e_pdf_font_type1 ||
319
5.03M
        current_font->pdfi_font_type == e_pdf_font_cff ||
320
3.45M
        current_font->pdfi_font_type == e_pdf_font_type3 ||
321
3.41M
        current_font->pdfi_font_type == e_pdf_font_cff ||
322
3.41M
        current_font->pdfi_font_type == e_pdf_font_truetype ||
323
1.38M
        current_font->pdfi_font_type == e_pdf_font_microtype ||
324
1.38M
        current_font->pdfi_font_type == e_pdf_font_type0)
325
13.4M
    {
326
        /* For Type 0 fonts, we apply the DW/W/DW2/W2 values when we retrieve the metrics for
327
           setcachedevice - see pdfi_fapi_set_cache()
328
         */
329
13.4M
        if (current_font->pdfi_font_type == e_pdf_font_type0 || current_font->Widths == NULL) {
330
5.23M
            text->operation = TEXT_RETURN_WIDTH;
331
5.23M
            if (Tc != 0) {
332
1.10M
                text->operation |= TEXT_ADD_TO_ALL_WIDTHS;
333
1.10M
                if (current_font->pfont && current_font->pfont->WMode == 0) {
334
1.10M
                    text->delta_all.x = Tc;
335
1.10M
                    text->delta_all.y = 0;
336
1.10M
                } else {
337
1
                    text->delta_all.y = Tc;
338
1
                    text->delta_all.x = 0;
339
1
                }
340
1.10M
            }
341
8.24M
        } else {
342
8.24M
            gs_point pt;
343
344
8.24M
            x_widths = (float *)gs_alloc_bytes(ctx->memory, (size_t)s->length * sizeof(float), "X widths array for text");
345
8.24M
            y_widths = (float *)gs_alloc_bytes(ctx->memory, (size_t)s->length * sizeof(float), "Y widths array for text");
346
8.24M
            if (x_widths == NULL || y_widths == NULL) {
347
0
                code = gs_note_error(gs_error_VMerror);
348
0
                goto text_params_error;
349
0
            }
350
351
8.24M
            memset(x_widths, 0x00, s->length * sizeof(float));
352
8.24M
            memset(y_widths, 0x00, s->length * sizeof(float));
353
354
            /* To calculate the Width (which is defined in unscaled text units) as a value in user
355
             * space we need to transform it by the FontMatrix
356
             */
357
8.24M
            mat = current_font->pfont->FontMatrix;
358
359
34.7M
            for (i = 0;i < s->length; i++) {
360
                /* Get the width (in unscaled text units) */
361
26.4M
                if (s->data[i] < current_font->FirstChar || s->data[i] > current_font->LastChar)
362
95.2k
                    width = current_font->MissingWidth;
363
26.3M
                else
364
26.3M
                    width = current_font->Widths[s->data[i] - current_font->FirstChar];
365
                /* And convert the width into an appropriate value for the current environment */
366
26.4M
                gs_distance_transform(width, 0, &mat, &pt);
367
26.4M
                x_widths[i] = hypot(pt.x, pt.y) * (width < 0 ? -1.0 : 1.0);
368
                /* Add any Tc value */
369
26.4M
                x_widths[i] += Tc;
370
26.4M
            }
371
8.24M
            text->operation = TEXT_RETURN_WIDTH | TEXT_REPLACE_WIDTHS;
372
8.24M
            text->x_widths = x_widths;
373
8.24M
            text->y_widths = y_widths;
374
8.24M
            text->widths_size = s->length * 2;
375
8.24M
        }
376
377
13.4M
        Tw = gs_currentwordspacing(ctx->pgs);
378
13.4M
        if (Tw != 0) {
379
910k
            text->operation |= TEXT_ADD_TO_SPACE_WIDTH;
380
            /* Division by PDFfontsize because these are in unscaled font units,
381
               and the font scale is now pickled into the text matrix, so we have to
382
               undo that.
383
             */
384
910k
            if (current_font->pfont && current_font->pfont->WMode == 0) {
385
910k
                text->delta_space.x = Tw / ctx->pgs->PDFfontsize;
386
910k
                text->delta_space.y = 0;
387
910k
            } else {
388
0
                text->delta_space.y = Tw / ctx->pgs->PDFfontsize;
389
0
                text->delta_space.x = 0;
390
0
            }
391
910k
            text->space.s_char = 0x20;
392
910k
        }
393
394
13.4M
        if (current_font->pdfi_font_type == e_pdf_font_type3) {
395
45.9k
            text->operation |= TEXT_FROM_CHARS;
396
45.9k
            text->data.chars = (const gs_char *)gs_alloc_bytes(ctx->memory, (size_t)s->length * sizeof(gs_char), "string gs_chars");
397
45.9k
            if (!text->data.chars) {
398
0
                code = gs_note_error(gs_error_VMerror);
399
0
                goto text_params_error;
400
0
            }
401
402
226k
            for (i = 0; i < s->length; i++) {
403
180k
                ((gs_char *)text->data.chars)[i] = (gs_char)s->data[i];
404
180k
            }
405
45.9k
        }
406
13.4M
        else {
407
13.4M
            text->operation |= TEXT_FROM_BYTES;
408
13.4M
            text->data.bytes = (const byte *)s->data;
409
13.4M
        }
410
13.4M
        text->size = s->length;
411
13.4M
    }
412
60
    else {
413
60
        code = gs_note_error(gs_error_invalidfont);
414
60
        goto text_params_error;
415
60
    }
416
417
13.4M
    return 0;
418
419
60
text_params_error:
420
60
    gs_free_object(ctx->memory, x_widths, "X widths array for text");
421
60
    text->x_widths = NULL;
422
60
    gs_free_object(ctx->memory, y_widths, "Y widths array for text");
423
60
    text->y_widths = NULL;
424
60
    gs_free_object(ctx->memory, (void *)text->data.chars, "string gs_chars");
425
60
    text->data.chars = NULL;
426
60
    return code;
427
13.4M
}
428
429
/* These routines actually perform the fill/stroke/clip of the text.
430
 * We build up the compound cases by reusing the basic cases which
431
 * is why we reset the TEXT_DO_ fields after use.
432
 */
433
434
static int pdfi_show_simple(pdf_context *ctx, gs_text_params_t *text)
435
13.3M
{
436
13.3M
    int code = 0;
437
13.3M
    gs_text_enum_t *penum=NULL, *saved_penum=NULL;
438
439
13.3M
    code = gs_text_begin(ctx->pgs, text, ctx->memory, &penum);
440
13.3M
    if (code >= 0) {
441
13.3M
        penum->single_byte_space = true;
442
13.3M
        saved_penum = ctx->text.current_enum;
443
13.3M
        ctx->text.current_enum = penum;
444
13.3M
        code = gs_text_process(penum);
445
13.3M
        gs_text_release(ctx->pgs, penum, "pdfi_Tj");
446
13.3M
        ctx->text.current_enum = saved_penum;
447
13.3M
    }
448
13.3M
    return code;
449
13.3M
}
450
451
/* Mode 0 - fill */
452
static int pdfi_show_Tr_0(pdf_context *ctx, gs_text_params_t *text)
453
10.8M
{
454
10.8M
    int code;
455
456
    /* just draw the text */
457
10.8M
    text->operation |= TEXT_DO_DRAW;
458
459
10.8M
    code = pdfi_show_simple(ctx, text);
460
461
10.8M
    text->operation &= ~TEXT_DO_DRAW;
462
10.8M
    return code;
463
10.8M
}
464
465
/* Mode 1 - stroke */
466
static int pdfi_show_Tr_1(pdf_context *ctx, gs_text_params_t *text)
467
1.45k
{
468
1.45k
    int code;
469
1.45k
    gs_text_enum_t *penum=NULL, *saved_penum=NULL;
470
1.45k
    gs_point end_point, initial_point;
471
1.45k
    gx_device *dev = ctx->pgs->device;
472
1.45k
    int galphabits = dev->color_info.anti_alias.graphics_bits, talphabits = dev->color_info.anti_alias.text_bits;
473
474
1.45k
    end_point.x = end_point.y = initial_point.x = initial_point.y = 0;
475
476
    /* Capture the current position */
477
1.45k
    code = gs_currentpoint(ctx->pgs, &initial_point);
478
1.45k
    if (code < 0)
479
0
        return code;
480
481
    /* We don't want to disturb the current path, so do a gsave now
482
     * We will grestore back to this point after we have stroked the
483
     * text, which will leave any current path unchanged.
484
     */
485
1.45k
    code = pdfi_gsave(ctx);
486
1.45k
    if (code < 0)
487
0
        goto Tr1_error;
488
489
    /* Start a new path (so our stroke doesn't include any
490
     * already extant path in the graphics state)
491
     */
492
1.45k
    code = gs_newpath(ctx->pgs);
493
1.45k
    if (code < 0)
494
0
        goto Tr1_error;
495
1.45k
    code = gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
496
1.45k
    if (code < 0)
497
0
        goto Tr1_error;
498
499
    /* Don't draw the text, create a path suitable for stroking */
500
1.45k
    text->operation |= TEXT_DO_FALSE_CHARPATH;
501
502
    /* Run the text methods to create the path */
503
1.45k
    code = gs_text_begin(ctx->pgs, text, ctx->memory, &penum);
504
1.45k
    if (code < 0)
505
45
        goto Tr1_error;
506
507
1.41k
    penum->single_byte_space = true;
508
1.41k
    saved_penum = ctx->text.current_enum;
509
1.41k
    ctx->text.current_enum = penum;
510
1.41k
    code = gs_text_process(penum);
511
1.41k
    gs_text_release(ctx->pgs, penum, "pdfi_Tj");
512
1.41k
    ctx->text.current_enum = saved_penum;
513
1.41k
    if (code < 0)
514
0
        goto Tr1_error;
515
516
    /* After a stroke operation there is no current point and we need
517
     * it to be set as if we had drawn the text. So capture it now
518
     * and we will append a 'move' to the current point when we have
519
     * finished the stroke.
520
     */
521
1.41k
    code = gs_currentpoint(ctx->pgs, &end_point);
522
1.41k
    if (code < 0)
523
0
        goto Tr1_error;
524
525
1.41k
    if (talphabits != galphabits)
526
0
        dev->color_info.anti_alias.graphics_bits = talphabits;
527
528
    /* Change to the current stroking colour */
529
1.41k
    gs_swapcolors_quick(ctx->pgs);
530
    /* Finally, stroke the actual path */
531
1.41k
    code = gs_stroke(ctx->pgs);
532
    /* Switch back to the non-stroke colour */
533
1.41k
    gs_swapcolors_quick(ctx->pgs);
534
535
1.41k
    if (talphabits != galphabits)
536
0
        dev->color_info.anti_alias.graphics_bits = galphabits;
537
538
1.45k
Tr1_error:
539
    /* And grestore back to where we started */
540
1.45k
    (void)pdfi_grestore(ctx);
541
    /* If everything went well, then move the current point to the
542
     * position we captured at the end of the path creation */
543
1.45k
    if (code >= 0)
544
1.41k
        code = gs_moveto(ctx->pgs, end_point.x, end_point.y);
545
546
1.45k
    text->operation &= ~TEXT_DO_FALSE_CHARPATH;
547
1.45k
    return code;
548
1.41k
}
549
550
/* Mode 2 - fill then stroke */
551
static int pdfi_show_Tr_2(pdf_context *ctx, gs_text_params_t *text)
552
8.51k
{
553
8.51k
    int code, restart = 0;
554
8.51k
    gs_text_enum_t *penum=NULL, *saved_penum=NULL;
555
8.51k
    gs_point end_point, initial_point;
556
8.51k
    gx_device *dev = ctx->pgs->device;
557
8.51k
    int galphabits = dev->color_info.anti_alias.graphics_bits, talphabits = dev->color_info.anti_alias.text_bits;
558
559
8.51k
    end_point.x = end_point.y = initial_point.x = initial_point.y = 0;
560
561
    /* Capture the current position */
562
8.51k
    code = gs_currentpoint(ctx->pgs, &initial_point);
563
8.51k
    if (code < 0)
564
0
        return code;
565
566
    /* We don't want to disturb the current path, so do a gsave now
567
     * We will grestore back to this point after we have stroked the
568
     * text, which will leave any current path unchanged.
569
     */
570
8.51k
    code = pdfi_gsave(ctx);
571
8.51k
    if (code < 0)
572
0
        goto Tr1_error;
573
574
    /* Start a new path (so our stroke doesn't include any
575
     * already extant path in the graphics state)
576
     */
577
8.51k
    code = gs_newpath(ctx->pgs);
578
8.51k
    if (code < 0)
579
0
        goto Tr1_error;
580
8.51k
    code = gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
581
8.51k
    if (code < 0)
582
0
        goto Tr1_error;
583
584
    /* Don't draw the text, create a path suitable for stroking */
585
8.51k
    text->operation |= TEXT_DO_FALSE_CHARPATH;
586
587
    /* Run the text methods to create the path */
588
8.51k
    code = gs_text_begin(ctx->pgs, text, ctx->memory, &penum);
589
8.51k
    if (code < 0)
590
0
        goto Tr1_error;
591
592
8.51k
    penum->single_byte_space = true;
593
8.51k
    saved_penum = ctx->text.current_enum;
594
8.51k
    ctx->text.current_enum = penum;
595
8.51k
    code = gs_text_process(penum);
596
8.51k
    gs_text_release(ctx->pgs, penum, "pdfi_Tj");
597
8.51k
    ctx->text.current_enum = saved_penum;
598
8.51k
    if (code < 0)
599
51
        goto Tr1_error;
600
601
    /* After a stroke operation there is no current point and we need
602
     * it to be set as if we had drawn the text. So capture it now
603
     * and we will append a 'move' to the current point when we have
604
     * finished the stroke.
605
     */
606
8.46k
    code = gs_currentpoint(ctx->pgs, &end_point);
607
8.46k
    if (code < 0)
608
0
        goto Tr1_error;
609
8.46k
    if (talphabits != galphabits)
610
0
        dev->color_info.anti_alias.graphics_bits = talphabits;
611
8.46k
    code = gs_fillstroke(ctx->pgs, &restart);
612
8.46k
    if (talphabits != galphabits)
613
0
        dev->color_info.anti_alias.graphics_bits = galphabits;
614
615
8.51k
Tr1_error:
616
    /* And grestore back to where we started */
617
8.51k
    (void)pdfi_grestore(ctx);
618
    /* If everything went well, then move the current point to the
619
     * position we captured at the end of the path creation */
620
8.51k
    if (code >= 0)
621
8.46k
        code = gs_moveto(ctx->pgs, end_point.x, end_point.y);
622
623
8.51k
    text->operation &= ~TEXT_DO_FALSE_CHARPATH;
624
8.51k
    return code;
625
8.46k
}
626
627
static int pdfi_show_Tr_3(pdf_context *ctx, gs_text_params_t *text)
628
139k
{
629
139k
    int code;
630
139k
    gs_text_enum_t *penum=NULL, *saved_penum=NULL;
631
632
    /* Don't draw the text */
633
139k
    text->operation |= TEXT_DO_NONE | TEXT_RETURN_WIDTH | TEXT_RENDER_MODE_3;
634
635
    /* Run the text methods to create the path */
636
139k
    code = gs_text_begin(ctx->pgs, text, ctx->memory, &penum);
637
139k
    if (code < 0)
638
0
        return code;
639
640
139k
    penum->single_byte_space = true;
641
139k
    saved_penum = ctx->text.current_enum;
642
139k
    ctx->text.current_enum = penum;
643
139k
    code = gs_text_process(penum);
644
139k
    if (code >= 0)
645
138k
        code = gs_rmoveto(ctx->pgs, penum->returned.total_width.x, penum->returned.total_width.y);
646
139k
    gs_text_release(ctx->pgs, penum, "pdfi_Tj");
647
139k
    ctx->text.current_enum = saved_penum;
648
649
139k
    return code;
650
139k
}
651
652
/* Prototype the basic 'clip' function, as the following routines will all use it */
653
static int pdfi_show_Tr_7(pdf_context *ctx, gs_text_params_t *text);
654
655
static int pdfi_show_Tr_4(pdf_context *ctx, gs_text_params_t *text)
656
2.41k
{
657
2.41k
    int code;
658
2.41k
    gs_point initial_point;
659
660
    /* Capture the current position */
661
2.41k
    code = gs_currentpoint(ctx->pgs, &initial_point);
662
2.41k
    if (code < 0)
663
0
        return code;
664
665
2.41k
    ctx->text.TextClip = true;
666
    /* First fill the text */
667
2.41k
    code = pdfi_show_Tr_0(ctx, text);
668
2.41k
    if (code < 0)
669
0
        return code;
670
671
    /* Return the current point to the initial point */
672
2.41k
    code = gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
673
2.41k
    if (code >= 0)
674
        /* And add the text to the acumulated path for clipping */
675
2.41k
        code = pdfi_show_Tr_7(ctx, text);
676
677
2.41k
    return code;
678
2.41k
}
679
680
static int pdfi_show_Tr_5(pdf_context *ctx, gs_text_params_t *text)
681
275
{
682
275
    int code;
683
275
    gs_point initial_point;
684
685
    /* Capture the current position */
686
275
    code = gs_currentpoint(ctx->pgs, &initial_point);
687
275
    if (code < 0)
688
0
        return code;
689
690
275
    ctx->text.TextClip = true;
691
    /* First stroke the text */
692
275
    code = pdfi_show_Tr_1(ctx, text);
693
275
    if (code < 0)
694
0
        return code;
695
696
    /* Return the current point to the initial point */
697
275
    code = gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
698
275
    if (code >= 0)
699
        /* And add the text to the acumulated path for clipping */
700
275
        code = pdfi_show_Tr_7(ctx, text);
701
702
275
    return code;
703
275
}
704
705
static int pdfi_show_Tr_6(pdf_context *ctx, gs_text_params_t *text)
706
301
{
707
301
    int code;
708
301
    gs_point initial_point;
709
710
    /* Capture the current position */
711
301
    code = gs_currentpoint(ctx->pgs, &initial_point);
712
301
    if (code < 0)
713
0
        return code;
714
715
301
    ctx->text.TextClip = true;
716
    /* First fill and stroke the text */
717
301
    code = pdfi_show_Tr_2(ctx, text);
718
301
    if (code < 0)
719
0
        return code;
720
721
    /* Return the current point to the initial point */
722
301
    code = gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
723
301
    if (code >= 0)
724
        /* And add the text to the acumulated path for clipping */
725
301
        code = pdfi_show_Tr_7(ctx, text);
726
727
301
    return code;
728
301
}
729
730
static int pdfi_show_Tr_7(pdf_context *ctx, gs_text_params_t *text)
731
3.11k
{
732
3.11k
    int code;
733
3.11k
    gs_text_enum_t *penum=NULL, *saved_penum=NULL;
734
735
3.11k
    ctx->text.TextClip = true;
736
    /* Don't draw the text, create a path suitable for filling */
737
3.11k
    text->operation |= TEXT_DO_TRUE_CHARPATH;
738
739
    /* Run the text methods to create the path */
740
3.11k
    code = gs_text_begin(ctx->pgs, text, ctx->memory, &penum);
741
3.11k
    if (code < 0)
742
0
        goto Tr7_error;
743
744
3.11k
    penum->single_byte_space = true;
745
3.11k
    saved_penum = ctx->text.current_enum;
746
3.11k
    ctx->text.current_enum = penum;
747
3.11k
    code = gs_text_process(penum);
748
3.11k
    gs_text_release(ctx->pgs, penum, "pdfi_Tj");
749
3.11k
    ctx->text.current_enum = saved_penum;
750
751
3.11k
Tr7_error:
752
3.11k
    text->operation &= ~TEXT_DO_TRUE_CHARPATH;
753
3.11k
    return code;
754
3.11k
}
755
756
static int pdfi_show_Tr_preserve(pdf_context *ctx, gs_text_params_t *text)
757
2.49M
{
758
2.49M
    int Trmode = 0, code;
759
2.49M
    pdf_font *current_font = NULL;
760
2.49M
    gs_point initial_point;
761
762
2.49M
    current_font = pdfi_get_current_pdf_font(ctx);
763
2.49M
    if (current_font == NULL)
764
0
        return_error(gs_error_invalidfont);
765
766
    /* Capture the current position, in case we need it for clipping */
767
2.49M
    code = gs_currentpoint(ctx->pgs, &initial_point);
768
769
2.49M
    Trmode = gs_currenttextrenderingmode(ctx->pgs);
770
2.49M
    if (Trmode == 3) {
771
4.21k
        if (current_font->pdfi_font_type == e_pdf_font_type3)
772
0
            text->operation = TEXT_RETURN_WIDTH | TEXT_FROM_CHARS | TEXT_DO_NONE | TEXT_RENDER_MODE_3;
773
4.21k
        else
774
4.21k
            text->operation = TEXT_RETURN_WIDTH | TEXT_FROM_BYTES | TEXT_DO_NONE | TEXT_RENDER_MODE_3;
775
4.21k
    }
776
2.49M
    else
777
2.49M
        text->operation |= TEXT_RETURN_WIDTH | TEXT_DO_DRAW;
778
779
    /* If we're preserving the text rendering mode, then we don't run a separate
780
     * stroke operation, we do effectively run a fill. The fill setup loads the
781
     * fill device colour whch, for patterns, creates the pattern tile.
782
     * But, because we never load the stroke colour into a device colour, the
783
     * pattern tile never gets loaded, so we get an error trying to draw the
784
     * text.
785
     * We could load the stroke colour in gs_text_begin, but that's potentially
786
     * wasteful given that most of the time we won't be using the stroke colour
787
     * os I've chosen to load the stroke device colour explicitly here.
788
     * But, obviously, only when preserving a stroking text rendering mode.
789
     */
790
2.49M
    if (Trmode == 1 || Trmode == 2 || Trmode == 5 || Trmode == 6) {
791
232
        gs_swapcolors_quick(ctx->pgs);
792
793
232
        code = gx_set_dev_color(ctx->pgs);
794
232
        if (code != 0)
795
0
            return code;
796
797
232
        code = gs_gstate_color_load(ctx->pgs);
798
232
        if (code < 0)
799
0
            return code;
800
801
232
        gs_swapcolors_quick(ctx->pgs);
802
232
    }
803
804
    /* If we've switched to aemitting text with a 'clip' rendering mode then we
805
     * tell pdfwrite that here, so that it can emit a 'q', which it can later
806
     * (see pdfi_op_Q) restore to with a Q. It's the only way to get the clipping
807
     * right.
808
     */
809
2.49M
    if (Trmode >= 4 && current_font->pdfi_font_type != e_pdf_font_type3 && ctx->text.TextClip == 0) {
810
15
        gx_device *dev = gs_currentdevice_inline(ctx->pgs);
811
812
15
        ctx->text.TextClip = true;
813
15
        code = dev_proc(dev, dev_spec_op)(dev, gxdso_hilevel_text_clip, (void *)ctx->pgs, 1);
814
15
        if (code < 0 && code != gs_error_undefined)
815
0
            return code;
816
15
  }
817
818
2.49M
    code = pdfi_show_simple(ctx, text);
819
2.49M
    return code;
820
2.49M
}
821
822
static int pdfi_show(pdf_context *ctx, pdf_string *s)
823
13.4M
{
824
13.4M
    int code = 0, SavedTextDepth = 0;
825
13.4M
    int code1 = 0;
826
13.4M
    gs_text_params_t text;
827
13.4M
    pdf_font *current_font = NULL;
828
13.4M
    int Trmode = 0;
829
13.4M
    int initial_gsave_level = ctx->pgs->level;
830
13.4M
    pdfi_trans_state_t state;
831
13.4M
    int outside_text_block = 0;
832
833
13.4M
    if (ctx->text.BlockDepth == 0) {
834
114k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_show", NULL);
835
114k
        outside_text_block = 1;
836
114k
    }
837
838
13.4M
    if (hypot(ctx->pgs->ctm.xx, ctx->pgs->ctm.xy) == 0.0
839
13.4M
     || hypot(ctx->pgs->ctm.yy, ctx->pgs->ctm.yx) == 0.0) {
840
        /* This can mean a font scaled to 0, which appears to be valid, or a degenerate
841
         * ctm/text matrix, which isn't. A degenrate matrix will have triggered a warning
842
         * before, so we just skip the operation here.
843
         */
844
10.0k
         return 0;
845
10.0k
    }
846
847
13.4M
    current_font = pdfi_get_current_pdf_font(ctx);
848
13.4M
    if (current_font == NULL)
849
11
        return_error(gs_error_invalidfont);
850
851
13.4M
    code = pdfi_show_set_params(ctx, s, &text);
852
13.4M
    if (code < 0)
853
60
        goto show_error;
854
855
    /* <sigh> Ugliness......
856
     * It seems that transparency can require that we set a transparency Group
857
     * during the course of a text operation, and that Group can, of course,
858
     * execute operations which are barred during text operations (because
859
     * the Group doesn't affect the text as such). So we need to not throw
860
     * errors if that happens. Do that by just setting the BlockDepth to 0.
861
     */
862
13.4M
    SavedTextDepth = ctx->text.BlockDepth;
863
13.4M
    ctx->text.BlockDepth = 0;
864
13.4M
    code = pdfi_trans_setup_text(ctx, &state, true);
865
13.4M
    ctx->text.BlockDepth = SavedTextDepth;
866
867
13.4M
    if (code >= 0) {
868
13.4M
        if (ctx->device_state.preserve_tr_mode) {
869
2.49M
            code = pdfi_show_Tr_preserve(ctx, &text);
870
10.9M
        } else {
871
10.9M
            Trmode = gs_currenttextrenderingmode(ctx->pgs);
872
873
            /* The spec says that we ignore text rendering modes other than 0 for
874
             * type 3 fonts, but Acrobat also honours mode 3 (do nothing)
875
             */
876
10.9M
            if (current_font->pdfi_font_type == e_pdf_font_type3 && Trmode != 0 && Trmode != 3)
877
260
                Trmode = 0;
878
879
10.9M
            switch(Trmode) {
880
10.8M
            case 0:
881
10.8M
                code = pdfi_show_Tr_0(ctx, &text);
882
10.8M
                break;
883
1.18k
            case 1:
884
1.18k
                code = pdfi_show_Tr_1(ctx, &text);
885
1.18k
                break;
886
8.21k
            case 2:
887
8.21k
                code = pdfi_show_Tr_2(ctx, &text);
888
8.21k
                break;
889
139k
            case 3:
890
139k
                code = pdfi_show_Tr_3(ctx, &text);
891
139k
                break;
892
2.41k
            case 4:
893
2.41k
                code = pdfi_show_Tr_4(ctx, &text);
894
2.41k
                break;
895
275
            case 5:
896
275
                code = pdfi_show_Tr_5(ctx, &text);
897
275
                break;
898
301
            case 6:
899
301
                code = pdfi_show_Tr_6(ctx, &text);
900
301
                break;
901
122
            case 7:
902
122
                code = pdfi_show_Tr_7(ctx, &text);
903
122
                break;
904
0
            default:
905
0
                break;
906
10.9M
            }
907
10.9M
        }
908
13.4M
        code1 = pdfi_trans_teardown_text(ctx, &state);
909
13.4M
        if (code == 0)
910
13.4M
            code = code1;
911
13.4M
    }
912
913
    /* We shouldn't need to do this, but..... It turns out that if we have text rendering mode 3 set
914
     * then gs_text_begin will execute a gsave, push the nulldevice and alter the saved gsave level.
915
     * If we then get an error while processing the text we don't gs_restore enough times which
916
     * leaves the nulldevice as the current device, and eats all the following content!
917
     * To avoid that, we save the gsave depth at the start of this routine, and grestore enough
918
     * times to get back to the same level. I think this is actually a bug in the graphics library
919
     * but it doesn't get exposed in normal usage so we'll just work around it here.
920
     */
921
13.4M
    while(ctx->pgs->level > initial_gsave_level)
922
437
        gs_grestore(ctx->pgs);
923
924
    /* If we were outside a text block when we started this function, then we effectively started
925
     * one by calling the show mechanism. Among other things, this can have pushed a transparency
926
     * group. We need to ensure that this is properly closed, so do the guts of the 'ET' operator
927
     * here (without adjusting the blockDepth, or giving more warnings). See Bug 707753. */
928
13.4M
    if (outside_text_block) {
929
110k
        code1 = do_ET(ctx);
930
110k
        if (code == 0)
931
103k
            code = code1;
932
110k
    }
933
934
13.4M
show_error:
935
13.4M
    if ((void *)text.data.chars != (void *)s->data)
936
46.0k
        gs_free_object(ctx->memory, (void *)text.data.chars, "string gs_chars");
937
938
13.4M
    gs_free_object(ctx->memory, (void *)text.x_widths, "Free X widths array on error");
939
13.4M
    gs_free_object(ctx->memory, (void *)text.y_widths, "Free Y widths array on error");
940
13.4M
    return code;
941
13.4M
}
942
943
/* NOTE: the bounding box this generates has llx and lly at 0,0,
944
   so is arguably not a "real" bounding box
945
 */
946
int pdfi_string_bbox(pdf_context *ctx, pdf_string *s, gs_rect *bboxout, gs_point *advance_width, bool for_stroke)
947
18.1k
{
948
18.1k
    int code = 0;
949
18.1k
    gx_device_bbox *bbdev;
950
18.1k
    pdf_font *current_font = pdfi_get_current_pdf_font(ctx);
951
18.1k
    gs_matrix tmpmat, Trm, Trm_ctm;
952
18.1k
    gs_point cppt, startpt;
953
954
18.1k
    if (current_font == NULL)
955
0
        return_error(gs_error_invalidfont);
956
957
958
18.1k
    for_stroke = current_font->pdfi_font_type == e_pdf_font_type3 ? false : for_stroke;
959
960
18.1k
    if (ctx->devbbox == NULL) {
961
198
        bbdev = gs_alloc_struct_immovable(ctx->memory, gx_device_bbox, &st_device_bbox, "pdfi_string_bbox(bbdev)");
962
198
        if (bbdev == NULL)
963
0
           return_error(gs_error_VMerror);
964
198
        gx_device_bbox_init(bbdev, NULL, ctx->memory);
965
198
        ctx->devbbox = (gx_device *)bbdev;
966
198
        rc_increment(ctx->devbbox);
967
198
    }
968
17.9k
    else {
969
17.9k
        bbdev = (gx_device_bbox *)ctx->devbbox;
970
17.9k
    }
971
18.1k
    gx_device_retain((gx_device *)bbdev, true);
972
18.1k
    gx_device_bbox_set_white_opaque(bbdev, true);
973
974
18.1k
    code = pdfi_gsave(ctx);
975
18.1k
    if (code < 0) {
976
0
        gx_device_retain((gx_device *)bbdev, false);
977
0
        return code;
978
0
    }
979
    /* The bbox device needs a fairly high resolution to get accurate results */
980
18.1k
    gx_device_set_resolution((gx_device *)bbdev, 720.0, 720.0);
981
982
18.1k
    code = gs_setdevice_no_erase(ctx->pgs, (gx_device *)bbdev);
983
18.1k
    if (code < 0)
984
0
        goto out;
985
986
18.1k
    Trm.xx = ctx->pgs->PDFfontsize * (ctx->pgs->texthscaling / 100);
987
18.1k
    Trm.xy = 0;
988
18.1k
    Trm.yx = 0;
989
18.1k
    Trm.yy = ctx->pgs->PDFfontsize;
990
18.1k
    Trm.tx = 0;
991
18.1k
    Trm.ty = ctx->pgs->textrise;
992
993
18.1k
    memcpy(&tmpmat, &ctx->pgs->textmatrix, sizeof(tmpmat));
994
    /* We want to avoid any translations unrelated to the extents of the text */
995
18.1k
    tmpmat.tx = tmpmat.ty = 0;
996
18.1k
    gs_matrix_multiply(&Trm, &tmpmat, &Trm);
997
998
18.1k
    memcpy(&tmpmat, &ctm_only(ctx->pgs), sizeof(tmpmat));
999
    /* As above */
1000
18.1k
    tmpmat.tx = tmpmat.ty = 0;
1001
18.1k
    gs_matrix_multiply(&Trm, &tmpmat, &Trm_ctm);
1002
18.1k
    gs_setmatrix(ctx->pgs, &Trm_ctm);
1003
1004
18.1k
    gs_settextrenderingmode(ctx->pgs, for_stroke ? 2 : 0);
1005
1006
18.1k
    code = pdfi_gs_setgray(ctx, 1.0);
1007
18.1k
    if (code < 0)
1008
0
        goto out;
1009
1010
    /* The bbox device (not surprisingly) clips to the device width/height
1011
       so we have to offset our initial point to cope with glyphs that include
1012
       negative coordinates. Most significantly, descender features - hence the
1013
       larger y offset.
1014
       The offsets are guesses.
1015
     */
1016
18.1k
    startpt.x = ctx->pgs->PDFfontsize;
1017
18.1k
    startpt.y = ctx->pgs->PDFfontsize * 16.0 * (ctx->pgs->textrise >= 0 ? 1 : -ctx->pgs->textrise);
1018
18.1k
    code = gs_moveto(ctx->pgs, startpt.x, startpt.y);
1019
18.1k
    if (code < 0)
1020
0
        goto out;
1021
1022
    /* Pretend to have at least one BT or pdfi_show will generate a spurious warning */
1023
18.1k
    ctx->text.BlockDepth++;
1024
18.1k
    code = pdfi_show(ctx, s);
1025
    /* And an ET */
1026
18.1k
    ctx->text.BlockDepth--;
1027
18.1k
    if (code < 0)
1028
0
        goto out;
1029
1030
18.1k
    code = gx_device_bbox_bbox(bbdev, bboxout);
1031
18.1k
    if (code < 0)
1032
0
        goto out;
1033
1034
18.1k
    bboxout->q.x -= bboxout->p.x;
1035
18.1k
    bboxout->q.y -= bboxout->p.y;
1036
18.1k
    bboxout->p.x = bboxout->p.y = 0;
1037
1038
18.1k
    code = gs_currentpoint(ctx->pgs, &cppt);
1039
18.1k
    if (code >= 0) {
1040
18.1k
        code = gs_point_transform(startpt.x, startpt.y, &ctm_only(ctx->pgs), &startpt);
1041
18.1k
        if (code < 0)
1042
0
            goto out;
1043
18.1k
        advance_width->x = ctx->pgs->current_point.x - startpt.x;
1044
18.1k
        advance_width->y = ctx->pgs->current_point.y - startpt.y;
1045
18.1k
        code = gs_point_transform_inverse(advance_width->x, advance_width->y, &tmpmat, advance_width);
1046
18.1k
    }
1047
18.1k
out:
1048
18.1k
    pdfi_grestore(ctx);
1049
18.1k
    (void)gs_closedevice((gx_device *)bbdev);
1050
18.1k
    gx_device_retain((gx_device *)bbdev, false);
1051
1052
18.1k
    return code;
1053
18.1k
}
1054
1055
int pdfi_Tj(pdf_context *ctx)
1056
1.69M
{
1057
1.69M
    int code = 0, code1 = 0;
1058
1.69M
    bool retrying = false, saved_preserve = false;
1059
1.69M
    pdf_string *s = NULL;
1060
1.69M
    gs_matrix saved, Trm;
1061
1.69M
    gs_point initial_point, current_point, pt;
1062
1.69M
    double linewidth = ctx->pgs->line_params.half_width;
1063
1.69M
    int initial_point_valid;
1064
1065
1.69M
    if (pdfi_count_stack(ctx) < 1)
1066
32.2k
        return_error(gs_error_stackunderflow);
1067
1068
1.65M
    if (pdfi_oc_is_off(ctx))
1069
2.00k
        goto exit;
1070
1071
1.65M
    s = (pdf_string *)ctx->stack_top[-1];
1072
1.65M
    if (pdfi_type_of(s) != PDF_STRING) {
1073
50.4k
        pdfi_pop(ctx, 1);
1074
50.4k
        return_error(gs_error_typecheck);
1075
50.4k
    }
1076
1077
    /* We can't rely on the stack reference because an error during
1078
       the text operation (i.e. retrieving objects for glyph metrics
1079
       may cause the stack to be cleared.
1080
     */
1081
1.60M
    pdfi_countup(s);
1082
1.60M
    pdfi_pop(ctx, 1);
1083
1084
1.60M
retry:
1085
    /* Save the CTM for later restoration */
1086
1.60M
    saved = ctm_only(ctx->pgs);
1087
1.60M
    initial_point_valid = (gs_currentpoint(ctx->pgs, &initial_point) >= 0);
1088
1089
1.60M
    Trm.xx = ctx->pgs->PDFfontsize * (ctx->pgs->texthscaling / 100);
1090
1.60M
    Trm.xy = 0;
1091
1.60M
    Trm.yx = 0;
1092
1.60M
    Trm.yy = ctx->pgs->PDFfontsize;
1093
1.60M
    Trm.tx = 0;
1094
1.60M
    Trm.ty = ctx->pgs->textrise;
1095
1096
1.60M
    code = gs_matrix_multiply(&Trm, &ctx->pgs->textmatrix, &Trm);
1097
1.60M
    if (code < 0)
1098
0
        goto exit;
1099
1100
1.60M
    if (!ctx->device_state.preserve_tr_mode) {
1101
1.53M
        code = gs_distance_transform_inverse(ctx->pgs->line_params.half_width, 0, &Trm, &pt);
1102
1.53M
        if (code < 0)
1103
56.4k
            goto exit;
1104
1.47M
        ctx->pgs->line_params.half_width = sqrt((pt.x * pt.x) + (pt.y * pt.y));
1105
1.47M
    } else {
1106
        /* We have to adjust the stroke width for pdfwrite so that we take into
1107
         * account the CTM, but we do not spply the font scaling. Because of
1108
         * the disconnect between pdfwrite and the interpreter, we also have to
1109
         * remove the scaling due to the resolution.
1110
         */
1111
74.9k
        gs_matrix devmatrix, matrix;
1112
74.9k
        gx_device *device = gs_currentdevice(ctx->pgs);
1113
1114
74.9k
        devmatrix.xx = 72.0 / device->HWResolution[0];
1115
74.9k
        devmatrix.xy = 0;
1116
74.9k
        devmatrix.yx = 0;
1117
74.9k
        devmatrix.yy = 72.0 / device->HWResolution[1];
1118
74.9k
        devmatrix.tx = 0;
1119
74.9k
        devmatrix.ty = 0;
1120
1121
74.9k
        code = gs_matrix_multiply(&saved, &devmatrix, &matrix);
1122
74.9k
        if (code < 0)
1123
0
            goto exit;
1124
1125
74.9k
        code = gs_distance_transform(ctx->pgs->line_params.half_width, 0, &matrix, &pt);
1126
74.9k
        if (code < 0)
1127
0
            goto exit;
1128
74.9k
        ctx->pgs->line_params.half_width = sqrt((pt.x * pt.x) + (pt.y * pt.y));
1129
74.9k
    }
1130
1131
1.55M
    code = gs_matrix_multiply(&Trm, &ctm_only(ctx->pgs), &Trm);
1132
1.55M
    if (code < 0)
1133
0
        goto exit;
1134
1135
1.55M
    code = gs_setmatrix(ctx->pgs, &Trm);
1136
1.55M
    if (code < 0)
1137
0
        goto exit;
1138
1139
1.55M
    code = gs_moveto(ctx->pgs, 0, 0);
1140
1.55M
    if (code < 0)
1141
0
        goto Tj_error;
1142
1143
1.55M
    code = pdfi_show(ctx, s);
1144
1.55M
    if (code < 0)
1145
42.1k
        goto Tj_error;
1146
1147
1.50M
    ctx->pgs->line_params.half_width = linewidth;
1148
    /* Update the Text matrix with the current point, for the next operation
1149
     */
1150
1.50M
    (void)gs_currentpoint(ctx->pgs, &current_point); /* Always valid */
1151
1.50M
    Trm.xx = ctx->pgs->PDFfontsize * (ctx->pgs->texthscaling / 100);
1152
1.50M
    Trm.xy = 0;
1153
1.50M
    Trm.yx = 0;
1154
1.50M
    Trm.yy = ctx->pgs->PDFfontsize;
1155
1.50M
    Trm.tx = 0;
1156
1.50M
    Trm.ty = 0;
1157
1.50M
    code = gs_matrix_multiply(&Trm, &ctx->pgs->textmatrix, &Trm);
1158
1.50M
    if (code < 0)
1159
0
        goto Tj_error;
1160
1161
1.50M
    code = gs_distance_transform(current_point.x, current_point.y, &Trm, &pt);
1162
1.50M
    if (code < 0)
1163
0
        goto Tj_error;
1164
1.50M
    ctx->pgs->textmatrix.tx += pt.x;
1165
1.50M
    ctx->pgs->textmatrix.ty += pt.y;
1166
1167
1.55M
Tj_error:
1168
    /* Restore the CTM to the saved value */
1169
1.55M
    (void)gs_setmatrix(ctx->pgs, &saved);
1170
    /* And restore the currentpoint */
1171
1.55M
    if (initial_point_valid)
1172
1.48M
        (void)gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
1173
68.3k
    else
1174
68.3k
        code1 = gs_newpath(ctx->pgs);
1175
    /* And the line width */
1176
1.55M
    ctx->pgs->line_params.half_width = linewidth;
1177
1.55M
    if (code == gs_error_fallback_failed && !retrying) {
1178
6
        retrying = true;
1179
6
        saved_preserve = ctx->device_state.preserve_tr_mode;
1180
6
        ctx->device_state.preserve_tr_mode = false;
1181
6
        goto retry;
1182
6
    }
1183
1184
1.55M
    if (code == 0 && code1 < 0)
1185
0
        code = code1;
1186
1.60M
exit:
1187
1.60M
    if (retrying)
1188
6
        ctx->device_state.preserve_tr_mode = saved_preserve;
1189
1.60M
    pdfi_countdown(s);
1190
1.60M
    return code;
1191
1.55M
}
1192
1193
int pdfi_TJ(pdf_context *ctx)
1194
1.90M
{
1195
1.90M
    int code = 0, code1 = 0, i;
1196
1.90M
    bool retrying = false, saved_preserve = false;
1197
1.90M
    pdf_array *a = NULL;
1198
1.90M
    pdf_obj *o = NULL;
1199
1.90M
    double dx = 0;
1200
1.90M
    gs_point pt;
1201
1.90M
    gs_matrix saved, Trm;
1202
1.90M
    gs_point initial_point, current_point;
1203
1.90M
    double linewidth = ctx->pgs->line_params.half_width;
1204
1.90M
    pdf_font *current_font = NULL;
1205
1.90M
    int initial_point_valid;
1206
1207
1.90M
    current_font = pdfi_get_current_pdf_font(ctx);
1208
1.90M
    if (current_font == NULL)
1209
5.52k
        return_error(gs_error_invalidfont);
1210
1211
1.90M
    if (ctx->text.BlockDepth == 0) {
1212
67.3k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_TJ", NULL);
1213
67.3k
    }
1214
1215
1.90M
    if (pdfi_count_stack(ctx) < 1)
1216
14.6k
        return_error(gs_error_stackunderflow);
1217
1218
1.88M
    if (pdfi_oc_is_off(ctx))
1219
454
        goto exit;
1220
1221
1.88M
    a = (pdf_array *)ctx->stack_top[-1];
1222
1.88M
    if (pdfi_type_of(a) != PDF_ARRAY) {
1223
54.3k
        pdfi_pop(ctx, 1);
1224
54.3k
        return gs_note_error(gs_error_typecheck);
1225
54.3k
    }
1226
1.83M
    pdfi_countup(a);
1227
1.83M
    pdfi_pop(ctx, 1);
1228
1229
1.83M
retry:
1230
    /* Save the CTM for later restoration */
1231
1.83M
    saved = ctm_only(ctx->pgs);
1232
1.83M
    ctx->text.initial_current_point_valid = (initial_point_valid = (gs_currentpoint(ctx->pgs, &initial_point) >= 0));
1233
1234
    /* Calculate the text rendering matrix, see section 1.7 PDF Reference
1235
     * page 409, section 5.3.3 Text Space details.
1236
     */
1237
1.83M
    Trm.xx = ctx->pgs->PDFfontsize * (ctx->pgs->texthscaling / 100);
1238
1.83M
    Trm.xy = 0;
1239
1.83M
    Trm.yx = 0;
1240
1.83M
    Trm.yy = ctx->pgs->PDFfontsize;
1241
1.83M
    Trm.tx = 0;
1242
1.83M
    Trm.ty = ctx->pgs->textrise;
1243
1244
1.83M
    code = gs_matrix_multiply(&Trm, &ctx->pgs->textmatrix, &Trm);
1245
1.83M
    if (code < 0)
1246
0
        goto exit;
1247
1248
1.83M
    if (!ctx->device_state.preserve_tr_mode) {
1249
1.65M
        code = gs_distance_transform_inverse(ctx->pgs->line_params.half_width, 0, &Trm, &pt);
1250
1.65M
        if (code < 0)
1251
13.7k
            goto exit;
1252
1.64M
        ctx->pgs->line_params.half_width = sqrt((pt.x * pt.x) + (pt.y * pt.y));
1253
1.64M
    } else {
1254
        /* We have to adjust the stroke width for pdfwrite so that we take into
1255
         * account the CTM, but we do not spply the font scaling. Because of
1256
         * the disconnect between pdfwrite and the interpreter, we also have to
1257
         * remove the scaling due to the resolution.
1258
         */
1259
178k
        gs_matrix devmatrix, matrix;
1260
178k
        gx_device *device = gs_currentdevice(ctx->pgs);
1261
1262
178k
        devmatrix.xx = 72.0 / device->HWResolution[0];
1263
178k
        devmatrix.xy = 0;
1264
178k
        devmatrix.yx = 0;
1265
178k
        devmatrix.yy = 72.0 / device->HWResolution[1];
1266
178k
        devmatrix.tx = 0;
1267
178k
        devmatrix.ty = 0;
1268
1269
178k
        code = gs_matrix_multiply(&saved, &devmatrix, &matrix);
1270
178k
        if (code < 0)
1271
0
            goto exit;
1272
1273
178k
        code = gs_distance_transform(ctx->pgs->line_params.half_width, 0, &matrix, &pt);
1274
178k
        if (code < 0)
1275
0
            goto exit;
1276
178k
        ctx->pgs->line_params.half_width = sqrt((pt.x * pt.x) + (pt.y * pt.y));
1277
178k
    }
1278
1279
1.81M
    code = gs_matrix_multiply(&Trm, &ctm_only(ctx->pgs), &Trm);
1280
1.81M
    if (code < 0)
1281
0
        goto exit;
1282
1.81M
    code = gs_setmatrix(ctx->pgs, &Trm);
1283
1.81M
    if (code < 0)
1284
0
        goto TJ_error;
1285
1286
1.81M
    code = gs_moveto(ctx->pgs, 0, 0);
1287
1.81M
    if (code < 0)
1288
0
        goto TJ_error;
1289
1290
23.8M
    for (i = 0; i < pdfi_array_size(a); i++) {
1291
22.1M
        code = pdfi_array_get(ctx, a, (uint64_t)i, &o);
1292
22.1M
        if (code < 0)
1293
1
            goto TJ_error;
1294
1295
22.1M
        if (pdfi_type_of(o) == PDF_STRING)
1296
11.9M
            code = pdfi_show(ctx, (pdf_string *)o);
1297
10.1M
        else {
1298
10.1M
            code = pdfi_obj_to_real(ctx, o, &dx);
1299
10.1M
            if (code < 0)
1300
90
                goto TJ_error;
1301
10.1M
            dx /= -1000;
1302
10.1M
            if (current_font->pfont && current_font->pfont->WMode == 0)
1303
10.1M
                code = gs_rmoveto(ctx->pgs, dx, 0);
1304
5.74k
            else
1305
5.74k
                code = gs_rmoveto(ctx->pgs, 0, dx);
1306
10.1M
        }
1307
22.1M
        pdfi_countdown(o);
1308
22.1M
        o = NULL;
1309
22.1M
        if (code < 0)
1310
43.6k
            goto TJ_error;
1311
22.1M
    }
1312
1313
    /* We don't want to update the Text matrix if we aren't in a text block, there
1314
     * is no point because the matrix isn't persistent between text blocks.
1315
     */
1316
1.77M
    if (ctx->text.BlockDepth > 0) {
1317
        /* Update the Text matrix with the current point, for the next operation
1318
         */
1319
1.75M
        (void)gs_currentpoint(ctx->pgs, &current_point); /* Always valid */
1320
1.75M
        Trm.xx = ctx->pgs->PDFfontsize * (ctx->pgs->texthscaling / 100);
1321
1.75M
        Trm.xy = 0;
1322
1.75M
        Trm.yx = 0;
1323
1.75M
        Trm.yy = ctx->pgs->PDFfontsize;
1324
1.75M
        Trm.tx = 0;
1325
1.75M
        Trm.ty = 0;
1326
1.75M
        code = gs_matrix_multiply(&Trm, &ctx->pgs->textmatrix, &Trm);
1327
1.75M
        if (code < 0)
1328
0
            goto TJ_error;
1329
1330
1.75M
        code = gs_distance_transform(current_point.x, current_point.y, &Trm, &pt);
1331
1.75M
        if (code < 0)
1332
0
            goto TJ_error;
1333
1.75M
        ctx->pgs->textmatrix.tx += pt.x;
1334
1.75M
        ctx->pgs->textmatrix.ty += pt.y;
1335
1.75M
    }
1336
1.81M
TJ_error:
1337
1.81M
    pdfi_countdown(o);
1338
    /* Restore the CTM to the saved value */
1339
1.81M
    (void)gs_setmatrix(ctx->pgs, &saved);
1340
    /* And restore the currentpoint */
1341
1.81M
    if (initial_point_valid)
1342
1.78M
        (void)gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
1343
33.7k
    else
1344
33.7k
        code1 = gs_newpath(ctx->pgs);
1345
    /* And the line width */
1346
1.81M
    ctx->pgs->line_params.half_width = linewidth;
1347
1.81M
    if (code == gs_error_fallback_failed && !retrying) {
1348
0
        retrying = true;
1349
0
        saved_preserve = ctx->device_state.preserve_tr_mode;
1350
0
        ctx->device_state.preserve_tr_mode = false;
1351
0
        goto retry;
1352
0
    }
1353
1354
1.81M
    if (code == 0 && code1 < 0)
1355
0
        code = code1;
1356
1.83M
 exit:
1357
1.83M
    if (retrying)
1358
0
        ctx->device_state.preserve_tr_mode = saved_preserve;
1359
1360
1.83M
    pdfi_countdown(a);
1361
1.83M
    return code;
1362
1.81M
}
1363
1364
static int pdfi_set_TL(pdf_context *ctx, double TL)
1365
414k
{
1366
414k
    return gs_settextleading(ctx->pgs, TL);
1367
414k
}
1368
1369
int pdfi_TL(pdf_context *ctx)
1370
12.5k
{
1371
12.5k
    int code;
1372
12.5k
    double d;
1373
1374
12.5k
    code = pdfi_destack_real(ctx, &d);
1375
12.5k
    if (code < 0)
1376
50
        return code;
1377
1378
12.5k
    return pdfi_set_TL(ctx, -d);
1379
12.5k
}
1380
1381
int pdfi_Tm(pdf_context *ctx)
1382
1.77M
{
1383
1.77M
    int code;
1384
1.77M
    float m[6];
1385
1.77M
    gs_matrix mat;
1386
1387
1.77M
    code = pdfi_destack_floats(ctx, m, 6);
1388
1.77M
    if (code < 0)
1389
183k
        return code;
1390
1391
1.59M
    if (ctx->text.BlockDepth == 0) {
1392
62.6k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_Tm", NULL);
1393
1394
62.6k
        gs_make_identity(&mat);
1395
62.6k
        code = gs_settextmatrix(ctx->pgs, &mat);
1396
62.6k
        if (code < 0)
1397
0
            return code;
1398
1399
62.6k
        code = gs_settextlinematrix(ctx->pgs, &mat);
1400
62.6k
        if (code < 0)
1401
0
            return code;
1402
62.6k
    }
1403
1404
1.59M
    if (hypot(m[0], m[1]) == 0.0 || hypot(m[3], m[2]) == 0.0)
1405
18.4k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_DEGENERATETM, "pdfi_Tm", NULL);
1406
1407
1.59M
    code = gs_settextmatrix(ctx->pgs, (gs_matrix *)&m);
1408
1.59M
    if (code < 0)
1409
0
        return code;
1410
1411
1.59M
    return gs_settextlinematrix(ctx->pgs, (gs_matrix *)&m);
1412
1.59M
}
1413
1414
int pdfi_Tr(pdf_context *ctx)
1415
621k
{
1416
621k
    int code;
1417
621k
    int64_t mode;
1418
1419
621k
    code = pdfi_destack_int(ctx, &mode);
1420
621k
    if (code < 0)
1421
7.84k
        return code;
1422
1423
613k
    if (mode < 0 || mode > 7)
1424
4.91k
        return_error(gs_error_rangecheck);
1425
1426
    /* Detect attempts to switch from a clipping mode to a non-clipping
1427
     * mode, this is defined as invalid in the spec. (We don't warn if we haven't yet
1428
     * drawn any text in the clipping mode).
1429
     */
1430
608k
    if (gs_currenttextrenderingmode(ctx->pgs) > 3 && mode < 4 && ctx->text.BlockDepth != 0 && ctx->text.TextClip)
1431
65
        pdfi_set_warning(ctx, 0, NULL, W_PDF_BADTRSWITCH, "pdfi_Tr", NULL);
1432
1433
608k
    if (ctx->device_state.preserve_tr_mode) {
1434
5.10k
        gs_settextrenderingmode(ctx->pgs, mode);
1435
5.10k
    } else
1436
603k
    {
1437
603k
        gs_point initial_point;
1438
1439
603k
        if (gs_currenttextrenderingmode(ctx->pgs) < 4 && mode >= 4 && ctx->text.BlockDepth != 0) {
1440
411
            gs_settextrenderingmode(ctx->pgs, mode);
1441
            /* Capture the current position */
1442
411
            code = gs_currentpoint(ctx->pgs, &initial_point);
1443
            /* Start a new path (so our clip doesn't include any
1444
             * already extant path in the graphics state)
1445
             */
1446
411
            gs_newpath(ctx->pgs);
1447
411
            if (code >= 0)
1448
365
                gs_moveto(ctx->pgs, initial_point.x, initial_point.y);
1449
411
            code = 0;
1450
603k
        } else if (gs_currenttextrenderingmode(ctx->pgs) >= 4 && mode < 4 && ctx->text.BlockDepth != 0) {
1451
            /* If we are switching from a clipping mode to a non-clipping
1452
             * mode then behave as if we had an implicit ET to flush the
1453
             * accumulated text to a clip, then set the text rendering mode
1454
             * to the non-clip mode, and perform an implicit BT.
1455
             */
1456
67
            code = pdfi_ET(ctx);
1457
67
            if (code < 0)
1458
2
                return code;
1459
65
            gs_settextrenderingmode(ctx->pgs, mode);
1460
65
            code = pdfi_BT(ctx);
1461
65
            if (code < 0)
1462
0
                return code;
1463
65
        }
1464
603k
        else
1465
603k
            gs_settextrenderingmode(ctx->pgs, mode);
1466
603k
    }
1467
608k
    return code;
1468
608k
}
1469
1470
static int pdfi_set_Ts(pdf_context *ctx, double Ts)
1471
3.59k
{
1472
3.59k
    return gs_settextrise(ctx->pgs, Ts);
1473
3.59k
}
1474
1475
int pdfi_Ts(pdf_context *ctx)
1476
4.09k
{
1477
4.09k
    int code;
1478
4.09k
    double d;
1479
1480
4.09k
    code = pdfi_destack_real(ctx, &d);
1481
4.09k
    if (code < 0)
1482
502
        return code;
1483
1484
3.59k
    return pdfi_set_Ts(ctx, d);
1485
4.09k
}
1486
1487
static int pdfi_set_Tw(pdf_context *ctx, double Tw)
1488
156k
{
1489
156k
    return gs_setwordspacing(ctx->pgs, Tw);
1490
156k
}
1491
1492
int pdfi_Tw(pdf_context *ctx)
1493
156k
{
1494
156k
    int code;
1495
156k
    double d;
1496
1497
156k
    code = pdfi_destack_real(ctx, &d);
1498
156k
    if (code < 0)
1499
820
        return code;
1500
1501
156k
    return pdfi_set_Tw(ctx, d);
1502
156k
}
1503
1504
int pdfi_Tz(pdf_context *ctx)
1505
97.7k
{
1506
97.7k
    int code;
1507
97.7k
    double d;
1508
1509
97.7k
    code = pdfi_destack_real(ctx, &d);
1510
97.7k
    if (code < 0)
1511
1.89k
        return code;
1512
1513
95.8k
    return gs_settexthscaling(ctx->pgs, d);
1514
97.7k
}
1515
1516
int pdfi_singlequote(pdf_context *ctx)
1517
18.4k
{
1518
18.4k
    int code;
1519
1520
18.4k
    if (ctx->text.BlockDepth == 0) {
1521
8.92k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_singlequote", NULL);
1522
8.92k
    }
1523
1524
18.4k
    code = pdfi_T_star(ctx);
1525
18.4k
    if (code < 0)
1526
0
        return code;
1527
1528
18.4k
    return pdfi_Tj(ctx);
1529
18.4k
}
1530
1531
int pdfi_doublequote(pdf_context *ctx)
1532
6.51k
{
1533
6.51k
    int code;
1534
6.51k
    double Tw, Tc;
1535
1536
6.51k
    if (ctx->text.BlockDepth == 0) {
1537
6.24k
        pdfi_set_warning(ctx, 0, NULL, W_PDF_TEXTOPNOBT, "pdfi_T_doublequote", NULL);
1538
6.24k
    }
1539
1540
6.51k
    if (pdfi_count_stack(ctx) < 3) {
1541
6.14k
        pdfi_clearstack(ctx);
1542
6.14k
        return_error(gs_error_stackunderflow);
1543
6.14k
    }
1544
1545
374
    if (pdfi_type_of(ctx->stack_top[-1]) != PDF_STRING) {
1546
181
        pdfi_pop(ctx, 3);
1547
181
        return gs_note_error(gs_error_typecheck);
1548
181
    }
1549
1550
193
    code = pdfi_obj_to_real(ctx, ctx->stack_top[-2], &Tc);
1551
193
    if (code < 0)
1552
3
        goto error;
1553
190
    code = pdfi_set_Tc(ctx, Tc);
1554
190
    if (code < 0)
1555
0
        goto error;
1556
1557
190
    code = pdfi_obj_to_real(ctx, ctx->stack_top[-3], &Tw);
1558
190
    if (code < 0)
1559
10
        goto error;
1560
180
    code = pdfi_set_Tw(ctx, Tw);
1561
180
    if (code < 0)
1562
0
        goto error;
1563
1564
180
    code = pdfi_T_star(ctx);
1565
180
    if (code < 0)
1566
0
        goto error;
1567
1568
180
    code = pdfi_Tj(ctx);
1569
    /* Tj pops one off the stack for us, leaving us 2 to go. */
1570
180
    pdfi_pop(ctx, 2);
1571
180
    return code;
1572
1573
13
error:
1574
13
    pdfi_pop(ctx, 3);
1575
13
    return code;
1576
180
}