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