Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/pcl/pxl/pxpaint.c
Line
Count
Source
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* pxpaint.c */
18
/* PCL XL painting operators */
19
20
#include "math_.h"
21
#include "stdio_.h"             /* std.h + NULL */
22
#include "pxoper.h"
23
#include "pxstate.h"
24
#include "pxfont.h"             /* for px_text */
25
#include "gsstate.h"
26
#include "gscoord.h"
27
#include "gspaint.h"
28
#include "gspath.h"
29
#include "gspath2.h"
30
#include "gsrop.h"
31
#include "gxfarith.h"
32
#include "gxfixed.h"
33
#include "gxgstate.h"
34
#include "gxmatrix.h"
35
#include "gxpath.h"
36
#include "pxptable.h"
37
#include "pxgstate.h" /* Prototype for px_high_level_pattern */
38
39
/*
40
 * The H-P documentation says we are supposed to draw rectangles
41
 * counter-clockwise on the page, which is clockwise in user space.
42
 * However, the LaserJet 6 (and probably the LJ 5 as well) draw rectangles
43
 * clockwise!  To draw rectangles clockwise, uncomment the following
44
 * #define.
45
 * clj4550 and clj4600 draw counter-clockwise rectangles
46
 */
47
/*#define DRAW_RECTANGLES_CLOCKWISE*/
48
/*
49
 * The H-P printers do really weird things for arcs, chords, or pies where
50
 * the width and/or height of the bounding box is negative.  To emulate
51
 * their behavior, uncomment the following #define.
52
 */
53
#define REFLECT_NEGATIVE_ARCS
54
55
/* Forward references */
56
px_operator_proc(pxNewPath);
57
58
/* ---------------- Utilities ---------------- */
59
60
/* Add lines to the path.  line_proc is gs_lineto or gs_rlineto. */
61
/* Attributes: pxaEndPoint, pxaNumberOfPoints, pxaPointType. */
62
static int
63
add_lines(px_args_t * par, px_state_t * pxs,
64
          int (*line_proc) (gs_gstate *, double, double))
65
14.6k
{
66
14.6k
    int code = 0;
67
68
14.6k
    if (par->pv[0]) {           /* Single segment, specified as argument. */
69
5.95k
        if (par->pv[1] || par->pv[2])
70
0
            return_error(errorIllegalAttributeCombination);
71
5.95k
        return (*line_proc) (pxs->pgs, real_value(par->pv[0], 0),
72
5.95k
                             real_value(par->pv[0], 1));
73
5.95k
    }
74
    /* Multiple segments, specified in source data. */
75
8.73k
    if (!(par->pv[1] && par->pv[2]))
76
2
        return_error(errorMissingAttribute);
77
8.73k
    {
78
8.73k
        int32_t num_points = par->pv[1]->value.i;
79
8.73k
        pxeDataType_t type = (pxeDataType_t) par->pv[2]->value.i;
80
8.73k
        int point_size = (type == eUByte || type == eSByte ? 2 : 4);
81
82
176k
        while (par->source.position < (ulong)num_points * point_size) {
83
172k
            const byte *dp = par->source.data;
84
172k
            int px, py;
85
86
172k
            if (par->source.available < point_size) {   /* We don't even have one point's worth of source data. */
87
4.49k
                return pxNeedData;
88
4.49k
            }
89
167k
            switch (type) {
90
0
                case eUByte:
91
0
                    px = dp[0];
92
0
                    py = dp[1];
93
0
                    break;
94
164k
                case eSByte:
95
164k
                    px = (int)(dp[0] ^ 0x80) - 0x80;
96
164k
                    py = (int)(dp[1] ^ 0x80) - 0x80;
97
164k
                    break;
98
0
                case eUInt16:
99
0
                    px = uint16at(dp, pxs->data_source_big_endian);
100
0
                    py = uint16at(dp + 2, pxs->data_source_big_endian);
101
0
                    break;
102
3.77k
                case eSInt16:
103
3.77k
                    px = sint16at(dp, pxs->data_source_big_endian);
104
3.77k
                    py = sint16at(dp + 2, pxs->data_source_big_endian);
105
3.77k
                    break;
106
0
                default:       /* can't happen, pacify compiler */
107
0
                    return_error(errorIllegalAttributeValue);
108
167k
            }
109
167k
            code = (*line_proc) (pxs->pgs, (double) px, (double) py);
110
167k
            if (code < 0)
111
0
                break;
112
167k
            par->source.position += point_size;
113
167k
            par->source.available -= point_size;
114
167k
            par->source.data += point_size;
115
167k
        }
116
8.73k
    }
117
4.24k
    return code;
118
8.73k
}
119
120
/* Add Bezier curves to the path.  curve_proc is gs_curveto or gs_rcurveto. */
121
/* Attributes: pxaNumberOfPoints, pxaPointType, pxaControlPoint1, */
122
/* pxaControlPoint2, pxaEndPoint. */
123
static int
124
add_curves(px_args_t * par, px_state_t * pxs,
125
           int (*curve_proc) (gs_gstate *, double, double, double, double,
126
                              double, double))
