Coverage Report

Created: 2025-04-22 06:20

/src/libspectre/ghostscript/base/gdevvec.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2020 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
17
/* Utilities for "vector" devices */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "string_.h"
21
#include "gx.h"
22
#include "gp.h"
23
#include "gserrors.h"
24
#include "gsparam.h"
25
#include "gsutil.h"
26
#include "gxfixed.h"
27
#include "gdevvec.h"
28
#include "gscspace.h"
29
#include "gxiparam.h"
30
#include "gxdcolor.h"
31
#include "gxpaint.h"    /* requires gx_path, ... */
32
#include "gzpath.h"
33
#include "gzcpath.h"
34
#include "gxdevsop.h"
35
36
#include "gdevkrnlsclass.h" /* 'standard' built in subclasses, currently First/Last Page and obejct filter */
37
38
/* Structure descriptors */
39
public_st_device_vector();
40
public_st_vector_image_enum();
41
42
/* ================ Default implementations of vector procs ================ */
43
44
int
45
gdev_vector_setflat(gx_device_vector * vdev, double flatness)
46
0
{
47
0
    return 0;
48
0
}
49
50
/* Put a path on the output file. */
51
static bool
52
coord_between(fixed start, fixed mid, fixed end)
53
0
{
54
0
    return (start <= end ? start <= mid && mid <= end :
55
0
            start >= mid && mid >= end);
56
0
}
57
int
58
gdev_vector_dopath(gx_device_vector *vdev, const gx_path * ppath,
59
                   gx_path_type_t type, const gs_matrix *pmat)
60
0
{
61
0
    bool do_close =
62
0
        (type & (gx_path_type_stroke | gx_path_type_always_close)) != 0;
63
0
    gs_fixed_rect rbox;
64
0
    gx_path_rectangular_type rtype = gx_path_is_rectangular(ppath, &rbox);
65
0
    gs_path_enum cenum;
66
0
    gdev_vector_dopath_state_t state;
67
0
    gs_fixed_point line_start, line_end;
68
0
    bool incomplete_line = false;
69
0
    bool need_moveto = false;
70
0
    int code;
71
72
0
    gdev_vector_dopath_init(&state, vdev, type, pmat);
73
    /*
74
     * if the path type is stroke, we only recognize closed
75
     * rectangles; otherwise, we recognize all rectangles.
76
     * Note that for stroking with a transformation, we can't use dorect,
77
     * which requires (untransformed) device coordinates.
78
     */
79
0
    if (rtype != prt_none &&
80
0
        (!(type & gx_path_type_stroke) || rtype == prt_closed) &&
81
0
        (pmat == 0 || is_xxyy(pmat) || is_xyyx(pmat)) &&
82
0
        (state.scale_mat.xx == 1.0 && state.scale_mat.yy == 1.0 &&
83
0
         is_xxyy(&state.scale_mat) &&
84
0
         is_fzero2(state.scale_mat.tx, state.scale_mat.ty))
85
0
        ) {
86
0
        gs_point p, q;
87
88
0
        gs_point_transform_inverse((double)rbox.p.x, (double)rbox.p.y,
89
0
                                   &state.scale_mat, &p);
90
0
        gs_point_transform_inverse((double)rbox.q.x, (double)rbox.q.y,
91
0
                                   &state.scale_mat, &q);
92
0
        code = vdev_proc(vdev, dorect)(vdev, (fixed)p.x, (fixed)p.y,
93
0
                                       (fixed)q.x, (fixed)q.y, type);
94
0
        if (code >= 0)
95
0
            return code;
96
        /* If the dorect proc failed, use a general path. */
97
0
    }
98
0
    code = vdev_proc(vdev, beginpath)(vdev, type);
99
0
    if (code < 0)
100
0
        return code;
101
0
    gx_path_enum_init(&cenum, ppath);
102
0
    for (;;) {
103
0
        gs_fixed_point vs[3];
104
0
        int pe_op = gx_path_enum_next(&cenum, vs);
105
106
0
    sw:
107
0
        if (type & gx_path_type_optimize) {
108
0
        opt:
109
            /* RJW: We fail to optimize gaptos */
110
0
            if (pe_op == gs_pe_lineto) {
111
0
                if (!incomplete_line) {
112
0
                    line_end = vs[0];
113
0
                    incomplete_line = true;
114
0
                    continue;
115
0
                }
116
                /*
117
                 * Merge collinear horizontal or vertical line segments
118
                 * going in the same direction.
119
                 */
120
0
                if (vs[0].x == line_end.x) {
121
0
                    if (vs[0].x == line_start.x &&
122
0
                        coord_between(line_start.y, line_end.y, vs[0].y)
123
0
                        ) {
124
0
                        line_end.y = vs[0].y;
125
0
                        continue;
126
0
                    }
127
0
                } else if (vs[0].y == line_end.y) {
128
0
                    if (vs[0].y == line_start.y &&
129
0
                        coord_between(line_start.x, line_end.x, vs[0].x)
130
0
                        ) {
131
0
                        line_end.x = vs[0].x;
132
0
                        continue;
133
0
                    }
134
0
                }
135
0
            }
136
0
            if (incomplete_line) {
137
0
                if (need_moveto) { /* see gs_pe_moveto case */
138
0
                    code = gdev_vector_dopath_segment(&state, gs_pe_moveto,
139
0
                                                      &line_start);
140
0
                    if (code < 0)
141
0
                        return code;
142
0
                    need_moveto = false;
143
0
                }
144
0
                code = gdev_vector_dopath_segment(&state, gs_pe_lineto,
145
0
                                                  &line_end);
146
0
                if (code < 0)
147
0
                    return code;
148
0
                line_start = line_end;
149
0
                incomplete_line = false;
150
0
                goto opt;
151
0
            }
152
0
        }
153
0
        switch (pe_op) {
154
0
        case 0:   /* done */
155
0
        done:
156
0
            code = vdev_proc(vdev, endpath)(vdev, type);
157
0
            return (code < 0 ? code : 0);
158
0
        case gs_pe_curveto:
159
0
            if (need_moveto) { /* see gs_pe_moveto case */
160
0
                code = gdev_vector_dopath_segment(&state, gs_pe_moveto,
161
0
                                                  &line_start);
162
0
                if (code < 0)
163
0
                    return code;
164
0
                need_moveto = false;
165
0
            }
166
0
            line_start = vs[2];
167
0
            goto draw;
168
0
        case gs_pe_moveto:
169
            /*
170
             * A bug in Acrobat Reader 4 causes it to draw a single pixel
171
             * for a fill with an isolated moveto.  If we're doing a fill
172
             * without a stroke, defer emitting a moveto until we know that
173
             * the subpath has more elements.
174
             */
175
0
            line_start = vs[0];
176
0
            if (!(type & gx_path_type_stroke) && (type & gx_path_type_fill)) {
177
0
                need_moveto = true;
178
0
                continue;
179
0
            }
180
0
            goto draw;
181
0
        case gs_pe_lineto:
182
0
        case gs_pe_gapto:
183
0
            if (need_moveto) { /* see gs_pe_moveto case */
184
0
                code = gdev_vector_dopath_segment(&state, gs_pe_moveto,
185
0
                                                  &line_start);
186
0
                if (code < 0)
187
0
                    return code;
188
0
                need_moveto = false;
189
0
            }
190
0
            line_start = vs[0];
191
0
            goto draw;
192
0
        case gs_pe_closepath:
193
0
            if (need_moveto) { /* see gs_pe_moveto case */
194
0
                need_moveto = false;
195
0
                continue;
196
0
            }
197
0
            if (!do_close) {
198
0
                pe_op = gx_path_enum_next(&cenum, vs);
199
0
                if (pe_op == 0)
200
0
                    goto done;
201
0
                code = gdev_vector_dopath_segment(&state, gs_pe_closepath, vs);
202
0
                if (code < 0)
203
0
                    return code;
204
0
                goto sw;
205
0
            }
206
            /* falls through */
207
0
        draw:
208
0
            code = gdev_vector_dopath_segment(&state, pe_op, vs);
209
0
            if (code < 0)
210
0
                return code;
211
0
        }
212
0
        incomplete_line = false; /* only needed if optimizing */
213
0
    }
214
0
}
215
216
int
217
gdev_vector_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
218
                   fixed y1, gx_path_type_t type)
