/src/ghostpdl/base/gstext.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 | | /* Driver text interface support */ |
18 | | |
19 | | #include "memory_.h" |
20 | | #include "gstypes.h" |
21 | | #include "gdebug.h" |
22 | | #include "gserrors.h" |
23 | | #include "gsmemory.h" |
24 | | #include "gsstruct.h" |
25 | | #include "gstypes.h" |
26 | | #include "gxfcache.h" |
27 | | #include "gxdevcli.h" |
28 | | #include "gxdcolor.h" /* for gs_gstate_color_load */ |
29 | | #include "gxfont.h" /* for init_fstack */ |
30 | | #include "gxpath.h" |
31 | | #include "gxtext.h" |
32 | | #include "gzstate.h" |
33 | | #include "gsutil.h" |
34 | | #include "gxdevsop.h" |
35 | | #include "gscspace.h" |
36 | | #include "gsicc_blacktext.h" |
37 | | |
38 | | /* GC descriptors */ |
39 | | public_st_gs_text_params(); |
40 | | public_st_gs_text_enum(); |
41 | | |
42 | | static |
43 | 72 | ENUM_PTRS_WITH(text_params_enum_ptrs, gs_text_params_t *tptr) return 0; |
44 | 18 | case 0: |
45 | 18 | if (tptr->operation & TEXT_FROM_STRING) { |
46 | 18 | return ENUM_CONST_STRING2(tptr->data.bytes, tptr->size); |
47 | 18 | } |
48 | 0 | if (tptr->operation & TEXT_FROM_BYTES) |
49 | 0 | return ENUM_OBJ(tptr->data.bytes); |
50 | 0 | if (tptr->operation & TEXT_FROM_CHARS) |
51 | 0 | return ENUM_OBJ(tptr->data.chars); |
52 | 0 | if (tptr->operation & TEXT_FROM_GLYPHS) |
53 | 0 | return ENUM_OBJ(tptr->data.glyphs); |
54 | 0 | return ENUM_OBJ(NULL); |
55 | 18 | case 1: |
56 | 18 | return ENUM_OBJ(tptr->operation & TEXT_REPLACE_WIDTHS ? |
57 | 0 | tptr->x_widths : NULL); |
58 | 18 | case 2: |
59 | 18 | return ENUM_OBJ(tptr->operation & TEXT_REPLACE_WIDTHS ? |
60 | 72 | tptr->y_widths : NULL); |
61 | 72 | ENUM_PTRS_END |
62 | 18 | static RELOC_PTRS_WITH(text_params_reloc_ptrs, gs_text_params_t *tptr) |
63 | 18 | { |
64 | 18 | if (tptr->operation & TEXT_FROM_STRING) { |
65 | 18 | gs_const_string str; |
66 | | |
67 | 18 | str.data = tptr->data.bytes; |
68 | 18 | str.size = tptr->size; |
69 | 18 | RELOC_CONST_STRING_VAR(str); |
70 | 18 | tptr->data.bytes = str.data; |
71 | 18 | } else if (tptr->operation & TEXT_FROM_BYTES) |
72 | 0 | RELOC_OBJ_VAR(tptr->data.bytes); |
73 | 0 | else if (tptr->operation & TEXT_FROM_CHARS) |
74 | 0 | RELOC_OBJ_VAR(tptr->data.chars); |
75 | 0 | else if (tptr->operation & TEXT_FROM_GLYPHS) |
76 | 0 | RELOC_OBJ_VAR(tptr->data.glyphs); |
77 | 18 | if (tptr->operation & TEXT_REPLACE_WIDTHS) { |
78 | 0 | RELOC_OBJ_VAR(tptr->x_widths); |
79 | 0 | RELOC_OBJ_VAR(tptr->y_widths); |
80 | 0 | } |
81 | 18 | } |
82 | 18 | RELOC_PTRS_END |
83 | | |
84 | 198 | static ENUM_PTRS_WITH(text_enum_enum_ptrs, gs_text_enum_t *eptr) |
85 | 90 | { |
86 | 90 | if (index == 6) { |
87 | 18 | if (eptr->pair != 0) |
88 | 18 | ENUM_RETURN(eptr->pair - eptr->pair->index); |
89 | 0 | else |
90 | 0 | ENUM_RETURN(0); |
91 | 18 | } |
92 | 72 | index -= 7; |
93 | 72 | if (index <= eptr->fstack.depth) |
94 | 0 | ENUM_RETURN(eptr->fstack.items[index].font); |
95 | 72 | index -= eptr->fstack.depth + 1; |
96 | 72 | return ENUM_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text), index); |
97 | 72 | } |
98 | 18 | case 0: return ENUM_OBJ(gx_device_enum_ptr(eptr->dev)); |
99 | 18 | case 1: return ENUM_OBJ(gx_device_enum_ptr(eptr->imaging_dev)); |
100 | 72 | ENUM_PTR2(2, gs_text_enum_t, pgs, orig_font); |
101 | 198 | ENUM_PTR2(4, gs_text_enum_t, pcpath, current_font); |
102 | 198 | ENUM_PTRS_END |
103 | | |
104 | 18 | static RELOC_PTRS_WITH(text_enum_reloc_ptrs, gs_text_enum_t *eptr) |
105 | 18 | { |
106 | 18 | int i; |
107 | | |
108 | 18 | RELOC_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text)); |
109 | 18 | eptr->dev = gx_device_reloc_ptr(eptr->dev, gcst); |
110 | 18 | eptr->imaging_dev = gx_device_reloc_ptr(eptr->imaging_dev, gcst); |
111 | 18 | RELOC_PTR2(gs_text_enum_t, pgs, orig_font); |
112 | 18 | RELOC_PTR2(gs_text_enum_t, pcpath, current_font); |
113 | 18 | if (eptr->pair != NULL) |
114 | 18 | eptr->pair = (cached_fm_pair *)RELOC_OBJ(eptr->pair - eptr->pair->index) + |
115 | 18 | eptr->pair->index; |
116 | 18 | for (i = 0; i <= eptr->fstack.depth; i++) |
117 | 18 | RELOC_PTR(gs_text_enum_t, fstack.items[i].font); |
118 | 18 | } |
119 | 18 | RELOC_PTRS_END |
120 | | |
121 | | /* Begin processing text. */ |
122 | | int |
123 | | gx_device_text_begin(gx_device * dev, gs_gstate * pgs, |
124 | | const gs_text_params_t * text, gs_font * font, |
125 | | const gx_clip_path * pcpath, /* DO_DRAW */ |
126 | | gs_text_enum_t ** ppte) |
127 | 6.60M | { |
128 | 6.60M | if (TEXT_PARAMS_ARE_INVALID(text)) |
129 | 0 | return_error(gs_error_rangecheck); |
130 | 6.60M | { |
131 | 6.60M | const gx_clip_path *tcpath = |
132 | 6.60M | (text->operation & TEXT_DO_DRAW ? pcpath : 0); |
133 | | |
134 | | /* A high level device need to know an initial device color |
135 | | for accumulates a charstring of a Type 3 font. |
136 | | Since the accumulation may happen while stringwidth. |
137 | | we pass the device color unconditionally. */ |
138 | 6.60M | return dev_proc(dev, text_begin) |
139 | 6.60M | (dev, pgs, text, font, tcpath, ppte); |
140 | 6.60M | } |
141 | 6.60M | } |
142 | | |
143 | | /* |
144 | | * Initialize a newly created text enumerator. Implementations of |
145 | | * text_begin must call this just after allocating the enumerator. |
146 | | */ |
147 | | static int |
148 | | gs_text_enum_init_dynamic(gs_text_enum_t *pte, gs_font *font) |
149 | 6.81M | { |
150 | 6.81M | uint operation = pte->text.operation; |
151 | 6.81M | bool propagate_charpath = (operation & TEXT_DO_DRAW) != 0; |
152 | | |
153 | 6.81M | pte->current_font = font; |
154 | 6.81M | pte->index = 0; |
155 | 6.81M | pte->xy_index = 0; |
156 | 6.81M | pte->FontBBox_as_Metrics2.x = pte->FontBBox_as_Metrics2.y = 0; |
157 | 6.81M | pte->pair = 0; |
158 | 6.81M | pte->device_disabled_grid_fitting = 0; |
159 | 6.81M | pte->outer_CID = GS_NO_GLYPH; |
160 | 6.81M | pte->single_byte_space = false; |
161 | 6.81M | pte->cc = NULL; |
162 | | |
163 | | /* We need to set the charpath_flag, as the PCL interpreter calls the |
164 | | * graphics lib to do some measurement operations, which relies on the |
165 | | * charpath_flag. See bug 700577 for more details. */ |
166 | 6.81M | if (operation & TEXT_DO_ANY_CHARPATH) |
167 | 5.45k | pte->charpath_flag = |
168 | 5.45k | (operation & TEXT_DO_FALSE_CHARPATH ? cpm_false_charpath : |
169 | 5.45k | operation & TEXT_DO_TRUE_CHARPATH ? cpm_true_charpath : |
170 | 2.84k | operation & TEXT_DO_FALSE_CHARBOXPATH ? cpm_false_charboxpath : |
171 | 0 | operation & TEXT_DO_TRUE_CHARBOXPATH ? cpm_true_charboxpath : |
172 | 0 | operation & TEXT_DO_CHARWIDTH ? cpm_charwidth : |
173 | 0 | cpm_show /* can't happen */ ); |
174 | 6.81M | else |
175 | 6.81M | pte->charpath_flag = |
176 | 6.81M | (propagate_charpath ? (pte->pgs ? pte->pgs->in_charpath : 0) : cpm_show); |
177 | 6.81M | pte->is_pure_color = pte->pgs ? gs_color_writes_pure(pte->pgs) : 0; |
178 | | |
179 | 6.81M | return font->procs.init_fstack(pte, font); |
180 | 6.81M | } |
181 | | int |
182 | | gs_text_enum_init(gs_text_enum_t *pte, const gs_text_enum_procs_t *procs, |
183 | | gx_device *dev, gs_gstate *pgs, |
184 | | const gs_text_params_t *text, gs_font *font, |
185 | | const gx_clip_path *pcpath, |
186 | | gs_memory_t *mem) |
187 | 6.81M | { |
188 | 6.81M | int code; |
189 | | |
190 | 6.81M | pte->text = *text; |
191 | 6.81M | pte->dev = dev; |
192 | 6.81M | pte->imaging_dev = NULL; |
193 | 6.81M | pte->pgs = pgs; |
194 | 6.81M | pte->orig_font = font; |
195 | 6.81M | pte->pcpath = pcpath; |
196 | 6.81M | pte->memory = mem; |
197 | 6.81M | pte->procs = procs; |
198 | | #ifdef DEBUG |
199 | | pte->text_enum_id = gs_next_text_enum_id(font); |
200 | | #else |
201 | 6.81M | pte->text_enum_id = 0; |
202 | 6.81M | #endif |
203 | 6.81M | pte->enum_client_data = NULL; |
204 | | /* text_begin procedure sets rc */ |
205 | | /* init_dynamic sets current_font */ |
206 | | |
207 | 6.81M | pte->log2_scale.x = pte->log2_scale.y = 0; |
208 | | /* init_dynamic sets index, xy_index, fstack */ |
209 | 6.81M | code = gs_text_enum_init_dynamic(pte, font); |
210 | 6.81M | pte->k_text_release = 0; |
211 | 6.81M | if (code >= 0) |
212 | 6.81M | rc_increment(dev); |
213 | 6.81M | return code; |
214 | 6.81M | } |
215 | | |
216 | | gs_text_enum_t * |
217 | | gs_text_enum_alloc(gs_memory_t * mem, gs_gstate * pgs, client_name_t cname) |
218 | 0 | { |
219 | 0 | gs_text_enum_t *penum; |
220 | |
|
221 | 0 | rc_alloc_struct_1(penum, gs_text_enum_t, &st_gs_text_enum, mem, |
222 | 0 | return 0, cname); |
223 | 0 | penum->rc.free = rc_free_text_enum; |
224 | | |
225 | | /* Initialize pointers for GC */ |
226 | 0 | penum->text.operation = 0; /* no pointers relevant */ |
227 | 0 | penum->dev = 0; |
228 | 0 | penum->pgs = pgs; |
229 | 0 | penum->fapi_log2_scale.x = penum->fapi_log2_scale.y = -1; |
230 | 0 | penum->fapi_glyph_shift.x = penum->fapi_glyph_shift.y = 0; |
231 | 0 | penum->fstack.depth = -1; |
232 | 0 | return penum; |
233 | 0 | } |
234 | | |
235 | | /* |
236 | | * Copy the dynamically changing elements from one enumerator to another. |
237 | | * This is useful primarily for enumerators that sometimes pass the |
238 | | * operation to a subsidiary enumerator. |
239 | | */ |
240 | | void |
241 | | gs_text_enum_copy_dynamic(gs_text_enum_t *pto, const gs_text_enum_t *pfrom, |
242 | | bool for_return) |
243 | 5.30M | { |
244 | 5.30M | int depth = pfrom->fstack.depth; |
245 | | |
246 | 5.30M | pto->current_font = pfrom->current_font; |
247 | 5.30M | pto->index = pfrom->index; |
248 | 5.30M | pto->bytes_decoded = pfrom->bytes_decoded; |
249 | 5.30M | pto->xy_index = pfrom->xy_index; |
250 | 5.30M | pto->fstack.depth = depth; |
251 | 5.30M | pto->FontBBox_as_Metrics2 = pfrom->FontBBox_as_Metrics2; |
252 | 5.30M | pto->pair = pfrom->pair; |
253 | 5.30M | pto->device_disabled_grid_fitting = pfrom->device_disabled_grid_fitting; |
254 | 5.30M | pto->outer_CID = pfrom->outer_CID; |
255 | 5.30M | if (depth >= 0) |
256 | 138k | memcpy(pto->fstack.items, pfrom->fstack.items, |
257 | 138k | (depth + 1) * sizeof(pto->fstack.items[0])); |
258 | 5.30M | if (for_return) { |
259 | 5.09M | pto->cmap_code = pfrom->cmap_code; |
260 | 5.09M | pto->returned = pfrom->returned; |
261 | 5.09M | } |
262 | 5.30M | } |
263 | | |
264 | | /* Begin processing text based on a graphics state. */ |
265 | | int |
266 | | gs_text_begin(gs_gstate * pgs, const gs_text_params_t * text, |
267 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
268 | 6.61M | { |
269 | 6.61M | gx_clip_path *pcpath = 0; |
270 | 6.61M | int code; |
271 | 6.61M | gs_overprint_params_t op_params = { 0 }; |
272 | 6.61M | bool op_active = dev_proc(pgs->device, dev_spec_op)(pgs->device, gxdso_overprint_active, NULL, 0); |
273 | 6.61M | bool text_op_fill = ((pgs->overprint || (!pgs->overprint && op_active)) && |
274 | 6.61M | (pgs->text_rendering_mode == 0)); |
275 | 6.61M | bool text_op_stroke = ((pgs->stroke_overprint || (!pgs->stroke_overprint && op_active)) && |
276 | 6.61M | (pgs->text_rendering_mode == 1)); |
277 | 6.61M | bool type3 = (pgs->font->FontType == ft_user_defined || |
278 | 6.61M | pgs->font->FontType == ft_PDF_user_defined || |
279 | 6.61M | pgs->font->FontType == ft_PCL_user_defined || |
280 | 6.61M | pgs->font->FontType == ft_MicroType || |
281 | 6.61M | pgs->font->FontType == ft_GL2_stick_user_defined || |
282 | 6.61M | pgs->font->FontType == ft_GL2_531); |
283 | 6.61M | bool in_smask = |
284 | 6.61M | (dev_proc(pgs->device, dev_spec_op)(pgs->device, gxdso_in_smask_construction, NULL, 0)) > 0; |
285 | 6.61M | bool black_text = (text->operation & (TEXT_DO_DRAW | TEXT_DO_ANY_CHARPATH)) && |
286 | 6.61M | !type3 && !in_smask; |
287 | 6.61M | cmm_dev_profile_t *icc_struct; |
288 | | |
289 | 6.61M | code = dev_proc(pgs->device, get_profile)((gx_device *)pgs->device, &icc_struct); |
290 | 6.61M | if (code < 0) |
291 | 0 | black_text = 0; |
292 | 6.61M | else |
293 | 6.61M | black_text = black_text && icc_struct->blacktext; |
294 | | |
295 | | /* |
296 | | * Detect nocurrentpoint now, even if the string is empty, for Adobe |
297 | | * compatibility. |
298 | | */ |
299 | 6.61M | if (text->operation & (TEXT_DO_DRAW | TEXT_DO_ANY_CHARPATH)) { |
300 | 6.58M | if (!pgs->current_point_valid) |
301 | 19 | return_error(gs_error_nocurrentpoint); |
302 | 6.58M | } |
303 | | /* Detect zero FontNatrix now for Adobe compatibility with CET tests. |
304 | | Note that matrixe\\ces like [1 0 0 0 0 0] are used in comparefiles |
305 | | to compute a text width. |
306 | | Note : FontType 3 throws error in setcachedevice. */ |
307 | 6.61M | if (pgs->font->FontType != ft_user_defined && |
308 | 6.61M | pgs->font->FontType != ft_PDF_user_defined && |
309 | 6.61M | pgs->font->FontType != ft_GL2_stick_user_defined && |
310 | 6.61M | pgs->font->FontType != ft_PCL_user_defined && |
311 | 6.61M | pgs->font->FontMatrix.xx == 0 && pgs->font->FontMatrix.xy == 0 && |
312 | 6.61M | pgs->font->FontMatrix.yx == 0 && pgs->font->FontMatrix.yy == 0) |
313 | 358 | return_error(gs_error_undefinedresult); /* sic! : CPSI compatibility */ |
314 | 6.61M | if (text->operation & TEXT_DO_DRAW) { |
315 | 6.57M | code = gx_effective_clip_path(pgs, &pcpath); |
316 | 6.57M | if (code < 0) |
317 | 0 | return code; |
318 | 6.57M | } |
319 | | /* We must load device color even with no TEXT_DO_DRAW, |
320 | | because a high level device accumulates a charstring |
321 | | of a Type 3 font while stringwidth. |
322 | | Unfortunately we can't effectively know a leaf font type here, |
323 | | so we load the color unconditionally . */ |
324 | | /* Processing a text object operation */ |
325 | 6.61M | ensure_tag_is_set(pgs, pgs->device, GS_TEXT_TAG); /* NB: may unset_dev_color */ |
326 | | |
327 | 6.61M | if (black_text && pgs->black_textvec_state == NULL) { |
328 | 0 | gsicc_setup_blacktextvec(pgs, (gx_device *)pgs->device, true); |
329 | 0 | } |
330 | | |
331 | 6.61M | code = gx_set_dev_color(pgs); |
332 | 6.61M | if (code != 0) |
333 | 10.7k | return code; |
334 | | |
335 | 6.60M | code = gs_gstate_color_load(pgs); |
336 | 6.60M | if (code < 0) |
337 | 0 | return code; |
338 | | |
339 | 6.60M | if (text_op_stroke) { |
340 | 0 | if_debug0m(gs_debug_flag_overprint, pgs->memory, |
341 | 0 | "[overprint] Stroke Text Overprint\n"); |
342 | 0 | code = gs_do_set_overprint(pgs); |
343 | 0 | if (code < 0) |
344 | 0 | return code; |
345 | 6.60M | } else if (text_op_fill) { |
346 | 41.2k | if_debug0m(gs_debug_flag_overprint, pgs->memory, |
347 | 41.2k | "[overprint] Fill Text Overprint\n"); |
348 | 41.2k | code = gs_do_set_overprint(pgs); |
349 | 41.2k | if (code < 0) |
350 | 0 | return code; |
351 | 41.2k | } |
352 | | |
353 | | /* If overprint is true, push the compositor action to set the op device state */ |
354 | 6.60M | if ((pgs->overprint && pgs->text_rendering_mode == 0) || |
355 | 6.60M | (pgs->stroke_overprint && pgs->text_rendering_mode == 1) || |
356 | 6.60M | op_active) { |
357 | 41.2k | gx_device* dev = pgs->device; |
358 | 41.2k | cmm_dev_profile_t* dev_profile; |
359 | | |
360 | 41.2k | dev_proc(dev, get_profile)(dev, &dev_profile); |
361 | 41.2k | if ((dev_profile->overprint_control != gs_overprint_control_disable) && |
362 | 41.2k | (dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]->data_cs == gsCMYK || |
363 | 41.2k | dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]->data_cs == gsNCHANNEL)) { |
364 | 18.4k | if (pgs->text_rendering_mode == 0) { |
365 | 18.4k | op_params.op_state = OP_STATE_FILL; |
366 | 18.4k | gs_gstate_update_overprint(pgs, &op_params); |
367 | 18.4k | } else if (pgs->text_rendering_mode == 1) { |
368 | 0 | op_params.op_state = OP_STATE_STROKE; |
369 | 0 | gs_gstate_update_overprint(pgs, &op_params); |
370 | 0 | } |
371 | 18.4k | } |
372 | 41.2k | } |
373 | | |
374 | 6.60M | pgs->device->sgr.stroke_stored = false; |
375 | 6.60M | code = gx_device_text_begin(pgs->device, pgs, |
376 | 6.60M | text, pgs->font, |
377 | 6.60M | pcpath, ppte); |
378 | | |
379 | | /* we need to know if we are doing a highlevel device. |
380 | | Also we need to know if we are doing any stroke |
381 | | or stroke fill operations. This determines when |
382 | | we need to release the black_textvec_state structure. */ |
383 | 6.60M | if (code >= 0 && *ppte != NULL) { |
384 | 6.60M | if (black_text) { |
385 | 0 | if (!((*ppte)->k_text_release)) { |
386 | | /* Not a high level device */ |
387 | 0 | if (pgs->text_rendering_mode == 0 || |
388 | 0 | pgs->text_rendering_mode == 4) { |
389 | | /* No stroke */ |
390 | 0 | (*ppte)->k_text_release = 1; |
391 | 0 | } |
392 | 0 | } |
393 | 0 | } else |
394 | 6.60M | (*ppte)->k_text_release = 0; |
395 | 6.60M | } |
396 | | |
397 | 6.60M | return code; |
398 | 6.60M | } |
399 | | |
400 | | /* |
401 | | * Update the device color to be used with text (because a kshow or |
402 | | * cshow procedure may have changed the current color). |
403 | | */ |
404 | | int |
405 | | gs_text_update_dev_color(gs_gstate * pgs, gs_text_enum_t * pte) |
406 | 1.15M | { |
407 | | /* Processing a text object operation */ |
408 | 1.15M | ensure_tag_is_set(pgs, pgs->device, GS_TEXT_TAG); /* NB: may unset_dev_color */ |
409 | | |
410 | 1.15M | if (pte->text.operation & TEXT_DO_DRAW) { |
411 | | /* FIXME: It feels bad that we're setting the dev color in |
412 | | * pgs, rather than pte->pgs. */ |
413 | 297k | int code = gx_set_dev_color(pgs); |
414 | 297k | if (code != 0) |
415 | 0 | return code; |
416 | 297k | } |
417 | 1.15M | return 0; |
418 | 1.15M | } |
419 | | |
420 | | static inline uint text_do_draw(gs_gstate * pgs) |
421 | 27.7k | { |
422 | 27.7k | return (pgs->text_rendering_mode == 3 ? |
423 | 27.7k | TEXT_DO_NONE | TEXT_RENDER_MODE_3 : TEXT_DO_DRAW); |
424 | 27.7k | } |
425 | | |
426 | | /* Begin PostScript-equivalent text operations. */ |
427 | | int |
428 | | gs_show_begin(gs_gstate * pgs, const byte * str, uint size, |
429 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
430 | 25.3k | { |
431 | 25.3k | gs_text_params_t text; |
432 | | |
433 | 25.3k | text.operation = TEXT_FROM_STRING | text_do_draw(pgs) | TEXT_RETURN_WIDTH; |
434 | 25.3k | text.data.bytes = str, text.size = size; |
435 | 25.3k | return gs_text_begin(pgs, &text, mem, ppte); |
436 | 25.3k | } |
437 | | int |
438 | | gs_ashow_begin(gs_gstate * pgs, double ax, double ay, const byte * str, uint size, |
439 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
440 | 2.45k | { |
441 | 2.45k | gs_text_params_t text; |
442 | | |
443 | 2.45k | text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_ALL_WIDTHS | |
444 | 2.45k | text_do_draw(pgs) | TEXT_RETURN_WIDTH; |
445 | 2.45k | text.data.bytes = str, text.size = size; |
446 | 2.45k | text.delta_all.x = ax; |
447 | 2.45k | text.delta_all.y = ay; |
448 | 2.45k | return gs_text_begin(pgs, &text, mem, ppte); |
449 | 2.45k | } |
450 | | int |
451 | | gs_widthshow_begin(gs_gstate * pgs, double cx, double cy, gs_char chr, |
452 | | const byte * str, uint size, |
453 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
454 | 0 | { |
455 | 0 | gs_text_params_t text; |
456 | |
|
457 | 0 | text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_SPACE_WIDTH | |
458 | 0 | text_do_draw(pgs) | TEXT_RETURN_WIDTH; |
459 | 0 | text.data.bytes = str, text.size = size; |
460 | 0 | text.delta_space.x = cx; |
461 | 0 | text.delta_space.y = cy; |
462 | 0 | text.space.s_char = chr; |
463 | 0 | return gs_text_begin(pgs, &text, mem, ppte); |
464 | 0 | } |
465 | | int |
466 | | gs_awidthshow_begin(gs_gstate * pgs, double cx, double cy, gs_char chr, |
467 | | double ax, double ay, const byte * str, uint size, |
468 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
469 | 0 | { |
470 | 0 | gs_text_params_t text; |
471 | |
|
472 | 0 | text.operation = TEXT_FROM_STRING | |
473 | 0 | TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH | |
474 | 0 | text_do_draw(pgs) | TEXT_RETURN_WIDTH; |
475 | 0 | text.data.bytes = str, text.size = size; |
476 | 0 | text.delta_space.x = cx; |
477 | 0 | text.delta_space.y = cy; |
478 | 0 | text.space.s_char = chr; |
479 | 0 | text.delta_all.x = ax; |
480 | 0 | text.delta_all.y = ay; |
481 | 0 | return gs_text_begin(pgs, &text, mem, ppte); |
482 | 0 | } |
483 | | int |
484 | | gs_kshow_begin(gs_gstate * pgs, const byte * str, uint size, |
485 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
486 | 0 | { |
487 | 0 | gs_text_params_t text; |
488 | | |
489 | | /* Detect degenerate CTM now for Adobe compatibility with CET 13-12-4. */ |
490 | 0 | if (pgs->ctm.xx * pgs->ctm.yy - pgs->ctm.yx * pgs->ctm.xy == 0) |
491 | 0 | return_error(gs_error_undefinedresult); /* sic! : CPSI compatibility */ |
492 | 0 | text.operation = TEXT_FROM_STRING | text_do_draw(pgs) | TEXT_INTERVENE | |
493 | 0 | TEXT_RETURN_WIDTH; |
494 | 0 | text.data.bytes = str, text.size = size; |
495 | 0 | return gs_text_begin(pgs, &text, mem, ppte); |
496 | 0 | } |
497 | | |
498 | | /* Retrieve text params from enumerator. */ |
499 | | gs_text_params_t * |
500 | | gs_get_text_params(gs_text_enum_t *pte) |
501 | 0 | { |
502 | 0 | return &pte->text; |
503 | 0 | } |
504 | | |
505 | | int |
506 | | gs_xyshow_begin(gs_gstate * pgs, const byte * str, uint size, |
507 | | const float *x_widths, const float *y_widths, |
508 | | uint widths_size, gs_memory_t * mem, gs_text_enum_t ** ppte) |
509 | 0 | { |
510 | 0 | gs_text_params_t text = {0}; |
511 | 0 | uint widths_needed; |
512 | 0 | int code; |
513 | |
|
514 | 0 | text.operation = TEXT_FROM_STRING | TEXT_REPLACE_WIDTHS | |
515 | 0 | text_do_draw(pgs) | TEXT_RETURN_WIDTH; |
516 | 0 | text.data.bytes = str, text.size = size; |
517 | 0 | text.x_widths = x_widths; |
518 | 0 | text.y_widths = y_widths; |
519 | 0 | text.widths_size = widths_size; |
520 | | |
521 | | /* |
522 | | * Check that the widths array is large enough. gs_text_replaced_width |
523 | | * checks this step-by-step, but Adobe's interpreters check it ahead of |
524 | | * time, and for CET compliance, we must also. This is very easy for |
525 | | * font types that always have 8-bit characters, but for others, we |
526 | | * must use the font's next_char_glyph procedure to determine how many |
527 | | * characters there are in the string. |
528 | | */ |
529 | 0 | code = gs_text_count_chars(pgs, &text, mem); |
530 | 0 | if (code < 0) |
531 | 0 | return code; |
532 | 0 | widths_needed = code; |
533 | 0 | if (x_widths && y_widths) |
534 | 0 | widths_needed <<= 1; |
535 | 0 | if (widths_size < widths_needed) |
536 | 0 | return_error(gs_error_rangecheck); |
537 | 0 | return gs_text_begin(pgs, &text, mem, ppte); |
538 | 0 | } |
539 | | |
540 | | static void |
541 | | setup_FontBBox_as_Metrics2 (gs_text_enum_t * pte, gs_font * pfont) |
542 | 0 | { |
543 | | /* When we exec a operator like `show' that has a a chance to get |
544 | | a glyph from a char, we can set FontBBox_as_Metrics2 in |
545 | | gschar0.c:gs_type0_next_char_glyph. In other hand, when we |
546 | | exec a operator like `glyphshow' that get a glyph directly from |
547 | | an input file, gschar0.c:gs_type0_next_char_glyph is exec'ed. |
548 | | For the later case, we set up FontBBox_as_Metrics2 with using |
549 | | this procedure.. */ |
550 | 0 | if (pfont->FontType == ft_CID_encrypted |
551 | 0 | || pfont->FontType == ft_CID_TrueType) |
552 | 0 | pte->FontBBox_as_Metrics2 = ((gs_font_base *)pfont)->FontBBox.q; |
553 | 0 | } |
554 | | |
555 | | int |
556 | | gs_glyphshow_begin(gs_gstate * pgs, gs_glyph glyph, |
557 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
558 | 1 | { |
559 | 1 | gs_text_params_t text; |
560 | 1 | int result; |
561 | | |
562 | 1 | text.operation = TEXT_FROM_SINGLE_GLYPH | text_do_draw(pgs) | TEXT_RETURN_WIDTH; |
563 | 1 | text.data.d_glyph = glyph; |
564 | 1 | text.size = 1; |
565 | 1 | result = gs_text_begin(pgs, &text, mem, ppte); |
566 | 1 | if (result == 0) |
567 | 0 | setup_FontBBox_as_Metrics2(*ppte, pgs->font); |
568 | 1 | return result; |
569 | 1 | } |
570 | | int |
571 | | gs_cshow_begin(gs_gstate * pgs, const byte * str, uint size, |
572 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
573 | 0 | { |
574 | 0 | gs_text_params_t text; |
575 | |
|
576 | 0 | text.operation = TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_INTERVENE; |
577 | 0 | text.data.bytes = str, text.size = size; |
578 | 0 | return gs_text_begin(pgs, &text, mem, ppte); |
579 | 0 | } |
580 | | int |
581 | | gs_stringwidth_begin(gs_gstate * pgs, const byte * str, uint size, |
582 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
583 | 410 | { |
584 | 410 | gs_text_params_t text; |
585 | | |
586 | 410 | text.operation = TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_RETURN_WIDTH; |
587 | 410 | text.data.bytes = str, text.size = size; |
588 | 410 | return gs_text_begin(pgs, &text, mem, ppte); |
589 | 410 | } |
590 | | int |
591 | | gs_charpath_begin(gs_gstate * pgs, const byte * str, uint size, bool stroke_path, |
592 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
593 | 2.04k | { |
594 | 2.04k | gs_text_params_t text; |
595 | | |
596 | 2.04k | text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH | |
597 | 2.04k | (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH); |
598 | 2.04k | text.data.bytes = str, text.size = size; |
599 | 2.04k | return gs_text_begin(pgs, &text, mem, ppte); |
600 | 2.04k | } |
601 | | int |
602 | | gs_charboxpath_begin(gs_gstate * pgs, const byte * str, uint size, |
603 | | bool stroke_path, gs_memory_t * mem, gs_text_enum_t ** ppte) |
604 | 0 | { |
605 | 0 | gs_text_params_t text; |
606 | |
|
607 | 0 | text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH | |
608 | 0 | (stroke_path ? TEXT_DO_TRUE_CHARBOXPATH : TEXT_DO_FALSE_CHARBOXPATH); |
609 | 0 | text.data.bytes = str, text.size = size; |
610 | 0 | return gs_text_begin(pgs, &text, mem, ppte); |
611 | 0 | } |
612 | | int |
613 | | gs_glyphpath_begin(gs_gstate * pgs, gs_glyph glyph, bool stroke_path, |
614 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
615 | 0 | { |
616 | 0 | gs_text_params_t text; |
617 | 0 | int result; |
618 | |
|
619 | 0 | text.operation = TEXT_FROM_SINGLE_GLYPH | TEXT_RETURN_WIDTH | |
620 | 0 | (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH); |
621 | 0 | text.data.d_glyph = glyph; |
622 | 0 | text.size = 1; |
623 | 0 | result = gs_text_begin(pgs, &text, mem, ppte); |
624 | 0 | if (result == 0) |
625 | 0 | setup_FontBBox_as_Metrics2(*ppte, pgs->font); |
626 | 0 | return result; |
627 | 0 | } |
628 | | int |
629 | | gs_glyphwidth_begin(gs_gstate * pgs, gs_glyph glyph, |
630 | | gs_memory_t * mem, gs_text_enum_t ** ppte) |
631 | 0 | { |
632 | 0 | gs_text_params_t text; |
633 | 0 | int result; |
634 | |
|
635 | 0 | text.operation = TEXT_FROM_SINGLE_GLYPH | TEXT_DO_NONE | TEXT_RETURN_WIDTH; |
636 | 0 | text.data.d_glyph = glyph; |
637 | 0 | text.size = 1; |
638 | 0 | result = gs_text_begin(pgs, &text, mem, ppte); |
639 | 0 | if (result == 0) |
640 | 0 | setup_FontBBox_as_Metrics2(*ppte, pgs->font); |
641 | 0 | return result; |
642 | 0 | } |
643 | | |
644 | | /* Restart processing with different parameters. */ |
645 | | int |
646 | | gs_text_restart(gs_text_enum_t *pte, const gs_text_params_t *text) |
647 | 0 | { |
648 | 0 | gs_text_enum_t tenum; |
649 | |
|
650 | 0 | tenum = *pte; |
651 | 0 | tenum.text = *text; |
652 | 0 | gs_text_enum_init_dynamic(&tenum, pte->orig_font); |
653 | 0 | setup_FontBBox_as_Metrics2(pte, pte->orig_font); |
654 | 0 | return gs_text_resync(pte, &tenum); |
655 | 0 | } |
656 | | |
657 | | /* |
658 | | * Resync text processing with new parameters and string position. |
659 | | */ |
660 | | int |
661 | | gs_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom) |
662 | 0 | { |
663 | 0 | return pte->procs->resync(pte, pfrom); |
664 | 0 | } |
665 | | |
666 | | /* Process text after 'begin'. */ |
667 | | int |
668 | | gs_text_process(gs_text_enum_t * pte) |
669 | 7.99M | { |
670 | 7.99M | return pte->procs->process(pte); |
671 | 7.99M | } |
672 | | |
673 | | /* Access elements of the enumerator. */ |
674 | | gs_font * |
675 | | gs_text_current_font(const gs_text_enum_t *pte) |
676 | 0 | { |
677 | 0 | return pte->current_font; |
678 | 0 | } |
679 | | gs_char |
680 | | gs_text_current_char(const gs_text_enum_t *pte) |
681 | 1.15M | { |
682 | 1.15M | return pte->returned.current_char; |
683 | 1.15M | } |
684 | | gs_char |
685 | | gs_text_next_char(const gs_text_enum_t *pte) |
686 | 0 | { |
687 | 0 | const uint operation = pte->text.operation; |
688 | |
|
689 | 0 | if (pte->index >= pte->text.size) |
690 | 0 | return gs_no_char; /* rangecheck */ |
691 | 0 | if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) |
692 | 0 | return pte->text.data.bytes[pte->index]; |
693 | 0 | if (operation & TEXT_FROM_CHARS) |
694 | 0 | return pte->text.data.chars[pte->index]; |
695 | 0 | return gs_no_char; /* rangecheck */ |
696 | 0 | } |
697 | | gs_glyph |
698 | | gs_text_current_glyph(const gs_text_enum_t *pte) |
699 | 1.15M | { |
700 | 1.15M | return pte->returned.current_glyph; |
701 | 1.15M | } |
702 | | int |
703 | | gs_text_total_width(const gs_text_enum_t *pte, gs_point *pwidth) |
704 | 408 | { |
705 | 408 | *pwidth = pte->returned.total_width; |
706 | 408 | return 0; |
707 | 408 | } |
708 | | |
709 | | /* Assuming REPLACE_WIDTHS is set, return the width of the i'th character. */ |
710 | | int |
711 | | gs_text_replaced_width(const gs_text_params_t *text, uint index, |
712 | | gs_point *pwidth) |
713 | 20.5M | { |
714 | 20.5M | const float *x_widths = text->x_widths; |
715 | 20.5M | const float *y_widths = text->y_widths; |
716 | | |
717 | 20.5M | if (x_widths == y_widths) { |
718 | 0 | if (x_widths) { |
719 | 0 | index *= 2; |
720 | 0 | if (index + 1 >= text->widths_size) |
721 | 0 | return_error(gs_error_rangecheck); |
722 | 0 | pwidth->x = x_widths[index]; |
723 | 0 | pwidth->y = x_widths[index + 1]; |
724 | 0 | } |
725 | 0 | else |
726 | 0 | pwidth->x = pwidth->y = 0; |
727 | 20.5M | } else { |
728 | 20.5M | if (index >= text->widths_size) |
729 | 0 | return_error(gs_error_rangecheck); |
730 | 20.5M | pwidth->x = (x_widths ? x_widths[index] : 0.0); |
731 | 20.5M | pwidth->y = (y_widths ? y_widths[index] : 0.0); |
732 | 20.5M | } |
733 | 20.5M | return 0; |
734 | 20.5M | } |
735 | | |
736 | | /* Determine whether only the width is needed. */ |
737 | | bool |
738 | | gs_text_is_width_only(const gs_text_enum_t * pte) |
739 | 4.89M | { |
740 | 4.89M | return pte->procs->is_width_only(pte); |
741 | 4.89M | } |
742 | | |
743 | | /* Return the width of the current character. */ |
744 | | int |
745 | | gs_text_current_width(const gs_text_enum_t * pte, gs_point *pwidth) |
746 | 0 | { |
747 | 0 | return pte->procs->current_width(pte, pwidth); |
748 | 0 | } |
749 | | |
750 | | /* Set text metrics and optionally enable caching. */ |
751 | | int |
752 | | gs_text_set_cache(gs_text_enum_t * pte, const double *values, |
753 | | gs_text_cache_control_t control) |
754 | 18.6k | { |
755 | 18.6k | return pte->procs->set_cache(pte, values, control); |
756 | 18.6k | } |
757 | | int |
758 | | gs_text_setcharwidth(gs_text_enum_t * pte, const double wxy[2]) |
759 | 191k | { |
760 | 191k | return pte->procs->set_cache(pte, wxy, TEXT_SET_CHAR_WIDTH); |
761 | 191k | } |
762 | | int |
763 | | gs_text_setcachedevice(gs_text_enum_t * pte, const double wbox[6]) |
764 | 971k | { |
765 | 971k | return pte->procs->set_cache(pte, wbox, TEXT_SET_CACHE_DEVICE); |
766 | 971k | } |
767 | | int |
768 | | gs_text_setcachedevice2(gs_text_enum_t * pte, const double wbox2[10]) |
769 | 2.75M | { |
770 | 2.75M | return pte->procs->set_cache(pte, wbox2, TEXT_SET_CACHE_DEVICE2); |
771 | 2.75M | } |
772 | | |
773 | | /* Retry processing the last character without caching. */ |
774 | | int |
775 | | gs_text_retry(gs_text_enum_t * pte) |
776 | 0 | { |
777 | 0 | return pte->procs->retry(pte); |
778 | 0 | } |
779 | | |
780 | | /* Release the text processing structures. */ |
781 | | void |
782 | | gx_default_text_release(gs_text_enum_t *pte, client_name_t cname) |
783 | 6.81M | { |
784 | 6.81M | rc_decrement_only(pte->dev, cname); |
785 | 6.81M | rc_decrement_only(pte->imaging_dev, cname); |
786 | 6.81M | } |
787 | | void |
788 | | rc_free_text_enum(gs_memory_t * mem, void *obj, client_name_t cname) |
789 | 6.81M | { |
790 | 6.81M | gs_text_enum_t *pte = obj; |
791 | | |
792 | 6.81M | pte->procs->release(pte, cname); |
793 | 6.81M | rc_free_struct_only(mem, obj, cname); |
794 | 6.81M | } |
795 | | void |
796 | | gs_text_release(gs_gstate *pgs, gs_text_enum_t * pte, client_name_t cname) |
797 | 6.81M | { |
798 | 6.81M | if (pgs != NULL && pgs->black_textvec_state != NULL) |
799 | 0 | gsicc_restore_blacktextvec(pgs, true); |
800 | 6.81M | rc_decrement_only(pte, cname); |
801 | 6.81M | } |
802 | | |
803 | | /* ---------------- Default font rendering procedures ---------------- */ |
804 | | |
805 | | /* Default fstack initialization procedure. */ |
806 | | int |
807 | | gs_default_init_fstack(gs_text_enum_t *pte, gs_font *pfont) |
808 | 6.35M | { |
809 | 6.35M | pte->fstack.depth = -1; |
810 | 6.35M | return 0; |
811 | 6.35M | } |
812 | | |
813 | | /* Default next-glyph procedure. */ |
814 | | int |
815 | | gs_default_next_char_glyph(gs_text_enum_t *pte, gs_char *pchr, gs_glyph *pglyph) |
816 | 48.0M | { |
817 | 48.0M | if (pte->index >= pte->text.size) |
818 | 8.75M | return 2; |
819 | 39.2M | if (pte->text.operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) { |
820 | | /* ordinary string */ |
821 | 39.2M | *pchr = pte->text.data.bytes[pte->index]; |
822 | 39.2M | if (pte->outer_CID != GS_NO_GLYPH) |
823 | 0 | *pglyph = pte->outer_CID; |
824 | 39.2M | else |
825 | 39.2M | *pglyph = GS_NO_GLYPH; |
826 | 39.2M | } else if (pte->text.operation & TEXT_FROM_SINGLE_GLYPH) { |
827 | | /* glyphshow or glyphpath */ |
828 | 0 | *pchr = gs_no_char; |
829 | 0 | *pglyph = pte->text.data.d_glyph; |
830 | 37.1k | } else if (pte->text.operation & TEXT_FROM_GLYPHS) { |
831 | 0 | *pchr = gs_no_char; |
832 | 0 | *pglyph = pte->text.data.glyphs[pte->index]; |
833 | 37.1k | } else if (pte->text.operation & TEXT_FROM_SINGLE_CHAR) { |
834 | 0 | *pchr = pte->text.data.d_char; |
835 | 0 | *pglyph = GS_NO_GLYPH; |
836 | 37.1k | } else if (pte->text.operation & TEXT_FROM_CHARS) { |
837 | 37.1k | *pchr = pte->text.data.chars[pte->index]; |
838 | 37.1k | *pglyph = GS_NO_GLYPH; |
839 | 37.1k | } else |
840 | 0 | return_error(gs_error_rangecheck); /* shouldn't happen */ |
841 | 39.2M | pte->index++; |
842 | 39.2M | return 0; |
843 | 39.2M | } |
844 | | |
845 | | /* Dummy (ineffective) BuildChar/BuildGlyph procedure */ |
846 | | int |
847 | | gs_no_build_char(gs_show_enum *pte, gs_gstate *pgs, gs_font *pfont, |
848 | | gs_char chr, gs_glyph glyph) |
849 | 1.15M | { |
850 | 1.15M | return 1; /* failure, but not error */ |
851 | 1.15M | } |