Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/psi/zmedia2.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2021 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
/* Media matching for setpagedevice */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "ghost.h"
21
#include "gsmatrix.h"
22
#include "oper.h"
23
#include "idict.h"
24
#include "idparam.h"
25
#include "iname.h"
26
#include "store.h"
27
28
/* <pagedict> <attrdict> <policydict> <keys> .matchmedia <key> true */
29
/* <pagedict> <attrdict> <policydict> <keys> .matchmedia false */
30
/* <pagedict> null <policydict> <keys> .matchmedia null true */
31
static int zmatch_page_size(const gs_memory_t *mem,
32
                             const ref * pvreq, const ref * pvmed,
33
                             int policy, int orient, bool roll,
34
                             float *best_mismatch, gs_matrix * pmat,
35
                             gs_point * pmsize);
36
typedef struct match_record_s {
37
    ref best_key, match_key;
38
    uint priority, no_match_priority;
39
} match_record_t;
40
static void
41
reset_match(match_record_t *match)
42
3.37M
{
43
3.37M
    make_null(&match->best_key);
44
3.37M
    make_null(&match->match_key);
45
3.37M
    match->priority = match->no_match_priority;
46
3.37M
}
47
static int
48
zmatchmedia(i_ctx_t *i_ctx_p)
49
750k
{
50
750k
    os_ptr op = osp;
51
750k
    os_ptr preq = op - 3;
52
750k
    os_ptr pattr = op - 2;
53
750k
    os_ptr ppol = op - 1;
54
750k
    os_ptr pkeys = op;    /* *const */
55
750k
    int policy_default;
56
750k
    float best_mismatch = (float)max_long; /* adhoc */
57
750k
    float mepos_penalty;
58
750k
    float mbest = best_mismatch;
59
750k
    match_record_t match;
60
750k
    ref no_priority;
61
750k
    ref *ppriority;
62
750k
    int mepos, orient;
63
750k
    bool roll;
64
750k
    int code;
65
750k
    int ai;
66
750k
    struct mkd_ {
67
750k
        ref key, dict;
68
750k
    } aelt;
69
750k
    if (r_has_type(pattr, t_null)) {
70
0
        check_op(4);
71
0
        make_null(op - 3);
72
0
        make_true(op - 2);
73
0
        pop(2);
74
0
        return 0;
75
0
    }
76
750k
    check_type(*preq, t_dictionary);
77
750k
    check_dict_read(*preq);
78
750k
    check_type(*pattr, t_dictionary);
79
750k
    check_dict_read(*pattr);
80
750k
    check_type(*ppol, t_dictionary);
81
750k
    check_dict_read(*ppol);
82
750k
    check_array(*pkeys);
83
750k
    check_read(*pkeys);
84
750k
    switch (code = dict_int_null_param(preq, "MediaPosition", 0, 0x7fff,
85
750k
                                       0, &mepos)) {
86
0
        default:
87
0
            return code;
88
0
        case 2:
89
750k
        case 1:
90
750k
            mepos = -1;
91
750k
        case 0:;
92
750k
    }
93
750k
    switch (code = dict_int_null_param(preq, "Orientation", 0, 3,
94
750k
                                       0, &orient)) {
95
0
        default:
96
0
            return code;
97
0
        case 2:
98
750k
        case 1:
99
750k
            orient = -1;
100
750k
        case 0:;
101
750k
    }
102
750k
    code = dict_bool_param(preq, "RollFedMedia", false, &roll);
103
750k
    if (code < 0)
104
0
        return code;
105
750k
    code = dict_int_param(ppol, "PolicyNotFound", 0, 7, 0,
106
750k
                          &policy_default);
107
750k
    if (code < 0)
108
0
        return code;
109
750k
    if (dict_find_string(pattr, "Priority", &ppriority) > 0) {
110
0
        check_array_only(*ppriority);
111
0
        check_read(*ppriority);
112
750k
    } else {
113
750k
        make_empty_array(&no_priority, a_readonly);
114
750k
        ppriority = &no_priority;
115
750k
    }
116
750k
    match.no_match_priority = r_size(ppriority);
117
750k
    reset_match(&match);
118
750k
    for (ai = dict_first(pattr);
119
25.5M
         (ai = dict_next(pattr, ai, (ref * /*[2]*/)&aelt)) >= 0;
120
24.7M
         ) {
121
24.7M
        if (r_has_type(&aelt.dict, t_dictionary) &&
122
24.7M
            r_has_attr(dict_access_ref(&aelt.dict), a_read) &&
123
24.7M
            r_has_type(&aelt.key, t_integer)
124
24.7M
            ) {
125
24.7M
            bool match_all;
126
24.7M
            uint ki, pi;
127
128
24.7M
            code = dict_bool_param(&aelt.dict, "MatchAll", false,
129
24.7M
                                   &match_all);
130
24.7M
            if (code < 0)
131
0
                return code;
132
49.5M
            for (ki = 0; ki < r_size(pkeys); ki++) {
133
24.7M
                ref key;
134
24.7M
                ref kstr;
135
24.7M
                ref *prvalue;
136
24.7M
                ref *pmvalue;
137
24.7M
                ref *ppvalue;
138
24.7M
                int policy;
139
140
24.7M
                array_get(imemory, pkeys, ki, &key);
141
24.7M
                if (dict_find(&aelt.dict, &key, &pmvalue) <= 0)
142
375k
                    continue;
143
24.4M
                if (dict_find(preq, &key, &prvalue) <= 0 ||
144
24.4M
                    r_has_type(prvalue, t_null)
145
24.4M
                    ) {
146
0
                    if (match_all)
147
0
                        goto no;
148
0
                    else
149
0
                        continue;
150
0
                }
151
                /* Look for the Policies entry for this key. */
152
24.4M
                if (dict_find(ppol, &key, &ppvalue) > 0) {
153
24.4M
                    check_type_only(*ppvalue, t_integer);
154
24.4M
                    policy = ppvalue->value.intval;
155
24.4M
                } else
156
0
                    policy = policy_default;
157
        /*
158
         * Match a requested attribute value with the attribute value in the
159
         * description of a medium.  For all attributes except PageSize,
160
         * matching means equality.  PageSize is special; see match_page_size
161
         * below.
162
         */
163
24.4M
                if (r_has_type(&key, t_name) &&
164
24.4M
                    (name_string_ref(imemory, &key, &kstr),
165
24.4M
                     r_size(&kstr) == 8 &&
166
24.4M
                     !memcmp(kstr.value.bytes, "PageSize", 8))
167
24.4M
                    ) {
168
24.4M
                    gs_matrix ignore_mat;
169
24.4M
                    gs_point ignore_msize;
170
171
24.4M
                    if (zmatch_page_size(imemory, prvalue, pmvalue,
172
24.4M
                                         policy, orient, roll,
173
24.4M
                                         &best_mismatch,
174
24.4M
                                         &ignore_mat,
175
24.4M
                                         &ignore_msize)
176
24.4M
                        <= 0)
177
2.01k
                        goto no;
178
24.4M
                } else if (!obj_eq(imemory, prvalue, pmvalue))
179
0
                    goto no;
180
24.4M
            }
181
182
24.7M
            mepos_penalty = (mepos < 0 || aelt.key.value.intval == mepos) ?
183
24.7M
                0 : .001;
184
185
            /* We have a match. Save the match in case no better match is found */
186
24.7M
            if (r_has_type(&match.match_key, t_null))
187
750k
                match.match_key = aelt.key;
188
            /*
189
             * If it is a better match than the current best it supersedes it
190
             * regardless of priority. If the match is the same, then update
191
             * to the current only if the key value is lower.
192
             */
193
24.7M
            if (best_mismatch + mepos_penalty <= mbest) {
194
24.7M
                if (best_mismatch + mepos_penalty < mbest  ||
195
24.7M
                    (r_has_type(&match.match_key, t_integer) &&
196
24.3M
                     match.match_key.value.intval > aelt.key.value.intval)) {
197
2.62M
                    reset_match(&match);
198
2.62M
                    match.match_key = aelt.key;
199
2.62M
                    mbest = best_mismatch + mepos_penalty;
200
2.62M
                }
201
24.7M
            }
202
            /* In case of a tie, see if the new match has priority. */
203
24.7M
            for (pi = match.priority; pi > 0;) {
204
0
                ref pri;
205
206
0
                pi--;
207
0
                array_get(imemory, ppriority, pi, &pri);
208
0
                if (obj_eq(imemory, &aelt.key, &pri)) { /* Yes, higher priority. */
209
0
                    match.best_key = aelt.key;
210
0
                    match.priority = pi;
211
0
                    break;
212
0
                }
213
0
            }
214
24.7M
no:;
215
24.7M
        }
216
24.7M
    }
217
750k
    if (r_has_type(&match.match_key, t_null)) {
218
31
        make_false(op - 3);
219
31
        pop(3);
220
750k
    } else {
221
750k
        if (r_has_type(&match.best_key, t_null))
222
750k
            op[-3] = match.match_key;
223
0
        else
224
0
            op[-3] = match.best_key;
225
750k
        make_true(op - 2);
226
750k
        pop(2);
227
750k
    }
228
750k
    return 0;
229
750k
}
230
231
/* [<req_x> <req_y>] [<med_x0> <med_y0> (<med_x1> <med_y1> | )]
232
 *     <policy> <orient|null> <roll> <matrix|null> .matchpagesize
233
 *   <matrix|null> <med_x> <med_y> true   -or-  false
234
 */