219
0
{
220
0
    int code = (*vdev_proc(vdev, beginpath)) (vdev, type);
221
222
0
    if (code < 0)
223
0
        return code;
224
0
    code = gdev_vector_write_rectangle(vdev, x0, y0, x1, y1,
225
0
                                       (type & gx_path_type_stroke) != 0,
226
0
                                       gx_rect_x_first);
227
0
    if (code < 0)
228
0
        return code;
229
0
    return (*vdev_proc(vdev, endpath)) (vdev, type);
230
0
}
231
232
/* ================ Utility procedures ================ */
233
234
/* Recompute the cached color values. */
235
static void
236
gdev_vector_load_cache(gx_device_vector * vdev)
237
0
{
238
0
    vdev->black = gx_device_black((gx_device *)vdev);
239
0
    vdev->white = gx_device_white((gx_device *)vdev);
240
0
}
241
242
/* Initialize the state. */
243
void
244
gdev_vector_init(gx_device_vector * vdev)
245
0
{
246
0
    gdev_vector_reset(vdev);
247
0
    if (dev_proc(vdev, dev_spec_op) == gx_default_dev_spec_op)
248
0
        set_dev_proc(vdev, dev_spec_op, gdev_vector_dev_spec_op);
249
250
0
    vdev->scale.x = vdev->scale.y = 1.0;
251
0
    vdev->in_page = false;
252
0
    gdev_vector_load_cache(vdev);
253
0
}
254
255
/* Reset the remembered graphics state. */
256
void
257
gdev_vector_reset(gx_device_vector * vdev)
258
0
{
259
0
    static const gs_gstate state_initial =
260
0
    {gs_gstate_initial(1.0)};
261
262
0
    vdev->state = state_initial;
263
0
    gx_hld_saved_color_init(&vdev->saved_fill_color);
264
0
    gx_hld_saved_color_init(&vdev->saved_stroke_color);
265
0
    vdev->clip_path_id =
266
0
        vdev->no_clip_path_id = gs_next_ids(vdev->memory, 1);
267
0
}
268
269
/* Open the output file and stream. */
270
int
271
gdev_vector_open_file_options(gx_device_vector * vdev, uint strmbuf_size,
272
                              int open_options)
273
0
{
274
0
    bool binary = !(open_options & VECTOR_OPEN_FILE_ASCII);
275
0
    int code = -1;    /* (only for testing, never returned) */
276
0
    cmm_dev_profile_t *icc_struct = 0;
277
278
    /* Open the file as seekable or sequential, as requested. */
279
0
    if (!(open_options & VECTOR_OPEN_FILE_SEQUENTIAL)) {
280
        /* Try to open as seekable. */
281
0
        code =
282
0
            gx_device_open_output_file((gx_device *)vdev, vdev->fname,
283
0
                                       binary, true, &vdev->file);
284
0
    }
285
0
    if (code < 0 && (open_options & (VECTOR_OPEN_FILE_SEQUENTIAL |
286
0
                                     VECTOR_OPEN_FILE_SEQUENTIAL_OK))) {
287
        /* Try to open as sequential. */
288
0
        code = gx_device_open_output_file((gx_device *)vdev, vdev->fname,
289
0
                                          binary, false, &vdev->file);
290
0
    }
291
0
    if ((code >= 0) && (dev_proc(vdev, get_profile) != NULL)) {
292
0
        code = dev_proc(vdev, get_profile)((gx_device *)vdev, &icc_struct);
293
0
    }
294
295
0
    if (code < 0)
296
0
        return code;
297
0
    if ((vdev->strmbuf = gs_alloc_bytes(vdev->v_memory, strmbuf_size,
298
0
                                        "vector_open(strmbuf)")) == 0 ||
299
0
        (vdev->strm = s_alloc(vdev->v_memory,
300
0
                              "vector_open(strm)")) == 0 ||
301
0
        ((open_options & VECTOR_OPEN_FILE_BBOX) &&
302
0
         (vdev->bbox_device =
303
0
          gs_alloc_struct_immovable(vdev->v_memory,
304
0
                                    gx_device_bbox, &st_device_bbox,
305
0
                                    "vector_open(bbox_device)")) == 0)
306
0
        ) {
307
0
        if (vdev->bbox_device)
308
0
            gs_free_object(vdev->v_memory, vdev->bbox_device,
309
0
                           "vector_open(bbox_device)");
310
0
        vdev->bbox_device = 0;
311
0
        if (vdev->strm)
312
0
            gs_free_object(vdev->v_memory, vdev->strm,
313
0
                           "vector_open(strm)");
314
0
        vdev->strm = 0;
315
0
        if (vdev->strmbuf)
316
0
            gs_free_object(vdev->v_memory, vdev->strmbuf,
317
0
                           "vector_open(strmbuf)");
318
0
        vdev->strmbuf = 0;
319
0
        gx_device_close_output_file((gx_device *)vdev, vdev->fname, vdev->file);
320
0
        vdev->file = 0;
321
0
        return_error(gs_error_VMerror);
322
0
    }
323
0
    vdev->strmbuf_size = strmbuf_size;
324
0
    swrite_file(vdev->strm, vdev->file, vdev->strmbuf, strmbuf_size);
325
0
    vdev->open_options = open_options;
326
    /*
327
     * We don't want finalization to close the file, but we do want it
328
     * to flush the stream buffer.
329
     */
330
0
    vdev->strm->procs.close = vdev->strm->procs.flush;
331
0
    if (vdev->bbox_device) {
332
0
        gx_device_bbox_init(vdev->bbox_device, NULL, vdev->v_memory);
333
0
        rc_increment(vdev->bbox_device);
334
335
0
        vdev->bbox_device->icc_struct = icc_struct;
336
0
        rc_increment(vdev->bbox_device->icc_struct);
337
338
0
        gx_device_set_resolution((gx_device *) vdev->bbox_device,
339
0
                                 vdev->HWResolution[0],
340
0
                                 vdev->HWResolution[1]);
341
        /* Do the right thing about upright vs. inverted. */
342
        /* (This is dangerous in general, since the procedure */
343
        /* might reference non-standard elements.) */
344
0
        set_dev_proc(vdev->bbox_device, get_initial_matrix,
345
0
                     dev_proc(vdev, get_initial_matrix));
346
0
        (*dev_proc(vdev->bbox_device, open_device))
347
0
            ((gx_device *) vdev->bbox_device);
348
0
    }
349
350
0
    code = install_internal_subclass_devices((gx_device **)&vdev, NULL);
351
0
    if (code < 0)
352
0
        return code;
353
354
0
    return 0;
355
0
}
356
357
/* Get the current stream, calling beginpage if in_page is false. */
358
stream *
359
gdev_vector_stream(gx_device_vector * vdev)
360
0
{
361
0
    if (!vdev->in_page) {
362
0
        (*vdev_proc(vdev, beginpage)) (vdev);
363
0
        vdev->in_page = true;
364
0
    }
365
0
    return vdev->strm;
366
0
}
367
368
/* Update the logical operation. */
369
int
370
gdev_vector_update_log_op(gx_device_vector * vdev, gs_logical_operation_t lop)
371
0
{
372
0
    gs_logical_operation_t diff = lop ^ vdev->state.log_op;
373
374
0
    if (diff != 0) {
375
0
        int code = (*vdev_proc(vdev, setlogop)) (vdev, lop, diff);
376
377
0
        if (code < 0)
378
0
            return code;
379
0
        vdev->state.log_op = lop;
380
0
    }
381
0
    return 0;
382
0
}
383
384
/* Update color (fill or stroke). */
385
static int
386
gdev_vector_update_color(gx_device_vector * vdev,
387
                              const gs_gstate * pgs,
388
                              const gx_drawing_color * pdcolor,
389
                              gx_hl_saved_color *sc,
390
                              int (*setcolor) (gx_device_vector * vdev,
391
                                               const gs_gstate * pgs,
392
                                               const gx_drawing_color * pdc))
