/* Copyright (C) 1997, 1998, 1999, 2000 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: gxtype1.c,v 1.8.4.1.2.1 2003/01/17 00:49:04 giles Exp $ */
/* Adobe Type 1 font interpreter support */
#include "math_.h"
#include "gx.h"
#include "gserrors.h"
#include "gsccode.h"
#include "gsline.h"
#include "gsstruct.h"
#include "gxarith.h"
#include "gxchrout.h"
#include "gxfixed.h"
#include "gxistate.h"
#include "gxmatrix.h"
#include "gxcoord.h"
#include "gxfont.h"
#include "gxfont1.h"
#include "gxtype1.h"
#include "gzpath.h"

/*
 * The routines in this file are used for both Type 1 and Type 2
 * charstring interpreters.
 */

/*
 * Define whether or not to force hints to "big pixel" boundaries
 * when rasterizing at higher resolution.  With the current algorithms,
 * a value of 1 is better for devices without alpha capability,
 * but 0 is better if alpha is available.
 */
#define FORCE_HINTS_TO_BIG_PIXELS 1

/* Structure descriptor */
public_st_gs_font_type1();

/* Define the structure type for a Type 1 interpreter state. */
public_st_gs_type1_state();
/* GC procedures */
private 
ENUM_PTRS_WITH(gs_type1_state_enum_ptrs, gs_type1_state *pcis)
{
    if (index < pcis->ips_count + 4) {
	ENUM_RETURN_CONST_STRING_PTR(gs_type1_state,
				     ipstack[index - 4].char_string);
    }
    return 0;
}
ENUM_PTR3(0, gs_type1_state, pfont, pis, path);
ENUM_PTR(3, gs_type1_state, callback_data);
ENUM_PTRS_END
private RELOC_PTRS_WITH(gs_type1_state_reloc_ptrs, gs_type1_state *pcis)
{
    int i;

    RELOC_PTR(gs_type1_state, pfont);
    RELOC_PTR(gs_type1_state, pis);
    RELOC_PTR(gs_type1_state, path);
    RELOC_PTR(gs_type1_state, callback_data);
    for (i = 0; i < pcis->ips_count; i++) {
	ip_state_t *ipsp = &pcis->ipstack[i];
	int diff = ipsp->ip - ipsp->char_string.data;

	RELOC_CONST_STRING_VAR(ipsp->char_string);
	ipsp->ip = ipsp->char_string.data + diff;
    }
} RELOC_PTRS_END

/* ------ Interpreter services ------ */

#define s (*ps)

/* We export this for the Type 2 charstring interpreter. */
void
accum_xy_proc(register is_ptr ps, fixed dx, fixed dy)
{
    ptx += c_fixed(dx, xx),
	pty += c_fixed(dy, yy);
    if (sfc.skewed)
	ptx += c_fixed(dy, yx),
	    pty += c_fixed(dx, xy);
}

/* Initialize a Type 1 interpreter. */
/* The caller must supply a string to the first call of gs_type1_interpret. */
int
gs_type1_interp_init(register gs_type1_state * pcis, gs_imager_state * pis,
    gx_path * ppath, const gs_log2_scale_point * pscale, bool charpath_flag,
		     int paint_type, gs_font_type1 * pfont)
{
    static const gs_log2_scale_point no_scale = {0, 0};
    const gs_log2_scale_point *plog2_scale =
	(FORCE_HINTS_TO_BIG_PIXELS ? pscale : &no_scale);

    pcis->pfont = pfont;
    pcis->pis = pis;
    pcis->path = ppath;
    pcis->callback_data = pfont; /* default callback data */
    /*
     * charpath_flag controls coordinate rounding, hinting, and
     * flatness enhancement.  If we allow it to be set to true,
     * charpath may produce results quite different from show.
     */
    pcis->charpath_flag = false /*charpath_flag */ ;
    pcis->paint_type = paint_type;
    pcis->os_count = 0;
    pcis->ips_count = 1;
    pcis->ipstack[0].ip = 0;
    pcis->ipstack[0].char_string.data = 0;
    pcis->ipstack[0].char_string.size = 0;
    pcis->ignore_pops = 0;
    pcis->init_done = -1;
    pcis->sb_set = false;
    pcis->width_set = false;
    pcis->have_hintmask = false;
    pcis->num_hints = 0;
    pcis->seac_accent = -1;

    /* Set the sampling scale. */
    set_pixel_scale(&pcis->scale.x, plog2_scale->x);
    set_pixel_scale(&pcis->scale.y, plog2_scale->y);

    return 0;
}

