~ubuntu-branches/ubuntu/jaunty/ghostscript/jaunty-updates

« back to all changes in this revision

Viewing changes to src/gdevpdts.c

  • Committer: Bazaar Package Importer
  • Author(s): Till Kamppeter
  • Date: 2009-01-20 16:40:45 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20090120164045-lnfhi0n30o5lwhwa
Tags: 8.64.dfsg.1~svn9377-0ubuntu1
* New upstream release (SVN rev 9377)
   o Fixes many bugs concerning PDF rendering, to make the PDF printing
     workflow correctly working.
   o Fixes long-standing bugs in many drivers, like input paper tray and
     duplex options not working for the built-in PCL 4, 5, 5c, 5e, and
     6/XL drivers, PDF input not working for bjc600, bjc800, and cups
     output devices, several options not working and uninitialized
     memory with cups output device.
   o Merged nearly all patches of the Ubuntu and Debian packages upstream.
   o Fixes LP: #317810, LP: #314439, LP: #314018.
* debian/patches/03_libpaper_support.dpatch,
  debian/patches/11_gs-cjk_font_glyph_handling_fix.dpatch,
  debian/patches/12_gs-cjk_vertical_writing_metrics_fix.dpatch,
  debian/patches/13_gs-cjk_cjkps_examples.dpatch,
  debian/patches/20_bbox_segv_fix.dpatch,
  debian/patches/21_brother_7x0_gdi_fix.dpatch,
  debian/patches/22_epsn_margin_workaround.dpatch,
  debian/patches/24_gs_man_fix.dpatch,
  debian/patches/25_toolbin_insecure_tmp_usage_fix.dpatch,
  debian/patches/26_assorted_script_fixes.dpatch,
  debian/patches/29_gs_css_fix.dpatch,
  debian/patches/30_ps2pdf_man_improvement.dpatch,
  debian/patches/31_fix-gc-sigbus.dpatch,
  debian/patches/34_ftbfs-on-hurd-fix.dpatch,
  debian/patches/35_disable_libcairo.dpatch,
  debian/patches/38_pxl-duplex.dpatch,
  debian/patches/39_pxl-resolution.dpatch,
  debian/patches/42_gs-init-ps-delaybind-fix.dpatch,
  debian/patches/45_bjc600-bjc800-pdf-input.dpatch,
  debian/patches/48_cups-output-device-pdf-duplex-uninitialized-memory-fix.dpatch,
  debian/patches/50_lips4-floating-point-exception.dpatch,
  debian/patches/52_cups-device-logging.dpatch,
  debian/patches/55_pcl-input-slot-fix.dpatch,
  debian/patches/57_pxl-input-slot-fix.dpatch,
  debian/patches/60_pxl-cups-driver-pdf.dpatch,
  debian/patches/62_onebitcmyk-pdf.dpatch,
  debian/patches/65_too-big-temp-files-1.dpatch,
  debian/patches/67_too-big-temp-files-2.dpatch,
  debian/patches/70_take-into-account-data-in-stream-buffer-before-refill.dpatch:
  Removed, applied upstream.
* debian/patches/01_docdir_fix_for_debian.dpatch,
  debian/patches/02_gs_man_fix_debian.dpatch,
  debian/patches/01_docdir-fix-for-debian.dpatch,
  debian/patches/02_docdir-fix-for-debian.dpatch: Renamed patches to
  make merging with Debian easier.
* debian/patches/32_improve-handling-of-media-size-changes-from-gv.dpatch, 
  debian/patches/33_bad-params-to-xinitimage-on-large-bitmaps.dpatch:
  regenerated for new source directory structure.
* debian/rules: Corrected paths to remove cidfmap (it is in Resource/Init/
  in GS 8.64) and to install headers (source paths are psi/ and base/ now).
* debian/rules: Remove all fontmaps, as DeFoMa replaces them.
* debian/local/pdftoraster/pdftoraster.c,
  debian/local/pdftoraster/pdftoraster.convs, debian/rules: Removed
  added pdftoraster filter and use the one which comes with Ghostscript.
* debian/ghostscript.links: s/8.63/8.64/

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2001-2006 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, modified
8
 
   or distributed except as expressly authorized under the terms of that
9
 
   license.  Refer to licensing information at http://www.artifex.com/
10
 
   or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11
 
   San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12
 
*/
13
 
 
14
 
/* $Id: gdevpdts.c 8518 2008-02-07 09:33:22Z ken $ */
15
 
/* Text state management for pdfwrite */
16
 
#include "math_.h"
17
 
#include "memory_.h"
18
 
#include "gx.h"
19
 
