xref: /plan9/sys/src/cmd/gs/src/gxchar.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
27dd7cddfSDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
57dd7cddfSDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: gxchar.c,v 1.47 2005/07/21 09:53:42 igor Exp $ */
187dd7cddfSDavid du Colombier /* Default implementation of text writing */
197dd7cddfSDavid du Colombier #include "gx.h"
207dd7cddfSDavid du Colombier #include "memory_.h"
217dd7cddfSDavid du Colombier #include "string_.h"
227dd7cddfSDavid du Colombier #include "gserrors.h"
237dd7cddfSDavid du Colombier #include "gsstruct.h"
247dd7cddfSDavid du Colombier #include "gxfixed.h"		/* ditto */
257dd7cddfSDavid du Colombier #include "gxarith.h"
267dd7cddfSDavid du Colombier #include "gxmatrix.h"
277dd7cddfSDavid du Colombier #include "gzstate.h"
287dd7cddfSDavid du Colombier #include "gxcoord.h"
297dd7cddfSDavid du Colombier #include "gxdevice.h"
307dd7cddfSDavid du Colombier #include "gxdevmem.h"
317dd7cddfSDavid du Colombier #include "gxchar.h"
327dd7cddfSDavid du Colombier #include "gxfont.h"
337dd7cddfSDavid du Colombier #include "gxfont0.h"
347dd7cddfSDavid du Colombier #include "gxfcache.h"
357dd7cddfSDavid du Colombier #include "gspath.h"
367dd7cddfSDavid du Colombier #include "gzpath.h"
37*593dc095SDavid du Colombier #include "gxfcid.h"
387dd7cddfSDavid du Colombier 
397dd7cddfSDavid du Colombier /* Define whether or not to cache characters rotated by angles other than */
407dd7cddfSDavid du Colombier /* multiples of 90 degrees. */
413ff48bf5SDavid du Colombier private const bool CACHE_ROTATED_CHARS = true;
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier /* Define the maximum size of a full temporary bitmap when rasterizing, */
447dd7cddfSDavid du Colombier /* in bits (not bytes). */
453ff48bf5SDavid du Colombier private const uint MAX_TEMP_BITMAP_BITS = 80000;
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier /* Define whether the show operation uses the character outline data, */
487dd7cddfSDavid du Colombier /* as opposed to just needing the width (or nothing). */
497dd7cddfSDavid du Colombier #define SHOW_USES_OUTLINE(penum)\
507dd7cddfSDavid du Colombier   !SHOW_IS(penum, TEXT_DO_NONE | TEXT_DO_CHARWIDTH)
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier /* Structure descriptors */
537dd7cddfSDavid du Colombier public_st_gs_show_enum();
547dd7cddfSDavid du Colombier extern_st(st_gs_text_enum);
557dd7cddfSDavid du Colombier extern_st(st_gs_state);		/* only for testing */
567dd7cddfSDavid du Colombier private
577dd7cddfSDavid du Colombier ENUM_PTRS_BEGIN(show_enum_enum_ptrs)
583ff48bf5SDavid du Colombier      return ENUM_USING(st_gs_text_enum, vptr, size, index - 5);
597dd7cddfSDavid du Colombier ENUM_PTR(0, gs_show_enum, pgs);
607dd7cddfSDavid du Colombier ENUM_PTR(1, gs_show_enum, show_gstate);
617dd7cddfSDavid du Colombier ENUM_PTR3(2, gs_show_enum, dev_cache, dev_cache2, dev_null);
627dd7cddfSDavid du Colombier ENUM_PTRS_END
RELOC_PTRS_WITH(show_enum_reloc_ptrs,gs_show_enum * eptr)637dd7cddfSDavid du Colombier private RELOC_PTRS_WITH(show_enum_reloc_ptrs, gs_show_enum *eptr)
647dd7cddfSDavid du Colombier {
657dd7cddfSDavid du Colombier     RELOC_USING(st_gs_text_enum, vptr, size);		/* superclass */
667dd7cddfSDavid du Colombier     RELOC_VAR(eptr->pgs);
677dd7cddfSDavid du Colombier     RELOC_VAR(eptr->show_gstate);
687dd7cddfSDavid du Colombier     RELOC_PTR3(gs_show_enum, dev_cache, dev_cache2, dev_null);
697dd7cddfSDavid du Colombier }
707dd7cddfSDavid du Colombier RELOC_PTRS_END
717dd7cddfSDavid du Colombier 
727dd7cddfSDavid du Colombier /* Forward declarations */
73*593dc095SDavid du Colombier private int continue_kshow(gs_show_enum *);
74*593dc095SDavid du Colombier private int continue_show(gs_show_enum *);
75*593dc095SDavid du Colombier private int continue_show_update(gs_show_enum *);
76*593dc095SDavid du Colombier private void show_set_scale(const gs_show_enum *, gs_log2_scale_point *log2_scale);
77*593dc095SDavid du Colombier private int show_cache_setup(gs_show_enum *);
78*593dc095SDavid du Colombier private int show_state_setup(gs_show_enum *);
79*593dc095SDavid du Colombier private int show_origin_setup(gs_state *, fixed, fixed, gs_show_enum * penum);
807dd7cddfSDavid du Colombier 
817dd7cddfSDavid du Colombier /* Accessors for current_char and current_glyph. */
827dd7cddfSDavid du Colombier #define CURRENT_CHAR(penum) ((penum)->returned.current_char)
837dd7cddfSDavid du Colombier #define SET_CURRENT_CHAR(penum, chr)\
847dd7cddfSDavid du Colombier   ((penum)->returned.current_char = (chr))
857dd7cddfSDavid du Colombier #define CURRENT_GLYPH(penum) ((penum)->returned.current_glyph)
867dd7cddfSDavid du Colombier #define SET_CURRENT_GLYPH(penum, glyph)\
877dd7cddfSDavid du Colombier   ((penum)->returned.current_glyph = (glyph))
887dd7cddfSDavid du Colombier 
897dd7cddfSDavid du Colombier /* Allocate a show enumerator. */
907dd7cddfSDavid du Colombier gs_show_enum *
gs_show_enum_alloc(gs_memory_t * mem,gs_state * pgs,client_name_t cname)917dd7cddfSDavid du Colombier gs_show_enum_alloc(gs_memory_t * mem, gs_state * pgs, client_name_t cname)
927dd7cddfSDavid du Colombier {
937dd7cddfSDavid du Colombier     gs_show_enum *penum;
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier     rc_alloc_struct_1(penum, gs_show_enum, &st_gs_show_enum, mem,
967dd7cddfSDavid du Colombier 		      return 0, cname);
977dd7cddfSDavid du Colombier     penum->rc.free = rc_free_text_enum;
987dd7cddfSDavid du Colombier     penum->auto_release = true;	/* old API */
997dd7cddfSDavid du Colombier     /* Initialize pointers for GC */
1007dd7cddfSDavid du Colombier     penum->text.operation = 0;	/* no pointers relevant */
1017dd7cddfSDavid du Colombier     penum->dev = 0;
1027dd7cddfSDavid du Colombier     penum->pgs = pgs;
1037dd7cddfSDavid du Colombier     penum->show_gstate = 0;
1047dd7cddfSDavid du Colombier     penum->dev_cache = 0;
1057dd7cddfSDavid du Colombier     penum->dev_cache2 = 0;
106*593dc095SDavid du Colombier     penum->fapi_log2_scale.x = penum->fapi_log2_scale.y = -1;
107*593dc095SDavid du Colombier     penum->fapi_glyph_shift.x = penum->fapi_glyph_shift.y = 0;
1087dd7cddfSDavid du Colombier     penum->dev_null = 0;
1097dd7cddfSDavid du Colombier     penum->fstack.depth = -1;
1107dd7cddfSDavid du Colombier     return penum;
1117dd7cddfSDavid du Colombier }
1127dd7cddfSDavid du Colombier 
1137dd7cddfSDavid du Colombier /* ------ Driver procedure ------ */
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier private text_enum_proc_resync(gx_show_text_resync);
1167dd7cddfSDavid du Colombier private text_enum_proc_process(gx_show_text_process);
1177dd7cddfSDavid du Colombier private text_enum_proc_is_width_only(gx_show_text_is_width_only);
1187dd7cddfSDavid du Colombier private text_enum_proc_current_width(gx_show_text_current_width);
1197dd7cddfSDavid du Colombier private text_enum_proc_set_cache(gx_show_text_set_cache);
1207dd7cddfSDavid du Colombier private text_enum_proc_retry(gx_show_text_retry);
1217dd7cddfSDavid du Colombier private text_enum_proc_release(gx_show_text_release); /* not default */
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier private const gs_text_enum_procs_t default_text_procs = {
1247dd7cddfSDavid du Colombier     gx_show_text_resync, gx_show_text_process,
1257dd7cddfSDavid du Colombier     gx_show_text_is_width_only, gx_show_text_current_width,
1267dd7cddfSDavid du Colombier     gx_show_text_set_cache, gx_show_text_retry,
1277dd7cddfSDavid du Colombier     gx_show_text_release
1287dd7cddfSDavid du Colombier };
1297dd7cddfSDavid du Colombier 
1307dd7cddfSDavid du Colombier 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)1317dd7cddfSDavid du Colombier gx_default_text_begin(gx_device * dev, gs_imager_state * pis,
1327dd7cddfSDavid du Colombier 		      const gs_text_params_t * text, gs_font * font,
1337dd7cddfSDavid du Colombier 		      gx_path * path, const gx_device_color * pdcolor,
1347dd7cddfSDavid du Colombier 		      const gx_clip_path * pcpath,
1357dd7cddfSDavid du Colombier 		      gs_memory_t * mem, gs_text_enum_t ** ppte)
1367dd7cddfSDavid du Colombier {
1377dd7cddfSDavid du Colombier     uint operation = text->operation;
1387dd7cddfSDavid du Colombier     bool propagate_charpath = (operation & TEXT_DO_DRAW) != 0;
1397dd7cddfSDavid du Colombier     int code;
1407dd7cddfSDavid du Colombier     gs_state *pgs = (gs_state *)pis;
1417dd7cddfSDavid du Colombier     gs_show_enum *penum;
1427dd7cddfSDavid du Colombier 
1437dd7cddfSDavid du Colombier     /*
1447dd7cddfSDavid du Colombier      * For the moment, require pis to be a gs_state *, since all the
1457dd7cddfSDavid du Colombier      * procedures for character rendering expect it.
1467dd7cddfSDavid du Colombier      */
1477dd7cddfSDavid du Colombier     if (gs_object_type(mem, pis) != &st_gs_state)
1487dd7cddfSDavid du Colombier 	return_error(gs_error_Fatal);
1497dd7cddfSDavid du Colombier     penum = gs_show_enum_alloc(mem, pgs, "gx_default_text_begin");
1507dd7cddfSDavid du Colombier     if (!penum)
1517dd7cddfSDavid du Colombier 	return_error(gs_error_VMerror);
1527dd7cddfSDavid du Colombier     code = gs_text_enum_init((gs_text_enum_t *)penum, &default_text_procs,
1537dd7cddfSDavid du Colombier 			     dev, pis, text, font, path, pdcolor, pcpath, mem);
1547dd7cddfSDavid du Colombier     if (code < 0) {
1557dd7cddfSDavid du Colombier 	gs_free_object(mem, penum, "gx_default_text_begin");
1567dd7cddfSDavid du Colombier 	return code;
1577dd7cddfSDavid du Colombier     }
1587dd7cddfSDavid du Colombier     penum->auto_release = false; /* new API */
1597dd7cddfSDavid du Colombier     penum->level = pgs->level;
1607dd7cddfSDavid du Colombier     if (operation & TEXT_DO_ANY_CHARPATH)
1617dd7cddfSDavid du Colombier 	penum->charpath_flag =
1627dd7cddfSDavid du Colombier 	    (operation & TEXT_DO_FALSE_CHARPATH ? cpm_false_charpath :
1637dd7cddfSDavid du Colombier 	     operation & TEXT_DO_TRUE_CHARPATH ? cpm_true_charpath :
1647dd7cddfSDavid du Colombier 	     operation & TEXT_DO_FALSE_CHARBOXPATH ? cpm_false_charboxpath :
1657dd7cddfSDavid du Colombier 	     operation & TEXT_DO_TRUE_CHARBOXPATH ? cpm_true_charboxpath :
1667dd7cddfSDavid du Colombier 	     operation & TEXT_DO_CHARWIDTH ? cpm_charwidth :
1677dd7cddfSDavid du Colombier 	     cpm_show /* can't happen */ );
1687dd7cddfSDavid du Colombier     else
1697dd7cddfSDavid du Colombier 	penum->charpath_flag =
1707dd7cddfSDavid du Colombier 	    (propagate_charpath ? pgs->in_charpath : cpm_show);
1717dd7cddfSDavid du Colombier     penum->cc = 0;
1727dd7cddfSDavid du Colombier     penum->continue_proc = continue_show;
1737dd7cddfSDavid du Colombier     /* Note: show_state_setup may reset can_cache. */
1747dd7cddfSDavid du Colombier     switch (penum->charpath_flag) {
1757dd7cddfSDavid du Colombier     case cpm_false_charpath: case cpm_true_charpath:
1767dd7cddfSDavid du Colombier 	penum->can_cache = -1; break;
1777dd7cddfSDavid du Colombier     case cpm_false_charboxpath: case cpm_true_charboxpath:
1787dd7cddfSDavid du Colombier 	penum->can_cache = 0; break;
1797dd7cddfSDavid du Colombier     case cpm_charwidth:
1807dd7cddfSDavid du Colombier     default:			/* cpm_show */
1817dd7cddfSDavid du Colombier 	penum->can_cache = 1; break;
1827dd7cddfSDavid du Colombier     }
1837dd7cddfSDavid du Colombier     code = show_state_setup(penum);
1847dd7cddfSDavid du Colombier     if (code < 0)
1857dd7cddfSDavid du Colombier 	return code;
1867dd7cddfSDavid du Colombier     penum->show_gstate =
1877dd7cddfSDavid du Colombier 	(propagate_charpath && (pgs->in_charpath != 0) ?
1887dd7cddfSDavid du Colombier 	 pgs->show_gstate : pgs);
1897dd7cddfSDavid du Colombier     if (!(~operation & (TEXT_DO_NONE | TEXT_RETURN_WIDTH))) {
1907dd7cddfSDavid du Colombier 	/* This is stringwidth. */
1917dd7cddfSDavid du Colombier 	gx_device_null *dev_null =
1927dd7cddfSDavid du Colombier 	    gs_alloc_struct(mem, gx_device_null, &st_device_null,
1937dd7cddfSDavid du Colombier 			    "stringwidth(dev_null)");
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 	if (dev_null == 0)
1967dd7cddfSDavid du Colombier 	    return_error(gs_error_VMerror);
1977dd7cddfSDavid du Colombier 	/* Do an extra gsave and suppress output */
1987dd7cddfSDavid du Colombier 	if ((code = gs_gsave(pgs)) < 0)
1997dd7cddfSDavid du Colombier 	    return code;
2007dd7cddfSDavid du Colombier 	penum->level = pgs->level;	/* for level check in show_update */
2017dd7cddfSDavid du Colombier 	/* Set up a null device that forwards xfont requests properly. */
2027dd7cddfSDavid du Colombier 	gs_make_null_device(dev_null, gs_currentdevice_inline(pgs), mem);
2037dd7cddfSDavid du Colombier 	pgs->ctm_default_set = false;
2047dd7cddfSDavid du Colombier 	penum->dev_null = dev_null;
2057dd7cddfSDavid du Colombier 	/* Retain this device, since it is referenced from the enumerator. */
2067dd7cddfSDavid du Colombier 	gx_device_retain((gx_device *)dev_null, true);
2077dd7cddfSDavid du Colombier 	gs_setdevice_no_init(pgs, (gx_device *) dev_null);
2087dd7cddfSDavid du Colombier 	/* Establish an arbitrary translation and current point. */
2097dd7cddfSDavid du Colombier 	gs_newpath(pgs);
2107dd7cddfSDavid du Colombier 	gx_translate_to_fixed(pgs, fixed_0, fixed_0);
2117dd7cddfSDavid du Colombier 	code = gx_path_add_point(pgs->path, fixed_0, fixed_0);
2127dd7cddfSDavid du Colombier 	if (code < 0)
2137dd7cddfSDavid du Colombier 	    return code;
2147dd7cddfSDavid du Colombier     }
2157dd7cddfSDavid du Colombier     *ppte = (gs_text_enum_t *)penum;
2167dd7cddfSDavid du Colombier     return 0;
2177dd7cddfSDavid du Colombier }
2187dd7cddfSDavid du Colombier 
219*593dc095SDavid du Colombier /* An auxiliary functions for pdfwrite to process type 3 fonts. */
220*593dc095SDavid du Colombier int
gx_hld_stringwidth_begin(gs_imager_state * pis,gx_path ** path)221*593dc095SDavid du Colombier gx_hld_stringwidth_begin(gs_imager_state * pis, gx_path **path)
222*593dc095SDavid du Colombier {
223*593dc095SDavid du Colombier     gs_state *pgs = (gs_state *)pis;
224*593dc095SDavid du Colombier     extern_st(st_gs_state);
225*593dc095SDavid du Colombier     int code;
226*593dc095SDavid du Colombier 
227*593dc095SDavid du Colombier     if (gs_object_type(pis->memory, pis) != &st_gs_state)
228*593dc095SDavid du Colombier 	return_error(gs_error_unregistered);
229*593dc095SDavid du Colombier     code = gs_gsave(pgs);
230*593dc095SDavid du Colombier     if (code < 0)
231*593dc095SDavid du Colombier 	return code;
232*593dc095SDavid du Colombier     gs_newpath(pgs);
233*593dc095SDavid du Colombier     *path = pgs->path;
234*593dc095SDavid du Colombier     gx_translate_to_fixed(pgs, fixed_0, fixed_0);
235*593dc095SDavid du Colombier     return gx_path_add_point(pgs->path, fixed_0, fixed_0);
236*593dc095SDavid du Colombier }
237*593dc095SDavid du Colombier 
238*593dc095SDavid du Colombier int
gx_default_text_restore_state(gs_text_enum_t * pte)239*593dc095SDavid du Colombier gx_default_text_restore_state(gs_text_enum_t *pte)
240*593dc095SDavid du Colombier {
241*593dc095SDavid du Colombier     gs_show_enum *penum;
242*593dc095SDavid du Colombier     gs_state *pgs;
243*593dc095SDavid du Colombier 
244*593dc095SDavid du Colombier     if (SHOW_IS(pte, TEXT_DO_NONE))
245*593dc095SDavid du Colombier 	return 0;
246*593dc095SDavid du Colombier     penum = (gs_show_enum *)pte;
247*593dc095SDavid du Colombier     pgs = penum->pgs;
248*593dc095SDavid du Colombier     return gs_grestore(pgs);
249*593dc095SDavid du Colombier }
2507dd7cddfSDavid du Colombier /* ------ Width/cache setting ------ */
2517dd7cddfSDavid du Colombier 
2527dd7cddfSDavid du Colombier private int
253*593dc095SDavid du Colombier     set_cache_device(gs_show_enum *penum, gs_state *pgs,
254*593dc095SDavid du Colombier 		     floatp llx, floatp lly, floatp urx, floatp ury);
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier /* This is the default implementation of text enumerator set_cache. */
2577dd7cddfSDavid du Colombier private int
gx_show_text_set_cache(gs_text_enum_t * pte,const double * pw,gs_text_cache_control_t control)2587dd7cddfSDavid du Colombier gx_show_text_set_cache(gs_text_enum_t *pte, const double *pw,
2597dd7cddfSDavid du Colombier 			  gs_text_cache_control_t control)
2607dd7cddfSDavid du Colombier {
2617dd7cddfSDavid du Colombier     gs_show_enum *const penum = (gs_show_enum *)pte;
2627dd7cddfSDavid du Colombier     gs_state *pgs = penum->pgs;
2637dd7cddfSDavid du Colombier 
2647dd7cddfSDavid du Colombier     switch (control) {
2657dd7cddfSDavid du Colombier     case TEXT_SET_CHAR_WIDTH:
2667dd7cddfSDavid du Colombier 	return set_char_width(penum, pgs, pw[0], pw[1]);
2677dd7cddfSDavid du Colombier     case TEXT_SET_CACHE_DEVICE: {
2687dd7cddfSDavid du Colombier 	int code = set_char_width(penum, pgs, pw[0], pw[1]);	/* default is don't cache */
2697dd7cddfSDavid du Colombier 
2707dd7cddfSDavid du Colombier 	if (code < 0)
2717dd7cddfSDavid du Colombier 	    return code;
272*593dc095SDavid du Colombier 	if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) /* cshow */
273*593dc095SDavid du Colombier             return code;
2747dd7cddfSDavid du Colombier 	return set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
2757dd7cddfSDavid du Colombier     }
2767dd7cddfSDavid du Colombier     case TEXT_SET_CACHE_DEVICE2: {
2777dd7cddfSDavid du Colombier 	int code;
278*593dc095SDavid du Colombier 	bool retry = (penum->width_status == sws_retry);
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier 	if (gs_rootfont(pgs)->WMode) {
2817dd7cddfSDavid du Colombier 	    float vx = pw[8], vy = pw[9];
2827dd7cddfSDavid du Colombier 	    gs_fixed_point pvxy, dvxy;
283*593dc095SDavid du Colombier 
284*593dc095SDavid du Colombier 	    gs_fixed_point rewind_pvxy;
285*593dc095SDavid du Colombier 	    int rewind_code;
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier 	    if ((code = gs_point_transform2fixed(&pgs->ctm, -vx, -vy, &pvxy)) < 0 ||
2887dd7cddfSDavid du Colombier 		(code = gs_distance_transform2fixed(&pgs->ctm, vx, vy, &dvxy)) < 0
2897dd7cddfSDavid du Colombier 		)
2907dd7cddfSDavid du Colombier 		return 0;		/* don't cache */
2917dd7cddfSDavid du Colombier 	    if ((code = set_char_width(penum, pgs, pw[6], pw[7])) < 0)
2927dd7cddfSDavid du Colombier 		return code;
293*593dc095SDavid du Colombier 	    if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE))
294*593dc095SDavid du Colombier 		return code;
2957dd7cddfSDavid du Colombier 	    /* Adjust the origin by (vx, vy). */
2967dd7cddfSDavid du Colombier 	    gx_translate_to_fixed(pgs, pvxy.x, pvxy.y);
2977dd7cddfSDavid du Colombier 	    code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
298*593dc095SDavid du Colombier 	    if (code != 1) {
299*593dc095SDavid du Colombier 	        if (retry) {
300*593dc095SDavid du Colombier 		   rewind_code = gs_point_transform2fixed(&pgs->ctm, vx, vy, &rewind_pvxy);
301*593dc095SDavid du Colombier 		   if (rewind_code < 0) {
302*593dc095SDavid du Colombier 		       /* If the control passes here, something is wrong. */
303*593dc095SDavid du Colombier 		       return_error(gs_error_unregistered);
304*593dc095SDavid du Colombier 		   }
305*593dc095SDavid du Colombier 		   /* Rewind the origin by (-vx, -vy) if the cache is failed. */
306*593dc095SDavid du Colombier 		   gx_translate_to_fixed(pgs, rewind_pvxy.x, rewind_pvxy.y);
307*593dc095SDavid du Colombier 		}
3087dd7cddfSDavid du Colombier 		return code;
309*593dc095SDavid du Colombier 	    }
3107dd7cddfSDavid du Colombier 	    /* Adjust the character origin too. */
311*593dc095SDavid du Colombier 	    (penum->cc)->offset.x += dvxy.x;
312*593dc095SDavid du Colombier 	    (penum->cc)->offset.y += dvxy.y;
3137dd7cddfSDavid du Colombier 	} else {
3147dd7cddfSDavid du Colombier 	    code = set_char_width(penum, pgs, pw[0], pw[1]);
3157dd7cddfSDavid du Colombier 	    if (code < 0)
3167dd7cddfSDavid du Colombier 		return code;
317*593dc095SDavid du Colombier 	    if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE))
318*593dc095SDavid du Colombier 		return code;
3197dd7cddfSDavid du Colombier 	    code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
3207dd7cddfSDavid du Colombier 	}
3217dd7cddfSDavid du Colombier 	return code;
3227dd7cddfSDavid du Colombier     }
3237dd7cddfSDavid du Colombier     default:
3247dd7cddfSDavid du Colombier 	return_error(gs_error_rangecheck);
3257dd7cddfSDavid du Colombier     }
3267dd7cddfSDavid du Colombier }
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier /* Set the character width. */
3297dd7cddfSDavid du Colombier /* Note that this returns 1 if the current show operation is */
3307dd7cddfSDavid du Colombier /* non-displaying (stringwidth or cshow). */
331*593dc095SDavid du Colombier int
set_char_width(gs_show_enum * penum,gs_state * pgs,floatp wx,floatp wy)3327dd7cddfSDavid du Colombier set_char_width(gs_show_enum *penum, gs_state *pgs, floatp wx, floatp wy)
3337dd7cddfSDavid du Colombier {
3347dd7cddfSDavid du Colombier     int code;
3357dd7cddfSDavid du Colombier 
336*593dc095SDavid du Colombier     if (penum->width_status != sws_none && penum->width_status != sws_retry)
3377dd7cddfSDavid du Colombier 	return_error(gs_error_undefined);
338*593dc095SDavid du Colombier     if (penum->fstack.depth > 0 &&
339*593dc095SDavid du Colombier 	penum->fstack.items[penum->fstack.depth].font->FontType == ft_CID_encrypted) {
340*593dc095SDavid du Colombier     	/* We must not convert advance width with a CID font leaf's FontMatrix,
341*593dc095SDavid du Colombier 	   because CDevProc is attached to the CID font rather than to its leaf.
342*593dc095SDavid du Colombier 	   But show_state_setup sets CTM with the leaf's matrix.
343*593dc095SDavid du Colombier 	   Compensate it here with inverse FontMatrix of the leaf.
344*593dc095SDavid du Colombier 	   ( We would like to do without an inverse transform, but
345*593dc095SDavid du Colombier 	     we don't like to extend general gs_state or gs_show_enum
346*593dc095SDavid du Colombier 	     for this particular reason. ) */
347*593dc095SDavid du Colombier 	const gx_font_stack_item_t *pfsi = &penum->fstack.items[penum->fstack.depth];
348*593dc095SDavid du Colombier 	gs_point p;
349*593dc095SDavid du Colombier 
350*593dc095SDavid du Colombier 	code = gs_distance_transform_inverse(wx, wy,
351*593dc095SDavid du Colombier 		&gs_cid0_indexed_font(pfsi->font, pfsi->index)->FontMatrix, &p);
352*593dc095SDavid du Colombier 	if (code < 0)
353*593dc095SDavid du Colombier 	    return code;
354*593dc095SDavid du Colombier 	wx = p.x;
355*593dc095SDavid du Colombier 	wy = p.y;
356*593dc095SDavid du Colombier     }
3577dd7cddfSDavid du Colombier     if ((code = gs_distance_transform2fixed(&pgs->ctm, wx, wy, &penum->wxy)) < 0)
3587dd7cddfSDavid du Colombier 	return code;
3597dd7cddfSDavid du Colombier     /* Check whether we're setting the scalable width */
3607dd7cddfSDavid du Colombier     /* for a cached xfont character. */
3617dd7cddfSDavid du Colombier     if (penum->cc != 0) {
3627dd7cddfSDavid du Colombier 	penum->cc->wxy = penum->wxy;
3637dd7cddfSDavid du Colombier 	penum->width_status = sws_cache_width_only;
3647dd7cddfSDavid du Colombier     } else {
3657dd7cddfSDavid du Colombier 	penum->width_status = sws_no_cache;
3667dd7cddfSDavid du Colombier     }
367*593dc095SDavid du Colombier     if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) /* cshow */
368*593dc095SDavid du Colombier 	gs_nulldevice(pgs);
3697dd7cddfSDavid du Colombier     return !SHOW_IS_DRAWING(penum);
3707dd7cddfSDavid du Colombier }
3717dd7cddfSDavid du Colombier 
372*593dc095SDavid du Colombier void
gx_compute_text_oversampling(const gs_show_enum * penum,const gs_font * pfont,int alpha_bits,gs_log2_scale_point * p_log2_scale)373*593dc095SDavid du Colombier gx_compute_text_oversampling(const gs_show_enum * penum, const gs_font *pfont,
374*593dc095SDavid du Colombier                              int alpha_bits, gs_log2_scale_point *p_log2_scale)
375*593dc095SDavid du Colombier {
376*593dc095SDavid du Colombier     gs_log2_scale_point log2_scale;
377*593dc095SDavid du Colombier 
378*593dc095SDavid du Colombier     if (alpha_bits == 1)
379*593dc095SDavid du Colombier 	log2_scale.x = log2_scale.y = 0;
380*593dc095SDavid du Colombier     else if (pfont->PaintType != 0) {
381*593dc095SDavid du Colombier 	/* Don't oversample artificially stroked fonts. */
382*593dc095SDavid du Colombier 	log2_scale.x = log2_scale.y = 0;
383*593dc095SDavid du Colombier     } else if (!penum->is_pure_color) {
384*593dc095SDavid du Colombier 	/* Don't oversample characters for rendering in non-pure color. */
385*593dc095SDavid du Colombier 	log2_scale.x = log2_scale.y = 0;
386*593dc095SDavid du Colombier     } else {
387*593dc095SDavid du Colombier 	int excess;
388*593dc095SDavid du Colombier 
389*593dc095SDavid du Colombier 	/* Get maximal scale according to cached bitmap size. */
390*593dc095SDavid du Colombier 	show_set_scale(penum, &log2_scale);
391*593dc095SDavid du Colombier 	/* Reduce the scale to fit into alpha bits. */
392*593dc095SDavid du Colombier 	excess = log2_scale.x + log2_scale.y - alpha_bits;
393*593dc095SDavid du Colombier 	while (excess > 0) {
394*593dc095SDavid du Colombier 	    if (log2_scale.y > 0) {
395*593dc095SDavid du Colombier 		log2_scale.y --;
396*593dc095SDavid du Colombier 		excess--;
397*593dc095SDavid du Colombier 		if (excess == 0)
398*593dc095SDavid du Colombier 		    break;
399*593dc095SDavid du Colombier 	    }
400*593dc095SDavid du Colombier 	    if (log2_scale.x > 0) {
401*593dc095SDavid du Colombier 		log2_scale.x --;
402*593dc095SDavid du Colombier 		excess--;
403*593dc095SDavid du Colombier 	    }
404*593dc095SDavid du Colombier 	}
405*593dc095SDavid du Colombier     }
406*593dc095SDavid du Colombier     *p_log2_scale = log2_scale;
407*593dc095SDavid du Colombier }
408*593dc095SDavid du Colombier 
409*593dc095SDavid du Colombier /* Compute glyph raster parameters */
410*593dc095SDavid du Colombier private int
compute_glyph_raster_params(gs_show_enum * penum,bool in_setcachedevice,int * alpha_bits,int * depth,gs_fixed_point * subpix_origin,gs_log2_scale_point * log2_scale)411*593dc095SDavid du Colombier compute_glyph_raster_params(gs_show_enum *penum, bool in_setcachedevice, int *alpha_bits,
412*593dc095SDavid du Colombier 		    int *depth,
413*593dc095SDavid du Colombier                     gs_fixed_point *subpix_origin, gs_log2_scale_point *log2_scale)
414*593dc095SDavid du Colombier {
415*593dc095SDavid du Colombier     gs_state *pgs = penum->pgs;
416*593dc095SDavid du Colombier     gx_device *dev = gs_currentdevice_inline(pgs);
417*593dc095SDavid du Colombier     int code;
418*593dc095SDavid du Colombier 
419*593dc095SDavid du Colombier     *alpha_bits = (*dev_proc(dev, get_alpha_bits)) (dev, go_text);
420*593dc095SDavid du Colombier     if (in_setcachedevice) {
421*593dc095SDavid du Colombier 	/* current point should already be in penum->origin */
422*593dc095SDavid du Colombier     } else {
423*593dc095SDavid du Colombier 	code = gx_path_current_point_inline(pgs->path, &penum->origin);
424*593dc095SDavid du Colombier 	if (code < 0) {
425*593dc095SDavid du Colombier 	    /* For cshow, having no current point is acceptable. */
426*593dc095SDavid du Colombier 	    if (!SHOW_IS(penum, TEXT_DO_NONE))
427*593dc095SDavid du Colombier 		return code;
428*593dc095SDavid du Colombier 	    penum->origin.x = penum->origin.y = 0;	/* arbitrary */
429*593dc095SDavid du Colombier 	}
430*593dc095SDavid du Colombier     }
431*593dc095SDavid du Colombier     if (penum->fapi_log2_scale.x != -1)
432*593dc095SDavid du Colombier 	*log2_scale = penum->fapi_log2_scale;
433*593dc095SDavid du Colombier     else
434*593dc095SDavid du Colombier 	gx_compute_text_oversampling(penum, penum->current_font, *alpha_bits, log2_scale);
435*593dc095SDavid du Colombier     /*	We never oversample over the device alpha_bits,
436*593dc095SDavid du Colombier      * so that we don't need to scale down. Perhaps it may happen
437*593dc095SDavid du Colombier      * that we underuse alpha_bits due to a big character raster,
438*593dc095SDavid du Colombier      * so we must compute log2_depth more accurately :
439*593dc095SDavid du Colombier      */
440*593dc095SDavid du Colombier     *depth = (log2_scale->x + log2_scale->y == 0 ?
441*593dc095SDavid du Colombier         1 : min(log2_scale->x + log2_scale->y, *alpha_bits));
442*593dc095SDavid du Colombier     if (gs_currentaligntopixels(penum->current_font->dir) == 0) {
443*593dc095SDavid du Colombier 	int scx = -1L << (_fixed_shift - log2_scale->x);
444*593dc095SDavid du Colombier 	int rdx =  1L << (_fixed_shift - 1 - log2_scale->x);
445*593dc095SDavid du Colombier 
446*593dc095SDavid du Colombier #	if 1 /* Ever align Y to pixels to provide an uniform glyph height. */
447*593dc095SDavid du Colombier 	    subpix_origin->y = 0;
448*593dc095SDavid du Colombier #	else
449*593dc095SDavid du Colombier 	    int scy = -1L << (_fixed_shift - log2_scale->y);
450*593dc095SDavid du Colombier 	    int rdy =  1L << (_fixed_shift - 1 - log2_scale->y);
451*593dc095SDavid du Colombier 
452*593dc095SDavid du Colombier 	    subpix_origin->y = ((penum->origin.y + rdy) & scy) & (fixed_1 - 1);
453*593dc095SDavid du Colombier #	endif
454*593dc095SDavid du Colombier 	subpix_origin->x = ((penum->origin.x + rdx) & scx) & (fixed_1 - 1);
455*593dc095SDavid du Colombier     } else
456*593dc095SDavid du Colombier 	subpix_origin->x = subpix_origin->y = 0;
457*593dc095SDavid du Colombier     return 0;
458*593dc095SDavid du Colombier }
459*593dc095SDavid du Colombier 
4607dd7cddfSDavid du Colombier /* Set up the cache device if relevant. */
4617dd7cddfSDavid du Colombier /* Return 1 if we just set up a cache device. */
4627dd7cddfSDavid du Colombier /* Used by setcachedevice and setcachedevice2. */
4637dd7cddfSDavid du Colombier private int
set_cache_device(gs_show_enum * penum,gs_state * pgs,floatp llx,floatp lly,floatp urx,floatp ury)4647dd7cddfSDavid du Colombier set_cache_device(gs_show_enum * penum, gs_state * pgs, floatp llx, floatp lly,
4657dd7cddfSDavid du Colombier 		 floatp urx, floatp ury)
4667dd7cddfSDavid du Colombier {
4677dd7cddfSDavid du Colombier     gs_glyph glyph;
4687dd7cddfSDavid du Colombier 
4697dd7cddfSDavid du Colombier     /* See if we want to cache this character. */
4707dd7cddfSDavid du Colombier     if (pgs->in_cachedevice)	/* no recursion! */
4717dd7cddfSDavid du Colombier 	return 0;
4723ff48bf5SDavid du Colombier     if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) { /* cshow */
4733ff48bf5SDavid du Colombier 	int code;
4743ff48bf5SDavid du Colombier 	if_debug0('k', "[k]no cache: cshow");
4753ff48bf5SDavid du Colombier 	code = gs_nulldevice(pgs);
4763ff48bf5SDavid du Colombier 	if (code < 0)
4773ff48bf5SDavid du Colombier 	    return code;
4783ff48bf5SDavid du Colombier 	return 0;
4793ff48bf5SDavid du Colombier     }
4807dd7cddfSDavid du Colombier     pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;	/* disable color/gray/image operators */
4817dd7cddfSDavid du Colombier     /* We can only use the cache if we know the glyph. */
4827dd7cddfSDavid du Colombier     glyph = CURRENT_GLYPH(penum);
4837dd7cddfSDavid du Colombier     if (glyph == gs_no_glyph)
4847dd7cddfSDavid du Colombier 	return 0;
4857dd7cddfSDavid du Colombier     /* We can only use the cache if ctm is unchanged */
4867dd7cddfSDavid du Colombier     /* (aside from a possible translation). */
4877dd7cddfSDavid du Colombier     if (penum->can_cache <= 0 || !pgs->char_tm_valid) {
4887dd7cddfSDavid du Colombier 	if_debug2('k', "[k]no cache: can_cache=%d, char_tm_valid=%d\n",
4897dd7cddfSDavid du Colombier 		  penum->can_cache, (int)pgs->char_tm_valid);
4907dd7cddfSDavid du Colombier 	return 0;
4917dd7cddfSDavid du Colombier     } {
4927dd7cddfSDavid du Colombier 	const gs_font *pfont = pgs->font;
4937dd7cddfSDavid du Colombier 	gs_font_dir *dir = pfont->dir;
494*593dc095SDavid du Colombier         int alpha_bits, depth;
4957dd7cddfSDavid du Colombier 	gs_log2_scale_point log2_scale;
496*593dc095SDavid du Colombier 	gs_fixed_point subpix_origin;
4977dd7cddfSDavid du Colombier         static const fixed max_cdim[3] =
4987dd7cddfSDavid du Colombier         {
4997dd7cddfSDavid du Colombier #define max_cd(n)\
5007dd7cddfSDavid du Colombier 	    (fixed_1 << (arch_sizeof_short * 8 - n)) - (fixed_1 >> n) * 3
5017dd7cddfSDavid du Colombier 	    max_cd(0), max_cd(1), max_cd(2)
5027dd7cddfSDavid du Colombier #undef max_cd
5037dd7cddfSDavid du Colombier         };
5047dd7cddfSDavid du Colombier 	ushort iwidth, iheight;
5057dd7cddfSDavid du Colombier 	cached_char *cc;
5067dd7cddfSDavid du Colombier 	gs_fixed_rect clip_box;
5077dd7cddfSDavid du Colombier 	int code;
5087dd7cddfSDavid du Colombier 
5097dd7cddfSDavid du Colombier 	/* Compute the bounding box of the transformed character. */
5107dd7cddfSDavid du Colombier 	/* Since we accept arbitrary transformations, the extrema */
5117dd7cddfSDavid du Colombier 	/* may occur in any order; however, we can save some work */
5127dd7cddfSDavid du Colombier 	/* by observing that opposite corners before transforming */
5137dd7cddfSDavid du Colombier 	/* are still opposite afterwards. */
5147dd7cddfSDavid du Colombier 	gs_fixed_point cll, clr, cul, cur, cdim;
5157dd7cddfSDavid du Colombier 
5167dd7cddfSDavid du Colombier 	if ((code = gs_distance_transform2fixed(&pgs->ctm, llx, lly, &cll)) < 0 ||
5177dd7cddfSDavid du Colombier 	    (code = gs_distance_transform2fixed(&pgs->ctm, llx, ury, &clr)) < 0 ||
5187dd7cddfSDavid du Colombier 	    (code = gs_distance_transform2fixed(&pgs->ctm, urx, lly, &cul)) < 0 ||
5197dd7cddfSDavid du Colombier 	 (code = gs_distance_transform2fixed(&pgs->ctm, urx, ury, &cur)) < 0
5207dd7cddfSDavid du Colombier 	    )
5217dd7cddfSDavid du Colombier 	    return 0;		/* don't cache */
5227dd7cddfSDavid du Colombier 	{
5237dd7cddfSDavid du Colombier 	    fixed ctemp;
5247dd7cddfSDavid du Colombier 
5257dd7cddfSDavid du Colombier #define swap(a, b) ctemp = a, a = b, b = ctemp
5267dd7cddfSDavid du Colombier #define make_min(a, b) if ( (a) > (b) ) swap(a, b)
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	    make_min(cll.x, cur.x);
5297dd7cddfSDavid du Colombier 	    make_min(cll.y, cur.y);
5307dd7cddfSDavid du Colombier 	    make_min(clr.x, cul.x);
5317dd7cddfSDavid du Colombier 	    make_min(clr.y, cul.y);
5327dd7cddfSDavid du Colombier #undef make_min
5337dd7cddfSDavid du Colombier #undef swap
5347dd7cddfSDavid du Colombier 	}
5357dd7cddfSDavid du Colombier 	/* Now take advantage of symmetry. */
5367dd7cddfSDavid du Colombier 	if (clr.x < cll.x)
5377dd7cddfSDavid du Colombier 	    cll.x = clr.x, cur.x = cul.x;
5387dd7cddfSDavid du Colombier 	if (clr.y < cll.y)
5397dd7cddfSDavid du Colombier 	    cll.y = clr.y, cur.y = cul.y;
5407dd7cddfSDavid du Colombier 	/* Now cll and cur are the extrema of the box. */
541*593dc095SDavid du Colombier 	code = compute_glyph_raster_params(penum, true, &alpha_bits, &depth,
542*593dc095SDavid du Colombier            &subpix_origin, &log2_scale);
543*593dc095SDavid du Colombier 	if (code < 0)
544*593dc095SDavid du Colombier 	    return code;
5457dd7cddfSDavid du Colombier #ifdef DEBUG
5467dd7cddfSDavid du Colombier         if (gs_debug_c('k')) {
5477dd7cddfSDavid du Colombier 	    dlprintf6("[k]cbox=[%g %g %g %g] scale=%dx%d\n",
5487dd7cddfSDavid du Colombier 		      fixed2float(cll.x), fixed2float(cll.y),
5497dd7cddfSDavid du Colombier 		      fixed2float(cur.x), fixed2float(cur.y),
5507dd7cddfSDavid du Colombier 		      1 << log2_scale.x, 1 << log2_scale.y);
5517dd7cddfSDavid du Colombier 	    dlprintf6("[p]  ctm=[%g %g %g %g %g %g]\n",
5527dd7cddfSDavid du Colombier 		      pgs->ctm.xx, pgs->ctm.xy, pgs->ctm.yx, pgs->ctm.yy,
5537dd7cddfSDavid du Colombier 		      pgs->ctm.tx, pgs->ctm.ty);
5547dd7cddfSDavid du Colombier         }
5557dd7cddfSDavid du Colombier #endif
556*593dc095SDavid du Colombier 	cdim.x = cur.x - cll.x;
557*593dc095SDavid du Colombier 	cdim.y = cur.y - cll.y;
5587dd7cddfSDavid du Colombier 	if (cdim.x > max_cdim[log2_scale.x] ||
5597dd7cddfSDavid du Colombier 	    cdim.y > max_cdim[log2_scale.y]
5607dd7cddfSDavid du Colombier 	    )
5617dd7cddfSDavid du Colombier 	    return 0;		/* much too big */
5627dd7cddfSDavid du Colombier 	iwidth = ((ushort) fixed2int_var(cdim.x) + 2) << log2_scale.x;
5637dd7cddfSDavid du Colombier 	iheight = ((ushort) fixed2int_var(cdim.y) + 2) << log2_scale.y;
5647dd7cddfSDavid du Colombier 	if_debug3('k', "[k]iwidth=%u iheight=%u dev_cache %s\n",
5657dd7cddfSDavid du Colombier 		  (uint) iwidth, (uint) iheight,
5667dd7cddfSDavid du Colombier 		  (penum->dev_cache == 0 ? "not set" : "set"));
5677dd7cddfSDavid du Colombier 	if (penum->dev_cache == 0) {
5687dd7cddfSDavid du Colombier 	    code = show_cache_setup(penum);
5697dd7cddfSDavid du Colombier 	    if (code < 0)
5707dd7cddfSDavid du Colombier 		return code;
5717dd7cddfSDavid du Colombier 	}
5727dd7cddfSDavid du Colombier 	/*
5737dd7cddfSDavid du Colombier 	 * If we're oversampling (i.e., the temporary bitmap is
5747dd7cddfSDavid du Colombier 	 * larger than the final monobit or alpha array) and the
5757dd7cddfSDavid du Colombier 	 * temporary bitmap is large, use incremental conversion
5767dd7cddfSDavid du Colombier 	 * from oversampled bitmap strips to alpha values instead of
5777dd7cddfSDavid du Colombier 	 * full oversampling with compression at the end.
5787dd7cddfSDavid du Colombier 	 */
5797dd7cddfSDavid du Colombier 	cc = gx_alloc_char_bits(dir, penum->dev_cache,
5807dd7cddfSDavid du Colombier 				(iwidth > MAX_TEMP_BITMAP_BITS / iheight &&
5817dd7cddfSDavid du Colombier 				 log2_scale.x + log2_scale.y > alpha_bits ?
5827dd7cddfSDavid du Colombier 				 penum->dev_cache2 : NULL),
583*593dc095SDavid du Colombier 				iwidth, iheight, &log2_scale, depth);
584*593dc095SDavid du Colombier 	if (cc == 0) {
585*593dc095SDavid du Colombier 	    /* too big for cache or no cache */
586*593dc095SDavid du Colombier 	    gx_path box_path;
587*593dc095SDavid du Colombier 
588*593dc095SDavid du Colombier 	    if (penum->current_font->FontType != ft_user_defined &&
589*593dc095SDavid du Colombier 		penum->current_font->FontType != ft_CID_user_defined) {
590*593dc095SDavid du Colombier 		/* Most fonts don't paint outside bbox,
591*593dc095SDavid du Colombier 		   so render with no clipping. */
592*593dc095SDavid du Colombier 		return 0;
593*593dc095SDavid du Colombier 	    }
594*593dc095SDavid du Colombier 	    /* Render with a clip. */
595*593dc095SDavid du Colombier 	    /* show_proceed already did gsave. */
596*593dc095SDavid du Colombier 	    pgs->in_cachedevice = CACHE_DEVICE_NONE; /* Provide a correct grestore on error. */
597*593dc095SDavid du Colombier 	    clip_box.p.x = penum->origin.x - fixed_ceiling(-cll.x);
598*593dc095SDavid du Colombier 	    clip_box.p.y = penum->origin.y - fixed_ceiling(-cll.y);
599*593dc095SDavid du Colombier 	    clip_box.q.x = clip_box.p.x + int2fixed(iwidth);
600*593dc095SDavid du Colombier 	    clip_box.q.y = clip_box.p.y + int2fixed(iheight);
601*593dc095SDavid du Colombier 	    gx_path_init_local(&box_path, pgs->memory);
602*593dc095SDavid du Colombier 	    code = gx_path_add_rectangle(&box_path, clip_box.p.x, clip_box.p.y,
603*593dc095SDavid du Colombier 						    clip_box.q.x, clip_box.q.y);
604*593dc095SDavid du Colombier 	    if (code < 0)
605*593dc095SDavid du Colombier 		return code;
606*593dc095SDavid du Colombier 	    code = gx_cpath_clip(pgs, pgs->clip_path, &box_path, gx_rule_winding_number);
607*593dc095SDavid du Colombier 	    gx_path_free(&box_path, "set_cache_device");
608*593dc095SDavid du Colombier 	    pgs->in_cachedevice = CACHE_DEVICE_NONE_AND_CLIP;
609*593dc095SDavid du Colombier 	    return 0;
610*593dc095SDavid du Colombier 	}
6117dd7cddfSDavid du Colombier 	/* The mins handle transposed coordinate systems.... */
6127dd7cddfSDavid du Colombier 	/* Truncate the offsets to avoid artifacts later. */
6137dd7cddfSDavid du Colombier 	cc->offset.x = fixed_ceiling(-cll.x);
6147dd7cddfSDavid du Colombier 	cc->offset.y = fixed_ceiling(-cll.y);
6157dd7cddfSDavid du Colombier 	if_debug4('k', "[k]width=%u, height=%u, offset=[%g %g]\n",
6167dd7cddfSDavid du Colombier 		  (uint) iwidth, (uint) iheight,
6177dd7cddfSDavid du Colombier 		  fixed2float(cc->offset.x),
6187dd7cddfSDavid du Colombier 		  fixed2float(cc->offset.y));
6193ff48bf5SDavid du Colombier 	pgs->in_cachedevice = CACHE_DEVICE_NONE; /* Provide correct grestore */
6207dd7cddfSDavid du Colombier 	if ((code = gs_gsave(pgs)) < 0) {
6217dd7cddfSDavid du Colombier 	    gx_free_cached_char(dir, cc);
6227dd7cddfSDavid du Colombier 	    return code;
6237dd7cddfSDavid du Colombier 	}
6247dd7cddfSDavid du Colombier 	/* Nothing can go wrong now.... */
6257dd7cddfSDavid du Colombier 	penum->cc = cc;
6267dd7cddfSDavid du Colombier 	cc->code = glyph;
6277dd7cddfSDavid du Colombier 	cc->wmode = gs_rootfont(pgs)->WMode;
6287dd7cddfSDavid du Colombier 	cc->wxy = penum->wxy;
629*593dc095SDavid du Colombier 	cc->subpix_origin = subpix_origin;
630*593dc095SDavid du Colombier 	if (penum->pair != 0)
631*593dc095SDavid du Colombier 	    cc_set_pair(cc, penum->pair);
632*593dc095SDavid du Colombier 	else
633*593dc095SDavid du Colombier 	    cc->pair = 0;
6347dd7cddfSDavid du Colombier 	/* Install the device */
6357dd7cddfSDavid du Colombier 	gx_set_device_only(pgs, (gx_device *) penum->dev_cache);
6367dd7cddfSDavid du Colombier 	pgs->ctm_default_set = false;
6377dd7cddfSDavid du Colombier 	/* Adjust the transformation in the graphics context */
6387dd7cddfSDavid du Colombier 	/* so that the character lines up with the cache. */
6397dd7cddfSDavid du Colombier 	gx_translate_to_fixed(pgs,
640*593dc095SDavid du Colombier 			      (cc->offset.x + subpix_origin.x) << log2_scale.x,
641*593dc095SDavid du Colombier 			      (cc->offset.y + subpix_origin.y) << log2_scale.y);
6427dd7cddfSDavid du Colombier 	if ((log2_scale.x | log2_scale.y) != 0)
6437dd7cddfSDavid du Colombier 	    gx_scale_char_matrix(pgs, 1 << log2_scale.x,
6447dd7cddfSDavid du Colombier 				 1 << log2_scale.y);
6457dd7cddfSDavid du Colombier 	/* Set the initial matrix for the cache device. */
6467dd7cddfSDavid du Colombier 	penum->dev_cache->initial_matrix = ctm_only(pgs);
6477dd7cddfSDavid du Colombier 	/* Set the oversampling factor. */
6487dd7cddfSDavid du Colombier 	penum->log2_scale.x = log2_scale.x;
6497dd7cddfSDavid du Colombier 	penum->log2_scale.y = log2_scale.y;
6507dd7cddfSDavid du Colombier 	/* Reset the clipping path to match the metrics. */
6517dd7cddfSDavid du Colombier 	clip_box.p.x = clip_box.p.y = 0;
6527dd7cddfSDavid du Colombier 	clip_box.q.x = int2fixed(iwidth);
6537dd7cddfSDavid du Colombier 	clip_box.q.y = int2fixed(iheight);
6547dd7cddfSDavid du Colombier 	if ((code = gx_clip_to_rectangle(pgs, &clip_box)) < 0)
6557dd7cddfSDavid du Colombier 	    return code;
6567dd7cddfSDavid du Colombier 	gx_set_device_color_1(pgs);	/* write 1's */
6577dd7cddfSDavid du Colombier 	pgs->in_cachedevice = CACHE_DEVICE_CACHING;
6587dd7cddfSDavid du Colombier     }
6597dd7cddfSDavid du Colombier     penum->width_status = sws_cache;
6607dd7cddfSDavid du Colombier     return 1;
6617dd7cddfSDavid du Colombier }
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier /* Return the cache device status. */
6647dd7cddfSDavid du Colombier gs_in_cache_device_t
gs_incachedevice(const gs_state * pgs)6657dd7cddfSDavid du Colombier gs_incachedevice(const gs_state *pgs)
6667dd7cddfSDavid du Colombier {
6677dd7cddfSDavid du Colombier     return pgs->in_cachedevice;
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier 
6707dd7cddfSDavid du Colombier /* ------ Enumerator ------ */
6717dd7cddfSDavid du Colombier 
6727dd7cddfSDavid du Colombier /*
6737dd7cddfSDavid du Colombier  * Set the encode_char procedure in an enumerator.
6747dd7cddfSDavid du Colombier  */
6757dd7cddfSDavid du Colombier private void
show_set_encode_char(gs_show_enum * penum)6767dd7cddfSDavid du Colombier show_set_encode_char(gs_show_enum * penum)
6777dd7cddfSDavid du Colombier {
6787dd7cddfSDavid du Colombier     penum->encode_char =
6797dd7cddfSDavid du Colombier 	(SHOW_IS(penum, TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH) ?
6807dd7cddfSDavid du Colombier 	 gs_no_encode_char :
6817dd7cddfSDavid du Colombier 	 gs_show_current_font(penum)->procs.encode_char);
6827dd7cddfSDavid du Colombier }
6837dd7cddfSDavid du Colombier 
6847dd7cddfSDavid du Colombier /*
6857dd7cddfSDavid du Colombier  * Resync a text operation with a different set of parameters.
6867dd7cddfSDavid du Colombier  * Currently this is implemented only for changing the data source.
6877dd7cddfSDavid du Colombier  */
6887dd7cddfSDavid du Colombier private int
gx_show_text_resync(gs_text_enum_t * pte,const gs_text_enum_t * pfrom)6897dd7cddfSDavid du Colombier gx_show_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
6907dd7cddfSDavid du Colombier {
6917dd7cddfSDavid du Colombier     gs_show_enum *const penum = (gs_show_enum *)pte;
6927dd7cddfSDavid du Colombier     int old_index = pte->index;
6937dd7cddfSDavid du Colombier 
6947dd7cddfSDavid du Colombier     if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY)
6957dd7cddfSDavid du Colombier 	return_error(gs_error_rangecheck);
6967dd7cddfSDavid du Colombier     pte->text = pfrom->text;
6977dd7cddfSDavid du Colombier     if (pte->index == old_index) {
6987dd7cddfSDavid du Colombier 	show_set_encode_char(penum);
6997dd7cddfSDavid du Colombier 	return 0;
7007dd7cddfSDavid du Colombier     } else
7017dd7cddfSDavid du Colombier 	return show_state_setup(penum);
7027dd7cddfSDavid du Colombier }
7037dd7cddfSDavid du Colombier 
7047dd7cddfSDavid du Colombier /* Do the next step of a show (or stringwidth) operation */
7057dd7cddfSDavid du Colombier private int
gx_show_text_process(gs_text_enum_t * pte)7067dd7cddfSDavid du Colombier gx_show_text_process(gs_text_enum_t *pte)
7077dd7cddfSDavid du Colombier {
7087dd7cddfSDavid du Colombier     gs_show_enum *const penum = (gs_show_enum *)pte;
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier     return (*penum->continue_proc)(penum);
7117dd7cddfSDavid du Colombier }
7127dd7cddfSDavid du Colombier 
7137dd7cddfSDavid du Colombier /* Continuation procedures */
714*593dc095SDavid du Colombier private int show_update(gs_show_enum * penum);
715*593dc095SDavid du Colombier private int show_move(gs_show_enum * penum);
716*593dc095SDavid du Colombier private int show_proceed(gs_show_enum * penum);
717*593dc095SDavid du Colombier private int show_finish(gs_show_enum * penum);
7187dd7cddfSDavid du Colombier private int
continue_show_update(gs_show_enum * penum)7197dd7cddfSDavid du Colombier continue_show_update(gs_show_enum * penum)
7207dd7cddfSDavid du Colombier {
7217dd7cddfSDavid du Colombier     int code = show_update(penum);
7227dd7cddfSDavid du Colombier 
7237dd7cddfSDavid du Colombier     if (code < 0)
7247dd7cddfSDavid du Colombier 	return code;
7257dd7cddfSDavid du Colombier     code = show_move(penum);
7267dd7cddfSDavid du Colombier     if (code != 0)
7277dd7cddfSDavid du Colombier 	return code;
7287dd7cddfSDavid du Colombier     return show_proceed(penum);
7297dd7cddfSDavid du Colombier }
7307dd7cddfSDavid du Colombier private int
continue_show(gs_show_enum * penum)7317dd7cddfSDavid du Colombier continue_show(gs_show_enum * penum)
7327dd7cddfSDavid du Colombier {
7337dd7cddfSDavid du Colombier     return show_proceed(penum);
7347dd7cddfSDavid du Colombier }
7357dd7cddfSDavid du Colombier /* For kshow, the CTM or font may have changed, so we have to reestablish */
7367dd7cddfSDavid du Colombier /* the cached values in the enumerator. */
7377dd7cddfSDavid du Colombier private int
continue_kshow(gs_show_enum * penum)7387dd7cddfSDavid du Colombier continue_kshow(gs_show_enum * penum)
7393ff48bf5SDavid du Colombier {   int code;
7403ff48bf5SDavid du Colombier     gs_state *pgs = penum->pgs;
7413ff48bf5SDavid du Colombier 
7423ff48bf5SDavid du Colombier     if (pgs->font != penum->orig_font)
7433ff48bf5SDavid du Colombier 	gs_setfont(pgs, penum->orig_font);
7443ff48bf5SDavid du Colombier 
7453ff48bf5SDavid du Colombier     code = show_state_setup(penum);
7467dd7cddfSDavid du Colombier 
7477dd7cddfSDavid du Colombier     if (code < 0)
7487dd7cddfSDavid du Colombier 	return code;
7497dd7cddfSDavid du Colombier     return show_proceed(penum);
7507dd7cddfSDavid du Colombier }
7517dd7cddfSDavid du Colombier 
7527dd7cddfSDavid du Colombier /* Update position */
7537dd7cddfSDavid du Colombier private int
show_update(gs_show_enum * penum)7547dd7cddfSDavid du Colombier show_update(gs_show_enum * penum)
7557dd7cddfSDavid du Colombier {
7567dd7cddfSDavid du Colombier     gs_state *pgs = penum->pgs;
7577dd7cddfSDavid du Colombier     cached_char *cc = penum->cc;
7587dd7cddfSDavid du Colombier     int code;
7597dd7cddfSDavid du Colombier 
7607dd7cddfSDavid du Colombier     /* Update position for last character */
7617dd7cddfSDavid du Colombier     switch (penum->width_status) {
7627dd7cddfSDavid du Colombier 	case sws_none:
763*593dc095SDavid du Colombier         case sws_retry:
7647dd7cddfSDavid du Colombier 	    /* Adobe interpreters assume a character width of 0, */
7657dd7cddfSDavid du Colombier 	    /* even though the documentation says this is an error.... */
7667dd7cddfSDavid du Colombier 	    penum->wxy.x = penum->wxy.y = 0;
7677dd7cddfSDavid du Colombier 	    break;
7687dd7cddfSDavid du Colombier 	case sws_cache:
7697dd7cddfSDavid du Colombier 	    /* Finish installing the cache entry. */
7707dd7cddfSDavid du Colombier 	    /* If the BuildChar/BuildGlyph procedure did a save and a */
7717dd7cddfSDavid du Colombier 	    /* restore, it already undid the gsave in setcachedevice. */
7727dd7cddfSDavid du Colombier 	    /* We have to check for this by comparing levels. */
7737dd7cddfSDavid du Colombier 	    switch (pgs->level - penum->level) {
7747dd7cddfSDavid du Colombier 		default:
7757dd7cddfSDavid du Colombier 		    return_error(gs_error_invalidfont);		/* WRONG */
7767dd7cddfSDavid du Colombier 		case 2:
7777dd7cddfSDavid du Colombier 		    code = gs_grestore(pgs);
7787dd7cddfSDavid du Colombier 		    if (code < 0)
7797dd7cddfSDavid du Colombier 			return code;
7807dd7cddfSDavid du Colombier 		case 1:
7817dd7cddfSDavid du Colombier 		    ;
7827dd7cddfSDavid du Colombier 	    }
783*593dc095SDavid du Colombier 	    {   cached_fm_pair *pair;
784*593dc095SDavid du Colombier 
785*593dc095SDavid du Colombier 		code = gx_lookup_fm_pair(pgs->font, &char_tm_only(pgs),
786*593dc095SDavid du Colombier 			    &penum->log2_scale, penum->charpath_flag != cpm_show, &pair);
787*593dc095SDavid du Colombier 		if (code < 0)
788*593dc095SDavid du Colombier 		    return code;
7897dd7cddfSDavid du Colombier 		gx_add_cached_char(pgs->font->dir, penum->dev_cache,
790*593dc095SDavid du Colombier 			       cc, pair, &penum->log2_scale);
791*593dc095SDavid du Colombier 	    }
7927dd7cddfSDavid du Colombier 	    if (!SHOW_USES_OUTLINE(penum) ||
7937dd7cddfSDavid du Colombier 		penum->charpath_flag != cpm_show
7947dd7cddfSDavid du Colombier 		)
7957dd7cddfSDavid du Colombier 		break;
7967dd7cddfSDavid du Colombier 	    /* falls through */
7977dd7cddfSDavid du Colombier 	case sws_cache_width_only:
7987dd7cddfSDavid du Colombier 	    /* Copy the bits to the real output device. */
7997dd7cddfSDavid du Colombier 	    code = gs_grestore(pgs);
8007dd7cddfSDavid du Colombier 	    if (code < 0)
8017dd7cddfSDavid du Colombier 		return code;
8027dd7cddfSDavid du Colombier 	    code = gs_state_color_load(pgs);
8037dd7cddfSDavid du Colombier 	    if (code < 0)
8047dd7cddfSDavid du Colombier 		return code;
8057dd7cddfSDavid du Colombier 	    return gx_image_cached_char(penum, cc);
8067dd7cddfSDavid du Colombier 	case sws_no_cache:
8077dd7cddfSDavid du Colombier 	    ;
8087dd7cddfSDavid du Colombier     }
8097dd7cddfSDavid du Colombier     if (penum->charpath_flag != cpm_show) {
8107dd7cddfSDavid du Colombier 	/* Move back to the character origin, so that */
8117dd7cddfSDavid du Colombier 	/* show_move will get us to the right place. */
8127dd7cddfSDavid du Colombier 	code = gx_path_add_point(pgs->show_gstate->path,
8137dd7cddfSDavid du Colombier 				 penum->origin.x, penum->origin.y);
8147dd7cddfSDavid du Colombier 	if (code < 0)
8157dd7cddfSDavid du Colombier 	    return code;
8167dd7cddfSDavid du Colombier     }
8177dd7cddfSDavid du Colombier     return gs_grestore(pgs);
8187dd7cddfSDavid du Colombier }
8197dd7cddfSDavid du Colombier 
8207dd7cddfSDavid du Colombier /* Move to next character */
8217dd7cddfSDavid du Colombier private int
show_fast_move(gs_state * pgs,gs_fixed_point * pwxy)8227dd7cddfSDavid du Colombier show_fast_move(gs_state * pgs, gs_fixed_point * pwxy)
8237dd7cddfSDavid du Colombier {
824*593dc095SDavid du Colombier     return gs_moveto_aux((gs_imager_state *)pgs, pgs->path,
825*593dc095SDavid du Colombier 			      pgs->current_point.x + fixed2float(pwxy->x),
826*593dc095SDavid du Colombier 			      pgs->current_point.y + fixed2float(pwxy->y));
8277dd7cddfSDavid du Colombier }
828*593dc095SDavid du Colombier 
829*593dc095SDavid du Colombier /* Get the current character code. */
gx_current_char(const gs_text_enum_t * pte)830*593dc095SDavid du Colombier int gx_current_char(const gs_text_enum_t * pte)
8317dd7cddfSDavid du Colombier {
832*593dc095SDavid du Colombier     const gs_show_enum *penum = (const gs_show_enum *)pte;
8337dd7cddfSDavid du Colombier     gs_char chr = CURRENT_CHAR(penum) & 0xff;
8347dd7cddfSDavid du Colombier     int fdepth = penum->fstack.depth;
8357dd7cddfSDavid du Colombier 
8367dd7cddfSDavid du Colombier     if (fdepth > 0) {
8377dd7cddfSDavid du Colombier 	/* Add in the shifted font number. */
8387dd7cddfSDavid du Colombier 	uint fidx = penum->fstack.items[fdepth].index;
8397dd7cddfSDavid du Colombier 
8407dd7cddfSDavid du Colombier 	switch (((gs_font_type0 *) (penum->fstack.items[fdepth - 1].font))->data.FMapType) {
8417dd7cddfSDavid du Colombier 	case fmap_1_7:
8427dd7cddfSDavid du Colombier 	case fmap_9_7:
8437dd7cddfSDavid du Colombier 	    chr += fidx << 7;
8447dd7cddfSDavid du Colombier 	    break;
8457dd7cddfSDavid du Colombier 	case fmap_CMap:
8467dd7cddfSDavid du Colombier 	    chr = CURRENT_CHAR(penum);  /* the full character */
8477dd7cddfSDavid du Colombier 	    if (!penum->cmap_code)
8487dd7cddfSDavid du Colombier 		break;
8497dd7cddfSDavid du Colombier 	    /* falls through */
8507dd7cddfSDavid du Colombier 	default:
8517dd7cddfSDavid du Colombier 	    chr += fidx << 8;
8527dd7cddfSDavid du Colombier 	}
8537dd7cddfSDavid du Colombier     }
854*593dc095SDavid du Colombier     return chr;
855*593dc095SDavid du Colombier }
856*593dc095SDavid du Colombier 
857*593dc095SDavid du Colombier private int
show_move(gs_show_enum * penum)858*593dc095SDavid du Colombier show_move(gs_show_enum * penum)
859*593dc095SDavid du Colombier {
860*593dc095SDavid du Colombier     gs_state *pgs = penum->pgs;
861*593dc095SDavid du Colombier 
862*593dc095SDavid du Colombier     if (SHOW_IS(penum, TEXT_REPLACE_WIDTHS)) {
863*593dc095SDavid du Colombier 	gs_point dpt;
864*593dc095SDavid du Colombier 
865*593dc095SDavid du Colombier 	gs_text_replaced_width(&penum->text, penum->xy_index - 1, &dpt);
866*593dc095SDavid du Colombier 	gs_distance_transform2fixed(&pgs->ctm, dpt.x, dpt.y, &penum->wxy);
867*593dc095SDavid du Colombier     } else {
868*593dc095SDavid du Colombier 	double dx = 0, dy = 0;
869*593dc095SDavid du Colombier 
870*593dc095SDavid du Colombier 	if (SHOW_IS_ADD_TO_SPACE(penum)) {
871*593dc095SDavid du Colombier 	    gs_char chr = gx_current_char((const gs_text_enum_t *)penum);
872*593dc095SDavid du Colombier 
8737dd7cddfSDavid du Colombier 	    if (chr == penum->text.space.s_char) {
8747dd7cddfSDavid du Colombier 		dx = penum->text.delta_space.x;
8757dd7cddfSDavid du Colombier 		dy = penum->text.delta_space.y;
8767dd7cddfSDavid du Colombier 	    }
8777dd7cddfSDavid du Colombier 	}
8787dd7cddfSDavid du Colombier 	if (SHOW_IS_ADD_TO_ALL(penum)) {
8797dd7cddfSDavid du Colombier 	    dx += penum->text.delta_all.x;
8807dd7cddfSDavid du Colombier 	    dy += penum->text.delta_all.y;
8817dd7cddfSDavid du Colombier 	}
8827dd7cddfSDavid du Colombier 	if (!is_fzero2(dx, dy)) {
8837dd7cddfSDavid du Colombier 	    gs_fixed_point dxy;
8847dd7cddfSDavid du Colombier 
8857dd7cddfSDavid du Colombier 	    gs_distance_transform2fixed(&pgs->ctm, dx, dy, &dxy);
8867dd7cddfSDavid du Colombier 	    penum->wxy.x += dxy.x;
8877dd7cddfSDavid du Colombier 	    penum->wxy.y += dxy.y;
8887dd7cddfSDavid du Colombier 	}
8897dd7cddfSDavid du Colombier     }
8907dd7cddfSDavid du Colombier     if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) {
8917dd7cddfSDavid du Colombier 	/* HACK for cshow */
8927dd7cddfSDavid du Colombier 	penum->continue_proc = continue_kshow;
8937dd7cddfSDavid du Colombier 	return TEXT_PROCESS_INTERVENE;
8947dd7cddfSDavid du Colombier     }
8957dd7cddfSDavid du Colombier     /* wxy is in device coordinates */
8967dd7cddfSDavid du Colombier     {
8977dd7cddfSDavid du Colombier 	int code = show_fast_move(pgs, &penum->wxy);
8987dd7cddfSDavid du Colombier 
8997dd7cddfSDavid du Colombier 	if (code < 0)
9007dd7cddfSDavid du Colombier 	    return code;
9017dd7cddfSDavid du Colombier     }
9027dd7cddfSDavid du Colombier     /* Check for kerning, but not on the last character. */
9037dd7cddfSDavid du Colombier     if (SHOW_IS_DO_KERN(penum) && penum->index < penum->text.size) {
9047dd7cddfSDavid du Colombier 	penum->continue_proc = continue_kshow;
9057dd7cddfSDavid du Colombier 	return TEXT_PROCESS_INTERVENE;
9067dd7cddfSDavid du Colombier     }
9077dd7cddfSDavid du Colombier     return 0;
9087dd7cddfSDavid du Colombier }
9097dd7cddfSDavid du Colombier /* Process next character */
9107dd7cddfSDavid du Colombier private int
show_proceed(gs_show_enum * penum)9117dd7cddfSDavid du Colombier show_proceed(gs_show_enum * penum)
9127dd7cddfSDavid du Colombier {
9137dd7cddfSDavid du Colombier     gs_state *pgs = penum->pgs;
9147dd7cddfSDavid du Colombier     gs_font *pfont;
9157dd7cddfSDavid du Colombier     cached_fm_pair *pair = 0;
9167dd7cddfSDavid du Colombier     gs_font *rfont =
9177dd7cddfSDavid du Colombier 	(penum->fstack.depth < 0 ? pgs->font : penum->fstack.items[0].font);
9187dd7cddfSDavid du Colombier     int wmode = rfont->WMode;
9197dd7cddfSDavid du Colombier     font_proc_next_char_glyph((*next_char_glyph)) =
9207dd7cddfSDavid du Colombier 	rfont->procs.next_char_glyph;
9217dd7cddfSDavid du Colombier #define get_next_char_glyph(pte, pchr, pglyph)\
9227dd7cddfSDavid du Colombier   (++(penum->xy_index), next_char_glyph(pte, pchr, pglyph))
9237dd7cddfSDavid du Colombier     gs_char chr;
9247dd7cddfSDavid du Colombier     gs_glyph glyph;
9257dd7cddfSDavid du Colombier     int code;
9267dd7cddfSDavid du Colombier     cached_char *cc;
927*593dc095SDavid du Colombier     gs_log2_scale_point log2_scale;
9287dd7cddfSDavid du Colombier 
9297dd7cddfSDavid du Colombier     if (penum->charpath_flag == cpm_show && SHOW_USES_OUTLINE(penum)) {
9307dd7cddfSDavid du Colombier 	code = gs_state_color_load(pgs);
9317dd7cddfSDavid du Colombier 	if (code < 0)
9327dd7cddfSDavid du Colombier 	    return code;
9337dd7cddfSDavid du Colombier     }
9347dd7cddfSDavid du Colombier   more:			/* Proceed to next character */
9357dd7cddfSDavid du Colombier     pfont = (penum->fstack.depth < 0 ? pgs->font :
9367dd7cddfSDavid du Colombier 	     penum->fstack.items[penum->fstack.depth].font);
9377dd7cddfSDavid du Colombier     penum->current_font = pfont;
9387dd7cddfSDavid du Colombier     /* can_cache >= 0 allows us to use cached characters, */
9397dd7cddfSDavid du Colombier     /* even if we can't make new cache entries. */
9407dd7cddfSDavid du Colombier     if (penum->can_cache >= 0) {
9417dd7cddfSDavid du Colombier 	/* Loop with cache */
9427dd7cddfSDavid du Colombier 	for (;;) {
9437dd7cddfSDavid du Colombier 	    switch ((code = get_next_char_glyph((gs_text_enum_t *)penum,
9447dd7cddfSDavid du Colombier 						&chr, &glyph))
9457dd7cddfSDavid du Colombier 		    ) {
9467dd7cddfSDavid du Colombier 		default:	/* error */
9477dd7cddfSDavid du Colombier 		    return code;
9487dd7cddfSDavid du Colombier 		case 2:	/* done */
9497dd7cddfSDavid du Colombier 		    return show_finish(penum);
9507dd7cddfSDavid du Colombier 		case 1:	/* font change */
9517dd7cddfSDavid du Colombier 		    pfont = penum->fstack.items[penum->fstack.depth].font;
9527dd7cddfSDavid du Colombier 		    penum->current_font = pfont;
9537dd7cddfSDavid du Colombier 		    pgs->char_tm_valid = false;
9547dd7cddfSDavid du Colombier 		    show_state_setup(penum);
9557dd7cddfSDavid du Colombier 		    pair = 0;
956*593dc095SDavid du Colombier 		    penum->pair = 0;
9577dd7cddfSDavid du Colombier 		    /* falls through */
9587dd7cddfSDavid du Colombier 		case 0:	/* plain char */
9597dd7cddfSDavid du Colombier 		    /*
9607dd7cddfSDavid du Colombier 		     * We don't need to set penum->current_char in the
9617dd7cddfSDavid du Colombier 		     * normal cases, but it's needed for widthshow,
9627dd7cddfSDavid du Colombier 		     * kshow, and one strange client, so we may as well
9637dd7cddfSDavid du Colombier 		     * do it here.
9647dd7cddfSDavid du Colombier 		     */
9657dd7cddfSDavid du Colombier 		    SET_CURRENT_CHAR(penum, chr);
966*593dc095SDavid du Colombier 		    /*
967*593dc095SDavid du Colombier 		     * Store glyph now, because pdfwrite needs it while
968*593dc095SDavid du Colombier 		     * synthezising bitmap fonts (see assign_char_code).
969*593dc095SDavid du Colombier 		     */
9707dd7cddfSDavid du Colombier 		    if (glyph == gs_no_glyph) {
9717dd7cddfSDavid du Colombier 			glyph = (*penum->encode_char)(pfont, chr,
9727dd7cddfSDavid du Colombier 						      GLYPH_SPACE_NAME);
973*593dc095SDavid du Colombier 			SET_CURRENT_GLYPH(penum, glyph);
974*593dc095SDavid du Colombier 		    } else
975*593dc095SDavid du Colombier     			SET_CURRENT_GLYPH(penum, glyph);
976*593dc095SDavid du Colombier 		    penum->is_pure_color = gs_color_writes_pure(penum->pgs); /* Save
977*593dc095SDavid du Colombier 		                 this data for compute_glyph_raster_params to work
978*593dc095SDavid du Colombier 				 independently on the color change in BuildChar.
979*593dc095SDavid du Colombier 				 Doing it here because cshow proc may modify
980*593dc095SDavid du Colombier 				 the graphic state.
981*593dc095SDavid du Colombier 				 */
982*593dc095SDavid du Colombier 		    {
983*593dc095SDavid du Colombier 			int alpha_bits, depth;
984*593dc095SDavid du Colombier 			gs_fixed_point subpix_origin;
985*593dc095SDavid du Colombier 
986*593dc095SDavid du Colombier 			code = compute_glyph_raster_params(penum, false,
987*593dc095SDavid du Colombier 				    &alpha_bits, &depth, &subpix_origin, &log2_scale);
988*593dc095SDavid du Colombier 			if (code < 0)
989*593dc095SDavid du Colombier 			    return code;
990*593dc095SDavid du Colombier 			if (pair == 0) {
991*593dc095SDavid du Colombier 			    code = gx_lookup_fm_pair(pfont, &char_tm_only(pgs), &log2_scale,
992*593dc095SDavid du Colombier 				penum->charpath_flag != cpm_show, &pair);
993*593dc095SDavid du Colombier 			    if (code < 0)
994*593dc095SDavid du Colombier 				return code;
995*593dc095SDavid du Colombier 			}
996*593dc095SDavid du Colombier 			penum->pair = pair;
9977dd7cddfSDavid du Colombier 			if (glyph == gs_no_glyph) {
9987dd7cddfSDavid du Colombier 			    cc = 0;
9997dd7cddfSDavid du Colombier 			    goto no_cache;
10007dd7cddfSDavid du Colombier 			}
10017dd7cddfSDavid du Colombier 			cc = gx_lookup_cached_char(pfont, pair, glyph, wmode,
1002*593dc095SDavid du Colombier 						   depth, &subpix_origin);
1003*593dc095SDavid du Colombier 		    }
10047dd7cddfSDavid du Colombier 		    if (cc == 0) {
10057dd7cddfSDavid du Colombier 			/* Character is not in cache. */
10067dd7cddfSDavid du Colombier 			/* If possible, try for an xfont before */
10077dd7cddfSDavid du Colombier 			/* rendering from the outline. */
1008*593dc095SDavid du Colombier 
1009*593dc095SDavid du Colombier 		        /* If antialiasing is in effect, don't use xfont */
1010*593dc095SDavid du Colombier 		        if (log2_scale.x + log2_scale.y > 0)
1011*593dc095SDavid du Colombier 			    goto no_cache;
10127dd7cddfSDavid du Colombier 			if (pfont->ExactSize == fbit_use_outlines ||
10137dd7cddfSDavid du Colombier 			    pfont->PaintType == 2
10147dd7cddfSDavid du Colombier 			    )
10157dd7cddfSDavid du Colombier 			    goto no_cache;
10167dd7cddfSDavid du Colombier 			if (pfont->BitmapWidths) {
10177dd7cddfSDavid du Colombier 			    cc = gx_lookup_xfont_char(pgs, pair, chr,
1018*593dc095SDavid du Colombier 				     glyph, wmode);
10197dd7cddfSDavid du Colombier 			    if (cc == 0)
10207dd7cddfSDavid du Colombier 				goto no_cache;
10217dd7cddfSDavid du Colombier 			} else {
10227dd7cddfSDavid du Colombier 			    if (!SHOW_USES_OUTLINE(penum) ||
10237dd7cddfSDavid du Colombier 				(penum->charpath_flag != cpm_show &&
10247dd7cddfSDavid du Colombier 				 penum->charpath_flag != cpm_charwidth)
10257dd7cddfSDavid du Colombier 				)
10267dd7cddfSDavid du Colombier 				goto no_cache;
10277dd7cddfSDavid du Colombier 			    /* We might have an xfont, but we still */
10287dd7cddfSDavid du Colombier 			    /* want the scalable widths. */
10297dd7cddfSDavid du Colombier 			    cc = gx_lookup_xfont_char(pgs, pair, chr,
1030*593dc095SDavid du Colombier 				     glyph, wmode);
10317dd7cddfSDavid du Colombier 			    /* Render up to the point of */
10327dd7cddfSDavid du Colombier 			    /* setcharwidth or setcachedevice, */
10337dd7cddfSDavid du Colombier 			    /* just as for stringwidth. */
10347dd7cddfSDavid du Colombier 			    /* This is the only case in which we can */
10357dd7cddfSDavid du Colombier 			    /* to go no_cache with cc != 0. */
10367dd7cddfSDavid du Colombier 			    goto no_cache;
10377dd7cddfSDavid du Colombier 			}
10387dd7cddfSDavid du Colombier 		    }
10397dd7cddfSDavid du Colombier 		    /* Character is in cache. */
10407dd7cddfSDavid du Colombier 		    /* We might be doing .charboxpath or stringwidth; */
10417dd7cddfSDavid du Colombier 		    /* check for these now. */
10427dd7cddfSDavid du Colombier 		    if (penum->charpath_flag == cpm_charwidth) {
10437dd7cddfSDavid du Colombier 			/* This is charwidth.  Just move by the width. */
10447dd7cddfSDavid du Colombier 			DO_NOTHING;
10457dd7cddfSDavid du Colombier 		    } else if (penum->charpath_flag != cpm_show) {
10467dd7cddfSDavid du Colombier 			/* This is .charboxpath. Get the bounding box */
10477dd7cddfSDavid du Colombier 			/* and append it to a path. */
10487dd7cddfSDavid du Colombier 			gx_path box_path;
10497dd7cddfSDavid du Colombier 			gs_fixed_point pt;
10507dd7cddfSDavid du Colombier 			fixed llx, lly, urx, ury;
10517dd7cddfSDavid du Colombier 
10527dd7cddfSDavid du Colombier 			code = gx_path_current_point(pgs->path, &pt);
10537dd7cddfSDavid du Colombier 			if (code < 0)
10547dd7cddfSDavid du Colombier 			    return code;
10557dd7cddfSDavid du Colombier 			llx = fixed_rounded(pt.x - cc->offset.x) +
10567dd7cddfSDavid du Colombier 			    int2fixed(penum->ftx);
10577dd7cddfSDavid du Colombier 			lly = fixed_rounded(pt.y - cc->offset.y) +
10587dd7cddfSDavid du Colombier 			    int2fixed(penum->fty);
10597dd7cddfSDavid du Colombier 			urx = llx + int2fixed(cc->width),
10607dd7cddfSDavid du Colombier 			    ury = lly + int2fixed(cc->height);
10617dd7cddfSDavid du Colombier 			gx_path_init_local(&box_path, pgs->memory);
10627dd7cddfSDavid du Colombier 			code =
10637dd7cddfSDavid du Colombier 			    gx_path_add_rectangle(&box_path, llx, lly,
10647dd7cddfSDavid du Colombier 						  urx, ury);
10657dd7cddfSDavid du Colombier 			if (code >= 0)
10667dd7cddfSDavid du Colombier 			    code =
10677dd7cddfSDavid du Colombier 				gx_path_add_char_path(pgs->show_gstate->path,
10687dd7cddfSDavid du Colombier 						      &box_path,
10697dd7cddfSDavid du Colombier 						      penum->charpath_flag);
10707dd7cddfSDavid du Colombier 			if (code >= 0)
10717dd7cddfSDavid du Colombier 			    code = gx_path_add_point(pgs->path, pt.x, pt.y);
10727dd7cddfSDavid du Colombier 			gx_path_free(&box_path, "show_proceed(box path)");
10737dd7cddfSDavid du Colombier 			if (code < 0)
10747dd7cddfSDavid du Colombier 			    return code;
10757dd7cddfSDavid du Colombier 		    } else if (SHOW_IS_DRAWING(penum)) {
10767dd7cddfSDavid du Colombier 			code = gx_image_cached_char(penum, cc);
10777dd7cddfSDavid du Colombier 			if (code < 0)
10787dd7cddfSDavid du Colombier 			    return code;
10797dd7cddfSDavid du Colombier 			else if (code > 0) {
10807dd7cddfSDavid du Colombier 			    cc = 0;
10817dd7cddfSDavid du Colombier 			    goto no_cache;
10827dd7cddfSDavid du Colombier 			}
10837dd7cddfSDavid du Colombier 		    }
10847dd7cddfSDavid du Colombier 		    if (SHOW_IS_SLOW(penum)) {
10857dd7cddfSDavid du Colombier 			/* Split up the assignment so that the */
10867dd7cddfSDavid du Colombier 			/* Watcom compiler won't reserve esi/edi. */
10877dd7cddfSDavid du Colombier 			penum->wxy.x = cc->wxy.x;
10887dd7cddfSDavid du Colombier 			penum->wxy.y = cc->wxy.y;
10897dd7cddfSDavid du Colombier 			code = show_move(penum);
10907dd7cddfSDavid du Colombier 		    } else
10917dd7cddfSDavid du Colombier 			code = show_fast_move(pgs, &cc->wxy);
10927dd7cddfSDavid du Colombier 		    if (code) {
1093*593dc095SDavid du Colombier 			/* Might be kshow, glyph is stored above. */
10947dd7cddfSDavid du Colombier 			return code;
10957dd7cddfSDavid du Colombier 		    }
10967dd7cddfSDavid du Colombier 	    }
10977dd7cddfSDavid du Colombier 	}
10987dd7cddfSDavid du Colombier     } else {
10997dd7cddfSDavid du Colombier 	/* Can't use cache */
11007dd7cddfSDavid du Colombier 	switch ((code = get_next_char_glyph((gs_text_enum_t *)penum,
11017dd7cddfSDavid du Colombier 					    &chr, &glyph))
11027dd7cddfSDavid du Colombier 		) {
11037dd7cddfSDavid du Colombier 	    default:
11047dd7cddfSDavid du Colombier 		return code;
11057dd7cddfSDavid du Colombier 	    case 2:
11067dd7cddfSDavid du Colombier 		return show_finish(penum);
11077dd7cddfSDavid du Colombier 	    case 1:
11087dd7cddfSDavid du Colombier 		pfont = penum->fstack.items[penum->fstack.depth].font;
11097dd7cddfSDavid du Colombier 		penum->current_font = pfont;
11107dd7cddfSDavid du Colombier 		show_state_setup(penum);
1111*593dc095SDavid du Colombier 		pair = 0;
11127dd7cddfSDavid du Colombier 	    case 0:
1113*593dc095SDavid du Colombier 		{   int alpha_bits, depth;
1114*593dc095SDavid du Colombier 		    gs_log2_scale_point log2_scale;
1115*593dc095SDavid du Colombier 		    gs_fixed_point subpix_origin;
1116*593dc095SDavid du Colombier 
1117*593dc095SDavid du Colombier 		    code = compute_glyph_raster_params(penum, false, &alpha_bits, &depth, &subpix_origin, &log2_scale);
1118*593dc095SDavid du Colombier 		    if (code < 0)
1119*593dc095SDavid du Colombier 			return code;
1120*593dc095SDavid du Colombier 		    if (pair == 0) {
1121*593dc095SDavid du Colombier 			code = gx_lookup_fm_pair(pfont, &char_tm_only(pgs), &log2_scale,
1122*593dc095SDavid du Colombier 				penum->charpath_flag != cpm_show, &pair);
1123*593dc095SDavid du Colombier 			if (code < 0)
1124*593dc095SDavid du Colombier 			    return code;
1125*593dc095SDavid du Colombier 		    }
1126*593dc095SDavid du Colombier 		    penum->pair = pair;
1127*593dc095SDavid du Colombier 		}
11287dd7cddfSDavid du Colombier 	}
11297dd7cddfSDavid du Colombier 	SET_CURRENT_CHAR(penum, chr);
11307dd7cddfSDavid du Colombier 	if (glyph == gs_no_glyph) {
11317dd7cddfSDavid du Colombier 	    glyph = (*penum->encode_char)(pfont, chr, GLYPH_SPACE_NAME);
11327dd7cddfSDavid du Colombier 	}
1133*593dc095SDavid du Colombier         SET_CURRENT_GLYPH(penum, glyph);
11347dd7cddfSDavid du Colombier 	cc = 0;
11357dd7cddfSDavid du Colombier     }
11367dd7cddfSDavid du Colombier   no_cache:
11377dd7cddfSDavid du Colombier     /*
11387dd7cddfSDavid du Colombier      * We must call the client's rendering code.  Normally,
11397dd7cddfSDavid du Colombier      * we only do this if the character is not cached (cc = 0);
11407dd7cddfSDavid du Colombier      * however, we also must do this if we have an xfont but
11417dd7cddfSDavid du Colombier      * are using scalable widths.  In this case, and only this case,
1142*593dc095SDavid du Colombier      * we get here with cc != 0.  penum->current_char and penum->current_glyph
1143*593dc095SDavid du Colombier      * has already been set.
11447dd7cddfSDavid du Colombier      */
11457dd7cddfSDavid du Colombier     if ((code = gs_gsave(pgs)) < 0)
11467dd7cddfSDavid du Colombier 	return code;
11477dd7cddfSDavid du Colombier     /* Set the font to the current descendant font. */
11487dd7cddfSDavid du Colombier     pgs->font = pfont;
11497dd7cddfSDavid du Colombier     /* Reset the in_cachedevice flag, so that a recursive show */
11507dd7cddfSDavid du Colombier     /* will use the cache properly. */
11517dd7cddfSDavid du Colombier     pgs->in_cachedevice = CACHE_DEVICE_NONE;
11527dd7cddfSDavid du Colombier     /* Set the charpath data in the graphics context if necessary, */
11537dd7cddfSDavid du Colombier     /* so that fill and stroke will add to the path */
11547dd7cddfSDavid du Colombier     /* rather than having their usual effect. */
11557dd7cddfSDavid du Colombier     pgs->in_charpath = penum->charpath_flag;
11567dd7cddfSDavid du Colombier     pgs->show_gstate =
11577dd7cddfSDavid du Colombier 	(penum->show_gstate == pgs ? pgs->saved : penum->show_gstate);
11587dd7cddfSDavid du Colombier     pgs->stroke_adjust = false;	/* per specification */
11597dd7cddfSDavid du Colombier     {
11607dd7cddfSDavid du Colombier 	gs_fixed_point cpt;
11617dd7cddfSDavid du Colombier 	gx_path *ppath = pgs->path;
11627dd7cddfSDavid du Colombier 
11637dd7cddfSDavid du Colombier 	if ((code = gx_path_current_point_inline(ppath, &cpt)) < 0) {
11647dd7cddfSDavid du Colombier 	    /* For cshow, having no current point is acceptable. */
11657dd7cddfSDavid du Colombier 	    if (!SHOW_IS(penum, TEXT_DO_NONE))
11667dd7cddfSDavid du Colombier 		goto rret;
11677dd7cddfSDavid du Colombier 	    cpt.x = cpt.y = 0;	/* arbitrary */
11687dd7cddfSDavid du Colombier 	}
11697dd7cddfSDavid du Colombier 	penum->origin.x = cpt.x;
11707dd7cddfSDavid du Colombier 	penum->origin.y = cpt.y;
11717dd7cddfSDavid du Colombier 	/* Normally, char_tm is valid because of show_state_setup, */
11727dd7cddfSDavid du Colombier 	/* but if we're in a cshow, it may not be. */
11737dd7cddfSDavid du Colombier 	gs_currentcharmatrix(pgs, NULL, true);
11747dd7cddfSDavid du Colombier #if 1				/*USE_FPU <= 0 */
11757dd7cddfSDavid du Colombier 	if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) {
11767dd7cddfSDavid du Colombier 	    fixed tx = pgs->ctm.tx_fixed;
11777dd7cddfSDavid du Colombier 	    fixed ty = pgs->ctm.ty_fixed;
11787dd7cddfSDavid du Colombier 
11797dd7cddfSDavid du Colombier 	    gs_settocharmatrix(pgs);
11807dd7cddfSDavid du Colombier 	    cpt.x += pgs->ctm.tx_fixed - tx;
11817dd7cddfSDavid du Colombier 	    cpt.y += pgs->ctm.ty_fixed - ty;
11827dd7cddfSDavid du Colombier 	} else
11837dd7cddfSDavid du Colombier #endif
11847dd7cddfSDavid du Colombier 	{
11857dd7cddfSDavid du Colombier 	    double tx = pgs->ctm.tx;
11867dd7cddfSDavid du Colombier 	    double ty = pgs->ctm.ty;
11877dd7cddfSDavid du Colombier 	    double fpx, fpy;
11887dd7cddfSDavid du Colombier 
11897dd7cddfSDavid du Colombier 	    gs_settocharmatrix(pgs);
11907dd7cddfSDavid du Colombier 	    fpx = fixed2float(cpt.x) + (pgs->ctm.tx - tx);
11917dd7cddfSDavid du Colombier 	    fpy = fixed2float(cpt.y) + (pgs->ctm.ty - ty);
11927dd7cddfSDavid du Colombier #define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
11937dd7cddfSDavid du Colombier 	    if (!(f_fits_in_fixed(fpx) && f_fits_in_fixed(fpy))) {
11947dd7cddfSDavid du Colombier 		gs_note_error(code = gs_error_limitcheck);
11957dd7cddfSDavid du Colombier 		goto rret;
11967dd7cddfSDavid du Colombier 	    }
11977dd7cddfSDavid du Colombier 	    cpt.x = float2fixed(fpx);
11987dd7cddfSDavid du Colombier 	    cpt.y = float2fixed(fpy);
11997dd7cddfSDavid du Colombier 	}
12007dd7cddfSDavid du Colombier 	gs_newpath(pgs);
1201*593dc095SDavid du Colombier 	code = show_origin_setup(pgs, cpt.x, cpt.y, penum);
12027dd7cddfSDavid du Colombier 	if (code < 0)
12037dd7cddfSDavid du Colombier 	    goto rret;
12047dd7cddfSDavid du Colombier     }
12057dd7cddfSDavid du Colombier     penum->width_status = sws_none;
12067dd7cddfSDavid du Colombier     penum->continue_proc = continue_show_update;
1207*593dc095SDavid du Colombier     /* Reset the sampling scale. */
1208*593dc095SDavid du Colombier     penum->log2_scale.x = penum->log2_scale.y = 0;
12097dd7cddfSDavid du Colombier     /* Try using the build procedure in the font. */
12107dd7cddfSDavid du Colombier     /* < 0 means error, 0 means success, 1 means failure. */
12117dd7cddfSDavid du Colombier     penum->cc = cc;		/* set this now for build procedure */
12127dd7cddfSDavid du Colombier     code = (*pfont->procs.build_char)((gs_text_enum_t *)penum, pgs, pfont,
12137dd7cddfSDavid du Colombier 				      chr, glyph);
12147dd7cddfSDavid du Colombier     if (code < 0) {
12157dd7cddfSDavid du Colombier 	discard(gs_note_error(code));
12167dd7cddfSDavid du Colombier 	goto rret;
12177dd7cddfSDavid du Colombier     }
12187dd7cddfSDavid du Colombier     if (code == 0) {
12197dd7cddfSDavid du Colombier 	code = show_update(penum);
12207dd7cddfSDavid du Colombier 	if (code < 0)
12217dd7cddfSDavid du Colombier 	    goto rret;
12227dd7cddfSDavid du Colombier 	/* Note that show_update does a grestore.... */
12237dd7cddfSDavid du Colombier 	code = show_move(penum);
12247dd7cddfSDavid du Colombier 	if (code)
12257dd7cddfSDavid du Colombier 	    return code;	/* ... so don't go to rret here. */
12267dd7cddfSDavid du Colombier 	goto more;
12277dd7cddfSDavid du Colombier     }
12287dd7cddfSDavid du Colombier     /*
12297dd7cddfSDavid du Colombier      * Some BuildChar procedures do a save before the setcachedevice,
12307dd7cddfSDavid du Colombier      * and a restore at the end.  If we waited to allocate the cache
12317dd7cddfSDavid du Colombier      * device until the setcachedevice, we would attempt to free it
12327dd7cddfSDavid du Colombier      * after the restore.  Therefore, allocate it now.
12337dd7cddfSDavid du Colombier      */
12347dd7cddfSDavid du Colombier     if (penum->dev_cache == 0) {
12357dd7cddfSDavid du Colombier 	code = show_cache_setup(penum);
12367dd7cddfSDavid du Colombier 	if (code < 0)
12377dd7cddfSDavid du Colombier 	    goto rret;
12387dd7cddfSDavid du Colombier     }
12397dd7cddfSDavid du Colombier     return TEXT_PROCESS_RENDER;
12407dd7cddfSDavid du Colombier     /* If we get an error while setting up for BuildChar, */
12417dd7cddfSDavid du Colombier     /* we must undo the partial setup. */
12427dd7cddfSDavid du Colombier   rret:gs_grestore(pgs);
12437dd7cddfSDavid du Colombier     return code;
12447dd7cddfSDavid du Colombier #undef get_next_char_glyph
12457dd7cddfSDavid du Colombier }
12467dd7cddfSDavid du Colombier 
12477dd7cddfSDavid du Colombier /*
12487dd7cddfSDavid du Colombier  * Prepare to retry rendering of the current character.  (This is only used
12497dd7cddfSDavid du Colombier  * in one place in zchar1.c; a different approach may be better.)
12507dd7cddfSDavid du Colombier  */
12517dd7cddfSDavid du Colombier private int
gx_show_text_retry(gs_text_enum_t * pte)12527dd7cddfSDavid du Colombier gx_show_text_retry(gs_text_enum_t *pte)
12537dd7cddfSDavid du Colombier {
12547dd7cddfSDavid du Colombier     gs_show_enum *const penum = (gs_show_enum *)pte;
12557dd7cddfSDavid du Colombier 
12567dd7cddfSDavid du Colombier     if (penum->cc) {
12577dd7cddfSDavid du Colombier 	gs_font *pfont = penum->current_font;
12587dd7cddfSDavid du Colombier 
12597dd7cddfSDavid du Colombier 	gx_free_cached_char(pfont->dir, penum->cc);
12607dd7cddfSDavid du Colombier 	penum->cc = 0;
12617dd7cddfSDavid du Colombier     }
12627dd7cddfSDavid du Colombier     gs_grestore(penum->pgs);
1263*593dc095SDavid du Colombier     penum->width_status = sws_retry;
12647dd7cddfSDavid du Colombier     penum->log2_scale.x = penum->log2_scale.y = 0;
1265*593dc095SDavid du Colombier     penum->pair = 0;
12667dd7cddfSDavid du Colombier     return 0;
12677dd7cddfSDavid du Colombier }
12687dd7cddfSDavid du Colombier 
12697dd7cddfSDavid du Colombier /* Finish show or stringwidth */
12707dd7cddfSDavid du Colombier private int
show_finish(gs_show_enum * penum)12717dd7cddfSDavid du Colombier show_finish(gs_show_enum * penum)
12727dd7cddfSDavid du Colombier {
12737dd7cddfSDavid du Colombier     gs_state *pgs = penum->pgs;
12747dd7cddfSDavid du Colombier     int code, rcode;
12757dd7cddfSDavid du Colombier 
12767dd7cddfSDavid du Colombier     if (penum->auto_release)
12777dd7cddfSDavid du Colombier 	penum->procs->release((gs_text_enum_t *)penum, "show_finish");
12787dd7cddfSDavid du Colombier     if (!SHOW_IS_STRINGWIDTH(penum))
12797dd7cddfSDavid du Colombier 	return 0;
12807dd7cddfSDavid du Colombier     /* Save the accumulated width before returning, */
12817dd7cddfSDavid du Colombier     /* and undo the extra gsave. */
12827dd7cddfSDavid du Colombier     code = gs_currentpoint(pgs, &penum->returned.total_width);
12837dd7cddfSDavid du Colombier     rcode = gs_grestore(pgs);
12847dd7cddfSDavid du Colombier     return (code < 0 ? code : rcode);
12857dd7cddfSDavid du Colombier }
12867dd7cddfSDavid du Colombier 
12877dd7cddfSDavid du Colombier /* Release the structure. */
12887dd7cddfSDavid du Colombier private void
gx_show_text_release(gs_text_enum_t * pte,client_name_t cname)12897dd7cddfSDavid du Colombier gx_show_text_release(gs_text_enum_t *pte, client_name_t cname)
12907dd7cddfSDavid du Colombier {
12917dd7cddfSDavid du Colombier     gs_show_enum *const penum = (gs_show_enum *)pte;
12927dd7cddfSDavid du Colombier 
12937dd7cddfSDavid du Colombier     penum->cc = 0;
12947dd7cddfSDavid du Colombier     if (penum->dev_cache2) {
12957dd7cddfSDavid du Colombier 	gx_device_retain((gx_device *)penum->dev_cache2, false);
12967dd7cddfSDavid du Colombier 	penum->dev_cache2 = 0;
12977dd7cddfSDavid du Colombier     }
12987dd7cddfSDavid du Colombier     if (penum->dev_cache) {
12997dd7cddfSDavid du Colombier 	gx_device_retain((gx_device *)penum->dev_cache, false);
13007dd7cddfSDavid du Colombier 	penum->dev_cache = 0;
13017dd7cddfSDavid du Colombier     }
13027dd7cddfSDavid du Colombier     if (penum->dev_null) {
13037dd7cddfSDavid du Colombier 	gx_device_retain((gx_device *)penum->dev_null, false);
13047dd7cddfSDavid du Colombier 	penum->dev_null = 0;
13057dd7cddfSDavid du Colombier     }
13067dd7cddfSDavid du Colombier     gx_default_text_release(pte, cname);
13077dd7cddfSDavid du Colombier }
13087dd7cddfSDavid du Colombier 
13097dd7cddfSDavid du Colombier /* ------ Miscellaneous accessors ------ */
13107dd7cddfSDavid du Colombier 
13117dd7cddfSDavid du Colombier /* Return the charpath mode. */
13127dd7cddfSDavid du Colombier gs_char_path_mode
gs_show_in_charpath(const gs_show_enum * penum)13137dd7cddfSDavid du Colombier gs_show_in_charpath(const gs_show_enum * penum)
13147dd7cddfSDavid du Colombier {
13157dd7cddfSDavid du Colombier     return penum->charpath_flag;
13167dd7cddfSDavid du Colombier }
13177dd7cddfSDavid du Colombier 
13187dd7cddfSDavid du Colombier /* Return true if we only need the width from the rasterizer */
13197dd7cddfSDavid du Colombier /* and can short-circuit the full rendering of the character, */
13207dd7cddfSDavid du Colombier /* false if we need the actual character bits. */
13217dd7cddfSDavid du Colombier /* This is only meaningful just before calling gs_setcharwidth or */
13227dd7cddfSDavid du Colombier /* gs_setcachedevice[2]. */
13237dd7cddfSDavid du Colombier /* Note that we can't do this if the procedure has done any extra [g]saves. */
13247dd7cddfSDavid du Colombier private bool
gx_show_text_is_width_only(const gs_text_enum_t * pte)13257dd7cddfSDavid du Colombier gx_show_text_is_width_only(const gs_text_enum_t *pte)
13267dd7cddfSDavid du Colombier {
13277dd7cddfSDavid du Colombier     const gs_show_enum *const penum = (const gs_show_enum *)pte;
13287dd7cddfSDavid du Colombier 
13297dd7cddfSDavid du Colombier     /* penum->cc will be non-zero iff we are calculating */
13307dd7cddfSDavid du Colombier     /* the scalable width for an xfont character. */
13317dd7cddfSDavid du Colombier     return ((!SHOW_USES_OUTLINE(penum) || penum->cc != 0) &&
13327dd7cddfSDavid du Colombier 	    penum->pgs->level == penum->level + 1);
13337dd7cddfSDavid du Colombier }
13347dd7cddfSDavid du Colombier 
13357dd7cddfSDavid du Colombier /* Return the width of the just-enumerated character (for cshow). */
13367dd7cddfSDavid du Colombier private int
gx_show_text_current_width(const gs_text_enum_t * pte,gs_point * pwidth)13377dd7cddfSDavid du Colombier gx_show_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
13387dd7cddfSDavid du Colombier {
13397dd7cddfSDavid du Colombier     const gs_show_enum *const penum = (const gs_show_enum *)pte;
13407dd7cddfSDavid du Colombier 
13417dd7cddfSDavid du Colombier     return gs_idtransform(penum->pgs,
13427dd7cddfSDavid du Colombier 			  fixed2float(penum->wxy.x),
13437dd7cddfSDavid du Colombier 			  fixed2float(penum->wxy.y), pwidth);
13447dd7cddfSDavid du Colombier }
13457dd7cddfSDavid du Colombier 
13467dd7cddfSDavid du Colombier /* Return the current font for cshow. */
13477dd7cddfSDavid du Colombier gs_font *
gs_show_current_font(const gs_show_enum * penum)13487dd7cddfSDavid du Colombier gs_show_current_font(const gs_show_enum * penum)
13497dd7cddfSDavid du Colombier {
13507dd7cddfSDavid du Colombier     return (penum->fstack.depth < 0 ? penum->pgs->font :
13517dd7cddfSDavid du Colombier 	    penum->fstack.items[penum->fstack.depth].font);
13527dd7cddfSDavid du Colombier }
13537dd7cddfSDavid du Colombier 
13547dd7cddfSDavid du Colombier /* ------ Internal routines ------ */
13557dd7cddfSDavid du Colombier 
1356*593dc095SDavid du Colombier private inline bool
is_matrix_good_for_caching(const gs_matrix_fixed * m)1357*593dc095SDavid du Colombier is_matrix_good_for_caching(const gs_matrix_fixed *m)
1358*593dc095SDavid du Colombier {
1359*593dc095SDavid du Colombier     /* Skewing or non-rectangular rotation are not supported,
1360*593dc095SDavid du Colombier        but we ignore a small noise skew. */
1361*593dc095SDavid du Colombier     const float axx = any_abs(m->xx), axy = any_abs(m->xy);
1362*593dc095SDavid du Colombier     const float ayx = any_abs(m->yx), ayy = any_abs(m->yy);
1363*593dc095SDavid du Colombier     const float thr = 5000; /* examples/alphabet.ps */
1364*593dc095SDavid du Colombier 
1365*593dc095SDavid du Colombier     if (ayx * thr < axx || axy * thr < ayy)
1366*593dc095SDavid du Colombier 	return true;
1367*593dc095SDavid du Colombier     if (axx * thr < ayx || ayy * thr < axy)
1368*593dc095SDavid du Colombier 	return true;
1369*593dc095SDavid du Colombier     return false;
1370*593dc095SDavid du Colombier }
1371*593dc095SDavid du Colombier 
13727dd7cddfSDavid du Colombier /* Initialize the gstate-derived parts of a show enumerator. */
13737dd7cddfSDavid du Colombier /* We do this both when starting the show operation, */
13747dd7cddfSDavid du Colombier /* and when returning from the kshow callout. */
13757dd7cddfSDavid du Colombier /* Uses only penum->pgs, penum->fstack. */
13767dd7cddfSDavid du Colombier private int
show_state_setup(gs_show_enum * penum)13777dd7cddfSDavid du Colombier show_state_setup(gs_show_enum * penum)
13787dd7cddfSDavid du Colombier {
13797dd7cddfSDavid du Colombier     gs_state *pgs = penum->pgs;
13807dd7cddfSDavid du Colombier     gx_clip_path *pcpath;
13817dd7cddfSDavid du Colombier     gs_font *pfont;
13827dd7cddfSDavid du Colombier 
13837dd7cddfSDavid du Colombier     if (penum->fstack.depth <= 0) {
13847dd7cddfSDavid du Colombier 	pfont = pgs->font;
13857dd7cddfSDavid du Colombier 	gs_currentcharmatrix(pgs, NULL, 1);	/* make char_tm valid */
13867dd7cddfSDavid du Colombier     } else {
13877dd7cddfSDavid du Colombier 	/* We have to concatenate the parent's FontMatrix as well. */
13887dd7cddfSDavid du Colombier 	gs_matrix mat;
13897dd7cddfSDavid du Colombier 	const gx_font_stack_item_t *pfsi =
13907dd7cddfSDavid du Colombier 	    &penum->fstack.items[penum->fstack.depth];
13917dd7cddfSDavid du Colombier 
13927dd7cddfSDavid du Colombier 	pfont = pfsi->font;
13937dd7cddfSDavid du Colombier 	gs_matrix_multiply(&pfont->FontMatrix,
13947dd7cddfSDavid du Colombier 			   &pfsi[-1].font->FontMatrix, &mat);
1395*593dc095SDavid du Colombier 	if (pfont->FontType == ft_CID_encrypted) {
1396*593dc095SDavid du Colombier 	    /* concatenate the Type9 leaf's matrix */
1397*593dc095SDavid du Colombier 	    gs_matrix_multiply(&mat,
1398*593dc095SDavid du Colombier 		&(gs_cid0_indexed_font(pfont, pfsi->index)->FontMatrix), &mat);
1399*593dc095SDavid du Colombier 	}
14007dd7cddfSDavid du Colombier 	gs_setcharmatrix(pgs, &mat);
14017dd7cddfSDavid du Colombier     }
14027dd7cddfSDavid du Colombier     penum->current_font = pfont;
14037dd7cddfSDavid du Colombier     /* Skewing or non-rectangular rotation are not supported. */
1404*593dc095SDavid du Colombier     if (!CACHE_ROTATED_CHARS && is_matrix_good_for_caching(&pgs->char_tm))
14057dd7cddfSDavid du Colombier 	penum->can_cache = 0;
14067dd7cddfSDavid du Colombier     if (penum->can_cache >= 0 &&
14077dd7cddfSDavid du Colombier 	gx_effective_clip_path(pgs, &pcpath) >= 0
14087dd7cddfSDavid du Colombier 	) {
14097dd7cddfSDavid du Colombier 	gs_fixed_rect cbox;
14107dd7cddfSDavid du Colombier 
14117dd7cddfSDavid du Colombier 	gx_cpath_inner_box(pcpath, &cbox);
14127dd7cddfSDavid du Colombier 	/* Since characters occupy an integral number of pixels, */
14137dd7cddfSDavid du Colombier 	/* we can (and should) round the inner clipping box */
14147dd7cddfSDavid du Colombier 	/* outward rather than inward. */
14157dd7cddfSDavid du Colombier 	penum->ibox.p.x = fixed2int_var(cbox.p.x);
14167dd7cddfSDavid du Colombier 	penum->ibox.p.y = fixed2int_var(cbox.p.y);
14177dd7cddfSDavid du Colombier 	penum->ibox.q.x = fixed2int_var_ceiling(cbox.q.x);
14187dd7cddfSDavid du Colombier 	penum->ibox.q.y = fixed2int_var_ceiling(cbox.q.y);
14197dd7cddfSDavid du Colombier 	gx_cpath_outer_box(pcpath, &cbox);
14207dd7cddfSDavid du Colombier 	penum->obox.p.x = fixed2int_var(cbox.p.x);
14217dd7cddfSDavid du Colombier 	penum->obox.p.y = fixed2int_var(cbox.p.y);
14227dd7cddfSDavid du Colombier 	penum->obox.q.x = fixed2int_var_ceiling(cbox.q.x);
14237dd7cddfSDavid du Colombier 	penum->obox.q.y = fixed2int_var_ceiling(cbox.q.y);
14247dd7cddfSDavid du Colombier #if 1				/*USE_FPU <= 0 */
14257dd7cddfSDavid du Colombier 	if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) {
14267dd7cddfSDavid du Colombier 	    penum->ftx = (int)fixed2long(pgs->char_tm.tx_fixed -
14277dd7cddfSDavid du Colombier 					 pgs->ctm.tx_fixed);
14287dd7cddfSDavid du Colombier 	    penum->fty = (int)fixed2long(pgs->char_tm.ty_fixed -
14297dd7cddfSDavid du Colombier 					 pgs->ctm.ty_fixed);
14307dd7cddfSDavid du Colombier 	} else {
14317dd7cddfSDavid du Colombier #endif
14327dd7cddfSDavid du Colombier 	    double fdx = pgs->char_tm.tx - pgs->ctm.tx;
14337dd7cddfSDavid du Colombier 	    double fdy = pgs->char_tm.ty - pgs->ctm.ty;
14347dd7cddfSDavid du Colombier 
14357dd7cddfSDavid du Colombier #define int_bits (arch_sizeof_int * 8 - 1)
14367dd7cddfSDavid du Colombier 	    if (!(f_fits_in_bits(fdx, int_bits) &&
14377dd7cddfSDavid du Colombier 		  f_fits_in_bits(fdy, int_bits))
14387dd7cddfSDavid du Colombier 		)
14397dd7cddfSDavid du Colombier 		return_error(gs_error_limitcheck);
14407dd7cddfSDavid du Colombier #undef int_bits
14417dd7cddfSDavid du Colombier 	    penum->ftx = (int)fdx;
14427dd7cddfSDavid du Colombier 	    penum->fty = (int)fdy;
14437dd7cddfSDavid du Colombier 	}
14447dd7cddfSDavid du Colombier     }
14457dd7cddfSDavid du Colombier     show_set_encode_char(penum);
14467dd7cddfSDavid du Colombier     return 0;
14477dd7cddfSDavid du Colombier }
14487dd7cddfSDavid du Colombier 
14497dd7cddfSDavid du Colombier /* Set the suggested oversampling scale for character rendering. */
14507dd7cddfSDavid du Colombier private void
show_set_scale(const gs_show_enum * penum,gs_log2_scale_point * log2_scale)1451*593dc095SDavid du Colombier show_set_scale(const gs_show_enum * penum, gs_log2_scale_point *log2_scale)
14527dd7cddfSDavid du Colombier {
14537dd7cddfSDavid du Colombier     /*
14547dd7cddfSDavid du Colombier      * Decide whether to oversample.
14557dd7cddfSDavid du Colombier      * We have to decide this each time setcachedevice is called.
14567dd7cddfSDavid du Colombier      */
14577dd7cddfSDavid du Colombier     const gs_state *pgs = penum->pgs;
14587dd7cddfSDavid du Colombier 
14597dd7cddfSDavid du Colombier     if ((penum->charpath_flag == cpm_show ||
14607dd7cddfSDavid du Colombier 	 penum->charpath_flag == cpm_charwidth) &&
14617dd7cddfSDavid du Colombier 	SHOW_USES_OUTLINE(penum) &&
1462*593dc095SDavid du Colombier 	/* gx_path_is_void_inline(pgs->path) && */
14637dd7cddfSDavid du Colombier     /* Oversampling rotated characters doesn't work well. */
1464*593dc095SDavid du Colombier 	is_matrix_good_for_caching(&pgs->char_tm)
14657dd7cddfSDavid du Colombier 	) {
1466*593dc095SDavid du Colombier 	const gs_font_base *pfont = (const gs_font_base *)penum->current_font;
14677dd7cddfSDavid du Colombier 	gs_fixed_point extent;
14687dd7cddfSDavid du Colombier 	int code = gs_distance_transform2fixed(&pgs->char_tm,
14697dd7cddfSDavid du Colombier 				  pfont->FontBBox.q.x - pfont->FontBBox.p.x,
14707dd7cddfSDavid du Colombier 				  pfont->FontBBox.q.y - pfont->FontBBox.p.y,
14717dd7cddfSDavid du Colombier 					       &extent);
14727dd7cddfSDavid du Colombier 
14737dd7cddfSDavid du Colombier 	if (code >= 0) {
14747dd7cddfSDavid du Colombier 	    int sx =
1475*593dc095SDavid du Colombier 	    (any_abs(extent.x) < int2fixed(60) ? 2 :
1476*593dc095SDavid du Colombier 	     any_abs(extent.x) < int2fixed(200) ? 1 :
14777dd7cddfSDavid du Colombier 	     0);
14787dd7cddfSDavid du Colombier 	    int sy =
1479*593dc095SDavid du Colombier 	    (any_abs(extent.y) < int2fixed(60) ? 2 :
1480*593dc095SDavid du Colombier 	     any_abs(extent.y) < int2fixed(200) ? 1 :
14817dd7cddfSDavid du Colombier 	     0);
14827dd7cddfSDavid du Colombier 
14837dd7cddfSDavid du Colombier 	    /* If we oversample at all, make sure we do it */
14847dd7cddfSDavid du Colombier 	    /* in both X and Y. */
14857dd7cddfSDavid du Colombier 	    if (sx == 0 && sy != 0)
14867dd7cddfSDavid du Colombier 		sx = 1;
14877dd7cddfSDavid du Colombier 	    else if (sy == 0 && sx != 0)
14887dd7cddfSDavid du Colombier 		sy = 1;
1489*593dc095SDavid du Colombier 	    log2_scale->x = sx;
1490*593dc095SDavid du Colombier 	    log2_scale->y = sy;
14917dd7cddfSDavid du Colombier 	    return;
14927dd7cddfSDavid du Colombier 	}
14937dd7cddfSDavid du Colombier     }
14947dd7cddfSDavid du Colombier     /* By default, don't scale. */
1495*593dc095SDavid du Colombier     log2_scale->x = log2_scale->y = 0;
14967dd7cddfSDavid du Colombier }
14977dd7cddfSDavid du Colombier 
14987dd7cddfSDavid du Colombier /* Set up the cache device and related information. */
14997dd7cddfSDavid du Colombier /* Note that we always allocate both cache devices, */
15007dd7cddfSDavid du Colombier /* even if we only use one of them. */
15017dd7cddfSDavid du Colombier private int
show_cache_setup(gs_show_enum * penum)15027dd7cddfSDavid du Colombier show_cache_setup(gs_show_enum * penum)
15037dd7cddfSDavid du Colombier {
15047dd7cddfSDavid du Colombier     gs_state *pgs = penum->pgs;
1505*593dc095SDavid du Colombier     gs_memory_t *mem = penum->memory;
15067dd7cddfSDavid du Colombier     gx_device_memory *dev =
15077dd7cddfSDavid du Colombier 	gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
15087dd7cddfSDavid du Colombier 			"show_cache_setup(dev_cache)");
15097dd7cddfSDavid du Colombier     gx_device_memory *dev2 =
15107dd7cddfSDavid du Colombier 	gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
15117dd7cddfSDavid du Colombier 			"show_cache_setup(dev_cache2)");
15127dd7cddfSDavid du Colombier 
15137dd7cddfSDavid du Colombier     if (dev == 0 || dev2 == 0) {
15147dd7cddfSDavid du Colombier 	gs_free_object(mem, dev2, "show_cache_setup(dev_cache2)");
15157dd7cddfSDavid du Colombier 	gs_free_object(mem, dev, "show_cache_setup(dev_cache)");
15167dd7cddfSDavid du Colombier 	return_error(gs_error_VMerror);
15177dd7cddfSDavid du Colombier     }
15187dd7cddfSDavid du Colombier     /*
15197dd7cddfSDavid du Colombier      * We only initialize the devices for the sake of the GC,
15207dd7cddfSDavid du Colombier      * (since we have to re-initialize dev as either a mem_mono
15217dd7cddfSDavid du Colombier      * or a mem_abuf device before actually using it) and also
15227dd7cddfSDavid du Colombier      * to set its memory pointer.
15237dd7cddfSDavid du Colombier      */
15247dd7cddfSDavid du Colombier     gs_make_mem_mono_device(dev, mem, gs_currentdevice_inline(pgs));
15257dd7cddfSDavid du Colombier     penum->dev_cache = dev;
15267dd7cddfSDavid du Colombier     gs_make_mem_mono_device(dev2, mem, gs_currentdevice_inline(pgs));
15277dd7cddfSDavid du Colombier     penum->dev_cache2 = dev2;
15287dd7cddfSDavid du Colombier     /* Retain these devices, since they are referenced from the enumerator. */
15297dd7cddfSDavid du Colombier     gx_device_retain((gx_device *)dev, true);
15307dd7cddfSDavid du Colombier     gx_device_retain((gx_device *)dev2, true);
15317dd7cddfSDavid du Colombier     return 0;
15327dd7cddfSDavid du Colombier }
15337dd7cddfSDavid du Colombier 
15347dd7cddfSDavid du Colombier /* Set the character origin as the origin of the coordinate system. */
15357dd7cddfSDavid du Colombier /* Used before rendering characters, and for moving the origin */
15367dd7cddfSDavid du Colombier /* in setcachedevice2 when WMode=1. */
15377dd7cddfSDavid du Colombier private int
show_origin_setup(gs_state * pgs,fixed cpt_x,fixed cpt_y,gs_show_enum * penum)1538*593dc095SDavid du Colombier show_origin_setup(gs_state * pgs, fixed cpt_x, fixed cpt_y, gs_show_enum * penum)
15397dd7cddfSDavid du Colombier {
1540*593dc095SDavid du Colombier     if (penum->charpath_flag == cpm_show) {
15417dd7cddfSDavid du Colombier 	/* Round the translation in the graphics state. */
15427dd7cddfSDavid du Colombier 	/* This helps prevent rounding artifacts later. */
1543*593dc095SDavid du Colombier 	if (gs_currentaligntopixels(penum->current_font->dir) == 0) {
1544*593dc095SDavid du Colombier 	    int scx = -1L << (_fixed_shift - penum->log2_scale.x);
1545*593dc095SDavid du Colombier 	    int scy = -1L << (_fixed_shift - penum->log2_scale.y);
1546*593dc095SDavid du Colombier 	    int rdx =  1L << (_fixed_shift - 1 - penum->log2_scale.x);
1547*593dc095SDavid du Colombier 	    int rdy =  1L << (_fixed_shift - 1 - penum->log2_scale.y);
1548*593dc095SDavid du Colombier 
1549*593dc095SDavid du Colombier 	    cpt_x = (cpt_x + rdx) & scx;
1550*593dc095SDavid du Colombier 	    cpt_y = (cpt_y + rdy) & scy;
1551*593dc095SDavid du Colombier 	} else {
15527dd7cddfSDavid du Colombier 	    cpt_x = fixed_rounded(cpt_x);
15537dd7cddfSDavid du Colombier 	    cpt_y = fixed_rounded(cpt_y);
15547dd7cddfSDavid du Colombier 	}
1555*593dc095SDavid du Colombier     }
15567dd7cddfSDavid du Colombier     /*
15577dd7cddfSDavid du Colombier      * BuildChar procedures expect the current point to be undefined,
15587dd7cddfSDavid du Colombier      * so we omit the gx_path_add_point with ctm.t*_fixed.
15597dd7cddfSDavid du Colombier      */
15607dd7cddfSDavid du Colombier     return gx_translate_to_fixed(pgs, cpt_x, cpt_y);
15617dd7cddfSDavid du Colombier }
1562