393
0
{
394
0
    gx_hl_saved_color temp;
395
0
    int code;
396
0
    bool hl_color = (*vdev_proc(vdev, can_handle_hl_color)) (vdev, pgs, pdcolor);
397
0
    const gs_gstate *pgs_for_hl_color = (hl_color ? pgs : NULL);
398
399
0
    gx_hld_save_color(pgs_for_hl_color, pdcolor, &temp);
400
0
    if (gx_hld_saved_color_equal(&temp, sc))
401
0
        return 0;
402
0
    code = (*setcolor) (vdev, pgs_for_hl_color, pdcolor);
403
0
    if (code < 0)
404
0
        return code;
405
0
    *sc = temp;
406
0
    return 0;
407
0
}
408
409
/* Update the fill color. */
410
int
411
gdev_vector_update_fill_color(gx_device_vector * vdev,
412
                              const gs_gstate * pgs,
413
                              const gx_drawing_color * pdcolor)
414
0
{
415
0
    return gdev_vector_update_color(vdev, pgs, pdcolor, &vdev->saved_fill_color,
416
0
                                    vdev_proc(vdev, setfillcolor));
417
0
}
418
419
/* Update the state for filling a region. */
420
static int
421
update_fill(gx_device_vector * vdev, const gs_gstate * pgs,
422
            const gx_drawing_color * pdcolor, gs_logical_operation_t lop)
423
0
{
424
0
    int code = gdev_vector_update_fill_color(vdev, pgs, pdcolor);
425
426
0
    if (code < 0)
427
0
        return code;
428
0
    return gdev_vector_update_log_op(vdev, lop);
429
0
}
430
431
/* Bring state up to date for filling. */
432
int
433
gdev_vector_prepare_fill(gx_device_vector * vdev, const gs_gstate * pgs,
434
            const gx_fill_params * params, const gx_drawing_color * pdcolor)
435
0
{
436
0
    if (params->flatness != vdev->state.flatness) {
437
0
        int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
438
439
0
        if (code < 0)
440
0
            return code;
441
0
        vdev->state.flatness = params->flatness;
442
0
    }
443
0
    return update_fill(vdev, pgs, pdcolor, pgs->log_op);
444
0
}
445
446
/* Compare two dash patterns. */
447
static bool
448
dash_pattern_eq(const float *stored, const gx_dash_params * set, double scale)
449
0
{
450
0
    int i;
451
452
0
    for (i = 0; i < set->pattern_size; ++i)
453
0
        if (stored[i] != (float)(set->pattern[i] * scale))
454
0
            return false;
455
0
    return true;
456
0
}
457
458
/* Bring state up to date for stroking. */
459
int
460
gdev_vector_prepare_stroke(gx_device_vector * vdev,
461
                           const gs_gstate * pgs, /* may be NULL */
462
                           const gx_stroke_params * params, /* may be NULL */
463
                           const gx_drawing_color * pdcolor, /* may be NULL */
464
                           double scale)
465
0
{
466
0
    if (pgs) {
467
0
        int pattern_size = pgs->line_params.dash.pattern_size;
468
0
        float dash_offset = pgs->line_params.dash.offset * scale;
469
0
        float half_width = pgs->line_params.half_width * scale;
470
471
0
        if (dash_offset != vdev->state.line_params.dash.offset ||
472
0
            pattern_size != vdev->state.line_params.dash.pattern_size ||
473
0
            (pattern_size != 0 &&
474
0
             !dash_pattern_eq(vdev->dash_pattern, &pgs->line_params.dash,
475
0
                              scale))
476
0
            ) {
477
0
            float *pattern;
478
0
            int i, code;
479
480
0
            pattern = (float *)gs_alloc_bytes(vdev->memory->stable_memory, pattern_size * sizeof(float), "vector allocate dash pattern");
481
0
            for (i = 0; i < pattern_size; ++i)
482
0
                pattern[i] = pgs->line_params.dash.pattern[i] * scale;
483
0
            code = (*vdev_proc(vdev, setdash))
484
0
                (vdev, pattern, pattern_size, dash_offset);
485
0
            if (code < 0)
486
0
                return code;
487
0
            if (vdev->dash_pattern)
488
0
                gs_free_object(vdev->memory->stable_memory, vdev->dash_pattern, "vector free old dash pattern");
489
0
            vdev->dash_pattern = pattern;
490
0
            vdev->dash_pattern_size = pattern_size;
491
492
0
            vdev->state.line_params.dash.pattern_size = pattern_size;
493
0
            vdev->state.line_params.dash.offset = dash_offset;
494
0
        }
495
0
        if (half_width != vdev->state.line_params.half_width) {
496
0
            int code = (*vdev_proc(vdev, setlinewidth))
497
0
                (vdev, half_width * 2);
498
499
0
            if (code < 0)
500
0
                return code;
501
0
            vdev->state.line_params.half_width = half_width;
502
0
        }
503
0
        if (pgs->line_params.miter_limit != vdev->state.line_params.miter_limit) {
504
0
            int code = (*vdev_proc(vdev, setmiterlimit))
505
0
                (vdev, pgs->line_params.miter_limit);
506
507
0
            if (code < 0)
508
0
                return code;
509
0
            gx_set_miter_limit(&vdev->state.line_params,
510
0
                               pgs->line_params.miter_limit);
511
0
        }
512
        /* FIXME: Should cope with end_cap and dash_cap too */
513
0
        if (pgs->line_params.start_cap != vdev->state.line_params.start_cap) {
514
0
            int code = (*vdev_proc(vdev, setlinecap))
515
0
                (vdev, pgs->line_params.start_cap);
516
517
0
            if (code < 0)
518
0
                return code;
519
0
            vdev->state.line_params.start_cap = pgs->line_params.start_cap;
520
0
        }
521
0
        if (pgs->line_params.join != vdev->state.line_params.join) {
522
0
            int code = (*vdev_proc(vdev, setlinejoin))
523
0
                (vdev, pgs->line_params.join);
524
525
0
            if (code < 0)
526
0
                return code;
527
0
            vdev->state.line_params.join = pgs->line_params.join;
528
0
        } {
529
0
            int code = gdev_vector_update_log_op(vdev, pgs->log_op);
530
531
0
            if (code < 0)
532
0
                return code;
533
0
        }
534
0
    }
535
0
    if (params) {
536
0
        if (params->flatness != vdev->state.flatness) {
537
0
            int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
538
539
0
            if (code < 0)
540
0
                return code;
541
0
            vdev->state.flatness = params->flatness;
542
0
        }
543
0
    }
544
0
    if (pdcolor) {
545
0
        int code = gdev_vector_update_color(vdev, pgs, pdcolor,
546
0
                    &vdev->saved_stroke_color, vdev_proc(vdev, setstrokecolor));
547
548
0
        if (code < 0)
549
0
            return code;
550
0
    }
551
0
    return 0;
552
0
}
553
554
/*
555
 * Compute the scale for transforming the line width and dash pattern for a
556
 * stroke operation, and, if necessary to handle anisotropic scaling, a full
557
 * transformation matrix to be inverse-applied to the path elements as well.
558
 * Return 0 if only scaling, 1 if a full matrix is needed.
559
 */