/* Set the push/pop callback data. */
void
gs_type1_set_callback_data(gs_type1_state *pcis, void *callback_data)
{
    pcis->callback_data = callback_data;
}


/* Preset the left side bearing and/or width. */
void
gs_type1_set_lsb(gs_type1_state * pcis, const gs_point * psbpt)
{
    pcis->lsb.x = float2fixed(psbpt->x);
    pcis->lsb.y = float2fixed(psbpt->y);
    pcis->sb_set = true;
}
void
gs_type1_set_width(gs_type1_state * pcis, const gs_point * pwpt)
{
    pcis->width.x = float2fixed(pwpt->x);
    pcis->width.y = float2fixed(pwpt->y);
    pcis->width_set = true;
}

/* Finish initializing the interpreter if we are actually rasterizing */
/* the character, as opposed to just computing the side bearing and width. */
void
gs_type1_finish_init(gs_type1_state * pcis, gs_op1_state * ps)
{
    gs_imager_state *pis = pcis->pis;

    /* Set up the fixed version of the transformation. */
    gx_matrix_to_fixed_coeff(&ctm_only(pis), &pcis->fc, max_coeff_bits);
    sfc = pcis->fc;

    /* Set the current point of the path to the origin, */
    /* in anticipation of the initial [h]sbw. */
    {
	gx_path *ppath = pcis->path;

	ptx = pcis->origin.x = ppath->position.x;
	pty = pcis->origin.y = ppath->position.y;
    }

    /* Initialize hint-related scalars. */
    pcis->asb_diff = pcis->adxy.x = pcis->adxy.y = 0;
    pcis->flex_count = flex_max;	/* not in Flex */
    pcis->dotsection_flag = dotsection_out;
    pcis->vstem3_set = false;
    pcis->vs_offset.x = pcis->vs_offset.y = 0;
    pcis->hints_initial = 0;	/* probably not needed */
    pcis->hint_next = 0;
    pcis->hints_pending = 0;

    /* Assimilate the hints proper. */
    {
	gs_log2_scale_point log2_scale;

	log2_scale.x = pcis->scale.x.log2_unit;
	log2_scale.y = pcis->scale.y.log2_unit;
	if (pcis->charpath_flag)
	    reset_font_hints(&pcis->fh, &log2_scale);
	else
	    compute_font_hints(&pcis->fh, &pis->ctm, &log2_scale,
			       &pcis->pfont->data);
    }
    reset_stem_hints(pcis);

    /* Compute the flatness needed for accurate rendering. */
    pcis->flatness = gs_char_flatness(pis, 0.001);

    /* Move to the side bearing point. */
    accum_xy(pcis->lsb.x, pcis->lsb.y);
    pcis->position.x = ptx;
    pcis->position.y = pty;

    pcis->init_done = 1;
}

/* ------ Operator procedures ------ */

int
gs_op1_closepath(register is_ptr ps)
{				/* Note that this does NOT reset the current point! */
    gx_path *ppath = sppath;
    subpath *psub;
    segment *pseg;
    fixed dx, dy;
    int code;

    /* Check for and suppress a microscopic closing line. */
    if ((psub = ppath->current_subpath) != 0 &&
	(pseg = psub->last) != 0 &&
	(dx = pseg->pt.x - psub->pt.x,
	 any_abs(dx) < float2fixed(0.1)) &&
	(dy = pseg->pt.y - psub->pt.y,
	 any_abs(dy) < float2fixed(0.1))
	)
	switch (pseg->type) {
	    case s_line:
		code = gx_path_pop_close_subpath(sppath);
		break;
	    case s_curve:
		/*
		 * Unfortunately, there is no "s_curve_close".  (Maybe there
		 * should be?)  Just adjust the final point of the curve so it
		 * is identical to the closing point.
		 */
		pseg->pt = psub->pt;
#define pcseg ((curve_segment *)pseg)
		pcseg->p2.x -= dx;
		pcseg->p2.y -= dy;
#undef pcseg
		/* falls through */
	    default:
		/* What else could it be?? */
		code = gx_path_close_subpath(sppath);
    } else
	code = gx_path_close_subpath(sppath);
    if (code < 0)
	return code;
    return gx_path_add_point(ppath, ptx, pty);	/* put the point where it was */
}