235
static int
236
zmatchpagesize(i_ctx_t *i_ctx_p)
237
779k
{
238
779k
    os_ptr op = osp;
239
779k
    gs_matrix mat;
240
779k
    float ignore_mismatch = (float)max_long;
241
779k
    gs_point media_size;
242
779k
    int orient;
243
779k
    bool roll;
244
779k
    int code;
245
246
779k
    check_type(op[-3], t_integer);
247
779k
    if (r_has_type(op - 2, t_null))
248
779k
        orient = -1;
249
0
    else {
250
0
        check_int_leu(op[-2], 3);
251
0
        orient = (int)op[-2].value.intval;
252
0
    }
253
779k
    check_type(op[-1], t_boolean);
254
779k
    roll = op[-1].value.boolval;
255
779k
    code = zmatch_page_size(imemory,
256
779k
                            op - 5, op - 4, (int)op[-3].value.intval,
257
779k
                            orient, roll,
258
779k
                            &ignore_mismatch, &mat, &media_size);
259
779k
    switch (code) {
260
31
        default:
261
31
            return code;
262
0
        case 0:
263
0
            make_false(op - 5);
264
0
            pop(5);
265
0
            break;
266
779k
        case 1:
267
779k
            code = write_matrix(op, &mat);
268
779k
            if (code < 0 && !r_has_type(op, t_null))
269
0
                return code;
270
779k
            op[-5] = *op;
271
779k
            make_real(op - 4, media_size.x);
272
779k
            make_real(op - 3, media_size.y);
273
779k
            make_true(op - 2);
274
779k
            pop(2);
275
779k
            break;
276
779k
    }
277
779k
    return 0;
278
779k
}
279
/* Match the PageSize.  See below for details. */
280
static int
281
match_page_size(const gs_point * request,
282
                             const gs_rect * medium,
283
                             int policy, int orient, bool roll,
284
                             float *best_mismatch, gs_matrix * pmat,
285
                             gs_point * pmsize);
