Coverage Report

Created: 2025-12-31 07:31

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