diff options
author | Thomas Deutschmann <whissi@gentoo.org> | 2019-10-15 12:24:12 +0200 |
---|---|---|
committer | Thomas Deutschmann <whissi@gentoo.org> | 2020-08-13 11:26:55 +0200 |
commit | e088156d5b620e5e639580dacf85c6dc13823c74 (patch) | |
tree | 57f5c025e203279944da512166c20bc0521d8ccd /psi/zfunc4.c | |
download | ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.gz ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.bz2 ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.zip |
Import Ghostscript 9.50ghostscript-9.50
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'psi/zfunc4.c')
-rw-r--r-- | psi/zfunc4.c | 599 |
1 files changed, 599 insertions, 0 deletions
diff --git a/psi/zfunc4.c b/psi/zfunc4.c new file mode 100644 index 00000000..99cdde67 --- /dev/null +++ b/psi/zfunc4.c @@ -0,0 +1,599 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* PostScript language support for FunctionType 4 (PS Calculator) Functions */ +#include "memory_.h" +#include "ghost.h" +#include "oper.h" +#include "opextern.h" +#include "gsfunc.h" +#include "gsfunc4.h" +#include "gsutil.h" +#include "idict.h" +#include "ifunc.h" +#include "iname.h" +#include "dstack.h" +#include "ialloc.h" +#include "gzstate.h" /* these are needed to check device parameters */ +#include "gsparam.h" /* these are needed to check device parameters */ +#include "zfunc.h" +#include "zcolor.h" +#include "gxdevsop.h" + +/* + * FunctionType 4 functions are not defined in the PostScript language. We + * provide support for them because they are needed for PDF 1.3. In + * addition to the standard FunctionType, Domain, and Range keys, they have + * a Function key whose value is a procedure in a restricted subset of the + * PostScript language. Specifically, the procedure must (recursively) + * contain only integer, real, Boolean, and procedure constants (only as + * literal operands of if and and ifelse), and operators chosen from the set + * given below. Note that names other than true and false are not allowed: + * the procedure must be 'bound'. + * + * The following list is taken directly from the PDF 1.3 documentation. + */ +#define XOP(zfn) int zfn(i_ctx_t *) +XOP(zabs); XOP(zand); XOP(zatan); XOP(zbitshift); +XOP(zceiling); XOP(zcos); XOP(zcvi); XOP(zcvr); +XOP(zdiv); XOP(zexp); XOP(zfloor); XOP(zidiv); +XOP(zln); XOP(zlog); XOP(zmod); XOP(zmul); +XOP(zneg); XOP(znot); XOP(zor); XOP(zround); +XOP(zsin); XOP(zsqrt); XOP(ztruncate); XOP(zxor); +XOP(zeq); XOP(zge); XOP(zgt); XOP(zle); XOP(zlt); XOP(zne); +XOP(z2copy); +#undef XOP +typedef struct calc_op_s { + op_proc_t proc; + gs_PtCr_opcode_t opcode; +} calc_op_t; +static const calc_op_t calc_ops[] = { + + /* Arithmetic operators */ + + {zabs, PtCr_abs}, + {zadd, PtCr_add}, + {zand, PtCr_and}, + {zatan, PtCr_atan}, + {zbitshift, PtCr_bitshift}, + {zceiling, PtCr_ceiling}, + {zcos, PtCr_cos}, + {zcvi, PtCr_cvi}, + {zcvr, PtCr_cvr}, + {zdiv, PtCr_div}, + {zexp, PtCr_exp}, + {zfloor, PtCr_floor}, + {zidiv, PtCr_idiv}, + {zln, PtCr_ln}, + {zlog, PtCr_log}, + {zmod, PtCr_mod}, + {zmul, PtCr_mul}, + {zneg, PtCr_neg}, + {znot, PtCr_not}, + {zor, PtCr_or}, + {zround, PtCr_round}, + {zsin, PtCr_sin}, + {zsqrt, PtCr_sqrt}, + {zsub, PtCr_sub}, + {ztruncate, PtCr_truncate}, + {zxor, PtCr_xor}, + + /* Comparison operators */ + + {zeq, PtCr_eq}, + {zge, PtCr_ge}, + {zgt, PtCr_gt}, + {zle, PtCr_le}, + {zlt, PtCr_lt}, + {zne, PtCr_ne}, + + /* Stack operators */ + + {zcopy, PtCr_copy}, + {z2copy, PtCr_copy}, + {zdup, PtCr_dup}, + {zexch, PtCr_exch}, + {zindex, PtCr_index}, + {zpop, PtCr_pop}, + {zroll, PtCr_roll} + + /* Special operators */ + + /*{zif, PtCr_if},*/ + /*{zifelse, PtCr_ifelse},*/ + /*{ztrue, PtCr_true},*/ + /*{zfalse, PtCr_false}*/ +}; + +/* Fix up an if or ifelse forward reference. */ +static void +psc_fixup(byte *p, byte *to) +{ + int skip = to - (p + 3); + + p[1] = (byte)(skip >> 8); + p[2] = (byte)skip; +} + +/* Check whether the ref is a given operator or resolves to it */ +static bool +resolves_to_oper(i_ctx_t *i_ctx_p, const ref *pref, const op_proc_t proc) +{ + if (!r_has_attr(pref, a_executable)) + return false; + if (r_btype(pref) == t_operator) { + return pref->value.opproc == proc; + } else if (r_btype(pref) == t_name) { + ref * val; + if (dict_find(systemdict, pref, &val) <= 0) + return false; + if (r_btype(val) != t_operator) + return false; + if (!r_has_attr(val, a_executable)) + return false; + return val->value.opproc == proc; + } + else + return false; +} + +/* Store an int in the buffer */ +static int +put_int(byte **p, int n) { + if (n == (byte)n) { + if (*p) { + (*p)[0] = PtCr_byte; + (*p)[1] = (byte)n; + *p += 2; + } + return 2; + } else { + if (*p) { + **p = PtCr_int; + memcpy(*p + 1, &n, sizeof(int)); + *p += sizeof(int) + 1; + } + return (sizeof(int) + 1); + } +} + +/* Store a float in the buffer */ +static int +put_float(byte **p, float n) { + if (*p) { + **p = PtCr_float; + memcpy(*p + 1, &n, sizeof(float)); + *p += sizeof(float) + 1; + } + return (sizeof(float) + 1); +} + +/* Store an op code in the buffer */ +static int +put_op(byte **p, byte op) { + if (*p) + *(*p)++ = op; + return 1; +} + +/* + * Check a calculator function for validity, optionally storing its encoded + * representation and add the size of the encoded representation to *psize. + * Note that we arbitrarily limit the depth of procedure nesting. pref is + * known to be a procedure. + */ +static int +check_psc_function(i_ctx_t *i_ctx_p, const ref *pref, int depth, byte *ops, int *psize, bool AllowRepeat) +{ + int code; + uint i, j; + uint size = r_size(pref); + byte *p; + + if (size == 2 && depth == 0) { + /* Bug 690986. Recognize and replace Corel tint transform function. + { tint_params CorelTintTransformFunction } + + Where tint_params resolves to an arrey like: + [ 1.0 0.0 0.0 0.0 + 0.0 1.0 0.0 0.0 + 0.0 0.0 1.0 0.0 + 0.0 0.0 0.0 1.0 + 0.2 0.81 0.76 0.61 + ] + And CorelTintTransformFunction is: + { /colorantSpecArray exch def + /nColorants colorantSpecArray length 4 idiv def + /inColor nColorants 1 add 1 roll nColorants array astore def + /outColor 4 array def + 0 1 3 { + /nOutInk exch def + 1 + 0 1 nColorants 1 sub { + dup inColor exch get + exch 4 mul nOutInk add colorantSpecArray exch get mul + 1 exch sub mul + } for + 1 exch sub + outColor nOutInk 3 -1 roll put + } for + outColor aload pop + } + */ + ref r_tp, r_cttf; /* original references */ + ref n_tp, n_cttf; /* names */ + ref *v_tp, *v_cttf; /* values */ + int sz; + + p = ops; + sz = *psize; + if(array_get(imemory, pref, 0, &r_tp) < 0) + goto idiom_failed; + if (array_get(imemory, pref, 1, &r_cttf) < 0) + goto idiom_failed; + if (r_has_type(&r_tp, t_name) && r_has_type(&r_cttf, t_name)) { + if ((code = name_enter_string(imemory, "tint_params", &n_tp)) < 0) + return code; + if (r_tp.value.pname == n_tp.value.pname) { + if ((code = name_enter_string(imemory, "CorelTintTransformFunction", &n_cttf)) < 0) + return code; + if (r_cttf.value.pname == n_cttf.value.pname) { + v_tp = dict_find_name(&n_tp); + v_cttf = dict_find_name(&n_cttf); + if (v_tp && v_cttf && r_is_array(v_tp) && r_is_array(v_cttf)) { + uint n_elem = r_size(v_tp); + + if ((n_elem & 3) == 0 && r_size(v_cttf) == 31) { + /* Enough testing, idiom recognition tests less. */ + uint n_col = n_elem/4; + + for (i = 0; i < 4; i ++) { + ref v; + float fv; + bool first = true; + + for (j = 0; j < n_col; j++) { + if (array_get(imemory, v_tp, j*4 + i, &v) < 0) + goto idiom_failed; + if (r_type(&v) == t_integer) + fv = (float)v.value.intval; + else if (r_type(&v) == t_real) + fv = v.value.realval; + else + goto idiom_failed; + + if (fv != 0.) { + if (first) + sz += put_int(&p, 1); + sz += put_int(&p, 1); + sz += put_int(&p, n_col + 1 - j + i + !first); + sz += put_op(&p, PtCr_index); + if (fv != 1.) { + sz += put_float(&p, fv); + sz += put_op(&p, PtCr_mul); + } + sz += put_op(&p, PtCr_sub); + if (first) + first = false; + else + sz += put_op(&p, PtCr_mul); + } + } + if (first) + sz += put_int(&p, 0); + else + sz += put_op(&p, PtCr_sub); + } + /* n_col+4 4 roll pop ... pop */ + sz += put_int(&p, n_col + 4); + sz += put_int(&p, 4); + sz += put_op(&p, PtCr_roll); + for (j = 0; j < n_col; j++) + sz += put_op(&p, PtCr_pop); + *psize = sz; + return 0; + } + } + } + } + } + } + idiom_failed:; + for (i = 0; i < size; ++i) { + byte no_ops[1 + max(sizeof(int), sizeof(float))]; + ref elt, elt2, elt3; + ref * delp; + + p = (ops ? ops + *psize : no_ops); + array_get(imemory, pref, i, &elt); + switch (r_btype(&elt)) { + case t_integer: + *psize += put_int(&p, elt.value.intval); + break; + case t_real: + *psize += put_float(&p, elt.value.realval); + break; + case t_boolean: + *p = (elt.value.boolval ? PtCr_true : PtCr_false); + ++*psize; + break; + case t_name: + if (!r_has_attr(&elt, a_executable)) + return_error(gs_error_rangecheck); + name_string_ref(imemory, &elt, &elt); + if (!bytes_compare(elt.value.bytes, r_size(&elt), + (const byte *)"true", 4)) { + *p = PtCr_true; + ++*psize; + break; + } + if (!bytes_compare(elt.value.bytes, r_size(&elt), + (const byte *)"false", 5)) { + *p = PtCr_false; + ++*psize; + break; + } + /* Check if the name is a valid operator in systemdict */ + if (dict_find(systemdict, &elt, &delp) <= 0) + return_error(gs_error_undefined); + if (r_btype(delp) != t_operator) + return_error(gs_error_typecheck); + if (!r_has_attr(delp, a_executable)) + return_error(gs_error_rangecheck); + elt = *delp; + /* Fall into the operator case */ + case t_operator: { + int j; + + for (j = 0; j < countof(calc_ops); ++j) + if (elt.value.opproc == calc_ops[j].proc) { + *p = calc_ops[j].opcode; + ++*psize; + goto next; + } + return_error(gs_error_rangecheck); + } + default: { + if (!r_is_proc(&elt)) + return_error(gs_error_typecheck); + if (depth == MAX_PSC_FUNCTION_NESTING) + return_error(gs_error_limitcheck); + if ((code = array_get(imemory, pref, ++i, &elt2)) < 0) + return code; + *psize += 3; + code = check_psc_function(i_ctx_p, &elt, depth + 1, ops, psize, AllowRepeat); + if (code < 0) + return code; + /* Check for { proc } repeat | {proc} if | {proc1} {proc2} ifelse */ + if (resolves_to_oper(i_ctx_p, &elt2, zrepeat)) { + if (!AllowRepeat) + return_error(gs_error_rangecheck); + if (ops) { + *p = PtCr_repeat; + psc_fixup(p, ops + *psize); + p = ops + *psize; + *p++ = PtCr_repeat_end; + } + *psize += 1; /* extra room for repeat_end */ + } else if (resolves_to_oper(i_ctx_p, &elt2, zif)) { + if (ops) { + *p = PtCr_if; + psc_fixup(p, ops + *psize); + } + } else if (!r_is_proc(&elt2)) + return_error(gs_error_rangecheck); + else if ((code = array_get(imemory, pref, ++i, &elt3)) < 0) + return code; + else if (resolves_to_oper(i_ctx_p, &elt3, zifelse)) { + if (ops) { + *p = PtCr_if; + psc_fixup(p, ops + *psize + 3); + p = ops + *psize; + *p = PtCr_else; + } + *psize += 3; + code = check_psc_function(i_ctx_p, &elt2, depth + 1, ops, psize, AllowRepeat); + if (code < 0) + return code; + if (ops) + psc_fixup(p, ops + *psize); + } else + return_error(gs_error_rangecheck); + } /* end 'default' */ + } + next: + DO_NOTHING; + } + return 0; +} +#undef MAX_PSC_FUNCTION_NESTING + +/* Check prototype */ +build_function_proc(gs_build_function_4); + +/* Finish building a FunctionType 4 (PostScript Calculator) function. */ +int +gs_build_function_4(i_ctx_t *i_ctx_p, const ref *op, const gs_function_params_t * mnDR, + int depth, gs_function_t ** ppfn, gs_memory_t *mem) +{ + gs_function_PtCr_params_t params; + ref *proc; + int code; + byte *ops; + int size; + int AllowRepeat = 1; /* Default to permitting Repeat, devices which can't handle it implement the spec_op */ + + *(gs_function_params_t *)¶ms = *mnDR; + params.ops.data = 0; /* in case of failure */ + params.ops.size = 0; /* ditto */ + if (dict_find_string(op, "Function", &proc) <= 0) { + code = gs_note_error(gs_error_rangecheck); + goto fail; + } + if (!r_is_proc(proc)) { + code = gs_note_error(gs_error_typecheck); + goto fail; + } + size = 0; + + /* Check if the device allows the use of repeat in functions */ + /* We can't handle 'repeat' with pdfwrite since it emits FunctionType 4 */ + { + char data[] = {"AllowPSRepeatFunctions"}; + dev_param_req_t request; + gs_c_param_list list; + + gs_c_param_list_write(&list, i_ctx_p->pgs->device->memory); + /* Stuff the data into a structure for passing to the spec_op */ + request.Param = data; + request.list = &list; + code = dev_proc(i_ctx_p->pgs->device, dev_spec_op)(i_ctx_p->pgs->device, gxdso_get_dev_param, &request, sizeof(dev_param_req_t)); + if (code < 0 && code != gs_error_undefined) { + gs_c_param_list_release(&list); + return code; + } + gs_c_param_list_read(&list); + code = param_read_bool((gs_param_list *)&list, + "AllowPSRepeatFunctions", + &AllowRepeat); + gs_c_param_list_release(&list); + if (code < 0) + return code; + } + + code = check_psc_function(i_ctx_p, proc, 0, NULL, &size, AllowRepeat); + if (code < 0) + goto fail; + ops = gs_alloc_string(mem, size + 1, "gs_build_function_4(ops)"); + if (ops == 0) { + code = gs_note_error(gs_error_VMerror); + goto fail; + } + size = 0; + check_psc_function(i_ctx_p, proc, 0, ops, &size, AllowRepeat); /* can't fail */ + ops[size] = PtCr_return; + params.ops.data = ops; + params.ops.size = size + 1; + code = gs_function_PtCr_init(ppfn, ¶ms, mem); + if (code >= 0) + return 0; + /* free_params will free the ops string */ +fail: + gs_function_PtCr_free_params(¶ms, mem); + return (code < 0 ? code : gs_note_error(gs_error_rangecheck)); +} + +int make_type4_function(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, gs_function_t **func) +{ + int code, size, num_components, CIESubst; + byte *ops; + gs_function_PtCr_params_t params; + float *ptr; + ref alternatespace, *palternatespace = &alternatespace; + PS_colour_space_t *space, *altspace; + int AllowRepeat = 1; /* Default to permitting Repeat, devices which can't handle it implement the spec_op */ + + code = get_space_object(i_ctx_p, arr, &space); + if (code < 0) + return code; + if (!space->alternateproc) + return gs_error_typecheck; + code = space->alternateproc(i_ctx_p, arr, &palternatespace, &CIESubst); + if (code < 0) + return code; + code = get_space_object(i_ctx_p, palternatespace, &altspace); + if (code < 0) + return code; + + code = space->numcomponents(i_ctx_p, arr, &num_components); + if (code < 0) + return code; + ptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_type4_function(Domain)"); + if (!ptr) + return gs_error_VMerror; + code = space->domain(i_ctx_p, arr, ptr); + if (code < 0) { + gs_free_const_object(imemory, ptr, "make_type4_function(Domain)"); + return code; + } + params.Domain = ptr; + params.m = num_components; + + code = altspace->numcomponents(i_ctx_p, &alternatespace, &num_components); + if (code < 0) { + gs_free_const_object(imemory, params.Domain, "make_type4_function(Domain)"); + return code; + } + ptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_type4_function(Range)"); + if (!ptr) { + gs_free_const_object(imemory, params.Domain, "make_type4_function(Domain)"); + return gs_error_VMerror; + } + code = altspace->range(i_ctx_p, &alternatespace, ptr); + if (code < 0) { + gs_free_const_object(imemory, ptr, "make_type4_function(Domain)"); + gs_free_const_object(imemory, params.Domain, "make_type4_function(Range)"); + return code; + } + params.Range = ptr; + params.n = num_components; + + params.ops.data = 0; /* in case of failure, see gs_function_PtCr_free_params */ + params.ops.size = 0; /* ditto */ + size = 0; + + /* Check if the device allows the use of repeat in functions */ + /* We can't handle 'repeat' with pdfwrite since it emits FunctionType 4 */ + { + char data[] = {"AllowPSRepeatFunctions"}; + dev_param_req_t request; + gs_c_param_list list; + + gs_c_param_list_write(&list, i_ctx_p->pgs->device->memory); + /* Stuff the data into a structure for passing to the spec_op */ + request.Param = data; + request.list = &list; + code = dev_proc(i_ctx_p->pgs->device, dev_spec_op)(i_ctx_p->pgs->device, gxdso_get_dev_param, &request, sizeof(dev_param_req_t)); + if (code < 0 && code != gs_error_undefined) { + gs_c_param_list_release(&list); + return code; + } + gs_c_param_list_read(&list); + code = param_read_bool((gs_param_list *)&list, + "AllowPSRepeatFunctions", + &AllowRepeat); + gs_c_param_list_release(&list); + if (code < 0) + return code; + } + + code = check_psc_function(i_ctx_p, (const ref *)pproc, 0, NULL, &size, AllowRepeat); + if (code < 0) { + gs_function_PtCr_free_params(¶ms, imemory); + return code; + } + ops = gs_alloc_string(imemory, size + 1, "make_type4_function(ops)"); + size = 0; + check_psc_function(i_ctx_p, (const ref *)pproc, 0, ops, &size, AllowRepeat); /* can't fail */ + ops[size] = PtCr_return; + params.ops.data = ops; + params.ops.size = size + 1; + code = gs_function_PtCr_init(func, ¶ms, imemory); + if (code < 0) + gs_function_PtCr_free_params(¶ms, imemory); + + return code; +} |