/* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  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 version 2
  as published by the Free Software Foundation.


  This software is provided AS-IS with no warranty, either express or
  implied. That is, 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.
  
  For more information about licensing, please refer to
  http://www.ghostscript.com/licensing/. For information on
  commercial licensing, go to http://www.artifex.com/licensing/ or
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
*/

/* $Id: gxchar.c,v 1.17 2002/10/31 08:34:51 ray Exp $ */
/* Default implementation of text writing */
#include "gx.h"
#include "memory_.h"
#include "string_.h"
#include "gserrors.h"
#include "gsstruct.h"
#include "gxfixed.h"		/* ditto */
#include "gxarith.h"
#include "gxmatrix.h"
#include "gzstate.h"
#include "gxcoord.h"
#include "gxdevice.h"
#include "gxdevmem.h"
#include "gxchar.h"
#include "gxfont.h"
#include "gxfont0.h"
#include "gxfcache.h"
#include "gspath.h"
#include "gzpath.h"
#include "gxfcid.h"

/* Define whether or not to cache characters rotated by angles other than */
/* multiples of 90 degrees. */
private const bool CACHE_ROTATED_CHARS = true;

/* Define whether or not to oversample characters at small sizes. */
private const bool OVERSAMPLE = true;

/* Define the maximum size of a full temporary bitmap when rasterizing, */
/* in bits (not bytes). */
private const uint MAX_TEMP_BITMAP_BITS = 80000;

/* Define whether the show operation uses the character outline data, */
/* as opposed to just needing the width (or nothing). */
#define SHOW_USES_OUTLINE(penum)\
  !SHOW_IS(penum, TEXT_DO_NONE | TEXT_DO_CHARWIDTH)

/* Structure descriptors */
public_st_gs_show_enum();
extern_st(st_gs_text_enum);
extern_st(st_gs_state);		/* only for testing */
private 
ENUM_PTRS_BEGIN(show_enum_enum_ptrs)
     return ENUM_USING(st_gs_text_enum, vptr, size, index - 5);
ENUM_PTR(0, gs_show_enum, pgs);
ENUM_PTR(1, gs_show_enum, show_gstate);
ENUM_PTR3(2, gs_show_enum, dev_cache, dev_cache2, dev_null);
ENUM_PTRS_END
private RELOC_PTRS_WITH(show_enum_reloc_ptrs, gs_show_enum *eptr)
{
    RELOC_USING(st_gs_text_enum, vptr, size);		/* superclass */
    RELOC_VAR(eptr->pgs);
    RELOC_VAR(eptr->show_gstate);
    RELOC_PTR3(gs_show_enum, dev_cache, dev_cache2, dev_null);
}
RELOC_PTRS_END

/* Forward declarations */
private int continue_kshow(gs_show_enum *);
private int continue_show(gs_show_enum *);
private int continue_show_update(gs_show_enum *);
private void show_set_scale(const gs_show_enum *, gs_log2_scale_point *log2_scale);
private int show_cache_setup(gs_show_enum *);
private int show_state_setup(gs_show_enum *);
private int show_origin_setup(gs_state *, fixed, fixed, gs_char_path_mode);

/* Accessors for current_char and current_glyph. */
#define CURRENT_CHAR(penum) ((penum)->returned.current_char)
#define SET_CURRENT_CHAR(penum, chr)\
  ((penum)->returned.current_char = (chr))
#define CURRENT_GLYPH(penum) ((penum)->returned.current_glyph)
#define SET_CURRENT_GLYPH(penum, glyph)\
  ((penum)->returned.current_glyph = (glyph))

/* Allocate a show enumerator. */
gs_show_enum *
gs_show_enum_alloc(gs_memory_t * mem, gs_state * pgs, client_name_t cname)
{
    gs_show_enum *penum;

    rc_alloc_struct_1(penum, gs_show_enum, &st_gs_show_enum, mem,
		      return 0, cname);
    penum->rc.free = rc_free_text_enum;
    penum->auto_release = true;	/* old API */
    /* Initialize pointers for GC */
    penum->text.operation = 0;	/* no pointers relevant */
    penum->dev = 0;
    penum->pgs = pgs;
    penum->show_gstate = 0;
    penum->dev_cache = 0;
    penum->dev_cache2 = 0;
    penum->fapi_log2_scale.x = penum->fapi_log2_scale.y = -1;
    penum->dev_null = 0;
    penum->fstack.depth = -1;
    return penum;
}

/* ------ Driver procedure ------ */

private text_enum_proc_resync(gx_show_text_resync);
private text_enum_proc_process(gx_show_text_process);
private text_enum_proc_is_width_only(gx_show_text_is_width_only);
private text_enum_proc_current_width(gx_show_text_current_width);
private text_enum_proc_set_cache(gx_show_text_set_cache);
private text_enum_proc_retry(gx_show_text_retry);
private text_enum_proc_release(gx_show_text_release); /* not default */

private const gs_text_enum_procs_t default_text_procs = {
    gx_show_text_resync, gx_show_text_process,
    gx_show_text_is_width_only, gx_show_text_current_width,
    gx_show_text_set_cache, gx_show_text_retry,
    gx_show_text_release
};