int
gs_op1_rrcurveto(register is_ptr ps, fixed dx1, fixed dy1,
		 fixed dx2, fixed dy2, fixed dx3, fixed dy3)
{
    gs_fixed_point pt1, pt2;
    fixed ax0 = sppath->position.x - ptx;
    fixed ay0 = sppath->position.y - pty;

    accum_xy(dx1, dy1);
    pt1.x = ptx + ax0, pt1.y = pty + ay0;
    accum_xy(dx2, dy2);
    pt2.x = ptx, pt2.y = pty;
    accum_xy(dx3, dy3);
    return gx_path_add_curve(sppath, pt1.x, pt1.y, pt2.x, pt2.y, ptx, pty);
}

#undef s

/* Record the side bearing and character width. */
int
gs_type1_sbw(gs_type1_state * pcis, fixed lsbx, fixed lsby, fixed wx, fixed wy)
{
    if (!pcis->sb_set)
	pcis->lsb.x = lsbx, pcis->lsb.y = lsby,
	    pcis->sb_set = true;	/* needed for accented chars */
    if (!pcis->width_set)
	pcis->width.x = wx, pcis->width.y = wy,
	    pcis->width_set = true;
    if_debug4('1', "[1]sb=(%g,%g) w=(%g,%g)\n",
	      fixed2float(pcis->lsb.x), fixed2float(pcis->lsb.y),
	      fixed2float(pcis->width.x), fixed2float(pcis->width.y));
    return 0;
}

/* Blend values for a Multiple Master font instance. */
/* The stack holds values ... K*N othersubr#. */
int
gs_type1_blend(gs_type1_state *pcis, fixed *csp, int num_results)
{
    gs_type1_data *pdata = &pcis->pfont->data;
    int num_values = fixed2int_var(csp[-1]);
    int k1 = num_values / num_results - 1;
    int i, j;
    fixed *base;
    fixed *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];
    pcis->ignore_pops = num_results;
    return num_values - num_results + 2;
}

/*
 * Handle a seac.  Do the base character now; when it finishes (detected
 * in endchar), do the accent.  Note that we pass only 4 operands on the
 * stack, and pass asb separately.
 */
int
gs_type1_seac(gs_type1_state * pcis, const fixed * cstack, fixed asb,
	      ip_state_t * ipsp)
{
    gs_font_type1 *pfont = pcis->pfont;
    gs_const_string bcstr;
    int code;

    /* Save away all the operands. */
    pcis->seac_accent = fixed2int_var(cstack[3]);
    pcis->save_asb = asb;
    pcis->save_lsb = pcis->lsb;
    pcis->save_adxy.x = cstack[0];
    pcis->save_adxy.y = cstack[1];
    pcis->os_count = 0;		/* clear */
    /* Ask the caller to provide the base character's CharString. */
    code = pfont->data.procs.seac_data
	(pfont, fixed2int_var(cstack[2]), NULL, &bcstr);
    if (code < 0)
	return code;
    /* Continue with the supplied string. */
    ipsp->char_string = bcstr;
    ipsp->free_char_string = code;
    return 0;
}

/*
 * Handle the end of a character.  Return 0 if this is really the end of a
 * character, or 1 if we still have to process the accent of a seac.
 * In the latter case, the interpreter control stack has been set up to
 * point to the start of the accent's CharString; the caller must
 * also set ptx/y to pcis->position.x/y.
 */