127
14.7k
{
128
14.7k
    int code = 0;
129
130
14.7k
    if (par->pv[2] && par->pv[3] && par->pv[4]) {       /* Single curve, specified as argument. */
131
0
        if (par->pv[0] || par->pv[1])
132
0
            return_error(errorIllegalAttributeCombination);
133
0
        return (*curve_proc) (pxs->pgs, real_value(par->pv[2], 0),
134
0
                              real_value(par->pv[2], 1),
135
0
                              real_value(par->pv[3], 0),
136
0
                              real_value(par->pv[3], 1),
137
0
                              real_value(par->pv[4], 0),
138
0
                              real_value(par->pv[4], 1));
139
0
    }
140
    /* Multiple segments, specified in source data. */
141
14.7k
    else if (par->pv[0] && par->pv[1]) {
142
14.7k
        if (par->pv[2] || par->pv[3] || par->pv[4])
143
1
            return_error(errorIllegalAttributeCombination);
144
14.7k
    } else
145
4
        return_error(errorMissingAttribute);
146
14.7k
    {
147
14.7k
        int32_t num_points = par->pv[0]->value.i;
148
14.7k
        pxeDataType_t type = (pxeDataType_t) par->pv[1]->value.i;
149
14.7k
        int point_size = (type == eUByte || type == eSByte ? 2 : 4);
150
14.7k
        int segment_size = point_size * 3;
151
152
14.7k
        if (num_points % 3)
153
1
            return_error(errorIllegalDataLength);
154
42.4k
        while (par->source.position < (ulong)num_points * point_size) {
155
35.1k
            const byte *dp = par->source.data;
156
35.1k
            int points[6];
157
35.1k
            int i;
158
159
35.1k
            if (par->source.available < point_size * 3) {       /* We don't even have one point's worth of source data. */
160
7.43k
                return pxNeedData;
161
7.43k
            }
162
27.6k
            switch (type) {
163
0
                case eUByte:
164
0
                    for (i = 0; i < 6; ++i)
165
0
                        points[i] = dp[i];
166
0
                    break;
167
4.10k
                case eSByte:
168
28.7k
                    for (i = 0; i < 6; ++i)
169
24.6k
                        points[i] = (int)(dp[i] ^ 0x80) - 0x80;
170
4.10k
                    break;
171
0
                case eUInt16:
172
0
                    for (i = 0; i < 12; i += 2)
173
0
                        points[i >> 1] =
174
0
                            uint16at(dp + i, pxs->data_source_big_endian);
175
0
                    break;
176
23.5k
                case eSInt16:
177
165k
                    for (i = 0; i < 12; i += 2)
178
141k
                        points[i >> 1]
179
141k
                            = sint16at(dp + i, pxs->data_source_big_endian);
180
23.5k
                    break;
181
0
                default:       /* can't happen, pacify compiler */
182
0
                    return_error(errorIllegalAttributeValue);
183
27.6k
            }
184
27.6k
            code = (*curve_proc) (pxs->pgs,
185
27.6k
                                  (double) points[0], (double) points[1],
186
27.6k
                                  (double) points[2], (double) points[3],
187
27.6k
                                  (double) points[4], (double) points[5]);
188
27.6k
            if (code < 0)
189
1
                break;
190
27.6k
            par->source.position += segment_size;
191
27.6k
            par->source.available -= segment_size;
192
27.6k
            par->source.data += segment_size;
193
27.6k
        }
194
14.7k
    }
195
7.29k
    return code;
196
14.7k
}
197
198
/*
199
 * Set up all the parameters for an arc, chord, ellipse, or pie.  If pp3 and
200
 * pp4 are NULL, we're filling the entire box.  Store the upper left box
201
 * corner (for repositioning the cursor), the center, the radius, and the
202
 * two angles in *params, and return one of the following (or a negative
203
 * error code):
204
 */