int
gx_default_text_begin(gx_device * dev, gs_imager_state * pis,
		      const gs_text_params_t * text, gs_font * font,
		      gx_path * path, const gx_device_color * pdcolor,
		      const gx_clip_path * pcpath,
		      gs_memory_t * mem, gs_text_enum_t ** ppte)
{
    uint operation = text->operation;
    bool propagate_charpath = (operation & TEXT_DO_DRAW) != 0;
    int code;
    gs_state *pgs = (gs_state *)pis;
    gs_show_enum *penum;

    /*
     * For the moment, require pis to be a gs_state *, since all the
     * procedures for character rendering expect it.
     */
    if (gs_object_type(mem, pis) != &st_gs_state)
	return_error(gs_error_Fatal);
    penum = gs_show_enum_alloc(mem, pgs, "gx_default_text_begin");
    if (!penum)
	return_error(gs_error_VMerror);
    code = gs_text_enum_init((gs_text_enum_t *)penum, &default_text_procs,
			     dev, pis, text, font, path, pdcolor, pcpath, mem);
    if (code < 0) {
	gs_free_object(mem, penum, "gx_default_text_begin");
	return code;
    }
    penum->auto_release = false; /* new API */
    penum->level = pgs->level;
    if (operation & TEXT_DO_ANY_CHARPATH)
	penum->charpath_flag =
	    (operation & TEXT_DO_FALSE_CHARPATH ? cpm_false_charpath :
	     operation & TEXT_DO_TRUE_CHARPATH ? cpm_true_charpath :
	     operation & TEXT_DO_FALSE_CHARBOXPATH ? cpm_false_charboxpath :
	     operation & TEXT_DO_TRUE_CHARBOXPATH ? cpm_true_charboxpath :
	     operation & TEXT_DO_CHARWIDTH ? cpm_charwidth :
	     cpm_show /* can't happen */ );
    else
	penum->charpath_flag =
	    (propagate_charpath ? pgs->in_charpath : cpm_show);
    penum->cc = 0;
    penum->continue_proc = continue_show;
    /* Note: show_state_setup may reset can_cache. */
    switch (penum->charpath_flag) {
    case cpm_false_charpath: case cpm_true_charpath:
	penum->can_cache = -1; break;
    case cpm_false_charboxpath: case cpm_true_charboxpath:
	penum->can_cache = 0; break;
    case cpm_charwidth:
    default:			/* cpm_show */
	penum->can_cache = 1; break;
    }
    code = show_state_setup(penum);
    if (code < 0)
	return code;
    penum->show_gstate =
	(propagate_charpath && (pgs->in_charpath != 0) ?
	 pgs->show_gstate : pgs);
    if (!(~operation & (TEXT_DO_NONE | TEXT_RETURN_WIDTH))) {
	/* This is stringwidth. */
	gx_device_null *dev_null =
	    gs_alloc_struct(mem, gx_device_null, &st_device_null,
			    "stringwidth(dev_null)");

	if (dev_null == 0)
	    return_error(gs_error_VMerror);
	/* Do an extra gsave and suppress output */
	if ((code = gs_gsave(pgs)) < 0)
	    return code;
	penum->level = pgs->level;	/* for level check in show_update */
	/* Set up a null device that forwards xfont requests properly. */
	gs_make_null_device(dev_null, gs_currentdevice_inline(pgs), mem);
	pgs->ctm_default_set = false;
	penum->dev_null = dev_null;
	/* Retain this device, since it is referenced from the enumerator. */
	gx_device_retain((gx_device *)dev_null, true);
	gs_setdevice_no_init(pgs, (gx_device *) dev_null);
	/* Establish an arbitrary translation and current point. */
	gs_newpath(pgs);
	gx_translate_to_fixed(pgs, fixed_0, fixed_0);
	code = gx_path_add_point(pgs->path, fixed_0, fixed_0);
	if (code < 0)
	    return code;
    }
    *ppte = (gs_text_enum_t *)penum;
    return 0;
}

/* ------ Width/cache setting ------ */

private int
    set_cache_device(gs_show_enum *penum, gs_state *pgs,
		     floatp llx, floatp lly, floatp urx, floatp ury);

/* This is the default implementation of text enumerator set_cache. */
private int
gx_show_text_set_cache(gs_text_enum_t *pte, const double *pw,
			  gs_text_cache_control_t control)
{
    gs_show_enum *const penum = (gs_show_enum *)pte;
    gs_state *pgs = penum->pgs;

    switch (control) {
    case TEXT_SET_CHAR_WIDTH:
	return set_char_width(penum, pgs, pw[0], pw[1]);
    case TEXT_SET_CACHE_DEVICE: {
	int code = set_char_width(penum, pgs, pw[0], pw[1]);	/* default is don't cache */

	if (code < 0)
	    return code;
	if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) /* cshow */
            return code;
	return set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
    }
    case TEXT_SET_CACHE_DEVICE2: {
	int code;
	bool retry = (penum->width_status == sws_retry);

	if (gs_rootfont(pgs)->WMode) {
	    float vx = pw[8], vy = pw[9];
	    gs_fixed_point pvxy, dvxy;

	    gs_fixed_point rewind_pvxy;
	    int rewind_code;

	    if ((code = gs_point_transform2fixed(&pgs->ctm, -vx, -vy, &pvxy)) < 0 ||
		(code = gs_distance_transform2fixed(&pgs->ctm, vx, vy, &dvxy)) < 0
		)
		return 0;		/* don't cache */
	    if ((code = set_char_width(penum, pgs, pw[6], pw[7])) < 0)
		return code;
	    if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE))
		return code;
	    /* Adjust the origin by (vx, vy). */
	    gx_translate_to_fixed(pgs, pvxy.x, pvxy.y);
	    code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
	    if (code != 1) {
	        if (retry) {
		   rewind_code = gs_point_transform2fixed(&pgs->ctm, vx, vy, &rewind_pvxy);
		   if (rewind_code < 0) {
		       /* If the control passes here, something is wrong. */
		       return_error(gs_error_unregistered);
		   }
		   /* Rewind the origin by (-vx, -vy) if the cache is failed. */
		   gx_translate_to_fixed(pgs, rewind_pvxy.x, rewind_pvxy.y);
		}
		return code;
	    }
	    /* Adjust the character origin too. */
	    (penum->cc)->offset.x += dvxy.x;
	    (penum->cc)->offset.y += dvxy.y;
	} else {
	    code = set_char_width(penum, pgs, pw[0], pw[1]);
	    if (code < 0)
		return code;
	    if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE))
		return code;
	    code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
	}
	return code;
    }
    default:
	return_error(gs_error_rangecheck);
    }
}

/* Set the character width. */
/* Note that this returns 1 if the current show operation is */
/* non-displaying (stringwidth or cshow). */
int
set_char_width(gs_show_enum *penum, gs_state *pgs, floatp wx, floatp wy)
{
    int code;

    if (penum->width_status != sws_none && penum->width_status != sws_retry)
	return_error(gs_error_undefined);
    if ((code = gs_distance_transform2fixed(&pgs->ctm, wx, wy, &penum->wxy)) < 0)
	return code;
    /* Check whether we're setting the scalable width */
    /* for a cached xfont character. */
    if (penum->cc != 0) {
	penum->cc->wxy = penum->wxy;
	penum->width_status = sws_cache_width_only;
    } else {
	penum->width_status = sws_no_cache;
    }
    if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) /* cshow */
	gs_nulldevice(pgs);
    return !SHOW_IS_DRAWING(penum);
}

