xref: /plan9-contrib/sys/src/cmd/gs/src/gxchar.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 /* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gxchar.c,v 1.5.2.1 2001/10/26 16:35:25 raph Exp $ */
20 /* Default implementation of text writing */
21 #include "gx.h"
22 #include "memory_.h"
23 #include "string_.h"
24 #include "gserrors.h"
25 #include "gsstruct.h"
26 #include "gxfixed.h"		/* ditto */
27 #include "gxarith.h"
28 #include "gxmatrix.h"
29 #include "gzstate.h"
30 #include "gxcoord.h"
31 #include "gxdevice.h"
32 #include "gxdevmem.h"
33 #include "gxchar.h"
34 #include "gxfont.h"
35 #include "gxfont0.h"
36 #include "gxfcache.h"
37 #include "gspath.h"
38 #include "gzpath.h"
39 
40 /* Define whether or not to cache characters rotated by angles other than */
41 /* multiples of 90 degrees. */
42 private const bool CACHE_ROTATED_CHARS = true;
43 
44 /* Define whether or not to oversample characters at small sizes. */
45 private const bool OVERSAMPLE = true;
46 
47 /* Define the maximum size of a full temporary bitmap when rasterizing, */
48 /* in bits (not bytes). */
49 private const uint MAX_TEMP_BITMAP_BITS = 80000;
50 
51 /* Define whether the show operation uses the character outline data, */
52 /* as opposed to just needing the width (or nothing). */
53 #define SHOW_USES_OUTLINE(penum)\
54   !SHOW_IS(penum, TEXT_DO_NONE | TEXT_DO_CHARWIDTH)
55 
56 /* Structure descriptors */
57 public_st_gs_show_enum();
58 extern_st(st_gs_text_enum);
59 extern_st(st_gs_state);		/* only for testing */
60 private
61 ENUM_PTRS_BEGIN(show_enum_enum_ptrs)
62      return ENUM_USING(st_gs_text_enum, vptr, size, index - 5);
63 ENUM_PTR(0, gs_show_enum, pgs);
64 ENUM_PTR(1, gs_show_enum, show_gstate);
65 ENUM_PTR3(2, gs_show_enum, dev_cache, dev_cache2, dev_null);
66 ENUM_PTRS_END
67 private RELOC_PTRS_WITH(show_enum_reloc_ptrs, gs_show_enum *eptr)
68 {
69     RELOC_USING(st_gs_text_enum, vptr, size);		/* superclass */
70     RELOC_VAR(eptr->pgs);
71     RELOC_VAR(eptr->show_gstate);
72     RELOC_PTR3(gs_show_enum, dev_cache, dev_cache2, dev_null);
73 }
74 RELOC_PTRS_END
75 
76 /* Forward declarations */
77 private int continue_kshow(P1(gs_show_enum *));
78 private int continue_show(P1(gs_show_enum *));
79 private int continue_show_update(P1(gs_show_enum *));
80 private void show_set_scale(P1(gs_show_enum *));
81 private int show_cache_setup(P1(gs_show_enum *));
82 private int show_state_setup(P1(gs_show_enum *));
83 private int show_origin_setup(P4(gs_state *, fixed, fixed, gs_char_path_mode));
84 
85 /* Accessors for current_char and current_glyph. */
86 #define CURRENT_CHAR(penum) ((penum)->returned.current_char)
87 #define SET_CURRENT_CHAR(penum, chr)\
88   ((penum)->returned.current_char = (chr))
89 #define CURRENT_GLYPH(penum) ((penum)->returned.current_glyph)
90 #define SET_CURRENT_GLYPH(penum, glyph)\
91   ((penum)->returned.current_glyph = (glyph))
92 
93 /* Allocate a show enumerator. */
94 gs_show_enum *
95 gs_show_enum_alloc(gs_memory_t * mem, gs_state * pgs, client_name_t cname)
96 {
97     gs_show_enum *penum;
98 
99     rc_alloc_struct_1(penum, gs_show_enum, &st_gs_show_enum, mem,
100 		      return 0, cname);
101     penum->rc.free = rc_free_text_enum;
102     penum->auto_release = true;	/* old API */
103     /* Initialize pointers for GC */
104     penum->text.operation = 0;	/* no pointers relevant */
105     penum->dev = 0;
106     penum->pgs = pgs;
107     penum->show_gstate = 0;
108     penum->dev_cache = 0;
109     penum->dev_cache2 = 0;
110     penum->dev_null = 0;
111     penum->fstack.depth = -1;
112     return penum;
113 }
114 
115 /* ------ Driver procedure ------ */
116 
117 private text_enum_proc_resync(gx_show_text_resync);
118 private text_enum_proc_process(gx_show_text_process);
119 private text_enum_proc_is_width_only(gx_show_text_is_width_only);
120 private text_enum_proc_current_width(gx_show_text_current_width);
121 private text_enum_proc_set_cache(gx_show_text_set_cache);
122 private text_enum_proc_retry(gx_show_text_retry);
123 private text_enum_proc_release(gx_show_text_release); /* not default */
124 
125 private const gs_text_enum_procs_t default_text_procs = {
126     gx_show_text_resync, gx_show_text_process,
127     gx_show_text_is_width_only, gx_show_text_current_width,
128     gx_show_text_set_cache, gx_show_text_retry,
129     gx_show_text_release
130 };
131 
132 int
133 gx_default_text_begin(gx_device * dev, gs_imager_state * pis,
134 		      const gs_text_params_t * text, gs_font * font,
135 		      gx_path * path, const gx_device_color * pdcolor,
136 		      const gx_clip_path * pcpath,
137 		      gs_memory_t * mem, gs_text_enum_t ** ppte)
138 {
139     uint operation = text->operation;
140     bool propagate_charpath = (operation & TEXT_DO_DRAW) != 0;
141     int code;
142     gs_state *pgs = (gs_state *)pis;
143     gs_show_enum *penum;
144 
145     /*
146      * For the moment, require pis to be a gs_state *, since all the
147      * procedures for character rendering expect it.
148      */
149     if (gs_object_type(mem, pis) != &st_gs_state)
150 	return_error(gs_error_Fatal);
151     penum = gs_show_enum_alloc(mem, pgs, "gx_default_text_begin");
152     if (!penum)
153 	return_error(gs_error_VMerror);
154     code = gs_text_enum_init((gs_text_enum_t *)penum, &default_text_procs,
155 			     dev, pis, text, font, path, pdcolor, pcpath, mem);
156     if (code < 0) {
157 	gs_free_object(mem, penum, "gx_default_text_begin");
158 	return code;
159     }
160     penum->auto_release = false; /* new API */
161     penum->level = pgs->level;
162     if (operation & TEXT_DO_ANY_CHARPATH)
163 	penum->charpath_flag =
164 	    (operation & TEXT_DO_FALSE_CHARPATH ? cpm_false_charpath :
165 	     operation & TEXT_DO_TRUE_CHARPATH ? cpm_true_charpath :
166 	     operation & TEXT_DO_FALSE_CHARBOXPATH ? cpm_false_charboxpath :
167 	     operation & TEXT_DO_TRUE_CHARBOXPATH ? cpm_true_charboxpath :
168 	     operation & TEXT_DO_CHARWIDTH ? cpm_charwidth :
169 	     cpm_show /* can't happen */ );
170     else
171 	penum->charpath_flag =
172 	    (propagate_charpath ? pgs->in_charpath : cpm_show);
173     penum->cc = 0;
174     penum->continue_proc = continue_show;
175     /* Note: show_state_setup may reset can_cache. */
176     switch (penum->charpath_flag) {
177     case cpm_false_charpath: case cpm_true_charpath:
178 	penum->can_cache = -1; break;
179     case cpm_false_charboxpath: case cpm_true_charboxpath:
180 	penum->can_cache = 0; break;
181     case cpm_charwidth:
182     default:			/* cpm_show */
183 	penum->can_cache = 1; break;
184     }
185     code = show_state_setup(penum);
186     if (code < 0)
187 	return code;
188     penum->show_gstate =
189 	(propagate_charpath && (pgs->in_charpath != 0) ?
190 	 pgs->show_gstate : pgs);
191     if (!(~operation & (TEXT_DO_NONE | TEXT_RETURN_WIDTH))) {
192 	/* This is stringwidth. */
193 	gx_device_null *dev_null =
194 	    gs_alloc_struct(mem, gx_device_null, &st_device_null,
195 			    "stringwidth(dev_null)");
196 
197 	if (dev_null == 0)
198 	    return_error(gs_error_VMerror);
199 	/* Do an extra gsave and suppress output */
200 	if ((code = gs_gsave(pgs)) < 0)
201 	    return code;
202 	penum->level = pgs->level;	/* for level check in show_update */
203 	/* Set up a null device that forwards xfont requests properly. */
204 	gs_make_null_device(dev_null, gs_currentdevice_inline(pgs), mem);
205 	pgs->ctm_default_set = false;
206 	penum->dev_null = dev_null;
207 	/* Retain this device, since it is referenced from the enumerator. */
208 	gx_device_retain((gx_device *)dev_null, true);
209 	gs_setdevice_no_init(pgs, (gx_device *) dev_null);
210 	/* Establish an arbitrary translation and current point. */
211 	gs_newpath(pgs);
212 	gx_translate_to_fixed(pgs, fixed_0, fixed_0);
213 	code = gx_path_add_point(pgs->path, fixed_0, fixed_0);
214 	if (code < 0)
215 	    return code;
216     }
217     *ppte = (gs_text_enum_t *)penum;
218     return 0;
219 }
220 
221 /* ------ Width/cache setting ------ */
222 
223 private int
224     set_char_width(P4(gs_show_enum *penum, gs_state *pgs,
225 		      floatp wx, floatp wy)),
226     set_cache_device(P6(gs_show_enum *penum, gs_state *pgs,
227 			floatp llx, floatp lly, floatp urx, floatp ury));
228 
229 /* This is the default implementation of text enumerator set_cache. */
230 private int
231 gx_show_text_set_cache(gs_text_enum_t *pte, const double *pw,
232 			  gs_text_cache_control_t control)
233 {
234     gs_show_enum *const penum = (gs_show_enum *)pte;
235     gs_state *pgs = penum->pgs;
236 
237     switch (control) {
238     case TEXT_SET_CHAR_WIDTH:
239 	return set_char_width(penum, pgs, pw[0], pw[1]);
240     case TEXT_SET_CACHE_DEVICE: {
241 	int code = set_char_width(penum, pgs, pw[0], pw[1]);	/* default is don't cache */
242 
243 	if (code < 0)
244 	    return code;
245 	return set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
246     }
247     case TEXT_SET_CACHE_DEVICE2: {
248 	int code;
249 
250 	if (gs_rootfont(pgs)->WMode) {
251 	    float vx = pw[8], vy = pw[9];
252 	    gs_fixed_point pvxy, dvxy;
253 	    cached_char *cc;
254 
255 	    if ((code = gs_point_transform2fixed(&pgs->ctm, -vx, -vy, &pvxy)) < 0 ||
256 		(code = gs_distance_transform2fixed(&pgs->ctm, vx, vy, &dvxy)) < 0
257 		)
258 		return 0;		/* don't cache */
259 	    if ((code = set_char_width(penum, pgs, pw[6], pw[7])) < 0)
260 		return code;
261 	    /* Adjust the origin by (vx, vy). */
262 	    gx_translate_to_fixed(pgs, pvxy.x, pvxy.y);
263 	    code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
264 	    if (code != 1)
265 		return code;
266 	    /* Adjust the character origin too. */
267 	    cc = penum->cc;
268 	    cc->offset.x += dvxy.x;
269 	    cc->offset.y += dvxy.y;
270 	} else {
271 	    code = set_char_width(penum, pgs, pw[0], pw[1]);
272 	    if (code < 0)
273 		return code;
274 	    code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]);
275 	}
276 	return code;
277     }
278     default:
279 	return_error(gs_error_rangecheck);
280     }
281 }
282 
283 /* Set the character width. */
284 /* Note that this returns 1 if the current show operation is */
285 /* non-displaying (stringwidth or cshow). */
286 private int
287 set_char_width(gs_show_enum *penum, gs_state *pgs, floatp wx, floatp wy)
288 {
289     int code;
290 
291     if (penum->width_status != sws_none)
292 	return_error(gs_error_undefined);
293     if ((code = gs_distance_transform2fixed(&pgs->ctm, wx, wy, &penum->wxy)) < 0)
294 	return code;
295     /* Check whether we're setting the scalable width */
296     /* for a cached xfont character. */
297     if (penum->cc != 0) {
298 	penum->cc->wxy = penum->wxy;
299 	penum->width_status = sws_cache_width_only;
300     } else {
301 	penum->width_status = sws_no_cache;
302     }
303     return !SHOW_IS_DRAWING(penum);
304 }
305 
306 /* Set up the cache device if relevant. */
307 /* Return 1 if we just set up a cache device. */
308 /* Used by setcachedevice and setcachedevice2. */
309 private int
310 set_cache_device(gs_show_enum * penum, gs_state * pgs, floatp llx, floatp lly,
311 		 floatp urx, floatp ury)
312 {
313     gs_glyph glyph;
314 
315     /* See if we want to cache this character. */
316     if (pgs->in_cachedevice)	/* no recursion! */
317 	return 0;
318     if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) { /* cshow */
319 	int code;
320 	if_debug0('k', "[k]no cache: cshow");
321 	code = gs_nulldevice(pgs);
322 	if (code < 0)
323 	    return code;
324 	return 0;
325     }
326     pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;	/* disable color/gray/image operators */
327     /* We can only use the cache if we know the glyph. */
328     glyph = CURRENT_GLYPH(penum);
329     if (glyph == gs_no_glyph)
330 	return 0;
331     /* We can only use the cache if ctm is unchanged */
332     /* (aside from a possible translation). */
333     if (penum->can_cache <= 0 || !pgs->char_tm_valid) {
334 	if_debug2('k', "[k]no cache: can_cache=%d, char_tm_valid=%d\n",
335 		  penum->can_cache, (int)pgs->char_tm_valid);
336 	return 0;
337     } {
338 	const gs_font *pfont = pgs->font;
339 	gs_font_dir *dir = pfont->dir;
340 	gx_device *dev = gs_currentdevice_inline(pgs);
341 	int alpha_bits =
342 	(*dev_proc(dev, get_alpha_bits)) (dev, go_text);
343 	gs_log2_scale_point log2_scale;
344 	static const fixed max_cdim[3] =
345 	{
346 #define max_cd(n)\
347 	    (fixed_1 << (arch_sizeof_short * 8 - n)) - (fixed_1 >> n) * 3
348 	    max_cd(0), max_cd(1), max_cd(2)
349 #undef max_cd
350 	};
351 	ushort iwidth, iheight;
352 	cached_char *cc;
353 	gs_fixed_rect clip_box;
354 	int code;
355 
356 	/* Compute the bounding box of the transformed character. */
357 	/* Since we accept arbitrary transformations, the extrema */
358 	/* may occur in any order; however, we can save some work */
359 	/* by observing that opposite corners before transforming */
360 	/* are still opposite afterwards. */
361 	gs_fixed_point cll, clr, cul, cur, cdim;
362 
363 	if ((code = gs_distance_transform2fixed(&pgs->ctm, llx, lly, &cll)) < 0 ||
364 	    (code = gs_distance_transform2fixed(&pgs->ctm, llx, ury, &clr)) < 0 ||
365 	    (code = gs_distance_transform2fixed(&pgs->ctm, urx, lly, &cul)) < 0 ||
366 	 (code = gs_distance_transform2fixed(&pgs->ctm, urx, ury, &cur)) < 0
367 	    )
368 	    return 0;		/* don't cache */
369 	{
370 	    fixed ctemp;
371 
372 #define swap(a, b) ctemp = a, a = b, b = ctemp
373 #define make_min(a, b) if ( (a) > (b) ) swap(a, b)
374 
375 	    make_min(cll.x, cur.x);
376 	    make_min(cll.y, cur.y);
377 	    make_min(clr.x, cul.x);
378 	    make_min(clr.y, cul.y);
379 #undef make_min
380 #undef swap
381 	}
382 	/* Now take advantage of symmetry. */
383 	if (clr.x < cll.x)
384 	    cll.x = clr.x, cur.x = cul.x;
385 	if (clr.y < cll.y)
386 	    cll.y = clr.y, cur.y = cul.y;
387 	/* Now cll and cur are the extrema of the box. */
388 	cdim.x = cur.x - cll.x;
389 	cdim.y = cur.y - cll.y;
390 	show_set_scale(penum);
391 	log2_scale.x = penum->log2_suggested_scale.x;
392 	log2_scale.y = penum->log2_suggested_scale.y;
393 #ifdef DEBUG
394 	if (gs_debug_c('k')) {
395 	    dlprintf6("[k]cbox=[%g %g %g %g] scale=%dx%d\n",
396 		      fixed2float(cll.x), fixed2float(cll.y),
397 		      fixed2float(cur.x), fixed2float(cur.y),
398 		      1 << log2_scale.x, 1 << log2_scale.y);
399 	    dlprintf6("[p]  ctm=[%g %g %g %g %g %g]\n",
400 		      pgs->ctm.xx, pgs->ctm.xy, pgs->ctm.yx, pgs->ctm.yy,
401 		      pgs->ctm.tx, pgs->ctm.ty);
402 	}
403 #endif
404 	/*
405 	 * If the device wants anti-aliased text,
406 	 * increase the sampling scale to ensure that
407 	 * if we want N bits of alpha, we generate
408 	 * at least 2^N sampled bits per pixel.
409 	 */
410 	if (alpha_bits > 1) {
411 	    int more_bits =
412 	    alpha_bits - (log2_scale.x + log2_scale.y);
413 
414 	    if (more_bits > 0) {
415 		if (log2_scale.x <= log2_scale.y) {
416 		    log2_scale.x += (more_bits + 1) >> 1;
417 		    log2_scale.y += more_bits >> 1;
418 		} else {
419 		    log2_scale.x += more_bits >> 1;
420 		    log2_scale.y += (more_bits + 1) >> 1;
421 		}
422 	    }
423 	} else if (!OVERSAMPLE || pfont->PaintType != 0) {
424 	    /* Don't oversample artificially stroked fonts. */
425 	    log2_scale.x = log2_scale.y = 0;
426 	}
427 	if (cdim.x > max_cdim[log2_scale.x] ||
428 	    cdim.y > max_cdim[log2_scale.y]
429 	    )
430 	    return 0;		/* much too big */
431 	iwidth = ((ushort) fixed2int_var(cdim.x) + 2) << log2_scale.x;
432 	iheight = ((ushort) fixed2int_var(cdim.y) + 2) << log2_scale.y;
433 	if_debug3('k', "[k]iwidth=%u iheight=%u dev_cache %s\n",
434 		  (uint) iwidth, (uint) iheight,
435 		  (penum->dev_cache == 0 ? "not set" : "set"));
436 	if (penum->dev_cache == 0) {
437 	    code = show_cache_setup(penum);
438 	    if (code < 0)
439 		return code;
440 	}
441 	/*
442 	 * If we're oversampling (i.e., the temporary bitmap is
443 	 * larger than the final monobit or alpha array) and the
444 	 * temporary bitmap is large, use incremental conversion
445 	 * from oversampled bitmap strips to alpha values instead of
446 	 * full oversampling with compression at the end.
447 	 */
448 	cc = gx_alloc_char_bits(dir, penum->dev_cache,
449 				(iwidth > MAX_TEMP_BITMAP_BITS / iheight &&
450 				 log2_scale.x + log2_scale.y > alpha_bits ?
451 				 penum->dev_cache2 : NULL),
452 				iwidth, iheight, &log2_scale, alpha_bits);
453 	if (cc == 0)
454 	    return 0;		/* too big for cache */
455 	/* The mins handle transposed coordinate systems.... */
456 	/* Truncate the offsets to avoid artifacts later. */
457 	cc->offset.x = fixed_ceiling(-cll.x);
458 	cc->offset.y = fixed_ceiling(-cll.y);
459 	if_debug4('k', "[k]width=%u, height=%u, offset=[%g %g]\n",
460 		  (uint) iwidth, (uint) iheight,
461 		  fixed2float(cc->offset.x),
462 		  fixed2float(cc->offset.y));
463 	pgs->in_cachedevice = CACHE_DEVICE_NONE; /* Provide correct grestore */
464 	if ((code = gs_gsave(pgs)) < 0) {
465 	    gx_free_cached_char(dir, cc);
466 	    return code;
467 	}
468 	/* Nothing can go wrong now.... */
469 	penum->cc = cc;
470 	cc->code = glyph;
471 	cc->wmode = gs_rootfont(pgs)->WMode;
472 	cc->wxy = penum->wxy;
473 	/* Install the device */
474 	gx_set_device_only(pgs, (gx_device *) penum->dev_cache);
475 	pgs->ctm_default_set = false;
476 	/* Adjust the transformation in the graphics context */
477 	/* so that the character lines up with the cache. */
478 	gx_translate_to_fixed(pgs,
479 			      cc->offset.x << log2_scale.x,
480 			      cc->offset.y << log2_scale.y);
481 	if ((log2_scale.x | log2_scale.y) != 0)
482 	    gx_scale_char_matrix(pgs, 1 << log2_scale.x,
483 				 1 << log2_scale.y);
484 	/* Set the initial matrix for the cache device. */
485 	penum->dev_cache->initial_matrix = ctm_only(pgs);
486 	/* Set the oversampling factor. */
487 	penum->log2_scale.x = log2_scale.x;
488 	penum->log2_scale.y = log2_scale.y;
489 	/* Reset the clipping path to match the metrics. */
490 	clip_box.p.x = clip_box.p.y = 0;
491 	clip_box.q.x = int2fixed(iwidth);
492 	clip_box.q.y = int2fixed(iheight);
493 	if ((code = gx_clip_to_rectangle(pgs, &clip_box)) < 0)
494 	    return code;
495 	gx_set_device_color_1(pgs);	/* write 1's */
496 	pgs->in_cachedevice = CACHE_DEVICE_CACHING;
497     }
498     penum->width_status = sws_cache;
499     return 1;
500 }
501 
502 /* Return the cache device status. */
503 gs_in_cache_device_t
504 gs_incachedevice(const gs_state *pgs)
505 {
506     return pgs->in_cachedevice;
507 }
508 
509 /* ------ Enumerator ------ */
510 
511 /*
512  * Set the encode_char procedure in an enumerator.
513  */
514 private void
515 show_set_encode_char(gs_show_enum * penum)
516 {
517     penum->encode_char =
518 	(SHOW_IS(penum, TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH) ?
519 	 gs_no_encode_char :
520 	 gs_show_current_font(penum)->procs.encode_char);
521 }
522 
523 /*
524  * Resync a text operation with a different set of parameters.
525  * Currently this is implemented only for changing the data source.
526  */
527 private int
528 gx_show_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
529 {
530     gs_show_enum *const penum = (gs_show_enum *)pte;
531     int old_index = pte->index;
532 
533     if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY)
534 	return_error(gs_error_rangecheck);
535     pte->text = pfrom->text;
536     if (pte->index == old_index) {
537 	show_set_encode_char(penum);
538 	return 0;
539     } else
540 	return show_state_setup(penum);
541 }
542 
543 /* Do the next step of a show (or stringwidth) operation */
544 private int
545 gx_show_text_process(gs_text_enum_t *pte)
546 {
547     gs_show_enum *const penum = (gs_show_enum *)pte;
548 
549     return (*penum->continue_proc)(penum);
550 }
551 
552 /* Continuation procedures */
553 private int show_update(P1(gs_show_enum * penum));
554 private int show_move(P1(gs_show_enum * penum));
555 private int show_proceed(P1(gs_show_enum * penum));
556 private int show_finish(P1(gs_show_enum * penum));
557 private int
558 continue_show_update(gs_show_enum * penum)
559 {
560     int code = show_update(penum);
561 
562     if (code < 0)
563 	return code;
564     code = show_move(penum);
565     if (code != 0)
566 	return code;
567     return show_proceed(penum);
568 }
569 private int
570 continue_show(gs_show_enum * penum)
571 {
572     return show_proceed(penum);
573 }
574 /* For kshow, the CTM or font may have changed, so we have to reestablish */
575 /* the cached values in the enumerator. */
576 private int
577 continue_kshow(gs_show_enum * penum)
578 {   int code;
579     gs_state *pgs = penum->pgs;
580 
581     if (pgs->font != penum->orig_font)
582 	gs_setfont(pgs, penum->orig_font);
583 
584     code = show_state_setup(penum);
585 
586     if (code < 0)
587 	return code;
588     return show_proceed(penum);
589 }
590 
591 /* Update position */
592 private int
593 show_update(gs_show_enum * penum)
594 {
595     gs_state *pgs = penum->pgs;
596     cached_char *cc = penum->cc;
597     int code;
598 
599     /* Update position for last character */
600     switch (penum->width_status) {
601 	case sws_none:
602 	    /* Adobe interpreters assume a character width of 0, */
603 	    /* even though the documentation says this is an error.... */
604 	    penum->wxy.x = penum->wxy.y = 0;
605 	    break;
606 	case sws_cache:
607 	    /* Finish installing the cache entry. */
608 	    /* If the BuildChar/BuildGlyph procedure did a save and a */
609 	    /* restore, it already undid the gsave in setcachedevice. */
610 	    /* We have to check for this by comparing levels. */
611 	    switch (pgs->level - penum->level) {
612 		default:
613 		    return_error(gs_error_invalidfont);		/* WRONG */
614 		case 2:
615 		    code = gs_grestore(pgs);
616 		    if (code < 0)
617 			return code;
618 		case 1:
619 		    ;
620 	    }
621 	    gx_add_cached_char(pgs->font->dir, penum->dev_cache,
622 			       cc, gx_lookup_fm_pair(pgs->font, pgs),
623 			       &penum->log2_scale);
624 	    if (!SHOW_USES_OUTLINE(penum) ||
625 		penum->charpath_flag != cpm_show
626 		)
627 		break;
628 	    /* falls through */
629 	case sws_cache_width_only:
630 	    /* Copy the bits to the real output device. */
631 	    code = gs_grestore(pgs);
632 	    if (code < 0)
633 		return code;
634 	    code = gs_state_color_load(pgs);
635 	    if (code < 0)
636 		return code;
637 	    return gx_image_cached_char(penum, cc);
638 	case sws_no_cache:
639 	    ;
640     }
641     if (penum->charpath_flag != cpm_show) {
642 	/* Move back to the character origin, so that */
643 	/* show_move will get us to the right place. */
644 	code = gx_path_add_point(pgs->show_gstate->path,
645 				 penum->origin.x, penum->origin.y);
646 	if (code < 0)
647 	    return code;
648     }
649     return gs_grestore(pgs);
650 }
651 
652 /* Move to next character */
653 private int
654 show_fast_move(gs_state * pgs, gs_fixed_point * pwxy)
655 {
656     int code = gx_path_add_rel_point_inline(pgs->path, pwxy->x, pwxy->y);
657 
658     /* If the current position is out of range, don't try to move. */
659     if (code == gs_error_limitcheck && pgs->clamp_coordinates)
660 	code = 0;
661     return code;
662 }
663 private int
664 show_move(gs_show_enum * penum)
665 {
666     gs_state *pgs = penum->pgs;
667 
668     if (SHOW_IS(penum, TEXT_REPLACE_WIDTHS)) {
669 	gs_point dpt;
670 
671 	gs_text_replaced_width(&penum->text, penum->xy_index - 1, &dpt);
672 	gs_distance_transform2fixed(&pgs->ctm, dpt.x, dpt.y, &penum->wxy);
673     } else {
674 	double dx = 0, dy = 0;
675 
676 	if (SHOW_IS_ADD_TO_SPACE(penum)) {
677 	    gs_char chr = CURRENT_CHAR(penum) & 0xff;
678 	    int fdepth = penum->fstack.depth;
679 
680 	    if (fdepth > 0) {
681 		/* Add in the shifted font number. */
682 		uint fidx = penum->fstack.items[fdepth].index;
683 
684 		switch (((gs_font_type0 *) (penum->fstack.items[fdepth - 1].font))->data.FMapType) {
685 		case fmap_1_7:
686 		case fmap_9_7:
687 		    chr += fidx << 7;
688 		    break;
689 		case fmap_CMap:
690 		    chr = CURRENT_CHAR(penum);  /* the full character */
691 		    if (!penum->cmap_code)
692 			break;
693 		    /* falls through */
694 		default:
695 		    chr += fidx << 8;
696 		}
697 	    }
698 	    if (chr == penum->text.space.s_char) {
699 		dx = penum->text.delta_space.x;
700 		dy = penum->text.delta_space.y;
701 	    }
702 	}
703 	if (SHOW_IS_ADD_TO_ALL(penum)) {
704 	    dx += penum->text.delta_all.x;
705 	    dy += penum->text.delta_all.y;
706 	}
707 	if (!is_fzero2(dx, dy)) {
708 	    gs_fixed_point dxy;
709 
710 	    gs_distance_transform2fixed(&pgs->ctm, dx, dy, &dxy);
711 	    penum->wxy.x += dxy.x;
712 	    penum->wxy.y += dxy.y;
713 	}
714     }
715     if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) {
716 	/* HACK for cshow */
717 	penum->continue_proc = continue_kshow;
718 	return TEXT_PROCESS_INTERVENE;
719     }
720     /* wxy is in device coordinates */
721     {
722 	int code = show_fast_move(pgs, &penum->wxy);
723 
724 	if (code < 0)
725 	    return code;
726     }
727     /* Check for kerning, but not on the last character. */
728     if (SHOW_IS_DO_KERN(penum) && penum->index < penum->text.size) {
729 	penum->continue_proc = continue_kshow;
730 	return TEXT_PROCESS_INTERVENE;
731     }
732     return 0;
733 }
734 /* Process next character */
735 private int
736 show_proceed(gs_show_enum * penum)
737 {
738     gs_state *pgs = penum->pgs;
739     gs_font *pfont;
740     cached_fm_pair *pair = 0;
741     gs_font *rfont =
742 	(penum->fstack.depth < 0 ? pgs->font : penum->fstack.items[0].font);
743     int wmode = rfont->WMode;
744     font_proc_next_char_glyph((*next_char_glyph)) =
745 	rfont->procs.next_char_glyph;
746 #define get_next_char_glyph(pte, pchr, pglyph)\
747   (++(penum->xy_index), next_char_glyph(pte, pchr, pglyph))
748     gs_char chr;
749     gs_glyph glyph;
750     int code;
751     cached_char *cc;
752     gx_device *dev = gs_currentdevice_inline(pgs);
753     int alpha_bits = (*dev_proc(dev, get_alpha_bits)) (dev, go_text);
754 
755     if (penum->charpath_flag == cpm_show && SHOW_USES_OUTLINE(penum)) {
756 	code = gs_state_color_load(pgs);
757 	if (code < 0)
758 	    return code;
759     }
760   more:			/* Proceed to next character */
761     pfont = (penum->fstack.depth < 0 ? pgs->font :
762 	     penum->fstack.items[penum->fstack.depth].font);
763     penum->current_font = pfont;
764     /* can_cache >= 0 allows us to use cached characters, */
765     /* even if we can't make new cache entries. */
766     if (penum->can_cache >= 0) {
767 	/* Loop with cache */
768 	for (;;) {
769 	    switch ((code = get_next_char_glyph((gs_text_enum_t *)penum,
770 						&chr, &glyph))
771 		    ) {
772 		default:	/* error */
773 		    return code;
774 		case 2:	/* done */
775 		    return show_finish(penum);
776 		case 1:	/* font change */
777 		    pfont = penum->fstack.items[penum->fstack.depth].font;
778 		    penum->current_font = pfont;
779 		    pgs->char_tm_valid = false;
780 		    show_state_setup(penum);
781 		    pair = 0;
782 		    /* falls through */
783 		case 0:	/* plain char */
784 		    /*
785 		     * We don't need to set penum->current_char in the
786 		     * normal cases, but it's needed for widthshow,
787 		     * kshow, and one strange client, so we may as well
788 		     * do it here.
789 		     */
790 		    SET_CURRENT_CHAR(penum, chr);
791 		    if (glyph == gs_no_glyph) {
792 			glyph = (*penum->encode_char)(pfont, chr,
793 						      GLYPH_SPACE_NAME);
794 			if (glyph == gs_no_glyph) {
795 			    cc = 0;
796 			    goto no_cache;
797 			}
798 		    }
799 		    if (pair == 0)
800 			pair = gx_lookup_fm_pair(pfont, pgs);
801 		    cc = gx_lookup_cached_char(pfont, pair, glyph, wmode,
802 					       alpha_bits);
803 		    if (cc == 0) {
804 			/* Character is not in cache. */
805 			/* If possible, try for an xfont before */
806 			/* rendering from the outline. */
807 			if (pfont->ExactSize == fbit_use_outlines ||
808 			    pfont->PaintType == 2
809 			    )
810 			    goto no_cache;
811 			if (pfont->BitmapWidths) {
812 			    cc = gx_lookup_xfont_char(pgs, pair, chr,
813 				     glyph, &pfont->procs.callbacks, wmode);
814 			    if (cc == 0)
815 				goto no_cache;
816 			} else {
817 			    if (!SHOW_USES_OUTLINE(penum) ||
818 				(penum->charpath_flag != cpm_show &&
819 				 penum->charpath_flag != cpm_charwidth)
820 				)
821 				goto no_cache;
822 			    /* We might have an xfont, but we still */
823 			    /* want the scalable widths. */
824 			    cc = gx_lookup_xfont_char(pgs, pair, chr,
825 				     glyph, &pfont->procs.callbacks, wmode);
826 			    /* Render up to the point of */
827 			    /* setcharwidth or setcachedevice, */
828 			    /* just as for stringwidth. */
829 			    /* This is the only case in which we can */
830 			    /* to go no_cache with cc != 0. */
831 			    goto no_cache;
832 			}
833 		    }
834 		    /* Character is in cache. */
835 		    /* We might be doing .charboxpath or stringwidth; */
836 		    /* check for these now. */
837 		    if (penum->charpath_flag == cpm_charwidth) {
838 			/* This is charwidth.  Just move by the width. */
839 			DO_NOTHING;
840 		    } else if (penum->charpath_flag != cpm_show) {
841 			/* This is .charboxpath. Get the bounding box */
842 			/* and append it to a path. */
843 			gx_path box_path;
844 			gs_fixed_point pt;
845 			fixed llx, lly, urx, ury;
846 
847 			code = gx_path_current_point(pgs->path, &pt);
848 			if (code < 0)
849 			    return code;
850 			llx = fixed_rounded(pt.x - cc->offset.x) +
851 			    int2fixed(penum->ftx);
852 			lly = fixed_rounded(pt.y - cc->offset.y) +
853 			    int2fixed(penum->fty);
854 			urx = llx + int2fixed(cc->width),
855 			    ury = lly + int2fixed(cc->height);
856 			gx_path_init_local(&box_path, pgs->memory);
857 			code =
858 			    gx_path_add_rectangle(&box_path, llx, lly,
859 						  urx, ury);
860 			if (code >= 0)
861 			    code =
862 				gx_path_add_char_path(pgs->show_gstate->path,
863 						      &box_path,
864 						      penum->charpath_flag);
865 			if (code >= 0)
866 			    code = gx_path_add_point(pgs->path, pt.x, pt.y);
867 			gx_path_free(&box_path, "show_proceed(box path)");
868 			if (code < 0)
869 			    return code;
870 		    } else if (SHOW_IS_DRAWING(penum)) {
871 			code = gx_image_cached_char(penum, cc);
872 			if (code < 0)
873 			    return code;
874 			else if (code > 0) {
875 			    cc = 0;
876 			    goto no_cache;
877 			}
878 		    }
879 		    if (SHOW_IS_SLOW(penum)) {
880 			/* Split up the assignment so that the */
881 			/* Watcom compiler won't reserve esi/edi. */
882 			penum->wxy.x = cc->wxy.x;
883 			penum->wxy.y = cc->wxy.y;
884 			code = show_move(penum);
885 		    } else
886 			code = show_fast_move(pgs, &cc->wxy);
887 		    if (code) {
888 			/* Might be kshow, so store the state. */
889 			SET_CURRENT_GLYPH(penum, glyph);
890 			return code;
891 		    }
892 	    }
893 	}
894     } else {
895 	/* Can't use cache */
896 	switch ((code = get_next_char_glyph((gs_text_enum_t *)penum,
897 					    &chr, &glyph))
898 		) {
899 	    default:
900 		return code;
901 	    case 2:
902 		return show_finish(penum);
903 	    case 1:
904 		pfont = penum->fstack.items[penum->fstack.depth].font;
905 		penum->current_font = pfont;
906 		show_state_setup(penum);
907 	    case 0:
908 		;
909 	}
910 	SET_CURRENT_CHAR(penum, chr);
911 	if (glyph == gs_no_glyph) {
912 	    glyph = (*penum->encode_char)(pfont, chr, GLYPH_SPACE_NAME);
913 	}
914 	cc = 0;
915     }
916   no_cache:
917     /*
918      * We must call the client's rendering code.  Normally,
919      * we only do this if the character is not cached (cc = 0);
920      * however, we also must do this if we have an xfont but
921      * are using scalable widths.  In this case, and only this case,
922      * we get here with cc != 0.  penum->current_char has already
923      * been set, but not penum->current_glyph.
924      */
925     SET_CURRENT_GLYPH(penum, glyph);
926     if ((code = gs_gsave(pgs)) < 0)
927 	return code;
928     /* Set the font to the current descendant font. */
929     pgs->font = pfont;
930     /* Reset the in_cachedevice flag, so that a recursive show */
931     /* will use the cache properly. */
932     pgs->in_cachedevice = CACHE_DEVICE_NONE;
933     /* Reset the sampling scale. */
934     penum->log2_scale.x = penum->log2_scale.y = 0;
935     /* Set the charpath data in the graphics context if necessary, */
936     /* so that fill and stroke will add to the path */
937     /* rather than having their usual effect. */
938     pgs->in_charpath = penum->charpath_flag;
939     pgs->show_gstate =
940 	(penum->show_gstate == pgs ? pgs->saved : penum->show_gstate);
941     pgs->stroke_adjust = false;	/* per specification */
942     {
943 	gs_fixed_point cpt;
944 	gx_path *ppath = pgs->path;
945 
946 	if ((code = gx_path_current_point_inline(ppath, &cpt)) < 0) {
947 	    /* For cshow, having no current point is acceptable. */
948 	    if (!SHOW_IS(penum, TEXT_DO_NONE))
949 		goto rret;
950 	    cpt.x = cpt.y = 0;	/* arbitrary */
951 	}
952 	penum->origin.x = cpt.x;
953 	penum->origin.y = cpt.y;
954 	/* Normally, char_tm is valid because of show_state_setup, */
955 	/* but if we're in a cshow, it may not be. */
956 	gs_currentcharmatrix(pgs, NULL, true);
957 #if 1				/*USE_FPU <= 0 */
958 	if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) {
959 	    fixed tx = pgs->ctm.tx_fixed;
960 	    fixed ty = pgs->ctm.ty_fixed;
961 
962 	    gs_settocharmatrix(pgs);
963 	    cpt.x += pgs->ctm.tx_fixed - tx;
964 	    cpt.y += pgs->ctm.ty_fixed - ty;
965 	} else
966 #endif
967 	{
968 	    double tx = pgs->ctm.tx;
969 	    double ty = pgs->ctm.ty;
970 	    double fpx, fpy;
971 
972 	    gs_settocharmatrix(pgs);
973 	    fpx = fixed2float(cpt.x) + (pgs->ctm.tx - tx);
974 	    fpy = fixed2float(cpt.y) + (pgs->ctm.ty - ty);
975 #define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
976 	    if (!(f_fits_in_fixed(fpx) && f_fits_in_fixed(fpy))) {
977 		gs_note_error(code = gs_error_limitcheck);
978 		goto rret;
979 	    }
980 	    cpt.x = float2fixed(fpx);
981 	    cpt.y = float2fixed(fpy);
982 	}
983 	gs_newpath(pgs);
984 	code = show_origin_setup(pgs, cpt.x, cpt.y,
985 				 penum->charpath_flag);
986 	if (code < 0)
987 	    goto rret;
988     }
989     penum->width_status = sws_none;
990     penum->continue_proc = continue_show_update;
991     /* Try using the build procedure in the font. */
992     /* < 0 means error, 0 means success, 1 means failure. */
993     penum->cc = cc;		/* set this now for build procedure */
994     code = (*pfont->procs.build_char)((gs_text_enum_t *)penum, pgs, pfont,
995 				      chr, glyph);
996     if (code < 0) {
997 	discard(gs_note_error(code));
998 	goto rret;
999     }
1000     if (code == 0) {
1001 	code = show_update(penum);
1002 	if (code < 0)
1003 	    goto rret;
1004 	/* Note that show_update does a grestore.... */
1005 	code = show_move(penum);
1006 	if (code)
1007 	    return code;	/* ... so don't go to rret here. */
1008 	goto more;
1009     }
1010     /*
1011      * Some BuildChar procedures do a save before the setcachedevice,
1012      * and a restore at the end.  If we waited to allocate the cache
1013      * device until the setcachedevice, we would attempt to free it
1014      * after the restore.  Therefore, allocate it now.
1015      */
1016     if (penum->dev_cache == 0) {
1017 	code = show_cache_setup(penum);
1018 	if (code < 0)
1019 	    goto rret;
1020     }
1021     return TEXT_PROCESS_RENDER;
1022     /* If we get an error while setting up for BuildChar, */
1023     /* we must undo the partial setup. */
1024   rret:gs_grestore(pgs);
1025     return code;
1026 #undef get_next_char_glyph
1027 }
1028 
1029 /*
1030  * Prepare to retry rendering of the current character.  (This is only used
1031  * in one place in zchar1.c; a different approach may be better.)
1032  */
1033 private int
1034 gx_show_text_retry(gs_text_enum_t *pte)
1035 {
1036     gs_show_enum *const penum = (gs_show_enum *)pte;
1037 
1038     if (penum->cc) {
1039 	gs_font *pfont = penum->current_font;
1040 
1041 	gx_free_cached_char(pfont->dir, penum->cc);
1042 	penum->cc = 0;
1043     }
1044     gs_grestore(penum->pgs);
1045     penum->width_status = sws_none;
1046     penum->log2_scale.x = penum->log2_scale.y = 0;
1047     return 0;
1048 }
1049 
1050 /* Finish show or stringwidth */
1051 private int
1052 show_finish(gs_show_enum * penum)
1053 {
1054     gs_state *pgs = penum->pgs;
1055     int code, rcode;
1056 
1057     if (penum->auto_release)
1058 	penum->procs->release((gs_text_enum_t *)penum, "show_finish");
1059     if (!SHOW_IS_STRINGWIDTH(penum))
1060 	return 0;
1061     /* Save the accumulated width before returning, */
1062     /* and undo the extra gsave. */
1063     code = gs_currentpoint(pgs, &penum->returned.total_width);
1064     rcode = gs_grestore(pgs);
1065     return (code < 0 ? code : rcode);
1066 }
1067 
1068 /* Release the structure. */
1069 private void
1070 gx_show_text_release(gs_text_enum_t *pte, client_name_t cname)
1071 {
1072     gs_show_enum *const penum = (gs_show_enum *)pte;
1073 
1074     penum->cc = 0;
1075     if (penum->dev_cache2) {
1076 	gx_device_retain((gx_device *)penum->dev_cache2, false);
1077 	penum->dev_cache2 = 0;
1078     }
1079     if (penum->dev_cache) {
1080 	gx_device_retain((gx_device *)penum->dev_cache, false);
1081 	penum->dev_cache = 0;
1082     }
1083     if (penum->dev_null) {
1084 	gx_device_retain((gx_device *)penum->dev_null, false);
1085 	penum->dev_null = 0;
1086     }
1087     gx_default_text_release(pte, cname);
1088 }
1089 
1090 /* ------ Miscellaneous accessors ------ */
1091 
1092 /* Return the charpath mode. */
1093 gs_char_path_mode
1094 gs_show_in_charpath(const gs_show_enum * penum)
1095 {
1096     return penum->charpath_flag;
1097 }
1098 
1099 /* Return true if we only need the width from the rasterizer */
1100 /* and can short-circuit the full rendering of the character, */
1101 /* false if we need the actual character bits. */
1102 /* This is only meaningful just before calling gs_setcharwidth or */
1103 /* gs_setcachedevice[2]. */
1104 /* Note that we can't do this if the procedure has done any extra [g]saves. */
1105 private bool
1106 gx_show_text_is_width_only(const gs_text_enum_t *pte)
1107 {
1108     const gs_show_enum *const penum = (const gs_show_enum *)pte;
1109 
1110     /* penum->cc will be non-zero iff we are calculating */
1111     /* the scalable width for an xfont character. */
1112     return ((!SHOW_USES_OUTLINE(penum) || penum->cc != 0) &&
1113 	    penum->pgs->level == penum->level + 1);
1114 }
1115 
1116 /* Return the width of the just-enumerated character (for cshow). */
1117 private int
1118 gx_show_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
1119 {
1120     const gs_show_enum *const penum = (const gs_show_enum *)pte;
1121 
1122     return gs_idtransform(penum->pgs,
1123 			  fixed2float(penum->wxy.x),
1124 			  fixed2float(penum->wxy.y), pwidth);
1125 }
1126 
1127 /* Return the current font for cshow. */
1128 gs_font *
1129 gs_show_current_font(const gs_show_enum * penum)
1130 {
1131     return (penum->fstack.depth < 0 ? penum->pgs->font :
1132 	    penum->fstack.items[penum->fstack.depth].font);
1133 }
1134 
1135 /* ------ Internal routines ------ */
1136 
1137 /* Initialize the gstate-derived parts of a show enumerator. */
1138 /* We do this both when starting the show operation, */
1139 /* and when returning from the kshow callout. */
1140 /* Uses only penum->pgs, penum->fstack. */
1141 private int
1142 show_state_setup(gs_show_enum * penum)
1143 {
1144     gs_state *pgs = penum->pgs;
1145     gx_clip_path *pcpath;
1146     gs_font *pfont;
1147 
1148     if (penum->fstack.depth <= 0) {
1149 	pfont = pgs->font;
1150 	gs_currentcharmatrix(pgs, NULL, 1);	/* make char_tm valid */
1151     } else {
1152 	/* We have to concatenate the parent's FontMatrix as well. */
1153 	gs_matrix mat;
1154 	const gx_font_stack_item_t *pfsi =
1155 	    &penum->fstack.items[penum->fstack.depth];
1156 
1157 	pfont = pfsi->font;
1158 	gs_matrix_multiply(&pfont->FontMatrix,
1159 			   &pfsi[-1].font->FontMatrix, &mat);
1160 	gs_setcharmatrix(pgs, &mat);
1161     }
1162     penum->current_font = pfont;
1163     /* Skewing or non-rectangular rotation are not supported. */
1164     if (!CACHE_ROTATED_CHARS &&
1165 	(is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
1166 	 is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy))
1167 	)
1168 	penum->can_cache = 0;
1169     if (penum->can_cache >= 0 &&
1170 	gx_effective_clip_path(pgs, &pcpath) >= 0
1171 	) {
1172 	gs_fixed_rect cbox;
1173 
1174 	gx_cpath_inner_box(pcpath, &cbox);
1175 	/* Since characters occupy an integral number of pixels, */
1176 	/* we can (and should) round the inner clipping box */
1177 	/* outward rather than inward. */
1178 	penum->ibox.p.x = fixed2int_var(cbox.p.x);
1179 	penum->ibox.p.y = fixed2int_var(cbox.p.y);
1180 	penum->ibox.q.x = fixed2int_var_ceiling(cbox.q.x);
1181 	penum->ibox.q.y = fixed2int_var_ceiling(cbox.q.y);
1182 	gx_cpath_outer_box(pcpath, &cbox);
1183 	penum->obox.p.x = fixed2int_var(cbox.p.x);
1184 	penum->obox.p.y = fixed2int_var(cbox.p.y);
1185 	penum->obox.q.x = fixed2int_var_ceiling(cbox.q.x);
1186 	penum->obox.q.y = fixed2int_var_ceiling(cbox.q.y);
1187 #if 1				/*USE_FPU <= 0 */
1188 	if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) {
1189 	    penum->ftx = (int)fixed2long(pgs->char_tm.tx_fixed -
1190 					 pgs->ctm.tx_fixed);
1191 	    penum->fty = (int)fixed2long(pgs->char_tm.ty_fixed -
1192 					 pgs->ctm.ty_fixed);
1193 	} else {
1194 #endif
1195 	    double fdx = pgs->char_tm.tx - pgs->ctm.tx;
1196 	    double fdy = pgs->char_tm.ty - pgs->ctm.ty;
1197 
1198 #define int_bits (arch_sizeof_int * 8 - 1)
1199 	    if (!(f_fits_in_bits(fdx, int_bits) &&
1200 		  f_fits_in_bits(fdy, int_bits))
1201 		)
1202 		return_error(gs_error_limitcheck);
1203 #undef int_bits
1204 	    penum->ftx = (int)fdx;
1205 	    penum->fty = (int)fdy;
1206 	}
1207     }
1208     show_set_encode_char(penum);
1209     return 0;
1210 }
1211 
1212 /* Set the suggested oversampling scale for character rendering. */
1213 private void
1214 show_set_scale(gs_show_enum * penum)
1215 {
1216     /*
1217      * Decide whether to oversample.
1218      * We have to decide this each time setcachedevice is called.
1219      */
1220     const gs_state *pgs = penum->pgs;
1221 
1222     if ((penum->charpath_flag == cpm_show ||
1223 	 penum->charpath_flag == cpm_charwidth) &&
1224 	SHOW_USES_OUTLINE(penum) &&
1225 	gx_path_is_void_inline(pgs->path) &&
1226     /* Oversampling rotated characters doesn't work well. */
1227 	(is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
1228 	 is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy))
1229 	) {
1230 	const gs_font_base *pfont = (gs_font_base *) pgs->font;
1231 	gs_fixed_point extent;
1232 	int code = gs_distance_transform2fixed(&pgs->char_tm,
1233 				  pfont->FontBBox.q.x - pfont->FontBBox.p.x,
1234 				  pfont->FontBBox.q.y - pfont->FontBBox.p.y,
1235 					       &extent);
1236 
1237 	if (code >= 0) {
1238 	    int sx =
1239 	    (extent.x == 0 ? 0 :
1240 	     any_abs(extent.x) < int2fixed(25) ? 2 :
1241 	     any_abs(extent.x) < int2fixed(60) ? 1 :
1242 	     0);
1243 	    int sy =
1244 	    (extent.y == 0 ? 0 :
1245 	     any_abs(extent.y) < int2fixed(25) ? 2 :
1246 	     any_abs(extent.y) < int2fixed(60) ? 1 :
1247 	     0);
1248 
1249 	    /* If we oversample at all, make sure we do it */
1250 	    /* in both X and Y. */
1251 	    if (sx == 0 && sy != 0)
1252 		sx = 1;
1253 	    else if (sy == 0 && sx != 0)
1254 		sy = 1;
1255 	    penum->log2_suggested_scale.x = sx;
1256 	    penum->log2_suggested_scale.y = sy;
1257 	    return;
1258 	}
1259     }
1260     /* By default, don't scale. */
1261     penum->log2_suggested_scale.x =
1262 	penum->log2_suggested_scale.y = 0;
1263 }
1264 
1265 /* Set up the cache device and related information. */
1266 /* Note that we always allocate both cache devices, */
1267 /* even if we only use one of them. */
1268 private int
1269 show_cache_setup(gs_show_enum * penum)
1270 {
1271     gs_state *pgs = penum->pgs;
1272     gs_memory_t *mem = pgs->memory;
1273     gx_device_memory *dev =
1274 	gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
1275 			"show_cache_setup(dev_cache)");
1276     gx_device_memory *dev2 =
1277 	gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
1278 			"show_cache_setup(dev_cache2)");
1279 
1280     if (dev == 0 || dev2 == 0) {
1281 	gs_free_object(mem, dev2, "show_cache_setup(dev_cache2)");
1282 	gs_free_object(mem, dev, "show_cache_setup(dev_cache)");
1283 	return_error(gs_error_VMerror);
1284     }
1285     /*
1286      * We only initialize the devices for the sake of the GC,
1287      * (since we have to re-initialize dev as either a mem_mono
1288      * or a mem_abuf device before actually using it) and also
1289      * to set its memory pointer.
1290      */
1291     gs_make_mem_mono_device(dev, mem, gs_currentdevice_inline(pgs));
1292     penum->dev_cache = dev;
1293     gs_make_mem_mono_device(dev2, mem, gs_currentdevice_inline(pgs));
1294     penum->dev_cache2 = dev2;
1295     /* Retain these devices, since they are referenced from the enumerator. */
1296     gx_device_retain((gx_device *)dev, true);
1297     gx_device_retain((gx_device *)dev2, true);
1298     return 0;
1299 }
1300 
1301 /* Set the character origin as the origin of the coordinate system. */
1302 /* Used before rendering characters, and for moving the origin */
1303 /* in setcachedevice2 when WMode=1. */
1304 private int
1305 show_origin_setup(gs_state * pgs, fixed cpt_x, fixed cpt_y,
1306 		  gs_char_path_mode charpath_flag)
1307 {
1308     if (charpath_flag == cpm_show) {
1309 	/* Round the translation in the graphics state. */
1310 	/* This helps prevent rounding artifacts later. */
1311 	cpt_x = fixed_rounded(cpt_x);
1312 	cpt_y = fixed_rounded(cpt_y);
1313     }
1314     /*
1315      * BuildChar procedures expect the current point to be undefined,
1316      * so we omit the gx_path_add_point with ctm.t*_fixed.
1317      */
1318     return gx_translate_to_fixed(pgs, cpt_x, cpt_y);
1319 }
1320