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