#include "gserrors.h"
20
 
#include "gdevpdfx.h"
21
 
#include "gdevpdtx.h"
22
 
#include "gdevpdtf.h"           /* for pdfont->FontType */
23
 
#include "gdevpdts.h"
24
 
#include "gdevpdtt.h"
25
 
 
26
 
/* ================ Types and structures ================ */
27
 
 
28
 
/*
29
 
 * We accumulate text, and possibly horizontal or vertical moves (depending
30
 
 * on the font's writing direction), until forced to emit them.  This
31
 
 * happens when changing text state parameters, when the buffer is full, or
32
 
 * when exiting text mode.
33
 
 *
34
 
 * Note that movement distances are measured in unscaled text space.
35
 
 */
36
 
typedef struct pdf_text_move_s {
37
 
    int index;                  /* within buffer.chars */
38
 
    float amount;
39
 
} pdf_text_move_t;
40
 
#define MAX_TEXT_BUFFER_CHARS 200 /* arbitrary, but overflow costs 5 chars */
41
 
#define MAX_TEXT_BUFFER_MOVES 50 /* ibid. */
42
 
typedef struct pdf_text_buffer_s {
43
 
    /*
44
 
     * Invariant:
45
 
     *   count_moves <= MAX_TEXT_BUFFER_MOVES
46
 
     *   count_chars <= MAX_TEXT_BUFFER_CHARS
47
 
     *   0 < moves[0].index < moves[1].index < ... moves[count_moves-1].index
48
 
     *     <= count_chars
49
 
     *   moves[*].amount != 0
50
 
     */
51
 
    pdf_text_move_t moves[MAX_TEXT_BUFFER_MOVES + 1];
52
 
    byte chars[MAX_TEXT_BUFFER_CHARS];
53
 
    int count_moves;
54
 
    int count_chars;
55
 
} pdf_text_buffer_t;
56
 
#define TEXT_BUFFER_DEFAULT\
57
 
    { { 0, 0 } },               /* moves */\
58
 
    { 0 },                      /* chars */\
59
 
    0,                          /* count_moves */\
60
 
    0                           /* count_chars */
61
 
 
62
 
/*
63
 
 * We maintain two sets of text state values (as defined in gdevpdts.h): the
64
 
 * "in" set reflects the current state as seen by the client, while the
65
 
 * "out" set reflects the current state as seen by an interpreter processing
66
 
 * the content stream emitted so far.  We emit commands to make "out" the
67
 
 * same as "in" when necessary.
68
 
 */
69
 
/*typedef struct pdf_text_state_s pdf_text_state_t;*/  /* gdevpdts.h */
70
 
struct pdf_text_state_s {
71
 
    /* State as seen by client */
72
 
    pdf_text_state_values_t in; /* see above */
73
 
    gs_point start;             /* in.txy as of start of buffer */
74
 
    pdf_text_buffer_t buffer;
75
 
    int wmode;                  /* WMode of in.font */
76
 
    /* State relative to content stream */
77
 
    pdf_text_state_values_t out; /* see above */
78
 
    double leading;             /* TL (not settable, only used internally) */
79
 
    bool use_leading;           /* if true, use T* or ' */
80
 
    bool continue_line;
81
 
    gs_point line_start;
82
 
    gs_point out_pos;           /* output position */
83
 
};
84
 
static const pdf_text_state_t ts_default = {
85
 
    /* State as seen by client */
86
 
    { TEXT_STATE_VALUES_DEFAULT },      /* in */
87
 
    { 0, 0 },                   /* start */
88
 
    { TEXT_BUFFER_DEFAULT },    /* buffer */
89
 
    0,                          /* wmode */
90
 
    /* State relative to content stream */
91
 
    { TEXT_STATE_VALUES_DEFAULT },      /* out */
92
 
    0,                          /* leading */
93
 
    0 /*false*/,                /* use_leading */
94
 
    0 /*false*/,                /* continue_line */
95
 
    { 0, 0 },                   /* line_start */
96
 
    { 0, 0 }                    /* output position */
97
 
};
98
 
/* GC descriptor */
99
 
gs_private_st_ptrs2(st_pdf_text_state, pdf_text_state_t,  "pdf_text_state_t",
100
 
                    pdf_text_state_enum_ptrs, pdf_text_state_reloc_ptrs,
101
 
                    in.pdfont, out.pdfont);
102
 
 
103
 
/* ================ Procedures ================ */
104
 
 
105
 
/* ---------------- Private ---------------- */
106
 
 
107
 
