/* Copyright (C) 1991, 1992, 1993, 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: gschar0.c,v 1.3.4.3.2.1 2003/01/17 00:49:02 giles Exp $ */
/* Composite font decoding for Ghostscript library */
#include "memory_.h"
#include "gx.h"
#include "gserrors.h"
#include "gsstruct.h"
#include "gsfcmap.h"
#include "gxfcmap.h"
#include "gxfixed.h"
#include "gxdevice.h"
#include "gxfont.h"
#include "gxfont0.h"
#include "gxtext.h"

/* Stack up modal composite fonts, down to a non-modal or base font. */
private int
gs_stack_modal_fonts(gs_text_enum_t *pte)
{
    int fdepth = pte->fstack.depth;
    gs_font *cfont = pte->fstack.items[fdepth].font;

    while (cfont->FontType == ft_composite) {
	gs_font_type0 *const cmfont = (gs_font_type0 *) cfont;

	if (!fmap_type_is_modal(cmfont->data.FMapType))
	    break;
	if (fdepth == MAX_FONT_STACK)
	    return_error(gs_error_invalidfont);
	fdepth++;
	cfont = cmfont->data.FDepVector[cmfont->data.Encoding[0]];
	pte->fstack.items[fdepth].font = cfont;
	pte->fstack.items[fdepth].index = 0;
	if_debug2('j', "[j]stacking depth=%d font=0x%lx\n",
		  fdepth, (ulong) cfont);
    }
    pte->fstack.depth = fdepth;
    return 0;
}
/* Initialize the composite font stack for a show enumerator. */
/* Return an error if the data is not a byte string. */
int
gs_type0_init_fstack(gs_text_enum_t *pte, gs_font * pfont)
{
    if (!(pte->text.operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)))
	return_error(gs_error_invalidfont);
    if_debug1('j', "[j]stacking depth=0 font=0x%lx\n",
	      (ulong) pfont);
    pte->fstack.depth = 0;
    pte->fstack.items[0].font = pfont;
    pte->fstack.items[0].index = 0;
    return gs_stack_modal_fonts(pte);
}

/* Select the appropriate descendant of a font. */
/* Uses free variables: pte. */
/* Uses pdata, uses & updates fdepth, sets pfont. */
#define select_descendant(pfont, pdata, fidx, fdepth)\
  if (fidx >= pdata->encoding_size)\
    return_error(gs_error_rangecheck);\
  if (fdepth == MAX_FONT_STACK)\
    return_error(gs_error_invalidfont);\
  pfont = pdata->FDepVector[pdata->Encoding[fidx]];\
  if (++fdepth > orig_depth || pfont != pte->fstack.items[fdepth].font)\
    pte->fstack.items[fdepth].font = pfont, changed = 1;\
  pte->fstack.items[fdepth].index = fidx

/* Get the root EscChar of a composite font, which overrides the EscChar */
/* of descendant fonts. */
private uint
root_esc_char(const gs_text_enum_t *pte)
{
    return ((gs_font_type0 *) (pte->fstack.items[0].font))->data.EscChar;
}

