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