summaryrefslogtreecommitdiff
path: root/gs/src/gstype1.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/src/gstype1.c')
-rw-r--r--gs/src/gstype1.c588
1 files changed, 588 insertions, 0 deletions
diff --git a/gs/src/gstype1.c b/gs/src/gstype1.c
new file mode 100644
index 000000000..efae286e5
--- /dev/null
+++ b/gs/src/gstype1.c
@@ -0,0 +1,588 @@
+/* Copyright (C) 1990, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved.
+
+ This file is part of Aladdin Ghostscript.
+
+ Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author
+ or distributor accepts any responsibility for the consequences of using it,
+ or for whether it serves any particular purpose or works at all, unless he
+ or she says so in writing. Refer to the Aladdin Ghostscript Free Public
+ License (the "License") for full details.
+
+ Every copy of Aladdin Ghostscript must include a copy of the License,
+ normally in a plain ASCII text file named PUBLIC. The License grants you
+ the right to copy, modify and redistribute Aladdin Ghostscript, but only
+ under certain conditions described in the License. Among other things, the
+ License requires that the copyright notice and this notice be preserved on
+ all copies.
+*/
+
+/* gstype1.c */
+/* Adobe Type 1 charstring interpreter */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxcoord.h"
+#include "gxistate.h"
+#include "gzpath.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+
+/*
+ * Define whether to always do Flex segments as curves.
+ * This is only an issue because some old Adobe DPS fonts
+ * seem to violate the Flex specification in a way that requires this.
+ * We changed this from 1 to 0 in release 5.02: if it causes any
+ * problems, we'll implement a more sophisticated test.
+ */
+#define ALWAYS_DO_FLEX_AS_CURVE 0
+
+/* ------ Main interpreter ------ */
+
+/* Define a pointer to the charstring interpreter stack. */
+typedef fixed _ss *cs_ptr;
+
+/*
+ * Continue interpreting a Type 1 charstring. If str != 0, it is taken as
+ * the byte string to interpret. Return 0 on successful completion, <0 on
+ * error, or >0 when client intervention is required (or allowed). The int*
+ * argument is where the othersubr # is stored for callothersubr.
+ */
+private int
+gs_type1_charstring_interpret(gs_type1_state *pcis,
+ const gs_const_string *str, int *pindex)
+{ gs_font_type1 *pfont = pcis->pfont;
+ gs_type1_data *pdata = &pfont->data;
+ bool encrypted = pdata->lenIV >= 0;
+ gs_op1_state s;
+ fixed cstack[ostack_size];
+#define cs0 cstack[0]
+#define ics0 fixed2int_var(cs0)
+#define cs1 cstack[1]
+#define ics1 fixed2int_var(cs1)
+#define cs2 cstack[2]
+#define ics2 fixed2int_var(cs2)
+#define cs3 cstack[3]
+#define ics3 fixed2int_var(cs3)
+#define cs4 cstack[4]
+#define ics4 fixed2int_var(cs4)
+#define cs5 cstack[5]
+#define ics5 fixed2int_var(cs5)
+ cs_ptr csp;
+#define clear csp = cstack - 1
+ ip_state *ipsp = &pcis->ipstack[pcis->ips_count - 1];
+ register const byte *cip;
+ register crypt_state state;
+ register int c;
+ int code = 0;
+ fixed ftx = pcis->origin.x, fty = pcis->origin.y;
+
+ switch ( pcis->init_done )
+ {
+ case -1:
+ break;
+ case 0:
+ gs_type1_finish_init(pcis, &s); /* sets sfc, ptx, pty, origin */
+ ftx = pcis->origin.x, fty = pcis->origin.y;
+ break;
+ default /*case 1*/:
+ ptx = pcis->position.x;
+ pty = pcis->position.y;
+ sfc = pcis->fc;
+ }
+ sppath = pcis->path;
+ s.pcis = pcis;
+ init_cstack(cstack, csp, pcis);
+
+ if ( str == 0 )
+ goto cont;
+ ipsp->char_string = *str;
+ cip = str->data;
+call: state = crypt_charstring_seed;
+ if ( encrypted )
+ { int skip = pdata->lenIV;
+ /* Skip initial random bytes */
+ for ( ; skip > 0; ++cip, --skip )
+ decrypt_skip_next(*cip, state);
+ }
+ goto top;
+cont: cip = ipsp->ip;
+ state = ipsp->dstate;
+top: for ( ; ; )
+ { uint c0 = *cip++;
+
+ charstring_next(c0, state, c, encrypted);
+ if ( c >= c_num1 )
+ {
+ /* This is a number, decode it and push it on the stack. */
+
+ if ( c < c_pos2_0 )
+ { /* 1-byte number */
+ decode_push_num1(csp, c);
+ }
+ else if ( c < cx_num4 )
+ { /* 2-byte number */
+ decode_push_num2(csp, c, cip, state, encrypted);
+ }
+ else if ( c == cx_num4 )
+ { /* 4-byte number */
+ long lw;
+
+ decode_num4(lw, cip, state, encrypted);
+ *++csp = int2fixed(lw);
+ if ( lw != fixed2long(*csp) )
+ return_error(gs_error_rangecheck);
+ }
+ else /* not possible */
+ return_error(gs_error_invalidfont);
+pushed: if_debug3('1', "[1]%d: (%d) %f\n",
+ (int)(csp - cstack), c, fixed2float(*csp));
+ continue;
+ }
+
+#ifdef DEBUG
+ if ( gs_debug['1'] )
+ { static const char *c1names[] =
+ { char1_command_names };
+ if ( c1names[c] == 0 )
+ dprintf2("[1]0x%lx: %02x??\n", (ulong)(cip - 1), c);
+ else
+ dprintf3("[1]0x%lx: %02x %s\n", (ulong)(cip - 1), c,
+ c1names[c]);
+ }
+#endif
+ switch ( (char_command)c )
+ {
+#define cnext clear; goto top
+#define inext goto top
+
+ /* Commands with identical functions in Type 1 and Type 2, */
+ /* except for 'escape'. */
+
+ case c_undef0:
+ case c_undef2:
+ case c_undef17:
+ return_error(gs_error_invalidfont);
+ case c_callsubr:
+ c = fixed2int_var(*csp) + pdata->subroutineNumberBias;
+ code = (*pdata->subr_proc)
+ (pfont, c, false, &ipsp[1].char_string);
+ if ( code < 0 )
+ return_error(code);
+ --csp;
+ ipsp->ip = cip, ipsp->dstate = state;
+ ++ipsp;
+ cip = ipsp->char_string.data;
+ goto call;
+ case c_return:
+ --ipsp;
+ goto cont;
+ case c_undoc15:
+ /* See gstype1.h for information on this opcode. */
+ cnext;
+
+ /* Commands with similar but not identical functions */
+ /* in Type 1 and Type 2 charstrings. */
+
+ case cx_hstem:
+ apply_path_hints(pcis, false);
+ type1_hstem(pcis, cs0, cs1);
+ cnext;
+ case cx_vstem:
+ apply_path_hints(pcis, false);
+ type1_vstem(pcis, cs0, cs1);
+ cnext;
+ case cx_vmoveto:
+ cs1 = cs0;
+ cs0 = 0;
+ accum_y(cs1);
+move: /* cs0 = dx, cs1 = dy for hint checking. */
+ if ( (pcis->hint_next != 0 || path_is_drawing(sppath)) &&
+ pcis->flex_count == flex_max
+ )
+ apply_path_hints(pcis, true);
+ code = gx_path_add_point(sppath, ptx, pty);
+ goto cc;
+ case cx_rlineto:
+ accum_xy(cs0, cs1);
+line: /* cs0 = dx, cs1 = dy for hint checking. */
+ code = gx_path_add_line(sppath, ptx, pty);
+cc: if ( code < 0 )
+ return code;
+pp: if_debug2('1', "[1]pt=(%g,%g)\n",
+ fixed2float(ptx), fixed2float(pty));
+ cnext;
+ case cx_hlineto:
+ accum_x(cs0);
+ cs1 = 0;
+ goto line;
+ case cx_vlineto:
+ cs1 = cs0;
+ cs0 = 0;
+ accum_y(cs1);
+ goto line;
+ case cx_rrcurveto:
+ code = gs_op1_rrcurveto(&s, cs0, cs1, cs2, cs3, cs4, cs5);
+ goto cc;
+ case cx_endchar:
+ if ( pcis->seac_base >= 0 )
+ { /* We just finished the accent of a seac. */
+ /* Do the base character. */
+ gs_const_string bstr;
+ int bchar = pcis->seac_base;
+ pcis->seac_base = -1;
+ /* Restore the coordinate system origin */
+ pcis->asb_diff = pcis->adxy.x = pcis->adxy.y = 0;
+ sppath->position.x = pcis->position.x = ftx;
+ sppath->position.y = pcis->position.y = fty;
+ pcis->os_count = 0; /* clear */
+ /* Clear the ipstack, in case the accent ended */
+ /* inside a subroutine. */
+ pcis->ips_count = 1;
+ /* Remove any accent hints. */
+ reset_stem_hints(pcis);
+ /* Ask the caller to provide a new string. */
+ code = (*pdata->seac_proc)(pfont, bchar, &bstr);
+ if ( code != 0 )
+ { *pindex = bchar;
+ return code;
+ }
+ /* Continue with the supplied string. */
+ clear;
+ ptx = ftx, pty = fty;
+ ipsp = &pcis->ipstack[0];
+ ipsp->char_string = bstr;
+ cip = bstr.data;
+ goto call;
+ }
+ /* This is a real endchar. Handle it below. */
+ return gs_type1_endchar(pcis);
+ case cx_rmoveto:
+ accum_xy(cs0, cs1);
+ goto move;
+ case cx_hmoveto:
+ accum_x(cs0);
+ cs1 = 0;
+ goto move;
+ case cx_vhcurveto:
+ { gs_fixed_point pt1, pt2;
+ fixed ax0 = sppath->position.x - ptx;
+ fixed ay0 = sppath->position.y - pty;
+ accum_y(cs0);
+ pt1.x = ptx + ax0, pt1.y = pty + ay0;
+ accum_xy(cs1, cs2);
+ pt2.x = ptx, pt2.y = pty;
+ accum_x(cs3);
+ code = gx_path_add_curve(sppath, pt1.x, pt1.y, pt2.x, pt2.y, ptx, pty);
+ } goto cc;
+ case cx_hvcurveto:
+ { gs_fixed_point pt1, pt2;
+ fixed ax0 = sppath->position.x - ptx;
+ fixed ay0 = sppath->position.y - pty;
+ accum_x(cs0);
+ pt1.x = ptx + ax0, pt1.y = pty + ay0;
+ accum_xy(cs1, cs2);
+ pt2.x = ptx, pt2.y = pty;
+ accum_y(cs3);
+ code = gx_path_add_curve(sppath, pt1.x, pt1.y, pt2.x, pt2.y, ptx, pty);
+ } goto cc;
+
+ /* Commands only recognized in Type 1 charstrings, */
+ /* plus 'escape'. */
+
+ case c1_closepath:
+ code = gs_op1_closepath(&s);
+ apply_path_hints(pcis, true);
+ goto cc;
+ case c1_hsbw:
+ gs_type1_sbw(pcis, cs0, fixed_0, cs1, fixed_0);
+rsbw: /* Give the caller the opportunity to intervene. */
+ pcis->os_count = 0; /* clear */
+ ipsp->ip = cip, ipsp->dstate = state;
+ pcis->ips_count = ipsp - &pcis->ipstack[0] + 1;
+ /* If we aren't in a seac, do nothing else now; */
+ /* finish_init will take care of the rest. */
+ if ( pcis->init_done < 0 )
+ { /* Finish init when we return. */
+ pcis->init_done = 0;
+ }
+ else
+ { /* Do accumulate the side bearing now. */
+ accum_xy(pcis->lsb.x, pcis->lsb.y);
+ pcis->position.x = ptx;
+ pcis->position.y = pty;
+ }
+ return type1_result_sbw;
+ case cx_escape:
+ charstring_next(*cip, state, c, encrypted); ++cip;
+#ifdef DEBUG
+ if ( gs_debug['1'] && c < char1_extended_command_count )
+ { static const char *ce1names[] =
+ { char1_extended_command_names };
+ if ( ce1names[c] == 0 )
+ dprintf2("[1]0x%lx: %02x??\n", (ulong)(cip - 1), c);
+ else
+ dprintf3("[1]0x%lx: %02x %s\n", (ulong)(cip - 1), c,
+ ce1names[c]);
+ }
+#endif
+ switch ( (char1_extended_command)c )
+ {
+ case ce1_dotsection:
+ pcis->dotsection_flag ^=
+ (dotsection_in ^ dotsection_out);
+ cnext;
+ case ce1_vstem3:
+ apply_path_hints(pcis, false);
+ if ( !pcis->vstem3_set && pcis->fh.use_x_hints )
+ { center_vstem(pcis, pcis->lsb.x + cs2, cs3);
+ /* Adjust the current point */
+ /* (center_vstem handles everything else). */
+ ptx += pcis->vs_offset.x;
+ pty += pcis->vs_offset.y;
+ pcis->vstem3_set = true;
+ }
+ type1_vstem(pcis, cs0, cs1);
+ type1_vstem(pcis, cs2, cs3);
+ type1_vstem(pcis, cs4, cs5);
+ cnext;
+ case ce1_hstem3:
+ apply_path_hints(pcis, false);
+ type1_hstem(pcis, cs0, cs1);
+ type1_hstem(pcis, cs2, cs3);
+ type1_hstem(pcis, cs4, cs5);
+ cnext;
+ case ce1_seac:
+ { gs_const_string acstr;
+ /* Do the accent now. When it finishes */
+ /* (detected in endchar), do the base character. */
+ pcis->seac_base = ics3;
+ /* Adjust the origin of the coordinate system */
+ /* for the accent (endchar puts it back). */
+ ptx = ftx, pty = fty;
+ pcis->asb_diff = cs0 - pcis->lsb.x;
+ pcis->adxy.x = cs1;
+ pcis->adxy.y = cs2;
+ accum_xy(cs1, cs2);
+ sppath->position.x = pcis->position.x = ptx;
+ sppath->position.y = pcis->position.y = pty;
+ pcis->os_count = 0; /* clear */
+ /* Ask the caller to provide a new string. */
+ code = (*pdata->seac_proc)(pfont, ics4, &acstr);
+ if ( code != 0 )
+ { *pindex = ics4;
+ return code;
+ }
+ /* Continue with the supplied string. */
+ clear;
+ ipsp->char_string = acstr;
+ cip = acstr.data;
+ goto call;
+ }
+ case ce1_sbw:
+ gs_type1_sbw(pcis, cs0, cs1, cs2, cs3);
+ goto rsbw;
+ case ce1_div:
+ csp[-1] = float2fixed((float)csp[-1] / (float)*csp);
+ --csp; goto pushed;
+ case ce1_undoc15:
+ /* See gstype1.h for information on this opcode. */
+ cnext;
+ case ce1_callothersubr:
+ { int num_results;
+#define fpts pcis->flex_points
+ /* We must remember to pop both the othersubr # */
+ /* and the argument count off the stack. */
+ switch ( *pindex = fixed2int_var(*csp) )
+ {
+ case 0:
+ { /* We have to do something really sleazy */
+ /* here, namely, make it look as though */
+ /* the rmovetos never really happened, */
+ /* because we don't want to interrupt */
+ /* the current subpath. */
+ gs_fixed_point ept;
+#if defined(DEBUG) || !ALWAYS_DO_FLEX_AS_CURVE
+ fixed fheight = csp[-4];
+ gs_fixed_point hpt;
+#endif
+
+ if ( pcis->flex_count != 8 )
+ return_error(gs_error_invalidfont);
+ /* Assume the next two opcodes */
+ /* are `pop' `pop'. Unfortunately, some */
+ /* Monotype fonts put these in a Subr, */
+ /* so we can't just look ahead in the */
+ /* opcode stream. */
+ pcis->ignore_pops = 2;
+ csp[-4] = csp[-3] - pcis->asb_diff;
+ csp[-3] = csp[-2];
+ csp -= 3;
+ gx_path_current_point(sppath, &ept);
+ gx_path_add_point(sppath, fpts[0].x, fpts[0].y);
+ sppath->state_flags = /* <--- sleaze */
+ pcis->flex_path_state_flags;
+#if defined(DEBUG) || !ALWAYS_DO_FLEX_AS_CURVE
+ /* Decide whether to do the flex as a curve. */
+ hpt.x = fpts[1].x - fpts[4].x;
+ hpt.y = fpts[1].y - fpts[4].y;
+ if_debug3('1',
+ "[1]flex: d=(%g,%g), height=%g\n",
+ fixed2float(hpt.x), fixed2float(hpt.y),
+ fixed2float(fheight) / 100);
+#endif
+#if !ALWAYS_DO_FLEX_AS_CURVE /* See beginning of file. */
+ if ( any_abs(hpt.x) + any_abs(hpt.y) <
+ fheight / 100
+ )
+ { /* Do the flex as a line. */
+ code = gx_path_add_line(sppath,
+ ept.x, ept.y);
+ }
+ else
+#endif
+ { /* Do the flex as a curve. */
+ code = gx_path_add_curve(sppath,
+ fpts[2].x, fpts[2].y,
+ fpts[3].x, fpts[3].y,
+ fpts[4].x, fpts[4].y);
+ if ( code < 0 )
+ return code;
+ code = gx_path_add_curve(sppath,
+ fpts[5].x, fpts[5].y,
+ fpts[6].x, fpts[6].y,
+ fpts[7].x, fpts[7].y);
+ }
+ }
+ if ( code < 0 )
+ return code;
+ pcis->flex_count = flex_max; /* not inside flex */
+ inext;
+ case 1:
+ gx_path_current_point(sppath, &fpts[0]);
+ pcis->flex_path_state_flags = /* <--- more sleaze */
+ sppath->state_flags;
+ pcis->flex_count = 1;
+ csp -= 2;
+ inext;
+ case 2:
+ if ( pcis->flex_count >= flex_max )
+ return_error(gs_error_invalidfont);
+ gx_path_current_point(sppath,
+ &fpts[pcis->flex_count++]);
+ csp -= 2;
+ inext;
+ case 3:
+ /* Assume the next opcode is a `pop'. */
+ /* See above as to why we don't just */
+ /* look ahead in the opcode stream. */
+ pcis->ignore_pops = 1;
+ replace_stem_hints(pcis);
+ csp -= 2;
+ inext;
+ case 14:
+ num_results = 1;
+blend: { int num_values = fixed2int_var(csp[-1]);
+ int k1 = num_values / num_results - 1;
+ int i, j;
+ cs_ptr base, deltas;
+
+ if ( num_values < num_results ||
+ num_values % num_results != 0
+ )
+ return_error(gs_error_invalidfont);
+ base = csp - 1 - num_values;
+ deltas = base + num_results - 1;
+ for ( j = 0; j < num_results;
+ j++, base++, deltas += k1
+ )
+ for ( i = 1; i <= k1; i++ )
+ *base += deltas[i] *
+ pdata->WeightVector.values[i];
+ csp = base - 1;
+ }
+ pcis->ignore_pops = num_results;
+ inext;
+ case 15:
+ num_results = 2;
+ goto blend;
+ case 16:
+ num_results = 3;
+ goto blend;
+ case 17:
+ num_results = 4;
+ goto blend;
+ case 18:
+ num_results = 6;
+ goto blend;
+ }
+ }
+#undef fpts
+ /* Not a recognized othersubr, */
+ /* let the client handle it. */
+ { int scount = csp - cstack;
+ int n;
+ /* Copy the arguments to the caller's stack. */
+ if ( scount < 1 || csp[-1] < 0 ||
+ csp[-1] > int2fixed(scount - 1)
+ )
+ return_error(gs_error_invalidfont);
+ n = fixed2int_var(csp[-1]);
+ code = (*pdata->push_proc)(pfont, csp - (n + 1), n);
+ if ( code < 0 )
+ return_error(code);
+ scount -= n + 1;
+ pcis->position.x = ptx;
+ pcis->position.y = pty;
+ apply_path_hints(pcis, false);
+ /* Exit to caller */
+ ipsp->ip = cip, ipsp->dstate = state;
+ pcis->os_count = scount;
+ pcis->ips_count = ipsp - &pcis->ipstack[0] + 1;
+ if ( scount )
+ memcpy(pcis->ostack, cstack, scount * sizeof(fixed));
+ return type1_result_callothersubr;
+ }
+ case ce1_pop:
+ /* Check whether we're ignoring the pops after */
+ /* a known othersubr. */
+ if ( pcis->ignore_pops != 0 )
+ { pcis->ignore_pops--;
+ inext;
+ }
+ ++csp;
+ code = (*pdata->pop_proc)(pfont, csp);
+ if ( code < 0 )
+ return_error(code);
+ goto pushed;
+ case ce1_setcurrentpoint:
+ ptx = ftx, pty = fty;
+ cs0 += pcis->adxy.x;
+ cs1 += pcis->adxy.y;
+ accum_xy(cs0, cs1);
+ goto pp;
+ default:
+ return_error(gs_error_invalidfont);
+ }
+ /*NOTREACHED*/
+
+ /* Fill up the dispatch up to 32. */
+
+ case_c1_undefs:
+ default: /* pacify compiler */
+ return_error(gs_error_invalidfont);
+ }
+ }
+}
+
+/* Register the interpreter. */
+void
+gs_gstype1_init(gs_memory_t *mem)
+{ gs_charstring_interpreter[1] = gs_type1_charstring_interpret;
+}