/* Copyright (C) 1993 Aladdin Enterprises.  All rights reserved.
  
  This file is part of Aladdin Ghostscript.
  
  Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  or distributor accepts any responsibility for the consequences of using it,
  or for whether it serves any particular purpose or works at all, unless he
  or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  License (the "License") for full details.
  
  Every copy of Aladdin Ghostscript must include a copy of the License,
  normally in a plain ASCII text file named PUBLIC.  The License grants you
  the right to copy, modify and redistribute Aladdin Ghostscript, but only
  under certain conditions described in the License.  Among other things, the
  License requires that the copyright notice and this notice be preserved on
  all copies.
*/

/* zchar1.c */
/* Type 1 character display operator */
#include "ghost.h"
#include "errors.h"
#include "oper.h"
#include "gsstruct.h"
#include "gxfixed.h"
#include "gxmatrix.h"
#include "gschar.h"
#include "gxdevice.h"		/* for gxfont.h */
#include "gxfont.h"
#include "gxfont1.h"
#include "gxtype1.h"
#include "estack.h"
#include "ialloc.h"
#include "ichar.h"
#include "idict.h"
#include "ifont.h"
#include "igstate.h"
#include "store.h"

/* Forward references */
private void op_type1_free(P1(os_ptr));
private int type1_call_OtherSubr(P3(gs_type1_state *, int (*)(P1(os_ptr)), const ref *));
private int type1_continue_dispatch(P3(gs_type1_state *, const byte *, ref *));

/* <string> .type1getsbw <lsbx> <lsby> <wx> <wy> */
private int type1getsbw_continue(P1(os_ptr));
private int type1getsbw_push(P2(os_ptr, gs_type1_state *));
int
ztype1getsbw(register os_ptr op)
{	gs_show_enum *penum = op_show_find();
	gs_font_type1 *pfont = (gs_font_type1 *)gs_currentfont(igs);
	gs_type1_data *pdata;
	gs_type1_state is;		/* stack allocate to avoid sandbars */
	gs_type1_state *pis = &is;
	int code;
	ref other_subr;
	if ( penum == 0 || pfont->FontType != ft_encrypted )
	  return_error(e_undefined);
	check_type(*op, t_string);
	pdata = &pfont->data;
	if ( r_size(op) <= pdata->lenIV )
	  return_error(e_invalidfont);
	check_estack(4);	/* in case we need to do a callback */
	check_ostack(3);	/* for returning the result */
	code = gs_type1_init(pis, penum, NULL,
			     gs_show_in_charpath(penum), pdata->PaintType,
			     pdata);
	if ( code < 0 )
	  return code;
	code = type1_continue_dispatch(pis, op->value.const_bytes,
				       &other_subr);
	switch ( code )
	{
	default:		/* code < 0 or done, error */
		return((code < 0 ? code : gs_note_error(e_invalidfont)));
	case type1_result_callothersubr:	/* unknown OtherSubr */
		return type1_call_OtherSubr(pis, type1getsbw_continue,
					    &other_subr);
	case type1_result_sbw:			/* [h]sbw, done */
		break;
	}
	return type1getsbw_push(op, pis);
}

/* Push the results on the o-stack.  The caller must have done */
/* a check_ostack. */
private int
type1getsbw_push(register os_ptr op, gs_type1_state *pis)
{	push(3);
	make_real(op - 3, fixed2float(pis->lsb.x));
	make_real(op - 2, fixed2float(pis->lsb.y));
	make_real(op - 1, fixed2float(pis->width.x));
	make_real(op, fixed2float(pis->width.y));
	return 0;
}