560
int
561
gdev_vector_stroke_scaling(const gx_device_vector *vdev,
562
                           const gs_gstate *pgs,
563
                           double *pscale, gs_matrix *pmat)
564
0
{
565
0
    bool set_ctm = true;
566
0
    double scale = 1;
567
568
    /*
569
     * If the CTM is not uniform, stroke width depends on angle.
570
     * We'd like to avoid resetting the CTM, so we check for uniform
571
     * CTMs explicitly.  Note that in PDF, unlike PostScript, it is
572
     * the CTM at the time of the stroke operation, not the CTM at
573
     * the time the path was constructed, that is used for transforming
574
     * the points of the path; so if we have to reset the CTM, we must
575
     * do it before constructing the path, and inverse-transform all
576
     * the coordinates.
577
     */
578
0
    if (is_xxyy(&pgs->ctm)) {
579
0
        scale = fabs(pgs->ctm.xx);
580
0
        set_ctm = fabs(pgs->ctm.yy) != scale;
581
0
    } else if (is_xyyx(&pgs->ctm)) {
582
0
        scale = fabs(pgs->ctm.xy);
583
0
        set_ctm = fabs(pgs->ctm.yx) != scale;
584
0
    } else if ((pgs->ctm.xx == pgs->ctm.yy && pgs->ctm.xy == -pgs->ctm.yx) ||
585
0
               (pgs->ctm.xx == -pgs->ctm.yy && pgs->ctm.xy == pgs->ctm.yx)
586
0
        ) {
587
0
        scale = hypot(pgs->ctm.xx, pgs->ctm.xy);
588
0
        set_ctm = false;
589
0
    }
590
0
    if (set_ctm) {
591
        /*
592
         * Adobe Acrobat Reader has limitations on the maximum user
593
         * coordinate value.  If we scale the matrix down too far, the
594
         * coordinates will get too big: limit the scale factor to prevent
595
         * this from happening.  (This does no harm for other output
596
         * formats.)
597
         */
598
0
        double
599
0
            mxx = pgs->ctm.xx / vdev->scale.x,
600
0
            mxy = pgs->ctm.xy / vdev->scale.y,
601
0
            myx = pgs->ctm.yx / vdev->scale.x,
602
0
            myy = pgs->ctm.yy / vdev->scale.y;
603
604
0
        scale = 0.5 * (fabs(mxx) + fabs(mxy) + fabs(myx) + fabs(myy));
605
0
        pmat->xx = mxx / scale, pmat->xy = mxy / scale;
606
0
        pmat->yx = myx / scale, pmat->yy = myy / scale;
607
0
        pmat->tx = pmat->ty = 0;
608
0
    }
609
0
    *pscale = scale;
610
0
    return (int)set_ctm;
611
0
}
612
613
/* Initialize for writing a path using the default implementation. */
614
void
615
gdev_vector_dopath_init(gdev_vector_dopath_state_t *state,
616
                        gx_device_vector *vdev, gx_path_type_t type,
617
                        const gs_matrix *pmat)
618
0
{
619
0
    state->vdev = vdev;
620
0
    state->type = type;
621
0
    if (pmat) {
622
0
        state->scale_mat = *pmat;
623
        /*
624
         * The path element writing procedures all divide the coordinates
625
         * by the scale, so we must compensate for that here.
626
         */
627
0
        gs_matrix_scale(&state->scale_mat, 1.0 / vdev->scale.x,
628
0
                        1.0 / vdev->scale.y, &state->scale_mat);
629
0
    } else {
630
0
        gs_make_scaling(vdev->scale.x, vdev->scale.y, &state->scale_mat);
631
0
    }
632
0
    state->first = true;
633
634
    /* This is purely to prevent Coverity from thinking gdev_vector_dopath()
635
    could use uninitialised state->start.x. */
636
0
    state->start.x = 0;
637
0
    state->start.y = 0;
638
0
}
639
640
/*
641
 * Put a segment of an enumerated path on the output file.
642
 * pe_op is assumed to be valid and non-zero.
643
 */
644
int
645
gdev_vector_dopath_segment(gdev_vector_dopath_state_t *state, int pe_op,
646
                           gs_fixed_point vs[3])
647
0
{
648
0
    gx_device_vector *vdev = state->vdev;
649
0
    const gs_matrix *const pmat = &state->scale_mat;
650
0
    gs_point vp[3];
651
0
    int code;
652
653
0
    switch (pe_op) {
654
0
        case gs_pe_moveto:
655
0
            code = gs_point_transform_inverse(fixed2float(vs[0].x),
656
0
                                       fixed2float(vs[0].y), pmat, &vp[0]);
657
0
            if (code < 0)
658
0
                return code;
659
0
            if (state->first)
660
0
                state->start = vp[0], state->first = false;
661
0
            code = vdev_proc(vdev, moveto)
662
0
                (vdev, 0/*unused*/, 0/*unused*/, vp[0].x, vp[0].y,
663
0
                 state->type);
664
0
            state->prev = vp[0];
665
0
            break;
666
0
        case gs_pe_lineto:
667
0
        case gs_pe_gapto: /* FIXME */
668
0
            code = gs_point_transform_inverse(fixed2float(vs[0].x),
669
0
                                       fixed2float(vs[0].y), pmat, &vp[0]);
670
0
            if (code < 0)
671
0
                return code;
672
0
            code = vdev_proc(vdev, lineto)
673
0
                (vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
674
0
                 state->type);
675
0
            state->prev = vp[0];
676
0
            break;
677
0
        case gs_pe_curveto:
678
0
            code = gs_point_transform_inverse(fixed2float(vs[0].x),
679
0
                                       fixed2float(vs[0].y), pmat, &vp[0]);
680
0
            if (code < 0)
681
0
                return code;
682
0
            code = gs_point_transform_inverse(fixed2float(vs[1].x),
683
0
                                       fixed2float(vs[1].y), pmat, &vp[1]);
684
0
            if (code < 0)
685
0
                return code;
686
0
            gs_point_transform_inverse(fixed2float(vs[2].x),
687
0
                                       fixed2float(vs[2].y), pmat, &vp[2]);
688
0
            code = vdev_proc(vdev, curveto)
689
0
                (vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
690
0
                 vp[1].x, vp[1].y, vp[2].x, vp[2].y, state->type);
691
0
            state->prev = vp[2];
692
0
            break;
693
0
        case gs_pe_closepath:
694
0
            code = vdev_proc(vdev, closepath)
695
0
                (vdev, state->prev.x, state->prev.y, state->start.x,
696
0
                 state->start.y, state->type);
697
0
            state->prev = state->start;
698
0
            break;
699
0
        default:    /* can't happen */
700
0
            return -1;
701
0
    }
702
0
    return code;
703
0
}
704
705
/* Write a polygon as part of a path. */
706
/* May call beginpath, moveto, lineto, closepath, endpath. */
707
int
708
gdev_vector_write_polygon(gx_device_vector * vdev, const gs_fixed_point * points,
709
                          uint count, bool close, gx_path_type_t type)