/*
108
 
 * Append a writing-direction movement to the text being accumulated.  If
109
 
 * the buffer is full, or the requested movement is not in writing
110
 
 * direction, return <0 and do nothing.  (This is different from
111
 
 * pdf_append_chars.)  Requires pts->buffer.count_chars > 0.
112
 
 */
113
 
static int
114
 
append_text_move(pdf_text_state_t *pts, floatp dw)
115
 
{
116
 
    int count = pts->buffer.count_moves;
117
 
    int pos = pts->buffer.count_chars;
118
 
    double rounded;
119
 
 
120
 
    if (count > 0 && pts->buffer.moves[count - 1].index == pos) {
121
 
        /* Merge adjacent moves. */
122
 
        dw += pts->buffer.moves[--count].amount;
123
 
    }
124
 
    /* Round dw if it's very close to an integer. */
125
 
    rounded = floor(dw + 0.5);
126
 
    if (fabs(dw - rounded) < 0.001)
127
 
        dw = rounded;
128
 
    if (dw < -MAX_USER_COORD) {
129
 
        /* Acrobat reader 4.0c, 5.0 can't handle big offsets. 
130
 
           Adobe Reader 6 can. */
131
 
        return -1;
132
 
    }
133
 
    if (dw != 0) {
134
 
        if (count == MAX_TEXT_BUFFER_MOVES)
135
 
            return -1;
136
 
        pts->buffer.moves[count].index = pos;
137
 
        pts->buffer.moves[count].amount = dw;
138
 
        ++count;
139
 
    }
140
 
    pts->buffer.count_moves = count;
141
 
    return 0;
142
 
}
143
 
 
144
 
/*
145
 
 * Set *pdist to the distance (dx,dy), in the space defined by *pmat.
146
 
 */
147
 
static int
148
 
set_text_distance(gs_point *pdist, floatp dx, floatp dy, const gs_matrix *pmat)
149
 
{
150
 
    int code = gs_distance_transform_inverse(dx, dy, pmat, pdist);
151
 
    double rounded;
152
 
 
153
 
    if (code == gs_error_undefinedresult) {
154
 
        /* The CTM is degenerate.
155
 
           Can't know the distance in user space.
156
 
           Set zero because we believe it is not important for rendering.
157
 
           We want to copy the text to PDF to make it searchable.
158
 
           Bug 689006.
159
 
         */
160
 
        pdist->x = pdist->y = 0;
161
 
    } else if (code < 0)
162
 
        return code;
163
 
    /* If the distance is very close to integers, round it. */
164
 
    if (fabs(pdist->x - (rounded = floor(pdist->x + 0.5))) < 0.0005)
165
 
        pdist->x = rounded;
166
 
    if (fabs(pdist->y - (rounded = floor(pdist->y + 0.5))) < 0.0005)
167
 
        pdist->y = rounded;
168
 
    return 0;
169
 
}
170
 
 
171
 
/*
172
 
 * Test whether the transformation parts of two matrices are compatible.
173
 
 */
174
 
static bool
175
 
matrix_is_compatible(const gs_matrix *pmat1, const gs_matrix *pmat2)
176
 
{
177
 
    return (pmat2->xx == pmat1->xx && pmat2->xy == pmat1->xy &&
178
 
            pmat2->yx == pmat1->yx && pmat2->yy == pmat1->yy);
179
 
}
180
 
 
181
 
/*
182
 
 * Try to handle a change of text position with TJ or a space
183
 
 * character.  If successful, return >=0, if not, return <0.
184
 
 */
185
 
static int
186
 
add_text_delta_move(gx_device_pdf *pdev, const gs_matrix *pmat)
187
 