205
typedef enum
206
{
207
    /*
208
     * Arc box is degenerate (zero width and/or height).
209
     * Only origin and center have been set.
210
     */
211
    arc_degenerate = 0,
212
    /*
213
     * Arc box is square.  No CTM adjustment was required; save_ctm is
214
     * not set.
215
     */
216
    arc_square,
217
    /*
218
     * Arc box is rectangular, CTM has been saved and adjusted.
219
     */
220
    arc_rectangular
221
} px_arc_type_t;
222
223
typedef struct px_arc_params_s
224
{
225
    gs_point origin;
226
    gs_point center;
227
    double radius;
228
    double ang3, ang4;
229
    gs_matrix save_ctm;
230
    bool reversed;
231
} px_arc_params_t;
232
233
static int                      /* px_arc_type_t or error code */
234
setup_arc(px_arc_params_t * params, const px_value_t * pbox,
235
          const px_value_t * pp3, const px_value_t * pp4,
236
          const px_state_t * pxs, bool ellipse)
237
0
{
238
0
    real x1 = real_value(pbox, 0);
239
0
    real y1 = real_value(pbox, 1);
240
0
    real x2 = real_value(pbox, 2);
241
0
    real y2 = real_value(pbox, 3);
242
0
    real xc = (x1 + x2) * 0.5;
243
0
    real yc = (y1 + y2) * 0.5;
244
0
    real xr, yr;
245
0
    bool rotated;
246
0
    int code;
247
248
0
#ifdef REFLECT_NEGATIVE_ARCS
249
0
    rotated = x1 > x2;
250
0
    params->reversed = rotated ^ (y1 > y2);
251
#else
252
    rotated = false;
253
    params->reversed = false;
254
#endif
255
0
    if (x1 > x2) {
256
0
        real temp = x1;
257
258
0
        x1 = x2;
259
0
        x2 = temp;
260
0
    }
261
0
    if (y1 > y2) {
262
0
        real temp = y1;
263
264
0
        y1 = y2;
265
0
        y2 = temp;
266
0
    }
267
0
    params->origin.x = x1;
268
0
    params->origin.y = y1;
269
0
    xr = (x2 - x1) * 0.5;
270
0
    yr = (y2 - y1) * 0.5;
271
    /* From what we can gather ellipses are degenerate if both
272
       width and height of the bounding box are 0.  Other objects
273
       behave as expected.  A 0 area bounding box is degenerate */
274
0
    if (ellipse) {
275
        /* The bounding box is degenerate, set what we can and exit. */
276
0
        if (xr == 0 && yr == 0) {
277
0
            params->center.x = xc;
278
0
            params->center.y = yc;
279
0
            return arc_degenerate;
280
0
        }
281
0
    } else {
282
0
        if (xr == 0 || yr == 0) {
283
0
            params->center.x = xc;
284
0
            params->center.y = yc;
285
0
            return arc_degenerate;
286
0
        }
287
0
    }
288
289
0
    if (pp3 && pp4) {
290
0
        real dx3 = real_value(pp3, 0) - xc;
291
0
        real dy3 = real_value(pp3, 1) - yc;
292
0
        real dx4 = real_value(pp4, 0) - xc;
293
0
        real dy4 = real_value(pp4, 1) - yc;
294
295
0
        if ((dx3 == 0 && dy3 == 0) || (dx4 == 0 && dy4 == 0))
296
0
            return_error(errorIllegalAttributeValue);
297
0
        {
298
0
            double ang3 = atan2((double)(dy3 * xr),
299
0
                                (double)(dx3 * yr)) * radians_to_degrees;
300
0
            double ang4 = atan2((double)(dy4 * xr),
301
0
                                (double)(dx4 * yr)) * radians_to_degrees;
302
303
0
            if (rotated)
304
0
                ang3 += 180, ang4 += 180;
305
0
            params->ang3 = ang3;
306
0
            params->ang4 = ang4;
307
0
        }
308
0
    }
309
0
    params->radius = yr;
310
0
    if (xr == yr) {
311
0
        params->center.x = xc;
312
0
        params->center.y = yc;
313
0
        return arc_square;
314
0
    } else {                    /* Must adjust the CTM.  Move the origin to (xc,yc) */
315
        /* for simplicity. */
316
0
        if ((code = gs_currentmatrix(pxs->pgs, &params->save_ctm)) < 0 ||
317
0
            (code = gs_translate(pxs->pgs, xc, yc)) < 0 ||
318
0
            (code = gs_scale(pxs->pgs, xr, yr)) < 0)
319
0
            return code;
320
0
        params->center.x = 0;
321
0
        params->center.y = 0;
322
0
        params->radius = 1.0;
323
0
        return arc_rectangular;
324
0
    }
325
0
}
326
327
/* per the nonsense in 5.7.3 (The ROP3 Operands) from the pxl
328
   reference manual the following rops are allowed for stroking. */