286
static int
287
zmatch_page_size(const gs_memory_t *mem, const ref * pvreq, const ref * pvmed,
288
                 int policy, int orient, bool roll,
289
                 float *best_mismatch, gs_matrix * pmat, gs_point * pmsize)
290
25.1M
{
291
25.1M
    uint nr, nm;
292
25.1M
    int code;
293
25.1M
    ref rv[6];
294
295
    /* array_get checks array types and size. */
296
    /* This allows normal or packed arrays to be used */
297
25.1M
    if ((code = array_get(mem, pvreq, 1, &rv[1])) < 0)
298
0
        return_error(code);
299
25.1M
    nr = r_size(pvreq);
300
25.1M
    if ((code = array_get(mem, pvmed, 1, &rv[3])) < 0)
301
0
        return_error(code);
302
25.1M
    nm = r_size(pvmed);
303
25.1M
    if (!((nm == 2 || nm == 4) && (nr == 2 || nr == nm)))
304
0
        return_error(gs_error_rangecheck);
305
25.1M
    {
306
25.1M
        uint i;
307
25.1M
        double v[6];
308
25.1M
        int code;
309
310
25.1M
        array_get(mem, pvreq, 0, &rv[0]);
311
125M
        for (i = 0; i < 4; ++i)
312
100M
            array_get(mem,pvmed, i % nm, &rv[i + 2]);
313
25.1M
        if ((code = num_params(rv + 5, 6, v)) < 0)
314
0
            return code;
315
25.1M
        {
316
25.1M
            gs_point request;
317
25.1M
            gs_rect medium;
318
319
25.1M
            request.x = v[0], request.y = v[1];
320
25.1M
            medium.p.x = v[2], medium.p.y = v[3],
321
25.1M
                medium.q.x = v[4], medium.q.y = v[5];
322
25.1M
            return match_page_size(&request, &medium, policy, orient,
323
25.1M
                                   roll, best_mismatch, pmat, pmsize);
324
25.1M
        }
325
25.1M
    }
326
25.1M
}
327
/*
328
 * Match a requested PageSize with the PageSize of a medium.  The medium
329
 * may specify either a single value [mx my] or a range
330
 * [mxmin mymin mxmax mymax]; matching means equality or inclusion
331
 * to within a tolerance of 5, possibly swapping the requested X and Y.
332
 * Take the Policies value into account, keeping track of the discrepancy
333
 * if needed.  When a match is found, also return the matrix to be
334
 * concatenated after setting up the default matrix, and the actual
335
 * media size.
336
 *
337
 * NOTE: The algorithm here doesn't work properly for variable-size media
338
 * when the match isn't exact.  We'll fix it if we ever need to.
339
 */