{
188
 
    pdf_text_state_t *const pts = pdev->text->text_state;
189
 
 
190
 
    if (matrix_is_compatible(pmat, &pts->in.matrix)) {
191
 
        double dx = pmat->tx - pts->in.matrix.tx,
192
 
            dy = pmat->ty - pts->in.matrix.ty;
193
 
        gs_point dist;
194
 
        double dw, dnotw, tdw;
195
 
        int code;
196
 
 
197
 
        code = set_text_distance(&dist, dx, dy, pmat);
198
 
        if (code < 0)
199
 
            return code;
200
 
        if (pts->wmode)
201
 
            dw = dist.y, dnotw = dist.x;
202
 
        else
203
 
            dw = dist.x, dnotw = dist.y;
204
 
        if (dnotw == 0 && pts->buffer.count_chars > 0 &&
205
 
            /*
206
 
             * Acrobat Reader limits the magnitude of user-space
207
 
             * coordinates.  Also, AR apparently doesn't handle large
208
 
             * positive movement values (negative X displacements), even
209
 
             * though the PDF Reference says this bug was fixed in AR3.
210
 
             *
211
 
             * Old revisions used the upper threshold 1000 for tdw,
212
 
             * but it appears too big when a font sets a too big
213
 
             * character width in setcachedevice. Particularly this happens 
214
 
             * with a Type 3 font generated by Aldus Freehand 4.0
215
 
             * to represent a texture - see bug #687051.
216
 
             * The problem is that when the Widths is multiplied 
217
 
             * to the font size, the viewer represents the result
218
 
             * with insufficient fraction bits to represent the precise width.
219
 
             * We work around that problem here restricting tdw
220
 
             * with a smaller threshold 990. Our intention is to 
221
 
             * disable Tj when the real glyph width appears smaller
222
 
             * than 1% of the width specified in setcachedevice.
223
 
             * A Td instruction will be generated instead.
224
 
             * Note that the value 990 is arbitrary and may need a
225
 
             * further adjustment.
226
 
             */
227
 
            (tdw = dw * -1000.0 / pts->in.size,
228
 
             tdw >= -MAX_USER_COORD && tdw < 990)
229
 
            ) {
230
 
            /* Use TJ. */
231
 
            int code = append_text_move(pts, tdw);
232
 
 
233
 
            if (code >= 0)
234
 
                goto finish;
235
 
        }
236
 
    }
237
 
    return -1;
238
 
 finish:
239
 
    pts->in.matrix = *pmat;
240
 
    return 0;
241
 
}
242
 
 
243
 
/*
244
 
 * Set the text matrix for writing text.  The translation component of the
245
 
 * matrix is the text origin.  If the non-translation components of the
246
 
 * matrix differ from the current ones, write a Tm command; if there is only
247
 
 * a Y translation, set use_leading so the next text string will be written
248
 
 * with ' rather than Tj; otherwise, write a Td command.
249
 
 */
250
 
static int
251
 
pdf_set_text_matrix(gx_device_pdf * pdev)
252
 
{
253
 
    pdf_text_state_t *pts = pdev->text->text_state;
254
 
    stream *s = pdev->strm;
255
 
 
256
 
    pts->use_leading = false;
257
 
    if (matrix_is_compatible(&pts->out.matrix, &pts->in.matrix)) {
258
 
        gs_point dist;
259
 
        int code;
260
 
 
261
 
        code = set_text_distance(&dist, pts->start.x - pts->line_start.x,
262
 
                          pts->start.y - pts->line_start.y, &pts->in.matrix);
263
 
        if (code < 0)
264
 
            return code;
265
 
        if (dist.x == 0 && dist.y < 0) {
266
 
            /* Use TL, if needed, and T* or '. */
267
 
            float dist_y = (float)-dist.y;
268
 
 
269
 
            if (fabs(pts->leading - dist_y) > 0.0005) {
270
 
                pprintg1(s, "%g TL\n", dist_y);
271
 
                pts->leading = dist_y;
272
 
            }
273
 
            pts->use_leading = true;
274
 
        } else {
275
 
            /* Use Td. */
276
 
            pprintg2(s, "%g %g Td\n", dist.x, dist.y);
277
 
        }
278
 
    } else {                    /* Use Tm. */
279
 
        /*
280
 
         * See stream_to_text in gdevpdfu.c for why we need the following
281
 
         * matrix adjustments.
282
 
         */
283
 
        double sx = 72.0 / pdev->HWResolution[0],
284
 
            sy = 72.0 / pdev->HWResolution[1];
285
 
 
286
 
        pprintg6(s, "%g %g %g %g %g %g Tm\n",
287
 
                 pts->in.matrix.xx * sx, pts->in.matrix.xy * sy,
288
 
                 pts->in.matrix.yx * sx, pts->in.matrix.yy * sy,
289
 
                 pts->start.x * sx, pts->start.y * sy);
290
 
    }
291
 
    pts->line_start.x = pts->start.x;
292
 
    pts->line_start.y = pts->start.y;
293
 
    pts->out.matrix = pts->in.matrix;
294
 
    return 0;
295
 
}
296
 
 
297
 
/* ---------------- Public ---------------- */
298
 
 
299
 
/*
300
 
 * Allocate and initialize text state bookkeeping.
301
 
 */
302
 
pdf_text_state_t *
303
 
pdf_text_state_alloc(gs_memory_t *mem)
304
 
{
305
 
    pdf_text_state_t *pts =
306
 
        gs_alloc_struct(mem, pdf_text_state_t, &st_pdf_text_state,
307
 
                        "pdf_text_state_alloc");
308
 
 
309
 
    if (pts == 0)
310
 
        return 0;
311
 
    *pts = ts_default;
312
 
    return pts;
313
 
}
314
 
 
315
 