329
static bool
330
pxl_allow_rop_for_stroke(gs_gstate * pgs)
331
0
{
332
0
    gs_rop3_t rop = gs_currentrasterop(pgs);
333
334
0
    if (rop == 0 || rop == 160 || rop == 170 || rop == 240 || rop == 250
335
0
        || rop == 255)
336
0
        return true;
337
0
    return false;
338
0
}
339
340
/* Paint (stroke and/or fill) the current path. */
341
static int
342
paint_path(px_state_t * pxs)
343
7.92k
{
344
7.92k
    gs_gstate *pgs = pxs->pgs;
345
7.92k
    gx_path *ppath = gx_current_path(pgs);
346
7.92k
    px_gstate_t *pxgs = pxs->pxgs;
347
7.92k
    bool will_stroke = pxgs->pen.type != pxpNull;
348
7.92k
    bool will_fill = pxgs->brush.type != pxpNull;
349
350
7.92k
    int code = 0;
351
    /* nothing to do. */
352
7.92k
    if (!will_fill && !will_stroke)
353
0
        return 0;
354
355
7.92k
    if (gx_path_is_void(ppath))
356
0
        return 0;
357
358
7.92k
    pxs->have_page = true;
359
360
7.92k
    if (will_fill) {
361
7.92k
        gx_path *stroke_path = NULL;
362
7.92k
        int (*fill_proc) (gs_gstate *) =
363
7.92k
            (pxgs->fill_mode == eEvenOdd ? gs_eofill : gs_fill);
364
365
7.92k
        if ((code = px_set_paint(&pxgs->brush, pxs)) < 0)
366
0
            return code;
367
368
        /* if we are also going to stroke the path, store a copy. */
369
7.92k
        if (will_stroke) {
370
0
            stroke_path = gx_path_alloc_shared(ppath, pxs->memory, "paint_path(save_for_stroke)");
371
0
            if (stroke_path == NULL)
372
0
                return_error(errorInsufficientMemory);
373
0
            gx_path_assign_preserve(stroke_path, ppath);
374
0
        }
375
376
        /* Make a reduced version of the path, and put that back. */
377
7.92k
        code = gx_path_elide_1d(ppath);
378
7.92k
        if (code < 0)
379
0
            return code;
380
381
        /* exit here if no stroke or the fill failed. */
382
7.92k
        code = (*fill_proc) (pgs);
383
7.92k
        if (code < 0 || !will_stroke) {
384
7.92k
            if (stroke_path)
385
0
                gx_path_free(stroke_path, "paint_path(error_with_fill)");
386
7.92k
            return code;
387
7.92k
        }
388
389
        /* restore the path for the stroke, will_stroke and hence
390
           stroke_path must be set at this point. */
391
0
        gx_path_assign_free(ppath, stroke_path);
392
0
    }
393
394
    /*
395
     * Per the description in the PCL XL reference documentation,
396
     * set a standard logical operation and transparency for stroking.
397
     * will_stroke is asserted true here.
398
     */
399
0
    {
400
0
        gs_rop3_t save_rop = gs_currentrasterop(pgs);
401
0
        bool save_transparent = gs_currenttexturetransparent(pgs);
402
0
        bool need_restore_rop = false;
403
404
0
        if (pxl_allow_rop_for_stroke(pgs) == false) {
405
0
            gs_setrasterop(pgs, rop3_T);
406
0
            gs_settexturetransparent(pgs, false);
407
0
            need_restore_rop = true;
408
0
        }
409
0
        code = px_set_paint(&pxgs->pen, pxs);
410
0
        if (code < 0)
411
0
            DO_NOTHING;
412
0
        code = gs_stroke(pgs);
413
        /* Bit hacky. Normally we handle this up at the interpreter level, and for
414
         * fill (above) that's how it works. However, px_set_paint() will call
415
         * gs_setpattern, which means that the high level pattern we've saved will
416
         * not be the one we use here. If we simply returned remap_color, as might be
417
         * expected, we would throw an error in the interpreter, and even if we didn't,
418
         * when we came back we would do the fill again, which is wasteful. Instead we
419
         * will cater for the situation here by calling the high level pattern routine
420
         * to install the pattern, then do the stroke again.
421
         */
422
0
        if (code == gs_error_Remap_Color) {
423
0
            code = px_high_level_pattern(pxs->pgs);
424
0
            code = gs_stroke(pgs);
425
0
        }
426
0
        if (code < 0)
427
0
            DO_NOTHING;
428
0
        if (need_restore_rop) {
429
0
            gs_setrasterop(pgs, save_rop);
430
0
            gs_settexturetransparent(pgs, save_transparent);
431
0
        }
432
0
    }
433
0
    return code;
434
7.92k
}
435
436
/* Paint a shape defined by a one-operator path. */
437
static int
438
paint_shape(px_args_t * par, px_state_t * pxs, px_operator_proc((*path_op)))
439
704
{
440
441
704
    int code;
442
704
    gs_gstate *pgs = pxs->pgs;
443
704
    gs_fixed_point fxp;
444
445
    /* build the path */
446
704
    if ((code = pxNewPath(par, pxs)) < 0 ||
447
704
        (code = (*path_op) (par, pxs)) < 0)
448
0
        return code;
449
450
    /* save position and stroke and or fill the path */
451
704
    code = gx_path_current_point(gx_current_path(pxs->pgs), &fxp);
452
704
    if (code < 0)
453
0
        return code;
454
455
704
    code = paint_path(pxs);
456
704
    if (code < 0)
457
0
        return code;
458
459
    /* restore the saved position, and open a new subpath  */
460
704
    code = gx_path_add_point(gx_current_path(pxs->pgs), fxp.x, fxp.y);
461
704
    if (code < 0)
462
0
        return code;
463
464
704
    return gx_setcurrentpoint_from_path(pgs, gx_current_path(pxs->pgs));
465
704
}
466
467
/* ---------------- Operators ---------------- */
468
469
const byte apxCloseSubPath[] = { 0, 0 };
470
int
471
pxCloseSubPath(px_args_t * par, px_state_t * pxs)
472
2.26k
{
473
2.26k
    return gs_closepath(pxs->pgs);
474
2.26k
}
475
476
const byte apxNewPath[] = { 0, 0 };
477
int
478
pxNewPath(px_args_t * par, px_state_t * pxs)
479
23.4k
{
480
23.4k
    return gs_newpath(pxs->pgs);
481
23.4k
}
482
483
/* Unlike painting single objects the PaintPath operator preserves the
484
   path */