void
gx_compute_text_oversampling(const gs_show_enum * penum, const gs_font *pfont, 
                             int alpha_bits, gs_log2_scale_point *p_log2_scale)
{
    gs_log2_scale_point log2_scale;
    show_set_scale(penum, &log2_scale);
    /*
     * If the device wants anti-aliased text,
     * increase the sampling scale to ensure that
     * if we want N bits of alpha, we generate
     * at least 2^N sampled bits per pixel.
     */
    if (alpha_bits > 1) {
	int more_bits =
	alpha_bits - (log2_scale.x + log2_scale.y);

	if (more_bits > 0) {
	    if (log2_scale.x <= log2_scale.y) {
		log2_scale.x += (more_bits + 1) >> 1;
		log2_scale.y += more_bits >> 1;
	    } else {
		log2_scale.x += more_bits >> 1;
		log2_scale.y += (more_bits + 1) >> 1;
	    }
	}
    } else if (!OVERSAMPLE || pfont->PaintType != 0) {
	/* Don't oversample artificially stroked fonts. */
	log2_scale.x = log2_scale.y = 0;
    }
    *p_log2_scale = log2_scale;
}

/* Set up the cache device if relevant. */
/* Return 1 if we just set up a cache device. */
/* Used by setcachedevice and setcachedevice2. */
private int
set_cache_device(gs_show_enum * penum, gs_state * pgs, floatp llx, floatp lly,
		 floatp urx, floatp ury)
{
    gs_glyph glyph;

    /* See if we want to cache this character. */
    if (pgs->in_cachedevice)	/* no recursion! */
	return 0;
    if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) { /* cshow */
	int code;
	if_debug0('k', "[k]no cache: cshow");
	code = gs_nulldevice(pgs);
	if (code < 0)
	    return code;
	return 0;
    }
    pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;	/* disable color/gray/image operators */
    /* We can only use the cache if we know the glyph. */
    glyph = CURRENT_GLYPH(penum);
    if (glyph == gs_no_glyph)
	return 0;
    /* We can only use the cache if ctm is unchanged */
    /* (aside from a possible translation). */
    if (penum->can_cache <= 0 || !pgs->char_tm_valid) {
	if_debug2('k', "[k]no cache: can_cache=%d, char_tm_valid=%d\n",
		  penum->can_cache, (int)pgs->char_tm_valid);
	return 0;
    } {
	const gs_font *pfont = pgs->font;
	gs_font_dir *dir = pfont->dir;
	gx_device *dev = gs_currentdevice_inline(pgs);
        int alpha_bits =
        (*dev_proc(dev, get_alpha_bits)) (dev, go_text);
	gs_log2_scale_point log2_scale;
        static const fixed max_cdim[3] =
        {
#define max_cd(n)\
	    (fixed_1 << (arch_sizeof_short * 8 - n)) - (fixed_1 >> n) * 3
	    max_cd(0), max_cd(1), max_cd(2)
#undef max_cd
        };
	ushort iwidth, iheight;
	cached_char *cc;
	gs_fixed_rect clip_box;
	int code;

	/* Compute the bounding box of the transformed character. */
	/* Since we accept arbitrary transformations, the extrema */
	/* may occur in any order; however, we can save some work */
	/* by observing that opposite corners before transforming */
	/* are still opposite afterwards. */
	gs_fixed_point cll, clr, cul, cur, cdim;

	if ((code = gs_distance_transform2fixed(&pgs->ctm, llx, lly, &cll)) < 0 ||
	    (code = gs_distance_transform2fixed(&pgs->ctm, llx, ury, &clr)) < 0 ||
	    (code = gs_distance_transform2fixed(&pgs->ctm, urx, lly, &cul)) < 0 ||
	 (code = gs_distance_transform2fixed(&pgs->ctm, urx, ury, &cur)) < 0
	    )
	    return 0;		/* don't cache */
	{
	    fixed ctemp;

#define swap(a, b) ctemp = a, a = b, b = ctemp
#define make_min(a, b) if ( (a) > (b) ) swap(a, b)

	    make_min(cll.x, cur.x);
	    make_min(cll.y, cur.y);
	    make_min(clr.x, cul.x);
	    make_min(clr.y, cul.y);
#undef make_min
#undef swap
	}
	/* Now take advantage of symmetry. */
	if (clr.x < cll.x)
	    cll.x = clr.x, cur.x = cul.x;
	if (clr.y < cll.y)
	    cll.y = clr.y, cur.y = cul.y;
	/* Now cll and cur are the extrema of the box. */
        if (penum->fapi_log2_scale.x != -1)
            log2_scale = penum->fapi_log2_scale;
        else
            gx_compute_text_oversampling(penum, pfont, alpha_bits, &log2_scale);
#ifdef DEBUG
        if (gs_debug_c('k')) {
	    dlprintf6("[k]cbox=[%g %g %g %g] scale=%dx%d\n",
		      fixed2float(cll.x), fixed2float(cll.y),
		      fixed2float(cur.x), fixed2float(cur.y),
		      1 << log2_scale.x, 1 << log2_scale.y);
	    dlprintf6("[p]  ctm=[%g %g %g %g %g %g]\n",
		      pgs->ctm.xx, pgs->ctm.xy, pgs->ctm.yx, pgs->ctm.yy,
		      pgs->ctm.tx, pgs->ctm.ty);
        }
#endif
	cdim.x = cur.x - cll.x;
	cdim.y = cur.y - cll.y;
	if (cdim.x > max_cdim[log2_scale.x] ||
	    cdim.y > max_cdim[log2_scale.y]
	    )
	    return 0;		/* much too big */
	iwidth = ((ushort) fixed2int_var(cdim.x) + 2) << log2_scale.x;
	iheight = ((ushort) fixed2int_var(cdim.y) + 2) << log2_scale.y;
	if_debug3('k', "[k]iwidth=%u iheight=%u dev_cache %s\n",
		  (uint) iwidth, (uint) iheight,
		  (penum->dev_cache == 0 ? "not set" : "set"));
	if (penum->dev_cache == 0) {
	    code = show_cache_setup(penum);
	    if (code < 0)
		return code;
	}
	/*
	 * If we're oversampling (i.e., the temporary bitmap is
	 * larger than the final monobit or alpha array) and the
	 * temporary bitmap is large, use incremental conversion
	 * from oversampled bitmap strips to alpha values instead of
	 * full oversampling with compression at the end.
	 */
	cc = gx_alloc_char_bits(dir, penum->dev_cache,
				(iwidth > MAX_TEMP_BITMAP_BITS / iheight &&
				 log2_scale.x + log2_scale.y > alpha_bits ?
				 penum->dev_cache2 : NULL),
				iwidth, iheight, &log2_scale, alpha_bits);
	if (cc == 0)
	    return 0;		/* too big for cache */
	/* The mins handle transposed coordinate systems.... */
	/* Truncate the offsets to avoid artifacts later. */
	cc->offset.x = fixed_ceiling(-cll.x);
	cc->offset.y = fixed_ceiling(-cll.y);
	if_debug4('k', "[k]width=%u, height=%u, offset=[%g %g]\n",
		  (uint) iwidth, (uint) iheight,
		  fixed2float(cc->offset.x),
		  fixed2float(cc->offset.y));
	pgs->in_cachedevice = CACHE_DEVICE_NONE; /* Provide correct grestore */
	if ((code = gs_gsave(pgs)) < 0) {
	    gx_free_cached_char(dir, cc);
	    return code;
	}
	/* Nothing can go wrong now.... */
	penum->cc = cc;
	cc->code = glyph;
	cc->wmode = gs_rootfont(pgs)->WMode;
	cc->wxy = penum->wxy;
	/* Install the device */
	gx_set_device_only(pgs, (gx_device *) penum->dev_cache);
	pgs->ctm_default_set = false;
	/* Adjust the transformation in the graphics context */
	/* so that the character lines up with the cache. */
	gx_translate_to_fixed(pgs,
			      cc->offset.x << log2_scale.x,
			      cc->offset.y << log2_scale.y);
	if ((log2_scale.x | log2_scale.y) != 0)
	    gx_scale_char_matrix(pgs, 1 << log2_scale.x,
				 1 << log2_scale.y);
	/* Set the initial matrix for the cache device. */
	penum->dev_cache->initial_matrix = ctm_only(pgs);
	/* Set the oversampling factor. */
	penum->log2_scale.x = log2_scale.x;
	penum->log2_scale.y = log2_scale.y;
	/* Reset the clipping path to match the metrics. */
	clip_box.p.x = clip_box.p.y = 0;
	clip_box.q.x = int2fixed(iwidth);
	clip_box.q.y = int2fixed(iheight);
	if ((code = gx_clip_to_rectangle(pgs, &clip_box)) < 0)
	    return code;
	gx_set_device_color_1(pgs);	/* write 1's */
	pgs->in_cachedevice = CACHE_DEVICE_CACHING;
    }
    penum->width_status = sws_cache;
    return 1;
}