/*
316
 
 * Set the text state to default values.
317
 
 */
318
 
void
319
 
pdf_set_text_state_default(pdf_text_state_t *pts)
320
 
{
321
 
    *pts = ts_default;
322
 
}
323
 
 
324
 
/*
325
 
 * Copy the text state.
326
 
 */
327
 
void
328
 
pdf_text_state_copy(pdf_text_state_t *pts_to, pdf_text_state_t *pts_from)
329
 
{
330
 
    *pts_to = *pts_from;
331
 
}
332
 
 
333
 
/*
334
 
 * Reset the text state to its condition at the beginning of the page.
335
 
 */
336
 
void
337
 
pdf_reset_text_page(pdf_text_data_t *ptd)
338
 
{
339
 
    pdf_set_text_state_default(ptd->text_state);
340
 
}
341
 
 
342
 
/*
343
 
 * Reset the text state after a grestore.
344
 
 */
345
 
void
346
 
pdf_reset_text_state(pdf_text_data_t *ptd)
347
 
{
348
 
    pdf_text_state_t *pts = ptd->text_state;
349
 
 
350
 
    pts->out = ts_default.out;
351
 
    pts->leading = 0;
352
 
}
353
 
 
354
 
/*
355
 
 * Transition from stream context to text context.
356
 
 */
357
 
int
358
 
pdf_from_stream_to_text(gx_device_pdf *pdev)
359
 
{
360
 
    pdf_text_state_t *pts = pdev->text->text_state;
361
 
 
362
 
    gs_make_identity(&pts->out.matrix);
363
 
    pts->line_start.x = pts->line_start.y = 0;
364
 
    pts->continue_line = false; /* Not sure, probably doesn't matter. */
365
 
    pts->buffer.count_chars = 0;
366
 
    pts->buffer.count_moves = 0;
367
 
    return 0;
368
 
}
369
 
 
370
 
int
371
 
pdf_get_stoted_text_size(pdf_text_state_t *state)
372
 
{
373
 
    return state->buffer.count_chars;
374
 
}
375
 
 
376
 
/*
377
 
 *  Flush text from buffer.
378
 
 */
379
 
static int
380
 
flush_text_buffer(gx_device_pdf *pdev)
381
 
{
382
 
    pdf_text_state_t *pts = pdev->text->text_state;
383
 
    stream *s = pdev->strm;
384
 
 
385
 
    if (pts->buffer.count_chars != 0) {
386
 
        pdf_font_resource_t *pdfont = pts->in.pdfont;
387
 
        int code = pdf_assign_font_object_id(pdev, pdfont);
388
 
 
389
 
        if (code < 0)
390
 
            return code;
391
 
        code = pdf_add_resource(pdev, pdev->substream_Resources, "/Font", (pdf_resource_t *)pdfont);
392
 
        if (code < 0)
393
 
            return code;
394
 
    }
395
 
    if (pts->buffer.count_moves > 0) {
396
 
        int i, cur = 0;
397
 
 
398
 
        if (pts->use_leading)
399
 
            stream_puts(s, "T*");
400
 
        stream_puts(s, "[");
401
 
        for (i = 0; i < pts->buffer.count_moves; ++i) {
402
 
            int next = pts->buffer.moves[i].index;
403
 
 
404
 
            pdf_put_string(pdev, pts->buffer.chars + cur, next - cur);
405
 
            pprintg1(s, "%g", pts->buffer.moves[i].amount);
406
 
            cur = next;
407
 
        }
408
 
        if (pts->buffer.count_chars > cur)
409
 
            pdf_put_string(pdev, pts->buffer.chars + cur,
410
 
                           pts->buffer.count_chars - cur);
411
 
        stream_puts(s, "]TJ\n");
412
 
    } else {
413
 
        pdf_put_string(pdev, pts->buffer.chars, pts->buffer.count_chars);
414
 
        stream_puts(s, (pts->use_leading ? "'\n" : "Tj\n"));
415
 
    }
416
 
    pts->buffer.count_chars = 0;
417
 
    pts->buffer.count_moves = 0;
418
 
    pts->use_leading = false;
419
 
    return 0;
420
 
}
421
 
 
422
 
/*
423
 
 * Transition from string context to text context.
424
 
 */
425
 
static int
426
 
sync_text_state(gx_device_pdf *pdev)
427
 
