xref: /plan9-contrib/sys/src/cmd/gs/src/gdevpsfx.c (revision 6a9fc400c33447ef5e1cda7185cb4de2c8e8010e)
1 /* Copyright (C) 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gdevpsfx.c,v 1.9 2001/07/26 03:04:51 lpd Exp $ */
20 /* Convert Type 1 Charstrings to Type 2 */
21 #include "math_.h"
22 #include "memory_.h"
23 #include "gx.h"
24 #include "gserrors.h"
25 #include "gxfixed.h"
26 #include "gxmatrix.h"		/* for gsfont.h */
27 #include "gxfont.h"
28 #include "gxfont1.h"
29 #include "gxtype1.h"
30 #include "stream.h"
31 #include "gdevpsf.h"
32 
33 /* ------ Type 1 Charstring parsing ------ */
34 
35 /*
36  * The parsing code handles numbers on its own; it reports callsubr and
37  * return operators to the caller, but also executes them.
38  *
39  * Only the following elements of the Type 1 state are used:
40  *	ostack, os_count, ipstack, ips_count
41  */
42 
43 #define CE_OFFSET 32		/* offset for extended opcodes */
44 
45 /* Skip over the initial bytes in a Charstring, if any. */
46 private void
47 skip_iv(gs_type1_state *pcis)
48 {
49     int skip = pcis->pfont->data.lenIV;
50     ip_state_t *ipsp = &pcis->ipstack[pcis->ips_count - 1];
51     const byte *cip = ipsp->char_string.data;
52     crypt_state state = crypt_charstring_seed;
53 
54     for (; skip > 0; ++cip, --skip)
55 	decrypt_skip_next(*cip, state);
56     ipsp->ip = cip;
57     ipsp->dstate = state;
58 }
59 
60 /*
61  * Set up for parsing a Type 1 Charstring.
62  *
63  * Only uses the following elements of *pfont:
64  *	data.lenIV
65  */
66 private void
67 type1_next_init(gs_type1_state *pcis, const gs_const_string *pstr,
68 		gs_font_type1 *pfont)
69 {
70     static const gs_log2_scale_point no_scale = {0, 0};
71 
72     gs_type1_interp_init(pcis, NULL, NULL, &no_scale, false, 0, pfont);
73     pcis->flex_count = flex_max;
74     pcis->dotsection_flag = dotsection_out;
75     pcis->ipstack[0].char_string = *pstr;
76     pcis->ipstack[0].free_char_string = 0;
77     skip_iv(pcis);
78 }
79 
80 /* Clear the Type 1 operand stack. */
81 inline private void
82 type1_clear(gs_type1_state *pcis)
83 {
84     pcis->os_count = 0;
85 }
86 
87 /* Execute a callsubr. */
88 private int
89 type1_callsubr(gs_type1_state *pcis, int index)
90 {
91     gs_font_type1 *pfont = pcis->pfont;
92     ip_state_t *ipsp1 = &pcis->ipstack[pcis->ips_count];
93     int code = pfont->data.procs.subr_data(pfont, index, false,
94 					   &ipsp1->char_string);
95 
96     if (code < 0)
97 	return_error(code);
98     ipsp1->free_char_string = code;
99     pcis->ips_count++;
100     skip_iv(pcis);
101     return code;
102 }
103 
104 /* Add 1 or 3 stem hints. */
105 private int
106 type1_stem1(gs_type1_state *pcis, stem_hint_table *psht, const fixed *pv,
107 	    byte *active_hints)
108 {
109     fixed v0 = pv[0], v1 = v0 + pv[1];
110     stem_hint *bot = &psht->data[0];
111     stem_hint *orig_top = bot + psht->count;
112     stem_hint *top = orig_top;
113 
114     if (psht->count >= max_stems)
115 	return_error(gs_error_limitcheck);
116     while (top > bot &&
117 	   (v0 < top[-1].v0 || (v0 == top[-1].v0 && v1 < top[-1].v1))
118 	   ) {
119 	*top = top[-1];
120 	top--;
121     }
122     if (top > bot && v0 == top[-1].v0 && v1 == top[-1].v1) {
123 	/* Duplicate hint, don't add it. */
124 	memmove(top, top + 1, (char *)orig_top - (char *)top);
125 	if (active_hints) {
126 	    uint index = top[-1].index;
127 
128 	    active_hints[index >> 3] |= 0x80 >> (index & 7);
129 	}
130 	return 0;
131     }
132     top->v0 = v0;
133     top->v1 = v1;
134     psht->count++;
135     return 0;
136 }
137 private void
138 type1_stem3(gs_type1_state *pcis, stem_hint_table *psht, const fixed *pv3,
139 	    byte *active_hints)
140 {
141     type1_stem1(pcis, psht, pv3, active_hints);
142     type1_stem1(pcis, psht, pv3 + 2, active_hints);
143     type1_stem1(pcis, psht, pv3 + 4, active_hints);
144 }
145 
146 /*
147  * Get the next operator from a Type 1 Charstring.  This procedure handles
148  * numbers, div, blend, pop, and callsubr/return.
149  */
150 private int
151 type1_next(gs_type1_state *pcis)
152 {
153     ip_state_t *ipsp = &pcis->ipstack[pcis->ips_count - 1];
154     const byte *cip;
155     crypt_state state;
156 #define CLEAR (csp = pcis->ostack - 1)
157     fixed *csp = &pcis->ostack[pcis->os_count - 1];
158     const bool encrypted = pcis->pfont->data.lenIV >= 0;
159     int c, code, num_results;
160 
161  load:
162     cip = ipsp->ip;
163     state = ipsp->dstate;
164     for (;;) {
165 	uint c0 = *cip++;
166 
167 	charstring_next(c0, state, c, encrypted);
168 	if (c >= c_num1) {
169 	    /* This is a number, decode it and push it on the stack. */
170 	    if (c < c_pos2_0) {	/* 1-byte number */
171 		decode_push_num1(csp, c);
172 	    } else if (c < cx_num4) {	/* 2-byte number */
173 		decode_push_num2(csp, c, cip, state, encrypted);
174 	    } else if (c == cx_num4) {	/* 4-byte number */
175 		long lw;
176 
177 		decode_num4(lw, cip, state, encrypted);
178 		*++csp = int2fixed(lw);
179 	    } else		/* not possible */
180 		return_error(gs_error_invalidfont);
181 	    continue;
182 	}
183 #ifdef DEBUG
184 	if (gs_debug_c('1')) {
185 	    const fixed *p;
186 
187 	    for (p = pcis->ostack; p <= csp; ++p)
188 		dprintf1(" %g", fixed2float(*p));
189 	    if (c == cx_escape) {
190 		crypt_state cstate = state;
191 		int cn;
192 
193 		charstring_next(*cip, cstate, cn, encrypted);
194 		dprintf1(" [*%d]\n", cn);
195 	    } else
196 		dprintf1(" [%d]\n", c);
197 	}
198 #endif
199 	switch ((char_command) c) {
200 	default:
201 	    break;
202 	case c_undef0:
203 	case c_undef2:
204 	case c_undef17:
205 	    return_error(gs_error_invalidfont);
206 	case c_callsubr:
207 	    code = type1_callsubr(pcis, fixed2int_var(*csp));
208 	    if (code < 0)
209 		return_error(code);
210 	    ipsp->ip = cip, ipsp->dstate = state;
211 	    --csp;
212 	    ++ipsp;
213 	    goto load;
214 	case c_return:
215 	    if (ipsp->free_char_string > 0)
216 		gs_free_const_string(pcis->pfont->memory,
217 				     ipsp->char_string.data,
218 				     ipsp->char_string.size, "type1_next");
219 	    pcis->ips_count--;
220 	    --ipsp;
221 	    goto load;
222 	case cx_escape:
223 	    charstring_next(*cip, state, c, encrypted);
224 	    ++cip;
225 	    switch ((char1_extended_command) c) {
226 	    default:
227 		c += CE_OFFSET;
228 		break;
229 	    case ce1_div:
230 		csp[-1] = float2fixed((double)csp[-1] / (double)*csp);
231 		--csp;
232 		continue;
233 	    case ce1_undoc15:	/* see gstype1.h */
234 		CLEAR;
235 		continue;
236 	    case ce1_callothersubr:
237 		switch (fixed2int_var(*csp)) {
238 		case 0:
239 		    pcis->ignore_pops = 2;
240 		    break;	/* pass to caller */
241 		case 3:
242 		    pcis->ignore_pops = 1;
243 		    break;	/* pass to caller */
244 		case 14:
245 		    num_results = 1; goto blend;
246 		case 15:
247 		    num_results = 2; goto blend;
248 		case 16:
249 		    num_results = 3; goto blend;
250 		case 17:
251 		    num_results = 4; goto blend;
252 		case 18:
253 		    num_results = 6;
254 		blend:
255 		    code = gs_type1_blend(pcis, csp, num_results);
256 		    if (code < 0)
257 			return code;
258 		    csp -= code;
259 		    continue;
260 		default:
261 		    break;	/* pass to caller */
262 		}
263 		break;
264 	    case ce1_pop:
265 		if (pcis->ignore_pops != 0) {
266 		    pcis->ignore_pops--;
267 		    continue;
268 		}
269 		return_error(gs_error_rangecheck);
270 	    }
271 	    break;
272 	}
273 	break;
274     }
275     ipsp->ip = cip, ipsp->dstate = state;
276     pcis->ips_count = ipsp + 1 - &pcis->ipstack[0];
277     pcis->os_count = csp + 1 - &pcis->ostack[0];
278     return c;
279 }
280 
281 /* ------ Output ------ */
282 
283 /* Put 2 or 4 bytes on a stream (big-endian). */
284 private void
285 sputc2(stream *s, int i)
286 {
287     sputc(s, (byte)(i >> 8));
288     sputc(s, (byte)i);
289 }
290 private void
291 sputc4(stream *s, int i)
292 {
293     sputc2(s, i >> 16);
294     sputc2(s, i);
295 }
296 
297 /* Put a Type 2 operator on a stream. */
298 private void
299 type2_put_op(stream *s, int op)
300 {
301     if (op >= CE_OFFSET) {
302 	spputc(s, cx_escape);
303 	spputc(s, op - CE_OFFSET);
304     } else
305 	sputc(s, op);
306 }
307 
308 /* Put a Type 2 number on a stream. */
309 private void
310 type2_put_int(stream *s, int i)
311 {
312     if (i >= -107 && i <= 107)
313 	sputc(s, (byte)(i + 139));
314     else if (i <= 1131 && i >= 0)
315 	sputc2(s, (c_pos2_0 << 8) + i - 108);
316     else if (i >= -1131 && i < 0)
317 	sputc2(s, (c_neg2_0 << 8) - i - 108);
318     else if (i >= -32768 && i <= 32767) {
319 	spputc(s, c2_shortint);
320 	sputc2(s, i);
321     } else {
322 	/*
323 	 * We can't represent this number directly: compute it.
324 	 * (This can be done much more efficiently in particular cases;
325 	 * we'll do this if it ever seems worthwhile.)
326 	 */
327 	type2_put_int(s, i >> 10);
328 	type2_put_int(s, 1024);
329 	type2_put_op(s, CE_OFFSET + ce2_mul);
330 	type2_put_int(s, i & 1023);
331 	type2_put_op(s, CE_OFFSET + ce2_add);
332     }
333 }
334 
335 /* Put a fixed value on a stream. */
336 private void
337 type2_put_fixed(stream *s, fixed v)
338 {
339     if (fixed_is_int(v))
340 	type2_put_int(s, fixed2int_var(v));
341     else if (v >= int2fixed(-32768) && v < int2fixed(32768)) {
342 	/* We can represent this as a 16:16 number. */
343 	spputc(s, cx_num4);
344 	sputc4(s, v << (16 - _fixed_shift));
345     } else {
346 	type2_put_int(s, fixed2int_var(v));
347 	type2_put_fixed(s, fixed_fraction(v));
348 	type2_put_op(s, CE_OFFSET + ce2_add);
349     }
350 }
351 
352 /* Put a stem hint table on a stream. */
353 private void
354 type2_put_stems(stream *s, const stem_hint_table *psht, int op)
355 {
356     fixed prev = 0;
357     int pushed = 0;
358     int i;
359 
360     for (i = 0; i < psht->count; ++i, pushed += 2) {
361 	fixed v0 = psht->data[i].v0;
362 	fixed v1 = psht->data[i].v1;
363 
364 	if (pushed > ostack_size - 2) {
365 	    type2_put_op(s, op);
366 	    pushed = 0;
367 	}
368 	type2_put_fixed(s, v0 - prev);
369 	type2_put_fixed(s, v1 - v0);
370 	prev = v1;
371     }
372     type2_put_op(s, op);
373 }
374 
375 /* Put out a hintmask command. */
376 private void
377 type2_put_hintmask(stream *s, const byte *mask, uint size)
378 {
379     uint ignore;
380 
381     type2_put_op(s, c2_hintmask);
382     sputs(s, mask, size, &ignore);
383 }
384 
385 /* ------ Main program ------ */
386 
387 /*
388  * Convert a Type 1 Charstring to (unencrypted) Type 2.
389  * For simplicity, we expand all Subrs in-line.
390  * We still need to optimize the output using these patterns:
391  *	(vhcurveto hvcurveto)* (vhcurveto hrcurveto | vrcurveto) =>
392  *	  vhcurveto
393  *	(hvcurveto vhcurveto)* (hvcurveto vrcurveto | hrcurveto) =>
394  *	  hvcurveto
395  */
396 #define MAX_STACK ostack_size
397 int
398 psf_convert_type1_to_type2(stream *s, const gs_const_string *pstr,
399 			   gs_font_type1 *pfont)
400 {
401     gs_type1_state cis;
402     bool first = true;
403     bool replace_hints = false;
404     bool hints_changed = false;
405     byte active_hints[(max_total_stem_hints + 7) / 8];
406     byte dot_save_hints[(max_total_stem_hints + 7) / 8];
407     uint hintmask_size;
408 #define HINTS_CHANGED()\
409   BEGIN\
410     hints_changed = replace_hints;\
411     if (hints_changed)\
412 	CHECK_OP();		/* see below */\
413   END
414 #define CHECK_HINTS_CHANGED()\
415   BEGIN\
416     if (hints_changed) {\
417 	type2_put_hintmask(s, active_hints, hintmask_size);\
418 	hints_changed = false;\
419     }\
420   END
421     /*
422      * In order to combine Type 1 operators, we usually delay writing
423      * out operators (but not their operands).  We must keep track of
424      * the stack depth so we don't exceed it when combining operators.
425      */
426     int depth;			/* of operands on stack */
427     int prev_op;		/* operator to write, -1 if none */
428 #define CLEAR_OP()\
429   (depth = 0, prev_op = -1)
430 #define CHECK_OP()\
431   BEGIN\
432     if (prev_op >= 0) {\
433 	type2_put_op(s, prev_op);\
434 	CLEAR_OP();\
435     }\
436   END
437 
438     /* Do a first pass to collect hints. */
439     reset_stem_hints(&cis);
440     type1_next_init(&cis, pstr, pfont);
441     for (;;) {
442 	int c = type1_next(&cis);
443 	fixed *csp = &cis.ostack[cis.os_count - 1];
444 
445 	switch (c) {
446 	default:
447 	    if (c < 0)
448 		return c;
449 	    type1_clear(&cis);
450 	    continue;
451 	case cx_hstem:
452 	    type1_stem1(&cis, &cis.hstem_hints, csp - 1, NULL);
453 	    goto clear;
454 	case cx_vstem:
455 	    type1_stem1(&cis, &cis.vstem_hints, csp - 1, NULL);
456 	    goto clear;
457 	case CE_OFFSET + ce1_vstem3:
458 	    type1_stem3(&cis, &cis.vstem_hints, csp - 5, NULL);
459 	    goto clear;
460 	case CE_OFFSET + ce1_hstem3:
461 	    type1_stem3(&cis, &cis.hstem_hints, csp - 5, NULL);
462 	clear:
463 	    type1_clear(&cis);
464 	    continue;
465 	case ce1_callothersubr:
466 	    if (*csp == int2fixed(3))
467 		replace_hints = true;
468 	    cis.os_count -= 2;
469 	    continue;
470 	case CE_OFFSET + ce1_dotsection:
471 	    replace_hints = true;
472 	    continue;
473 	case CE_OFFSET + ce1_seac:
474 	case cx_endchar:
475 	    break;
476 	}
477 	break;
478     }
479     /*
480      * Number the hints for hintmask.  We must do this even if we never
481      * replace hints, because type1_stem# uses the index to set bits in
482      * active_hints.
483      */
484     {
485 	int i;
486 
487 	for (i = 0; i < cis.hstem_hints.count; ++i)
488 	    cis.hstem_hints.data[i].index = i;
489 	for (i = 0; i < cis.vstem_hints.count; ++i)
490 	    cis.vstem_hints.data[i].index = i + cis.hstem_hints.count;
491     }
492     if (replace_hints) {
493 	hintmask_size =
494 	    (cis.hstem_hints.count + cis.vstem_hints.count + 7) / 8;
495 	memset(active_hints, 0, hintmask_size);
496     } else
497 	hintmask_size = 0;
498 
499     /* Do a second pass to write the result. */
500     type1_next_init(&cis, pstr, pfont);
501     CLEAR_OP();
502     for (;;) {
503 	int c = type1_next(&cis);
504 	fixed *csp = &cis.ostack[cis.os_count - 1];
505 #define POP(n)\
506   (csp -= (n), cis.os_count -= (n))
507 	int i;
508 	fixed mx, my;
509 
510 	switch (c) {
511 	default:
512 	    if (c < 0)
513 		return c;
514 	    if (c >= CE_OFFSET)
515 		return_error(gs_error_rangecheck);
516 	    /* The Type 1 use of all other operators is the same in Type 2. */
517 	copy:
518 	    CHECK_OP();
519 	    CHECK_HINTS_CHANGED();
520 	put:
521 	    for (i = 0; i < cis.os_count; ++i)
522 		type2_put_fixed(s, cis.ostack[i]);
523 	    depth += cis.os_count;
524 	    prev_op = c;
525 	    type1_clear(&cis);
526 	    continue;
527 	case cx_hstem:
528 	    type1_stem1(&cis, &cis.hstem_hints, csp - 1, active_hints);
529 	hint:
530 	    HINTS_CHANGED();
531 	    type1_clear(&cis);
532 	    continue;
533 	case cx_vstem:
534 	    type1_stem1(&cis, &cis.vstem_hints, csp - 1, active_hints);
535 	    goto hint;
536 	case CE_OFFSET + ce1_vstem3:
537 	    type1_stem3(&cis, &cis.vstem_hints, csp - 5, active_hints);
538 	    goto hint;
539 	case CE_OFFSET + ce1_hstem3:
540 	    type1_stem3(&cis, &cis.hstem_hints, csp - 5, active_hints);
541 	    goto hint;
542 	case CE_OFFSET + ce1_dotsection:
543 	    if (cis.dotsection_flag == dotsection_out) {
544 		memcpy(dot_save_hints, active_hints, hintmask_size);
545 		memset(active_hints, 0, hintmask_size);
546 		cis.dotsection_flag = dotsection_in;
547 	    } else {
548 		memcpy(active_hints, dot_save_hints, hintmask_size);
549 		cis.dotsection_flag = dotsection_out;
550 	    }
551 	    HINTS_CHANGED();
552 	    continue;
553 	case c1_closepath:
554 	case CE_OFFSET + ce1_setcurrentpoint:
555 	    continue;
556 	case cx_vmoveto:
557 	    mx = 0, my = *csp;
558 	    POP(1); goto move;
559 	case cx_hmoveto:
560 	    mx = *csp, my = 0;
561 	    POP(1); goto move;
562 	case cx_rmoveto:
563 	    mx = csp[-1], my = *csp;
564 	    POP(2);
565 	move:
566 	    CHECK_OP();
567 	    if (first) {
568 		if (cis.os_count)
569 		    type2_put_fixed(s, *csp); /* width */
570 		mx += cis.lsb.x, my += cis.lsb.y;
571 		first = false;
572 	    }
573 	    if (cis.flex_count != flex_max) {
574 		/* We're accumulating points for a flex. */
575 		if (type1_next(&cis) != ce1_callothersubr)
576 		    return_error(gs_error_rangecheck);
577 		csp = &cis.ostack[cis.os_count - 1];
578 		if (*csp != int2fixed(2) || csp[-1] != fixed_0)
579 		    return_error(gs_error_rangecheck);
580 		cis.flex_count++;
581 		csp[-1] = mx, *csp = my;
582 		continue;
583 	    }
584 	    CHECK_HINTS_CHANGED();
585 	    if (mx == 0) {
586 		type2_put_fixed(s, my);
587 		depth = 1, prev_op = cx_vmoveto;
588 	    } else if (my == 0) {
589 		type2_put_fixed(s, mx);
590 		depth = 1, prev_op = cx_hmoveto;
591 	    } else {
592 		type2_put_fixed(s, mx);
593 		type2_put_fixed(s, my);
594 		depth = 2, prev_op = cx_rmoveto;
595 	    }
596 	    type1_clear(&cis);
597 	    continue;
598 	case c1_hsbw:
599 	    gs_type1_sbw(&cis, cis.ostack[0], fixed_0, cis.ostack[1], fixed_0);
600 	    /*
601 	     * Leave the l.s.b. on the operand stack for the initial hint,
602 	     * moveto, or endchar command.
603 	     */
604 	    cis.ostack[0] = cis.ostack[1];
605 	sbw:
606 	    if (cis.ostack[0] == pfont->data.defaultWidthX)
607 		cis.os_count = 0;
608 	    else {
609 		cis.ostack[0] -= pfont->data.nominalWidthX;
610 		cis.os_count = 1;
611 	    }
612 	    if (cis.hstem_hints.count) {
613 		if (cis.os_count)
614 		    type2_put_fixed(s, cis.ostack[0]);
615 		cis.os_count = 0;
616 		type2_put_stems(s, &cis.hstem_hints,
617 				(replace_hints ? c2_hstemhm : cx_hstem));
618 	    }
619 	    if (cis.vstem_hints.count) {
620 		if (cis.os_count)
621 		    type2_put_fixed(s, cis.ostack[0]);
622 		cis.os_count = 0;
623 		type2_put_stems(s, &cis.vstem_hints,
624 				(replace_hints ? c2_vstemhm : cx_vstem));
625 	    }
626 	    continue;
627 	case CE_OFFSET + ce1_seac:
628 	    /*
629 	     * It is an undocumented feature of the Type 2 CharString
630 	     * format that endchar + 4 or 5 operands is equivalent to
631 	     * seac with an implicit asb operand + endchar with 0 or 1
632 	     * operands.  Remove the asb argument from the stack, but
633 	     * adjust the adx argument to compensate for the fact that
634 	     * Type 2 CharStrings don't have any concept of l.s.b.
635 	     */
636 	    csp[-3] += cis.lsb.x - csp[-4];
637 	    memmove(csp - 4, csp - 3, sizeof(*csp) * 4);
638 	    POP(1);
639 	    /* (falls through) */
640 	case cx_endchar:
641 	    CHECK_OP();
642 	    for (i = 0; i < cis.os_count; ++i)
643 		type2_put_fixed(s, cis.ostack[i]);
644 	    type2_put_op(s, cx_endchar);
645 	    return 0;
646 	case CE_OFFSET + ce1_sbw:
647 	    gs_type1_sbw(&cis, cis.ostack[0], cis.ostack[1],
648 			 cis.ostack[2], cis.ostack[3]);
649 	    cis.ostack[0] = cis.ostack[2];
650 	    goto sbw;
651 	case ce1_callothersubr:
652 	    CHECK_OP();
653 	    switch (fixed2int_var(*csp)) {
654 	    default:
655 		return_error(gs_error_rangecheck);
656 	    case 0:
657 		/*
658 		 * The operand stack contains: delta to reference point,
659 		 * 6 deltas for the two curves, fd, final point, 3, 0.
660 		 */
661 		csp[-18] += csp[-16], csp[-17] += csp[-15];
662 		memmove(csp - 16, csp - 14, sizeof(*csp) * 11);
663 		cis.os_count -= 6, csp -= 6;
664 		/*
665 		 * We could optimize by using [h]flex[1],
666 		 * but it isn't worth the trouble.
667 		 */
668 		c = CE_OFFSET + ce2_flex;
669 		cis.flex_count = flex_max;	/* not inside flex */
670 		cis.ignore_pops = 2;
671 		goto copy;
672 	    case 1:
673 		cis.flex_count = 0;
674 		cis.os_count -= 2;
675 		continue;
676 	    /*case 2:*/		/* detected in *moveto */
677 	    case 3:
678 		memset(active_hints, 0, hintmask_size);
679 		HINTS_CHANGED();
680 		cis.ignore_pops = 1;
681 		cis.os_count -= 2;
682 		continue;
683 	    case 12:
684 	    case 13:
685 		/* Counter control is not implemented. */
686 		cis.os_count -= 2 + fixed2int(csp[-1]);
687 		continue;
688 	    }
689 	    /*
690 	     * The remaining cases are strictly for optimization.
691 	     */
692 	case cx_rlineto:
693 	    if (depth > MAX_STACK - 2)
694 		goto copy;
695 	    switch (prev_op) {
696 	    case cx_rlineto:	/* rlineto+ => rlineto */
697 		goto put;
698 	    case cx_rrcurveto:	/* rrcurveto+ rlineto => rcurveline */
699 		c = c2_rcurveline;
700 		goto put;
701 	    default:
702 		goto copy;
703 	    }
704 	case cx_hlineto:  /* hlineto (vlineto hlineto)* [vlineto] => hlineto */
705 	    if (depth > MAX_STACK - 1 ||
706 		prev_op != (depth & 1 ? cx_vlineto : cx_hlineto))
707 		goto copy;
708 	    c = prev_op;
709 	    goto put;
710 	case cx_vlineto:  /* vlineto (hlineto vlineto)* [hlineto] => vlineto */
711 	    if (depth > MAX_STACK - 1 ||
712 		prev_op != (depth & 1 ? cx_hlineto : cx_vlineto))
713 		goto copy;
714 	    c = prev_op;
715 	    goto put;
716 	case cx_hvcurveto: /* hvcurveto (vhcurveto hvcurveto)* => hvcurveto */
717 				/* (vhcurveto hvcurveto)+ => vhcurveto  */
718 	    /*
719 	     * We have to check (depth & 1) because the last curve might
720 	     * have 5 parameters rather than 4 (see rrcurveto below).
721 	     */
722 	    if ((depth & 1) || depth > MAX_STACK - 4 ||
723 		prev_op != (depth & 4 ? cx_vhcurveto : cx_hvcurveto))
724 		goto copy;
725 	    c = prev_op;
726 	    goto put;
727 	case cx_vhcurveto: /* vhcurveto (hvcurveto vhcurveto)* => vhcurveto */
728 				/* (hvcurveto vhcurveto)+ => hvcurveto  */
729 	    /* See above re the (depth & 1) check. */
730 	    if ((depth & 1) || depth > MAX_STACK - 4 ||
731 		prev_op != (depth & 4 ? cx_hvcurveto : cx_vhcurveto))
732 		goto copy;
733 	    c = prev_op;
734 	    goto put;
735 	case cx_rrcurveto:
736 	    if (depth == 0) {
737 		if (csp[-1] == 0) {
738 		    /* A|0 B C D 0 F rrcurveto => [A] B C D F vvcurveto */
739 		    c = c2_vvcurveto;
740 		    csp[-1] = csp[0];
741 		    if (csp[-5] == 0) {
742 			memcpy(csp - 5, csp - 4, sizeof(*csp) * 4);
743 			POP(2);
744 		    } else
745 			POP(1);
746 		} else if (*csp == 0) {
747 		    /* A B|0 C D E 0 rrcurveto => [B] A C D E hhcurveto */
748 		    c = c2_hhcurveto;
749 		    if (csp[-4] == 0) {
750 			memcpy(csp - 4, csp - 3, sizeof(*csp) * 3);
751 			POP(2);
752 		    } else {
753 			*csp = csp[-5], csp[-5] = csp[-4], csp[-4] = *csp;
754 			POP(1);
755 		    }
756 		}
757 		/*
758 		 * We could also optimize:
759 		 *   0 B C D E F|0 rrcurveto => B C D E [F] vhcurveto
760 		 *   A 0 C D E|0 F rrcurveto => A C D F [E] hvcurveto
761 		 * but this gets in the way of subsequent optimization
762 		 * of multiple rrcurvetos, so we don't do it.
763 		 */
764 		goto copy;
765 	    }
766 	    if (depth > MAX_STACK - 6)
767 		goto copy;
768 	    switch (prev_op) {
769 	    case c2_hhcurveto:	/* hrcurveto (x1 0 x2 y2 x3 0 rrcurveto)* => */
770 				/* hhcurveto */
771 		if (csp[-4] == 0 && *csp == 0) {
772 		    memcpy(csp - 4, csp - 3, sizeof(*csp) * 3);
773 		    c = prev_op;
774 		    POP(2);
775 		    goto put;
776 		}
777 		goto copy;
778 	    case c2_vvcurveto:	/* rvcurveto (0 y1 x2 y2 0 y3 rrcurveto)* => */
779 				/* vvcurveto */
780 		if (csp[-5] == 0 && csp[-1] == 0) {
781 		    memcpy(csp - 5, csp - 4, sizeof(*csp) * 3);
782 		    csp[-2] = *csp;
783 		    c = prev_op;
784 		    POP(2);
785 		    goto put;
786 		}
787 		goto copy;
788 	    case cx_hvcurveto:
789 		if (depth & 1)
790 		    goto copy;
791 		if (!(depth & 4))
792 		    goto hrc;
793 	    vrc:  /* (vhcurveto hvcurveto)+ vrcurveto => vhcurveto */
794 		/* hvcurveto (vhcurveto hvcurveto)* vrcurveto => hvcurveto */
795 		if (csp[-5] != 0)
796 		    goto copy;
797 		memcpy(csp - 5, csp - 4, sizeof(*csp) * 5);
798 		c = prev_op;
799 		POP(1);
800 		goto put;
801 	    case cx_vhcurveto:
802 		if (depth & 1)
803 		    goto copy;
804 		if (!(depth & 4))
805 		    goto vrc;
806 	    hrc:  /* (hvcurveto vhcurveto)+ hrcurveto => hvcurveto */
807 		/* vhcurveto (hvcurveto vhcurveto)* hrcurveto => vhcurveto */
808 		if (csp[-4] != 0)
809 		    goto copy;
810 		/* A 0 C D E F => A C D F E */
811 		memcpy(csp - 4, csp - 3, sizeof(*csp) * 2);
812 		csp[-2] = *csp;
813 		c = prev_op;
814 		POP(1);
815 		goto put;
816 	    case cx_rlineto:	/* rlineto+ rrcurveto => rlinecurve */
817 		c = c2_rlinecurve;
818 		goto put;
819 	    case cx_rrcurveto:	/* rrcurveto+ => rrcurveto */
820 		goto put;
821 	    default:
822 		goto copy;
823 	    }
824 	}
825     }
826 }
827