int
gs_type1_endchar(gs_type1_state * pcis)
{
    gs_imager_state *pis = pcis->pis;
    gx_path *ppath = pcis->path;

    if (pcis->seac_accent >= 0) {	/* We just finished the base character of a seac. */
	/* Do the accent. */
	gs_font_type1 *pfont = pcis->pfont;
	gs_op1_state s;
	gs_const_string astr;
	int achar = pcis->seac_accent;
	int code;

	pcis->seac_accent = -1;
	/* Reset the coordinate system origin */
	sfc = pcis->fc;
	ptx = pcis->origin.x, pty = pcis->origin.y;
	pcis->asb_diff = pcis->save_asb - pcis->save_lsb.x;
	pcis->adxy = pcis->save_adxy;
	/*
	 * We're going to add in the lsb of the accented character
	 * (*not* the lsb of the accent) when we encounter the
	 * [h]sbw of the accent, so ignore the lsb for now.
	 */
	accum_xy(pcis->adxy.x, pcis->adxy.y);
	ppath->position.x = pcis->position.x = ptx;
	ppath->position.y = pcis->position.y = pty;
	pcis->os_count = 0;	/* clear */
	/* Clear the ipstack, in case the base character */
	/* ended inside a subroutine. */
	pcis->ips_count = 1;
	/* Remove any base character hints. */
	reset_stem_hints(pcis);
	/* Ask the caller to provide the accent's CharString. */
	code = pfont->data.procs.seac_data(pfont, achar, NULL, &astr);
	if (code < 0)
	    return code;
	/* Continue with the supplied string. */
	pcis->ips_count = 1;
	pcis->ipstack[0].char_string = astr;
	pcis->ipstack[0].free_char_string = code;
	return 1;
    }
    if (pcis->hint_next != 0 || path_is_drawing(ppath))
	apply_path_hints(pcis, true);
    /* Set the current point to the character origin */
    /* plus the width. */
    {
	gs_fixed_point pt;

	gs_point_transform2fixed(&pis->ctm,
				 fixed2float(pcis->width.x),
				 fixed2float(pcis->width.y),
				 &pt);
	gx_path_add_point(ppath, pt.x, pt.y);
    }
    if (pcis->scale.x.log2_unit + pcis->scale.y.log2_unit == 0) {	/*
									 * Tweak up the fill adjustment.  This is a hack for when
									 * we can't oversample.  The values here are based entirely
									 * on experience, not theory, and are designed primarily
									 * for displays and low-resolution fax.
									 */
	gs_fixed_rect bbox;
	int dx, dy, dmax;

	gx_path_bbox(ppath, &bbox);
	dx = fixed2int_ceiling(bbox.q.x - bbox.p.x);
	dy = fixed2int_ceiling(bbox.q.y - bbox.p.y);
	dmax = max(dx, dy);
	if (pcis->fh.snap_h.count || pcis->fh.snap_v.count ||
	    pcis->fh.a_zone_count
	    ) {			/* We have hints.  Only tweak up a little at */
	    /* very small sizes, to help nearly-vertical */
	    /* or nearly-horizontal diagonals. */
	    pis->fill_adjust.x = pis->fill_adjust.y =
		(dmax < 15 ? float2fixed(0.15) :
		 dmax < 25 ? float2fixed(0.1) :
		 fixed_0);
	} else {		/* No hints.  Tweak a little more to compensate */
	    /* for lack of snapping to pixel grid. */
	    pis->fill_adjust.x = pis->fill_adjust.y =
		(dmax < 10 ? float2fixed(0.2) :
		 dmax < 25 ? float2fixed(0.1) :
		 float2fixed(0.05));
	}
    } else {			/* Don't do any adjusting. */
	pis->fill_adjust.x = pis->fill_adjust.y = fixed_0;
    }
    /* Set the flatness for curve rendering. */
    if (!pcis->charpath_flag)
	gs_imager_setflat(pis, pcis->flatness);
    return 0;
}

/* ------ Font procedures ------ */

int
gs_type1_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
		    int members, gs_glyph_info_t *info)
{
    gs_font_type1 *const pfont = (gs_font_type1 *)font;
    gs_type1_data *const pdata = &pfont->data;
    int wmode = pfont->WMode;
    int piece_members = members & (GLYPH_INFO_NUM_PIECES | GLYPH_INFO_PIECES);
    int width_members = members & (GLYPH_INFO_WIDTH0 << wmode);
    int default_members = members - (piece_members + width_members);
    int code = 0, gcode = 0;
    gs_const_string str;

    if (default_members) {
	code = gs_default_glyph_info(font, glyph, pmat, default_members, info);

	if (code < 0)
	    return code;
    } else
	info->members = 0;

    if (default_members != members) {
	if ((gcode = pdata->procs.glyph_data(pfont, glyph, &str)) < 0)
	    return gcode;		/* non-existent glyph */
    }

    if (piece_members) {
	gs_glyph *pieces =
	    (members & GLYPH_INFO_PIECES ? info->pieces : (gs_glyph *)0);
	/*
	 * Decode the CharString looking for seac.  We have to process
	 * callsubr, callothersubr, and return operators, but if we see
	 * any other operators other than [h]sbw, pop, hint operators,
	 * or endchar, we can return immediately.  We have to include
	 * endchar because it is an (undocumented) equivalent for seac
	 * in Type 2 CharStrings: see the cx_endchar case in
	 * gs_type2_interpret in gstype2.c.
	 *
	 * It's really unfortunate that we have to duplicate so much parsing
	 * code, but factoring out the parser from the interpreter would
	 * involve more restructuring than we're prepared to do right now.
	 */
	bool encrypted = pdata->lenIV >= 0;
	fixed cstack[ostack_size];
	fixed *csp;
	ip_state_t ipstack[ipstack_size + 1];
	ip_state_t *ipsp = &ipstack[0];
	const byte *cip;
	crypt_state state;
	int c;
    
	CLEAR_CSTACK(cstack, csp);
	info->num_pieces = 0;	/* default */
	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);
	}
    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);
		} else		/* not possible */
		    return_error(gs_error_invalidfont);
		continue;
	    }