{
428
 
    pdf_text_state_t *pts = pdev->text->text_state;
429
 
    stream *s = pdev->strm;
430
 
    int code;
431
 
 
432
 
    if (pts->buffer.count_chars == 0)
433
 
        return 0;               /* nothing to output */
434
 
 
435
 
    if (pts->continue_line) 
436
 
        return flush_text_buffer(pdev);
437
 
 
438
 
    /* Bring text state parameters up to date. */
439
 
 
440
 
    if (pts->out.character_spacing != pts->in.character_spacing) {
441
 
        pprintg1(s, "%g Tc\n", pts->in.character_spacing);
442
 
        pts->out.character_spacing = pts->in.character_spacing;
443
 
    }
444
 
 
445
 
    if (pts->out.pdfont != pts->in.pdfont || pts->out.size != pts->in.size) {
446
 
        pdf_font_resource_t *pdfont = pts->in.pdfont;
447
 
 
448
 
        code = pdf_assign_font_object_id(pdev, pdfont);
449
 
        if (code < 0)
450
 
            return code;
451
 
        pprints1(s, "/%s ", pdfont->rname);
452
 
        pprintg1(s, "%g Tf\n", pts->in.size);
453
 
        pts->out.pdfont = pdfont;
454
 
        pts->out.size = pts->in.size;
455
 
        /*
456
 
         * In PDF, the only place to specify WMode is in the CMap
457
 
         * (a.k.a. Encoding) of a Type 0 font.
458
 
         */
459
 
        pts->wmode =
460
 
            (pdfont->FontType == ft_composite ?
461
 
             pdfont->u.type0.WMode : 0);
462
 
        code = pdf_used_charproc_resources(pdev, pdfont);
463
 
        if (code < 0)
464
 
            return code;
465
 
    }
466
 
 
467
 
    if (memcmp(&pts->in.matrix, &pts->out.matrix, sizeof(pts->in.matrix)) ||
468
 
         ((pts->start.x != pts->out_pos.x || pts->start.y != pts->out_pos.y) &&
469
 
          (pts->buffer.count_chars != 0 || pts->buffer.count_moves != 0))) {
470
 
        /* pdf_set_text_matrix sets out.matrix = in.matrix */
471
 
        code = pdf_set_text_matrix(pdev);
472
 
        if (code < 0)
473
 
            return code;
474
 
    }
475
 
 
476
 
    if (pts->out.render_mode != pts->in.render_mode) {
477
 
        pprintg1(s, "%g Tr\n", pts->in.render_mode);
478
 
        pts->out.render_mode = pts->in.render_mode;
479
 
    }
480
 
 
481
 
    if (pts->out.word_spacing != pts->in.word_spacing) {
482
 
        if (memchr(pts->buffer.chars, 32, pts->buffer.count_chars)) {
483
 
            pprintg1(s, "%g Tw\n", pts->in.word_spacing);
484
 
            pts->out.word_spacing = pts->in.word_spacing;
485
 
        }
486
 
    }
487
 
 
488
 
    return flush_text_buffer(pdev);
489
 
}
490
 
 
491
 
int
492
 
pdf_from_string_to_text(gx_device_pdf *pdev)
493
 
{
494
 
    return sync_text_state(pdev);
495
 
}
496
 
 
497
 
/*
498
 
 * Close the text aspect of the current contents part.
499
 
 */
500
 
void
501
 
pdf_close_text_contents(gx_device_pdf *pdev)
502
 
{
503
 
    /*
504
 
     * Clear the font pointer.  This is probably left over from old code,
505
 
     * but it is appropriate in case we ever choose in the future to write
506
 
     * out and free font resources before the end of the document.
507
 
     */
508
 
    pdf_text_state_t *pts = pdev->text->text_state;
509
 
 
510
 
    pts->in.pdfont = pts->out.pdfont = 0;
511
 
    pts->in.size = pts->out.size = 0;
512
 
}
513
 
 
514
 
/*
515
 
 * Test whether a change in render_mode requires resetting the stroke
516
 
 * parameters.
517
 
 */
518
 
bool
519
 
pdf_render_mode_uses_stroke(const gx_device_pdf *pdev,
520
 
                            const pdf_text_state_values_t *ptsv)
521
 
{
522
 
    return (pdev->text->text_state->in.render_mode != ptsv->render_mode &&
523
 
            ptsv->render_mode == 1 || ptsv->render_mode == 2 ||
524
 
            ptsv->render_mode == 5 || ptsv->render_mode == 6);
525
 
}
526
 
 
527
 
/*
528
 
 * Read the stored client view of text state values.
529
 
 */
530
 
void
531
 
pdf_get_text_state_values(gx_device_pdf *pdev, pdf_text_state_values_t *ptsv)
532
 