710
0
{
711
0
    int code = 0;
712
713
0
    if (type != gx_path_type_none &&
714
0
        (code = (*vdev_proc(vdev, beginpath)) (vdev, type)) < 0
715
0
        )
716
0
        return code;
717
0
    if (count > 0) {
718
0
        double x = fixed2float(points[0].x) / vdev->scale.x, y = fixed2float(points[0].y) / vdev->scale.y;
719
0
        double x_start = x, y_start = y, x_prev, y_prev;
720
0
        uint i;
721
722
0
        code = (*vdev_proc(vdev, moveto))
723
0
            (vdev, 0.0, 0.0, x, y, type);
724
0
        if (code >= 0)
725
0
            for (i = 1; i < count && code >= 0; ++i) {
726
0
                x_prev = x, y_prev = y;
727
0
                code = (*vdev_proc(vdev, lineto))
728
0
                    (vdev, x_prev, y_prev,
729
0
                     (x = fixed2float(points[i].x) / vdev->scale.x),
730
0
                     (y = fixed2float(points[i].y) / vdev->scale.y),
731
0
                     type);
732
0
            }
733
0
        if (code >= 0 && close)
734
0
            code = (*vdev_proc(vdev, closepath))
735
0
                (vdev, x, y, x_start, y_start, type);
736
0
    }
737
0
    return (code >= 0 && type != gx_path_type_none ?
738
0
            (*vdev_proc(vdev, endpath)) (vdev, type) : code);
739
0
}
740
741
/* Write a rectangle as part of a path. */
742
/* May call moveto, lineto, closepath. */
743
int
744
gdev_vector_write_rectangle(gx_device_vector * vdev, fixed x0, fixed y0,
745
              fixed x1, fixed y1, bool close, gx_rect_direction_t direction)
746
0
{
747
0
    gs_fixed_point points[4];
748
749
0
    points[0].x = x0, points[0].y = y0;
750
0
    points[2].x = x1, points[2].y = y1;
751
0
    if (direction == gx_rect_x_first)
752
0
        points[1].x = x1, points[1].y = y0,
753
0
            points[3].x = x0, points[3].y = y1;
754
0
    else
755
0
        points[1].x = x0, points[1].y = y1,
756
0
            points[3].x = x1, points[3].y = y0;
757
0
    return gdev_vector_write_polygon(vdev, points, 4, close,
758
0
                                     gx_path_type_none);
759
0
}
760
761
/* Write a clipping path by calling the path procedures. */
762
int
763
gdev_vector_write_clip_path(gx_device_vector * vdev,
764
                            const gx_clip_path * pcpath)
765
0
{
766
0
    const gx_clip_rect *prect;
767
0
    gx_clip_rect page_rect;
768
0
    int code;
769
770
0
    if (pcpath == 0) {
771
        /* There's no special provision for initclip. */
772
        /* Write a rectangle that covers the entire page. */
773
0
        page_rect.xmin = page_rect.ymin = 0;
774
0
        page_rect.xmax = vdev->width;
775
0
        page_rect.ymax = vdev->height;
776
0
        page_rect.next = 0;
777
0
        prect = &page_rect;
778
0
    } else if (pcpath->path_valid) {
779
0
        return (*vdev_proc(vdev, dopath))
780
0
            (vdev, &pcpath->path,
781
0
             (pcpath->rule <= 0 ?
782
0
              gx_path_type_clip | gx_path_type_winding_number :
783
0
              gx_path_type_clip | gx_path_type_even_odd),
784
0
             NULL);
785
0
    } else {
786
0
        const gx_clip_list *list = gx_cpath_list(pcpath);
787
788
0
        prect = list->head;
789
0
        if (prect == 0)
790
0
            prect = &list->single;
791
0
    }
792
    /* Write out the rectangles. */
793
0
    code = (*vdev_proc(vdev, beginpath)) (vdev, gx_path_type_clip);
794
0
    for (; code >= 0 && prect != 0; prect = prect->next)
795
0
        if (prect->xmax > prect->xmin && prect->ymax > prect->ymin)
796
0
            code = gdev_vector_write_rectangle
797
0
                (vdev, int2fixed(prect->xmin), int2fixed(prect->ymin),
798
0
                 int2fixed(prect->xmax), int2fixed(prect->ymax),
799
0
                 false, gx_rect_x_first);
800
0
    if (code >= 0)
801
0
        code = (*vdev_proc(vdev, endpath)) (vdev, gx_path_type_clip);
802
0
    return code;
803
0
}
804
805
/* Update the clipping path if needed. */
806
int
807
gdev_vector_update_clip_path(gx_device_vector * vdev,
808
                             const gx_clip_path * pcpath)
809
0
{
810
0
    if (pcpath) {
811
0
        if (pcpath->id != vdev->clip_path_id) {
812
0
            int code = gdev_vector_write_clip_path(vdev, pcpath);
813
814
0
            if (code < 0)
815
0
                return code;
816
0
            vdev->clip_path_id = pcpath->id;
817
0
        }
818
0
    } else {
819
0
        if (vdev->clip_path_id != vdev->no_clip_path_id) {
820
0
            int code = gdev_vector_write_clip_path(vdev, NULL);
821
822
0
            if (code < 0)
823
0
                return code;
824
0
            vdev->clip_path_id = vdev->no_clip_path_id;
825
0
        }
826
0
    }
827
0
    return 0;
828
0
}
829
830
/* Close the output file and stream. */
831
int
832
gdev_vector_close_file(gx_device_vector * vdev)
833
0
{
834
0
    gp_file *f = vdev->file;
835
0
    int err;
836
837
0
    if (vdev->dash_pattern) {
838
0
        gs_free_object(vdev->memory->stable_memory, vdev->dash_pattern, "vector free dash pattern");
839
0
        vdev->dash_pattern = 0;
840
0
    }
841
0
    if (vdev->bbox_device) {
842
0
        rc_decrement(vdev->bbox_device->icc_struct, "vector_close(bbox_device->icc_struct");
843
0
        vdev->bbox_device->icc_struct = NULL;
844
0
        gs_free_object(vdev->v_memory, vdev->bbox_device,
845
0
                   "vector_close(bbox_device)");
846
0
        vdev->bbox_device = 0;
847
0
    }
848
849
0
    if (vdev->strm) {
850
0
        sclose(vdev->strm);
851
0
        gs_free_object(vdev->v_memory, vdev->strm, "vector_close(strm)");
852
0
        vdev->strm = 0;
853
0
        gs_free_object(vdev->v_memory, vdev->strmbuf, "vector_close(strmbuf)");
854
0
        vdev->strmbuf = 0;
855
0
    }
856
0
    vdev->file = 0;
857
0
    if (f) {
858
0
        err = gp_ferror(f);
859
        /* We prevented sclose from closing the file. */
860
0
        if (gx_device_close_output_file((gx_device *)vdev, vdev->fname, f) != 0
861
0
                || err != 0)
862
0
            return_error(gs_error_ioerror);
863
0
    }
864
0
    return 0;
865
0
}
866
867
/* ---------------- Image enumeration ---------------- */
868
869
/* Initialize for enumerating an image. */
870
int
871
gdev_vector_begin_image(gx_device_vector * vdev,
872
                        const gs_gstate * pgs, const gs_image_t * pim,
873
                        gs_image_format_t format, const gs_int_rect * prect,
874
              const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
875
                    gs_memory_t * mem, const gx_image_enum_procs_t * pprocs,
876
                        gdev_vector_image_enum_t * pie)