485
const byte apxPaintPath[] = { 0, 0 };
486
int
487
pxPaintPath(px_args_t * par, px_state_t * pxs)
488
7.22k
{
489
7.22k
    gx_path *ppath = gx_current_path(pxs->pgs);
490
7.22k
    gx_path *save_path =
491
7.22k
        gx_path_alloc_shared(ppath, pxs->memory, "pxPaintPath");
492
7.22k
    int code;
493
494
7.22k
    if (save_path == 0)
495
0
        return_error(errorInsufficientMemory);
496
497
7.22k
    gx_path_assign_preserve(save_path, ppath);
498
7.22k
    code = paint_path(pxs);
499
7.22k
    gx_path_assign_free(ppath, save_path);
500
501
7.22k
    if (code >= 0)
502
7.22k
        code = gx_setcurrentpoint_from_path(pxs->pgs, ppath);
503
504
7.22k
    return code;
505
7.22k
}
506
507
const byte apxArcPath[] = {
508
    pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0,
509
    pxaArcDirection, 0
510
};
511
int
512
pxArcPath(px_args_t * par, px_state_t * pxs)
513
0
{                               /*
514
                                 * Note that "clockwise" in user space is counter-clockwise on
515
                                 * the page, because the Y coordinate is inverted.
516
                                 */
517
0
    bool clockwise = (par->pv[3] != 0 && par->pv[3]->value.i == eClockWise);
518
0
    px_arc_params_t params;
519
0
    int code =
520
0
        setup_arc(&params, par->pv[0], par->pv[1], par->pv[2], pxs, false);
521
0
    int rcode = code;
522
523
0
    if (code >= 0 && code != arc_degenerate) {
524
0
        bool closed = params.ang3 == params.ang4;
525
526
0
        clockwise ^= params.reversed;
527
0
        if (closed) {
528
0
            if (clockwise)
529
0
                params.ang4 += 360;
530
0
            else
531
0
                params.ang3 += 360;
532
0
        }
533
0
        code = gs_arc_add(pxs->pgs, !clockwise, params.center.x,
534
0
                          params.center.y, params.radius, params.ang3,
535
0
                          params.ang4, false);
536
0
        if (code >= 0 && closed) {      /* We have to close the path explicitly. */
537
0
            code = gs_closepath(pxs->pgs);
538
0
        }
539
0
    }
540
0
    if (rcode == arc_rectangular)
541
0
        gs_setmatrix(pxs->pgs, &params.save_ctm);
542
0
    return code;
543
0
}
544
545
const byte apxBezierPath[] = {
546
    0, pxaNumberOfPoints, pxaPointType, pxaControlPoint1, pxaControlPoint2,
547
    pxaEndPoint, 0
548
};
549
int
550
pxBezierPath(px_args_t * par, px_state_t * pxs)
551
10.5k
{
552
10.5k
    return add_curves(par, pxs, gs_curveto);
553
10.5k
}
554
555
const byte apxBezierRelPath[] = {
556
    0, pxaNumberOfPoints, pxaPointType, pxaControlPoint1, pxaControlPoint2,
557
    pxaEndPoint, 0
558
};
559
int
560
pxBezierRelPath(px_args_t * par, px_state_t * pxs)
561
4.19k
{
562
4.19k
    return add_curves(par, pxs, gs_rcurveto);
563
4.19k
}
564
565
const byte apxChord[] = {
566
    pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
567
};
568
px_operator_proc(pxChordPath);
569
int
570
pxChord(px_args_t * par, px_state_t * pxs)
571
0
{
572
0
    return paint_shape(par, pxs, pxChordPath);
573
0
}
574
575
const byte apxChordPath[] = {
576
    pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
577
};
578
int
579
pxChordPath(px_args_t * par, px_state_t * pxs)
580
0
{
581
0
    px_arc_params_t params;
582
0
    int code =
583
0
        setup_arc(&params, par->pv[0], par->pv[1], par->pv[2], pxs, false);
584
0
    int rcode = code;
585
586
    /* See ArcPath above for the meaning of "clockwise". */
587
0
    if (code >= 0 && code != arc_degenerate) {
588
0
        if (params.ang3 == params.ang4)
589
0
            params.ang3 += 360;
590
0
        code = gs_arc_add(pxs->pgs, !params.reversed,
591
0
                          params.center.x, params.center.y,
592
0
                          params.radius, params.ang3, params.ang4, false);
593
0
        if (code >= 0)
594
0
            code = gs_closepath(pxs->pgs);
595
0
    }
596
0
    if (rcode == arc_rectangular)
597
0
        gs_setmatrix(pxs->pgs, &params.save_ctm);
598
0
    if (code >= 0)
599
0
        code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y);