340
static void make_adjustment_matrix(const gs_point * request,
341
                                    const gs_rect * medium,
342
                                    gs_matrix * pmat,
343
                                    bool scale, int rotate);
344
static int
345
match_page_size(const gs_point * request, const gs_rect * medium, int policy,
346
                int orient, bool roll, float *best_mismatch, gs_matrix * pmat,
347
                gs_point * pmsize)
348
25.1M
{
349
25.1M
    double rx = request->x, ry = request->y;
350
351
25.1M
    if ((rx <= 0) || (ry <= 0))
352
2.04k
        return_error(gs_error_rangecheck);
353
25.1M
    if (policy == 7) {
354
                /* (Adobe) hack: just impose requested values */
355
25.1M
        *best_mismatch = 0;
356
25.1M
        gs_make_identity(pmat);
357
25.1M
        *pmsize = *request;
358
25.1M
    } else {
359
0
        int fit_direct  = rx - medium->p.x >= -5 && rx - medium->q.x <= 5
360
0
                       && ry - medium->p.y >= -5 && ry - medium->q.y <= 5;
361
0
        int fit_rotated = rx - medium->p.y >= -5 && rx - medium->q.y <= 5
362
0
                       && ry - medium->p.x >= -5 && ry - medium->q.x <= 5;
363
364
        /* Fudge matches from a non-standard page size match (4 element array) */
365
        /* as worse than an exact match from a standard (2 element array), but */
366
        /* better than for a rotated match to a standard pagesize. This should */
367
        /* prevent rotation unless we have to (particularly for raster file    */
368
        /* formats like TIFF, JPEG, PNG, PCX, BMP, etc. and also should allow  */
369
        /* exact page size specification when there is a range PageSize entry. */
370
        /* As the comment in gs_setpd.ps says "Devices that care will provide  */
371
        /* a real InputAttributes dictionary (most without a range pagesize)   */
372
0
        if ( fit_direct && fit_rotated) {
373
0
            make_adjustment_matrix(request, medium, pmat, false, orient < 0 ? 0 : orient);
374
0
            if (medium->p.x < medium->q.x || medium->p.y < medium->q.y)
375
0
                *best_mismatch = (float)0.001;   /* fudge a match to a range as a small number */
376
0
            else  /* should be 0 for an exact match */
377
0
                *best_mismatch = fabs((rx - medium->p.x) * (medium->q.x - rx)) +
378
0
                                fabs((ry - medium->p.y) * (medium->q.y - ry));
379
0
        } else if ( fit_direct ) {
380
0
            int rotate = orient < 0 ? 0 : orient;
381
382
0
            make_adjustment_matrix(request, medium, pmat, false, (rotate + 1) & 2);
383
0
            *best_mismatch = fabs((medium->p.x - rx) * (medium->q.x - rx)) +
384
0
                                fabs((medium->p.y - ry) * (medium->q.y - ry)) +
385
0
                                    (pmat->xx == 0.0 || (rotate & 1) == 1 ? 0.01 : 0);  /* rotated */
386
0
        } else if ( fit_rotated ) {
387
0
            int rotate = (orient < 0 ? 1 : orient);
388
389
0
            make_adjustment_matrix(request, medium, pmat, false, rotate | 1);
390
0
            *best_mismatch = fabs((medium->p.y - rx) * (medium->q.y - rx)) +
391
0
                                fabs((medium->p.x - ry) * (medium->q.x - ry)) +
392
0
                                    (pmat->xx == 0.0 || (rotate & 1) == 1 ? 0.01 : 0);  /* rotated */
393
0
        } else {
394
0
            int rotate =
395
0
                (orient >= 0 ? orient :
396
0
                 (rx < ry) ^ (medium->q.x < medium->q.y));
397
0
            bool larger = (policy == 13) ? 0 :
398
0
                (rotate & 1 ? medium->q.y >= rx && medium->q.x >= ry :
399
0
                 medium->q.x >= rx && medium->q.y >= ry);
400
0
            bool adjust = false;
401
0
            float mismatch = medium->q.x * medium->q.y - rx * ry;
402
403
0
            switch (policy) {
404
0
                default:    /* exact match only */
405
0
                    return 0;
406
0
                case 3:   /* nearest match, adjust */
407
0
                case 13:        /* non-standard, nearest match, scale down OR up */
408
0
                    adjust = true;
409
                    /* fall through */
410
0
                case 5:   /* nearest match, don't adjust */
411
0
                    if (fabs(mismatch) >= fabs(*best_mismatch))
412
0
                        return 0;
413
0
                    break;
414
0
                case 4:   /* next larger match, adjust */
415
0
                    adjust = true;
416
                    /* fall through */
417
0
                case 6:   /* next larger match, don't adjust */
418
0
                    if (!larger || mismatch >= *best_mismatch)
419
0
                        return 0;
420
0
                    break;
421
0
            }
422
0
            if (adjust)
423
0
                make_adjustment_matrix(request, medium, pmat, !larger, rotate);
424
0
            else {
425
0
                gs_rect req_rect;
426
0
                if(rotate & 1) {
427
0
                    req_rect.p.x = ry;
428
0
                    req_rect.p.y = rx;
429
0
                } else {
430
0
                    req_rect.p.x = rx;
431
0
                    req_rect.p.y = ry;
432
0
                }
433
0
                req_rect.q = req_rect.p;
434
0
                make_adjustment_matrix(request, &req_rect, pmat, false, rotate);
435
0
            }
436
0
            *best_mismatch = fabs(mismatch);
437
0
        }
438
0
        if (pmat->xx == 0) { /* Swap request X and Y. */
439
0
            double temp = rx;
440
441
0
            rx = ry, ry = temp;
442
0
        }
443
0
#define ADJUST_INTO(req, mmin, mmax)\
444
0
      (req < mmin ? mmin : req > mmax ? mmax : req)
445
0
        pmsize->x = ADJUST_INTO(rx, medium->p.x, medium->q.x);
446
0
        pmsize->y = ADJUST_INTO(ry, medium->p.y, medium->q.y);
447
0
#undef ADJUST_INTO
448
0
    }
449
25.1M
    return 1;
450
25.1M
}
451
/*
452
 * Compute the adjustment matrix for scaling and/or rotating the page
453
 * to match the medium.  If the medium is completely flexible in a given
454
 * dimension (e.g., roll media in one dimension, or displays in both),
455
 * we must adjust its size in that dimension to match the request.
456
 * We recognize this by an unreasonably small medium->p.{x,y}.
457
 * The PageSize Policy 3 only scales down, so 'scale' will be false if
458
 * the medium is larger than the request. Policy 13 scales up OR down.
459
 */