/* Continue from an OtherSubr callback. */
private int
type1getsbw_continue(os_ptr op)
{	ref other_subr;
	gs_type1_state *pis = r_ptr(esp, gs_type1_state);
	int code;
	check_ostack(3);	/* for returning the result */
	code = type1_continue_dispatch(pis, (const byte *)0, &other_subr);
	op = osp;		/* in case z1_push/pop_proc was called */
	switch ( code )
	{
	default:		/* code < 0 or done, error */
		op_type1_free(op);
		return((code < 0 ? code : gs_note_error(e_invalidfont)));
	case type1_result_callothersubr:	/* unknown OtherSubr */
		push_op_estack(type1getsbw_continue);
		++esp;
		*esp = other_subr;
		return o_push_estack;
	case type1_result_sbw:			/* [h]sbw, done */
		op_type1_free(op);
		type1getsbw_push(op, pis);
		return o_pop_estack;
	}
}

/* <string> .type1addpath - */
/* <string> <lsbx> <lsby> .type1addpath - */
private int type1addpath_continue(P1(os_ptr));
private int op_type1_cleanup(P1(os_ptr));
int
ztype1addpath(register os_ptr op)
{	int code = 0;
	ref other_subr;
	const byte *str;
	gs_show_enum *penum = op_show_find();
	gs_font_type1 *pfont = (gs_font_type1 *)gs_currentfont(igs);
	gs_type1_data *pdata;
	gs_type1_state is;		/* stack allocate to avoid sandbars */
	gs_type1_state *pis = &is;
	float sbxy[2];
	gs_point sbpt;
	gs_point *psbpt = 0;
	os_ptr opc = op;
	if ( penum == 0 || pfont->FontType != ft_encrypted )
	  return_error(e_undefined);
	if ( !r_has_type(opc, t_string) )
	  {	check_op(3);
		code = num_params(op, 2, sbxy);
		if ( code < 0 )
		  return code;
		sbpt.x = sbxy[0];
		sbpt.y = sbxy[1];
		psbpt = &sbpt;
		opc -= 2;
		check_type(*opc, t_string);
	  }
	pdata = &pfont->data;
	if ( r_size(opc) <= pdata->lenIV )
	   {	/* String is empty, or too short.  Just ignore it. */
		goto ret;
	   }
	check_estack(4);	/* in case we need to do a callback */
	code = gs_type1_init(pis, penum, psbpt,
			     gs_show_in_charpath(penum), pdata->PaintType,
			     pdata);
	if ( code < 0 )
	  return code;
	str = opc->value.const_bytes;
cont:	code = type1_continue_dispatch(pis, str, &other_subr);
	switch ( code )
	{
	case 0:			/* all done */
		break;
	default:		/* code < 0, error */
		return code;
	case type1_result_callothersubr:	/* unknown OtherSubr */
		return type1_call_OtherSubr(pis, type1addpath_continue,
					    &other_subr);
	case type1_result_sbw:			/* [h]sbw, just continue */
		str = 0;
		goto cont;
	}
ret:	pop((psbpt == 0 ? 1 : 3));
	return code;
}

/* Continue from an OtherSubr callback. */
private int
type1addpath_continue(os_ptr op)
{	ref other_subr;
	gs_type1_state *pis = r_ptr(esp, gs_type1_state);
	int code;
cont:	code = type1_continue_dispatch(pis, (const byte *)0, &other_subr);
	op = osp;		/* in case z1_push/pop_proc was called */
	switch ( code )
	{
	case 0:			/* all done */
	{	/* Assume the OtherSubrs didn't mess with the o-stack.... */
		int npop = (r_has_type(op, t_string) ? 1 : 3);
		pop(npop);  op -= npop;
		op_type1_free(op);
		return o_pop_estack;
	}
	default:		/* code < 0, error */
		op_type1_free(op);
		if ( code < 0 )
			return code;
		else
			return_error(e_invalidfont);
	case type1_result_callothersubr:	/* unknown OtherSubr */
		push_op_estack(type1addpath_continue);
		++esp;
		*esp = other_subr;
		return o_push_estack;
	case type1_result_sbw:			/* [h]sbw, just continue */
		goto cont;
	}
}

/* ----- Internal procedures ------ */