600
0
    return code;
601
602
0
}
603
604
const byte apxEllipse[] = {
605
    pxaBoundingBox, 0, 0
606
};
607
px_operator_proc(pxEllipsePath);
608
int
609
pxEllipse(px_args_t * par, px_state_t * pxs)
610
0
{
611
0
    return paint_shape(par, pxs, pxEllipsePath);
612
0
}
613
614
const byte apxEllipsePath[] = {
615
    pxaBoundingBox, 0, 0
616
};
617
int
618
pxEllipsePath(px_args_t * par, px_state_t * pxs)
619
0
{
620
0
    px_arc_params_t params;
621
0
    int code = setup_arc(&params, par->pv[0], NULL, NULL, pxs, true);
622
0
    int rcode = code;
623
0
    real a_start = 180.0;
624
0
    real a_end = -180.0;
625
626
    /* swap start and end angles if counter clockwise ellipse */
627
0
    if (params.reversed) {
628
0
        a_start = -180.0;
629
0
        a_end = 180.0;
630
0
    }
631
    /* See ArcPath above for the meaning of "clockwise". */
632
0
    if (code < 0 || code == arc_degenerate ||
633
0
        (code = gs_arc_add(pxs->pgs, !params.reversed,
634
0
                           params.center.x, params.center.y,
635
0
                           params.radius, a_start, a_end, false)) < 0 ||
636
        /* We have to close the path explicitly. */
637
0
        (code = gs_closepath(pxs->pgs)) < 0)
638
0
        DO_NOTHING;
639
0
    if (rcode == arc_rectangular)
640
0
        gs_setmatrix(pxs->pgs, &params.save_ctm);
641
0
    if (code >= 0)
642
0
        code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y);
643
0
    return code;