/* Return the cache device status. */
gs_in_cache_device_t
gs_incachedevice(const gs_state *pgs)
{
    return pgs->in_cachedevice;
}

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

/*
 * Set the encode_char procedure in an enumerator.
 */
private void
show_set_encode_char(gs_show_enum * penum)
{
    penum->encode_char =
	(SHOW_IS(penum, TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH) ?
	 gs_no_encode_char :
	 gs_show_current_font(penum)->procs.encode_char);
}

/*
 * Resync a text operation with a different set of parameters.
 * Currently this is implemented only for changing the data source.
 */
private int
gx_show_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
{
    gs_show_enum *const penum = (gs_show_enum *)pte;
    int old_index = pte->index;

    if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY)
	return_error(gs_error_rangecheck);
    pte->text = pfrom->text;
    if (pte->index == old_index) {
	show_set_encode_char(penum);
	return 0;
    } else
	return show_state_setup(penum);
}

/* Do the next step of a show (or stringwidth) operation */
private int
gx_show_text_process(gs_text_enum_t *pte)
{
    gs_show_enum *const penum = (gs_show_enum *)pte;

    return (*penum->continue_proc)(penum);
}

/* Continuation procedures */
private int show_update(gs_show_enum * penum);
private int show_move(gs_show_enum * penum);
private int show_proceed(gs_show_enum * penum);
private int show_finish(gs_show_enum * penum);
private int
continue_show_update(gs_show_enum * penum)
{
    int code = show_update(penum);

    if (code < 0)
	return code;
    code = show_move(penum);
    if (code != 0)
	return code;
    return show_proceed(penum);
}
private int
continue_show(gs_show_enum * penum)
{
    return show_proceed(penum);
}
/* For kshow, the CTM or font may have changed, so we have to reestablish */
/* the cached values in the enumerator. */
private int
continue_kshow(gs_show_enum * penum)
{   int code;
    gs_state *pgs = penum->pgs;

    if (pgs->font != penum->orig_font) 
	gs_setfont(pgs, penum->orig_font);

    code = show_state_setup(penum);

    if (code < 0)
	return code;
    return show_proceed(penum);
}

/* Update position */
private int
show_update(gs_show_enum * penum)
{
    gs_state *pgs = penum->pgs;
    cached_char *cc = penum->cc;
    int code;

    /* Update position for last character */
    switch (penum->width_status) {
	case sws_none:
        case sws_retry:	  
	    /* Adobe interpreters assume a character width of 0, */
	    /* even though the documentation says this is an error.... */
	    penum->wxy.x = penum->wxy.y = 0;
	    break;
	case sws_cache:
	    /* Finish installing the cache entry. */
	    /* If the BuildChar/BuildGlyph procedure did a save and a */
	    /* restore, it already undid the gsave in setcachedevice. */
	    /* We have to check for this by comparing levels. */
	    switch (pgs->level - penum->level) {
		default:
		    return_error(gs_error_invalidfont);		/* WRONG */
		case 2:
		    code = gs_grestore(pgs);
		    if (code < 0)
			return code;
		case 1:
		    ;
	    }
	    gx_add_cached_char(pgs->font->dir, penum->dev_cache,
			       cc, gx_lookup_fm_pair(pgs->font, pgs),
			       &penum->log2_scale);
	    if (!SHOW_USES_OUTLINE(penum) ||
		penum->charpath_flag != cpm_show
		)
		break;
	    /* falls through */
	case sws_cache_width_only:
	    /* Copy the bits to the real output device. */
	    code = gs_grestore(pgs);
	    if (code < 0)
		return code;
	    code = gs_state_color_load(pgs);
	    if (code < 0)
		return code;
	    return gx_image_cached_char(penum, cc);
	case sws_no_cache:
	    ;
    }
    if (penum->charpath_flag != cpm_show) {
	/* Move back to the character origin, so that */
	/* show_move will get us to the right place. */
	code = gx_path_add_point(pgs->show_gstate->path,
				 penum->origin.x, penum->origin.y);
	if (code < 0)
	    return code;
    }
    return gs_grestore(pgs);
}