/* Do a callback to an OtherSubr implemented in PostScript. */
/* The caller must have done a check_estack. */
private int
type1_call_OtherSubr(gs_type1_state *pis, int (*cont)(P1(os_ptr)),
  const ref *pos)
{	/* Move the Type 1 interpreter state to the heap. */
	gs_type1_state *hpis = ialloc_struct(gs_type1_state,
					     &st_gs_type1_state,
					     ".type1addpath");
	if ( hpis == 0 )
	  return_error(e_VMerror);
	*hpis = *pis;
	update_stem_hints(hpis);
	push_mark_estack(es_show, op_type1_cleanup);
	++esp;
	make_istruct(esp, 0, hpis);
	push_op_estack(cont);
	++esp;
	*esp = *pos;
	return o_push_estack;
}

/* Handle the results of gs_type1_interpret. */
private int
type1_continue_dispatch(gs_type1_state *pis, const byte *charstring, ref *pos)
{	int value;
	int code;
cont:	code = gs_type1_interpret(pis, charstring, &value);
	switch ( code )
	{
	case type1_result_seac:
	{	font_data *pfdata = pfont_data(gs_currentfont(igs));
		ref *pcstr;
		ref enc_entry;
		code = array_get(&StandardEncoding, (long)value, &enc_entry);
		if ( code < 0 )
			return code;
		if ( dict_find(&pfdata->CharStrings,
			       &enc_entry, &pcstr) <= 0 )
			return_error(e_undefined);
		if ( !r_has_type(pcstr, t_string) )
			return_error(e_invalidfont);
		charstring = pcstr->value.const_bytes;
	}
		goto cont;
	case type1_result_callothersubr:
	{	/* The Type 1 interpreter handles all known othersubrs, */
		/* so this must be an unknown one. */
		font_data *pfdata = pfont_data(gs_currentfont(igs));
		code = array_get(&pfdata->OtherSubrs, (long)value, pos);
		return (code < 0 ? code : type1_result_callothersubr);
	}
	}
	return code;
}
/* Clean up after a Type 1 callback. */
private int
op_type1_cleanup(os_ptr op)
{	ifree_object(r_ptr(esp + 2, void), "op_type1_cleanup");
	return 0;
}
private void
op_type1_free(os_ptr op)
{	esp -= 2;		/* hpis, cleanup op */
	op_type1_cleanup(op);
}

/* ------ Auxiliary procedures for type 1 fonts ------ */

int
z1_subr_proc(gs_type1_data *pdata, int index, const byte **pstr)
{	/* Get the enclosing font by working backwards. */
	gs_font_type1 *pfont = (gs_font_type1 *)
		((char *)pdata - offset_of(gs_font_type1, data));
	font_data *pfdata = pfont_data(pfont);
	ref *psubr;
	if ( index < 0 || index >= r_size(&pfdata->Subrs) )
		return_error(e_rangecheck);
	psubr = pfdata->Subrs.value.refs + index;
	check_type(*psubr, t_string);
	*pstr = psubr->value.bytes;
	return 0;
}

int
z1_push_proc(gs_type1_data *pdata, const fixed *pf, int count)
{	const fixed *p = pf + count - 1;
	int i;
	check_ostack(count);
	for ( i = 0; i < count; i++, p-- )
	  {	osp++;
		make_real(osp, fixed2float(*p));
	  }
	return 0;
}

int
z1_pop_proc(gs_type1_data *pdata, fixed *pf)
{	float val;
	int code = num_params(osp, 1, &val);
	if ( code < 0 )
		return code;
	*pf = float2fixed(val);
	osp--;
	return 0;
}

/* ------ Initialization procedure ------ */

op_def zchar1_op_defs[] = {
	{"1.type1getsbw", ztype1getsbw},
	{"1.type1addpath", ztype1addpath},
		/* Internal operators */
	{"0%type1getsbw_continue", type1getsbw_continue},
	{"0%type1addpath_continue", type1addpath_continue},
	op_def_end(0)
};
