Commit | Line | Data |
---|---|---|
3134ed76 WJ |
1 | /* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
19 | ||
20 | /* zchar.c */ | |
21 | /* Character operators for Ghostscript */ | |
22 | #include "ghost.h" | |
23 | #include "errors.h" | |
24 | #include "oper.h" | |
25 | #include "gxfixed.h" /* for gstype1.h */ | |
26 | #include "gxmatrix.h" /* for font.h */ | |
27 | #include "gschar.h" | |
28 | #include "gstype1.h" | |
29 | #include "gxdevice.h" /* for gxfont.h */ | |
30 | #include "gxfont.h" | |
31 | #include "gzpath.h" /* for type1addpath: see below */ | |
32 | #include "gzstate.h" | |
33 | #include "alloc.h" | |
34 | #include "dict.h" | |
35 | #include "dstack.h" /* for systemdict */ | |
36 | #include "font.h" | |
37 | #include "estack.h" | |
38 | #include "state.h" | |
39 | #include "store.h" | |
40 | ||
41 | /* All the character rendering operators use the execution stack */ | |
42 | /* for loop control -- see estack.h for details. */ | |
43 | /* The information pushed by these operators is as follows: */ | |
44 | /* the enumerator (t_string, but points to a gs_show_enum); */ | |
45 | /* a slot for the procedure for kshow, unused otherwise; */ | |
46 | /* the procedure to be called at the end of the enumeration */ | |
47 | /* (t_operator, but called directly, not by the interpreter); */ | |
48 | /* the usual e-stack mark (t_null). */ | |
49 | #define snumpush 4 | |
50 | #define senum (gs_show_enum *)(esp->value.bytes) | |
51 | #define sslot esp[-1] | |
52 | #define seproc esp[-2] | |
53 | ||
54 | /* Imports */ | |
55 | extern int array_get(P3(ref *, long, ref *)); | |
56 | extern ref name_StandardEncoding; | |
57 | ||
58 | /* Forward references */ | |
59 | private int setup_show(P3(ref *, op_proc_p, int)); | |
60 | private int show_continue(P1(os_ptr)); | |
61 | private int i_show_continue; | |
62 | private int finish_show(P1(os_ptr)); | |
63 | private int i_finish_show; | |
64 | private int finish_stringwidth(P1(os_ptr)); | |
65 | private int i_finish_stringwidth; | |
66 | private gs_show_enum *find_show(P0()); | |
67 | private void free_show(P0()); | |
68 | ||
69 | /* show */ | |
70 | int | |
71 | zshow(register os_ptr op) | |
72 | { int code = setup_show(op, finish_show, i_finish_show); | |
73 | if ( code < 0 ) return code; | |
74 | if ( (code = gs_show_n_init(senum, igs, (char *)op->value.bytes, r_size(op))) < 0 ) | |
75 | { free_show(); | |
76 | return code; | |
77 | } | |
78 | pop(1); op--; | |
79 | return show_continue(op); | |
80 | } | |
81 | ||
82 | /* ashow */ | |
83 | int | |
84 | zashow(register os_ptr op) | |
85 | { int code; | |
86 | float axy[2]; | |
87 | if ( (code = num_params(op - 1, 2, axy)) < 0 || | |
88 | (code = setup_show(op, finish_show, i_finish_show)) < 0 | |
89 | ) | |
90 | return code; | |
91 | if ( (code = gs_ashow_n_init(senum, igs, axy[0], axy[1], (char *)op->value.bytes, r_size(op))) < 0 ) | |
92 | { free_show(); | |
93 | return code; | |
94 | } | |
95 | pop(3); op -= 3; | |
96 | return show_continue(op); | |
97 | } | |
98 | ||
99 | /* widthshow */ | |
100 | int | |
101 | zwidthshow(register os_ptr op) | |
102 | { int code; | |
103 | float cxy[2]; | |
104 | check_type(op[-1], t_integer); | |
105 | if ( (ulong)(op[-1].value.intval) > 255 ) return e_rangecheck; | |
106 | if ( (code = num_params(op - 2, 2, cxy)) < 0 || | |
107 | (code = setup_show(op, finish_show, i_finish_show)) < 0 | |
108 | ) | |
109 | return code; | |
110 | if ( (code = gs_widthshow_n_init(senum, igs, cxy[0], cxy[1], | |
111 | (char)op[-1].value.intval, | |
112 | (char *)op->value.bytes, | |
113 | r_size(op))) < 0 ) | |
114 | { free_show(); | |
115 | return code; | |
116 | } | |
117 | pop(4); op -= 4; | |
118 | return show_continue(op); | |
119 | } | |
120 | ||
121 | /* awidthshow */ | |
122 | int | |
123 | zawidthshow(register os_ptr op) | |
124 | { int code; | |
125 | float cxy[2], axy[2]; | |
126 | check_type(op[-3], t_integer); | |
127 | if ( (ulong)(op[-3].value.intval) > 255 ) return e_rangecheck; | |
128 | if ( (code = num_params(op - 4, 2, cxy)) < 0 || | |
129 | (code = num_params(op - 1, 2, axy)) < 0 || | |
130 | (code = setup_show(op, finish_show, i_finish_show)) < 0 | |
131 | ) | |
132 | return code; | |
133 | if ( (code = gs_awidthshow_n_init(senum, igs, cxy[0], cxy[1], | |
134 | (char)op[-3].value.intval, | |
135 | axy[0], axy[1], | |
136 | (char *)op->value.bytes, | |
137 | r_size(op))) < 0 ) | |
138 | { free_show(); | |
139 | return code; | |
140 | } | |
141 | pop(6); op -= 6; | |
142 | return show_continue(op); | |
143 | } | |
144 | ||
145 | /* kshow */ | |
146 | int | |
147 | zkshow(register os_ptr op) | |
148 | { int code; | |
149 | check_proc(op[-1]); | |
150 | if ( (code = setup_show(op, finish_show, i_finish_show)) < 0 ) return code; | |
151 | if ( (code = gs_kshow_n_init(senum, igs, (char *)op->value.bytes, r_size(op))) < 0 ) | |
152 | { free_show(); | |
153 | return code; | |
154 | } | |
155 | sslot = op[-1]; /* save kerning proc */ | |
156 | pop(2); op -= 2; | |
157 | return show_continue(op); | |
158 | } | |
159 | ||
160 | /* Common finish procedure for all show operations. */ | |
161 | /* Doesn't have to do anything. */ | |
162 | private int | |
163 | finish_show(os_ptr op) | |
164 | { return 0; | |
165 | } | |
166 | ||
167 | /* stringwidth */ | |
168 | int | |
169 | zstringwidth(register os_ptr op) | |
170 | { int code = setup_show(op, finish_stringwidth, i_finish_stringwidth); | |
171 | if ( code < 0 ) return code; | |
172 | if ( (code = gs_stringwidth_n_init(senum, igs, (char *)op->value.bytes, r_size(op))) < 0 ) | |
173 | { free_show(); | |
174 | return code; | |
175 | } | |
176 | pop(1); op--; | |
177 | return show_continue(op); | |
178 | } | |
179 | /* Finishing procedure for stringwidth. */ | |
180 | /* Pushes the accumulated width. */ | |
181 | private int | |
182 | finish_stringwidth(register os_ptr op) | |
183 | { gs_point width; | |
184 | gs_show_width(senum, &width); | |
185 | push(2); | |
186 | make_real(op - 1, width.x); | |
187 | make_real(op, width.y); | |
188 | return 0; | |
189 | } | |
190 | ||
191 | /* charpath */ | |
192 | int | |
193 | zcharpath(register os_ptr op) | |
194 | { int code; | |
195 | check_type(*op, t_boolean); | |
196 | code = setup_show(op - 1, finish_show, i_finish_show); | |
197 | if ( code < 0 ) return code; | |
198 | if ( (code = gs_charpath_n_init(senum, igs, (char *)op[-1].value.bytes, r_size(op - 1), op->value.index)) < 0 ) | |
199 | { free_show(); | |
200 | return code; | |
201 | } | |
202 | pop(2); op -= 2; | |
203 | return show_continue(op); | |
204 | } | |
205 | ||
206 | /* setcachedevice */ | |
207 | int | |
208 | zsetcachedevice(register os_ptr op) | |
209 | { float wbox[6]; | |
210 | int npop = 6; | |
211 | gs_show_enum *penum = find_show(); | |
212 | int code = num_params(op, 6, wbox); | |
213 | if ( penum == 0 ) return e_undefined; | |
214 | if ( code < 0 ) | |
215 | { /* P*stScr*pt implementations apparently allow the */ | |
216 | /* bounding box to be specified as a 4-element array. */ | |
217 | /* Check for this here. */ | |
218 | check_array(*op); | |
219 | if ( r_size(op) != 4 || | |
220 | num_params(op - 1, 2, wbox) < 0 || | |
221 | num_params(op->value.refs + 3, 4, wbox + 2) < 0 | |
222 | ) | |
223 | return code; | |
224 | npop = 3; | |
225 | } | |
226 | if ( (code = gs_setcachedevice(penum, igs, wbox[0], wbox[1], wbox[2], wbox[3], wbox[4], wbox[5])) < 0 ) | |
227 | return code; | |
228 | pop(npop); | |
229 | return 0; | |
230 | } | |
231 | ||
232 | /* setcharwidth */ | |
233 | int | |
234 | zsetcharwidth(register os_ptr op) | |
235 | { float width[2]; | |
236 | gs_show_enum *penum = find_show(); | |
237 | int code = num_params(op, 2, width); | |
238 | if ( penum == 0 ) return e_undefined; | |
239 | if ( code < 0 || | |
240 | (code = gs_setcharwidth(penum, igs, width[0], width[1])) < 0 | |
241 | ) | |
242 | return code; | |
243 | pop(2); | |
244 | return 0; | |
245 | } | |
246 | ||
247 | /* setmetrics */ | |
248 | int | |
249 | zsetmetrics(register os_ptr op) | |
250 | { float params[4]; | |
251 | gs_point sb, w; | |
252 | gs_show_enum *penum = find_show(); | |
253 | int code, size; | |
254 | if ( penum == 0 ) return e_undefined; | |
255 | switch ( r_type(op) ) | |
256 | { | |
257 | case t_array: | |
258 | switch ( (size = r_size(op)) ) | |
259 | { | |
260 | case 2: case 4: break; | |
261 | default: return e_invalidfont; | |
262 | } | |
263 | code = num_params(op->value.refs + size - 1, size, params); | |
264 | if ( code < 0 ) return code; | |
265 | sb.x = params[0]; | |
266 | if ( size == 4 ) | |
267 | sb.y = params[1], w.x = params[2], w.y = params[3]; | |
268 | else | |
269 | sb.y = 0, w.x = params[1], w.y = 0; | |
270 | code = gs_setmetrics(penum, igs, &sb, &w); | |
271 | break; | |
272 | default: | |
273 | code = real_param(op, params); | |
274 | if ( code < 0 ) return code; | |
275 | w.x = params[0]; | |
276 | w.y = 0; | |
277 | code = gs_setmetrics(penum, igs, 0, &w); | |
278 | } | |
279 | if ( code >= 0 ) pop(1); | |
280 | return code; | |
281 | } | |
282 | ||
283 | /* type1addpath */ | |
284 | typedef struct { | |
285 | gs_font *pfont; | |
286 | fixed *osptr; /* fake interpreter operand stack */ | |
287 | fixed ostack[2]; | |
288 | } z1_data; | |
289 | int | |
290 | ztype1addpath(register os_ptr op) | |
291 | { int code, value; | |
292 | gs_show_enum *penum = find_show(); | |
293 | gs_font *pfont = gs_currentfont(igs); | |
294 | font_data *pfdata = (font_data *)pfont->client_data; | |
295 | gs_type1_state *pis; | |
296 | fixed discard; | |
297 | gs_fixed_point spt, ept; | |
298 | int flex_path_was_open; | |
299 | gs_type1_data tdata; | |
300 | z1_data zdata; | |
301 | byte *charstring = 0; | |
302 | ref enc_entry; | |
303 | if ( penum == 0 ) return e_undefined; | |
304 | check_type(*op, t_string); | |
305 | tdata = pfont->data.base.type1_data; | |
306 | zdata.pfont = pfont; | |
307 | zdata.osptr = zdata.ostack; | |
308 | tdata.proc_data = (char *)&zdata; | |
309 | if ( r_size(op) <= tdata.lenIV ) | |
310 | { /* String is empty, or too short. Just ignore it. */ | |
311 | pop(1); | |
312 | return 0; | |
313 | } | |
314 | pis = (gs_type1_state *)alloc(1, gs_type1_state_sizeof, "type1addpath"); | |
315 | if ( pis == 0 ) return e_VMerror; | |
316 | code = gs_type1_init(pis, penum, | |
317 | gs_show_in_charpath(penum), tdata.PaintType, | |
318 | &tdata); | |
319 | if ( code < 0 ) | |
320 | { alloc_free((char *)pis, 1, gs_type1_state_sizeof, "type1addpath"); | |
321 | return code; | |
322 | } | |
323 | charstring = op->value.bytes; | |
324 | more: code = gs_type1_interpret(pis, charstring, &value); | |
325 | charstring = 0; | |
326 | switch ( code ) | |
327 | { | |
328 | case type1_result_seac: | |
329 | { ref *pstdenc, *pcstr; | |
330 | if ( dict_find(&systemdict, | |
331 | &name_StandardEncoding, &pstdenc) <= 0 ) | |
332 | return e_undefined; | |
333 | code = array_get(pstdenc, (long)value, &enc_entry); | |
334 | if ( code < 0 ) return code; | |
335 | if ( dict_find(&pfdata->CharStrings, | |
336 | &enc_entry, &pcstr) <= 0 ) | |
337 | return e_undefined; | |
338 | if ( !r_has_type(pcstr, t_string) ) | |
339 | return e_invalidfont; | |
340 | charstring = pcstr->value.bytes; | |
341 | } goto more; | |
342 | case type1_result_callothersubr: | |
343 | { /* We aren't prepared to call the interpreter here, */ | |
344 | /* so we fake the Flex feature. */ | |
345 | gx_path *ppath = igs->path; | |
346 | gs_type1_pop(pis, &discard); /* pop # of args */ | |
347 | switch ( value ) | |
348 | { | |
349 | case 0: | |
350 | /* We have to do something really sleazy here, */ | |
351 | /* namely, make it look as though the rmovetos */ | |
352 | /* never really happened, because we don't want */ | |
353 | /* to interrupt the current subpath. */ | |
354 | gx_path_current_point(ppath, &ept); | |
355 | gx_path_add_point(ppath, spt.x, spt.y); | |
356 | ppath->subpath_open = flex_path_was_open; | |
357 | /* ^--- sleaze */ | |
358 | gx_path_add_line(ppath, ept.x, ept.y); | |
359 | /* Transfer endpoint coordinates to 'ostack' */ | |
360 | gs_type1_pop(pis, &zdata.ostack[0]); | |
361 | gs_type1_pop(pis, &zdata.ostack[1]); | |
362 | gs_type1_pop(pis, &discard); | |
363 | zdata.osptr = &zdata.ostack[2]; | |
364 | goto more; | |
365 | case 1: | |
366 | gx_path_current_point(ppath, &spt); | |
367 | flex_path_was_open = ppath->subpath_open; | |
368 | /* ^--- more sleaze */ | |
369 | goto more; | |
370 | case 2: | |
371 | goto more; | |
372 | case 3: | |
373 | gs_type1_pop(pis, &discard); /* pop subr# */ | |
374 | zdata.ostack[0] = int2fixed(3); | |
375 | zdata.osptr = &zdata.ostack[1]; | |
376 | goto more; | |
377 | } | |
378 | /* Unrecognized othersubr */ | |
379 | code = e_rangecheck; | |
380 | } break; | |
381 | } | |
382 | alloc_free((char *)pis, 1, gs_type1_state_sizeof, "type1addpath"); | |
383 | if ( code >= 0 ) pop(1); | |
384 | return code; | |
385 | } | |
386 | ||
387 | /* type1imagepath */ | |
388 | int | |
389 | ztype1imagepath(register os_ptr op) | |
390 | { float woxy[4]; | |
391 | int code; | |
392 | check_type(op[-7], t_string); | |
393 | check_type(op[-6], t_integer); | |
394 | check_type(op[-5], t_integer); | |
395 | if ( (code = num_params(op - 1, 4, woxy)) < 0 ) return code; | |
396 | check_write_type(*op, t_string); | |
397 | code = gs_type1imagepath(igs, op[-7].value.bytes, | |
398 | (int)op[-6].value.intval, (int)op[-5].value.intval, | |
399 | woxy[0], woxy[1], woxy[2], woxy[3], | |
400 | op->value.bytes, r_size(op)); | |
401 | if ( code < 0 ) return code; | |
402 | op[-7] = *op; | |
403 | r_set_size(op - 7, code); | |
404 | pop(7); | |
405 | return 0; | |
406 | } | |
407 | ||
408 | /* ------ Auxiliary procedures for type 1 fonts ------ */ | |
409 | ||
410 | int | |
411 | z1_subr_proc(gs_type1_data *pdata, int index, byte **pstr) | |
412 | { gs_font *pfont = ((z1_data *)(pdata->proc_data))->pfont; | |
413 | font_data *pfdata = (font_data *)(pfont->client_data); | |
414 | ref *psubr; | |
415 | if ( index < 0 || index >= r_size(&pfdata->Subrs) ) | |
416 | return e_rangecheck; | |
417 | psubr = pfdata->Subrs.value.refs + index; | |
418 | check_type(*psubr, t_string); | |
419 | *pstr = psubr->value.bytes; | |
420 | return 0; | |
421 | } | |
422 | ||
423 | int | |
424 | z1_pop_proc(gs_type1_data *pdata, fixed *pf) | |
425 | { *pf = *--(((z1_data *)(pdata->proc_data))->osptr); | |
426 | return 0; | |
427 | } | |
428 | ||
429 | /* ------ Initialization procedure ------ */ | |
430 | ||
431 | op_def zchar_op_defs[] = { | |
432 | {"3ashow", zashow}, | |
433 | {"6awidthshow", zawidthshow}, | |
434 | {"2charpath", zcharpath}, | |
435 | {"2kshow", zkshow}, | |
436 | {"3setcachedevice", zsetcachedevice}, | |
437 | {"2setcharwidth", zsetcharwidth}, | |
438 | {"1.setmetrics", zsetmetrics}, | |
439 | {"1show", zshow}, | |
440 | {"1stringwidth", zstringwidth}, | |
441 | {"1type1addpath", ztype1addpath}, | |
442 | {"8type1imagepath", ztype1imagepath}, | |
443 | {"4widthshow", zwidthshow}, | |
444 | /* Internal operators */ | |
445 | {"0%finish_show", finish_show, &i_finish_show}, | |
446 | {"0%finish_stringwidth", finish_stringwidth, &i_finish_stringwidth}, | |
447 | {"0%show_continue", show_continue, &i_show_continue}, | |
448 | op_def_end(0) | |
449 | }; | |
450 | ||
451 | /* ------ Internal routines ------ */ | |
452 | ||
453 | /* Set up for a show operator. */ | |
454 | /* The top stack element must be the string to be scanned. */ | |
455 | /* The caller has already done all other argument checking. */ | |
456 | private int | |
457 | setup_show(ref *op, op_proc_p endproc /* end procedure */, int proc_index) | |
458 | { gs_show_enum *penum; | |
459 | check_read_type(*op, t_string); | |
460 | check_estack(snumpush + 2); | |
461 | if ( (penum = (gs_show_enum *)alloc(1, gs_show_enum_sizeof, "setup_show")) == 0 ) | |
462 | return e_VMerror; | |
463 | mark_estack(es_show); | |
464 | push_op_estack(endproc, proc_index); | |
465 | ++esp; | |
466 | make_tv(esp, t_null, index, 0); /* reserve slot */ | |
467 | ++esp; | |
468 | make_tasv(esp, t_string, 0, gs_show_enum_sizeof, bytes, (byte *)penum); | |
469 | return o_push_estack; | |
470 | } | |
471 | ||
472 | /* Continuation operator for character rendering. */ | |
473 | private int | |
474 | show_continue(register os_ptr op) | |
475 | { gs_show_enum *penum = senum; | |
476 | int code = gs_show_next(penum); | |
477 | switch ( code ) | |
478 | { | |
479 | case 0: /* all done */ | |
480 | code = (*real_opproc(&seproc))(op); | |
481 | free_show(); | |
482 | return (code >= 0 ? o_pop_estack : code); | |
483 | case gs_show_kern: | |
484 | { ref *pslot = &sslot; | |
485 | push(2); | |
486 | make_int(op - 1, gs_kshow_previous_char(penum)); | |
487 | make_int(op, gs_kshow_next_char(penum)); | |
488 | push_op_estack(show_continue, i_show_continue); /* continue after kerning */ | |
489 | *++esp = *pslot; /* kerning procedure */ | |
490 | } | |
491 | return o_push_estack; | |
492 | case gs_show_render: | |
493 | { font_data *pfont = (font_data *)gs_currentfont(igs)->client_data; | |
494 | push(2); | |
495 | op[-1] = pfont->dict; /* push the font */ | |
496 | make_int(op, gs_show_current_char(penum)); | |
497 | push_op_estack(show_continue, i_show_continue); | |
498 | *++esp = pfont->BuildChar; | |
499 | } | |
500 | return o_push_estack; | |
501 | default: /* error */ | |
502 | free_show(); | |
503 | return code; | |
504 | } | |
505 | } | |
506 | ||
507 | /* Find the current show enumerator on the e-stack. */ | |
508 | private gs_show_enum * | |
509 | find_show() | |
510 | { es_ptr ep = esp; | |
511 | while ( !(r_has_type(ep, t_null) && ep->value.index == es_show) ) | |
512 | { if ( --ep < esbot ) return 0; /* no mark */ | |
513 | } | |
514 | return (gs_show_enum *)ep[snumpush - 1].value.bytes; | |
515 | } | |
516 | ||
517 | /* Discard the show record (after an error, or at the end). */ | |
518 | private void | |
519 | free_show() | |
520 | { alloc_free((char *)senum, 1, gs_show_enum_sizeof, "free_show"); | |
521 | esp -= snumpush; | |
522 | } |