/src/ghostpdl/pcl/pcl/pctext.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 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 | | /* pctext.c - PCL5 text printing commands */ |
18 | | |
19 | | #include "math_.h" |
20 | | #include "gx.h" |
21 | | #include "gsimage.h" |
22 | | #include "plvalue.h" |
23 | | #include "plvocab.h" |
24 | | #include "pcommand.h" |
25 | | #include "pcstate.h" |
26 | | #include "pcdraw.h" |
27 | | #include "pcfont.h" |
28 | | #include "pcursor.h" |
29 | | #include "pcpage.h" |
30 | | #include "pcfrgrnd.h" |
31 | | #include "gdebug.h" |
32 | | #include "gscoord.h" |
33 | | #include "gsline.h" |
34 | | #include "gspaint.h" |
35 | | #include "gspath.h" |
36 | | #include "gspath2.h" |
37 | | #include "gsrop.h" |
38 | | #include "gsstate.h" |
39 | | #include "gxchar.h" |
40 | | #include "gxfont.h" /* for setting next_char proc */ |
41 | | #include "gxstate.h" |
42 | | |
43 | | #include "gxdevsop.h" /* For special ops */ |
44 | | #include "gsdevice.h" /* for gs_currentdevice */ |
45 | | |
46 | | /* pseudo-"dots" (actually 1/300" units) used in underline only */ |
47 | 0 | #define dots(n) ((float)(7200 / 300 * n)) |
48 | | |
49 | | /* |
50 | | * Install a font in the graphic state. |
51 | | */ |
52 | | static void |
53 | | set_gs_font(pcl_state_t * pcs) |
54 | 11.6M | { |
55 | 11.6M | gs_font *pfont = (gs_font *) pcs->font->pfont; |
56 | | |
57 | 11.6M | gs_setfont(pcs->pgs, pfont); |
58 | | /* font scaling is reflected directly in the ctm */ |
59 | 11.6M | pfont->FontMatrix = pfont->orig_FontMatrix; |
60 | 11.6M | } |
61 | | |
62 | | bool |
63 | | pcl_downloaded_and_bound(const pl_font_t * plfont) |
64 | 34.7M | { |
65 | 34.7M | return (plfont->storage != pcds_internal && pl_font_is_bound(plfont)); |
66 | 34.7M | } |
67 | | |
68 | | /* |
69 | | * Check if a character code is considered "printable" by given symbol set. |
70 | | */ |
71 | | bool |
72 | | char_is_printable(const pl_font_t *font, const pl_symbol_map_t *map, gs_char chr, bool is_stick, bool literal) |
73 | 18.4M | { |
74 | 18.4M | bool printable = false; |
75 | | |
76 | 18.4M | if (literal) { /* transparent data */ |
77 | 1.49M | printable = true; |
78 | 16.9M | } else { |
79 | 16.9M | if (is_stick) { |
80 | 164k | printable = (chr >= ' ') && (chr <= 0xff); |
81 | 16.8M | } else { |
82 | 16.8M | int map_type = 0; |
83 | 16.8M | if (map == 0 || pcl_downloaded_and_bound(font)) { |
84 | | /* PCL TRM 11-18 */ |
85 | 0 | if (font) |
86 | 0 | { |
87 | 0 | map_type = font->font_type; |
88 | 0 | } |
89 | 16.8M | } else { |
90 | | /* PCL TRM 10-7 |
91 | | * symbol map type overrides, font map type |
92 | | */ |
93 | 16.8M | map_type = map->type; |
94 | 16.8M | } |
95 | | |
96 | | /* We do not treat map type as defined in the |
97 | | specification. Instead the default is to use the behavior we have |
98 | | observed on several HP devices: Map type 0 is treated as map type |
99 | | 1. */ |
100 | 16.8M | if (map_type == 0) { |
101 | 0 | map_type = 1; |
102 | 0 | } |
103 | | |
104 | 16.8M | if (map_type == 1) { |
105 | 2.68M | chr &= 0x7f; |
106 | 2.68M | printable = (chr >= ' '); /* 0-31 and 128-159 are not printable */ |
107 | 14.1M | } else if (map_type >= 2) { |
108 | | /* 2 is correct but will force all types above 2 here */ |
109 | 14.1M | if ((chr == 0) || (chr == '\033') || |
110 | 12.7M | ((chr >= '\007') && (chr <= '\017'))) |
111 | 1.61M | printable = false; |
112 | 12.5M | else |
113 | 12.5M | printable = true; |
114 | 14.1M | } |
115 | 16.8M | } |
116 | 16.9M | } |
117 | 18.4M | return printable; |
118 | 18.4M | } |
119 | | |
120 | | static bool |
121 | | substituting_allowed(pcl_state_t * pcs, gs_char mapped_chr) |
122 | 105k | { |
123 | 105k | gs_char remapped_chr; /* NB wrong type */ |
124 | | |
125 | 105k | if ( |
126 | | /* msl not yet supported. */ |
127 | 105k | (pcs->map && pcs->map->format == 1) || |
128 | | /* by experiment HP does not support substitution with bitmap fonts */ |
129 | 105k | (pcs->font->scaling_technology == plfst_bitmap) || |
130 | | /* the font must be downloaded */ |
131 | 105k | (!(pcs->font->storage & pcds_downloaded))) |
132 | 105k | return false; |
133 | | |
134 | | /* mapped chr is something of a misnomer, if the font is bound and |
135 | | downloaded it has been identity mappped. */ |
136 | | |
137 | 0 | remapped_chr = pl_map_symbol(pcs->map, mapped_chr, false, /* storage not internal */ |
138 | 0 | false, /* unicode not msl */ |
139 | 0 | pcs->memory); |
140 | | |
141 | | /* now we can assume the characters are unicode */ |
142 | 0 | if ( |
143 | | /* arrows */ |
144 | 0 | ((remapped_chr >= 0x2190) && (remapped_chr <= 0x21FF)) || |
145 | | /* coptic */ |
146 | 0 | ((remapped_chr >= 0x0370) && (remapped_chr <= 0x03FF)) || |
147 | | /* math operators */ |
148 | 0 | ((remapped_chr >= 0x2200) && (remapped_chr <= 0x22FF)) || |
149 | | /* box drawing characters */ |
150 | 0 | ((remapped_chr >= 0x2500) && (remapped_chr <= 0x257F)) || |
151 | | /* block elements (contiguous with box drawing) */ |
152 | 0 | ((remapped_chr >= 0x2580) && (remapped_chr <= 0x259F)) || |
153 | | /* Geometric shapes (contiguos with block elements) */ |
154 | 0 | ((remapped_chr >= 0x25A0) && (remapped_chr <= 0x25FF)) || |
155 | | /* miscellaneous symbols */ |
156 | 0 | ((remapped_chr >= 0x2600) && (remapped_chr <= 0x26FF)) || |
157 | | /* miscellaneous technical */ |
158 | 0 | ((remapped_chr >= 0x2300) && (remapped_chr <= 0x23FF)) || |
159 | | /* general punctuation */ |
160 | 0 | ((remapped_chr >= 0x2000) && (remapped_chr <= 0x206F)) || |
161 | | /* vertical line, less than, greater than, low line, or micro sign character, */ |
162 | 0 | ((remapped_chr == 0x007C) || (remapped_chr == 0x003C) || |
163 | 0 | (remapped_chr == 0x003E) || (remapped_chr == 0x005F) || |
164 | 0 | (remapped_chr == 0x00B5)) |
165 | 0 | ) |
166 | 0 | return true; |
167 | 0 | return false; |
168 | 0 | } |
169 | | |
170 | | /* |
171 | | * Retrieve the next character identifier from a string. |
172 | | * |
173 | | * Both the string pointer and the length are modified. |
174 | | * |
175 | | * The final operand is true if the text was provided via the literal |
176 | | * (transparent) text command: ESC & p <nbytes> X. This distinction is |
177 | | * important for characters that are not considered printable by the |
178 | | * current symbol set. Normally, such characters are ignored. But if they |
179 | | * resulted from the literal (transparent) text command, they are handled as |
180 | | * spaces. Characters that are mapped by the symbol set but are not in a font |
181 | | * are always dealt with as space characters. |
182 | | * |
183 | | * The special handling provided for the character code 32 below is not, |
184 | | * in fact, correct. PCL fonts may map non-space characters to code 32, and |
185 | | * if this is done no special handling is provided for this code; in PCL, |
186 | | * a space character is a character not present in the font. Unfortunately, |
187 | | * some of the resident fonts used have explicit space characters, and to |
188 | | * handle the hmi properly when these fonts are used, this code must handle |
189 | | * fonts that have actual characters at code 32 improperly. |
190 | | * |
191 | | * Returns 0 on success, 2 if the string is exhausted. Note that it is not an |
192 | | * error for the string to end in the middle of a 2-byte sequence. |
193 | | */ |
194 | | static int |
195 | | get_next_char(pcl_state_t * pcs, |
196 | | const byte ** ppb, |
197 | | uint * plen, |
198 | | gs_char * pchr, |
199 | | gs_char * porig_char, |
200 | | bool * pis_space, |
201 | | bool * pprint_undefined, |
202 | | bool literal, gs_point * pwidth, bool * unstyled_substitution) |
203 | 21.6M | { |
204 | 21.6M | const byte *pb = *ppb; |
205 | 21.6M | int len = *plen; |
206 | 21.6M | pl_font_t *plfont = pcs->font; |
207 | 21.6M | bool substituting = false; |
208 | 21.6M | int code = 0; |
209 | 21.6M | gs_char chr; |
210 | 21.6M | gs_char mapped_chr; /* NB wrong type */ |
211 | 21.6M | bool db; |
212 | | |
213 | 21.6M | if (len <= 0) |
214 | 3.66M | return 2; |
215 | 18.0M | *pis_space = false; |
216 | 18.0M | *unstyled_substitution = false; |
217 | 18.0M | chr = pcl_char_get_char(pcs->text_parsing_method, &pb, len); |
218 | | /* invalid char: pb has not been incremented */ |
219 | 18.0M | if (pb == *ppb) { |
220 | 0 | pb++; |
221 | 0 | } |
222 | 18.0M | len -= (pb - *ppb); |
223 | 18.0M | *ppb = pb; |
224 | 18.0M | *plen = len; |
225 | 18.0M | *porig_char = chr; |
226 | | /* check if the code is considered "printable" in the current symbol set */ |
227 | 18.0M | if (!char_is_printable(pcs->font, pcs->map, chr, false, literal)) { |
228 | 1.70M | *pis_space = literal; |
229 | 1.70M | *pchr = 0xffff; |
230 | 1.70M | return 0; |
231 | 1.70M | } |
232 | | |
233 | | /* map the symbol. If the font is downloaded and bound there is |
234 | | no symbol set. We do use the symbol set for internal bound |
235 | | fonts. NB WE AREN'T HAPPY WITH THIS LABEL & GOTO. */ |
236 | 16.2M | r:db = pcl_downloaded_and_bound(plfont); |
237 | 16.2M | mapped_chr = pl_map_symbol((db ? NULL : pcs->map), chr, |
238 | 16.2M | plfont->storage == pcds_internal, |
239 | 16.2M | plfont->font_type == plft_MSL, |
240 | 16.2M | pcs->memory); |
241 | 16.2M | *pchr = mapped_chr; |
242 | 16.2M | if (mapped_chr == 0xffff) { |
243 | 39.0k | if ((plfont->storage != pcds_internal) && |
244 | 0 | (pl_font_char_width |
245 | 0 | (plfont, (void *)(pcs->pgs), mapped_chr, pwidth) == 0)) { |
246 | 0 | *pprint_undefined = true; |
247 | 0 | return 0; |
248 | 0 | } |
249 | 39.0k | *pis_space = true; |
250 | 39.0k | return 0; |
251 | 39.0k | } |
252 | | |
253 | | /* NB we assume all internal fonts use unicode */ |
254 | 16.2M | if (plfont->storage == pcds_internal && mapped_chr == 0x0020 |
255 | 77.9k | && !substituting) { |
256 | 77.9k | *pis_space = true; |
257 | 77.9k | *pchr = 0xffff; |
258 | 77.9k | return 0; |
259 | 77.9k | } |
260 | | |
261 | | /* For internal fonts we simulate the font missing the space |
262 | | character here. The character complement is checked to see if |
263 | | the the font is Western Latin and Unicode 0020. We could |
264 | | also check for an MSL space here but we know the internal |
265 | | reportoire will never contain an MSL font that requires |
266 | | simulating a missing space character. */ |
267 | 16.1M | if (plfont->storage == pcds_internal && |
268 | 16.1M | chr == 0x0020 && |
269 | 0 | plfont->character_complement[5] == 0x3f && |
270 | 0 | pl_complement_to_vocab(plfont->character_complement) == |
271 | 0 | plgv_Unicode) { |
272 | 0 | *pis_space = true; |
273 | 0 | *pchr = 0xffff; |
274 | 0 | return 0; |
275 | 0 | } |
276 | | |
277 | | /* check if the character is in the font and get the character |
278 | | width at the same time */ |
279 | 16.1M | if (*pis_space == false) |
280 | 16.1M | if (pl_font_char_width(plfont, (void *)(pcs->pgs), mapped_chr, pwidth) |
281 | 16.1M | == 0) |
282 | 16.0M | return 0; |
283 | | |
284 | | /* |
285 | | * Try an unstyled substitution |
286 | | */ |
287 | 105k | if (!substituting && substituting_allowed(pcs, db ? mapped_chr : chr)) { |
288 | 0 | pcl_decache_font(pcs, -1, true); |
289 | 0 | code = pcl_recompute_font(pcs, true); |
290 | 0 | if (code < 0) |
291 | 0 | return code; |
292 | 0 | substituting = true; |
293 | 0 | *unstyled_substitution = true; |
294 | 0 | plfont = pcs->font; |
295 | 0 | set_gs_font(pcs); |
296 | 0 | goto r; |
297 | 0 | } |
298 | | |
299 | | /* we substituted and didn't find the character in the font. |
300 | | Restore the old font */ |
301 | 105k | if (substituting) { |
302 | 0 | pcl_decache_font(pcs, -1, true); |
303 | 0 | code = pcl_recompute_font(pcs, false); |
304 | 0 | if (code < 0) |
305 | 0 | return code; |
306 | 0 | set_gs_font(pcs); |
307 | 0 | } |
308 | | |
309 | | /* |
310 | | * If we get to this point deem the character an undefined |
311 | | * character - a space in pcl. |
312 | | */ |
313 | 105k | *pis_space = true; |
314 | 105k | *pchr = 0xffff; |
315 | 105k | return 0; |
316 | 105k | } |
317 | | /* |
318 | | * return length of multibyte sequence from starting byte |
319 | | * replacement of macro pcl_char_is_2_byte, UTF-8 sequence length may be up to 6 bytes |
320 | | * |
321 | | * Returns 0 for invalid byte, byte length > 0 of multibyte character sequence |
322 | | */ |
323 | | int |
324 | | pcl_char_bytelen(byte ch, pcl_text_parsing_method_t tpm) |
325 | 28.9M | { |
326 | | |
327 | 28.9M | int bytelen = 1; |
328 | | |
329 | 28.9M | switch (tpm) { |
330 | 27.8M | default: |
331 | | /* byte length defaults to 1 */ |
332 | 27.8M | break; |
333 | | |
334 | 27.8M | case tpm_21_DBCS7: |
335 | | /* 0x21-0xff are double-byte */ |
336 | 0 | bytelen = (ch < 0x21) ? 1 : 2; |
337 | 0 | break; |
338 | | |
339 | 0 | case tpm_31_sjis: |
340 | | /* 0x81-0x9f, 0xe0-0xfc are double-byte */ |
341 | 0 | bytelen = (ch < 0x81 || (ch > 0x9f && ch < 0xe0) |
342 | 0 | || ch > 0xfc) ? 1 : 2; |
343 | 0 | break; |
344 | | |
345 | 0 | case tpm_38_DBCS8: |
346 | | /* 0x80-0xff are double-byte */ |
347 | 0 | bytelen = (ch < 0x80) ? 1 : 2; |
348 | 0 | break; |
349 | 1.07M | case tpm_83_utf8: |
350 | 1.07M | case tpm_1008_utf8: |
351 | 1.07M | if (ch < 0x80) { |
352 | | /* 0xxxxxxx */ |
353 | 910k | bytelen = 1; |
354 | 910k | break; |
355 | 910k | } |
356 | 168k | if (ch < 0xc2) { |
357 | 0 | bytelen = 0; /* illegal */ |
358 | 0 | break; |
359 | 0 | } |
360 | 168k | if (ch < 0xe0) { |
361 | | /* 110XXXXx 10xxxxxx */ |
362 | 93.9k | bytelen = 2; |
363 | 93.9k | break; |
364 | 93.9k | } |
365 | 74.3k | if (ch < 0xf0) { |
366 | | /* 1110XXXX 10Xxxxxx 10xxxxxx */ |
367 | 74.3k | bytelen = 3; |
368 | 74.3k | break; |
369 | 74.3k | } |
370 | 14 | if (ch < 0xf8) { |
371 | | /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ |
372 | 14 | bytelen = 4; |
373 | 14 | break; |
374 | 14 | } |
375 | 0 | if (ch < 0xfc) { |
376 | | /* 111110XX 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
377 | 0 | bytelen = 5; |
378 | 0 | break; |
379 | 0 | } |
380 | 0 | if (ch < 0xfe) { |
381 | | /* 1111110X 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
382 | 0 | bytelen = 6; |
383 | 0 | break; |
384 | 0 | } |
385 | 0 | bytelen = 0; /* illegal */ |
386 | 0 | break; |
387 | 28.9M | } |
388 | 28.9M | return bytelen; |
389 | 28.9M | } |
390 | | /* |
391 | | * convert multibyte sequence to unicode (16-bit) |
392 | | * Both the string pointer and the length are modified. |
393 | | * |
394 | | * Returns 0 for invalid byte, byte length > 0 of multibyte character sequence |
395 | | */ |
396 | | gs_char |
397 | | pcl_char_get_char(pcl_text_parsing_method_t tpm, const byte ** psrc, |
398 | | int src_len) |
399 | | /* src_len minimum 1 */ |
400 | 18.0M | { |
401 | 18.0M | gs_char chr; |
402 | 18.0M | const byte *src = *psrc; |
403 | 18.0M | int bytelen = pcl_char_bytelen(src[0], tpm); |
404 | | |
405 | 18.0M | if (bytelen == 0 || bytelen > src_len) { |
406 | 0 | return INVALID_UC; |
407 | 0 | } |
408 | 18.0M | switch (tpm) { |
409 | 17.4M | default: |
410 | 17.4M | chr = src[0]; |
411 | 17.4M | break; |
412 | | |
413 | 0 | case tpm_21_DBCS7: |
414 | | /* 0x21-0xff are double-byte */ |
415 | 0 | chr = (src[0] < 0x21) ? src[0] : (src[0] << 8 | src[1]); |
416 | 0 | break; |
417 | | |
418 | 0 | case tpm_31_sjis: |
419 | | /* 0x81-0x9f, 0xe0-0xfc are double-byte */ |
420 | 0 | chr = (src[0] < 0x81 || (src[0] > 0x9f && src[0] < 0xe0) |
421 | 0 | || src[0] > 0xfc) ? src[0] : (src[0] << 8 | src[1]); |
422 | 0 | break; |
423 | | |
424 | 0 | case tpm_38_DBCS8: |
425 | | /* 0x80-0xff are double-byte */ |
426 | 0 | chr = (src[0] < 0x80) ? src[0] : (src[0] << 8 | src[1]); |
427 | 0 | break; |
428 | 533k | case tpm_83_utf8: |
429 | 533k | case tpm_1008_utf8: |
430 | 533k | if (src[0] < 0x80) { |
431 | | /* 0xxxxxxx */ |
432 | 449k | chr = src[0]; |
433 | 449k | break; |
434 | 449k | } |
435 | 84.1k | if (src[0] < 0xc2) { |
436 | 0 | chr = INVALID_UC; |
437 | 0 | break; |
438 | 0 | } |
439 | 84.1k | if (src[0] < 0xe0) { |
440 | | /* 110XXXXx 10xxxxxx */ |
441 | 46.9k | chr = (src[0] & 0x1f); |
442 | 46.9k | chr = (chr << 6) | (src[1] & 0x3f); |
443 | 46.9k | break; |
444 | 46.9k | } |
445 | 37.1k | if (src[0] < 0xf0) { |
446 | | /* 1110XXXX 10Xxxxxx 10xxxxxx */ |
447 | 37.1k | chr = (src[0] & 0x0f); |
448 | 37.1k | chr = (chr << 6) | (src[1] & 0x3f); |
449 | 37.1k | chr = (chr << 6) | (src[2] & 0x3f); |
450 | 37.1k | break; |
451 | 37.1k | } |
452 | 7 | if (src[0] < 0xf8) { |
453 | | /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ |
454 | | /* chr is 16 bit: overflow */ |
455 | 7 | chr = INVALID_UC; |
456 | 7 | break; |
457 | 7 | } |
458 | 0 | if (src[0] < 0xfc) { |
459 | | /* 111110XX 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
460 | | /* chr is 16 bit: overflow */ |
461 | 0 | chr = INVALID_UC; |
462 | 0 | break; |
463 | 0 | } |
464 | 0 | if (src[0] < 0xfe) { |
465 | | /* 1111110X 10XXxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
466 | | /* chr is 16 bit: overflow */ |
467 | 0 | chr = INVALID_UC; |
468 | 0 | break; |
469 | 0 | } |
470 | 0 | chr = INVALID_UC; |
471 | 0 | break; |
472 | 18.0M | } |
473 | 18.0M | *psrc += bytelen; |
474 | 18.0M | return chr; |
475 | 18.0M | } |
476 | | |
477 | | /* |
478 | | * Draw the foreground of a character. For transparent text this is the only |
479 | | * part that must be drawn. |
480 | | */ |
481 | | static int |
482 | | show_char_foreground(const pcl_state_t * pcs, const gs_char * pbuff) |
483 | 9.37M | { |
484 | 9.37M | int code = 0; |
485 | 9.37M | gs_text_enum_t *penum; |
486 | 9.37M | pl_font_t *plfont = pcs->font; |
487 | 9.37M | gs_font *pfont = plfont->pfont; |
488 | 9.37M | gs_text_params_t text; |
489 | | |
490 | | /* set vertical writing if -1 which requires double bytes or 1 */ |
491 | 9.37M | if ((pcs->text_path == -1 && ((pbuff[0] & 0xff00) != 0)) || |
492 | 9.37M | (pcs->text_path == 1)) |
493 | 0 | pfont->WMode = 1; |
494 | 9.37M | else |
495 | 9.37M | pfont->WMode = 0; |
496 | 9.37M | text.operation = TEXT_FROM_CHARS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; |
497 | 9.37M | text.data.chars = pbuff; |
498 | 9.37M | text.size = 1; |
499 | 9.37M | code = gs_text_begin(pcs->pgs, &text, pcs->memory, &penum); |
500 | 9.37M | if (code >= 0) { |
501 | 9.37M | code = gs_text_process(penum); |
502 | 9.37M | gs_text_release(pcs->pgs, penum, "show_char_foreground"); |
503 | 9.37M | } |
504 | 9.37M | return code; |
505 | 9.37M | } |
506 | | |
507 | | static int |
508 | | show_char_invisible_foreground(const pcl_state_t * pcs, const gs_char * pbuff) |
509 | 0 | { |
510 | |
|
511 | 0 | gs_c_param_list list; |
512 | 0 | dev_param_req_t request; |
513 | 0 | gs_param_name ParamName = "PreserveTrMode"; |
514 | 0 | gs_param_typed_value Param; |
515 | 0 | char *data; |
516 | 0 | gs_gstate *pgs = pcs->pgs; |
517 | 0 | uint saved_mode = gs_currenttextrenderingmode(pgs); |
518 | 0 | int code = 0; |
519 | | |
520 | | /* Interrogate the device to see if it supports Text Rendering Mode |
521 | | * If it does we can mimic the 'invisible text' by using mode 3. If it |
522 | | * doesn't then we drop the text. |
523 | | */ |
524 | 0 | data = (char *)gs_alloc_bytes(pcs->memory, 15, "temporary special_op string"); |
525 | 0 | if (data == NULL) |
526 | 0 | return_error(gs_error_VMerror); |
527 | 0 | memset(data, 0x00, 15); |
528 | 0 | memcpy(data, "PreserveTrMode", 15); |
529 | 0 | gs_c_param_list_write(&list, pcs->memory); |
530 | | /* Make a null object so that the param list won't check for requests */ |
531 | 0 | Param.type = gs_param_type_null; |
532 | 0 | list.procs->xmit_typed((gs_param_list *)&list, ParamName, &Param); |
533 | | /* Stuff the data into a structure for passing to the spec_op */ |
534 | 0 | request.Param = data; |
535 | 0 | request.list = &list; |
536 | |
|
537 | 0 | code = dev_proc(gs_currentdevice(pgs), dev_spec_op)(gs_currentdevice(pgs), gxdso_get_dev_param, |
538 | 0 | &request, sizeof(dev_param_req_t)); |
539 | |
|
540 | 0 | if (code != gs_error_undefined) { |
541 | | /* The parameter is present in the device, now we need to see its value */ |
542 | 0 | gs_c_param_list_read(&list); |
543 | 0 | list.procs->xmit_typed((gs_param_list *)&list, ParamName, &Param); |
544 | |
|
545 | 0 | if (Param.type != gs_param_type_bool) { |
546 | | /* This really shoudn't happen, but its best to be sure */ |
547 | 0 | gs_free_object(pcs->memory, data,"temporary special_op string"); |
548 | 0 | gs_c_param_list_release(&list); |
549 | 0 | return gs_error_typecheck; |
550 | 0 | } |
551 | | |
552 | 0 | if (Param.value.b) { |
553 | | /* Its true, so we can set the Tr mode to 3, draw the text |
554 | | and then reset the Tr mode */ |
555 | 0 | gs_settextrenderingmode(pgs, 3); |
556 | 0 | code = show_char_foreground(pcs, pbuff); |
557 | 0 | gs_settextrenderingmode(pgs, saved_mode); |
558 | 0 | } |
559 | 0 | } else { |
560 | 0 | code = 0; |
561 | 0 | } |
562 | 0 | gs_free_object(pcs->memory, data,"temporary special_op string"); |
563 | 0 | gs_c_param_list_release(&list); |
564 | 0 | return code; |
565 | 0 | } |
566 | | |
567 | | |
568 | | |
569 | | /* |
570 | | * draw the opaque background of a character. |
571 | | * |
572 | | * In the graphic library, characters are masks, hence they are always |
573 | | * transparent. Not so in PCL, where characters may be either opaque or |
574 | | * transparent. |
575 | | * |
576 | | * To deal with this dichotomy, opaque characters are rendered as a pair of |
577 | | * masks. One is the normal character mask; the other is the bounding box of |
578 | | * the character less the character itself. |
579 | | * |
580 | | * The manner in which the second mask is formed varies based on the font type. |
581 | | * For bitmap fonts, the inverse mask is formed as an imagemask object, with |
582 | | * inverted polarity. For scalable fonts (which have only provided a path), |
583 | | * the inverse is formed by adding the bounding box rectangle as a path to |
584 | | * the character path, and using eofill on the resultant path. |
585 | | * |
586 | | * Special handling is required to achieve the desired raster operation on the |
587 | | * "background" mask. From the point of view of the graphic library, the |
588 | | * background mask is a normal mask, and hence would utiltise the S = 0 |
589 | | * portion of the current logical operation (recall that rop's are expressed |
590 | | * in an additive sense). The desired effect is, however, the S = 1 portion |
591 | | * of the current rop, so the current rop must be inverted in the sense of the |
592 | | * source to achive the desired result. In principle, the S = 1 porition of |
593 | | * the background rop should be set to the no-op rop, but this is not necessary |
594 | | * as the source is a mask. |
595 | | * |
596 | | * An additional complication arises from the specification provided by HP for |
597 | | * handling the source opaque, pattern transparent situation. In this case, |
598 | | * the pattern affects only for the foreground pixels of the source; the back- |
599 | | * ground must be rendered as a solid, opaque white. |
600 | | */ |
601 | | static int |
602 | | show_char_background(pcl_state_t * pcs, const gs_char * pbuff) |
603 | 14.9k | { |
604 | 14.9k | gs_gstate *pgs = pcs->pgs; |
605 | 14.9k | gs_rop3_t rop = (gs_rop3_t) (pcs->logical_op); |
606 | 14.9k | const pl_font_t *plfont = pcs->font; |
607 | 14.9k | gs_font *pfont = plfont->pfont; |
608 | 14.9k | gs_point pt; |
609 | 14.9k | int code = 0, code2; |
610 | | |
611 | | /* save the graphic state and set the background raster operation */ |
612 | 14.9k | code = pcl_gsave(pcs); |
613 | 14.9k | if (code < 0) |
614 | 0 | return code; |
615 | 14.9k | if (pcs->pattern_transparent) { |
616 | 14.9k | code = pcl_set_drawing_color(pcs, pcl_pattern_solid_white, 0, false); |
617 | 14.9k | if (code < 0) { |
618 | 0 | (void)pcl_grestore(pcs); |
619 | 0 | return code; |
620 | 0 | } |
621 | 14.9k | } |
622 | | /* In this case, instead of just ignoring the unsupported rasterop we |
623 | | * abort the operation. Otherwise we end up with lots of black rectangles |
624 | | * over the text. Dropping the background works 'better'. |
625 | | */ |
626 | 14.9k | code = check_rasterops(pcs, (gs_rop3_t) rop3_know_S_1((int)rop)); |
627 | 14.9k | if (code < 0) { |
628 | 0 | (void)pcl_grestore(pcs); |
629 | 0 | return 0; |
630 | 0 | } |
631 | 14.9k | if (((code = gs_setrasterop(pgs, (gs_rop3_t) rop3_know_S_1((int)rop))) < 0) || |
632 | 14.9k | ((code = gs_currentpoint(pgs, &pt)) < 0)) { |
633 | 0 | (void)pcl_grestore(pcs); |
634 | 0 | return code; |
635 | 0 | } |
636 | | |
637 | 14.9k | if (plfont->scaling_technology == plfst_bitmap) { |
638 | 0 | gs_char chr = pbuff[0]; |
639 | 0 | gs_glyph glyph = pfont->procs.encode_char(pfont, chr, GS_NO_GLYPH); |
640 | 0 | const byte *cdata = pl_font_lookup_glyph(plfont, glyph)->data; |
641 | 0 | int nbytes; |
642 | 0 | uint used; |
643 | 0 | gs_image_enum *pen = NULL; |
644 | 0 | gs_image1_t mask; |
645 | | |
646 | | /* empty characters have no background */ |
647 | 0 | if (cdata == 0) { |
648 | 0 | return pcl_grestore(pcs); |
649 | 0 | } |
650 | | |
651 | | /* allocate the image enumerator */ |
652 | 0 | pen = |
653 | 0 | gs_image_enum_alloc(gs_gstate_memory(pgs), |
654 | 0 | "bitmap font background"); |
655 | 0 | if (pen == 0) { |
656 | 0 | (void)pcl_grestore(pcs); |
657 | 0 | return e_Memory; |
658 | 0 | } |
659 | | |
660 | | /* translate the origin to the ul corner of the image */ |
661 | 0 | pt.x += (float)pl_get_int16(cdata + 6); |
662 | 0 | pt.y -= (float)pl_get_int16(cdata + 8); |
663 | 0 | gs_translate(pgs, pt.x, pt.y); |
664 | | |
665 | | /* set up and render the image mask */ |
666 | 0 | gs_image_t_init_mask(&mask, false); |
667 | 0 | mask.adjust = false; |
668 | 0 | mask.Width = pl_get_uint16(cdata + 10); |
669 | 0 | mask.Height = pl_get_uint16(cdata + 12); |
670 | 0 | nbytes = ((mask.Width + 7) / 8) * mask.Height; |
671 | 0 | code = gs_image_init(pen, &mask, false, false, pgs); |
672 | 0 | if (code >= 0) |
673 | 0 | code = gs_image_next(pen, cdata + 16, nbytes, &used); |
674 | | |
675 | | /* clean up */ |
676 | 0 | code2 = gs_image_cleanup(pen, pgs); |
677 | 0 | if (code >= 0) |
678 | 0 | code = code2; |
679 | 0 | gs_free_object(gs_gstate_memory(pgs), pen, "bitmap font background"); |
680 | |
|
681 | 14.9k | } else { |
682 | 14.9k | gs_text_params_t text; |
683 | 14.9k | gs_rect bbox; |
684 | 14.9k | gs_text_enum_t *penum = NULL; |
685 | | |
686 | | /* clear the path; start the new one from the current point */ |
687 | 14.9k | if (((code = gs_newpath(pgs)) < 0) || |
688 | 14.9k | ((code = gs_moveto(pgs, pt.x, pt.y)) < 0)) { |
689 | 0 | (void)pcl_grestore(pcs); |
690 | 0 | return code; |
691 | 0 | } |
692 | 14.9k | text.data.chars = pbuff; |
693 | 14.9k | text.size = 1; |
694 | 14.9k | text.operation = |
695 | 14.9k | TEXT_FROM_CHARS | TEXT_DO_TRUE_CHARPATH | TEXT_RETURN_WIDTH; |
696 | 14.9k | code = gs_text_begin(pgs, &text, pcs->memory, &penum); |
697 | 14.9k | if (code >= 0) |
698 | 14.9k | code = gs_text_process(penum); |
699 | 14.9k | if (code >= 0) { |
700 | | /* append the characters bounding box and use eofill */ |
701 | 14.9k | if ((code = gs_pathbbox(pgs, &bbox)) >= 0 && |
702 | 14.9k | (code = gs_rectappend(pgs, &bbox, 1)) >= 0 && |
703 | 14.9k | (code = gs_eofill(pgs)) >= 0) |
704 | 14.9k | { |
705 | | /* fall through */ |
706 | 14.9k | } |
707 | 14.9k | } |
708 | 14.9k | gs_text_release(pgs, penum, "show_char_background"); |
709 | 14.9k | } |
710 | | |
711 | 14.9k | code2 = pcl_grestore(pcs); |
712 | 14.9k | if (code >= 0) |
713 | 14.9k | code = code2; |
714 | 14.9k | return code; |
715 | 14.9k | } |
716 | | |
717 | | /* |
718 | | * Set color and ctm for a font |
719 | | */ |
720 | | static int |
721 | | pcl_set_gstate_for_font(pcl_state_t *pcs, const gs_point *scale) |
722 | 11.6M | { |
723 | 11.6M | int code; |
724 | 11.6M | code = pcl_set_drawing_color(pcs, |
725 | 11.6M | pcs->pattern_type, |
726 | 11.6M | pcs->current_pattern_id, false); |
727 | 11.6M | if (code >= 0) |
728 | 11.6M | code = pcl_set_graphics_state(pcs); |
729 | 11.6M | if (code < 0) |
730 | 0 | return code; |
731 | 11.6M | set_gs_font(pcs); |
732 | 11.6M | return gs_scale(pcs->pgs, scale->x, scale->y); |
733 | 11.6M | } |
734 | | |
735 | | /* |
736 | | * get the advance width. |
737 | | */ |
738 | | static int |
739 | | pcl_get_width(pcl_state_t * pcs, gs_point * advance_vector, |
740 | | const gs_point * pscale, gs_char chr, bool is_space, |
741 | | bool print_undefined, double *output_width) |
742 | 18.0M | { |
743 | 18.0M | int code = 0; |
744 | 18.0M | pcl_font_selection_t *pfp = &(pcs->font_selection[pcs->font_selected]); |
745 | 18.0M | double width; |
746 | | |
747 | 18.0M | if (chr != 0xffff || print_undefined) { |
748 | 16.0M | if (!pfp->params.proportional_spacing || is_space) |
749 | 14.5M | { |
750 | 14.5M | code = pcl_update_hmi_cp(pcs); |
751 | 14.5M | if (code < 0) |
752 | 0 | return code; |
753 | | |
754 | 14.5M | width = pcs->hmi_cp; |
755 | 14.5M | } |
756 | 1.54M | else { |
757 | 1.54M | if (pcs->font->scaling_technology == plfst_TrueType || |
758 | 1.54M | pcs->font->scaling_technology == plfst_MicroType) { |
759 | 1.54M | double tmp; |
760 | | |
761 | 1.54M | tmp = pscale->x / (double) pcs->uom_cp + 0.5; |
762 | 1.54M | tmp -= fmod(tmp, (double) 1.0); |
763 | 1.54M | tmp *= (double) pcs->uom_cp; |
764 | 1.54M | width = advance_vector->x * tmp; |
765 | | |
766 | 1.54M | } else |
767 | 0 | width = advance_vector->x * pscale->x; |
768 | 1.54M | width += (double) pcs->uom_cp / 2.0; |
769 | 1.54M | width -= fmod(width, (double) pcs->uom_cp); |
770 | 1.54M | } |
771 | 16.0M | } else if (is_space) { |
772 | 222k | code = pcl_update_hmi_cp(pcs); |
773 | 222k | if (code < 0) |
774 | 0 | return code; |
775 | 222k | width = pcs->hmi_cp; |
776 | 222k | } |
777 | 1.70M | else |
778 | 1.70M | width = 0.0; |
779 | | /* round to nearest integral pcl units */ |
780 | 18.0M | *output_width = width; |
781 | 18.0M | return code; |
782 | 18.0M | } |
783 | | |
784 | | /* |
785 | | * Show a string of characters. Provide a general purpose function |
786 | | * that can be used in all cases (pcl_show_chars_slow) and a faster |
787 | | * function (pcl_show_chars_fast) that can be used for most |
788 | | * circumstances. The latter algorithm can print strings of |
789 | | * characters the slow algorithm only prints one character at a time. |
790 | | * |
791 | | * As is the case for other parts of this code, this code is made more complex |
792 | | * by the PostScript-centric nature of the the graphics library, and by a |
793 | | * long standing flaw in the PostScript view of fonts. Specifically, the |
794 | | * initial introduction of Encoding arrays into PostScript fonts, followed by |
795 | | * composite font mechanism, very much confused the concepts of font and text |
796 | | * parsing method. |
797 | | * |
798 | | * A font is an object which accepts a character identifier and returns a |
799 | | * "rendering" of that character (which may be a bitmap, may be a path, may |
800 | | * be an advance vector, or may be some combination of the above); it may also |
801 | | * in some cases apply this rendering to the graphic state (which may include |
802 | | * modifying the output). Whether or not a font caches or expects its client |
803 | | * to handle caching is a separate issue; there are good areguments for either |
804 | | * approach. |
805 | | * |
806 | | * A text parsing method is an object that accepts a character string and |
807 | | * returns one or more character identifiers. A text parsing method is, in |
808 | | * principle, completely independent of a font, though for historical reasons |
809 | | * the two concepts are often linked at the application level. |
810 | | * |
811 | | * Because of the PostScript origins of the graphic library, its font interface |
812 | | * handles both text parsing and rendering. To achieve flexibility, the client |
813 | | * may provide a "get next character" procedure for the graphic library font |
814 | | * machinery to work with, but this flexibility is not sufficient for PCL, as |
815 | | * the latter potentially needs to perform additional operations on each |
816 | | * character. Hence, PCL will not ask the font machiner to render more than |
817 | | * one character at a time. |
818 | | * |
819 | | * Complicating this picture is the nature of memory management in the graphic |
820 | | * library. The show class operators in PostScript generally take a string as |
821 | | * an operand. PostScript strings have the "getinterval" property: one string |
822 | | * may be part of another string. Hence strings cannot have headers. In a |
823 | | * relocating memory systems, this implies that strings must be dealt with |
824 | | * separately from other objects: they must be allocated as strings. In the |
825 | | * case of PCL, this is not necessarily the case (see, for example, the case |
826 | | * of transparent mode, below). |
827 | | * |
828 | | * The original implementation of this routine ignored this distinction and |
829 | | * could, in principle, have failed if re-location was enabled. It was also |
830 | | * rather hard to read, because it parsed the input string (at least) twice: |
831 | | * once the find the character so that PCL-specific actions could be taken, |
832 | | * then again via the font machiner. |
833 | | * |
834 | | * |
835 | | * The final operand is true if the text was provided via the literal |
836 | | * (transparent) text command: ESC & p <nbytes> X. This distinction is |
837 | | * important for characters that are not mapped by the current symbol set. */ |
838 | | |
839 | | static int |
840 | | pcl_show_chars_slow(pcl_state_t * pcs, |
841 | | const gs_point * pscale, |
842 | | const byte * str, uint size, bool literal) |
843 | 11.6M | { |
844 | 11.6M | gs_gstate *pgs = pcs->pgs; |
845 | 11.6M | gs_char buff[1]; |
846 | 11.6M | double rmargin = pcs->margins.right; |
847 | 11.6M | double page_size = pcs->xfm_state.pd_size.x; |
848 | 11.6M | bool source_opaque = !pcs->source_transparent; |
849 | 11.6M | bool invisible_pattern = is_invisible_pattern(pcs); |
850 | 11.6M | bool wrap = pcs->end_of_line_wrap; |
851 | 11.6M | bool is_space = false; |
852 | 11.6M | bool print_undefined = false; |
853 | 11.6M | bool use_rmargin = (pcs->cap.x <= rmargin); |
854 | 11.6M | gs_char chr, orig_chr; |
855 | 11.6M | int code = 0; |
856 | 11.6M | double width; |
857 | 11.6M | gs_point advance_vector; |
858 | 11.6M | bool unstyled_substitution; |
859 | | |
860 | 11.6M | width = pcs->last_width; |
861 | | |
862 | 21.6M | while (get_next_char(pcs, &str, &size, &chr, |
863 | 21.6M | &orig_chr, &is_space, &print_undefined, literal, |
864 | 21.6M | &advance_vector, &unstyled_substitution) == 0) { |
865 | 18.0M | double tmp_x; |
866 | | |
867 | | /* check if a character was found */ |
868 | 18.0M | buff[0] = chr; |
869 | | |
870 | | /* round width to integral pcl current units */ |
871 | 18.0M | code = |
872 | 18.0M | (pcl_get_width |
873 | 18.0M | (pcs, &advance_vector, pscale, chr, is_space, print_undefined, &width)); |
874 | 18.0M | if (code < 0) |
875 | 0 | return code; |
876 | | |
877 | | /* |
878 | | * Check for transitions of the left margin; this check is |
879 | | * disabled if the immediately preceding character was a back-space. |
880 | | * A move beyond the "right" logical page edge is also considered |
881 | | * a margin transition. |
882 | | * |
883 | | * A little-known feature of PCL is the effect of the line-wrap |
884 | | * command on the interpretation of the right margin. If line |
885 | | * wrap is in effect, a transition of the left margin will cause |
886 | | * a <cr><lf> operation BEFORE the current character is printed. If |
887 | | * line-wrap is not in effect, a transition of the right margin will |
888 | | * stop printing AFTER the current character is printed. |
889 | | * |
890 | | * A special case occurs in the non-wrap situation when the current |
891 | | * position exactly equals the current margin. In that case, no |
892 | | * character is printed. |
893 | | */ |
894 | 18.0M | if (!pcs->last_was_BS) { |
895 | 17.9M | if (wrap) { |
896 | 533k | if ((use_rmargin && (pcs->cap.x + width > rmargin)) || |
897 | 529k | (pcs->cap.x + width > page_size)) { |
898 | 4.18k | code = pcl_do_CR(pcs); |
899 | 4.18k | if (code < 0) |
900 | 0 | return code; |
901 | 4.18k | code = pcl_do_LF(pcs); |
902 | 4.18k | if (code < 0) |
903 | 0 | return code; |
904 | | /* A LF can cause a page feed which in turn can |
905 | | * change the CTM, reapply the current font |
906 | | * scaling */ |
907 | 4.18k | if (pcl_page_marked(pcs) == false) { |
908 | 55 | code = pcl_set_gstate_for_font(pcs, pscale); |
909 | 55 | if (code < 0) |
910 | 0 | return code; |
911 | 55 | } |
912 | 4.18k | use_rmargin = true; |
913 | 4.18k | } |
914 | 17.3M | } else { |
915 | 17.3M | if (use_rmargin && (pcs->cap.x == rmargin)) |
916 | 7.86M | break; |
917 | 9.52M | else if (pcs->cap.x >= (coord)page_size) { |
918 | 353 | pcs->cap.x = (coord)page_size; |
919 | 353 | break; |
920 | 353 | } |
921 | 17.3M | } |
922 | 17.9M | } |
923 | | |
924 | | /* |
925 | | * If the immediately preceding character was a BS, the code will |
926 | | * center the current character on top of the preceding one. After |
927 | | * the character is printed, the current point is returned to the |
928 | | * prior point. |
929 | | */ |
930 | 10.1M | tmp_x = pcs->cap.x; |
931 | 10.1M | if (pcs->last_was_BS) { |
932 | | /* hack alert. It seems if the last width is large, we |
933 | | use the horizontal dimension of the page as a guess, the |
934 | | centering is replaced by returning to the zero |
935 | | coordinate. It would take quite a bit of time to |
936 | | investigate what the hp is doing in this pathological |
937 | | case, so we have not done a detailed analysis. This |
938 | | solution prints the tests we have correctly. */ |
939 | 79.5k | if (pcs->last_width > pcs->xfm_state.pd_size.x) |
940 | 0 | tmp_x = 0; |
941 | 79.5k | else |
942 | 79.5k | tmp_x += (pcs->last_width - width) / 2; |
943 | 79.5k | } |
944 | 10.1M | code = gs_moveto(pgs, tmp_x / pscale->x, pcs->cap.y / pscale->y); |
945 | 10.1M | if (code < 0) |
946 | 0 | return code; |
947 | | |
948 | 10.1M | if (chr != 0xffff || print_undefined) { |
949 | | /* if source is opaque, show and opaque background */ |
950 | | /* retrieve the current cursor position: leftside of character */ |
951 | 9.37M | gs_fixed_point pt; |
952 | 9.37M | code = gx_path_current_point(gx_current_path(pcs->pgs), &pt); |
953 | 9.37M | if (source_opaque) |
954 | 14.9k | code = show_char_background(pcs, buff); |
955 | 9.37M | if (code < 0) |
956 | 0 | break; |
957 | | |
958 | 9.37M | if (invisible_pattern) |
959 | 0 | code = show_char_invisible_foreground(pcs, buff); |
960 | 9.37M | else |
961 | 9.37M | code = show_char_foreground(pcs, buff); |
962 | | |
963 | 9.37M | if (code < 0) |
964 | 0 | break; |
965 | | |
966 | 9.37M | if ((code = pcl_mark_page_for_character(pcs, &pt)) < 0) |
967 | 0 | return code; |
968 | 9.37M | } |
969 | | |
970 | | /* |
971 | | * Check again for the first character following a back-space. if |
972 | | * this is the case, go back to the original position. |
973 | | */ |
974 | 10.1M | if (pcs->last_was_BS) { |
975 | 79.5k | pcs->cap.x += (coord)pcs->last_width; |
976 | 79.5k | pcs->last_was_BS = false; |
977 | 79.5k | } else |
978 | 10.0M | pcs->cap.x += (coord)width; |
979 | | |
980 | | /* check for going beyond the margin if not wrapping */ |
981 | 10.1M | if (!wrap) { |
982 | 9.60M | if (use_rmargin && (pcs->cap.x > rmargin)) { |
983 | 2.07k | pcs->cap.x = (coord)rmargin; |
984 | 2.07k | break; |
985 | 9.60M | } else if (pcs->cap.x >= (coord)page_size) { |
986 | 91.2k | pcs->cap.x = (coord)page_size; |
987 | 91.2k | break; |
988 | 91.2k | } |
989 | 9.60M | } |
990 | 10.0M | if (unstyled_substitution) { |
991 | 0 | pcl_decache_font(pcs, -1, true); |
992 | 0 | code = pcl_recompute_font(pcs, false); |
993 | 0 | if (code < 0) |
994 | 0 | return code; |
995 | 0 | set_gs_font(pcs); |
996 | 0 | } |
997 | 10.0M | } |
998 | | |
999 | | /* record the last width */ |
1000 | 11.6M | pcs->last_width = width; |
1001 | | |
1002 | 11.6M | return code; |
1003 | 11.6M | } |
1004 | | |
1005 | | void |
1006 | | pcl_font_scale(pcl_state_t * pcs, gs_point * pscale) |
1007 | 11.6M | { |
1008 | | /* set up the font transformation */ |
1009 | 11.6M | if (pcs->font->scaling_technology == plfst_bitmap) { |
1010 | 0 | pscale->x = (double) pcl_coord_scale / pcs->font->resolution.x; |
1011 | 0 | pscale->y = (double) pcl_coord_scale / pcs->font->resolution.y; |
1012 | 11.6M | } else { |
1013 | | /* |
1014 | | * Outline fonts are 1-point; the font height is given in |
1015 | | * (quarter-)points. However, if the font is fixed-width, |
1016 | | * it must be scaled by pitch, not by height, relative to |
1017 | | * the nominal pitch of the outline. |
1018 | | */ |
1019 | 11.6M | pcl_font_selection_t *pfp = &pcs->font_selection[pcs->font_selected]; |
1020 | | |
1021 | | /* AGFA madness - 72.307 points per inch for intellifonts */ |
1022 | 11.6M | double ppi = |
1023 | 11.6M | (pfp->font->scaling_technology == |
1024 | 11.6M | plfst_Intellifont) ? 72.307 : 72.0; |
1025 | 11.6M | if (pfp->font->params.proportional_spacing) { |
1026 | 79.9k | pscale->x = pscale->y = pfp->params.height_4ths |
1027 | 79.9k | * 0.25 * 7200.0 / ppi; |
1028 | 11.5M | } else { |
1029 | 11.5M | pscale->x = pscale->y = pl_fp_pitch_cp(&pfp->params) |
1030 | 11.5M | * (1000.0 / pl_fp_pitch_cp(&pfp->font->params)) |
1031 | 11.5M | * (7200.0 / (100.0 * ppi)); |
1032 | | |
1033 | | /* hack for our internal scalable lineprinter font. If a |
1034 | | real lineprinter bitmap font is available it will be |
1035 | | handled by the bitmap scaling case above */ |
1036 | 11.5M | if ((pfp->font->params.typeface_family == 0) && |
1037 | 70.6k | (pfp->font->storage == pcds_internal)) { |
1038 | 70.6k | pscale->x = pscale->y = 850.0; |
1039 | 70.6k | } |
1040 | | |
1041 | 11.5M | } |
1042 | | /* |
1043 | | * Scalable fonts use an upright coordinate system, |
1044 | | * the opposite from the usual PCL system. |
1045 | | */ |
1046 | 11.6M | pscale->y = -pscale->y; |
1047 | 11.6M | } |
1048 | 11.6M | } |
1049 | | |
1050 | | /* |
1051 | | * Set up to handle a string of text. |
1052 | | * |
1053 | | * The final operand is true if the text was provided via the literal |
1054 | | * (transparent) text command: ESC & p <nbytes> X. This distinction is |
1055 | | * important for characters that are not mapped by the current symbol set. |
1056 | | */ |
1057 | | int |
1058 | | pcl_text(const byte * str, uint size, pcl_state_t * pcs, bool literal) |
1059 | 11.6M | { |
1060 | 11.6M | gs_point scale; |
1061 | 11.6M | int code; |
1062 | | |
1063 | | /* rtl files can have text in them - we don't print any characters |
1064 | | in rtl */ |
1065 | 11.6M | if (pcs->personality == rtl) |
1066 | 0 | return 0; |
1067 | | /* set up the current font and HMI */ |
1068 | 11.6M | if ((pcs->font == 0) || pcs->font_selection[pcs->font_selected].font == 0) { |
1069 | 312k | code = pcl_recompute_font(pcs, false); |
1070 | 312k | if (code < 0) |
1071 | 0 | return gs_rethrow_code(code); |
1072 | 312k | } |
1073 | | |
1074 | 11.6M | pcl_font_scale(pcs, &scale); |
1075 | 11.6M | code = pcl_set_gstate_for_font(pcs, &scale); |
1076 | 11.6M | if (code < 0) |
1077 | 0 | return code; |
1078 | | |
1079 | | /* |
1080 | | * If floating underline is on, since we're about to print a real |
1081 | | * character, track the best-underline position. |
1082 | | * XXX Until we have the font's design value for underline position, |
1083 | | * use 0.2 em. This is enough to almost clear descenders in typical |
1084 | | * fonts; it's also large enough for us to check that the mechanism |
1085 | | * works. |
1086 | | */ |
1087 | 11.6M | if (pcs->underline_enabled && pcs->underline_floating) { |
1088 | 0 | float yu = fabs(scale.y) / 5.0; |
1089 | |
|
1090 | 0 | if (yu > pcs->underline_position) |
1091 | 0 | pcs->underline_position = yu; |
1092 | 0 | } |
1093 | | |
1094 | | /* it is not clear if vertical substitutes are allowed in mode -1 */ |
1095 | 11.6M | if (pcs->text_path != 0) |
1096 | 0 | pcs->font->allow_vertical_substitutes = true; |
1097 | 11.6M | else |
1098 | 11.6M | pcs->font->allow_vertical_substitutes = false; |
1099 | | |
1100 | | /* Print the characters. */ |
1101 | 11.6M | code = pcl_show_chars_slow(pcs, &scale, str, size, literal); |
1102 | 11.6M | if (code > 0) /* shouldn't happen */ |
1103 | 0 | code = gs_note_error(gs_error_invalidfont); |
1104 | 11.6M | return code; |
1105 | 11.6M | } |
1106 | | |
1107 | | /* |
1108 | | * Individual non-command/control characters |
1109 | | */ |
1110 | | int |
1111 | | pcl_plain_char(pcl_args_t * pargs, pcl_state_t * pcs) |
1112 | 1.28M | { |
1113 | 1.28M | return pcl_text((const byte *)&(pargs->command), 1, pcs, |
1114 | 1.28M | pcs->display_functions); |
1115 | 1.28M | } |
1116 | | |
1117 | | /* |
1118 | | * Do any underlining just before a break in motion (vertical motion or |
1119 | | * negative horizontal motion)... |
1120 | | */ |
1121 | | int pcl_break_underline(pcl_state_t * pcs) |
1122 | 1.15M | { |
1123 | 1.15M | int code = 0; |
1124 | | |
1125 | 1.15M | if (pcs->underline_enabled) |
1126 | 0 | code = pcl_do_underline(pcs); |
1127 | | |
1128 | 1.15M | return code; |
1129 | 1.15M | } |
1130 | | |
1131 | | /* |
1132 | | * draw underline up to current point, adjust status |
1133 | | */ |
1134 | | int |
1135 | | pcl_do_underline(pcl_state_t * pcs) |
1136 | 0 | { |
1137 | 0 | int code = 0; |
1138 | |
|
1139 | 0 | if (pcs->underline_start.x != pcs->cap.x) { |
1140 | 0 | gs_gstate *pgs = pcs->pgs; |
1141 | 0 | float y = pcs->underline_start.y + pcs->underline_position; |
1142 | | |
1143 | | /* save the graphics state */ |
1144 | 0 | code = pcl_gsave(pcs); |
1145 | 0 | if (code < 0) |
1146 | 0 | return code; |
1147 | | |
1148 | 0 | code = pcl_set_drawing_color(pcs, |
1149 | 0 | pcs->pattern_type, |
1150 | 0 | pcs->current_pattern_id, false); |
1151 | 0 | if (code >= 0) |
1152 | 0 | code = pcl_set_graphics_state(pcs); |
1153 | 0 | if (code < 0) { |
1154 | 0 | (void)pcl_grestore(pcs); |
1155 | 0 | return code; |
1156 | 0 | } |
1157 | | |
1158 | | /* |
1159 | | * TRM says (8-34) that underline is 3 dots. In a victory for |
1160 | | * common sense, it's not. Rather, it's 0.01" (which *is* 3 dots |
1161 | | * at 300 dpi only) |
1162 | | */ |
1163 | 0 | gs_setlinewidth(pgs, dots(3)); |
1164 | 0 | if ((gs_moveto(pgs, pcs->underline_start.x, y) < 0) || |
1165 | 0 | (gs_lineto(pgs, pcs->cap.x, y) < 0) || |
1166 | 0 | (gs_stroke(pgs) < 0)) { |
1167 | 0 | (void)pcl_grestore(pcs); |
1168 | 0 | return code; |
1169 | 0 | } |
1170 | | |
1171 | 0 | code = pcl_grestore(pcs); |
1172 | 0 | if (code < 0) |
1173 | 0 | return code; |
1174 | 0 | } |
1175 | | |
1176 | | /* |
1177 | | * Fixed underline is 5 "dots" (actually 5/300") down. Floating |
1178 | | * will be determined on the fly. |
1179 | | */ |
1180 | 0 | pcs->underline_start = pcs->cap; |
1181 | 0 | pcs->underline_position = pcs->underline_floating ? 0.0 : dots(5); |
1182 | 0 | return code; |
1183 | 0 | } |
1184 | | |
1185 | | /* ------ Commands ------ */ |
1186 | | |
1187 | | /* |
1188 | | * ESC & p <count> X |
1189 | | * |
1190 | | * Unparsed text command |
1191 | | */ |
1192 | | static int |
1193 | | pcl_transparent_mode(pcl_args_t * pargs, pcl_state_t * pcs) |
1194 | 1.74k | { |
1195 | | |
1196 | | #ifdef DEBUG |
1197 | | if (gs_debug_c('i')) { |
1198 | | pcl_debug_dump_data(pcs->memory, arg_data(pargs), uint_arg(pargs)); |
1199 | | } |
1200 | | #endif |
1201 | | |
1202 | 1.74k | return pcl_text(arg_data(pargs), uint_arg(pargs), pcs, true); |
1203 | 1.74k | } |
1204 | | |
1205 | | /* |
1206 | | * ESC & d <0|3> D |
1207 | | * |
1208 | | * Enable floating or fixed-depth underlining. |
1209 | | * |
1210 | | * NB: If underlining is already enabled, this command is ignored. Underlining |
1211 | | * must be specifically disabled to switch from fixed to floating. |
1212 | | */ |
1213 | | static int |
1214 | | pcl_enable_underline(pcl_args_t * pargs, pcl_state_t * pcs) |
1215 | 0 | { |
1216 | 0 | int type = int_arg(pargs); |
1217 | | |
1218 | | /* ignore command if underlining is already enabled */ |
1219 | 0 | if (pcs->underline_enabled) |
1220 | 0 | return 0; |
1221 | | |
1222 | 0 | if ((type == 0) || (type == 1)) { |
1223 | 0 | pcs->underline_floating = false; |
1224 | 0 | pcs->underline_position = dots(5); |
1225 | 0 | } else if (type == 3) { |
1226 | 0 | pcs->underline_floating = true; |
1227 | 0 | pcs->underline_position = 0.0; |
1228 | 0 | } else |
1229 | 0 | return 0; |
1230 | | |
1231 | 0 | pcs->underline_enabled = true; |
1232 | 0 | pcs->underline_start = pcs->cap; |
1233 | 0 | return 0; |
1234 | 0 | } |
1235 | | |
1236 | | /* |
1237 | | * ESC & d @ |
1238 | | * |
1239 | | * Disable underlining |
1240 | | */ |
1241 | | static int |
1242 | | pcl_disable_underline(pcl_args_t * pargs, pcl_state_t * pcs) |
1243 | 240 | { |
1244 | 240 | int code = 0; |
1245 | | |
1246 | | /* apparently disabling underlining has the side effect of |
1247 | | flushing any pending underlines. This side effect is not |
1248 | | documented */ |
1249 | 240 | if (pcs->underline_enabled == true) { |
1250 | 0 | code = pcl_do_underline(pcs); |
1251 | 0 | pcs->underline_enabled = false; |
1252 | 0 | } |
1253 | 240 | return code; |
1254 | 240 | } |
1255 | | |
1256 | | /* (From PCL5 Comparison Guide, p. 1-56) */ |
1257 | | |
1258 | | /* |
1259 | | * ESC & t <method> P |
1260 | | * |
1261 | | * Select the text parsing method. |
1262 | | */ |
1263 | | static int |
1264 | | pcl_text_parsing_method(pcl_args_t * pargs, pcl_state_t * pcs) |
1265 | 519 | { |
1266 | 519 | switch (int_arg(pargs)) { |
1267 | | |
1268 | 14 | case 0: |
1269 | 14 | case 1: |
1270 | 14 | pcs->text_parsing_method = tpm_0_SBCS; |
1271 | 14 | break; |
1272 | | |
1273 | 0 | case 21: |
1274 | 0 | pcs->text_parsing_method = tpm_21_DBCS7; |
1275 | 0 | break; |
1276 | | |
1277 | 0 | case 31: |
1278 | 0 | pcs->text_parsing_method = tpm_31_sjis; |
1279 | 0 | break; |
1280 | | |
1281 | 0 | case 38: |
1282 | 0 | pcs->text_parsing_method = tpm_38_DBCS8; |
1283 | 0 | break; |
1284 | | |
1285 | 504 | case 83: |
1286 | 504 | pcs->text_parsing_method = tpm_83_utf8; |
1287 | 504 | break; |
1288 | | |
1289 | 0 | case 1008: |
1290 | 0 | pcs->text_parsing_method = tpm_1008_utf8; |
1291 | 0 | break; |
1292 | | |
1293 | 1 | default: |
1294 | 1 | return e_Range; |
1295 | 519 | } |
1296 | | |
1297 | 518 | return 0; |
1298 | 519 | } |
1299 | | |
1300 | | /* (From PCL5 Comparison Guide, p. 1-57) */ |
1301 | | |
1302 | | /* |
1303 | | * ESC & c <direction> T |
1304 | | * |
1305 | | * Set the text path direction - not yet implemented. |
1306 | | */ |
1307 | | static int |
1308 | | pcl_text_path_direction(pcl_args_t * pargs, pcl_state_t * pcs) |
1309 | 0 | { |
1310 | 0 | int direction = int_arg(pargs); |
1311 | |
|
1312 | 0 | switch (direction) { |
1313 | | |
1314 | 0 | case 0: |
1315 | 0 | case 1: |
1316 | 0 | case -1: |
1317 | 0 | break; |
1318 | | |
1319 | 0 | default: |
1320 | 0 | return e_Range; |
1321 | 0 | } |
1322 | | |
1323 | 0 | pcs->text_path = direction; |
1324 | 0 | return 0; |
1325 | 0 | } |
1326 | | |
1327 | | /* ------ Initialization ------ */ |
1328 | | static int |
1329 | | pctext_do_registration(pcl_parser_state_t * pcl_parser_state, |
1330 | | gs_memory_t * mem) |
1331 | 8.97k | { |
1332 | | /* Register commands */ |
1333 | 8.97k | DEFINE_CONTROL(0, "(plain char)", pcl_plain_char); |
1334 | | |
1335 | 8.97k | DEFINE_CLASS('&') { |
1336 | 8.97k | 'p', 'X', |
1337 | 8.97k | PCL_COMMAND("Transparent Mode", pcl_transparent_mode, pca_bytes) |
1338 | 8.97k | }, { |
1339 | 8.97k | 'd', 'D', |
1340 | 8.97k | PCL_COMMAND("Enable Underline", |
1341 | 8.97k | pcl_enable_underline, pca_neg_ignore | pca_big_ignore) |
1342 | 8.97k | }, { |
1343 | 8.97k | 'd', '@', |
1344 | 8.97k | PCL_COMMAND("Disable Underline", |
1345 | 8.97k | pcl_disable_underline, |
1346 | 8.97k | pca_neg_ignore | pca_big_ignore) |
1347 | 8.97k | }, END_CLASS DEFINE_CLASS('&') { |
1348 | 8.97k | 't', 'P', |
1349 | 8.97k | PCL_COMMAND("Text Parsing Method", |
1350 | 8.97k | pcl_text_parsing_method, |
1351 | 8.97k | pca_neg_error | pca_big_error) |
1352 | 8.97k | }, { |
1353 | 8.97k | 'c', 'T', |
1354 | 8.97k | PCL_COMMAND("Text Path Direction", |
1355 | 8.97k | pcl_text_path_direction, pca_neg_ok | pca_big_error) |
1356 | 8.97k | }, END_CLASS DEFINE_CONTROL(1, "(plain char)", pcl_plain_char); /* default "command" */ |
1357 | | |
1358 | 8.97k | return 0; |
1359 | 8.97k | } |
1360 | | |
1361 | | static int |
1362 | | pctext_do_reset(pcl_state_t * pcs, pcl_reset_type_t type) |
1363 | 35.3k | { |
1364 | 35.3k | static const uint mask = (pcl_reset_initial |
1365 | 35.3k | | pcl_reset_printer | pcl_reset_overlay); |
1366 | | |
1367 | 35.3k | if ((type & mask) != 0) { |
1368 | 35.3k | pcs->underline_enabled = false; |
1369 | 35.3k | pcs->last_was_BS = false; |
1370 | 35.3k | pcs->last_width = inch2coord(1.0 / 10.0); |
1371 | 35.3k | pcs->text_parsing_method = tpm_0_SBCS; |
1372 | 35.3k | pcs->text_path = 0; |
1373 | 35.3k | } |
1374 | 35.3k | return 0; |
1375 | 35.3k | } |
1376 | | |
1377 | | const pcl_init_t pctext_init = { pctext_do_registration, pctext_do_reset, 0 }; |