877
0
{
878
0
    const gs_color_space *pcs = pim->ColorSpace;
879
0
    int num_components;
880
0
    int bits_per_pixel;
881
0
    int code;
882
883
0
    if (pim->ImageMask)
884
0
        bits_per_pixel = num_components = 1;
885
0
    else
886
0
        num_components = gs_color_space_num_components(pcs),
887
0
            bits_per_pixel = pim->BitsPerComponent;
888
0
    code = gx_image_enum_common_init((gx_image_enum_common_t *) pie,
889
0
                                     (const gs_data_image_t *)pim,
890
0
                                     pprocs, (gx_device *) vdev,
891
0
                                     num_components, format);
892
0
    if (code < 0)
893
0
        return code;
894
0
    pie->bits_per_pixel = bits_per_pixel * num_components /
895
0
        pie->num_planes;
896
0
    pie->default_info = 0;
897
0
    pie->bbox_info = 0;
898
0
    if ((code = gdev_vector_update_log_op(vdev, pgs->log_op)) < 0 ||
899
0
        (code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
900
0
        ((pim->ImageMask ||
901
0
          (pim->CombineWithColor && rop3_uses_T(pgs->log_op))) &&
902
0
         (code = gdev_vector_update_fill_color(vdev, pgs, pdcolor)) < 0) ||
903
0
        (vdev->bbox_device &&
904
0
         (code = (*dev_proc(vdev->bbox_device, begin_image))
905
0
          ((gx_device *) vdev->bbox_device, pgs, pim, format, prect,
906
0
           pdcolor, pcpath, mem, &pie->bbox_info)) < 0)
907
0
        )
908
0
        return code;
909
0
    pie->memory = mem;
910
0
    if (prect)
911
0
        pie->width = prect->q.x - prect->p.x,
912
0
            pie->height = prect->q.y - prect->p.y;
913
0
    else
914
0
        pie->width = pim->Width, pie->height = pim->Height;
915
0
    pie->bits_per_row = pie->width * pie->bits_per_pixel;
916
0
    pie->y = 0;
917
0
    return 0;
918
0
}
919
920
/* End an image, optionally supplying any necessary blank padding rows. */
921
/* Return 0 if we used the default implementation, 1 if not. */
922
int
923
gdev_vector_end_image(gx_device_vector * vdev,
924
         gdev_vector_image_enum_t * pie, bool draw_last, gx_color_index pad)
925
0
{
926
0
    int code;
927
928
0
    if (pie->default_info) {
929
0
        code = gx_default_end_image((gx_device *) vdev, pie->default_info,
930
0
                                    draw_last);
931
0
        if (code >= 0)
932
0
            code = 0;
933
0
    } else {     /* Fill out to the full image height. */
934
0
        if (pie->y < pie->height && pad != gx_no_color_index) {
935
0
            uint bytes_per_row = (pie->bits_per_row + 7) >> 3;
936
0
            byte *row = gs_alloc_bytes(pie->memory, bytes_per_row,
937
0
                                       "gdev_vector_end_image(fill)");
938
939
0
            if (row == 0)
940
0
                return_error(gs_error_VMerror);
941
/****** FILL VALUE IS WRONG ******/
942
0
            memset(row, (byte) pad, bytes_per_row);
943
0
            for (; pie->y < pie->height; pie->y++)
944
0
                gx_image_data((gx_image_enum_common_t *) pie,
945
0
                              (const byte **)&row, 0,
946
0
                              bytes_per_row, 1);
947
0
            gs_free_object(pie->memory, row,
948
0
                           "gdev_vector_end_image(fill)");
949
0
        }
950
0
        code = 1;
951
0
    }
952
0
    if (vdev->bbox_device) {
953
0
        int bcode = gx_image_end(pie->bbox_info, draw_last);
954
955
0
        if (bcode < 0)
956
0
            code = bcode;
957
0
    }
958
0
    gx_image_free_enum((gx_image_enum_common_t **)&pie);
959
0
    return code;
960
0
}
961
962
/* ================ Device procedures ================ */
963
964
0
#define vdev ((gx_device_vector *)dev)
965
966
int gdev_vector_get_param(gx_device *dev, char *Param, void *list)
967
0
{
968
0
    gs_param_list * plist = (gs_param_list *)list;
969
0
    gs_param_string ofns;
970
0
    bool bool_true = 1;
971
972
0
    ofns.data = (const byte *)vdev->fname,
973
0
        ofns.size = strlen(vdev->fname),
974
0
        ofns.persistent = false;
975
0
    if (strcmp(Param, "OutputFile") == 0) {
976
0
        return param_write_string(plist, "OutputFile", &ofns);
977
0
    }
978
0
    if (strcmp(Param, "HighLevelDevice") == 0) {
979
0
        return param_write_bool(plist, "HighLevelDevice", &bool_true);
980
0
    }
981
0
    if (strcmp(Param, "NoInterpolateImagemasks") == 0) {
982
0
        return param_write_bool(plist, "NoInterpolateImagemasks", &bool_true);
983
0
    }
984
0
    return gx_default_get_param(dev, Param, list);
985
0
}
986
987
/* Get parameters. */
988
int
989
gdev_vector_get_params(gx_device * dev, gs_param_list * plist)
990
0
{
991
0
    int code = gx_default_get_params(dev, plist);
992
0
    int ecode;
993
0
    gs_param_string ofns;
994
0
    bool bool_true = 1;
995
996
0
    if (code < 0)
997
0
        return code;
998
0
    ofns.data = (const byte *)vdev->fname,
999
0
        ofns.size = strlen(vdev->fname),
1000
0
        ofns.persistent = false;
1001
0
    if ((ecode = param_write_string(plist, "OutputFile", &ofns)) < 0)
1002
0
        return ecode;
1003
0
    if ((ecode = param_write_bool(plist, "HighLevelDevice", &bool_true)) < 0)
1004
0
        return ecode;
1005
0
    if ((ecode = param_write_bool(plist, "NoInterpolateImagemasks", &bool_true)) < 0)
1006
0
        return ecode;
1007
0
    return code;
1008
0
}
1009
1010
/* Put parameters. */
1011
int
1012
gdev_vector_put_params(gx_device * dev, gs_param_list * plist)
1013
434
{
1014
434
    int ecode = 0;
1015
434
    int code;
1016
434
    int igni;
1017
434
    bool ignb;
1018
434
    gs_param_name param_name;
1019
434
    gs_param_string ofns;
1020
434
    bool open = dev->is_open, HighLevelDevice, NoInterpolateImagemasks;
1021
1022
434
    code = param_read_bool(plist, (param_name = "HighLevelDevice"), &HighLevelDevice);
1023
434
    if (code < 0)
1024
0
        return code;
1025
1026
434
    code = param_read_bool(plist, (param_name = "NoInterpolateImagemasks"), &NoInterpolateImagemasks);
1027
434
    if (code < 0)
1028
0
        return code;
1029
1030
434
    switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofns)) {
1031
0
        case 0:
1032
            /*
1033
             * Vector devices typically write header information at the
1034
             * beginning of the file: changing the file name after writing
1035
             * any pages should be an error.
1036
             */
1037
0
            if (ofns.size > fname_size) {
1038
0
                eprintf1("\nERROR: Output filename too long (maximum %d bytes).\n", fname_size);
1039
0
                ecode = gs_error_limitcheck;
1040
0
            }
1041
0
            else if (!bytes_compare(ofns.data, ofns.size,
1042
0
                                    (const byte *)vdev->fname,
1043
0
                                    strlen(vdev->fname))
1044
0
                     ) {
1045
                /* The new name is the same as the old name.  Do nothing. */
1046
0
                ofns.data = 0;
1047
0
                break;
1048
0
            } else if (dev->LockSafetyParams) {
1049
0
                    ecode = gs_error_invalidaccess;
1050
0
                    goto ofe;
1051
0
            }
1052
0
            break;
1053
0
        default:
1054
0
            ecode = code;
1055
0
ofe:        param_signal_error(plist, param_name, ecode);
1056
            /* fall through */
1057
434
        case 1:
1058
434
            ofns.data = 0;
1059
434
            break;
1060
434
    }