460
static void
461
make_adjustment_matrix(const gs_point * request, const gs_rect * medium,
462
                       gs_matrix * pmat, bool scale, int rotate)
463
0
{
464
0
    double rx = request->x, ry = request->y;
465
0
    double mx = medium->q.x, my = medium->q.y;
466
467
    /* Rotate the request if necessary. */
468
0
    if (rotate & 1) {
469
0
        double temp = rx;
470
471
0
        rx = ry, ry = temp;
472
0
    }
473
    /* If 'medium' is flexible, adjust 'mx' and 'my' towards 'rx' and 'ry',
474
       respectively. Note that 'mx' and 'my' have just acquired the largest
475
       permissible value, medium->q. */
476
0
    if (medium->p.x < mx) { /* non-empty width range */
477
0
        if (rx < medium->p.x)
478
0
            mx = medium->p.x; /* use minimum of the range */
479
0
        else if (rx < mx)
480
0
            mx = rx;   /* fits */
481
                /* else leave mx == medium->q.x, i.e., the maximum */
482
0
    }
483
0
    if (medium->p.y < my) { /* non-empty height range */
484
0
        if (ry < medium->p.y)
485
0
            my = medium->p.y; /* use minimum of the range */
486
0
        else if (ry < my)
487
0
            my = ry;   /* fits */
488
            /* else leave my == medium->q.y, i.e., the maximum */
489
0
    }
490
491
    /* Translate to align the centers. */
492
0
    gs_make_translation(mx / 2, my / 2, pmat);
493
494
    /* Rotate if needed. */
495
0
    if (rotate)
496
0
        gs_matrix_rotate(pmat, 90.0 * rotate, pmat);
497
498
    /* Scale if needed. */
499
0
    if (scale) {
500
0
        double xfactor = mx / rx;
501
0
        double yfactor = my / ry;
502
0
        double factor = min(xfactor, yfactor);
503
504
0
        gs_matrix_scale(pmat, factor, factor, pmat);
505
0
    }
506
    /* Now translate the origin back, */
507
    /* using the original, unswapped request. */
508
0
    gs_matrix_translate(pmat, -request->x / 2, -request->y / 2, pmat);
509
0
}
510
#undef MIN_MEDIA_SIZE
511
512
/* ------ Initialization procedure ------ */
513
514
const op_def zmedia2_l2_op_defs[] =
515
{
516
    op_def_begin_level2(),
517
    {"4.matchmedia", zmatchmedia},
518
    {"6.matchpagesize", zmatchpagesize},
519
    op_def_end(0)
520
};