/* Move to next character */
private int
show_fast_move(gs_state * pgs, gs_fixed_point * pwxy)
{
    int code = gx_path_add_rel_point_inline(pgs->path, pwxy->x, pwxy->y);

    /* If the current position is out of range, don't try to move. */
    if (code == gs_error_limitcheck && pgs->clamp_coordinates)
	code = 0;
    return code;
}
private int
show_move(gs_show_enum * penum)
{
    gs_state *pgs = penum->pgs;

    if (SHOW_IS(penum, TEXT_REPLACE_WIDTHS)) {
	gs_point dpt;

	gs_text_replaced_width(&penum->text, penum->xy_index - 1, &dpt);
	gs_distance_transform2fixed(&pgs->ctm, dpt.x, dpt.y, &penum->wxy);
    } else {
	double dx = 0, dy = 0;

	if (SHOW_IS_ADD_TO_SPACE(penum)) {
	    gs_char chr = CURRENT_CHAR(penum) & 0xff;
	    int fdepth = penum->fstack.depth;

	    if (fdepth > 0) {
		/* Add in the shifted font number. */
		uint fidx = penum->fstack.items[fdepth].index;

		switch (((gs_font_type0 *) (penum->fstack.items[fdepth - 1].font))->data.FMapType) {
		case fmap_1_7:
		case fmap_9_7:
		    chr += fidx << 7;
		    break;
		case fmap_CMap:
		    chr = CURRENT_CHAR(penum);  /* the full character */
		    if (!penum->cmap_code)
			break;
		    /* falls through */
		default:
		    chr += fidx << 8;
		}
	    }
	    if (chr == penum->text.space.s_char) {
		dx = penum->text.delta_space.x;
		dy = penum->text.delta_space.y;
	    }
	}
	if (SHOW_IS_ADD_TO_ALL(penum)) {
	    dx += penum->text.delta_all.x;
	    dy += penum->text.delta_all.y;
	}
	if (!is_fzero2(dx, dy)) {
	    gs_fixed_point dxy;

	    gs_distance_transform2fixed(&pgs->ctm, dx, dy, &dxy);
	    penum->wxy.x += dxy.x;
	    penum->wxy.y += dxy.y;
	}
    }
    if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) {
	/* HACK for cshow */
	penum->continue_proc = continue_kshow;
	return TEXT_PROCESS_INTERVENE;
    }
    /* wxy is in device coordinates */
    {
	int code = show_fast_move(pgs, &penum->wxy);

	if (code < 0)
	    return code;
    }
    /* Check for kerning, but not on the last character. */
    if (SHOW_IS_DO_KERN(penum) && penum->index < penum->text.size) {
	penum->continue_proc = continue_kshow;
	return TEXT_PROCESS_INTERVENE;
    }
    return 0;
}
/* Process next character */
private int
show_proceed(gs_show_enum * penum)
{
    gs_state *pgs = penum->pgs;
    gs_font *pfont;
    cached_fm_pair *pair = 0;
    gs_font *rfont =
	(penum->fstack.depth < 0 ? pgs->font : penum->fstack.items[0].font);
    int wmode = rfont->WMode;
    font_proc_next_char_glyph((*next_char_glyph)) =
	rfont->procs.next_char_glyph;
#define get_next_char_glyph(pte, pchr, pglyph)\
  (++(penum->xy_index), next_char_glyph(pte, pchr, pglyph))
    gs_char chr;
    gs_glyph glyph;
    int code;
    cached_char *cc;
    gx_device *dev = gs_currentdevice_inline(pgs);
    int alpha_bits = (*dev_proc(dev, get_alpha_bits)) (dev, go_text);

    if (penum->charpath_flag == cpm_show && SHOW_USES_OUTLINE(penum)) {
	code = gs_state_color_load(pgs);
	if (code < 0)
	    return code;
    }
  more:			/* Proceed to next character */
    pfont = (penum->fstack.depth < 0 ? pgs->font :
	     penum->fstack.items[penum->fstack.depth].font);
    penum->current_font = pfont;
    /* can_cache >= 0 allows us to use cached characters, */
    /* even if we can't make new cache entries. */
    if (penum->can_cache >= 0) {
	/* Loop with cache */
	for (;;) {
	    switch ((code = get_next_char_glyph((gs_text_enum_t *)penum,
						&chr, &glyph))
		    ) {
		default:	/* error */
		    return code;
		case 2:	/* done */
		    return show_finish(penum);
		case 1:	/* font change */
		    pfont = penum->fstack.items[penum->fstack.depth].font;
		    penum->current_font = pfont;
		    pgs->char_tm_valid = false;
		    show_state_setup(penum);
		    pair = 0;
		    /* falls through */
		case 0:	/* plain char */
		    /*
		     * We don't need to set penum->current_char in the
		     * normal cases, but it's needed for widthshow,
		     * kshow, and one strange client, so we may as well
		     * do it here.
		     */
		    SET_CURRENT_CHAR(penum, chr);
		    if (glyph == gs_no_glyph) {
			glyph = (*penum->encode_char)(pfont, chr,
						      GLYPH_SPACE_NAME);
			if (glyph == gs_no_glyph) {
			    cc = 0;
			    goto no_cache;
			}
		    }
		    if (pair == 0)
			pair = gx_lookup_fm_pair(pfont, pgs);
		    cc = gx_lookup_cached_char(pfont, pair, glyph, wmode,
					       alpha_bits);
		    if (cc == 0) {
			/* Character is not in cache. */
			/* If possible, try for an xfont before */
			/* rendering from the outline. */
			if (pfont->ExactSize == fbit_use_outlines ||
			    pfont->PaintType == 2
			    )
			    goto no_cache;
			if (pfont->BitmapWidths) {
			    cc = gx_lookup_xfont_char(pgs, pair, chr,
				     glyph, wmode);
			    if (cc == 0)
				goto no_cache;
			} else {
			    if (!SHOW_USES_OUTLINE(penum) ||
				(penum->charpath_flag != cpm_show &&
				 penum->charpath_flag != cpm_charwidth)
				)
				goto no_cache;
			    /* We might have an xfont, but we still */
			    /* want the scalable widths. */
			    cc = gx_lookup_xfont_char(pgs, pair, chr,
				     glyph, wmode);
			    /* Render up to the point of */
			    /* setcharwidth or setcachedevice, */
			    /* just as for stringwidth. */
			    /* This is the only case in which we can */
			    /* to go no_cache with cc != 0. */
			    goto no_cache;
			}
		    }
		    /* Character is in cache. */
		    /* We might be doing .charboxpath or stringwidth; */
		    /* check for these now. */
		    if (penum->charpath_flag == cpm_charwidth) {
			/* This is charwidth.  Just move by the width. */
			DO_NOTHING;
		    } else if (penum->charpath_flag != cpm_show) {
			/* This is .charboxpath. Get the bounding box */
			/* and append it to a path. */
			gx_path box_path;
			gs_fixed_point pt;
			fixed llx, lly, urx, ury;

			code = gx_path_current_point(pgs->path, &pt);
			if (code < 0)
			    return code;
			llx = fixed_rounded(pt.x - cc->offset.x) +
			    int2fixed(penum->ftx);
			lly = fixed_rounded(pt.y - cc->offset.y) +
			    int2fixed(penum->fty);
			urx = llx + int2fixed(cc->width),
			    ury = lly + int2fixed(cc->height);
			gx_path_init_local(&box_path, pgs->memory);
			code =
			    gx_path_add_rectangle(&box_path, llx, lly,
						  urx, ury);
			if (code >= 0)
			    code =
				gx_path_add_char_path(pgs->show_gstate->path,
						      &box_path,
						      penum->charpath_flag);
			if (code >= 0)
			    code = gx_path_add_point(pgs->path, pt.x, pt.y);
			gx_path_free(&box_path, "show_proceed(box path)");
			if (code < 0)
			    return code;
		    } else if (SHOW_IS_DRAWING(penum)) {
			code = gx_image_cached_char(penum, cc);
			if (code < 0)
			    return code;
			else if (code > 0) {
			    cc = 0;
			    goto no_cache;
			}
		    }
		    if (SHOW_IS_SLOW(penum)) {
			/* Split up the assignment so that the */
			/* Watcom compiler won't reserve esi/edi. */
			penum->wxy.x = cc->wxy.x;
			penum->wxy.y = cc->wxy.y;
			code = show_move(penum);
		    } else
			code = show_fast_move(pgs, &cc->wxy);
		    if (code) {
			/* Might be kshow, so store the state. */
			SET_CURRENT_GLYPH(penum, glyph);
			return code;
		    }
	    }
	}
    } else {
	/* Can't use cache */
	switch ((code = get_next_char_glyph((gs_text_enum_t *)penum,
					    &chr, &glyph))
		) {
	    default:
		return code;
	    case 2:
		return show_finish(penum);
	    case 1:
		pfont = penum->fstack.items[penum->fstack.depth].font;
		penum->current_font = pfont;
		show_state_setup(penum);
	    case 0:
		;
	}
	SET_CURRENT_CHAR(penum, chr);
	if (glyph == gs_no_glyph) {
	    glyph = (*penum->encode_char)(pfont, chr, GLYPH_SPACE_NAME);
	}
	cc = 0;
    }
  no_cache:
    /*
     * We must call the client's rendering code.  Normally,
     * we only do this if the character is not cached (cc = 0);
     * however, we also must do this if we have an xfont but
     * are using scalable widths.  In this case, and only this case,
     * we get here with cc != 0.  penum->current_char has already
     * been set, but not penum->current_glyph.
     */
    SET_CURRENT_GLYPH(penum, glyph);
    if ((code = gs_gsave(pgs)) < 0)
	return code;
    /* Set the font to the current descendant font. */
    pgs->font = pfont;
    /* Reset the in_cachedevice flag, so that a recursive show */
    /* will use the cache properly. */
    pgs->in_cachedevice = CACHE_DEVICE_NONE;
    /* Reset the sampling scale. */
    penum->log2_scale.x = penum->log2_scale.y = 0;
    /* Set the charpath data in the graphics context if necessary, */
    /* so that fill and stroke will add to the path */
    /* rather than having their usual effect. */
    pgs->in_charpath = penum->charpath_flag;
    pgs->show_gstate =
	(penum->show_gstate == pgs ? pgs->saved : penum->show_gstate);
    pgs->stroke_adjust = false;	/* per specification */
    {
	gs_fixed_point cpt;
	gx_path *ppath = pgs->path;

	if ((code = gx_path_current_point_inline(ppath, &cpt)) < 0) {
	    /* For cshow, having no current point is acceptable. */
	    if (!SHOW_IS(penum, TEXT_DO_NONE))
		goto rret;
	    cpt.x = cpt.y = 0;	/* arbitrary */
	}
	penum->origin.x = cpt.x;
	penum->origin.y = cpt.y;
	/* Normally, char_tm is valid because of show_state_setup, */
	/* but if we're in a cshow, it may not be. */
	gs_currentcharmatrix(pgs, NULL, true);
#if 1				/*USE_FPU <= 0 */
	if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) {
	    fixed tx = pgs->ctm.tx_fixed;
	    fixed ty = pgs->ctm.ty_fixed;

	    gs_settocharmatrix(pgs);
	    cpt.x += pgs->ctm.tx_fixed - tx;
	    cpt.y += pgs->ctm.ty_fixed - ty;
	} else
#endif
	{
	    double tx = pgs->ctm.tx;
	    double ty = pgs->ctm.ty;
	    double fpx, fpy;

	    gs_settocharmatrix(pgs);
	    fpx = fixed2float(cpt.x) + (pgs->ctm.tx - tx);
	    fpy = fixed2float(cpt.y) + (pgs->ctm.ty - ty);
#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
	    if (!(f_fits_in_fixed(fpx) && f_fits_in_fixed(fpy))) {
		gs_note_error(code = gs_error_limitcheck);
		goto rret;
	    }
	    cpt.x = float2fixed(fpx);
	    cpt.y = float2fixed(fpy);
	}
	gs_newpath(pgs);
	code = show_origin_setup(pgs, cpt.x, cpt.y,
				 penum->charpath_flag);
	if (code < 0)
	    goto rret;
    }
    penum->width_status = sws_none;
    penum->continue_proc = continue_show_update;
    /* Try using the build procedure in the font. */
    /* < 0 means error, 0 means success, 1 means failure. */
    penum->cc = cc;		/* set this now for build procedure */
    code = (*pfont->procs.build_char)((gs_text_enum_t *)penum, pgs, pfont,
				      chr, glyph);
    if (code < 0) {
	discard(gs_note_error(code));
	goto rret;
    }
    if (code == 0) {
	code = show_update(penum);
	if (code < 0)
	    goto rret;
	/* Note that show_update does a grestore.... */
	code = show_move(penum);
	if (code)
	    return code;	/* ... so don't go to rret here. */
	goto more;
    }
    /*
     * Some BuildChar procedures do a save before the setcachedevice,
     * and a restore at the end.  If we waited to allocate the cache
     * device until the setcachedevice, we would attempt to free it
     * after the restore.  Therefore, allocate it now.
     */
    if (penum->dev_cache == 0) {
	code = show_cache_setup(penum);
	if (code < 0)
	    goto rret;
    }
    return TEXT_PROCESS_RENDER;
    /* If we get an error while setting up for BuildChar, */
    /* we must undo the partial setup. */
  rret:gs_grestore(pgs);
    return code;
