Coverage Report

Created: 2022-10-31 07:00

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