/* Copyright (C) 1991, 1992 Aladdin Enterprises.  All rights reserved.

This file is part of Ghostscript.

Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing.  Refer
to the Ghostscript General Public License for full details.

Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License.  A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities.  It should be in a file named COPYING.  Among other
things, the copyright notice and this notice must be preserved on all
copies.  */

/* gschar0.c */
/* Composite font decoding for Ghostscript library */
/****** DOESN'T WORK. DON'T TRY TO USE IT. ******/
#include "gx.h"
#include "gserrors.h"
#include "gxfixed.h"
#include "gxmatrix.h"			/* for gzstate.h */
#include "gzstate.h"			/* must precede gzdevice */
#include "gzdevice.h"			/* must precede gxchar */
#include "gxdevmem.h"
#include "gxchar.h"
#include "gxfont.h"

/* Select the appropriate descendant of a font. */
/* Free variables: fdepth, index, p. */
#define select_descendant(pfont, pdata, n)\
  if ( (n) >= pdata->encoding_size )\
    return_error(gs_error_rangecheck);\
  if ( fdepth >= max_font_depth - 1 )\
    return_error(gs_error_invalidfont);\
  pfont = pdata->FDepVector[pdata->Encoding[n]];\
  penum->fstack[penum->fdepth = ++fdepth] = pfont;\
  penum->index = index = p - penum->str

/* Define a structure for a (character code, font index) pair. */
typedef struct {
	uint fidx;
	gs_char chr;
} mapped_char;

/* Forward declarations */
private int map_SubsVector(P4(const byte *, int, gs_type0_data *, mapped_char *));

/* Get the next character (to penum->chr) and font (to penum->pfont)
/* 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 1.  Otherwise, return 0. */
int
gs_type0_next(gs_show_enum *penum)
{	uint index = penum->index;
	const byte *p = penum->str + index;
	uint size = penum->size;
	int fdepth = 0;
	gs_font *pfont = penum->fstack[0];
	gs_type0_data *pdata;
	mapped_char mc;
	int code;
	if ( size == index ) return 1;
#define need_left(n)\
  if ( size - index < n ) return_error(gs_error_rangecheck)
top:	pdata = &pfont->data.type0_data;
top1:	switch ( pdata->FMapType )
	   {
	default:			/* can't happen */
		return_error(gs_error_invalidfont);

		/* ------ Non-modal mappings ------ */

	case fmap_8_8:
		need_left(2);
		mc.fidx = *p++;
		mc.chr = *p++;
		break;

	case fmap_1_7:
		mc.fidx = *p >> 7;
		mc.chr = *p++ & 0x7f;
		break;

	case fmap_9_7:
		need_left(2);
		mc.fidx = ((uint)*p << 1) + (p[1] >> 7);
		mc.chr = p[1] & 0x7f;
		p += 2;
		break;

	case fmap_SubsVector:
	{	int width = pdata->subs_width;
		need_left(width);
		code = map_SubsVector(p, width, pdata, &mc);
		if ( code < 0 ) return code;
		p += width;
		break;
	}

		/* ------ Modal mappings ------ */

	case fmap_escape:
		if ( *p != pdata->EscChar ) goto same_mode;
		need_left(2);
		mc.fidx = p[1]; p += 2; goto cmode;

	case fmap_double_escape:
		if ( *p != pdata->EscChar ) goto same_mode;
		need_left(2);
		if ( p[1] == *p )
		   {	need_left(3);
			mc.fidx = p[2] + 256; p += 3; goto cmode;
		   }
		mc.fidx = p[1]; p += 2; goto cmode;
		/* Enter a mode. */
cmode:		penum->fdepth = 1;
		break;

	case fmap_shift:
		if ( *p == pdata->ShiftIn ) mc.fidx = 0;
		else if ( *p == pdata->ShiftOut ) mc.fidx = 1;
		else goto same_mode;
		select_descendant(pfont, pdata, mc.fidx);
		goto top;

		/* Stay in the same mode. */
same_mode:	for ( ; ; )
		{	pfont = penum->fstack[++fdepth];
			if ( pfont->FontType != ft_composite) break;
			pdata = &pfont->data.type0_data;
			if ( pdata->FMapType != fmap_escape )
				goto top1;	/* must be non-modal */
		}
/******		penum->chr = *p++;	******/
		penum->index = index + 1;
		return 0;

	   }

	/* Control continues here for non-modal mappings. */
remap:	select_descendant(pfont, pdata, mc.fidx);
	/****** WHERE DOES THE FOLLOWING COMMENT BELONG?? ******/
	/* We pre-checked the encoding vector, so we know that */
	/* fidx is now a legal subscript for FDepVector. */
	if ( pfont->FontType == ft_composite )	/* must be non-modal */
	{	pdata = &pfont->data.type0_data;
		switch ( pdata->FMapType )
		{

		case fmap_8_8:
			need_left(1);
			mc.fidx = mc.chr;
			mc.chr = *p++;
			break;

		case fmap_1_7:
		case fmap_9_7:
			need_left(1);
			mc.fidx = mc.chr;
			mc.chr = *p++ & 0x7f;
			break;

		case fmap_SubsVector:
		{	int width = pdata->subs_width;
			/* It isn't clear what to use for the input */
			/* to the mapping algorithm.... */
			byte c[4];
			int i = width;
			while ( --i >= 0 )
				c[i] = (byte)mc.chr, mc.chr >>= 8;
			code = map_SubsVector(c, width, pdata, &mc);
			if ( code < 0 ) return code;
			break;
		}

		default:		/* can't happen */
			return_error(gs_error_invalidfont);
		}
		goto remap;
	}
	penum->pfont = pfont;
/******	penum->chr = mc.chr;	******/
	return 0;
}

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

/* Get the font index for a character from a SubsVector. */
private int
map_SubsVector(const byte *p, int width, gs_type0_data *pdata, mapped_char *pmc)
{	uint subs_count = pdata->subs_size;
	const byte *psv = pdata->SubsVector;
#define subs_loop(subs_elt, width)\
  while ( subs_count != 0 && chr >= (schr = subs_elt) )\
    subs_count--, chr -= schr, psv += width;\
  pmc->chr = chr; break
	switch ( width )
	{
	default:		/* can't happen */
		return_error(gs_error_invalidfont);
	case 1:
	   {	byte chr = *p, schr;
		subs_loop(*psv, 1);
	   }
	case 2:
#define w2(p) (((ushort)*p << 8) + p[1])
	   {	ushort chr = w2(p), schr;
		subs_loop(w2(psv), 2);
	   }
#undef w2
	case 3:
#define w3(p) (((ulong)*p << 16) + ((uint)p[1] << 8) + p[2])
	   {	ulong chr = w3(p), schr;
		subs_loop(w3(psv), 3);
	   }
#undef w3
	case 4:
#define w4(p)\
  (((ulong)*p << 24) + ((ulong)p[1] << 16) + ((uint)p[2] << 8) + p[3])
	   {	ulong chr = w4(p), schr;
		subs_loop(w4(psv), 4);
	   }
#undef w4
#undef subs_loop
	}
	pmc->fidx = pdata->subs_size - subs_count;
	return 0;
}