/* Get the next character or glyph from a composite string. */
/* If we run off the end of the string in the middle of a */
/* multi-byte sequence, return gs_error_rangecheck. */
/* If the string is empty, return 2. */
/* If the current (base) font changed, return 1.  Otherwise, return 0. */
int
gs_type0_next_char_glyph(gs_text_enum_t *pte, gs_char *pchr, gs_glyph *pglyph)
{
    const byte *str = pte->text.data.bytes;
    const byte *p = str + pte->index;
    const byte *end = str + pte->text.size;
    int fdepth = pte->fstack.depth;
    int orig_depth = fdepth;
    gs_font *pfont;

#define pfont0 ((gs_font_type0 *)pfont)
    gs_type0_data *pdata;
    uint fidx;
    gs_char chr;
    gs_glyph glyph = gs_no_glyph;
    int changed = 0;

    pte->FontBBox_as_Metrics2.x = pte->FontBBox_as_Metrics2.y = 0;

#define need_left(n)\
  if ( end - p < n ) return_error(gs_error_rangecheck)

    /*
     * Although the Adobe documentation doesn't say anything about this,
     * if the root font is modal and the very first character of the
     * string being decoded is an escape or shift character, then
     * font selection via the escape mechanism works down from the root,
     * rather than up from the lowest modal font.  (This was first
     * reported by Norio Katayama, and confirmed by someone at Adobe.)
     */

    if (pte->index == 0) {
	int idepth = 0;

	pfont = pte->fstack.items[0].font;
	for (; pfont->FontType == ft_composite;) {
	    fmap_type fmt = (pdata = &pfont0->data)->FMapType;

	    if (p == end)
		return 2;
	    chr = *p;
	    switch (fmt) {
		case fmap_escape:
		    if (chr != root_esc_char(pte))
			break;
		    need_left(2);
		    fidx = p[1];
		    p += 2;
		    if_debug1('j', "[j]from root: escape %d\n", fidx);
		  rdown:select_descendant(pfont, pdata, fidx, idepth);
		    if_debug2('j', "[j]... new depth=%d, new font=0x%lx\n",
			      idepth, (ulong) pfont);
		    continue;
		case fmap_double_escape:
		    if (chr != root_esc_char(pte))
			break;
		    need_left(2);
		    fidx = p[1];
		    p += 2;
		    if (fidx == chr) {
			need_left(1);
			fidx = *p++ + 256;
		    }
		    if_debug1('j', "[j]from root: double escape %d\n", fidx);
		    goto rdown;
		case fmap_shift:
		    if (chr == pdata->ShiftIn)
			fidx = 0;
		    else if (chr == pdata->ShiftOut)
			fidx = 1;
		    else
			break;
		    p++;
		    if_debug1('j', "[j]from root: shift %d\n", fidx);
		    goto rdown;
		default:
		    break;
	    }
	    break;
	}
	/* If we saw any initial escapes or shifts, */
	/* compute a new initial base font. */
	if (idepth != 0) {
	    int code;

	    pte->fstack.depth = idepth;
	    code = gs_stack_modal_fonts(pte);
	    if (code < 0)
		return code;
	    if (pte->fstack.depth > idepth)
		changed = 1;
	    orig_depth = fdepth = pte->fstack.depth;
	}
    }
    /* Handle initial escapes or shifts. */

  up:if (p == end)
	return 2;
    chr = *p;
    while (fdepth > 0) {
	pfont = pte->fstack.items[fdepth - 1].font;
	pdata = &pfont0->data;
	switch (pdata->FMapType) {
	    default:		/* non-modal */
		fdepth--;
		continue;

	    case fmap_escape:
		if (chr != root_esc_char(pte))
		    break;
		need_left(2);
		fidx = *++p;
		if_debug1('j', "[j]next: escape %d\n", fidx);
		/* Per Adobe, if we get an escape at the root, */
		/* treat it as an ordinary character (font index). */
		if (fidx == chr && fdepth > 1) {
		    fdepth--;
		    goto up;
		}
	      down:if (++p == end)
		    return 2;
		chr = *p;
		fdepth--;
		do {
		    select_descendant(pfont, pdata, fidx, fdepth);
		    if_debug3('j', "[j]down from modal: new depth=%d, index=%d, new font=0x%lx\n",
			      fdepth, fidx, (ulong) pfont);
		    if (pfont->FontType != ft_composite)
			break;
		    pdata = &pfont0->data;
		    fidx = 0;
		}
		while (pdata->FMapType == fmap_escape);
		continue;

	    case fmap_double_escape:
		if (chr != root_esc_char(pte))
		    break;
		need_left(2);
		fidx = *++p;
		if (fidx == chr) {
		    need_left(2);
		    fidx = *++p + 256;
		}
		if_debug1('j', "[j]next: double escape %d\n", fidx);
		goto down;

	    case fmap_shift:
		if (chr == pdata->ShiftIn)
		    fidx = 0;
		else if (chr == pdata->ShiftOut)
		    fidx = 1;
		else
		    break;
		if_debug1('j', "[j]next: shift %d\n", fidx);
		goto down;
	}
	break;
    }
    /* At this point, chr == *p. */
    /* (This is important to know for CMap'ed fonts.) */
    p++;

    /*
     * Now handle non-modal descendants.
     * The PostScript language manual has some confusing
     * wording about the parent supplying the "first part"
     * of the child's decoding information; what this means
     * is not (as one might imagine) the font index, but
     * simply the first byte of the data.
     */

    while ((pfont = pte->fstack.items[fdepth].font)->FontType == ft_composite) {
	pdata = &pfont0->data;
	switch (pdata->FMapType) {
	    default:		/* can't happen */
		return_error(gs_error_invalidfont);

	    case fmap_8_8:
		need_left(1);
		fidx = chr;
		chr = *p++;
		if_debug2('J', "[J]8/8 index=%d, char=%ld\n",
			  fidx, chr);
		break;

	    case fmap_1_7:
		fidx = chr >> 7;
		chr &= 0x7f;
		if_debug2('J', "[J]1/7 index=%d, char=%ld\n",
			  fidx, chr);
		break;

	    case fmap_9_7:
		need_left(1);
		fidx = ((uint) chr << 1) + (*p >> 7);
		chr = *p & 0x7f;
		if_debug2('J', "[J]9/7 index=%d, char=%ld\n",
			  fidx, chr);
		p++;
		break;

	    case fmap_SubsVector:
		{
		    int width = pdata->subs_width;
		    uint subs_count = pdata->subs_size;
		    const byte *psv = pdata->SubsVector.data;

#define subs_loop(subs_elt, width)\
  while ( subs_count != 0 && tchr >= (schr = subs_elt) )\
    subs_count--, tchr -= schr, psv += width;\
  chr = tchr; p += width - 1; break

		    switch (width) {
			default:	/* can't happen */
			    return_error(gs_error_invalidfont);
			case 1:
			    {
				byte tchr = (byte) chr, schr;

				subs_loop(*psv, 1);
			    }
			case 2:
			    need_left(1);
#define w2(p) (((ushort)*p << 8) + p[1])
			    {
				ushort tchr = ((ushort) chr << 8) + *p,
				       schr;

				subs_loop(w2(psv), 2);
			    }
			case 3:
			    need_left(2);
#define w3(p) (((ulong)*p << 16) + ((uint)p[1] << 8) + p[2])
			    {
				ulong tchr = ((ulong) chr << 16) + w2(p),
				      schr;

				subs_loop(w3(psv), 3);
			    }
			case 4:
			    need_left(3);
#define w4(p) (((ulong)*p << 24) + ((ulong)p[1] << 16) + ((uint)p[2] << 8) + p[3])
			    {
				ulong tchr = ((ulong) chr << 24) + w3(p),
				      schr;

				subs_loop(w4(psv), 4);
			    }
#undef w2
#undef w3
#undef w4
#undef subs_loop
		    }
		    fidx = pdata->subs_size - subs_count;
		    if_debug2('J', "[J]SubsVector index=%d, char=%ld\n",
			      fidx, chr);
		    break;
		}

	    case fmap_CMap:
		{
		    gs_const_string cstr;
		    uint mindex = p - str - 1;	/* p was incremented */
		    int code;

                    /*
                     * When decoding an FMapType4 or 5, the value
                     * of chr is modified; when an FMapType9 (CMap)
                     * composite font is used as a decendant font,
                     * we have to pass the text including a modified
                     * chr. Check whether chr has been modified, and
                     * if so, construct and pass a modified buffer.
                     */
		    if (*(p - 1) != chr) {
			byte substr[MAX_CMAP_CODE_SIZE];
			int submindex = 0;
			if_debug2('j', "[j] *(p-1) 0x%02x != chr 0x%02x, modified str should be passed\n",
				*(p-1), (byte)chr);
			memcpy(substr, p - 1,
				min(MAX_CMAP_CODE_SIZE, end - p + 1));
			substr[0] = chr;
			cstr.data = substr;
			cstr.size = min(MAX_CMAP_CODE_SIZE, end - p + 1);
			if (gs_debug_c('j')) {
			    dlprintf("[j] original str(");
			    debug_print_string_hex(str, end - str);
			    dlprintf(") -> modified substr(");
			    debug_print_string_hex(cstr.data, cstr.size);
			    dlprintf(")\n");
			}
			code = gs_cmap_decode_next(pdata->CMap, &cstr,
					(unsigned int*) &submindex, &fidx,
						   &chr, &glyph);
			mindex += submindex;
		    } else {
			cstr.data = str;
			cstr.size = end - str;
			code = gs_cmap_decode_next(pdata->CMap, &cstr, &mindex,
					       &fidx, &chr, &glyph);
		    }
		    if (code < 0)
			return code;
		    pte->cmap_code = code; /* hack for widthshow */
		    p = str + mindex;
		    if_debug3('J', "[J]CMap returns %d, chr=0x%lx, glyph=0x%lx\n",
			      code, (ulong) chr, (ulong) glyph);
		    if (code == 0) {
			if (glyph == gs_no_glyph) {
			    glyph = gs_min_cid_glyph;
			    if_debug0('J', "... undefined\n");
			    goto done;
			}
		    } else
			chr = (gs_char) glyph, glyph = gs_no_glyph;
		    /****** RESCAN chr IF DESCENDANT IS CMAP'ED ******/
		    break;
		}

	}

	select_descendant(pfont, pdata, fidx, fdepth);
	if_debug2('J', "... new depth=%d, new font=0x%lx\n",
		  fdepth, (ulong) pfont);
	/* FontBBox may be used as metrics2 with WMode=1 :
	*/
	if (pfont->FontType == ft_CID_encrypted ||
	    pfont->FontType == ft_CID_TrueType
	    ) {
	    gs_font_base *pfb = (gs_font_base *)pfont;

	    pte->FontBBox_as_Metrics2 = pfb->FontBBox.q;
	}
    }
done:
    *pchr = chr;
    *pglyph = glyph;
    /* Update the pointer into the original string, but only if */
    /* we didn't switch over to parsing a code from a CMap. */
    if (str == pte->text.data.bytes)
	pte->index = p - str;
    pte->fstack.depth = fdepth;
    if_debug4('J', "[J]depth=%d font=0x%lx index=%d changed=%d\n",
	      fdepth, (ulong) pte->fstack.items[fdepth].font,
	      pte->fstack.items[fdepth].index, changed);
    return changed;
#undef pfont0
}