644
0
}
645
646
const byte apxLinePath[] = {
647
    0, pxaEndPoint, pxaNumberOfPoints, pxaPointType, 0
648
};
649
int
650
pxLinePath(px_args_t * par, px_state_t * pxs)
651
7.49k
{
652
7.49k
    return add_lines(par, pxs, gs_lineto);
653
7.49k
}
654
655
const byte apxLineRelPath[] = {
656
    0, pxaEndPoint, pxaNumberOfPoints, pxaPointType, 0
657
};
658
int
659
pxLineRelPath(px_args_t * par, px_state_t * pxs)
660
7.20k
{
661
7.20k
    return add_lines(par, pxs, gs_rlineto);
662
7.20k
}
663
664
const byte apxPie[] = {
665
    pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
666
};
667
px_operator_proc(pxPiePath);
668
int
669
pxPie(px_args_t * par, px_state_t * pxs)
670
0
{
671
0
    return paint_shape(par, pxs, pxPiePath);
672
0
}
673
674
const byte apxPiePath[] = {
675
    pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
676
};
677
int
678
pxPiePath(px_args_t * par, px_state_t * pxs)
679
0
{
680
0
    px_arc_params_t params;
681
0
    int code =
682
0
        setup_arc(&params, par->pv[0], par->pv[1], par->pv[2], pxs, false);
683
0
    int rcode = code;
684
685
    /* See ArcPath above for the meaning of "clockwise". */
686
0
    if (code >= 0 && code != arc_degenerate) {
687
0
        if (params.ang3 == params.ang4)
688
0
            params.ang3 += 360;
689
0
        code = gs_moveto(pxs->pgs, params.center.x, params.center.y);
690
0
        if (code >= 0) {
691
0
            code = gs_arc_add(pxs->pgs, !params.reversed,
692
0
                              params.center.x, params.center.y,
693
0
                              params.radius, params.ang3, params.ang4, true);
694
0
        }
695
0
    }
696
0
    if (rcode == arc_rectangular)
697
0
        gs_setmatrix(pxs->pgs, &params.save_ctm);
698
0
    if (code < 0 || rcode == arc_degenerate ||
699
0
        (code = gs_closepath(pxs->pgs)) < 0 ||
700
0
        (code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y)) < 0)
701
0
        DO_NOTHING;
702
0
    return code;
