/src/ghostpdl/base/gxchar.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2024 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, 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 | 42.2M | !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 | 47.7k | ENUM_PTRS_BEGIN(show_enum_enum_ptrs) |
48 | 32.8k | return ENUM_USING(st_gs_text_enum, vptr, size, index - 5); |
49 | 2.98k | ENUM_PTR(0, gs_show_enum, pgs); |
50 | 2.98k | ENUM_PTR(1, gs_show_enum, show_gstate); |
51 | 47.7k | ENUM_PTR3(2, gs_show_enum, dev_cache, dev_cache2, dev_null); |
52 | 47.7k | ENUM_PTRS_END |
53 | 2.98k | static RELOC_PTRS_WITH(show_enum_reloc_ptrs, gs_show_enum *eptr) |
54 | 2.98k | { |
55 | 2.98k | RELOC_USING(st_gs_text_enum, vptr, size); /* superclass */ |
56 | 2.98k | RELOC_VAR(eptr->pgs); |
57 | 2.98k | RELOC_VAR(eptr->show_gstate); |
58 | 2.98k | RELOC_PTR3(gs_show_enum, dev_cache, dev_cache2, dev_null); |
59 | 2.98k | } |
60 | 2.98k | 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 | 2.61M | #define CURRENT_CHAR(penum) ((penum)->returned.current_char) |
73 | | #define SET_CURRENT_CHAR(penum, chr)\ |
74 | 62.3M | ((penum)->returned.current_char = (chr)) |
75 | 7.13M | #define CURRENT_GLYPH(penum) ((penum)->returned.current_glyph) |
76 | | #define SET_CURRENT_GLYPH(penum, glyph)\ |
77 | 62.3M | ((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 | 16.8M | { |
83 | 16.8M | gs_show_enum *penum; |
84 | | |
85 | 16.8M | rc_alloc_struct_1(penum, gs_show_enum, &st_gs_show_enum, mem, |
86 | 16.8M | return 0, cname); |
87 | 16.8M | penum->rc.free = rc_free_text_enum; |
88 | 16.8M | penum->auto_release = true; /* old API */ |
89 | | /* Initialize pointers for GC */ |
90 | 16.8M | penum->text.operation = 0; /* no pointers relevant */ |
91 | 16.8M | penum->dev = 0; |
92 | 16.8M | penum->pgs = pgs; |
93 | 16.8M | penum->show_gstate = 0; |
94 | 16.8M | penum->dev_cache = 0; |
95 | 16.8M | penum->dev_cache2 = 0; |
96 | 16.8M | penum->fapi_log2_scale.x = penum->fapi_log2_scale.y = -1; |
97 | 16.8M | penum->fapi_glyph_shift.x = penum->fapi_glyph_shift.y = 0; |
98 | 16.8M | penum->dev_null = 0; |
99 | 16.8M | penum->fstack.depth = -1; |
100 | 16.8M | return penum; |
101 | 16.8M | } |
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 | 16.8M | { |
126 | 16.8M | uint operation = text->operation; |
127 | 16.8M | bool propagate_charpath = (operation & TEXT_DO_DRAW) != 0; |
128 | 16.8M | int code; |
129 | 16.8M | gs_gstate *pgs = (gs_gstate *)pgs1; |
130 | 16.8M | gs_show_enum *penum; |
131 | 16.8M | gs_memory_t * mem = pgs->memory; |
132 | | |
133 | 16.8M | penum = gs_show_enum_alloc(mem, pgs, "gx_default_text_begin"); |
134 | 16.8M | if (!penum) |
135 | 0 | return_error(gs_error_VMerror); |
136 | 16.8M | code = gs_text_enum_init((gs_text_enum_t *)penum, &default_text_procs, |
137 | 16.8M | dev, pgs, text, font, pcpath, mem); |
138 | 16.8M | if (code < 0) { |
139 | 0 | gs_free_object(mem, penum, "gx_default_text_begin"); |
140 | 0 | return code; |
141 | 0 | } |
142 | 16.8M | penum->auto_release = false; /* new API */ |
143 | 16.8M | penum->level = pgs->level; |
144 | 16.8M | penum->cc = 0; |
145 | 16.8M | penum->continue_proc = continue_show; |
146 | 16.8M | switch (penum->charpath_flag) { |
147 | 17.5k | case cpm_false_charpath: case cpm_true_charpath: |
148 | 17.5k | 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 | 16.8M | default: /* cpm_show */ |
153 | 16.8M | penum->can_cache = 1; break; |
154 | 16.8M | } |
155 | 16.8M | code = show_state_setup(penum); |
156 | 16.8M | if (code < 0) { |
157 | 2.16k | gs_text_release(pgs, (gs_text_enum_t *)penum, "gx_default_text_begin"); |
158 | 2.16k | penum = NULL; |
159 | 2.16k | return code; |
160 | 2.16k | } |
161 | 16.8M | penum->show_gstate = |
162 | 16.8M | (propagate_charpath && (pgs->in_charpath != 0) ? |
163 | 16.8M | pgs->show_gstate : pgs); |
164 | 16.8M | if (!(~operation & (TEXT_DO_NONE | TEXT_RETURN_WIDTH))) { |
165 | | /* This is stringwidth (or a PDF with text in rendering mode 3) . */ |
166 | 198k | gx_device_null *dev_null = |
167 | 198k | gs_alloc_struct(mem, gx_device_null, &st_device_null, |
168 | 198k | "stringwidth(dev_null)"); |
169 | | |
170 | 198k | 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 | 198k | gs_make_null_device(dev_null, gs_currentdevice_inline(pgs), mem); |
181 | | |
182 | | /* Do an extra gsave and suppress output */ |
183 | 198k | 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 | 198k | penum->level = pgs->level; /* for level check in show_update */ |
190 | 198k | pgs->ctm_default_set = false; |
191 | 198k | penum->dev_null = dev_null; |
192 | | /* Retain this device, since it is referenced from the enumerator. */ |
193 | 198k | gx_device_retain((gx_device *)dev_null, true); |
194 | 198k | gs_setdevice_no_init(pgs, (gx_device *) dev_null); |
195 | | /* Establish an arbitrary translation and current point. */ |
196 | 198k | gs_newpath(pgs); |
197 | 198k | gx_translate_to_fixed(pgs, fixed_0, fixed_0); |
198 | 198k | code = gx_path_add_point(pgs->path, fixed_0, fixed_0); |
199 | 198k | 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 | 198k | } |
206 | 16.8M | *ppte = (gs_text_enum_t *)penum; |
207 | 16.8M | return 0; |
208 | 16.8M | } |
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 | 31 | { |
248 | 31 | int code; |
249 | | |
250 | 31 | code = gs_gsave(pgs); |
251 | 31 | if (code < 0) |
252 | 0 | return code; |
253 | 31 | gs_newpath(pgs); |
254 | 31 | *path = pgs->path; |
255 | 31 | gx_translate_to_fixed(pgs, fixed_0, fixed_0); |
256 | 31 | return gx_path_add_point(pgs->path, fixed_0, fixed_0); |
257 | 31 | } |
258 | | |
259 | | int |
260 | | gx_default_text_restore_state(gs_text_enum_t *pte) |
261 | 98.6k | { |
262 | 98.6k | gs_show_enum *penum; |
263 | 98.6k | gs_gstate *pgs; |
264 | | |
265 | 98.6k | if (SHOW_IS(pte, TEXT_DO_NONE)) |
266 | 23 | return 0; |
267 | 98.5k | penum = (gs_show_enum *)pte; |
268 | 98.5k | pgs = penum->pgs; |
269 | 98.5k | return gs_grestore(pgs); |
270 | 98.6k | } |
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 | 16.2M | { |
282 | 16.2M | gs_show_enum *const penum = (gs_show_enum *)pte; |
283 | 16.2M | gs_gstate *pgs = penum->pgs; |
284 | 16.2M | 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 | 16.2M | if (pfont->FontMatrix.xx == 0 && pfont->FontMatrix.xy == 0 && |
290 | 16.2M | pfont->FontMatrix.yx == 0 && pfont->FontMatrix.yy == 0) |
291 | 5 | return_error(gs_error_undefinedresult); /* sic! : CPSI compatibility */ |
292 | 16.2M | switch (control) { |
293 | 253k | case TEXT_SET_CHAR_WIDTH: |
294 | 253k | return set_char_width(penum, pgs, pw[0], pw[1]); |
295 | 2.10M | case TEXT_SET_CACHE_DEVICE: { |
296 | 2.10M | int code = set_char_width(penum, pgs, pw[0], pw[1]); /* default is don't cache */ |
297 | | |
298 | 2.10M | if (code < 0) |
299 | 34 | return code; |
300 | 2.10M | if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) /* cshow */ |
301 | 0 | return code; |
302 | 2.10M | return set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]); |
303 | 2.10M | } |
304 | 13.8M | case TEXT_SET_CACHE_DEVICE2: { |
305 | 13.8M | int code; |
306 | 13.8M | bool retry = (penum->width_status == sws_retry); |
307 | | |
308 | 13.8M | if (pfont->WMode) { |
309 | 6.69k | float vx = pw[8], vy = pw[9]; |
310 | 6.69k | gs_fixed_point pvxy, dvxy; |
311 | | |
312 | 6.69k | gs_fixed_point rewind_pvxy; |
313 | 6.69k | int rewind_code; |
314 | | |
315 | 6.69k | if ((code = gs_point_transform2fixed(&pgs->ctm, -vx, -vy, &pvxy)) < 0 || |
316 | 6.69k | (code = gs_distance_transform2fixed(&pgs->ctm, vx, vy, &dvxy)) < 0 |
317 | 6.69k | ) |
318 | 85 | return 0; /* don't cache */ |
319 | 6.61k | if ((code = set_char_width(penum, pgs, pw[6], pw[7])) < 0) |
320 | 0 | return code; |
321 | 6.61k | if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) |
322 | 0 | return code; |
323 | | /* Adjust the origin by (vx, vy). */ |
324 | 6.61k | gx_translate_to_fixed(pgs, pvxy.x, pvxy.y); |
325 | 6.61k | code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]); |
326 | 6.61k | if (code != 1) { |
327 | 5.62k | 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 | 5.62k | return code; |
337 | 5.62k | } |
338 | | /* Adjust the character origin too. */ |
339 | 982 | (penum->cc)->offset.x += dvxy.x; |
340 | 982 | (penum->cc)->offset.y += dvxy.y; |
341 | 13.8M | } else { |
342 | 13.8M | code = set_char_width(penum, pgs, pw[0], pw[1]); |
343 | 13.8M | if (code < 0) |
344 | 0 | return code; |
345 | 13.8M | if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) |
346 | 0 | return code; |
347 | 13.8M | code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]); |
348 | 13.8M | } |
349 | 13.8M | return code; |
350 | 13.8M | } |
351 | 0 | default: |
352 | 0 | return_error(gs_error_rangecheck); |
353 | 16.2M | } |
354 | 16.2M | } |
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 | 16.2M | { |
362 | 16.2M | int code; |
363 | | |
364 | 16.2M | if (penum->width_status != sws_none && penum->width_status != sws_retry) |
365 | 37 | return_error(gs_error_undefined); |
366 | 16.2M | code = gs_distance_transform2fixed(&pgs->ctm, wx, wy, &penum->wxy); |
367 | 16.2M | if (code < 0 && penum->cc == 0) { |
368 | | /* Can't represent in 'fixed', use floats. */ |
369 | 349k | code = gs_distance_transform(wx, wy, &ctm_only(pgs), &penum->wxy_float); |
370 | 349k | penum->wxy.x = penum->wxy.y = 0; |
371 | 349k | penum->use_wxy_float = true; |
372 | 15.9M | } else { |
373 | 15.9M | penum->use_wxy_float = false; |
374 | 15.9M | penum->wxy_float.x = penum->wxy_float.y = 0; |
375 | 15.9M | } |
376 | 16.2M | if (code < 0) |
377 | 0 | return code; |
378 | | /* Check whether we're setting the scalable width */ |
379 | | /* for a cached xfont character. */ |
380 | 16.2M | if (penum->cc != 0) { |
381 | 0 | penum->cc->wxy = penum->wxy; |
382 | 0 | penum->width_status = sws_cache_width_only; |
383 | 16.2M | } else { |
384 | 16.2M | penum->width_status = sws_no_cache; |
385 | 16.2M | } |
386 | 16.2M | if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) /* cshow */ |
387 | 0 | gs_nulldevice(pgs); |
388 | 16.2M | return !SHOW_IS_DRAWING(penum); |
389 | 16.2M | } |
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 | 61.6M | { |
395 | 61.6M | gs_log2_scale_point log2_scale; |
396 | | |
397 | 61.6M | if (alpha_bits == 1) |
398 | 61.6M | 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 | 61.6M | *p_log2_scale = log2_scale; |
426 | 61.6M | } |
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 | 69.1M | { |
434 | 69.1M | gs_gstate *pgs = penum->pgs; |
435 | 69.1M | gx_device *dev = gs_currentdevice_inline(pgs); |
436 | 69.1M | int code; |
437 | | |
438 | 69.1M | *alpha_bits = (*dev_proc(dev, get_alpha_bits)) (dev, go_text); |
439 | 69.1M | if (in_setcachedevice) { |
440 | | /* current point should already be in penum->origin */ |
441 | 62.3M | } else { |
442 | 62.3M | code = gx_path_current_point_inline(pgs, &penum->origin); |
443 | 62.3M | 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 | 62.3M | } |
450 | 69.1M | if (penum->fapi_log2_scale.x != -1) |
451 | 22.9M | *log2_scale = penum->fapi_log2_scale; |
452 | 46.2M | else |
453 | 46.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 | 69.1M | *depth = (log2_scale->x + log2_scale->y == 0 ? |
460 | 69.1M | 1 : min(log2_scale->x + log2_scale->y, *alpha_bits)); |
461 | 69.1M | if (gs_currentaligntopixels(penum->current_font->dir) == 0) { |
462 | 69.1M | int scx = -(1L << (_fixed_shift - log2_scale->x)); |
463 | 69.1M | int rdx = 1L << (_fixed_shift - 1 - log2_scale->x); |
464 | | |
465 | 69.1M | # if 1 /* Ever align Y to pixels to provide an uniform glyph height. */ |
466 | 69.1M | 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 | 69.1M | subpix_origin->x = ((penum->origin.x + rdx) & scx) & (fixed_1 - 1); |
474 | 69.1M | } else |
475 | 0 | subpix_origin->x = subpix_origin->y = 0; |
476 | 69.1M | return 0; |
477 | 69.1M | } |
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 | 16.0M | { |
486 | 16.0M | gs_glyph glyph; |
487 | 16.0M | int code = 0; |
488 | | |
489 | | /* See if we want to cache this character. */ |
490 | 16.0M | if (pgs->in_cachedevice) /* no recursion! */ |
491 | 8.86M | return 0; |
492 | 7.13M | 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 | 7.13M | 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 | 7.13M | glyph = CURRENT_GLYPH(penum); |
502 | 7.13M | 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 | 7.13M | if (penum->can_cache <= 0 || !pgs->char_tm_valid) { |
507 | 3 | if_debug2m('k', penum->memory, "[k]no cache: can_cache=%d, char_tm_valid=%d\n", |
508 | 3 | penum->can_cache, (int)pgs->char_tm_valid); |
509 | 3 | return 0; |
510 | 7.13M | } { |
511 | 7.13M | const gs_font *pfont = pgs->font; |
512 | 7.13M | gs_font_dir *dir = pfont->dir; |
513 | 7.13M | int alpha_bits, depth; |
514 | 7.13M | gs_log2_scale_point log2_scale; |
515 | 7.13M | gs_fixed_point subpix_origin; |
516 | 7.13M | static const fixed max_cdim[3] = |
517 | 7.13M | { |
518 | 7.13M | #define max_cd(n)\ |
519 | 21.4M | (fixed_1 << (ARCH_SIZEOF_SHORT * 8 - n)) - (fixed_1 >> n) * 3 |
520 | 7.13M | max_cd(0), max_cd(1), max_cd(2) |
521 | 7.13M | #undef max_cd |
522 | 7.13M | }; |
523 | 7.13M | ushort iwidth, iheight; |
524 | 7.13M | cached_char *cc; |
525 | 7.13M | gs_fixed_rect clip_box; |
526 | 7.13M | 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 | 7.13M | if (fabs(llx) > 32000. || fabs(lly) > 32000. || fabs(urx) > 32000. || fabs(ury) >= 32000.) |
532 | 168k | 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 | 6.96M | if ((code = gs_distance_transform2fixed(&pgs->ctm, llx, lly, &cll)) < 0 || |
541 | 6.96M | (code = gs_distance_transform2fixed(&pgs->ctm, llx, ury, &clr)) < 0 || |
542 | 6.96M | (code = gs_distance_transform2fixed(&pgs->ctm, urx, lly, &cul)) < 0 || |
543 | 6.96M | (code = gs_distance_transform2fixed(&pgs->ctm, urx, ury, &cur)) < 0 |
544 | 6.96M | ) |
545 | 126k | return 0; /* don't cache */ |
546 | 6.84M | { |
547 | 6.84M | fixed ctemp; |
548 | | |
549 | 7.03M | #define swap(a, b) ctemp = a, a = b, b = ctemp |
550 | 27.3M | #define make_min(a, b) if ( (a) > (b) ) swap(a, b) |
551 | | |
552 | 6.84M | make_min(cll.x, cur.x); |
553 | 6.84M | make_min(cll.y, cur.y); |
554 | 6.84M | make_min(clr.x, cul.x); |
555 | 6.84M | make_min(clr.y, cul.y); |
556 | 6.84M | #undef make_min |
557 | 6.84M | #undef swap |
558 | 6.84M | } |
559 | | /* Now take advantage of symmetry. */ |
560 | 6.84M | if (clr.x < cll.x) |
561 | 21.2k | cll.x = clr.x, cur.x = cul.x; |
562 | 6.84M | if (clr.y < cll.y) |
563 | 72.4k | cll.y = clr.y, cur.y = cul.y; |
564 | | /* Now cll and cur are the extrema of the box. */ |
565 | 6.84M | code = compute_glyph_raster_params(penum, true, &alpha_bits, &depth, |
566 | 6.84M | &subpix_origin, &log2_scale); |
567 | 6.84M | 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 | 6.84M | cdim.x = cur.x - cll.x; |
581 | 6.84M | cdim.y = cur.y - cll.y; |
582 | 6.84M | if (cdim.x > max_cdim[log2_scale.x] || |
583 | 6.84M | cdim.y > max_cdim[log2_scale.y] |
584 | 6.84M | ) |
585 | 109k | return 0; /* much too big */ |
586 | 6.73M | iwidth = ((ushort) fixed2int_var(cdim.x) + 3) << log2_scale.x; |
587 | 6.73M | iheight = ((ushort) fixed2int_var(cdim.y) + 3) << log2_scale.y; |
588 | 6.73M | if_debug3m('k', penum->memory, "[k]iwidth=%u iheight=%u dev_cache %s\n", |
589 | 6.73M | (uint) iwidth, (uint) iheight, |
590 | 6.73M | (penum->dev_cache == 0 ? "not set" : "set")); |
591 | 6.73M | if (penum->dev_cache == 0) { |
592 | 2.61M | code = show_cache_setup(penum); |
593 | 2.61M | if (code < 0) |
594 | 0 | return code; |
595 | 2.61M | } |
596 | 6.73M | code = gx_alloc_char_bits(dir, penum->dev_cache, |
597 | 6.73M | iwidth, iheight, &log2_scale, depth, &cc); |
598 | 6.73M | if (code < 0) |
599 | 52 | return code; |
600 | | |
601 | 6.73M | if (cc == 0) { |
602 | | /* too big for cache or no cache */ |
603 | 577k | gx_path box_path; |
604 | | |
605 | 577k | if (penum->current_font->FontType != ft_user_defined && |
606 | 577k | penum->current_font->FontType != ft_PDF_user_defined && |
607 | 577k | penum->current_font->FontType != ft_PCL_user_defined && |
608 | 577k | penum->current_font->FontType != ft_GL2_stick_user_defined && |
609 | 577k | penum->current_font->FontType != ft_CID_user_defined) { |
610 | | /* Most fonts don't paint outside bbox, |
611 | | so render with no clipping. */ |
612 | 311k | return 0; |
613 | 311k | } |
614 | | /* Render with a clip. */ |
615 | | /* show_proceed already did gsave. */ |
616 | 265k | pgs->in_cachedevice = CACHE_DEVICE_NONE; /* Provide a correct grestore on error. */ |
617 | 265k | clip_box.p.x = penum->origin.x - fixed_ceiling(-cll.x); |
618 | 265k | clip_box.p.y = penum->origin.y - fixed_ceiling(-cll.y); |
619 | 265k | clip_box.q.x = clip_box.p.x + int2fixed(iwidth); |
620 | 265k | clip_box.q.y = clip_box.p.y + int2fixed(iheight); |
621 | 265k | gx_path_init_local(&box_path, pgs->memory); |
622 | 265k | code = gx_path_add_rectangle(&box_path, clip_box.p.x, clip_box.p.y, |
623 | 265k | clip_box.q.x, clip_box.q.y); |
624 | 265k | if (code < 0) |
625 | 0 | return code; |
626 | 265k | code = gx_cpath_clip(pgs, pgs->clip_path, &box_path, gx_rule_winding_number); |
627 | 265k | if (code < 0) |
628 | 0 | return code; |
629 | 265k | gx_path_free(&box_path, "set_cache_device"); |
630 | 265k | pgs->in_cachedevice = CACHE_DEVICE_NONE_AND_CLIP; |
631 | 265k | return 0; |
632 | 265k | } |
633 | | /* The mins handle transposed coordinate systems.... */ |
634 | | /* Truncate the offsets to avoid artifacts later. */ |
635 | 6.15M | cc->offset.x = fixed_ceiling(-cll.x) + fixed_1; |
636 | 6.15M | cc->offset.y = fixed_ceiling(-cll.y) + fixed_1; |
637 | 6.15M | if_debug4m('k', penum->memory, "[k]width=%u, height=%u, offset=[%g %g]\n", |
638 | 6.15M | (uint) iwidth, (uint) iheight, |
639 | 6.15M | fixed2float(cc->offset.x), |
640 | 6.15M | fixed2float(cc->offset.y)); |
641 | 6.15M | pgs->in_cachedevice = CACHE_DEVICE_NONE; /* Provide correct grestore */ |
642 | 6.15M | 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 | 6.15M | penum->cc = cc; |
648 | 6.15M | cc->code = glyph; |
649 | 6.15M | cc->wmode = gs_rootfont(pgs)->WMode; |
650 | 6.15M | cc->wxy = penum->wxy; |
651 | 6.15M | cc->subpix_origin = subpix_origin; |
652 | 6.15M | if (penum->pair != 0) |
653 | 6.15M | cc_set_pair(cc, penum->pair); |
654 | 0 | else |
655 | 0 | cc->pair = 0; |
656 | | /* Install the device */ |
657 | 6.15M | gx_set_device_only(pgs, (gx_device *) penum->dev_cache); |
658 | 6.15M | pgs->ctm_default_set = false; |
659 | | /* Adjust the transformation in the graphics context */ |
660 | | /* so that the character lines up with the cache. */ |
661 | 6.15M | gx_translate_to_fixed(pgs, |
662 | 6.15M | (cc->offset.x + subpix_origin.x) << log2_scale.x, |
663 | 6.15M | (cc->offset.y + subpix_origin.y) << log2_scale.y); |
664 | 6.15M | 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 | 6.15M | penum->dev_cache->initial_matrix = ctm_only(pgs); |
669 | | /* Set the oversampling factor. */ |
670 | 6.15M | penum->log2_scale.x = log2_scale.x; |
671 | 6.15M | penum->log2_scale.y = log2_scale.y; |
672 | | /* Reset the clipping path to match the metrics. */ |
673 | 6.15M | clip_box.p.x = clip_box.p.y = 0; |
674 | 6.15M | clip_box.q.x = int2fixed(iwidth); |
675 | 6.15M | clip_box.q.y = int2fixed(iheight); |
676 | 6.15M | if ((code = gx_clip_to_rectangle(pgs, &clip_box)) < 0) |
677 | 0 | goto fail; |
678 | 6.15M | code = gx_set_device_color_1(pgs); /* write 1's */ |
679 | 6.15M | if (code < 0) |
680 | 0 | goto fail; |
681 | 6.15M | gs_swapcolors_quick(pgs); |
682 | 6.15M | code = gx_set_device_color_1(pgs); /* write 1's */ |
683 | 6.15M | if (code < 0) |
684 | 0 | goto fail; |
685 | 6.15M | gs_swapcolors_quick(pgs); |
686 | 6.15M | pgs->in_cachedevice = CACHE_DEVICE_CACHING; |
687 | 6.15M | } |
688 | 0 | penum->width_status = sws_cache; |
689 | 6.15M | return 1; |
690 | | |
691 | 0 | fail: |
692 | 0 | gs_grestore(pgs); |
693 | 0 | return code; |
694 | 6.15M | } |
695 | | |
696 | | /* Return the cache device status. */ |
697 | | gs_in_cache_device_t |
698 | | gs_incachedevice(const gs_gstate *pgs) |
699 | 589k | { |
700 | 589k | return pgs->in_cachedevice; |
701 | 589k | } |
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 | 18.6M | { |
711 | 18.6M | penum->encode_char = |
712 | 18.6M | (SHOW_IS(penum, TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH) ? |
713 | 0 | gs_no_encode_char : |
714 | 18.6M | gs_show_current_font(penum)->procs.encode_char); |
715 | 18.6M | } |
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 | 19.2M | { |
741 | 19.2M | gs_show_enum *const penum = (gs_show_enum *)pte; |
742 | | |
743 | 19.2M | return (*penum->continue_proc)(penum); |
744 | 19.2M | } |
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 | 2.32M | { |
754 | 2.32M | int code = show_update(penum); |
755 | | |
756 | 2.32M | if (code < 0) |
757 | 0 | return code; |
758 | 2.32M | code = show_move(penum); |
759 | 2.32M | if (code != 0) |
760 | 5 | return code; |
761 | 2.32M | return show_proceed(penum); |
762 | 2.32M | } |
763 | | static int |
764 | | continue_show(gs_show_enum * penum) |
765 | 16.8M | { |
766 | 16.8M | return show_proceed(penum); |
767 | 16.8M | } |
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 | 16.2M | { |
789 | 16.2M | gs_gstate *pgs = penum->pgs; |
790 | 16.2M | cached_char *cc = penum->cc; |
791 | 16.2M | int code; |
792 | | |
793 | | /* Update position for last character */ |
794 | 16.2M | switch (penum->width_status) { |
795 | 40.1k | case sws_none: |
796 | 40.1k | case sws_retry: |
797 | | /* Adobe interpreters assume a character width of 0, */ |
798 | | /* even though the documentation says this is an error.... */ |
799 | 40.1k | penum->wxy.x = penum->wxy.y = 0; |
800 | 40.1k | penum->wxy_float.x = penum->wxy_float.y = 0; |
801 | 40.1k | penum->use_wxy_float = false; |
802 | 40.1k | break; |
803 | 6.15M | 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 | 6.15M | 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 | 6.15M | case 2: |
813 | 6.15M | code = gs_grestore(pgs); |
814 | 6.15M | if (code < 0) |
815 | 0 | return code; |
816 | 6.15M | case 1: |
817 | 6.15M | ; |
818 | 6.15M | } |
819 | 6.15M | { cached_fm_pair *pair; |
820 | | |
821 | 6.15M | code = gx_lookup_fm_pair(pgs->font, &char_tm_only(pgs), |
822 | 6.15M | &penum->log2_scale, penum->charpath_flag != cpm_show, &pair); |
823 | 6.15M | if (code < 0) |
824 | 0 | return code; |
825 | 6.15M | code = gx_add_cached_char(pgs->font->dir, penum->dev_cache, |
826 | 6.15M | cc, pair, &penum->log2_scale); |
827 | 6.15M | if (code < 0) |
828 | 0 | return code; |
829 | 6.15M | } |
830 | 6.15M | if (!SHOW_USES_OUTLINE(penum) || |
831 | 6.15M | penum->charpath_flag != cpm_show |
832 | 6.15M | ) |
833 | 46.3k | break; |
834 | | /* falls through */ |
835 | 6.10M | case sws_cache_width_only: |
836 | | /* Copy the bits to the real output device. */ |
837 | 6.10M | code = gs_grestore(pgs); |
838 | 6.10M | if (code < 0) |
839 | 0 | return code; |
840 | 6.10M | code = gs_gstate_color_load(pgs); |
841 | 6.10M | if (code < 0) |
842 | 0 | return code; |
843 | 6.10M | return gx_image_cached_char(penum, cc); |
844 | 10.0M | case sws_no_cache: |
845 | 10.0M | ; |
846 | 16.2M | } |
847 | 10.1M | if (penum->charpath_flag != cpm_show) { |
848 | 1.01M | if (pgs->level <= penum->level) { |
849 | 0 | return_error(gs_error_invalidfont); |
850 | 0 | } |
851 | | /* Move back to the character origin, so that */ |
852 | | /* show_move will get us to the right place. */ |
853 | 1.01M | code = gx_path_add_point(pgs->show_gstate->path, |
854 | 1.01M | penum->origin.x, penum->origin.y); |
855 | 1.01M | if (code < 0) |
856 | 0 | return code; |
857 | 1.01M | } |
858 | 10.1M | return gs_grestore(pgs); |
859 | 10.1M | } |
860 | | |
861 | | /* Move to next character */ |
862 | | static inline int |
863 | | show_fast_move(gs_gstate * pgs, gs_fixed_point * pwxy) |
864 | 61.8M | { |
865 | 61.8M | return gs_moveto_aux(pgs, pgs->path, |
866 | 61.8M | pgs->current_point.x + fixed2float(pwxy->x), |
867 | 61.8M | pgs->current_point.y + fixed2float(pwxy->y)); |
868 | 61.8M | } |
869 | | |
870 | | /* Get the current character code. */ |
871 | | int gx_current_char(const gs_text_enum_t * pte) |
872 | 2.53M | { |
873 | 2.53M | const gs_show_enum *penum = (const gs_show_enum *)pte; |
874 | 2.53M | gs_char chr = CURRENT_CHAR(penum) & 0xff; |
875 | 2.53M | int fdepth = penum->fstack.depth; |
876 | | |
877 | 2.53M | if (fdepth > 0) { |
878 | | /* Add in the shifted font number. */ |
879 | 79.5k | uint fidx = penum->fstack.items[fdepth - 1].index; |
880 | | |
881 | 79.5k | switch (((gs_font_type0 *) (penum->fstack.items[fdepth - 1].font))->data.FMapType) { |
882 | 0 | case fmap_1_7: |
883 | 0 | case fmap_9_7: |
884 | 0 | chr += fidx << 7; |
885 | 0 | break; |
886 | 79.5k | case fmap_CMap: |
887 | 79.5k | chr = CURRENT_CHAR(penum); /* the full character */ |
888 | 79.5k | if (!penum->cmap_code) |
889 | 79.5k | break; |
890 | | /* falls through */ |
891 | 0 | default: |
892 | 0 | chr += fidx << 8; |
893 | 79.5k | } |
894 | 79.5k | } |
895 | 2.53M | return chr; |
896 | 2.53M | } |
897 | | |
898 | | static int |
899 | | show_move(gs_show_enum * penum) |
900 | 45.3M | { |
901 | 45.3M | gs_gstate *pgs = penum->pgs; |
902 | 45.3M | int code; |
903 | 45.3M | double dx = 0, dy = 0; |
904 | | |
905 | | /* Specifically for applying PDF word spacing, if single_byte_space == true |
906 | | we'll only apply the delta for single byte character codes == space.s_char |
907 | | See psi/zchar.c zpdfwidthshow and zpdfawidthshow for more detail |
908 | | */ |
909 | 45.3M | if (SHOW_IS_ADD_TO_SPACE(penum) |
910 | 45.3M | && (!penum->single_byte_space |
911 | 2.75M | || penum->bytes_decoded == 1)) { |
912 | 2.53M | gs_char chr = gx_current_char((const gs_text_enum_t *)penum); |
913 | | |
914 | 2.53M | if (chr == penum->text.space.s_char) { |
915 | 192k | dx = penum->text.delta_space.x; |
916 | 192k | dy = penum->text.delta_space.y; |
917 | 192k | } |
918 | 2.53M | } |
919 | | |
920 | 45.3M | if (SHOW_IS(penum, TEXT_REPLACE_WIDTHS)) { |
921 | 30.7M | gs_point dpt; |
922 | | |
923 | 30.7M | code = gs_text_replaced_width(&penum->text, penum->xy_index - 1, &dpt); |
924 | 30.7M | if (code < 0) |
925 | 0 | return code; |
926 | 30.7M | dpt.x += dx; |
927 | 30.7M | dpt.y += dy; |
928 | 30.7M | code = gs_distance_transform2fixed(&pgs->ctm, dpt.x, dpt.y, &penum->wxy); |
929 | 30.7M | if (code < 0) |
930 | 2.05k | return code; |
931 | 30.7M | } else { |
932 | 14.5M | if (SHOW_IS_ADD_TO_ALL(penum)) { |
933 | 4.47M | dx += penum->text.delta_all.x; |
934 | 4.47M | dy += penum->text.delta_all.y; |
935 | 4.47M | } |
936 | 14.5M | if (!is_fzero2(dx, dy)) { |
937 | 4.49M | gs_fixed_point dxy; |
938 | | |
939 | 4.49M | code = gs_distance_transform2fixed(&pgs->ctm, dx, dy, &dxy); |
940 | 4.49M | if (code < 0) |
941 | 1.80k | return code; |
942 | 4.49M | penum->wxy.x += dxy.x; |
943 | 4.49M | penum->wxy.y += dxy.y; |
944 | 4.49M | } |
945 | 14.5M | } |
946 | 45.3M | if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) { |
947 | | /* HACK for cshow */ |
948 | 0 | penum->continue_proc = continue_kshow; |
949 | 0 | return TEXT_PROCESS_INTERVENE; |
950 | 0 | } |
951 | | /* wxy is in device coordinates */ |
952 | 45.3M | { |
953 | 45.3M | int code; |
954 | | |
955 | 45.3M | if (penum->use_wxy_float) |
956 | 345k | code = gs_moveto_aux(pgs, pgs->path, |
957 | 345k | pgs->current_point.x + penum->wxy_float.x + fixed2float(penum->wxy.x), |
958 | 345k | pgs->current_point.y + penum->wxy_float.y + fixed2float(penum->wxy.y)); |
959 | 44.9M | else |
960 | 44.9M | code = show_fast_move(pgs, &penum->wxy); |
961 | 45.3M | if (code < 0) |
962 | 0 | return code; |
963 | 45.3M | } |
964 | | /* Check for kerning, but not on the last character. */ |
965 | 45.3M | if (SHOW_IS_DO_KERN(penum) && penum->index < penum->text.size) { |
966 | 0 | penum->continue_proc = continue_kshow; |
967 | 0 | return TEXT_PROCESS_INTERVENE; |
968 | 0 | } |
969 | 45.3M | return 0; |
970 | 45.3M | } |
971 | | |
972 | | static inline int |
973 | | get_next_char_glyph(gs_show_enum * penum, gs_char *chr, gs_glyph *glyph) |
974 | 79.1M | { |
975 | 79.1M | gs_font *rfont = |
976 | 79.1M | (penum->fstack.depth < 0 ? penum->pgs->font : penum->fstack.items[0].font); |
977 | 79.1M | penum->xy_index++; |
978 | | |
979 | 79.1M | return rfont->procs.next_char_glyph((gs_text_enum_t *)penum, chr, glyph); |
980 | 79.1M | } |
981 | | |
982 | | |
983 | | /* Process next character */ |
984 | | static int |
985 | | show_proceed(gs_show_enum * penum) |
986 | 19.2M | { |
987 | 19.2M | gs_gstate *pgs = penum->pgs; |
988 | 19.2M | gs_font *pfont; |
989 | 19.2M | cached_fm_pair *pair = 0; |
990 | 19.2M | gs_font *rfont = |
991 | 19.2M | (penum->fstack.depth < 0 ? pgs->font : penum->fstack.items[0].font); |
992 | 19.2M | int wmode = rfont->WMode; |
993 | 19.2M | gs_char chr; |
994 | 19.2M | gs_glyph glyph; |
995 | 19.2M | int code, start; |
996 | 19.2M | cached_char *cc; |
997 | 19.2M | gs_log2_scale_point log2_scale; |
998 | | |
999 | 19.2M | if (penum->charpath_flag == cpm_show && SHOW_USES_OUTLINE(penum)) { |
1000 | 17.9M | code = gs_gstate_color_load(pgs); |
1001 | 17.9M | if (code < 0) |
1002 | 0 | return code; |
1003 | 17.9M | } |
1004 | 33.1M | more: /* Proceed to next character */ |
1005 | 33.1M | pfont = (penum->fstack.depth < 0 ? pgs->font : |
1006 | 33.1M | penum->fstack.items[penum->fstack.depth].font); |
1007 | 33.1M | penum->current_font = pfont; |
1008 | | /* can_cache >= 0 allows us to use cached characters, */ |
1009 | | /* even if we can't make new cache entries. */ |
1010 | 33.1M | if (penum->can_cache >= 0) { |
1011 | | /* Loop with cache */ |
1012 | 78.0M | for (;;) { |
1013 | 78.0M | start = penum->index; |
1014 | 78.0M | switch ((code = get_next_char_glyph(penum, &chr, &glyph))) { |
1015 | 27 | default: /* error */ |
1016 | 27 | return code; |
1017 | 16.7M | case 2: /* done */ |
1018 | 16.7M | return show_finish(penum); |
1019 | 1.74M | case 1: /* font change */ |
1020 | 1.74M | pfont = penum->fstack.items[penum->fstack.depth].font; |
1021 | 1.74M | penum->current_font = pfont; |
1022 | 1.74M | pgs->char_tm_valid = false; |
1023 | 1.74M | show_state_setup(penum); |
1024 | 1.74M | pair = 0; |
1025 | 1.74M | penum->pair = 0; |
1026 | | /* falls through */ |
1027 | 61.3M | case 0: /* plain char */ |
1028 | | /* |
1029 | | * We don't need to set penum->current_char in the |
1030 | | * normal cases, but it's needed for widthshow, |
1031 | | * kshow, and one strange client, so we may as well |
1032 | | * do it here. |
1033 | | */ |
1034 | 61.3M | SET_CURRENT_CHAR(penum, chr); |
1035 | | /* |
1036 | | * Store glyph now, because pdfwrite needs it while |
1037 | | * synthezising bitmap fonts (see assign_char_code). |
1038 | | */ |
1039 | 61.3M | if (glyph == GS_NO_GLYPH) { |
1040 | 56.2M | glyph = (*penum->encode_char)(pfont, chr, |
1041 | 56.2M | GLYPH_SPACE_NAME); |
1042 | 56.2M | SET_CURRENT_GLYPH(penum, glyph); |
1043 | 56.2M | } else |
1044 | 5.07M | SET_CURRENT_GLYPH(penum, glyph); |
1045 | 61.3M | penum->bytes_decoded = penum->index - start; |
1046 | 61.3M | penum->is_pure_color = gs_color_writes_pure(penum->pgs); /* Save |
1047 | | this data for compute_glyph_raster_params to work |
1048 | | independently on the color change in BuildChar. |
1049 | | Doing it here because cshow proc may modify |
1050 | | the graphic state. |
1051 | | */ |
1052 | 61.3M | { |
1053 | 61.3M | int alpha_bits, depth; |
1054 | 61.3M | gs_fixed_point subpix_origin; |
1055 | | |
1056 | 61.3M | code = compute_glyph_raster_params(penum, false, |
1057 | 61.3M | &alpha_bits, &depth, &subpix_origin, &log2_scale); |
1058 | 61.3M | if (code < 0) |
1059 | 0 | return code; |
1060 | 61.3M | if (pair == 0) { |
1061 | 17.6M | code = gx_lookup_fm_pair(pfont, &char_tm_only(pgs), &log2_scale, |
1062 | 17.6M | penum->charpath_flag != cpm_show, &pair); |
1063 | 17.6M | if (code < 0) |
1064 | 0 | return code; |
1065 | 17.6M | } |
1066 | 61.3M | penum->pair = pair; |
1067 | 61.3M | if (glyph == GS_NO_GLYPH || SHOW_IS_ALL_OF(penum, TEXT_NO_CACHE)) { |
1068 | 681 | cc = 0; |
1069 | 681 | goto no_cache; |
1070 | 681 | } |
1071 | 61.3M | cc = gx_lookup_cached_char(pfont, pair, glyph, wmode, |
1072 | 61.3M | depth, &subpix_origin); |
1073 | 61.3M | } |
1074 | 61.3M | if (cc == 0) { |
1075 | 15.3M | goto no_cache; |
1076 | 15.3M | } |
1077 | | /* Character is in cache. */ |
1078 | | /* We might be doing .charboxpath or stringwidth; */ |
1079 | | /* check for these now. */ |
1080 | 45.9M | if (penum->charpath_flag == cpm_charwidth) { |
1081 | | /* This is charwidth. Just move by the width. */ |
1082 | 0 | DO_NOTHING; |
1083 | 45.9M | } else if (penum->charpath_flag != cpm_show) { |
1084 | | /* This is .charboxpath. Get the bounding box */ |
1085 | | /* and append it to a path. */ |
1086 | 0 | gx_path box_path; |
1087 | 0 | gs_fixed_point pt; |
1088 | 0 | fixed llx, lly, urx, ury; |
1089 | |
|
1090 | 0 | code = gx_path_current_point(pgs->path, &pt); |
1091 | 0 | if (code < 0) |
1092 | 0 | return code; |
1093 | 0 | llx = fixed_rounded(pt.x - cc->offset.x) + |
1094 | 0 | int2fixed(penum->ftx); |
1095 | 0 | lly = fixed_rounded(pt.y - cc->offset.y) + |
1096 | 0 | int2fixed(penum->fty); |
1097 | 0 | urx = llx + int2fixed(cc->width), |
1098 | 0 | ury = lly + int2fixed(cc->height); |
1099 | 0 | gx_path_init_local(&box_path, pgs->memory); |
1100 | 0 | code = |
1101 | 0 | gx_path_add_rectangle(&box_path, llx, lly, |
1102 | 0 | urx, ury); |
1103 | 0 | if (code >= 0) |
1104 | 0 | code = |
1105 | 0 | gx_path_add_char_path(pgs->show_gstate->path, |
1106 | 0 | &box_path, |
1107 | 0 | penum->charpath_flag); |
1108 | 0 | if (code >= 0) |
1109 | 0 | code = gx_path_add_point(pgs->path, pt.x, pt.y); |
1110 | 0 | gx_path_free(&box_path, "show_proceed(box path)"); |
1111 | 0 | if (code < 0) |
1112 | 0 | return code; |
1113 | 45.9M | } else if (SHOW_IS_DRAWING(penum)) { |
1114 | 45.8M | code = gx_image_cached_char(penum, cc); |
1115 | 45.8M | if (code < 0) |
1116 | 5 | return code; |
1117 | 45.8M | else if (code > 0) { |
1118 | 0 | cc = 0; |
1119 | 0 | goto no_cache; |
1120 | 0 | } |
1121 | 45.8M | } |
1122 | 45.9M | penum->use_wxy_float = false; |
1123 | 45.9M | penum->wxy_float.x = penum->wxy_float.y = 0; |
1124 | 45.9M | if (SHOW_IS_SLOW(penum)) { |
1125 | | /* Split up the assignment so that the */ |
1126 | | /* Watcom compiler won't reserve esi/edi. */ |
1127 | 29.0M | penum->wxy.x = cc->wxy.x; |
1128 | 29.0M | penum->wxy.y = cc->wxy.y; |
1129 | 29.0M | code = show_move(penum); |
1130 | 29.0M | } else |
1131 | 16.9M | code = show_fast_move(pgs, &cc->wxy); |
1132 | 45.9M | if (code) { |
1133 | | /* Might be kshow, glyph is stored above. */ |
1134 | 69 | return code; |
1135 | 69 | } |
1136 | 78.0M | } |
1137 | 78.0M | } |
1138 | 32.1M | } else { |
1139 | 1.03M | start = penum->index; |
1140 | | /* Can't use cache */ |
1141 | 1.03M | switch ((code = get_next_char_glyph(penum, &chr, &glyph))) { |
1142 | 0 | default: |
1143 | 0 | return code; |
1144 | 17.5k | case 2: |
1145 | 17.5k | return show_finish(penum); |
1146 | 2.90k | case 1: |
1147 | 2.90k | pfont = penum->fstack.items[penum->fstack.depth].font; |
1148 | 2.90k | penum->current_font = pfont; |
1149 | 2.90k | show_state_setup(penum); |
1150 | 2.90k | pair = 0; |
1151 | 1.02M | case 0: |
1152 | 1.02M | { int alpha_bits, depth; |
1153 | 1.02M | gs_log2_scale_point log2_scale; |
1154 | 1.02M | gs_fixed_point subpix_origin; |
1155 | | |
1156 | 1.02M | penum->bytes_decoded = penum->index - start; |
1157 | 1.02M | code = compute_glyph_raster_params(penum, false, &alpha_bits, &depth, &subpix_origin, &log2_scale); |
1158 | 1.02M | if (code < 0) |
1159 | 0 | return code; |
1160 | 1.02M | if (pair == 0) { |
1161 | 969k | code = gx_lookup_fm_pair(pfont, &char_tm_only(pgs), &log2_scale, |
1162 | 969k | penum->charpath_flag != cpm_show, &pair); |
1163 | 969k | if (code < 0) |
1164 | 0 | return code; |
1165 | 969k | } |
1166 | 1.02M | penum->pair = pair; |
1167 | 1.02M | } |
1168 | 1.03M | } |
1169 | 1.02M | SET_CURRENT_CHAR(penum, chr); |
1170 | 1.02M | if (glyph == GS_NO_GLYPH) { |
1171 | 1.00M | glyph = (*penum->encode_char)(pfont, chr, GLYPH_SPACE_NAME); |
1172 | 1.00M | } |
1173 | 1.02M | SET_CURRENT_GLYPH(penum, glyph); |
1174 | 1.02M | cc = 0; |
1175 | 1.02M | } |
1176 | 16.3M | no_cache: |
1177 | | /* |
1178 | | * We must call the client's rendering code. Normally, |
1179 | | * we only do this if the character is not cached (cc = 0); |
1180 | | * however, we also must do this if we have an xfont but |
1181 | | * are using scalable widths. In this case, and only this case, |
1182 | | * we get here with cc != 0. penum->current_char and penum->current_glyph |
1183 | | * has already been set. |
1184 | | */ |
1185 | 16.3M | if ((code = gs_gsave(pgs)) < 0) |
1186 | 0 | return code; |
1187 | | /* Set the font to the current descendant font. */ |
1188 | 16.3M | pgs->font = pfont; |
1189 | | /* Reset the in_cachedevice flag, so that a recursive show */ |
1190 | | /* will use the cache properly. */ |
1191 | 16.3M | pgs->in_cachedevice = CACHE_DEVICE_NONE; |
1192 | | /* Set the charpath data in the graphics context if necessary, */ |
1193 | | /* so that fill and stroke will add to the path */ |
1194 | | /* rather than having their usual effect. */ |
1195 | 16.3M | pgs->in_charpath = penum->charpath_flag; |
1196 | 16.3M | pgs->show_gstate = |
1197 | 16.3M | (penum->show_gstate == pgs ? pgs->saved : penum->show_gstate); |
1198 | 16.3M | pgs->stroke_adjust = false; /* per specification */ |
1199 | 16.3M | { |
1200 | 16.3M | gs_fixed_point cpt; |
1201 | | |
1202 | 16.3M | if ((code = gx_path_current_point_inline(pgs, &cpt)) < 0) { |
1203 | | /* For cshow, having no current point is acceptable. */ |
1204 | 0 | if (!SHOW_IS(penum, TEXT_DO_NONE)) |
1205 | 0 | goto rret; |
1206 | 0 | cpt.x = cpt.y = 0; /* arbitrary */ |
1207 | 0 | } |
1208 | 16.3M | penum->origin.x = cpt.x; |
1209 | 16.3M | penum->origin.y = cpt.y; |
1210 | | /* Normally, char_tm is valid because of show_state_setup, */ |
1211 | | /* but if we're in a cshow, it may not be. */ |
1212 | 16.3M | gs_currentcharmatrix(pgs, NULL, true); |
1213 | 16.3M | if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) { |
1214 | 16.0M | fixed tx = pgs->ctm.tx_fixed; |
1215 | 16.0M | fixed ty = pgs->ctm.ty_fixed; |
1216 | | |
1217 | 16.0M | gs_settocharmatrix(pgs); |
1218 | 16.0M | cpt.x += pgs->ctm.tx_fixed - tx; |
1219 | 16.0M | cpt.y += pgs->ctm.ty_fixed - ty; |
1220 | 16.0M | } else { |
1221 | 311k | double tx = pgs->ctm.tx; |
1222 | 311k | double ty = pgs->ctm.ty; |
1223 | 311k | double fpx, fpy; |
1224 | | |
1225 | 311k | gs_settocharmatrix(pgs); |
1226 | 311k | fpx = fixed2float(cpt.x) + (pgs->ctm.tx - tx); |
1227 | 311k | fpy = fixed2float(cpt.y) + (pgs->ctm.ty - ty); |
1228 | 311k | if (!(f_fits_in_bits(fpx, fixed_int_bits) |
1229 | 311k | && f_fits_in_bits(fpy, fixed_int_bits))) { |
1230 | 11 | gs_note_error(code = gs_error_limitcheck); |
1231 | 11 | goto rret; |
1232 | 11 | } |
1233 | 311k | cpt.x = float2fixed(fpx); |
1234 | 311k | cpt.y = float2fixed(fpy); |
1235 | 311k | } |
1236 | 16.3M | if (((code = gs_newpath(pgs)) < 0) || |
1237 | 16.3M | ((code = show_origin_setup(pgs, cpt.x, cpt.y, penum)) < 0)) |
1238 | 0 | goto rret; |
1239 | 16.3M | } |
1240 | 16.3M | penum->width_status = sws_none; |
1241 | 16.3M | penum->continue_proc = continue_show_update; |
1242 | | /* Reset the sampling scale. */ |
1243 | 16.3M | penum->log2_scale.x = penum->log2_scale.y = 0; |
1244 | | /* Try using the build procedure in the font. */ |
1245 | | /* < 0 means error, 0 means success, 1 means failure. */ |
1246 | 16.3M | penum->cc = cc; /* set this now for build procedure */ |
1247 | 16.3M | code = (*pfont->procs.build_char)(penum, pgs, pfont, |
1248 | 16.3M | chr, glyph); |
1249 | 16.3M | if (code < 0) { |
1250 | 10.7k | discard(gs_note_error(code)); |
1251 | 10.7k | goto rret; |
1252 | 10.7k | } |
1253 | 16.3M | if (code == 0) { |
1254 | 13.9M | code = show_update(penum); |
1255 | 13.9M | if (code < 0) |
1256 | 7 | goto rret; |
1257 | | /* Note that show_update does a grestore.... */ |
1258 | 13.9M | code = show_move(penum); |
1259 | 13.9M | if (code) |
1260 | 3.78k | return code; /* ... so don't go to rret here. */ |
1261 | 13.9M | goto more; |
1262 | 13.9M | } |
1263 | | /* |
1264 | | * Some BuildChar procedures do a save before the setcachedevice, |
1265 | | * and a restore at the end. If we waited to allocate the cache |
1266 | | * device until the setcachedevice, we would attempt to free it |
1267 | | * after the restore. Therefore, allocate it now. |
1268 | | */ |
1269 | 2.40M | if (penum->dev_cache == 0) { |
1270 | 683k | code = show_cache_setup(penum); |
1271 | 683k | if (code < 0) |
1272 | 0 | goto rret; |
1273 | 683k | } |
1274 | 2.40M | return TEXT_PROCESS_RENDER; |
1275 | | /* If we get an error while setting up for BuildChar, */ |
1276 | | /* we must undo the partial setup. */ |
1277 | 10.7k | rret: |
1278 | 21.7k | while (pgs->level > penum->level) { |
1279 | 10.9k | gs_grestore(pgs); |
1280 | 10.9k | } |
1281 | 10.7k | return code; |
1282 | 2.40M | } |
1283 | | |
1284 | | /* |
1285 | | * Prepare to retry rendering of the current character. (This is only used |
1286 | | * in one place in zchar1.c; a different approach may be better.) |
1287 | | */ |
1288 | | static int |
1289 | | gx_show_text_retry(gs_text_enum_t *pte) |
1290 | 0 | { |
1291 | 0 | gs_show_enum *const penum = (gs_show_enum *)pte; |
1292 | |
|
1293 | 0 | if (penum->cc) { |
1294 | 0 | gs_font *pfont = penum->current_font; |
1295 | |
|
1296 | 0 | gx_free_cached_char(pfont->dir, penum->cc); |
1297 | 0 | penum->cc = 0; |
1298 | 0 | } |
1299 | 0 | gs_grestore(penum->pgs); |
1300 | 0 | penum->width_status = sws_retry; |
1301 | 0 | penum->log2_scale.x = penum->log2_scale.y = 0; |
1302 | 0 | penum->pair = 0; |
1303 | 0 | return 0; |
1304 | 0 | } |
1305 | | |
1306 | | /* Finish show or stringwidth */ |
1307 | | static int |
1308 | | show_finish(gs_show_enum * penum) |
1309 | 16.8M | { |
1310 | 16.8M | gs_gstate *pgs = penum->pgs; |
1311 | 16.8M | int code = 0, rcode; |
1312 | | |
1313 | 16.8M | if ((penum->text.operation & TEXT_DO_FALSE_CHARPATH) || |
1314 | 16.8M | (penum->text.operation & TEXT_DO_TRUE_CHARPATH)) { |
1315 | 17.4k | if (pgs->path->current_subpath) |
1316 | 13.9k | pgs->path->last_charpath_segment = pgs->path->current_subpath->last; |
1317 | 17.4k | } |
1318 | 16.8M | if (penum->auto_release) |
1319 | 0 | penum->procs->release((gs_text_enum_t *)penum, "show_finish"); |
1320 | | |
1321 | 16.8M | if (!SHOW_IS_STRINGWIDTH(penum)) |
1322 | 16.6M | return 0; |
1323 | | |
1324 | | /* Save the accumulated width before returning, if we are not in PDF text rendering mode 3, */ |
1325 | | /* and undo the extra gsave. */ |
1326 | 198k | if (!(penum->text.operation & TEXT_RENDER_MODE_3)) |
1327 | 376 | code = gs_currentpoint(pgs, &penum->returned.total_width); |
1328 | 198k | rcode = gs_grestore(pgs); |
1329 | | |
1330 | 198k | return (code < 0 ? code : rcode); |
1331 | 16.8M | } |
1332 | | |
1333 | | /* Release the structure. */ |
1334 | | static void |
1335 | | gx_show_text_release(gs_text_enum_t *pte, client_name_t cname) |
1336 | 16.8M | { |
1337 | 16.8M | gs_show_enum *const penum = (gs_show_enum *)pte; |
1338 | | |
1339 | 16.8M | penum->cc = 0; |
1340 | 16.8M | if (penum->dev_cache2) { |
1341 | 3.30M | gx_device_retain((gx_device *)penum->dev_cache2, false); |
1342 | 3.30M | penum->dev_cache2 = 0; |
1343 | 3.30M | } |
1344 | 16.8M | if (penum->dev_cache) { |
1345 | 3.30M | gx_device_retain((gx_device *)penum->dev_cache, false); |
1346 | 3.30M | penum->dev_cache = 0; |
1347 | 3.30M | } |
1348 | 16.8M | if (penum->dev_null) { |
1349 | 198k | gx_device_retain((gx_device *)penum->dev_null, false); |
1350 | 198k | penum->dev_null = 0; |
1351 | 198k | } |
1352 | 16.8M | gx_default_text_release(pte, cname); |
1353 | 16.8M | } |
1354 | | |
1355 | | /* ------ Miscellaneous accessors ------ */ |
1356 | | |
1357 | | /* Return the charpath mode. */ |
1358 | | gs_char_path_mode |
1359 | | gs_show_in_charpath(const gs_show_enum * penum) |
1360 | 0 | { |
1361 | 0 | return penum->charpath_flag; |
1362 | 0 | } |
1363 | | |
1364 | | /* Return true if we only need the width from the rasterizer */ |
1365 | | /* and can short-circuit the full rendering of the character, */ |
1366 | | /* false if we need the actual character bits. */ |
1367 | | /* This is only meaningful just before calling gs_setcharwidth or */ |
1368 | | /* gs_setcachedevice[2]. */ |
1369 | | /* Note that we can't do this if the procedure has done any extra [g]saves. */ |
1370 | | static bool |
1371 | | gx_show_text_is_width_only(const gs_text_enum_t *pte) |
1372 | 17.8M | { |
1373 | 17.8M | const gs_show_enum *const penum = (const gs_show_enum *)pte; |
1374 | | |
1375 | | /* penum->cc will be non-zero iff we are calculating */ |
1376 | | /* the scalable width for an xfont character. */ |
1377 | 17.8M | return ((!SHOW_USES_OUTLINE(penum) || penum->cc != 0) && |
1378 | 17.8M | penum->pgs->level == penum->level + 1); |
1379 | 17.8M | } |
1380 | | |
1381 | | /* Return the width of the just-enumerated character (for cshow). */ |
1382 | | static int |
1383 | | gx_show_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth) |
1384 | 0 | { |
1385 | 0 | const gs_show_enum *const penum = (const gs_show_enum *)pte; |
1386 | |
|
1387 | 0 | return gs_idtransform(penum->pgs, |
1388 | 0 | fixed2float(penum->wxy.x), |
1389 | 0 | fixed2float(penum->wxy.y), pwidth); |
1390 | 0 | } |
1391 | | |
1392 | | /* Return the current font for cshow. */ |
1393 | | gs_font * |
1394 | | gs_show_current_font(const gs_show_enum * penum) |
1395 | 18.6M | { |
1396 | 18.6M | return (penum->fstack.depth < 0 ? penum->pgs->font : |
1397 | 18.6M | penum->fstack.items[penum->fstack.depth].font); |
1398 | 18.6M | } |
1399 | | |
1400 | | /* ------ Internal routines ------ */ |
1401 | | |
1402 | | /* Initialize the gstate-derived parts of a show enumerator. */ |
1403 | | /* We do this both when starting the show operation, */ |
1404 | | /* and when returning from the kshow callout. */ |
1405 | | /* Uses only penum->pgs, penum->fstack. */ |
1406 | | static int |
1407 | | show_state_setup(gs_show_enum * penum) |
1408 | 18.6M | { |
1409 | 18.6M | gs_gstate *pgs = penum->pgs; |
1410 | 18.6M | gx_clip_path *pcpath; |
1411 | 18.6M | gs_font *pfont; |
1412 | | |
1413 | 18.6M | if (penum->fstack.depth <= 0) { |
1414 | 16.8M | pfont = pgs->font; |
1415 | 16.8M | if (pfont->FontType == ft_CID_encrypted) { |
1416 | | /* doing 'cid glyphshow', |
1417 | | assuming penum->operation has TEXT_FROM_SINGLE_GLYPH */ |
1418 | 0 | gs_matrix mat; |
1419 | 0 | int fidx; |
1420 | 0 | int code = ((gs_font_cid0 *)pfont)->cidata.glyph_data((gs_font_base *)pfont, |
1421 | 0 | penum->text.data.d_glyph, NULL, &fidx); |
1422 | 0 | if (code < 0) { /* failed to load glyph data, reload glyph for CID 0 */ |
1423 | 0 | code = ((gs_font_cid0 *)pfont)->cidata.glyph_data((gs_font_base *)pfont, |
1424 | 0 | (gs_glyph)(GS_MIN_CID_GLYPH + 0), NULL, &fidx); |
1425 | 0 | if (code < 0) |
1426 | 0 | return_error(gs_error_invalidfont); |
1427 | 0 | } |
1428 | 0 | gs_matrix_multiply(&(gs_cid0_indexed_font(pfont, fidx)->FontMatrix), |
1429 | 0 | &pfont->FontMatrix, &mat); |
1430 | 0 | gs_setcharmatrix(pgs, &mat); |
1431 | 16.8M | } else { |
1432 | 16.8M | gs_currentcharmatrix(pgs, NULL, 1); /* make char_tm valid */ |
1433 | 16.8M | } |
1434 | 16.8M | } else { |
1435 | | /* We have to concatenate the parent's FontMatrix as well. */ |
1436 | 1.75M | gs_matrix mat; |
1437 | 1.75M | const gx_font_stack_item_t *pfsi = |
1438 | 1.75M | &penum->fstack.items[penum->fstack.depth]; |
1439 | | |
1440 | 1.75M | pfont = pfsi->font; |
1441 | 1.75M | gs_matrix_multiply(&pfont->FontMatrix, |
1442 | 1.75M | &pfsi[-1].font->FontMatrix, &mat); |
1443 | 1.75M | if (pfont->FontType == ft_CID_encrypted) { |
1444 | | /* concatenate the Type9 leaf's matrix */ |
1445 | 26.6k | gs_matrix_multiply(&(gs_cid0_indexed_font(pfont, pfsi->index)->FontMatrix), |
1446 | 26.6k | &mat, &mat); |
1447 | 26.6k | } |
1448 | 1.75M | gs_setcharmatrix(pgs, &mat); |
1449 | 1.75M | } |
1450 | 18.6M | penum->current_font = pfont; |
1451 | 18.6M | if (penum->can_cache >= 0 && |
1452 | 18.6M | gx_effective_clip_path(pgs, &pcpath) >= 0 |
1453 | 18.6M | ) { |
1454 | 18.6M | gs_fixed_rect cbox; |
1455 | | |
1456 | 18.6M | gx_cpath_inner_box(pcpath, &cbox); |
1457 | | /* Since characters occupy an integral number of pixels, */ |
1458 | | /* we can (and should) round the inner clipping box */ |
1459 | | /* outward rather than inward. */ |
1460 | 18.6M | penum->ibox.p.x = fixed2int_var(cbox.p.x); |
1461 | 18.6M | penum->ibox.p.y = fixed2int_var(cbox.p.y); |
1462 | 18.6M | penum->ibox.q.x = fixed2int_var_ceiling(cbox.q.x); |
1463 | 18.6M | penum->ibox.q.y = fixed2int_var_ceiling(cbox.q.y); |
1464 | 18.6M | gx_cpath_outer_box(pcpath, &cbox); |
1465 | 18.6M | penum->obox.p.x = fixed2int_var(cbox.p.x); |
1466 | 18.6M | penum->obox.p.y = fixed2int_var(cbox.p.y); |
1467 | 18.6M | penum->obox.q.x = fixed2int_var_ceiling(cbox.q.x); |
1468 | 18.6M | penum->obox.q.y = fixed2int_var_ceiling(cbox.q.y); |
1469 | 18.6M | if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) { |
1470 | 18.5M | penum->ftx = (int)fixed2long(pgs->char_tm.tx_fixed - |
1471 | 18.5M | pgs->ctm.tx_fixed); |
1472 | 18.5M | penum->fty = (int)fixed2long(pgs->char_tm.ty_fixed - |
1473 | 18.5M | pgs->ctm.ty_fixed); |
1474 | 18.5M | } else { |
1475 | 69.0k | double fdx = pgs->char_tm.tx - pgs->ctm.tx; |
1476 | 69.0k | double fdy = pgs->char_tm.ty - pgs->ctm.ty; |
1477 | | |
1478 | 69.0k | #define int_bits (ARCH_SIZEOF_INT * 8 - 1) |
1479 | 69.0k | if (!(f_fits_in_bits(fdx, int_bits) && |
1480 | 69.0k | f_fits_in_bits(fdy, int_bits)) |
1481 | 69.0k | ) |
1482 | 2.16k | return_error(gs_error_limitcheck); |
1483 | 66.8k | #undef int_bits |
1484 | 66.8k | penum->ftx = (int)fdx; |
1485 | 66.8k | penum->fty = (int)fdy; |
1486 | 66.8k | } |
1487 | 18.6M | } |
1488 | 18.6M | show_set_encode_char(penum); |
1489 | 18.6M | return 0; |
1490 | 18.6M | } |
1491 | | |
1492 | | /* Set the suggested oversampling scale for character rendering. */ |
1493 | | static void |
1494 | | show_set_scale(const gs_show_enum * penum, gs_log2_scale_point *log2_scale) |
1495 | 0 | { |
1496 | | /* |
1497 | | * Decide whether to oversample. |
1498 | | * We have to decide this each time setcachedevice is called. |
1499 | | */ |
1500 | 0 | const gs_gstate *pgs = NULL; |
1501 | |
|
1502 | 0 | if (gs_object_type(penum->pgs->memory, penum) == &st_gs_show_enum) { |
1503 | 0 | pgs = penum->pgs; |
1504 | 0 | } else { |
1505 | 0 | pgs = (gs_gstate *)penum->pgs; |
1506 | 0 | } |
1507 | |
|
1508 | 0 | if (pgs != NULL && (penum->charpath_flag == cpm_show || |
1509 | 0 | penum->charpath_flag == cpm_charwidth) && |
1510 | 0 | SHOW_USES_OUTLINE(penum) |
1511 | | /* && gx_path_is_void_inline(pgs->path) */ |
1512 | 0 | ) { |
1513 | 0 | const gs_font_base *pfont = (const gs_font_base *)penum->current_font; |
1514 | 0 | gs_fixed_point extent; |
1515 | 0 | int code = gs_distance_transform2fixed(&pgs->char_tm, |
1516 | 0 | pfont->FontBBox.q.x - pfont->FontBBox.p.x, |
1517 | 0 | pfont->FontBBox.q.y - pfont->FontBBox.p.y, |
1518 | 0 | &extent); |
1519 | |
|
1520 | 0 | if (code >= 0) { |
1521 | 0 | int sx = |
1522 | 0 | (any_abs(extent.x) < int2fixed(60) ? 2 : |
1523 | 0 | any_abs(extent.x) < int2fixed(200) ? 1 : |
1524 | 0 | 0); |
1525 | 0 | int sy = |
1526 | 0 | (any_abs(extent.y) < int2fixed(60) ? 2 : |
1527 | 0 | any_abs(extent.y) < int2fixed(200) ? 1 : |
1528 | 0 | 0); |
1529 | | |
1530 | | /* If we oversample at all, make sure we do it */ |
1531 | | /* in both X and Y. */ |
1532 | 0 | if (sx == 0 && sy != 0) |
1533 | 0 | sx = 1; |
1534 | 0 | else if (sy == 0 && sx != 0) |
1535 | 0 | sy = 1; |
1536 | 0 | log2_scale->x = sx; |
1537 | 0 | log2_scale->y = sy; |
1538 | 0 | return; |
1539 | 0 | } |
1540 | 0 | } |
1541 | | /* By default, don't scale. */ |
1542 | 0 | log2_scale->x = log2_scale->y = 0; |
1543 | 0 | } |
1544 | | |
1545 | | /* Set up the cache device and related information. */ |
1546 | | /* Note that we always allocate both cache devices, */ |
1547 | | /* even if we only use one of them. */ |
1548 | | static int |
1549 | | show_cache_setup(gs_show_enum * penum) |
1550 | 3.30M | { |
1551 | 3.30M | gs_gstate *pgs = penum->pgs; |
1552 | 3.30M | gs_memory_t *mem = penum->memory; |
1553 | 3.30M | gx_device_memory *dev = |
1554 | 3.30M | gs_alloc_struct_immovable(mem, gx_device_memory, &st_device_memory, |
1555 | 3.30M | "show_cache_setup(dev_cache)"); |
1556 | 3.30M | gx_device_memory *dev2 = |
1557 | 3.30M | gs_alloc_struct_immovable(mem, gx_device_memory, &st_device_memory, |
1558 | 3.30M | "show_cache_setup(dev_cache2)"); |
1559 | | |
1560 | 3.30M | if (dev == 0 || dev2 == 0) { |
1561 | | /* |
1562 | | * The structure is full of garbage so must not call the |
1563 | | * finalize method but still need to free the structure |
1564 | | */ |
1565 | 0 | gs_set_object_type(mem, dev2, &st_bytes); |
1566 | 0 | gs_set_object_type(mem, dev, &st_bytes); |
1567 | 0 | gs_free_object(mem, dev2, "show_cache_setup(dev_cache2)"); |
1568 | 0 | gs_free_object(mem, dev, "show_cache_setup(dev_cache)"); |
1569 | 0 | return_error(gs_error_VMerror); |
1570 | 0 | } |
1571 | | /* |
1572 | | * We only initialize the devices for the sake of the GC, |
1573 | | * (since we have to re-initialize dev as either a mem_mono |
1574 | | * or a mem_abuf device before actually using it) and also |
1575 | | * to set its memory pointer. |
1576 | | */ |
1577 | 3.30M | gs_make_mem_mono_device(dev, mem, gs_currentdevice_inline(pgs)); |
1578 | 3.30M | penum->dev_cache = dev; |
1579 | 3.30M | gs_make_mem_mono_device(dev2, mem, gs_currentdevice_inline(pgs)); |
1580 | 3.30M | penum->dev_cache2 = dev2; |
1581 | 3.30M | dev->HWResolution[0] = pgs->device->HWResolution[0]; |
1582 | 3.30M | dev->HWResolution[1] = pgs->device->HWResolution[1]; |
1583 | | /* Retain these devices, since they are referenced from the enumerator. */ |
1584 | 3.30M | gx_device_retain((gx_device *)dev, true); |
1585 | 3.30M | gx_device_retain((gx_device *)dev2, true); |
1586 | 3.30M | return 0; |
1587 | 3.30M | } |
1588 | | |
1589 | | /* Set the character origin as the origin of the coordinate system. */ |
1590 | | /* Used before rendering characters, and for moving the origin */ |
1591 | | /* in setcachedevice2 when WMode=1. */ |
1592 | | static int |
1593 | | show_origin_setup(gs_gstate * pgs, fixed cpt_x, fixed cpt_y, gs_show_enum * penum) |
1594 | 16.3M | { |
1595 | 16.3M | if (penum->charpath_flag == cpm_show) { |
1596 | | /* Round the translation in the graphics state. */ |
1597 | | /* This helps prevent rounding artifacts later. */ |
1598 | 15.3M | if (gs_currentaligntopixels(penum->current_font->dir) == 0) { |
1599 | 15.3M | int scx = -(1L << (_fixed_shift - penum->log2_scale.x)); |
1600 | 15.3M | int scy = -(1L << (_fixed_shift - penum->log2_scale.y)); |
1601 | 15.3M | int rdx = 1L << (_fixed_shift - 1 - penum->log2_scale.x); |
1602 | 15.3M | int rdy = 1L << (_fixed_shift - 1 - penum->log2_scale.y); |
1603 | | |
1604 | 15.3M | cpt_x = (cpt_x + rdx) & scx; |
1605 | 15.3M | cpt_y = (cpt_y + rdy) & scy; |
1606 | 15.3M | } else { |
1607 | 0 | cpt_x = fixed_rounded(cpt_x); |
1608 | 0 | cpt_y = fixed_rounded(cpt_y); |
1609 | 0 | } |
1610 | 15.3M | } |
1611 | | /* |
1612 | | * BuildChar procedures expect the current point to be undefined, |
1613 | | * so we omit the gx_path_add_point with ctm.t*_fixed. |
1614 | | */ |
1615 | 16.3M | return gx_translate_to_fixed(pgs, cpt_x, cpt_y); |
1616 | 16.3M | } |