diff options
Diffstat (limited to 'Build/source/texk/dvipdfm-x/src/t1_char.c')
-rw-r--r-- | Build/source/texk/dvipdfm-x/src/t1_char.c | 1590 |
1 files changed, 1590 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm-x/src/t1_char.c b/Build/source/texk/dvipdfm-x/src/t1_char.c new file mode 100644 index 00000000000..7b6788870bd --- /dev/null +++ b/Build/source/texk/dvipdfm-x/src/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; +} |