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