1061
    /* Ignore the following printer device params */
1062
434
    switch (code = param_read_bool(plist, (param_name = "BGPrint"), &ignb)) {
1063
0
        default:
1064
0
          ecode = code;
1065
0
          param_signal_error(plist, param_name, ecode);
1066
0
        case 0:
1067
434
        case 1:
1068
434
          break;
1069
434
    }
1070
434
    switch (code = param_read_int(plist, (param_name = "NumRenderingThreads"), &igni)) {
1071
0
        default:
1072
0
          ecode = code;
1073
0
          param_signal_error(plist, param_name, ecode);
1074
0
        case 0:
1075
434
        case 1:
1076
434
          break;
1077
434
    }
1078
1079
434
    if (ecode < 0)
1080
0
        return ecode;
1081
1082
434
    {
1083
        /* Don't let gx_default_put_params close the device. */
1084
434
        dev->is_open = false;
1085
434
        code = gx_default_put_params(dev, plist);
1086
434
        dev->is_open = open;
1087
434
    }
1088
434
    if (code < 0)
1089
0
        return code;
1090
1091
434
    if (dev->color_info.anti_alias.text_bits != 1 || dev->color_info.anti_alias.graphics_bits != 1) {
1092
0
        emprintf(dev->memory,
1093
0
            "\n\n  ERROR:\n    Can't set GraphicsAlphaBits or TextAlphaBits with a vector device.\n");
1094
0
        return_error(gs_error_unregistered);
1095
0
    }
1096
1097
434
    if (ofns.data != 0) {
1098
        /* If ofns.data is not NULL, then we have a different file name */
1099
0
        memcpy(vdev->fname, ofns.data, ofns.size);
1100
0
        vdev->fname[ofns.size] = 0;
1101
0
        if (dev->is_open && vdev->strm != 0 && stell(vdev->strm) != 0) {
1102
            /* we want to close and re-open the device so we can change the file */
1103
0
            ecode = gs_closedevice(dev);
1104
0
            if (ecode < 0) {
1105
0
                param_signal_error(plist, param_name, ecode);
1106
0
                return ecode;    /* THIS MAY CAUSE PROBLEMS SINCE THE DEVICE MAY BE CLOSED */
1107
0
            }
1108
0
            if (vdev->file != 0) {
1109
0
                gx_device_bbox *bbdev = vdev->bbox_device;
1110
1111
0
                vdev->bbox_device = 0; /* don't let it be freed */
1112
0
                code = gdev_vector_close_file(vdev);
1113
0
                vdev->bbox_device = bbdev;
1114
0
                if (code < 0)
1115
0
                    return code;
1116
0
            }
1117
0
            ecode = gs_opendevice(dev);    /* opendevice is expected to open the new file */
1118
0
            if (ecode < 0) {
1119
0
                param_signal_error(plist, param_name, ecode);
1120
0
                return ecode;
1121
0
            }
1122
0
        }
1123
        /* device is open and hasn't written data yet, so open the file */
1124
0
        else if (dev->is_open) {
1125
0
            return gdev_vector_open_file_options(vdev, vdev->strmbuf_size,
1126
0
                              vdev->open_options);
1127
0
        }
1128
0
    }
1129
434
    return 0;
1130
434
}
1131
1132
/* ---------------- Defaults ---------------- */
1133
1134
int
1135
gdev_vector_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
1136
                           gx_color_index color)