#undef get_next_char_glyph
}

/*
 * Prepare to retry rendering of the current character.  (This is only used
 * in one place in zchar1.c; a different approach may be better.)
 */
private int
gx_show_text_retry(gs_text_enum_t *pte)
{
    gs_show_enum *const penum = (gs_show_enum *)pte;

    if (penum->cc) {
	gs_font *pfont = penum->current_font;

	gx_free_cached_char(pfont->dir, penum->cc);
	penum->cc = 0;
    }
    gs_grestore(penum->pgs);
    penum->width_status = sws_retry;
    penum->log2_scale.x = penum->log2_scale.y = 0;
    return 0;
}

/* Finish show or stringwidth */
private int
show_finish(gs_show_enum * penum)
{
    gs_state *pgs = penum->pgs;
    int code, rcode;

    if (penum->auto_release)
	penum->procs->release((gs_text_enum_t *)penum, "show_finish");
    if (!SHOW_IS_STRINGWIDTH(penum))
	return 0;
    /* Save the accumulated width before returning, */
    /* and undo the extra gsave. */
    code = gs_currentpoint(pgs, &penum->returned.total_width);
    rcode = gs_grestore(pgs);
    return (code < 0 ? code : rcode);
}

/* Release the structure. */
private void
gx_show_text_release(gs_text_enum_t *pte, client_name_t cname)
{
    gs_show_enum *const penum = (gs_show_enum *)pte;

    penum->cc = 0;
    if (penum->dev_cache2) {
	gx_device_retain((gx_device *)penum->dev_cache2, false);
	penum->dev_cache2 = 0;
    }
    if (penum->dev_cache) {
	gx_device_retain((gx_device *)penum->dev_cache, false);
	penum->dev_cache = 0;
    }
    if (penum->dev_null) {
	gx_device_retain((gx_device *)penum->dev_null, false);
	penum->dev_null = 0;
    }
    gx_default_text_release(pte, cname);
}

