summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipdfm-x/t1_char.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvipdfm-x/t1_char.c')
-rw-r--r--Build/source/texk/dvipdfm-x/t1_char.c1590
1 files changed, 1590 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm-x/t1_char.c b/Build/source/texk/dvipdfm-x/t1_char.c
new file mode 100644
index 00000000000..7b6788870bd
--- /dev/null
+++ b/Build/source/texk/dvipdfm-x/t1_char.c
@@ -0,0 +1,1590 @@
+/*
+
+ This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
+
+ Copyright (C) 2002-2012 by Jin-Hwan Cho and Shunsaku Hirata,
+ the dvipdfmx project team.
+
+ Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
+
+ 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 USA.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <math.h>
+
+#include "system.h"
+#include "mem.h"
+#include "error.h"
+#include "numbers.h"
+
+#include "pdfobj.h"
+#include "pdffont.h"
+
+#include "pdfencoding.h"
+#include "unicode.h"
+
+#include "dpxutil.h"
+
+#include "pst_obj.h"
+#include "pst.h"
+
+#include "cff_limits.h"
+#include "cff_types.h"
+#include "cff_dict.h"
+#include "cff.h"
+
+#include "t1_char.h"
+
+/*
+ * Type 1 -> Type 1C
+ */
+
+/* Charstring decoder/encoder status codes */
+#define CS_OP_NOSUPPORT -4
+#define CS_BUFFER_ERROR -3
+#define CS_STACK_ERROR -2
+#define CS_PARSE_ERROR -1
+#define CS_PARSE_OK 0
+#define CS_PARSE_END 1
+#define CS_SUBR_RETURN 2
+#define CS_CHAR_END 3
+
+static int status = CS_PARSE_ERROR;
+
+#define DST_NEED(a,b) {if ((a) < (b)) { status = CS_BUFFER_ERROR ; return ; }}
+#define SRC_NEED(a,b) {if ((a) < (b)) { status = CS_PARSE_ERROR ; return ; }}
+#define NEED(a,b) {if ((a) < (b)) { status = CS_STACK_ERROR ; return ; }}
+
+#define T1_CS_PHASE_INIT 0
+#define T1_CS_PHASE_HINT 1
+#define T1_CS_PHASE_PATH 2
+#define T1_CS_PHASE_FLEX 3
+
+static int phase = -1;
+static int nest = -1;
+
+#ifndef CS_STEM_ZONE_MAX
+#define CS_STEM_ZONE_MAX 96
+#endif
+#ifndef CS_STEM_GROUP_MAX
+#define CS_STEM_GROUP_MAX CS_STEM_ZONE_MAX
+#endif
+#ifndef CS_ARG_STACK_MAX
+#define CS_ARG_STACK_MAX 48
+#endif
+#ifndef PS_ARG_STACK_MAX
+/*
+ * Counter control may have CS_STEM_ZONE_MAX*2+2 arguments.
+ */
+#define PS_ARG_STACK_MAX (CS_STEM_ZONE_MAX*2+2)
+#endif
+
+typedef struct t1_cpath {
+ int type;
+ int num_args;
+ double args[CS_ARG_STACK_MAX];
+ struct t1_cpath *next;
+} t1_cpath;
+
+#define HSTEM 0
+#define VSTEM 1
+typedef struct {
+ int id;
+ int dir;
+ double pos, del;
+} t1_stem;
+
+typedef struct {
+ int num_stems;
+ double stems[CS_STEM_ZONE_MAX]; /* int */
+} t1_stemgroup;
+
+#define T1_CS_FLAG_NONE 0
+#define T1_CS_FLAG_USE_HINTMASK (1 << 0)
+#define T1_CS_FLAG_USE_CNTRMASK (1 << 1)
+#define T1_CS_FLAG_USE_SEAC (1 << 2)
+
+typedef struct {
+ int flags;
+ struct {
+ double sbx, sby, wx, wy;
+ } sbw;
+ struct {
+ double llx, lly, urx, ury;
+ } bbox;
+ struct {
+ double asb, adx, ady;
+ card8 bchar, achar;
+ } seac;
+ int num_stems;
+ t1_stem stems[CS_STEM_ZONE_MAX];
+ t1_cpath *charpath;
+ t1_cpath *lastpath;
+} t1_chardesc;
+
+static int cs_stack_top = 0;
+static int ps_stack_top = 0;
+
+/* [vh]stem support require one more stack size. */
+static double cs_arg_stack[CS_ARG_STACK_MAX+1];
+static double ps_arg_stack[PS_ARG_STACK_MAX];
+
+#define CS_HINT_DECL -1
+#define CS_FLEX_CTRL -2
+#define CS_CNTR_CTRL -3
+
+/*
+ * Type 1/2 CharString encoding
+ */
+
+/*
+ * 1-byte CharString operaotrs:
+ * cs_escape is first byte of two-byte operator
+ */
+
+/* RESERVED 0 */
+#define cs_hstem 1
+/* RESERVED 2 */
+#define cs_vstem 3
+#define cs_vmoveto 4
+#define cs_rlineto 5
+#define cs_hlineto 6
+#define cs_vlineto 7
+#define cs_rrcurveto 8
+#define cs_closepath 9
+#define cs_callsubr 10
+#define cs_return 11
+#define cs_escape 12
+#define cs_hsbw 13
+#define cs_endchar 14
+/* RESERVED 15 */
+/* RESERVED 16 */
+/* RESERVED 17 */
+#define cs_hstemhm 18
+#define cs_hintmask 19
+#define cs_cntrmask 20
+#define cs_rmoveto 21
+#define cs_hmoveto 22
+#define cs_vstemhm 23
+#define cs_rcurveline 24
+#define cs_rlinecurve 25
+#define cs_vvcurveto 26
+#define cs_hhcurveto 27
+/* SHORTINT 28 : first byte of shortint*/
+#define cs_callgsubr 29
+#define cs_vhcurveto 30
+#define cs_hvcurveto 31
+
+/* 2-byte op. flex 34-37 used. */
+
+/*
+ * 2-byte CharString operaotrs:
+ * "dotsection" is obsoleted in Type 2 charstring.
+ */
+
+#define cs_dotsection 0
+#define cs_vstem3 1
+#define cs_hstem3 2
+#define cs_and 3
+#define cs_or 4
+#define cs_not 5
+#define cs_seac 6
+#define cs_sbw 7
+/* RESERVED 8 */
+#define cs_abs 9
+#define cs_add 10
+#define cs_sub 11
+#define cs_div 12
+/* RESERVED 13 */
+#define cs_neg 14
+#define cs_eq 15
+#define cs_callothersubr 16
+#define cs_pop 17
+#define cs_drop 18
+/* RESERVED 19 */
+#define cs_put 20
+#define cs_get 21
+#define cs_ifelse 22
+#define cs_random 23
+#define cs_mul 24
+/* RESERVED 25 */
+#define cs_sqrt 26
+#define cs_dup 27
+#define cs_exch 28
+#define cs_index 29
+#define cs_roll 30
+/* RESERVED 31 */
+/* RESERVED 32 */
+#define cs_setcurrentpoint 33
+#define cs_hflex 34
+#define cs_flex 35
+#define cs_hflex1 36
+#define cs_flex1 37
+
+#define IS_PATH_OPERATOR(o) (((o) >= cs_vmoveto && (o) <= cs_closepath) || \
+ ((o) >= cs_rmoveto && (o) <= cs_hvcurveto && \
+ (o) != cs_vstemhm && (o) != cs_callgsubr && (o) != 28)\
+ )
+
+/*
+ * Stem:
+ *
+ * 1. Stems must be sorted in the increasing bottom/left edge order.
+ * 2. The encoded values are all relative; The value x(y) of the first
+ * stem is relative to 0 for Type 2 charstring and is relative to
+ * the left(bottom) side-bearing for Type 1 charstring.
+ * 3. A width of -20(-21) specifies the top/right(bottom/left) edge
+ * of an edge hint in Type 2 charstring. But the width of 'ghost'
+ * hint in Type 1 charstring is positive with value 20 or 21.
+ * 4. The h(v)stemhm MUST be used instead of h(v)stem if charstring
+ * contains hintmask operator.
+ *
+ * TODO:
+ *
+ * Convert ghost hint to edge hint, Counter control for hstem3/vstem3.
+ */
+
+static int CDECL
+stem_compare (const void *v1, const void *v2)
+{
+ int cmp = 0;
+ const t1_stem *s1, *s2;
+
+ s1 = (const t1_stem *) v1;
+ s2 = (const t1_stem *) v2;
+ if (s1->dir == s2->dir) {
+ if (s1->pos == s2->pos) {
+ if (s1->del == s2->del)
+ cmp = 0;
+ else
+ cmp = (s1->del < s2->del) ? -1 : 1;
+ } else {
+ cmp = (s1->pos < s2->pos) ? -1 : 1;
+ }
+ } else {
+ cmp = (s1->dir == HSTEM) ? -1 : 1;
+ }
+
+ return cmp;
+}
+
+#define SORT_STEMS(cd) qsort((cd)->stems,(cd)->num_stems,sizeof(t1_stem),stem_compare)
+
+static int
+get_stem (t1_chardesc *cd, int stem_id)
+{
+ int i;
+
+ for (i = 0; i < cd->num_stems; i++) {
+ if (cd->stems[i].id == stem_id)
+ break;
+ }
+
+ return ((i < cd->num_stems) ? i : -1);
+}
+
+static int
+add_stem (t1_chardesc *cd, double pos, double del, int dir)
+{
+ int i;
+
+ ASSERT(cd);
+
+ pos += (dir == HSTEM) ? cd->sbw.sby : cd->sbw.sbx;
+ for (i = 0; i < cd->num_stems; i++) {
+ if (cd->stems[i].dir == dir &&
+ cd->stems[i].pos == pos &&
+ cd->stems[i].del == del)
+ break;
+ }
+ if (i == cd->num_stems) {
+ if (cd->num_stems == CS_STEM_ZONE_MAX)
+ return -1;
+ cd->stems[i].dir = dir;
+ cd->stems[i].pos = pos;
+ cd->stems[i].del = del;
+ cd->stems[i].id = cd->num_stems;
+ (cd->num_stems)++;
+ }
+
+ return cd->stems[i].id;
+}
+
+
+static void
+copy_args (double *args1, double *args2, int count)
+{
+ while (count-- > 0) {
+ *args1 = *args2;
+ args1++; args2++;
+ }
+}
+
+/*
+ * Stack:
+ */
+#define LIMITCHECK(n) do {\
+ if (cs_stack_top+(n) > CS_ARG_STACK_MAX) {\
+ status = CS_STACK_ERROR;\
+ return;\
+ }\
+ } while (0)
+#define CHECKSTACK(n) do {\
+ if (cs_stack_top < (n)) {\
+ status = CS_STACK_ERROR;\
+ return;\
+ }\
+ } while (0)
+#define CLEARSTACK() do {\
+ cs_stack_top = 0;\
+ } while (0)
+
+/*
+ * Path construction:
+ */
+/* Get operands from cs_arg_stack[] */
+static void
+add_charpath (t1_chardesc *cd, int type, double *argv, int argn)
+{
+ t1_cpath *p;
+
+ ASSERT(cd);
+ ASSERT(argn <= CS_ARG_STACK_MAX);
+
+ p = NEW(1, t1_cpath);
+ p->type = type;
+ p->num_args = argn;
+ p->next = NULL;
+
+ while (argn-- > 0)
+ p->args[argn] = argv[argn];
+
+ if (!cd->charpath)
+ cd->charpath = p;
+ if (cd->lastpath)
+ cd->lastpath->next = p;
+ cd->lastpath = p;
+
+ if (type >= 0 &&
+ phase != T1_CS_PHASE_FLEX && IS_PATH_OPERATOR(type))
+ phase = T1_CS_PHASE_PATH;
+}
+
+static void
+init_charpath (t1_chardesc *cd)
+{
+ cd->flags = T1_CS_FLAG_NONE;
+ cd->num_stems = 0;
+ cd->sbw.wx = cd->sbw.wy = 0.0;
+ cd->sbw.sbx = cd->sbw.sby = 0.0;
+ cd->bbox.llx = cd->bbox.lly = cd->bbox.urx = cd->bbox.ury = 0.0;
+ cd->charpath = cd->lastpath = NULL;
+}
+
+static void
+release_charpath (t1_chardesc *cd)
+{
+ t1_cpath *curr, *next;
+
+ ASSERT(cd);
+
+ curr = cd->charpath;
+ while (curr != NULL) {
+ next = curr->next;
+ RELEASE(curr);
+ curr = next;
+ }
+
+ cd->charpath = cd->lastpath = NULL;
+}
+
+/*
+ * Type 1 charstring operators:
+ */
+#define ADD_PATH(p,t,n) add_charpath((p),(t),&(cs_arg_stack[cs_stack_top-(n)]),(n))
+
+/*
+ * Single byte operators:
+ */
+static void
+do_operator1 (t1_chardesc *cd, card8 **data, card8 *endptr)
+{
+ card8 op = **data;
+
+ *data += 1;
+
+ switch (op) {
+ case cs_closepath:
+ /*
+ * From T1 spec.:
+ * Note that, unlike the closepath command in the PostScript language,
+ * this command does not reposition the current point. Any subsequent
+ * rmoveto must be relative to the current point in force before the
+ * Type 1 font format closepath command was given.
+ */
+ /* noop */
+ CLEARSTACK();
+ break;
+ case cs_hsbw:
+ CHECKSTACK(2);
+ cd->sbw.wx = cs_arg_stack[--cs_stack_top];
+ cd->sbw.wy = 0;
+ cd->sbw.sbx = cs_arg_stack[--cs_stack_top];
+ cd->sbw.sby = 0;
+ CLEARSTACK();
+ /* hsbw does NOT set currentpoint. */
+ break;
+ case cs_hstem:
+ case cs_vstem:
+ CHECKSTACK(2);
+ {
+ int stem_id;
+ stem_id = add_stem(cd,
+ cs_arg_stack[cs_stack_top-2],
+ cs_arg_stack[cs_stack_top-1],
+ ((op == cs_hstem) ? HSTEM : VSTEM));
+ if (stem_id < 0) {
+ WARN("Too many hints...");
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ /* Put stem_id onto the stack... */
+ cs_arg_stack[cs_stack_top++] = stem_id;
+ ADD_PATH(cd, CS_HINT_DECL, 1);
+ }
+ CLEARSTACK();
+ break;
+ case cs_rmoveto:
+ /*
+ * Reference point is (0, 0) in Type 2 charstring.
+ */
+ CHECKSTACK(2);
+ {
+ if (phase < T1_CS_PHASE_PATH) {
+ cs_arg_stack[cs_stack_top-2] += cd->sbw.sbx;
+ cs_arg_stack[cs_stack_top-1] += cd->sbw.sby;
+ }
+ ADD_PATH(cd, op, 2);
+ }
+ CLEARSTACK();
+ break;
+ case cs_hmoveto:
+ case cs_vmoveto:
+ CHECKSTACK(1);
+ {
+ int argn = 1;
+ if (phase < T1_CS_PHASE_PATH) {
+ /*
+ * The reference point for the first moveto operator is diferrent
+ * between Type 1 charstring and Type 2 charstring. We compensate it.
+ */
+ if (op == cs_hmoveto) {
+ cs_arg_stack[cs_stack_top-1] += cd->sbw.sbx;
+ if (cd->sbw.sby != 0.0) {
+ cs_arg_stack[cs_stack_top++] = cd->sbw.sby;
+ argn = 2;
+ op = cs_rmoveto;
+ }
+ } else {
+ cs_arg_stack[cs_stack_top-1] += cd->sbw.sby;
+ if (cd->sbw.sbx != 0.0) {
+ cs_arg_stack[cs_stack_top] = cs_arg_stack[cs_stack_top-1];
+ cs_arg_stack[cs_stack_top-1] = cd->sbw.sbx;
+ cs_stack_top++;
+ argn = 2;
+ op = cs_rmoveto;
+ }
+ }
+ }
+ ADD_PATH(cd, op, argn);
+ }
+ CLEARSTACK();
+ break;
+ case cs_endchar:
+ status = CS_CHAR_END;
+ CLEARSTACK();
+ break;
+ /* above oprators are candidate for first stack-clearing operator */
+ case cs_rlineto:
+ CHECKSTACK(2);
+ ADD_PATH(cd, op, 2);
+ CLEARSTACK();
+ break;
+ case cs_hlineto:
+ case cs_vlineto:
+ CHECKSTACK(1);
+ ADD_PATH(cd, op, 1);
+ CLEARSTACK();
+ break;
+ case cs_rrcurveto:
+ CHECKSTACK(6);
+ ADD_PATH(cd, op, 6);
+ CLEARSTACK();
+ break;
+ case cs_vhcurveto:
+ case cs_hvcurveto:
+ CHECKSTACK(4);
+ ADD_PATH(cd, op, 4);
+ CLEARSTACK();
+ break;
+ /* all operotors above are stack-clearing operator */
+ /* no output */
+ case cs_return:
+ break;
+ case cs_callsubr:
+ ERROR("Unexpected callsubr.");
+ break;
+ default:
+ /* no-op ? */
+ WARN("Unknown charstring operator: 0x%02x", op);
+ status = CS_PARSE_ERROR;
+ break;
+ }
+
+ return;
+}
+
+/*
+ * OtherSubrs:
+ *
+ * arg0 arg1 ... argn n othersubr# callothersubr
+ *
+ * 0: Build flex:
+ * fd x y 3 0 callothersubr
+ * Othersubr #0 pushes x and y to PostScript interpreter operand stack.
+ * 1: Start flex
+ * 0 1 callothersubr
+ * 2: Mark flex control points
+ * 0 2 callothersubr
+ * 3: Discard hint
+ * subr# 1 3 callothersubr pop callsubr
+ * Subroutine subr# (only) contains stem declaration.
+ * Othersubr #3 pushes subr# to PostScript interpreter operand stack.
+ * 12: Counter control
+ * A subr to avoid stack overflow.
+ * 13: Counter control
+ */
+
+/*
+ * Convert six control points marked as CS_FLEX_CTRL to a flex path.
+ */
+static void
+do_othersubr0 (t1_chardesc *cd)
+{
+ t1_cpath *flex, *cur, *next;
+
+ if (ps_stack_top < 1) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+
+ /* Seek first CS_FLEX_CTRL mark */
+ for (cur = cd->charpath; cur != NULL && cur->type != CS_FLEX_CTRL; cur = cur->next);
+ flex = cur;
+ {
+ int i;
+ cur = cur->next;
+ for (i = 1; i < 7; i++) {
+ if (cur == NULL || cur->type != CS_FLEX_CTRL ||
+ cur->num_args != 2) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ if (i == 1) {
+ flex->args[0] += cur->args[0];
+ flex->args[1] += cur->args[1];
+ } else {
+ copy_args(&(flex->args[2*i-2]), cur->args, 2);
+ }
+ next = cur->next;
+ RELEASE(cur);
+ cur = next;
+ }
+ }
+ if (cur != NULL) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ /*
+ * Now 'flex' have all six control points, the first pair is relative
+ * from starting point.
+ */
+ flex->type = cs_flex;
+ flex->args[12] = ps_arg_stack[--ps_stack_top]; /* flex depth */
+ flex->num_args = 13;
+ flex->next = NULL;
+ cd->lastpath = flex;
+
+ phase = T1_CS_PHASE_PATH;
+}
+
+/* Start flex */
+static void
+do_othersubr1 (t1_chardesc *cd)
+{
+ phase = T1_CS_PHASE_FLEX;
+}
+
+/* Mark flex control point */
+static void
+do_othersubr2 (t1_chardesc *cd)
+{
+ if (phase != T1_CS_PHASE_FLEX || !cd->lastpath) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+
+ switch (cd->lastpath->type) {
+ case cs_rmoveto:
+ break;
+ case cs_hmoveto:
+ cd->lastpath->num_args = 2;
+ cd->lastpath->args[1] = 0.0;
+ break;
+ case cs_vmoveto:
+ cd->lastpath->num_args = 2;
+ cd->lastpath->args[1] = cd->lastpath->args[0];
+ cd->lastpath->args[0] = 0.0;
+ break;
+ default:
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ cd->lastpath->type = CS_FLEX_CTRL;
+}
+
+/*
+ * Hint Replacement:
+ * "Adobe Type 1 Font Format", Chapter 8.
+ */
+static void
+do_othersubr3 (t1_chardesc *cd)
+{
+ cd->flags |= T1_CS_FLAG_USE_HINTMASK;
+}
+
+static void
+do_othersubr12 (t1_chardesc *cd)
+{
+ /* Othersubr12 call must immediately follow the hsbw or sbw. */
+ if (phase != T1_CS_PHASE_INIT) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ /* noop */
+}
+
+static void
+do_othersubr13 (t1_chardesc *cd)
+{
+ t1_stemgroup stemgroups[CS_STEM_GROUP_MAX];
+ int num_hgroups, num_vgroups, n, stem_id;
+ double pos, del;
+
+ /* After #12 callothersubr or hsbw or sbw. */
+ if (phase != T1_CS_PHASE_INIT) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ for (n = 0; n < CS_STEM_GROUP_MAX; n++) {
+ stemgroups[n].num_stems = 0;
+ }
+
+ num_hgroups = (int) ps_arg_stack[--ps_stack_top];
+ if (num_hgroups < 0 || num_hgroups > CS_STEM_GROUP_MAX) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ n = 0; pos = 0.0;
+ while (ps_stack_top >= 2 && n < num_hgroups) {
+ /* add_stem() add sidebearing */
+ pos += ps_arg_stack[--ps_stack_top];
+ del = ps_arg_stack[--ps_stack_top];
+ stem_id = add_stem(cd,
+ (del < 0.0) ? pos + del : pos,
+ (del < 0.0) ? -del : del,
+ HSTEM);
+ stemgroups[n].stems[stemgroups[n].num_stems] = stem_id;
+ stemgroups[n].num_stems += 1;
+ pos += del;
+ if (del < 0.0) {
+ pos = 0.0;
+ n++;
+ }
+ }
+ if (n != num_hgroups) {
+ status = CS_STACK_ERROR;
+ return;
+ }
+
+ num_vgroups = (int) ps_arg_stack[--ps_stack_top];
+ if (num_vgroups < 0 || num_vgroups > CS_STEM_GROUP_MAX) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ n = 0; pos = 0.0;
+ while (ps_stack_top >= 2 && n < num_vgroups) {
+ /* add_stem() add sidebearing */
+ pos += ps_arg_stack[--ps_stack_top];
+ del = ps_arg_stack[--ps_stack_top];
+ stem_id = add_stem(cd,
+ (del < 0.0) ? pos + del : pos,
+ (del < 0.0) ? -del : del,
+ VSTEM);
+ stemgroups[n].stems[stemgroups[n].num_stems] = stem_id;
+ stemgroups[n].num_stems += 1;
+ pos += del;
+ if (del < 0.0) {
+ pos = 0.0;
+ n++;
+ }
+ }
+ if (n != num_vgroups) {
+ status = CS_STACK_ERROR;
+ return;
+ }
+
+ for (n = 0; n < MAX(num_hgroups, num_vgroups); n++) {
+ add_charpath(cd, cs_cntrmask,
+ stemgroups[n].stems, stemgroups[n].num_stems);
+ }
+
+ cd->flags |= T1_CS_FLAG_USE_CNTRMASK;
+}
+
+static void
+do_callothersubr (t1_chardesc *cd)
+{
+ int argn, subrno;
+
+ CHECKSTACK(2);
+ subrno = (int) cs_arg_stack[--cs_stack_top];
+ argn = (int) cs_arg_stack[--cs_stack_top];
+
+ CHECKSTACK(argn);
+ if (ps_stack_top+argn > PS_ARG_STACK_MAX) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ while (argn-- > 0)
+ ps_arg_stack[ps_stack_top++] = cs_arg_stack[--cs_stack_top];
+
+ switch (subrno) {
+ case 0: do_othersubr0(cd) ; break;
+ case 1: do_othersubr1(cd) ; break;
+ case 2: do_othersubr2(cd) ; break;
+ case 3: do_othersubr3(cd) ; break;
+ case 12: do_othersubr12(cd); break;
+ case 13: do_othersubr13(cd); break;
+ default:
+ ERROR("Unknown othersubr #%ld.", subrno);
+ break;
+ }
+}
+
+/*
+ * Double byte operators:
+ */
+static void
+do_operator2 (t1_chardesc *cd, card8 **data, card8 *endptr)
+{
+ card8 op;
+
+ *data += 1;
+
+ SRC_NEED(endptr, *data + 1);
+
+ op = **data;
+ *data += 1;
+
+ switch(op) {
+ case cs_sbw:
+ CHECKSTACK(4);
+ cd->sbw.wy = cs_arg_stack[--cs_stack_top];
+ cd->sbw.wx = cs_arg_stack[--cs_stack_top];
+ cd->sbw.sby = cs_arg_stack[--cs_stack_top];
+ cd->sbw.sbx = cs_arg_stack[--cs_stack_top];
+ CLEARSTACK();
+ break;
+ case cs_hstem3:
+ case cs_vstem3:
+ /*
+ * TODO:
+ * The counter control can be used for hstem3 and vstem3
+ * operator if LanguageGroup is not equal to 1.
+ */
+ CHECKSTACK(6);
+ {
+ int i;
+ for (i = 2; i >= 0; i--) {
+ int stem_id;
+ stem_id = add_stem(cd,
+ cs_arg_stack[cs_stack_top-2*i-2],
+ cs_arg_stack[cs_stack_top-2*i-1],
+ ((op == cs_hstem3) ? HSTEM : VSTEM));
+ if (stem_id < 0) {
+ WARN("Too many hints...");
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ /* Put stem_id onto the stack... */
+ cs_arg_stack[cs_stack_top++] = stem_id;
+ ADD_PATH(cd, CS_HINT_DECL, 1);
+ cs_stack_top--;
+ }
+ }
+ CLEARSTACK();
+ break;
+ case cs_setcurrentpoint:
+ CHECKSTACK(2);
+ /* noop */
+ CLEARSTACK();
+ break;
+ /* all operator above are stack-clearing */
+ case cs_pop:
+ /*
+ * Transfer a operand from PS interpreter operand stack to BuildChar
+ * operand stack.
+ */
+ if (ps_stack_top < 1) {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+ LIMITCHECK(1);
+ cs_arg_stack[cs_stack_top++] = ps_arg_stack[--ps_stack_top];
+ break;
+ case cs_dotsection:
+#if 0
+ /*
+ * If the hint replacement feature is used in the font, the
+ * "dotsection" operator exist only for compatibility to older
+ * (more than 10 years old) Type 1 font rasterizer which can't
+ * perform hint replacement. In this case, we silently ignore
+ * the "dotsection" operator.
+ *
+ * The following code will wrongly warn about "dotsection" when
+ * the charstring only contains dot (e.g., "bullet") where the
+ * hint replacement is not necessary.
+ *
+ * Adobe ATM renderers always treat this operator as a no-op.
+ * (See, Adobe Technical Note #5177, Appendix C)
+ */
+ if (!(cd->flags & T1_CS_FLAG_USE_HINTMASK)) {
+ if (__verbose > 1)
+ WARN("Obsolete Type 1 charstring operator \"dotsection\" not supported.");
+ }
+#endif
+ /* noop */
+ break;
+ case cs_div: /* TODO: check overflow */
+ CHECKSTACK(2);
+ cs_arg_stack[cs_stack_top-2] /= cs_arg_stack[cs_stack_top-1];
+ cs_stack_top--;
+ break;
+ case cs_callothersubr:
+ do_callothersubr(cd);
+ break;
+ case cs_seac:
+ CHECKSTACK(5);
+ cd->flags |= T1_CS_FLAG_USE_SEAC;
+ cd->seac.achar = (card8) cs_arg_stack[--cs_stack_top];
+ cd->seac.bchar = (card8) cs_arg_stack[--cs_stack_top];
+ cd->seac.ady = cs_arg_stack[--cs_stack_top];
+ cd->seac.adx = cs_arg_stack[--cs_stack_top];
+ /* We must compensate the difference of the glyph origin. */
+ cd->seac.ady += cd->sbw.sby;
+ cd->seac.adx += cd->sbw.sbx - cs_arg_stack[--cs_stack_top];
+ CLEARSTACK();
+ break;
+ default:
+ /* no-op ? */
+ WARN("Unknown charstring operator: 0x0c%02x", op);
+ status = CS_PARSE_ERROR;
+ break;
+ }
+
+ return;
+}
+
+/*
+ * Charstring encoding:
+ * Copied from cs_type2.c
+ * Note:
+ * The Type 2 interpretation of a number encoded in five-bytes (those with
+ * an initial byte value of 255) differs from how it is interpreted in the
+ * Type 1 format.
+ */
+
+/* Type 2 5-bytes encoding used. */
+static void
+put_numbers (double *argv, int argn, card8 **dest, card8 *limit)
+{
+ int i;
+
+ for (i = 0; i < argn; i++) {
+ double value;
+ long ivalue;
+ value = argv[i];
+ /* Nearest integer value */
+ ivalue = (long) floor(value+0.5);
+ if (value >= 0x8000L || value <= (-0x8000L - 1)) {
+ /*
+ * This number cannot be represented as a single operand.
+ * We must use `a b mul ...' or `a c div' to represent large values.
+ */
+ ERROR("Argument value too large. (This is bug)");
+ } else if (fabs(value - ivalue) > 3.0e-5) {
+ /* 16.16-bit signed fixed value */
+ DST_NEED(limit, *dest + 5);
+ *(*dest)++ = 255;
+ ivalue = (long) floor(value); /* mantissa */
+ *(*dest)++ = (ivalue >> 8) & 0xff;
+ *(*dest)++ = ivalue & 0xff;
+ ivalue = (long)((value - ivalue) * 0x10000l); /* fraction */
+ *(*dest)++ = (ivalue >> 8) & 0xff;
+ *(*dest)++ = ivalue & 0xff;
+ /* Everything else are integers. */
+ } else if (ivalue >= -107 && ivalue <= 107) {
+ DST_NEED(limit, *dest + 1);
+ *(*dest)++ = ivalue + 139;
+ } else if (ivalue >= 108 && ivalue <= 1131) {
+ DST_NEED(limit, *dest + 2);
+ ivalue = 0xf700u + ivalue - 108;
+ *(*dest)++ = (ivalue >> 8) & 0xff;
+ *(*dest)++ = ivalue & 0xff;
+ } else if (ivalue >= -1131 && ivalue <= -108) {
+ DST_NEED(limit, *dest + 2);
+ ivalue = 0xfb00u - ivalue - 108;
+ *(*dest)++ = (ivalue >> 8) & 0xff;
+ *(*dest)++ = ivalue & 0xff;
+ } else if (ivalue >= -32768 && ivalue <= 32767) { /* shortint */
+ DST_NEED(limit, *dest + 3);
+ *(*dest)++ = 28;
+ *(*dest)++ = (ivalue >> 8) & 0xff;
+ *(*dest)++ = (ivalue) & 0xff;
+ } else { /* Shouldn't come here */
+ ERROR("Unexpected error.");
+ }
+ }
+
+ return;
+}
+
+static void
+get_integer (card8 **data, card8 *endptr)
+{
+ long result = 0;
+ card8 b0 = **data, b1, b2;
+
+ *data += 1;
+
+ if (b0 == 28) { /* shortint */
+ SRC_NEED(endptr, *data + 2);
+ b1 = **data;
+ b2 = *(*data+1);
+ result = b1*256+b2;
+ if (result > 0x7fff)
+ result -= 0x10000L;
+ *data += 2;
+ } else if (b0 >= 32 && b0 <= 246) { /* int (1) */
+ result = b0 - 139;
+ } else if (b0 >= 247 && b0 <= 250) { /* int (2) */
+ SRC_NEED(endptr, *data + 1);
+ b1 = **data;
+ result = (b0-247)*256+b1+108;
+ *data += 1;
+ } else if (b0 >= 251 && b0 <= 254) {
+ SRC_NEED(endptr, *data + 1);
+ b1 = **data;
+ result = -(b0-251)*256-b1-108;
+ *data += 1;
+ } else {
+ status = CS_PARSE_ERROR;
+ return;
+ }
+
+ LIMITCHECK(1);
+ cs_arg_stack[cs_stack_top++] = (double) result;
+
+ return;
+}
+
+/* Type 1 */
+static void
+get_longint (card8 **data, card8 *endptr)
+{
+ long result = 0;
+ int i;
+
+ *data += 1;
+ SRC_NEED(endptr, *data + 4);
+ result = **data;
+ if (result >= 0x80L)
+ result -= 0x100L;
+ *data += 1;
+ for (i = 1; i < 4; i++) {
+ result = result*256 + (**data);
+ *data += 1;
+ }
+
+ LIMITCHECK(1);
+ cs_arg_stack[cs_stack_top++] = (double) result;
+
+ return;
+}
+
+/*
+ * TODO:
+ * Check "seac"
+ * We cannot do backword parsing due to subroutine, div etc.
+ */
+
+/* Parse charstring and build charpath. */
+static void
+t1char_build_charpath (t1_chardesc *cd,
+ card8 **data, card8 *endptr, cff_index *subrs)
+{
+ card8 b0 = 0, *subr;
+ long len;
+
+ if (nest > CS_SUBR_NEST_MAX)
+ ERROR("Subroutine nested too deeply.");
+
+ nest++;
+ while (*data < endptr && status == CS_PARSE_OK) {
+ b0 = **data;
+ if (b0 == 255) {
+ get_longint(data, endptr); /* Type 1 */
+ } else if (b0 == cs_return) {
+ status = CS_SUBR_RETURN;
+ } else if (b0 == cs_callsubr) {
+ if (cs_stack_top < 1) {
+ status = CS_STACK_ERROR;
+ } else {
+ int idx;
+
+ idx = cs_arg_stack[--cs_stack_top];
+ if (!subrs || idx >= subrs->count)
+ ERROR("Invalid Subr#.");
+ subr = subrs->data + subrs->offset[idx] - 1;
+ len = subrs->offset[idx+1] - subrs->offset[idx];
+ t1char_build_charpath(cd, &subr, subr+len, subrs);
+ *data += 1;
+ }
+ } else if (b0 == cs_escape) {
+ do_operator2(cd, data, endptr);
+ } else if (b0 < 32 && b0 != 28) { /* 19, 20 need mask */
+ do_operator1(cd, data, endptr);
+ } else if ((b0 <= 22 && b0 >= 27) || b0 == 31) { /* reserved */
+ status = CS_PARSE_ERROR; /* not an error ? */
+ } else { /* integer */
+ get_integer(data, endptr);
+ }
+ }
+
+ if (status == CS_SUBR_RETURN) {
+ status = CS_PARSE_OK;
+ } else if (status == CS_CHAR_END && *data < endptr) {
+ if (!(*data == endptr - 1 && **data == cs_return))
+ WARN("Garbage after endchar. (%ld bytes)", (long) (endptr - *data));
+ } else if (status < CS_PARSE_OK) { /* error */
+ ERROR("Parsing charstring failed: (status=%d, stack=%d)", status, cs_stack_top);
+ }
+
+ nest--;
+
+ return;
+}
+
+/*
+ * Calculate BoundingBox and compress path.
+ * The essentials of PDF size reduction is not Type 2 charstring compression
+ * but Type 1 charstring encryption. Encryption makes lossless compression
+ * useless. We will only do very simple charstring compression.
+ */
+static void
+do_postproc (t1_chardesc *cd)
+{
+ int i;
+ t1_cpath *cur, *prev, *next;
+ double x, y;
+
+ if (!cd->charpath)
+ return;
+
+ /* Set dummy large value. */
+ cd->bbox.llx = cd->bbox.lly = 100000.0;
+ cd->bbox.urx = cd->bbox.ury = -100000.0;
+
+ cur = cd->charpath;
+ prev = NULL;
+ x = y = 0.0;
+
+#define UPDATE_BBOX(b,x,y) do {\
+ if ((b).llx > (x)) (b).llx = (x);\
+ if ((b).urx < (x)) (b).urx = (x);\
+ if ((b).lly > (y)) (b).lly = (y);\
+ if ((b).ury < (y)) (b).ury = (y);\
+} while (0)
+#define TRY_COMPACT (prev && cur && ((prev->num_args + cur->num_args) <= CS_ARG_STACK_MAX))
+
+ while (cur != NULL) {
+ next = cur->next;
+ switch (cur->type) {
+ case cs_rmoveto:
+ x += cur->args[0]; y += cur->args[1];
+ UPDATE_BBOX(cd->bbox, x, y);
+ break;
+ case cs_rlineto:
+ x += cur->args[0]; y += cur->args[1];
+ UPDATE_BBOX(cd->bbox, x, y);
+ if (TRY_COMPACT) {
+ if (prev->type == cs_rlineto) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ } else if (prev->type == cs_rrcurveto) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->type = cs_rcurveline;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ }
+ }
+ break;
+ case cs_hmoveto:
+ x += cur->args[0];
+ UPDATE_BBOX(cd->bbox, x, y);
+ break;
+ case cs_hlineto:
+ x += cur->args[0];
+ UPDATE_BBOX(cd->bbox, x, y);
+ if (TRY_COMPACT) {
+ if ((prev->type == cs_vlineto && (prev->num_args % 2) == 1) ||
+ (prev->type == cs_hlineto && (prev->num_args % 2) == 0)) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ }
+ }
+ break;
+ case cs_vmoveto:
+ y += cur->args[0];
+ UPDATE_BBOX(cd->bbox, x, y);
+ break;
+ case cs_vlineto:
+ y += cur->args[0];
+ UPDATE_BBOX(cd->bbox, x, y);
+ if (TRY_COMPACT) {
+ if ((prev->type == cs_hlineto && (prev->num_args % 2) == 1) ||
+ (prev->type == cs_vlineto && (prev->num_args % 2) == 0)) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ }
+ }
+ break;
+ case cs_rrcurveto:
+ for (i = 0; i < 3; i++) {
+ x += cur->args[2*i]; y += cur->args[2*i+1];
+ UPDATE_BBOX(cd->bbox, x, y);
+ }
+ if (TRY_COMPACT) {
+ if (prev->type == cs_rrcurveto) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ } else if (prev->type == cs_rlineto) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->type = cs_rlinecurve;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ }
+ }
+ break;
+ case cs_vhcurveto:
+ y += cur->args[0];
+ UPDATE_BBOX(cd->bbox, x, y);
+ x += cur->args[1]; y += cur->args[2];
+ UPDATE_BBOX(cd->bbox, x, y);
+ x += cur->args[3];
+ UPDATE_BBOX(cd->bbox, x, y);
+ if (TRY_COMPACT) {
+ if ((prev->type == cs_hvcurveto && ((prev->num_args / 4) % 2) == 1) ||
+ (prev->type == cs_vhcurveto && ((prev->num_args / 4) % 2) == 0)) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ }
+ }
+ break;
+ case cs_hvcurveto:
+ x += cur->args[0];
+ UPDATE_BBOX(cd->bbox, x, y);
+ x += cur->args[1]; y += cur->args[2];
+ UPDATE_BBOX(cd->bbox, x, y);
+ y += cur->args[3];
+ UPDATE_BBOX(cd->bbox, x, y);
+ if (TRY_COMPACT) {
+ if ((prev->type == cs_vhcurveto && ((prev->num_args / 4) % 2) == 1) ||
+ (prev->type == cs_hvcurveto && ((prev->num_args / 4) % 2) == 0)) {
+ copy_args(prev->args+prev->num_args, cur->args, cur->num_args);
+ prev->num_args += cur->num_args;
+ prev->next = next;
+ RELEASE(cur); cur = NULL;
+ }
+ }
+ break;
+ case cs_flex:
+ for (i = 0; i < 6; i++) {
+ x += cur->args[2*i]; y += cur->args[2*1+1];
+ UPDATE_BBOX(cd->bbox, x, y);
+ }
+ if (cur->args[12] == 50.0) {
+ if (cur->args[1] == 0.0 && cur->args[11] == 0.0 &&
+ cur->args[5] == 0.0 && cur->args[7] == 0.0 &&
+ cur->args[3] + cur->args[9] == 0.0) {
+ /* cur->args[0] = cur->args[0]; dx1 */
+ cur->args[1] = cur->args[2]; /* dx2 */
+ cur->args[2] = cur->args[3]; /* dy2 */
+ cur->args[3] = cur->args[4]; /* dx3 */
+ cur->args[4] = cur->args[6]; /* dx4 */
+ cur->args[5] = cur->args[8]; /* dx5 */
+ cur->args[6] = cur->args[10]; /* dx6 */
+ cur->num_args = 7;
+ cur->type = cs_hflex;
+ } else if (cur->args[5] == 0.0 && cur->args[7] == 0.0 &&
+ (cur->args[1] + cur->args[3] +
+ cur->args[9] + cur->args[11]) == 0) {
+ /* cur->args[0] = cur->args[0]; dx1 */
+ /* cur->args[1] = cur->args[1]; dy1 */
+ /* cur->args[2] = cur->args[2]; dx2 */
+ /* cur->args[3] = cur->args[3]; dy2 */
+ /* cur->args[4] = cur->args[4]; dx3 */
+ cur->args[5] = cur->args[6]; /* dx4 */
+ cur->args[6] = cur->args[8]; /* dx5 */
+ cur->args[7] = cur->args[9]; /* dy5 */
+ cur->args[8] = cur->args[10]; /* dx6 */
+ cur->num_args = 9;
+ cur->type = cs_hflex1;
+ }
+ }
+ break;
+ case CS_HINT_DECL:
+ case cs_cntrmask:
+ /* noop */
+ break;
+ default:
+ ERROR("Unexpected Type 2 charstring command %d.", cur->type);
+ break;
+ }
+ if (cur != NULL)
+ prev = cur;
+ cur = next;
+ }
+
+ /* Had no path. Fix lower-left point. */
+ if (cd->bbox.llx > cd->bbox.urx)
+ cd->bbox.llx = cd->bbox.urx = cd->sbw.wx;
+ if (cd->bbox.lly > cd->bbox.ury)
+ cd->bbox.lly = cd->bbox.ury = cd->sbw.wy;
+
+ return;
+}
+
+#define RESET_STATE() do {\
+ status = CS_PARSE_OK;\
+ phase = T1_CS_PHASE_INIT;\
+ nest = 0;\
+ ps_stack_top = 0;\
+} while (0)
+
+int
+t1char_get_metrics (card8 *src, long srclen, cff_index *subrs, t1_ginfo *ginfo)
+{
+ t1_chardesc t1char, *cd;
+
+ cd = &t1char;
+ init_charpath(cd);
+ RESET_STATE();
+ CLEARSTACK();
+ t1char_build_charpath(cd, &src, src+srclen, subrs);
+ if (cs_stack_top != 0 || ps_stack_top != 0)
+ WARN("Stack not empty. (%d, %d)", cs_stack_top, ps_stack_top);
+ do_postproc(cd);
+ if (ginfo) {
+ ginfo->wx = cd->sbw.wx;
+ ginfo->wy = cd->sbw.wy;
+ ginfo->bbox.llx = cd->bbox.llx;
+ ginfo->bbox.lly = cd->bbox.lly;
+ ginfo->bbox.urx = cd->bbox.urx;
+ ginfo->bbox.ury = cd->bbox.ury;
+ if (cd->flags & T1_CS_FLAG_USE_SEAC) {
+ ginfo->use_seac = 1;
+ ginfo->seac.adx = cd->seac.adx;
+ ginfo->seac.ady = cd->seac.ady;
+ ginfo->seac.bchar = cd->seac.bchar;
+ ginfo->seac.achar = cd->seac.achar;
+ } else {
+ ginfo->use_seac = 0;
+ }
+ }
+ release_charpath(cd);
+
+ return 0;
+}
+
+#define CHECK_BUFFER(n) if (dst+(n) >= endptr) {\
+ ERROR("Buffer overflow.");\
+}
+#define CHECK_STATUS() if (status != CS_PARSE_OK) {\
+ ERROR("Charstring encoder error: %d", status);\
+}
+
+/*
+ * Encode Charpath as a Type 2 Charstring
+ */
+static long
+t1char_encode_charpath (t1_chardesc *cd,
+ double default_width, double nominal_width,
+ card8 *dst, card8 *endptr)
+{
+ card8 *save;
+ t1_cpath *curr;
+
+ ASSERT(cd);
+
+ save = dst;
+ curr = cd->charpath;
+
+ RESET_STATE();
+ CLEARSTACK();
+ /*
+ * Advance Width
+ */
+ if (cd->sbw.wx != default_width) {
+ double wx = cd->sbw.wx - nominal_width;
+ put_numbers(&wx, 1, &dst, endptr);
+ CHECK_STATUS();
+ }
+ /*
+ * Hint Declaration
+ */
+ {
+ int num_hstems = 0, num_vstems = 0;
+ int i, reset = 1;
+ double stem[2];
+
+ for (i = 0; i < cd->num_stems && cd->stems[i].dir == HSTEM; i++) {
+ num_hstems++;
+ stem[0] = (reset ?
+ (cd->stems[i].pos) :
+ (cd->stems[i].pos - (cd->stems[i-1].pos + cd->stems[i-1].del)));
+ stem[1] = cd->stems[i].del;
+ put_numbers(stem, 2, &dst, endptr);
+ CHECK_STATUS();
+ reset = 0;
+ if (2*num_hstems == CS_ARG_STACK_MAX) {
+ CHECK_BUFFER(1);
+ *dst++ = (card8) ((cd->flags & T1_CS_FLAG_USE_HINTMASK) ? cs_hstemhm : cs_hstem);
+ reset = 1;
+ }
+ }
+ if (reset == 0) {
+ CHECK_BUFFER(1);
+ *dst++ = (card8) ((cd->flags & T1_CS_FLAG_USE_HINTMASK) ? cs_hstemhm : cs_hstem);
+ }
+ reset = 1;
+ if (cd->num_stems - num_hstems > 0) {
+ for (i = num_hstems; i < cd->num_stems; i++) {
+ num_vstems++;
+ stem[0] = (reset ?
+ (cd->stems[i].pos) :
+ (cd->stems[i].pos - (cd->stems[i-1].pos + cd->stems[i-1].del)));
+ stem[1] = cd->stems[i].del;
+ put_numbers(stem, 2, &dst, endptr);
+ CHECK_STATUS();
+ reset = 0;
+ if (2*num_vstems == CS_ARG_STACK_MAX) {
+ CHECK_BUFFER(1);
+ *dst++ = (card8) ((cd->flags & T1_CS_FLAG_USE_HINTMASK) ? cs_vstemhm : cs_vstem);
+ reset = 1;
+ }
+ }
+ if (reset == 0) {
+ CHECK_BUFFER(1);
+ if ((cd->flags & T1_CS_FLAG_USE_HINTMASK) ||
+ (cd->flags & T1_CS_FLAG_USE_CNTRMASK)) {
+ /*
+ * The vstem hint operator can be ommited if hstem and vstem hints
+ * are both declared at the beginning of a charstring, and is
+ * followed directly by the hintmask or cntrmask operators.
+ */
+ if (curr->type != CS_HINT_DECL &&
+ curr->type != cs_cntrmask) {
+ *dst++ = (card8) cs_vstemhm;
+ }
+ } else {
+ *dst++ = (card8) cs_vstem;
+ }
+ }
+ }
+ }
+ /*
+ * Path Construction and Hint Replacement
+ */
+ while (curr != NULL && curr->type != cs_endchar) {
+ switch (curr->type) {
+ case CS_HINT_DECL:
+ {
+ card8 hintmask[(CS_STEM_ZONE_MAX+7)/8];
+
+ memset(hintmask, 0, (cd->num_stems+7)/8);
+ while (curr != NULL && curr->type == CS_HINT_DECL) {
+ int stem_idx;
+
+ stem_idx = get_stem(cd, (int) curr->args[0]);
+ ASSERT(stem_idx < cd->num_stems);
+ hintmask[stem_idx/8] |= (1 << (7 - (stem_idx % 8)));
+ curr = curr->next;
+ }
+ if (cd->flags & T1_CS_FLAG_USE_HINTMASK) {
+ CHECK_BUFFER((cd->num_stems+7)/8 + 1);
+ *dst++ = (card8) cs_hintmask;
+ memcpy(dst, hintmask, (cd->num_stems+7)/8);
+ dst += (cd->num_stems+7)/8;
+ }
+ }
+ break;
+ case cs_cntrmask:
+ {
+ card8 cntrmask[(CS_STEM_ZONE_MAX+7)/8];
+ int stem_idx, i;
+
+ memset(cntrmask, 0, (cd->num_stems+7)/8);
+ for (i = 0; i < curr->num_args; i++) {
+ stem_idx = get_stem(cd, (int) curr->args[i]);
+ ASSERT(stem_idx < cd->num_stems);
+ cntrmask[stem_idx/8] |= (1 << (7 - (stem_idx % 8)));
+ }
+ CHECK_BUFFER((cd->num_stems+7)/8 + 1);
+ *dst++ = (card8) cs_cntrmask;
+ memcpy(dst, cntrmask, (cd->num_stems+7)/8);
+ dst += (cd->num_stems+7)/8;
+ curr = curr->next;
+ }
+ break;
+ case cs_rmoveto: case cs_hmoveto: case cs_vmoveto:
+ case cs_rlineto: case cs_hlineto: case cs_vlineto:
+ case cs_rrcurveto: case cs_hvcurveto: case cs_vhcurveto:
+ case cs_rlinecurve: case cs_rcurveline:
+ {
+ put_numbers(curr->args, curr->num_args, &dst, endptr);
+ CHECK_STATUS();
+ CHECK_BUFFER(1);
+ *dst++ = (card8) curr->type;
+ curr = curr->next;
+ }
+ break;
+ case cs_flex: case cs_hflex:
+ case cs_hflex1:
+ {
+ put_numbers(curr->args, curr->num_args, &dst, endptr);
+ CHECK_STATUS();
+ CHECK_BUFFER(2);
+ *dst++ = (card8) cs_escape;
+ *dst++ = (card8) curr->type;
+ curr = curr->next;
+ }
+ break;
+ default:
+ ERROR("Unknown Type 2 charstring command: %d", curr->type);
+ break;
+ }
+ }
+
+ /*
+ * (adx ady bchar achar) endchar
+ */
+ if (cd->flags & T1_CS_FLAG_USE_SEAC) {
+ double seac[4];
+ seac[0] = cd->seac.adx;
+ seac[1] = cd->seac.ady;
+ seac[2] = cd->seac.bchar;
+ seac[3] = cd->seac.achar;
+ put_numbers(seac, 4, &dst, endptr);
+ CHECK_STATUS();
+ CHECK_BUFFER(2);
+ WARN("Obsolete four arguments of \"endchar\" will be used for Type 1 \"seac\" operator.");
+ }
+ CHECK_BUFFER(1);
+ *dst++ = (card8) cs_endchar;
+
+ return (long) (dst - save);
+}
+
+long
+t1char_convert_charstring (card8 *dst, long dstlen,
+ card8 *src, long srclen, cff_index *subrs,
+ double default_width, double nominal_width,
+ t1_ginfo *ginfo)
+{
+ long length;
+ t1_chardesc t1char, *cd;
+
+ cd = &t1char;
+ init_charpath(cd);
+ RESET_STATE();
+ CLEARSTACK();
+ t1char_build_charpath(cd, &src, src+srclen, subrs);
+ if (cs_stack_top != 0 || ps_stack_top != 0)
+ WARN("Stack not empty. (%d, %d)", cs_stack_top, ps_stack_top);
+ do_postproc(cd);
+ SORT_STEMS(cd);
+
+ length = t1char_encode_charpath(cd, default_width, nominal_width, dst, dst+dstlen);
+
+ if (ginfo) {
+ ginfo->wx = cd->sbw.wx;
+ ginfo->wy = cd->sbw.wy;
+ ginfo->bbox.llx = cd->bbox.llx;
+ ginfo->bbox.lly = cd->bbox.lly;
+ ginfo->bbox.urx = cd->bbox.urx;
+ ginfo->bbox.ury = cd->bbox.ury;
+ if (cd->flags & T1_CS_FLAG_USE_SEAC) {
+ ginfo->use_seac = 1;
+ ginfo->seac.adx = cd->seac.adx;
+ ginfo->seac.ady = cd->seac.ady;
+ ginfo->seac.bchar = cd->seac.bchar;
+ ginfo->seac.achar = cd->seac.achar;
+ } else {
+ ginfo->use_seac = 0;
+ }
+ }
+ release_charpath(cd);
+
+ return length;
+}