{
533
 
    *ptsv = pdev->text->text_state->in;
534
 
}
535
 
 
536
 
/*
537
 
 * Set wmode to text state.
538
 
 */
539
 
void
540
 
pdf_set_text_wmode(gx_device_pdf *pdev, int wmode)
541
 
{
542
 
    pdf_text_state_t *pts = pdev->text->text_state;
543
 
 
544
 
    pts->wmode = wmode;
545
 
}
546
 
 
547
 
 
548
 
/*
549
 
 * Set the stored client view of text state values.
550
 
 */
551
 
int
552
 
pdf_set_text_state_values(gx_device_pdf *pdev,
553
 
                          const pdf_text_state_values_t *ptsv)
554
 
{
555
 
    pdf_text_state_t *pts = pdev->text->text_state;
556
 
 
557
 
    if (pts->buffer.count_chars > 0) {
558
 
        int code;
559
 
 
560
 
        if (pts->in.character_spacing == ptsv->character_spacing &&
561
 
            pts->in.pdfont == ptsv->pdfont && pts->in.size == ptsv->size &&
562
 
            pts->in.render_mode == ptsv->render_mode &&
563
 
            pts->in.word_spacing == ptsv->word_spacing
564
 
            ) {
565
 
            if (!memcmp(&pts->in.matrix, &ptsv->matrix,
566
 
                        sizeof(pts->in.matrix)))
567
 
                return 0;
568
 
            /* add_text_delta_move sets pts->in.matrix if successful */
569
 
            code = add_text_delta_move(pdev, &ptsv->matrix);
570
 
            if (code >= 0)
571
 
                return 0;
572
 
        }
573
 
        code = sync_text_state(pdev);
574
 
        if (code < 0)
575
 
            return code;
576
 
    }
577
 
 
578
 
    pts->in = *ptsv;
579
 
    pts->continue_line = false;
580
 
    return 0;
581
 
}
582
 
 
583
 
/*
584
 
 * Transform a distance from unscaled text space (text space ignoring the
585
 
 * scaling implied by the font size) to device space.
586
 
 */
587
 
int
588
 
pdf_text_distance_transform(floatp wx, floatp wy, const pdf_text_state_t *pts,
589
 
                            gs_point *ppt)
590
 
{
591
 
    return gs_distance_transform(wx, wy, &pts->in.matrix, ppt);
592
 
}
593
 
 
594
 
/*
595
 
 * Return the current (x,y) text position as seen by the client, in
596
 
 * unscaled text space.
597
 
 */
598
 
void
599
 
pdf_text_position(const gx_device_pdf *pdev, gs_point *ppt)
600
 
{
601
 
    pdf_text_state_t *pts = pdev->text->text_state;
602
 
 
603
 
    ppt->x = pts->in.matrix.tx;
604
 
    ppt->y = pts->in.matrix.ty;
605
 
}
606
 
 
607
 
/*
608
 
 * Append characters to text being accumulated, giving their advance width
609
 
 * in device space.
610
 
 */
611
 
int
612
 
pdf_append_chars(gx_device_pdf * pdev, const byte * str, uint size,
613
 
                 floatp wx, floatp wy, bool nobreak)
614
 
{
615
 
    pdf_text_state_t *pts = pdev->text->text_state;
616
 
    const byte *p = str;
617
 
    uint left = size;
618
 
 
619
 
    if (pts->buffer.count_chars == 0 && pts->buffer.count_moves == 0) {
620
 
        pts->out_pos.x = pts->start.x = pts->in.matrix.tx;
621
 
        pts->out_pos.y = pts->start.y = pts->in.matrix.ty;
622
 
    }
623
 
    while (left)
624
 
        if (pts->buffer.count_chars == MAX_TEXT_BUFFER_CHARS ||
625
 
            (nobreak && pts->buffer.count_chars + left > MAX_TEXT_BUFFER_CHARS)) {
626
 
            int code = sync_text_state(pdev);
627
 
 
628
 
            if (code < 0)
629
 
                return code;
630
 
            /* We'll keep a continuation of this line in the buffer,
631
 
             * but the current input parameters don't correspond to
632
 
             * the current position, because the text was broken in a
633
 
             * middle with unknown current point.
634
 
             * Don't change the output text state parameters
635
 
             * until input parameters are changed. 
636
 
             * pdf_set_text_state_values will reset the 'continue_line' flag 
637
 
             * at that time.
638
 
             */
639
 
            pts->continue_line = true;
640
 
        } else {
641
 
            int code = pdf_open_page(pdev, PDF_IN_STRING);
642
 
            uint copy;
643
 
 
644
 
            if (code < 0)
645
 
                return code;
646
 
            copy = min(MAX_TEXT_BUFFER_CHARS - pts->buffer.count_chars, left);
647
 
            memcpy(pts->buffer.chars + pts->buffer.count_chars, p, copy);
648
 
            pts->buffer.count_chars += copy;
649
 
            p += copy;
650
 
            left -= copy;
651
 
        }
652
 
    pts->in.matrix.tx += wx;
653
 
    pts->in.matrix.ty += wy;
654
 
    pts->out_pos.x += wx;
655
 
    pts->out_pos.y += wy;
656
 
    return 0;
657
 
}
658
 
 
659
 