/* ------ Miscellaneous accessors ------ */

/* Return the charpath mode. */
gs_char_path_mode
gs_show_in_charpath(const gs_show_enum * penum)
{
    return penum->charpath_flag;
}

/* 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. */
/* This is only meaningful just before calling gs_setcharwidth or */
/* gs_setcachedevice[2]. */
/* Note that we can't do this if the procedure has done any extra [g]saves. */
private bool
gx_show_text_is_width_only(const gs_text_enum_t *pte)
{
    const gs_show_enum *const penum = (const gs_show_enum *)pte;

    /* penum->cc will be non-zero iff we are calculating */
    /* the scalable width for an xfont character. */
    return ((!SHOW_USES_OUTLINE(penum) || penum->cc != 0) &&
	    penum->pgs->level == penum->level + 1);
}

/* Return the width of the just-enumerated character (for cshow). */
private int
gx_show_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
{
    const gs_show_enum *const penum = (const gs_show_enum *)pte;

    return gs_idtransform(penum->pgs,
			  fixed2float(penum->wxy.x),
			  fixed2float(penum->wxy.y), pwidth);
}

/* Return the current font for cshow. */
gs_font *
gs_show_current_font(const gs_show_enum * penum)
{
    return (penum->fstack.depth < 0 ? penum->pgs->font :
	    penum->fstack.items[penum->fstack.depth].font);
}

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