703
0
}
704
705
const byte apxRectangle[] = {
706
    pxaBoundingBox, 0, 0
707
};
708
px_operator_proc(pxRectanglePath);
709
int
710
pxRectangle(px_args_t * par, px_state_t * pxs)
711
704
{
712
704
    return paint_shape(par, pxs, pxRectanglePath);
713
704
}
714
715
const byte apxRectanglePath[] = {
716
    pxaBoundingBox, 0, 0
717
};
718
int
719
pxRectanglePath(px_args_t * par, px_state_t * pxs)
720
1.81k
{
721
1.81k
    double x1, y1, x2, y2;
722
1.81k
    gs_fixed_point p1;
723
1.81k
    gs_gstate *pgs = pxs->pgs;
724
1.81k
    gx_path *ppath = gx_current_path(pgs);
725
1.81k
    gs_fixed_point lines[3];
726
727
1.81k
#define p2 lines[1]
728
7.27k
#define pctm (&((const gs_gstate *)pgs)->ctm)
729
1.81k
    int code;
730
731
1.81k
    set_box_value(x1, y1, x2, y2, par->pv[0]);
732
    /*
733
     * Rectangles are always drawn in a canonical order.
734
     */
735
1.81k
    if (x1 > x2) {
736
2
        double t = x1;
737
738
2
        x1 = x2;
739
2
        x2 = t;
740
2
    }
741
1.81k
    if (y1 > y2) {
742
0
        double t = y1;
743
744
0
        y1 = y2;
745
0
        y2 = t;
746
0
    }
747
1.81k
    if ((code = gs_point_transform2fixed(pctm, x1, y1, &p1)) < 0 ||
748
1.81k
        (code = gs_point_transform2fixed(pctm, x2, y2, &p2)) < 0 ||
749
1.81k
        (code = gs_moveto(pgs, x1, y1)) < 0)
750
0
        return code;
751
#ifdef DRAW_RECTANGLES_CLOCKWISE
752
    /*
753
     * DRAW_RECTANGLES_CLOCKWISE means clockwise on the page, which is
754
     * counter-clockwise in user space.
755
     */
756
    if ((code = gs_point_transform2fixed(pctm, x2, y1, &lines[0])) < 0 ||
757
        (code = gs_point_transform2fixed(pctm, x1, y2, &lines[2])) < 0)
758
        return code;
759
#else
760
1.81k
    if ((code = gs_point_transform2fixed(pctm, x1, y2, &lines[0])) < 0 ||
761
1.81k
        (code = gs_point_transform2fixed(pctm, x2, y1, &lines[2])) < 0)
762
0
        return code;
763
1.81k
#endif
764
1.81k
    if ((code = gx_path_add_lines(ppath, lines, 3)) < 0)
765
0
        return code;
766
1.81k
    return gs_closepath(pgs);
767
1.81k
#undef pctm
768
1.81k
#undef p2
769
1.81k
}
770
771
const byte apxRoundRectangle[] = {
772
    pxaBoundingBox, pxaEllipseDimension, 0, 0
773
};
774
px_operator_proc(pxRoundRectanglePath);
775
int
776
pxRoundRectangle(px_args_t * par, px_state_t * pxs)
777
0
{
778
0
    return paint_shape(par, pxs, pxRoundRectanglePath);
779
0
}
780
781
const byte apxRoundRectanglePath[] = {
782
    pxaBoundingBox, pxaEllipseDimension, 0, 0
783
};
784
int
785
pxRoundRectanglePath(px_args_t * par, px_state_t * pxs)
786
0
{
787
0
    double x1, y1, x2, y2;
788
0
    real xr = real_value(par->pv[1], 0) * 0.5;
789
0
    real yr = real_value(par->pv[1], 1) * 0.5;
790
0
    real xd, yd;
791
0
    gs_matrix save_ctm;
792
0
    gs_gstate *pgs = pxs->pgs;
793
0
    int code;
794
795
0
    set_box_value(x1, y1, x2, y2, par->pv[0]);
796
0
    xd = x2 - x1;
797
0
    yd = y2 - y1;
798
    /*
799
     * H-P printers give an error if the points are specified out
800
     * of order.
801
     */
802
0
    if (xd < 0 || yd < 0)
803
0
        return_error(errorIllegalAttributeValue);
804
0
    if (xr == 0 || yr == 0)
805
0
        return pxRectanglePath(par, pxs);
806
0
    gs_currentmatrix(pgs, &save_ctm);
807
0
    gs_translate(pgs, x1, y1);
808
0
    if (xr != yr) {             /* Change coordinates so the arcs are circular. */
809
0
        double scale = xr / yr;
810
811
0
        if ((code = gs_scale(pgs, scale, 1.0)) < 0)
812
0
            return code;
813
0
        xd *= yr / xr;
814
0
    }
815
0
#define r yr
816
    /*
817
     * Draw the rectangle counter-clockwise on the page, which is
818
     * clockwise in user space.  (This may be reversed if the
819
     * coordinates are specified in a non-standard order.)
820
     */
821
0
    if ((code = gs_moveto(pgs, 0.0, r)) < 0 ||
822
0
        (code = gs_arcn(pgs, r, yd - r, r, 180.0, 90.0)) < 0 ||
823
0
        (code = gs_arcn(pgs, xd - r, yd - r, r, 90.0, 0.0)) < 0 ||
824
0
        (code = gs_arcn(pgs, xd - r, r, r, 0.0, 270.0)) < 0 ||
825
0
        (code = gs_arcn(pgs, r, r, r, 270.0, 180.0)) < 0 ||
826
0
        (code = gs_closepath(pgs)) < 0 ||
827
0
        (code = gs_moveto(pgs, 0.0, 0.0)) < 0)
828
0
        return code;
829
0
#undef r
830
0
    return gs_setmatrix(pgs, &save_ctm);
831
0
}
832
833
const byte apxText[] = {
834
    pxaTextData, 0, pxaXSpacingData, pxaYSpacingData, 0
835
};
836
int
837
pxText(px_args_t * par, px_state_t * pxs)
838
3.16k
{
839
3.16k
  {
840
3.16k
      int code = px_set_paint(&pxs->pxgs->brush, pxs);
841
842
3.16k
      if (code < 0)
843
0
          return code;
844
3.16k
  }
845
3.16k
  if (par->pv[0]->value.array.size != 0 && pxs->pxgs->brush.type != pxpNull)
846
3.16k
    pxs->have_page = true;
847
848
3.16k
  return px_text(par, pxs, false);
849
3.16k
}
850
851
const byte apxTextPath[] = {
852
    pxaTextData, 0, pxaXSpacingData, pxaYSpacingData, 0
853
};
854
int
855
pxTextPath(px_args_t * par, px_state_t * pxs)
856
0
{
857
0
    int code = px_set_paint(&pxs->pxgs->pen, pxs);
858
859
0
    if (code < 0)
860
0
        return code;
861
    /* NB this should be refactored with pxText (immediately above)
862
       and it is not a good heuristic for detecting a marked page. */
863
0
    if (par->pv[0]->value.array.size != 0 && pxs->pxgs->pen.type != pxpNull)
864
0
        pxs->have_page = true;
865
0
    return px_text(par, pxs, true);
866
0
}