#define cnext CLEAR_CSTACK(cstack, csp); goto top
	    switch ((char_command) c) {
	    default:
		goto out;
	    case c_callsubr:
		c = fixed2int_var(*csp);
		code = pdata->procs.subr_data
		    (pfont, c, false, &ipsp[1].char_string);
		if (code < 0)
		    return_error(code);
		--csp;
		ipsp->ip = cip, ipsp->dstate = state;
		++ipsp;
		ipsp->free_char_string = code;
		cip = ipsp->char_string.data;
		goto call;
	    case c_return:
		if (ipsp->free_char_string > 0)
		    gs_free_const_string(pfont->memory,
					 ipsp->char_string.data,
					 ipsp->char_string.size,
					 "gs_type1_glyph_info");
		--ipsp;
		cip = ipsp->ip, state = ipsp->dstate;
		goto top;
	    case cx_hstem:
	    case cx_vstem:
	    case c1_hsbw:
		cnext;
	    case cx_endchar:
		if (csp < cstack + 3)
		    goto out;	/* not seac */
	    do_seac:
		/* This is the payoff for all this code! */
		if (pieces) {
		    gs_char bchar = fixed2int(csp[-1]);
		    gs_char achar = fixed2int(csp[0]);
		    int bcode =
			pdata->procs.seac_data(pfont, bchar,
					       &pieces[0], NULL);
		    int acode =
			pdata->procs.seac_data(pfont, achar,
					       &pieces[1], NULL);

		    code = (bcode < 0 ? bcode : acode);
		}
		info->num_pieces = 2;
		goto out;
	    case cx_escape:
		charstring_next(*cip, state, c, encrypted);
		++cip;
		switch ((char1_extended_command) c) {
		default:
		    goto out;
		case ce1_vstem3:
		case ce1_hstem3:
		case ce1_sbw:
		    cnext;
		case ce1_pop:
		    /*
		     * pop must do nothing, since it is used after
		     * subr# 1 3 callothersubr.
		     */
		    goto top;
		case ce1_seac:
		    goto do_seac;
		case ce1_callothersubr:
		    switch (fixed2int_var(*csp)) {
		    default:
			goto out;
		    case 3:
			csp -= 2;
			goto top;
		    case 12:
		    case 13:
		    case 14:
		    case 15:
		    case 16:
		    case 17:
		    case 18:
			cnext;
		    }
		}
	    }
#undef cnext
	}
out:	info->members |= piece_members;
    }

    if (width_members) {
	/*
	 * Interpret the CharString until we get to the [h]sbw.
	 */
	gs_imager_state gis;
	gs_type1_state cis;
	static const gs_log2_scale_point no_scale = {0, 0};
	int value;

	/* Initialize just enough of the imager state. */
	if (pmat)
	    gs_matrix_fixed_from_matrix(&gis.ctm, pmat);
	else {
	    gs_matrix imat;

	    gs_make_identity(&imat);
	    gs_matrix_fixed_from_matrix(&gis.ctm, &imat);
	}
	gis.flatness = 0;
	code = gs_type1_interp_init(&cis, &gis, NULL /* no path needed */,
				    &no_scale, true, 0, pfont);
	if (code < 0)
	    return code;
	cis.charpath_flag = true;	/* suppress hinting */
	code = pdata->interpret(&cis, &str, &value);
	switch (code) {
	case 0:		/* done with no [h]sbw, error */
	    code = gs_note_error(gs_error_invalidfont);
	default:		/* code < 0, error */
	    return code;
	case type1_result_callothersubr:	/* unknown OtherSubr */
	    return_error(gs_error_rangecheck); /* can't handle it */
	case type1_result_sbw:
	    info->width[wmode].x = fixed2float(cis.width.x);
	    info->width[wmode].y = fixed2float(cis.width.y);
	    break;
	}
	info->members |= width_members;
    }

    if (gcode > 0)
	gs_free_const_string(font->memory, str.data, str.size,
			     "gs_type1_glyph_info");
    return code;
}