/* Initialize the gstate-derived parts of a show enumerator. */
/* We do this both when starting the show operation, */
/* and when returning from the kshow callout. */
/* Uses only penum->pgs, penum->fstack. */
private int
show_state_setup(gs_show_enum * penum)
{
    gs_state *pgs = penum->pgs;
    gx_clip_path *pcpath;
    gs_font *pfont;

    if (penum->fstack.depth <= 0) {
	pfont = pgs->font;
	gs_currentcharmatrix(pgs, NULL, 1);	/* make char_tm valid */
    } else {
	/* We have to concatenate the parent's FontMatrix as well. */
	gs_matrix mat;
	const gx_font_stack_item_t *pfsi =
	    &penum->fstack.items[penum->fstack.depth];

	pfont = pfsi->font;
	gs_matrix_multiply(&pfont->FontMatrix,
			   &pfsi[-1].font->FontMatrix, &mat);
	if (pfont->FontType == ft_CID_encrypted) {
	    /* concatenate the Type9 leaf's matrix */
	    gs_matrix_multiply(&mat,
		&(gs_cid0_indexed_font(pfont, pfsi->index)->FontMatrix), &mat);
	}
	gs_setcharmatrix(pgs, &mat);
    }
    penum->current_font = pfont;
    /* Skewing or non-rectangular rotation are not supported. */
    if (!CACHE_ROTATED_CHARS &&
	(is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
	 is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy))
	)
	penum->can_cache = 0;
    if (penum->can_cache >= 0 &&
	gx_effective_clip_path(pgs, &pcpath) >= 0
	) {
	gs_fixed_rect cbox;

	gx_cpath_inner_box(pcpath, &cbox);
	/* Since characters occupy an integral number of pixels, */
	/* we can (and should) round the inner clipping box */
	/* outward rather than inward. */
	penum->ibox.p.x = fixed2int_var(cbox.p.x);
	penum->ibox.p.y = fixed2int_var(cbox.p.y);
	penum->ibox.q.x = fixed2int_var_ceiling(cbox.q.x);
	penum->ibox.q.y = fixed2int_var_ceiling(cbox.q.y);
	gx_cpath_outer_box(pcpath, &cbox);
	penum->obox.p.x = fixed2int_var(cbox.p.x);
	penum->obox.p.y = fixed2int_var(cbox.p.y);
	penum->obox.q.x = fixed2int_var_ceiling(cbox.q.x);
	penum->obox.q.y = fixed2int_var_ceiling(cbox.q.y);
#if 1				/*USE_FPU <= 0 */
	if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) {
	    penum->ftx = (int)fixed2long(pgs->char_tm.tx_fixed -
					 pgs->ctm.tx_fixed);
	    penum->fty = (int)fixed2long(pgs->char_tm.ty_fixed -
					 pgs->ctm.ty_fixed);
	} else {
#endif
	    double fdx = pgs->char_tm.tx - pgs->ctm.tx;
	    double fdy = pgs->char_tm.ty - pgs->ctm.ty;

#define int_bits (arch_sizeof_int * 8 - 1)
	    if (!(f_fits_in_bits(fdx, int_bits) &&
		  f_fits_in_bits(fdy, int_bits))
		)
		return_error(gs_error_limitcheck);
#undef int_bits
	    penum->ftx = (int)fdx;
	    penum->fty = (int)fdy;
	}
    }
    show_set_encode_char(penum);
    return 0;
}

/* Set the suggested oversampling scale for character rendering. */
private void
show_set_scale(const gs_show_enum * penum, gs_log2_scale_point *log2_scale)
{
    /*
     * Decide whether to oversample.
     * We have to decide this each time setcachedevice is called.
     */
    const gs_state *pgs = penum->pgs;

    if ((penum->charpath_flag == cpm_show ||
	 penum->charpath_flag == cpm_charwidth) &&
	SHOW_USES_OUTLINE(penum) &&
	gx_path_is_void_inline(pgs->path) &&
    /* Oversampling rotated characters doesn't work well. */
	(is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
	 is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy))
	) {
	const gs_font_base *pfont = (gs_font_base *) pgs->font;
	gs_fixed_point extent;
	int code = gs_distance_transform2fixed(&pgs->char_tm,
				  pfont->FontBBox.q.x - pfont->FontBBox.p.x,
				  pfont->FontBBox.q.y - pfont->FontBBox.p.y,
					       &extent);

	if (code >= 0) {
	    int sx =
	    (extent.x == 0 ? 0 :
	     any_abs(extent.x) < int2fixed(25) ? 2 :
	     any_abs(extent.x) < int2fixed(60) ? 1 :
	     0);
	    int sy =
	    (extent.y == 0 ? 0 :
	     any_abs(extent.y) < int2fixed(25) ? 2 :
	     any_abs(extent.y) < int2fixed(60) ? 1 :
	     0);

	    /* If we oversample at all, make sure we do it */
	    /* in both X and Y. */
	    if (sx == 0 && sy != 0)
		sx = 1;
	    else if (sy == 0 && sx != 0)
		sy = 1;
	    log2_scale->x = sx;
	    log2_scale->y = sy;
	    return;
	}
    }
    /* By default, don't scale. */
    log2_scale->x = log2_scale->y = 0;
}

/* Set up the cache device and related information. */
/* Note that we always allocate both cache devices, */
/* even if we only use one of them. */
private int
show_cache_setup(gs_show_enum * penum)
{
    gs_state *pgs = penum->pgs;
    gs_memory_t *mem = pgs->memory;
    gx_device_memory *dev =
	gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
			"show_cache_setup(dev_cache)");
    gx_device_memory *dev2 =
	gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
			"show_cache_setup(dev_cache2)");

    if (dev == 0 || dev2 == 0) {
	gs_free_object(mem, dev2, "show_cache_setup(dev_cache2)");
	gs_free_object(mem, dev, "show_cache_setup(dev_cache)");
	return_error(gs_error_VMerror);
    }
    /*
     * We only initialize the devices for the sake of the GC,
     * (since we have to re-initialize dev as either a mem_mono
     * or a mem_abuf device before actually using it) and also
     * to set its memory pointer.
     */
    gs_make_mem_mono_device(dev, mem, gs_currentdevice_inline(pgs));
    penum->dev_cache = dev;
    gs_make_mem_mono_device(dev2, mem, gs_currentdevice_inline(pgs));
    penum->dev_cache2 = dev2;
    /* Retain these devices, since they are referenced from the enumerator. */
    gx_device_retain((gx_device *)dev, true);
    gx_device_retain((gx_device *)dev2, true);
    return 0;
}

/* Set the character origin as the origin of the coordinate system. */
/* Used before rendering characters, and for moving the origin */
/* in setcachedevice2 when WMode=1. */
private int
show_origin_setup(gs_state * pgs, fixed cpt_x, fixed cpt_y,
		  gs_char_path_mode charpath_flag)
{
    if (charpath_flag == cpm_show) {
	/* Round the translation in the graphics state. */
	/* This helps prevent rounding artifacts later. */
	cpt_x = fixed_rounded(cpt_x);
	cpt_y = fixed_rounded(cpt_y);
    }
    /*
     * BuildChar procedures expect the current point to be undefined,
     * so we omit the gx_path_add_point with ctm.t*_fixed.
     */
    return gx_translate_to_fixed(pgs, cpt_x, cpt_y);
}