/* Check a new piece of charpath text to see if its safe to combine
660
 
 * with a previous text operation using text rendering modes.
661
 
 */
662
 
bool pdf_compare_text_state_for_charpath(pdf_text_state_t *pts, gx_device_pdf *pdev, 
663
 
                                         gs_imager_state *pis, gs_font *font, 
664
 
                                         const gs_text_params_t *text)
665
 
{
666
 
    int code;
667
 
    float size;
668
 
    gs_matrix smat, tmat;
669
 
    struct pdf_font_resource_s *pdfont;
670
 
 
671
 
    /* check to ensure the new text has the same length as the saved text */
672
 
    if(text->size != pts->buffer.count_chars)
673
 
        return(false);
674
 
 
675
 
    if(font->FontType == ft_user_defined)
676
 
        return(false);
677
 
 
678
 
    /* check to ensure the new text has the same data as the saved text */
679
 
    if(memcmp(text->data.bytes, &pts->buffer.chars, text->size))
680
 
        return(false);
681
 
 
682
 
    /* See if the same font is in use by checking the attahced pdfont resource for
683
 
     * the currrent font and comparing with the saved text state
684
 
     */
685
 
    code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
686
 
    if(code < 0)
687
 
        return(false);
688
 
 
689
 
    if(!pdfont || pdfont != pts->in.pdfont)
690
 
        return(false);
691
 
 
692
 
    /* Check to see the new text starts at the same point as the saved text. 
693
 
     * NB! only check 2 decimal places, allow some slack in the match. This
694
 
     * still may prove to be too tight a requirement.
695
 
     */
696
 
    if((int)(pts->start.x * 100) != (int)(pis->current_point.x * 100) || 
697
 
        (int)(pts->start.y * 100) != (int)(pis->current_point.y * 100))
698
 
        return(false);
699
 
 
700
 
    size = pdf_calculate_text_size(pis, pdfont, &font->FontMatrix, &smat, &tmat, font, pdev);
701
 
 
702
 
    /* Finally, check the calculated size against the size stored in
703
 
     * the text state.
704
 
     */
705
 
    if(size != pts->in.size)
706
 
        return(false);
707
 
 
708
 
    return(true);
709
 
}
710
 
 
711
 
/* Add a render mode to the rendering mode of the current text.
712
 
 * mode 0 = fill
713
 
 * mode 1 = stroke
714
 
 * mode 2 = clip
715
 
 * If the modes are not compatible returns 0. NB currently only
716
 
 * a stroke rendering mode is supported.
717
 
 */
718
 
int pdf_modify_text_render_mode(pdf_text_state_t *pts, int render_mode)
719
 
{
720
 
    switch (pts->in.render_mode) {
721
 
        case 0:
722
 
            if (render_mode == 1) {
723
 
                pts->in.render_mode = 2;
724
 
                return(1);
725
 
            }
726
 
            break;
727
 
        case 1:
728
 
            if (render_mode == 1) 
729
 
                return(1);
730
 
            break;
731
 
        case 2:
732
 
            if (render_mode == 1) 
733
 
                return(1);
734
 
            break;
735
 
        case 3:
736
 
            if (render_mode == 1) {
737
 
                pts->in.render_mode = 1;
738
 
                return(1);
739
 
            }
740
 
            break;
741
 
        case 4:
742
 
            if (render_mode == 1) {
743
 
                pts->in.render_mode = 6;
744
 
                return(1);
745
 
            }
746
 
            break;
747
 
        case 5:
748
 
            if (render_mode == 1) 
749
 
                return(1);
750
 
            break;
751
 
        case 6:
752
 
            if (render_mode == 1) 
753
 
                return(1);
754
 
            break;
755
 
        case 7:
756
 
            if (render_mode == 1) {
757
 
                pts->in.render_mode = 5;
758
 
                return(1);
759
 
            }
760
 
            break;
761
 
        default:
762
 
            break;
763
 
    }
764
 
    return(0);
765
 
}