/* Copyright (C) 1993, 1995, 1996, 1997, 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: zmedia2.c,v 1.3.2.1.2.1 2003/01/17 00:49:06 giles Exp $ */ /* Media matching for setpagedevice */ #include "math_.h" #include "memory_.h" #include "ghost.h" #include "gsmatrix.h" #include "oper.h" #include "idict.h" #include "idparam.h" #include "iname.h" #include "store.h" /* .matchmedia true */ /* .matchmedia false */ /* null .matchmedia null true */ private int zmatch_page_size(P8(const ref * pvreq, const ref * pvmed, int policy, int orient, bool roll, float *best_mismatch, gs_matrix * pmat, gs_point * pmsize)); typedef struct match_record_s { ref best_key, match_key; uint priority, no_match_priority; } match_record_t; private void reset_match(match_record_t *match) { make_null(&match->best_key); make_null(&match->match_key); match->priority = match->no_match_priority; } private int zmatchmedia(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr preq = op - 3; os_ptr pattr = op - 2; os_ptr ppol = op - 1; os_ptr pkeys = op; /* *const */ int policy_default; float best_mismatch = (float)max_long; /* adhoc */ float mbest = best_mismatch; match_record_t match; ref no_priority; ref *ppriority; int mepos, orient; bool roll; int code; int ai; struct mkd_ { ref key, dict; } aelt; if (r_has_type(pattr, t_null)) { check_op(4); make_null(op - 3); make_true(op - 2); pop(2); return 0; } check_type(*preq, t_dictionary); check_dict_read(*preq); check_type(*pattr, t_dictionary); check_dict_read(*pattr); check_type(*ppol, t_dictionary); check_dict_read(*ppol); check_array(*pkeys); check_read(*pkeys); switch (code = dict_int_null_param(preq, "MediaPosition", 0, 0x7fff, 0, &mepos)) { default: return code; case 2: case 1: mepos = -1; case 0:; } switch (code = dict_int_null_param(preq, "Orientation", 0, 3, 0, &orient)) { default: return code; case 2: case 1: orient = -1; case 0:; } code = dict_bool_param(preq, "RollFedMedia", false, &roll); if (code < 0) return code; code = dict_int_param(ppol, "PolicyNotFound", 0, 7, 0, &policy_default); if (code < 0) return code; if (dict_find_string(pattr, "Priority", &ppriority) > 0) { check_array_only(*ppriority); check_read(*ppriority); } else { make_empty_array(&no_priority, a_readonly); ppriority = &no_priority; } match.no_match_priority = r_size(ppriority); reset_match(&match); for (ai = dict_first(pattr); (ai = dict_next(pattr, ai, (ref * /*[2]*/)&aelt)) >= 0; ) { if (r_has_type(&aelt.dict, t_dictionary) && r_has_attr(dict_access_ref(&aelt.dict), a_read) && r_has_type(&aelt.key, t_integer) && (mepos < 0 || aelt.key.value.intval == mepos) ) { bool match_all; uint ki, pi; code = dict_bool_param(&aelt.dict, "MatchAll", false, &match_all); if (code < 0) return code; for (ki = 0; ki < r_size(pkeys); ki++) { ref key; ref kstr; ref *prvalue; ref *pmvalue; ref *ppvalue; int policy; array_get(pkeys, ki, &key); if (dict_find(&aelt.dict, &key, &pmvalue) <= 0) continue; if (dict_find(preq, &key, &prvalue) <= 0 || r_has_type(prvalue, t_null) ) { if (match_all) goto no; else continue; } /* Look for the Policies entry for this key. */ if (dict_find(ppol, &key, &ppvalue) > 0) { check_type_only(*ppvalue, t_integer); policy = ppvalue->value.intval; } else policy = policy_default; /* * Match a requested attribute value with the attribute value in the * description of a medium. For all attributes except PageSize, * matching means equality. PageSize is special; see match_page_size * below. */ if (r_has_type(&key, t_name) && (name_string_ref(&key, &kstr), r_size(&kstr) == 8 && !memcmp(kstr.value.bytes, "PageSize", 8)) ) { gs_matrix ignore_mat; gs_point ignore_msize; if (zmatch_page_size(prvalue, pmvalue, policy, orient, roll, &best_mismatch, &ignore_mat, &ignore_msize) <= 0) goto no; } else if (!obj_eq(prvalue, pmvalue)) goto no; } /* We have a match. If it is a better match */ /* than the current best one, it supersedes it */ /* regardless of priority. */ if (best_mismatch < mbest) { mbest = best_mismatch; reset_match(&match); } /* In case of a tie, see if the new match has */ /* priority. */ for (pi = match.priority; pi > 0;) { ref pri; pi--; array_get(ppriority, pi, &pri); if (obj_eq(&aelt.key, &pri)) { /* Yes, higher priority. */ match.best_key = aelt.key; match.priority = pi; break; } } /* Save the match in case no match has priority. */ match.match_key = aelt.key; no:; } } if (r_has_type(&match.match_key, t_null)) { make_false(op - 3); pop(3); } else { if (r_has_type(&match.best_key, t_null)) op[-3] = match.match_key; else op[-3] = match.best_key; make_true(op - 2); pop(2); } return 0; } /* [ ] [ ( | )] * .matchpagesize * true -or- false */ private int zmatchpagesize(i_ctx_t *i_ctx_p) { os_ptr op = osp; gs_matrix mat; float ignore_mismatch = (float)max_long; gs_point media_size; int orient; bool roll; int code; check_type(op[-3], t_integer); if (r_has_type(op - 2, t_null)) orient = -1; else { check_int_leu(op[-2], 3); orient = (int)op[-2].value.intval; } check_type(op[-1], t_boolean); roll = op[-1].value.boolval; code = zmatch_page_size(op - 5, op - 4, (int)op[-3].value.intval, orient, roll, &ignore_mismatch, &mat, &media_size); switch (code) { default: return code; case 0: make_false(op - 5); pop(5); break; case 1: code = write_matrix(op, &mat); if (code < 0 && !r_has_type(op, t_null)) return code; op[-5] = *op; make_real(op - 4, media_size.x); make_real(op - 3, media_size.y); make_true(op - 2); pop(2); break; } return 0; } /* Match the PageSize. See below for details. */ private bool match_page_size(P8(const gs_point * request, const gs_rect * medium, int policy, int orient, bool roll, float *best_mismatch, gs_matrix * pmat, gs_point * pmsize)); private int zmatch_page_size(const ref * pvreq, const ref * pvmed, int policy, int orient, bool roll, float *best_mismatch, gs_matrix * pmat, gs_point * pmsize) { uint nr, nm; check_array(*pvreq); nr = r_size(pvreq); check_array(*pvmed); nm = r_size(pvmed); if (!((nm == 2 || nm == 4) && (nr == 2 || nr == nm))) return_error(e_rangecheck); { ref rv[6]; uint i; double v[6]; int code; array_get(pvreq, 0, &rv[0]); array_get(pvreq, 1, &rv[1]); for (i = 0; i < 4; ++i) array_get(pvmed, i % nm, &rv[i + 2]); if ((code = num_params(rv + 5, 6, v)) < 0) return code; { gs_point request; gs_rect medium; request.x = v[0], request.y = v[1]; medium.p.x = v[2], medium.p.y = v[3], medium.q.x = v[4], medium.q.y = v[5]; return match_page_size(&request, &medium, policy, orient, roll, best_mismatch, pmat, pmsize); } } } /* * Match a requested PageSize with the PageSize of a medium. The medium * may specify either a single value [mx my] or a range * [mxmin mymin mxmax mymax]; matching means equality or inclusion * to within a tolerance of 5, possibly swapping the requested X and Y. * Take the Policies value into account, keeping track of the discrepancy * if needed. When a match is found, also return the matrix to be * concatenated after setting up the default matrix, and the actual * media size. * * NOTE: The algorithm here doesn't work properly for variable-size media * when the match isn't exact. We'll fix it if we ever need to. */ private void make_adjustment_matrix(P5(const gs_point * request, const gs_rect * medium, gs_matrix * pmat, bool scale, int rotate)); private bool match_page_size(const gs_point * request, const gs_rect * medium, int policy, int orient, bool roll, float *best_mismatch, gs_matrix * pmat, gs_point * pmsize) { double rx = request->x, ry = request->y; if (policy == 7) { /* (Adobe) hack: just impose requested values */ *best_mismatch = 0; gs_make_identity(pmat); *pmsize = *request; } else { int fit_direct = rx - medium->p.x >= -5 && rx - medium->q.x <= 5 && ry - medium->p.y >= -5 && ry - medium->q.y <= 5; int fit_rotated = rx - medium->p.y >= -5 && rx - medium->q.y <= 5 && ry - medium->p.x >= -5 && ry - medium->q.x <= 5; if ( fit_direct && fit_rotated) { *best_mismatch = 0; make_adjustment_matrix(request, medium, pmat, false, orient < 0 ? 0 : orient); } else if ( fit_direct ) { int rotate = orient < 0 ? 0 : orient; *best_mismatch = rotate & 1 ? 0.1 : 0; make_adjustment_matrix(request, medium, pmat, false, (rotate + 1) & 2); } else if ( fit_rotated ) { int rotate = orient < 0 ? 1 : orient; *best_mismatch = rotate & 1 ? 0 : 0.1; make_adjustment_matrix(request, medium, pmat, false, rotate | 1); } else { int rotate = orient >= 0 ? orient : rx < ry ^ medium->q.x < medium->q.y; bool larger = (rotate & 1 ? medium->q.y >= rx && medium->q.x >= ry : medium->q.x >= rx && medium->q.y >= ry); bool adjust = false; float mismatch = medium->q.x * medium->q.y - rx * ry; switch (policy) { default: /* exact match only */ return false; case 3: /* nearest match, adjust */ adjust = true; case 5: /* nearest match, don't adjust */ if (fabs(mismatch) >= fabs(*best_mismatch)) return false; break; case 4: /* next larger match, adjust */ adjust = true; case 6: /* next larger match, don't adjust */ if (!larger || mismatch >= *best_mismatch) return false; break; } if (adjust) make_adjustment_matrix(request, medium, pmat, !larger, rotate); else { gs_rect req_rect; if(rotate & 1) { req_rect.p.x = ry; req_rect.p.y = rx; } else { req_rect.p.x = rx; req_rect.p.y = ry; } req_rect.q = req_rect.p; make_adjustment_matrix(request, &req_rect, pmat, false, rotate); } *best_mismatch = mismatch; } if (pmat->xx == 0) { /* Swap request X and Y. */ double temp = rx; rx = ry, ry = temp; } #define ADJUST_INTO(req, mmin, mmax)\ (req < mmin ? mmin : req > mmax ? mmax : req) pmsize->x = ADJUST_INTO(rx, medium->p.x, medium->q.x); pmsize->y = ADJUST_INTO(ry, medium->p.y, medium->q.y); #undef ADJUST_INTO } return true; } /* * Compute the adjustment matrix for scaling and/or rotating the page * to match the medium. If the medium is completely flexible in a given * dimension (e.g., roll media in one dimension, or displays in both), * we must adjust its size in that dimension to match the request. * We recognize this by an unreasonably small medium->p.{x,y}. */ #define MIN_MEDIA_SIZE 9 private void make_adjustment_matrix(const gs_point * request, const gs_rect * medium, gs_matrix * pmat, bool scale, int rotate) { double rx = request->x, ry = request->y; double mx = medium->q.x, my = medium->q.y; /* Rotate the request if necessary. */ if (rotate & 1) { double temp = rx; rx = ry, ry = temp; } /* If 'medium' is flexible, adjust 'mx' and 'my' towards 'rx' and 'ry', respectively. Note that 'mx' and 'my' have just acquired the largest permissible value, medium->q. */ if (medium->p.x < mx) /* non-empty width range */ if (rx < medium->p.x) mx = medium->p.x; /* minimum */ else if (mx > rx) mx = rx; /* fits */ /* else use medium->q.x, i.e., the maximum */ if (medium->p.y < my) /* non-empty height range */ if (ry < medium->p.y) my = medium->p.y; /* minimum */ else if (my > ry) my = ry; /* fits */ /* else use medium->q.y, i.e., the maximum */ /* Translate to align the centers. */ gs_make_translation(mx / 2, my / 2, pmat); /* Rotate if needed. */ if (rotate) gs_matrix_rotate(pmat, 90.0 * rotate, pmat); /* Scale if needed. */ if (scale) { double xfactor = mx / rx; double yfactor = my / ry; double factor = min(xfactor, yfactor); if (factor < 1) gs_matrix_scale(pmat, factor, factor, pmat); } /* Now translate the origin back, */ /* using the original, unswapped request. */ gs_matrix_translate(pmat, -request->x / 2, -request->y / 2, pmat); } #undef MIN_MEDIA_SIZE /* ------ Initialization procedure ------ */ const op_def zmedia2_l2_op_defs[] = { op_def_begin_level2(), {"4.matchmedia", zmatchmedia}, {"6.matchpagesize", zmatchpagesize}, op_def_end(0) };