/* Copyright (C) 1999 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.
 */

/*$Id: gschar.c $ */
/* Character writing "operators" for Ghostscript library */
#include "gx.h"
#include "gserrors.h"
#include "gsstruct.h"
#include "gsmatrix.h"		/* for gscoord.h */
#include "gscoord.h"		/* for gs_idtransform */
#include "gzstate.h"
#include "gxdevice.h"
#include "gxdevmem.h"
#include "gxchar.h"
#include "gxfont.h"

/* Forward declarations */
private int show_n_begin(P4(gs_show_enum *penum, gs_state *pgs, int code,
			    gs_text_enum_t *pte));

/* Structure descriptors */
extern_st(st_gs_show_enum);

/* ------ String writing operators ------ */

/* Free the contents of a show enumerator. */
void
gs_show_enum_release(gs_show_enum * penum, gs_memory_t * emem)
{
    if (penum->text.operation)	/* otherwise, never initialized */
	penum->procs->release((gs_text_enum_t *)penum, "gs_show_enum_release");
    if (emem != 0)
	gs_free_object(emem, penum, "gs_show_enum_release");
}

/* show[_n] */
int
gs_show_n_init(gs_show_enum * penum, gs_state * pgs,
	       const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code = gs_show_begin(pgs, (const byte *)str, size, pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* ashow[_n] */
int
gs_ashow_n_init(gs_show_enum * penum, gs_state * pgs,
		floatp ax, floatp ay, const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code = gs_ashow_begin(pgs, ax, ay, (const byte *)str, size,
			      pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* widthshow[_n] */
int
gs_widthshow_n_init(gs_show_enum * penum, gs_state * pgs,
		    floatp cx, floatp cy, gs_char chr,
		    const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code = gs_widthshow_begin(pgs, cx, cy, chr, (const byte *)str, size,
				  pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* awidthshow[_n] */
int
gs_awidthshow_n_init(gs_show_enum * penum, gs_state * pgs,
		     floatp cx, floatp cy, gs_char chr, floatp ax, floatp ay,
		     const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code = gs_awidthshow_begin(pgs, cx, cy, chr, ax, ay,
				   (const byte *)str, size, pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* kshow[_n] */
int
gs_kshow_n_init(gs_show_enum * penum,
		gs_state * pgs, const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code;

    switch (pgs->font->FontType) {
    case ft_composite:
    case ft_CID_encrypted:
    case ft_CID_user_defined:
    case ft_CID_TrueType:
    case ft_CID_bitmap:
	return_error(gs_error_invalidfont);
    default:
	break;
    }
    code = gs_kshow_begin(pgs, (const byte *)str, size, pgs->memory, &pte);
    return show_n_begin(penum, pgs, code, pte);
}

/* xyshow[_n] */
int
gs_xyshow_n_init(gs_show_enum * penum,
		 gs_state * pgs, const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code = gs_xyshow_begin(pgs, (const byte *)str, size, NULL, NULL, 0,
			       pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* glyphshow */
int
gs_glyphshow_init(gs_show_enum * penum, gs_state * pgs, gs_glyph glyph)
{
    gs_text_enum_t *pte;
    int code = gs_glyphshow_begin(pgs, glyph, pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}
int
gs_glyphpath_init(gs_show_enum * penum, gs_state * pgs, gs_glyph glyph,
		  bool stroke_path)
{
    gs_text_enum_t *pte;
    int code = gs_glyphpath_begin(pgs, glyph, stroke_path, pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* ------ Related operators ------ */

/* cshow[_n] */
int
gs_cshow_n_init(gs_show_enum * penum,
		gs_state * pgs, const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code = gs_cshow_begin(pgs, (const byte *)str, size, pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* stringwidth[_n] */
int
gs_stringwidth_n_init(gs_show_enum * penum, gs_state * pgs,
		      const char *str, uint size)
{
    gs_text_enum_t *pte;
    int code = gs_stringwidth_begin(pgs, (const byte *)str, size,
				    pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* charpath[_n] */
int
gs_charpath_n_init(gs_show_enum * penum, gs_state * pgs,
		   const char *str, uint size, bool stroke_path)
{
    gs_text_enum_t *pte;
    int code = gs_charpath_begin(pgs, (const byte *)str, size, stroke_path,
				 pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* charboxpath[_n] */
int
gs_charboxpath_n_init(gs_show_enum * penum, gs_state * pgs,
		      const char *str, uint size, bool use_boxes)
{
    gs_text_enum_t *pte;
    int code = gs_charboxpath_begin(pgs, (const byte *)str, size, use_boxes,
				    pgs->memory, &pte);

    return show_n_begin(penum, pgs, code, pte);
}

/* ------ Width/cache operators ------ */

/* setcachedevice */
/* The elements of pw are: wx, wy, llx, lly, urx, ury. */
/* Note that this returns 1 if we just set up the cache device. */
int
gs_setcachedevice_double(gs_show_enum *penum, gs_state *pgs, const double *pw)
{
    if (penum->pgs != pgs)
	return_error(gs_error_rangecheck);
    return gs_text_setcachedevice((gs_text_enum_t *)penum, pw);
}
/* The _float procedure is strictly for backward compatibility. */
int
gs_setcachedevice_float(gs_show_enum * penum, gs_state * pgs, const float *pw)
{
    double w[6];
    int i;

    for (i = 0; i < 6; ++i)
	w[i] = pw[i];
    return gs_setcachedevice_double(penum, pgs, w);
}

/* setcachedevice2 */
/* The elements of pw2 are: w0x, w0y, llx, lly, urx, ury, w1x, w1y, vx, vy. */
/* Note that this returns 1 if we just set up the cache device. */
int
gs_setcachedevice2_double(gs_show_enum * penum, gs_state * pgs,
			  const double *pw2)
{
    if (penum->pgs != pgs)
	return_error(gs_error_rangecheck);
    return gs_text_setcachedevice2((gs_text_enum_t *)penum, pw2);
}
/* The _float procedure is strictly for backward compatibility. */
int
gs_setcachedevice2_float(gs_show_enum * penum, gs_state * pgs, const float *pw2)
{
    double w2[10];
    int i;

    for (i = 0; i < 10; ++i)
	w2[i] = pw2[i];
    return gs_setcachedevice2_double(penum, pgs, w2);
}

/* setcharwidth */
/* Note that this returns 1 if the current show operation is */
/* non-displaying (stringwidth or cshow). */
int
gs_setcharwidth(gs_show_enum * penum, gs_state * pgs,
		floatp wx, floatp wy)
{
    double w[2];

    if (penum->pgs != pgs)
	return_error(gs_error_rangecheck);
    w[0] = wx, w[1] = wy;
    return gs_text_setcharwidth((gs_text_enum_t *)penum, w);
}

/* ------ Enumerator ------ */

/* Do the next step of a show (or stringwidth) operation */
int
gs_show_next(gs_show_enum * penum)
{
    return gs_text_process((gs_text_enum_t *)penum);
}

/*
 * Return true if we only need the width from the rasterizer
 * and can short-circuit the full rendering of the character,
 * false if we need the actual character bits.
 */
bool
gs_show_width_only(const gs_show_enum * penum)
{
    return gs_text_is_width_only((const gs_text_enum_t *)penum);
}

/* ------ Accessors ------ */

/* Return the current character for rendering. */
gs_char
gs_show_current_char(const gs_show_enum * penum)
{
    return gs_text_current_char((const gs_text_enum_t *)penum);
}

/* Return the current glyph for rendering. */
gs_glyph
gs_show_current_glyph(const gs_show_enum * penum)
{
    return gs_text_current_glyph((const gs_text_enum_t *)penum);
}

/* Return the width of the just-enumerated character (for cshow). */
int
gs_show_current_width(const gs_show_enum * penum, gs_point * ppt)
{
    return gs_text_current_width((const gs_text_enum_t *)penum, ppt);
}

/* Return the just-displayed character for kerning. */
gs_char
gs_kshow_previous_char(const gs_show_enum * penum)
{
    return gs_text_current_char((const gs_text_enum_t *)penum);
}

/* Return the about-to-be-displayed character for kerning. */
gs_char
gs_kshow_next_char(const gs_show_enum * penum)
{
    return penum->text.data.bytes[penum->index];
}

/* Return the accumulated width for stringwidth. */
void
gs_show_width(const gs_show_enum * penum, gs_point * ppt)
{
    gs_text_total_width((const gs_text_enum_t *)penum, ppt);
}

/* ------ Internal routines ------ */

/*
 * Force the enumerator to be a gs_show_enum *, which the current
 * implementation code requires.
 */
private int
show_n_begin(gs_show_enum *penum, gs_state *pgs, int code, gs_text_enum_t *pte)
{
    if (code < 0)
	return code;
    if (gs_object_type(pgs->memory, pte) != &st_gs_show_enum) {
	/* Use the default implementation. */
	gx_device *dev = pgs->device;
	gs_text_params_t text;
	gs_memory_t *mem = pte->memory;
	dev_proc_text_begin((*text_begin)) = dev_proc(dev, text_begin);

	text = pte->text;
	gs_text_release(pte, "show_n_begin");
	/* Temporarily reset the text_begin procedure to the default. */
	set_dev_proc(dev, text_begin, gx_default_text_begin);
	code = gs_text_begin(pgs, &text, mem, &pte);
	set_dev_proc(dev, text_begin, text_begin);
	if (code < 0)
	    return code;
    }
    /* Now we know pte points to a gs_show_enum. */
    *penum = *(gs_show_enum *)pte;
    gs_free_object(pgs->memory, pte, "show_n_begin");
    return code;
}
