/src/ghostpdl/pcl/pcl/pcsymbol.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2023 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 | | /* pcsymbol.c */ |
18 | | /* PCL5 user-defined symbol set commands */ |
19 | | #include "stdio_.h" /* std.h + NULL */ |
20 | | #include "plvalue.h" |
21 | | #include "pcommand.h" |
22 | | #include "pcstate.h" |
23 | | #include "pcfont.h" |
24 | | #include "pcsymbol.h" |
25 | | |
26 | | /* HP printers will not convert MSL coded symbol table to unicode. An |
27 | | MSL based font must be used with an MSL symbol set. We think this |
28 | | is a bug. The following definition can be uncommented to support |
29 | | the feature anyway */ |
30 | | |
31 | | /* #define SUPPORT_MSL_TO_UNICODE */ |
32 | | |
33 | | static int /* ESC * c <id> R */ |
34 | | pcl_symbol_set_id_code(pcl_args_t * pargs, pcl_state_t * pcs) |
35 | 129 | { |
36 | 129 | uint id = uint_arg(pargs); |
37 | | |
38 | 129 | id_set_value(pcs->symbol_set_id, id); |
39 | 129 | return 0; |
40 | 129 | } |
41 | | |
42 | | #ifdef DEBUG |
43 | | static void |
44 | | dump_dl_symbol_set(const gs_memory_t * mem, const pl_symbol_map_t * psm) |
45 | | { |
46 | | dmprintf6(mem, |
47 | | "header size:%d id:%d format:%s type:%d first code:%d last code:%d\n", |
48 | | pl_get_uint16(psm->header_size), pl_get_uint16(psm->id), |
49 | | (psm->format == |
50 | | 1 ? "MSL" : (psm->format == 3 ? "Unicode" : "Unknown")), |
51 | | psm->type, pl_get_uint16(psm->first_code), |
52 | | pl_get_uint16(psm->last_code)); |
53 | | { |
54 | | int i; |
55 | | int num_codes = |
56 | | pl_get_uint16(psm->last_code) - pl_get_uint16(psm->first_code) + |
57 | | 1; |
58 | | for (i = 0; i < num_codes; i++) { |
59 | | dmprintf2(mem, "index=%d, code:%d\n", i, psm->codes[i]); |
60 | | } |
61 | | } |
62 | | } |
63 | | #endif |
64 | | |
65 | | static int /* ESC ( f <count> W */ |
66 | | pcl_define_symbol_set(pcl_args_t * pargs, pcl_state_t * pcs) |
67 | 0 | { |
68 | 0 | uint count = uint_arg(pargs); |
69 | 0 | const pl_symbol_map_t *psm = (pl_symbol_map_t *) arg_data(pargs); |
70 | 0 | uint header_size; |
71 | 0 | uint first_code, last_code; |
72 | 0 | gs_memory_t *mem = pcs->memory; |
73 | 0 | pl_symbol_map_t *header; |
74 | 0 | pcl_symbol_set_t *symsetp; |
75 | 0 | pl_glyph_vocabulary_t gv; |
76 | |
|
77 | | #ifdef DEBUG |
78 | | if (gs_debug_c('i')) { |
79 | | pcl_debug_dump_data(pcs->memory, arg_data(pargs), uint_arg(pargs)); |
80 | | } |
81 | | #endif |
82 | |
|
83 | 0 | #define psm_header_size 18 |
84 | 0 | if (count < psm_header_size) |
85 | 0 | return e_Range; |
86 | 0 | header_size = pl_get_uint16(psm->header_size); |
87 | 0 | if (header_size < psm_header_size || |
88 | 0 | psm->id[0] != id_key(pcs->symbol_set_id)[0] || |
89 | 0 | psm->id[1] != id_key(pcs->symbol_set_id)[1] || psm->type > 2) |
90 | 0 | return e_Range; |
91 | 0 | switch (psm->format) { |
92 | 0 | case 1: |
93 | 0 | gv = plgv_MSL; |
94 | 0 | break; |
95 | 0 | case 3: |
96 | 0 | gv = plgv_Unicode; |
97 | 0 | break; |
98 | 0 | default: |
99 | 0 | return e_Range; |
100 | 0 | } |
101 | 0 | first_code = pl_get_uint16(psm->first_code); |
102 | 0 | last_code = pl_get_uint16(psm->last_code); |
103 | |
|
104 | 0 | if (last_code > 255 || first_code > last_code) |
105 | 0 | return e_Range; |
106 | | |
107 | 0 | { |
108 | 0 | int num_codes = last_code - first_code + 1; /* must be in [0,256] now. */ |
109 | 0 | int i; |
110 | |
|
111 | 0 | if (num_codes == 0 || (count != psm_header_size + num_codes * 2)) |
112 | 0 | return e_Range; |
113 | | |
114 | 0 | header = |
115 | 0 | (pl_symbol_map_t *) gs_alloc_bytes(mem, |
116 | 0 | sizeof(pl_symbol_map_t), |
117 | 0 | "pcl_font_header(header)"); |
118 | 0 | if (header == 0) |
119 | 0 | return_error(e_Memory); |
120 | 0 | memcpy((void *)header, (void *)psm, psm_header_size); |
121 | | /* allow mapping to and from msl and unicode */ |
122 | 0 | if (psm->format == 1) |
123 | | #ifdef SUPPORT_MSL_TO_UNICODE |
124 | | header->mapping_type = PLGV_M2U_MAPPING; |
125 | | #else |
126 | 0 | header->mapping_type = PLGV_NO_MAPPING; |
127 | 0 | #endif |
128 | 0 | else |
129 | 0 | header->mapping_type = PLGV_U2M_MAPPING; |
130 | | |
131 | | /* |
132 | | * Byte swap the codes now, so that we don't have to byte swap |
133 | | * them every time we access them. |
134 | | */ |
135 | 0 | for (i = 0; i < num_codes; i++) |
136 | 0 | header->codes[i] = |
137 | 0 | pl_get_uint16((byte *) psm + psm_header_size + i * 2); |
138 | 0 | } |
139 | 0 | #undef psm_header_size |
140 | | |
141 | | #ifdef DEBUG |
142 | | if (gs_debug_c('=')) |
143 | | dump_dl_symbol_set(mem, psm); |
144 | | #endif |
145 | | |
146 | | /* Symbol set may already exist; if so, we may be replacing one of |
147 | | * its existing maps or adding one for a new glyph vocabulary. */ |
148 | 0 | if (pl_dict_find(&pcs->soft_symbol_sets, id_key(pcs->symbol_set_id), |
149 | 0 | 2, (void **)&symsetp)) { |
150 | 0 | if (symsetp->maps[gv] != NULL) |
151 | 0 | gs_free_object(mem, symsetp->maps[gv], "symset map"); |
152 | 0 | } else { |
153 | 0 | int code = 0; |
154 | 0 | pl_glyph_vocabulary_t gx; |
155 | |
|
156 | 0 | symsetp = (pcl_symbol_set_t *) gs_alloc_bytes(mem, |
157 | 0 | sizeof |
158 | 0 | (pcl_symbol_set_t), |
159 | 0 | "symset dict value"); |
160 | 0 | if (!symsetp) { |
161 | 0 | gs_free_object(mem, header, "pcl_font_header(header)"); |
162 | 0 | return_error(e_Memory); |
163 | 0 | } |
164 | 0 | for (gx = plgv_MSL; gx < plgv_next; gx++) |
165 | 0 | symsetp->maps[gx] = NULL; |
166 | 0 | symsetp->storage = pcds_temporary; |
167 | 0 | code = pl_dict_put(&pcs->soft_symbol_sets, id_key(pcs->symbol_set_id), |
168 | 0 | 2, symsetp); |
169 | 0 | if (code < 0) { |
170 | 0 | gs_free_object(mem, header, "pcl_font_header(header)"); |
171 | 0 | return code; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | symsetp->maps[gv] = header; |
175 | |
|
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | | static int /* ESC * c <ssc_enum> S */ |
180 | | pcl_symbol_set_control(pcl_args_t * pargs, pcl_state_t * pcs) |
181 | 26.4k | { |
182 | 26.4k | gs_const_string key; |
183 | 26.4k | void *value; |
184 | 26.4k | pl_dict_enum_t denum; |
185 | | |
186 | 26.4k | switch (int_arg(pargs)) { |
187 | 51 | case 0: |
188 | 51 | { |
189 | | /* Delete all user-defined symbol sets. */ |
190 | | /* Note: When deleting symbol set(s), it is easier |
191 | | * (safer?) to decache and reselect fonts |
192 | | * unconditionally. (Consider, for example, deleting |
193 | | * a downloaded overload of a built-in which might be |
194 | | * the default ID.) |
195 | | */ |
196 | 51 | pl_dict_release(&pcs->soft_symbol_sets); |
197 | 51 | pcl_decache_font(pcs, -1, true); |
198 | 51 | } |
199 | 51 | return 0; |
200 | 26.3k | case 1: |
201 | 26.3k | { |
202 | | /* Delete all temporary symbol sets. */ |
203 | 26.3k | pl_dict_enum_stack_begin(&pcs->soft_symbol_sets, &denum, |
204 | 26.3k | false); |
205 | 26.3k | while (pl_dict_enum_next(&denum, &key, &value)) |
206 | 0 | if (((pcl_symbol_set_t *) value)->storage == |
207 | 0 | pcds_temporary) |
208 | 0 | pl_dict_undef(&pcs->soft_symbol_sets, key.data, |
209 | 0 | key.size); |
210 | 26.3k | pcl_decache_font(pcs, -1, true); |
211 | 26.3k | } |
212 | 26.3k | return 0; |
213 | 0 | case 2: |
214 | 0 | { |
215 | | /* Delete symbol set <symbol_set_id>. */ |
216 | 0 | pl_dict_undef(&pcs->soft_symbol_sets, |
217 | 0 | id_key(pcs->symbol_set_id), 2); |
218 | 0 | pcl_decache_font(pcs, -1, true); |
219 | 0 | } |
220 | 0 | return 0; |
221 | 0 | case 4: |
222 | 0 | { |
223 | | /* Make <symbol_set_id> temporary. */ |
224 | 0 | if (pl_dict_find(&pcs->soft_symbol_sets, |
225 | 0 | id_key(pcs->symbol_set_id), 2, &value)) |
226 | 0 | ((pcl_symbol_set_t *) value)->storage = pcds_temporary; |
227 | 0 | } |
228 | 0 | return 0; |
229 | 0 | case 5: |
230 | 0 | { |
231 | | /* Make <symbol_set_id> permanent. */ |
232 | 0 | if (pl_dict_find(&pcs->soft_symbol_sets, |
233 | 0 | id_key(pcs->symbol_set_id), 2, &value)) |
234 | 0 | ((pcl_symbol_set_t *) value)->storage = pcds_permanent; |
235 | 0 | } |
236 | 0 | return 0; |
237 | 23 | default: |
238 | 23 | return 0; |
239 | 26.4k | } |
240 | 26.4k | } |
241 | | |
242 | | static void /* free any symbol maps as well as dict value entry */ |
243 | | pcsymbol_dict_value_free(gs_memory_t * mem, void *value, client_name_t cname) |
244 | 484k | { |
245 | 484k | pcl_symbol_set_t *ssp = (pcl_symbol_set_t *) value; |
246 | 484k | pl_glyph_vocabulary_t gx; |
247 | | |
248 | 484k | if (ssp->storage != pcds_internal) { |
249 | 0 | for (gx = plgv_MSL; gx < plgv_next; gx++) { |
250 | 0 | if (ssp->maps[gx] != NULL) |
251 | 0 | gs_free_object(mem, (void *)ssp->maps[gx], cname); |
252 | 0 | } |
253 | 0 | } |
254 | 484k | gs_free_object(mem, value, cname); |
255 | 484k | } |
256 | | |
257 | | static int |
258 | | pcl_load_built_in_symbol_sets(pcl_state_t * pcs) |
259 | 8.97k | { |
260 | 8.97k | const pl_symbol_map_t **maplp; |
261 | 8.97k | pcl_symbol_set_t *symsetp; |
262 | 8.97k | pl_glyph_vocabulary_t gv; |
263 | | |
264 | 502k | for (maplp = &pl_built_in_symbol_maps[0]; *maplp; maplp++) { |
265 | 493k | const pl_symbol_map_t *mapp = *maplp; |
266 | | |
267 | | /* Create entry for symbol set if this is the first map for |
268 | | * that set. */ |
269 | 493k | if (!pl_dict_find(&pcs->built_in_symbol_sets, mapp->id, 2, |
270 | 493k | (void **)&symsetp)) { |
271 | 484k | pl_glyph_vocabulary_t gx; |
272 | | |
273 | 484k | symsetp = (pcl_symbol_set_t *) gs_alloc_bytes(pcs->memory, |
274 | 484k | sizeof |
275 | 484k | (pcl_symbol_set_t), |
276 | 484k | "symset init dict value"); |
277 | 484k | if (!symsetp) |
278 | 0 | return_error(e_Memory); |
279 | 1.45M | for (gx = plgv_MSL; gx < plgv_next; gx++) |
280 | 969k | symsetp->maps[gx] = NULL; |
281 | 484k | symsetp->storage = pcds_internal; |
282 | | |
283 | 484k | if (pl_dict_put(&pcs->built_in_symbol_sets, mapp->id, 2, symsetp) < 0) { |
284 | | /* on error, pl_dict_put consumes symsetp */ |
285 | 0 | return_error(gs_error_VMerror); |
286 | 0 | } |
287 | 484k | } |
288 | 493k | gv = (mapp->character_requirements[7] & 07) == 1 ? |
289 | 457k | plgv_Unicode : plgv_MSL; |
290 | 493k | symsetp->maps[gv] = (pl_symbol_map_t *) mapp; |
291 | 493k | } |
292 | 8.97k | return 0; |
293 | 8.97k | } |
294 | | |
295 | | bool |
296 | | pcl_check_symbol_support(const byte * symset_req, const byte * font_sup) |
297 | 18.8M | { |
298 | 18.8M | int i; |
299 | | |
300 | | /* if glyph vocabularies match, following will work on the |
301 | | * last 3 bits of last byte. Note that the font-support bits |
302 | | * are inverted (0 means available). |
303 | | */ |
304 | 149M | for (i = 0; i < 7; i++) |
305 | 131M | if (symset_req[i] & font_sup[i]) |
306 | 268k | return false; /* something needed, not present */ |
307 | | /* check the last byte but not the glyph vocabularies. */ |
308 | 18.5M | if ((symset_req[7] >> 3) & (font_sup[7] >> 3)) |
309 | 0 | return false; |
310 | | |
311 | 18.5M | return true; |
312 | 18.5M | } |
313 | | |
314 | | /* Find the symbol map for a particular symbol set and glyph vocabulary, |
315 | | * if it exists. |
316 | | * There are two dictionaries--one for soft (downloaded) symbol sets and |
317 | | * one for built-ins. These are searched separately. The actual maps |
318 | | * present for a symbol set may overlap between soft and built-in. */ |
319 | | pl_symbol_map_t * |
320 | | pcl_find_symbol_map(const pcl_state_t * pcs, const byte * id, |
321 | | pl_glyph_vocabulary_t gv, bool wide16) |
322 | 37.3M | { |
323 | 37.3M | pcl_symbol_set_t *setp; |
324 | | |
325 | 37.3M | if (pl_dict_find((pl_dict_t *) & pcs->soft_symbol_sets, |
326 | 37.3M | id, 2, (void **)&setp) || |
327 | 37.3M | pl_dict_find((pl_dict_t *) & pcs->built_in_symbol_sets, |
328 | 37.3M | id, 2, (void **)&setp)) { |
329 | | |
330 | | /* 16 bit sets are not supported, they aren't strictly |
331 | | necessary, yet. */ |
332 | 30.4M | if (wide16) |
333 | 0 | return NULL; |
334 | | /* simple case we found a matching symbol set */ |
335 | 30.4M | if (setp->maps[gv] != NULL) |
336 | 30.4M | return setp->maps[gv]; |
337 | | /* we requested a unicode symbol set and found an msl |
338 | | symbol set that can be mapped to unicode */ |
339 | 204 | if ((gv == plgv_Unicode) && |
340 | 204 | (setp->maps[plgv_MSL]) && |
341 | 204 | ((setp->maps[plgv_MSL])->mapping_type == PLGV_M2U_MAPPING)) |
342 | 0 | return setp->maps[plgv_MSL]; |
343 | | /* we requested an msl symbol set and found a unicode |
344 | | symbol set that can be mapped to msl */ |
345 | 204 | if ((gv == plgv_MSL) && |
346 | 0 | (setp->maps[plgv_Unicode]) && |
347 | 0 | ((setp->maps[plgv_Unicode])->mapping_type == PLGV_U2M_MAPPING)) |
348 | 0 | return setp->maps[plgv_Unicode]; |
349 | 204 | } |
350 | 6.89M | return NULL; |
351 | 37.3M | } |
352 | | |
353 | | /* Initialization */ |
354 | | static int |
355 | | pcsymbol_do_registration(pcl_parser_state_t * pcl_parser_state, |
356 | | gs_memory_t * mem) |
357 | 8.97k | { /* Register commands */ |
358 | 8.97k | DEFINE_CLASS_COMMAND_ARGS('*', 'c', 'R', "Symbol Set ID Code", |
359 | 8.97k | pcl_symbol_set_id_code, |
360 | 8.97k | pca_neg_error | pca_big_error) |
361 | 8.97k | DEFINE_CLASS_COMMAND_ARGS('(', 'f', 'W', "Define Symbol Set", |
362 | 8.97k | pcl_define_symbol_set, |
363 | 8.97k | pca_byte_data | pca_neg_error | |
364 | 8.97k | pca_big_clamp) |
365 | 8.97k | DEFINE_CLASS_COMMAND_ARGS('*', 'c', 'S', "Symbol Set Control", |
366 | 8.97k | pcl_symbol_set_control, |
367 | 8.97k | pca_neg_ignore | pca_big_ignore) |
368 | 8.97k | return 0; |
369 | 8.97k | } |
370 | | |
371 | | static int |
372 | | pcsymbol_do_reset(pcl_state_t * pcs, pcl_reset_type_t type) |
373 | 35.3k | { |
374 | 35.3k | int code; |
375 | | |
376 | 35.3k | if (type & (pcl_reset_initial | pcl_reset_printer | pcl_reset_overlay)) { |
377 | 35.3k | id_set_value(pcs->symbol_set_id, 0); |
378 | 35.3k | if (type & pcl_reset_initial) { |
379 | | /* Don't set a parent relationship from soft to built-in |
380 | | * symbol sets. Although it is arguably useful, it's |
381 | | * better to avoid it and keep anyone who's looking at the |
382 | | * soft symbol sets from mucking up the permanent ones. */ |
383 | 8.97k | pl_dict_init(&pcs->soft_symbol_sets, pcs->memory, |
384 | 8.97k | pcsymbol_dict_value_free); |
385 | 8.97k | pl_dict_init(&pcs->built_in_symbol_sets, pcs->memory, |
386 | 8.97k | pcsymbol_dict_value_free); |
387 | | /* NB. Symbol sets are require for RTL/HPGL/2 mode for |
388 | | * stickfonts but we shouldn't load all of them. */ |
389 | 8.97k | if (pcl_load_built_in_symbol_sets(pcs) < 0) |
390 | 8.97k | dmprintf(pcs->memory, "Internal error, no symbol sets found"); |
391 | 26.3k | } else if (type & pcl_reset_printer) { |
392 | 26.3k | pcl_args_t args; |
393 | | |
394 | 26.3k | arg_set_uint(&args, 1); /* delete temporary symbol sets */ |
395 | 26.3k | code = pcl_symbol_set_control(&args, pcs); |
396 | 26.3k | if (code < 0) |
397 | 0 | return code; |
398 | 26.3k | } |
399 | 35.3k | } |
400 | 35.3k | if (type & pcl_reset_permanent) { |
401 | 0 | pl_dict_release(&pcs->soft_symbol_sets); |
402 | 0 | pl_dict_release(&pcs->built_in_symbol_sets); |
403 | 0 | } |
404 | 35.3k | return 0; |
405 | 35.3k | } |
406 | | |
407 | | static int |
408 | | pcsymbol_do_copy(pcl_state_t * psaved, const pcl_state_t * pcs, |
409 | | pcl_copy_operation_t operation) |
410 | 0 | { |
411 | 0 | if (operation & pcl_copy_after) { |
412 | | /* Don't restore the downloaded symbol set dictionary. */ |
413 | 0 | psaved->built_in_symbol_sets = pcs->built_in_symbol_sets; |
414 | 0 | } |
415 | 0 | return 0; |
416 | 0 | } |
417 | | |
418 | | const pcl_init_t pcsymbol_init = { |
419 | | pcsymbol_do_registration, pcsymbol_do_reset, pcsymbol_do_copy |
420 | | }; |