1137
0
{
1138
0
    gx_drawing_color dcolor;
1139
1140
    /* Ignore the initial fill with white. */
1141
0
    if (!vdev->in_page && color == vdev->white)
1142
0
        return 0;
1143
    /*
1144
     * The original colorspace and client color are unknown so use
1145
     * set_nonclient_dev_color instead of color_set_pure.
1146
     */
1147
0
    set_nonclient_dev_color(&dcolor, color);
1148
0
    {
1149
        /* Make sure we aren't being clipped. */
1150
0
        int code = gdev_vector_update_clip_path(vdev, NULL);
1151
1152
0
        if (code < 0)
1153
0
            return code;
1154
0
        if ((code = update_fill(vdev, NULL, &dcolor, rop3_T)) < 0)
1155
0
            return code;
1156
0
    }
1157
0
    if (vdev->bbox_device) {
1158
0
        int code = (*dev_proc(vdev->bbox_device, fill_rectangle))
1159
0
        ((gx_device *) vdev->bbox_device, x, y, w, h, color);
1160
1161
0
        if (code < 0)
1162
0
            return code;
1163
0
    }
1164
0
    return (*vdev_proc(vdev, dorect)) (vdev, int2fixed(x), int2fixed(y),
1165
0
                                       int2fixed(x + w), int2fixed(y + h),
1166
0
                                       gx_path_type_fill);
1167
0
}
1168
1169
int
1170
gdev_vector_fill_path(gx_device * dev, const gs_gstate * pgs,
1171
                      gx_path * ppath, const gx_fill_params * params,
1172
                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
1173
0
{
1174
0
    int code;
1175
1176
0
    if ((code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
1177
0
        (code = gdev_vector_prepare_fill(vdev, pgs, params, pdevc)) < 0 ||
1178
0
        (vdev->bbox_device &&
1179
0
         (code = (*dev_proc(vdev->bbox_device, fill_path))
1180
0
          ((gx_device *) vdev->bbox_device, pgs, ppath, params,
1181
0
           pdevc, pcpath)) < 0) ||
1182
0
        (code = (*vdev_proc(vdev, dopath))
1183
0
         (vdev, ppath,
1184
0
          (params->rule > 0 ? gx_path_type_even_odd :
1185
0
           gx_path_type_winding_number) | gx_path_type_fill |
1186
0
           vdev->fill_options,
1187
0
         NULL)) < 0
1188
0
        )
1189
0
        return gx_default_fill_path(dev, pgs, ppath, params, pdevc, pcpath);
1190
0
    return code;
1191
0
}
1192
1193
int
1194
gdev_vector_stroke_path(gx_device * dev, const gs_gstate * pgs,
1195
                        gx_path * ppath, const gx_stroke_params * params,
1196
              const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
1197
0
{
1198
0
    int code;
1199
0
    double scale;
1200
0
    int set_ctm;
1201
0
    gs_matrix mat;
1202
1203
0
    if ((code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
1204
0
        (set_ctm = gdev_vector_stroke_scaling(vdev, pgs, &scale, &mat)) != 0 ||
1205
0
        (code = gdev_vector_prepare_stroke(vdev, pgs, params, pdcolor, scale)) < 0 ||
1206
0
        (vdev->bbox_device &&
1207
0
         (code = (*dev_proc(vdev->bbox_device, stroke_path))
1208
0
          ((gx_device *) vdev->bbox_device, pgs, ppath, params,
1209
0
           pdcolor, pcpath)) < 0) ||
1210
0
        (code = (*vdev_proc(vdev, dopath))
1211
0
         (vdev, ppath, gx_path_type_stroke | vdev->stroke_options, NULL)) < 0
1212
0
        )
1213
0
        return gx_default_stroke_path(dev, pgs, ppath, params, pdcolor, pcpath);
1214
0
    return code;
1215
0
}
1216
1217
int
1218
gdev_vector_fill_trapezoid(gx_device * dev, const gs_fixed_edge * left,
1219
        const gs_fixed_edge * right, fixed ybot, fixed ytop, bool swap_axes,
1220
                  const gx_device_color * pdevc, gs_logical_operation_t lop)
1221
0
{
1222
0
    fixed xl = left->start.x;
1223
0
    fixed wl = left->end.x - xl;
1224
0
    fixed yl = left->start.y;
1225
0
    fixed hl = left->end.y - yl;
1226
0
    fixed xr = right->start.x;
1227
0
    fixed wr = right->end.x - xr;
1228
0
    fixed yr = right->start.y;
1229
0
    fixed hr = right->end.y - yr;
1230
0
    fixed x0l = xl + fixed_mult_quo(wl, ybot - yl, hl);
1231
0
    fixed x1l = xl + fixed_mult_quo(wl, ytop - yl, hl);
1232
0
    fixed x0r = xr + fixed_mult_quo(wr, ybot - yr, hr);
1233
0
    fixed x1r = xr + fixed_mult_quo(wr, ytop - yr, hr);
1234
1235
0
#define y0 ybot
1236
0
#define y1 ytop
1237
0
    int code = update_fill(vdev, NULL, pdevc, lop);
1238
0
    gs_fixed_point points[4];
1239
1240
0
    if (code < 0)
1241
0
        return gx_default_fill_trapezoid(dev, left, right, ybot, ytop,
1242
0
                                         swap_axes, pdevc, lop);
1243
    /* Make sure we aren't being clipped. */
1244
0
    code = gdev_vector_update_clip_path(vdev, NULL);
1245
0
    if (code < 0)
1246
0
        return code;
1247
0
    if (swap_axes)
1248
0
        points[0].y = x0l, points[1].y = x0r,
1249
0
            points[0].x = points[1].x = y0,
1250
0
            points[2].y = x1r, points[3].y = x1l,
1251
0
            points[2].x = points[3].x = y1;
1252
0
    else
1253
0
        points[0].x = x0l, points[1].x = x0r,
1254
0
            points[0].y = points[1].y = y0,
1255
0
            points[2].x = x1r, points[3].x = x1l,
1256
0
            points[2].y = points[3].y = y1;
1257
0
#undef y0
1258
0
#undef y1
1259
0
    if (vdev->bbox_device) {
1260
0
        int code = (*dev_proc(vdev->bbox_device, fill_trapezoid))
1261
0
        ((gx_device *) vdev->bbox_device, left, right, ybot, ytop,
1262
0
         swap_axes, pdevc, lop);
1263
1264
0
        if (code < 0)
1265
0
            return code;
1266
0
    }
1267
0
    return gdev_vector_write_polygon(vdev, points, 4, true,
1268
0
                                     gx_path_type_fill);
1269
0
}
1270
1271
int
1272
gdev_vector_fill_parallelogram(gx_device * dev,
1273
                 fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
1274
                  const gx_device_color * pdevc, gs_logical_operation_t lop)
1275
0
{
1276
0
    fixed pax = px + ax, pay = py + ay;
1277
0
    int code = update_fill(vdev, NULL, pdevc, lop);
1278
0
    gs_fixed_point points[4];
1279
0
    bool need_color_reset = false;
1280
1281
0
    if (code < 0)
1282
0
        return gx_default_fill_parallelogram(dev, px, py, ax, ay, bx, by,
1283
0
                                             pdevc, lop);
1284
    /* Make sure we aren't being clipped. */
1285
0
    if (vdev->clip_path_id != vdev->no_clip_path_id)
1286
        /* There is a clip path, and when we emit it we will start
1287
         * by executing a grestore, which will overwrite the colour
1288
         * we set up above....
1289
         */
1290
0
        need_color_reset = true;
1291
1292
0
    code = gdev_vector_update_clip_path(vdev, NULL);
1293
0
    if (code < 0)
1294
0
        return code;
1295
1296
0
    if (need_color_reset) {
1297
0
        code = update_fill(vdev, NULL, pdevc, lop);
1298
0
        if (code < 0)
1299
0
            return code;
1300
0
    }
1301
0
    if (vdev->bbox_device) {
1302
0
        code = (*dev_proc(vdev->bbox_device, fill_parallelogram))
1303
0
            ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
1304
0
             pdevc, lop);
1305
0
        if (code < 0)
1306
0
            return code;
1307
0
    }
1308
0
    points[0].x = px, points[0].y = py;
1309
0
    points[1].x = pax, points[1].y = pay;
1310
0
    points[2].x = pax + bx, points[2].y = pay + by;
1311
0
    points[3].x = px + bx, points[3].y = py + by;
1312
0
    return gdev_vector_write_polygon(vdev, points, 4, true,
1313
0
                                     gx_path_type_fill);
1314
0
}
1315
1316
int
1317
gdev_vector_fill_triangle(gx_device * dev,
1318
                 fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
1319
                  const gx_device_color * pdevc, gs_logical_operation_t lop)
1320
0
{
1321
0
    int code = update_fill(vdev, NULL, pdevc, lop);
1322
0
    gs_fixed_point points[3];
1323
1324
0
    if (code < 0)
1325
0
        return gx_default_fill_triangle(dev, px, py, ax, ay, bx, by,
1326
0
                                        pdevc, lop);
1327
    /* Make sure we aren't being clipped. */
1328
0
    code = gdev_vector_update_clip_path(vdev, NULL);
1329
0
    if (code < 0)
1330
0
        return code;
1331
0
    if (vdev->bbox_device) {
1332
0
        code = (*dev_proc(vdev->bbox_device, fill_triangle))
1333
0
            ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
1334
0
             pdevc, lop);
1335
0
        if (code < 0)
1336
0
            return code;
1337
0
    }
1338
0
    points[0].x = px, points[0].y = py;
1339
0
    points[1].x = px + ax, points[1].y = py + ay;
1340
0
    points[2].x = px + bx, points[2].y = py + by;
1341
0
    return gdev_vector_write_polygon(vdev, points, 3, true,
1342
0
                                     gx_path_type_fill);
1343
0
}
1344
1345
int
1346
gdev_vector_dev_spec_op(gx_device *pdev, int dev_spec_op, void *data, int size)
1347
0
{
1348
0
    if (dev_spec_op == gxdso_get_dev_param) {
1349
0
        int code;
1350
0
        dev_param_req_t *request = (dev_param_req_t *)data;
1351
0
        code = gdev_vector_get_param(pdev, request->Param, request->list);
1352
0
        if (code != gs_error_undefined)
1353
0
            return code;
1354
0
    }
1355
0
    return gx_default_dev_spec_op(pdev, dev_spec_op, data, size);
1356
0
}
1357
1358
#undef vdev