| @@ -12,6 +12,30 @@ enum { · 11 unmodified lines |
| 12 | | 12 | |
| 13 | typedef struct Op Op; | 13 | typedef struct Op Op; |
| 14 | | 14 | |
| | 15 | static void |
| | 16 | matidentity(double *arr) |
| | 17 | { |
| | 18 | double src[6] = { |
| | 19 | 1, 0, |
| | 20 | 0, 1, |
| | 21 | 0, 0 |
| | 22 | }; |
| | 23 | memcpy(arr, src, sizeof(double) * 6); |
| | 24 | } |
| | 25 | |
| | 26 | static void |
| | 27 | matmult(double *m1, double *m2, double *out) |
| | 28 | { |
| | 29 | double result[6]; |
| | 30 | result[0] = m1[0] * m2[0] + m1[1] * m2[2]; |
| | 31 | result[1] = m1[0] * m2[1] + m1[1] * m2[3]; |
| | 32 | result[2] = m1[2] * m2[0] + m1[3] * m2[2]; |
| | 33 | result[3] = m1[2] * m2[1] + m1[3] * m2[3]; |
| | 34 | result[4] = m1[4] * m2[0] + m1[5] * m2[2] + m2[4]; |
| | 35 | result[5] = m1[4] * m2[1] + m1[5] * m2[3] + m2[5]; |
| | 36 | memcpy(out, result, sizeof(double) * 6); |
| | 37 | } |
| | 38 | |
| 15 | struct Op { | 39 | struct Op { |
| 16 | char *s; | 40 | char *s; |
| 17 | int (*f)(Op *op, Page *p); | 41 | int (*f)(Op *op, Page *p); |
| @@ -19,6 +43,16 @@ struct Op { · 1 unmodified line |
| 19 | int flags; | 43 | int flags; |
| 20 | }; | 44 | }; |
| 21 | | 45 | |
| | 46 | static int |
| | 47 | flagless(Op *op) |
| | 48 | { |
| | 49 | if(op->flags != 0){ |
| | 50 | fprint(2, "Op '%s' expected no flags\n", op->s); |
| | 51 | return 0; |
| | 52 | } |
| | 53 | return 1; |
| | 54 | } |
| | 55 | |
| 22 | static int | 56 | static int |
| 23 | cobegin(Op *op, Page *p) | 57 | cobegin(Op *op, Page *p) |
| 24 | { | 58 | { |
| @@ -36,29 +70,54 @@ coend(Op *op, Page *p) · 11 unmodified lines |
| 36 | static int | 70 | static int |
| 37 | gspush(Op *op, Page *p) | 71 | gspush(Op *op, Page *p) |
| 38 | { | 72 | { |
| 39 | USED(op, p); | 73 | USED(op); |
| 40 | return 0; | 74 | GS *r = realloc(p->GS, sizeof(GS) * (p->nGS + 1)); |
| | 75 | if(r == nil) |
| | 76 | return 0; |
| | 77 | p->GS = r; |
| | 78 | p->nGS += 1; |
| | 79 | p->GSactive = &p->GS[p->nGS - 1]; |
| | 80 | *(p->GSactive) = p->GS[p->nGS - 2]; |
| | 81 | return 1; |
| 41 | } | 82 | } |
| 42 | | 83 | |
| 43 | static int | 84 | static int |
| 44 | gspop(Op *op, Page *p) | 85 | gspop(Op *op, Page *p) |
| 45 | { | 86 | { |
| 46 | USED(op, p); | 87 | USED(op); |
| 47 | return 0; | 88 | GS *r = realloc(p->GS, sizeof(GS) * (p->nGS - 1)); |
| | 89 | if(r == nil) |
| | 90 | return 0; |
| | 91 | p->GS = r; |
| | 92 | p->nGS -= 1; |
| | 93 | p->GSactive = &p->GS[p->nGS - 1]; |
| | 94 | return 1; |
| 48 | } | 95 | } |
| 49 | | 96 | |
| | 97 | /* six parameters give the inputs a,b,c,d,e,f for the matrix |
| | 98 | [a b 0] |
| | 99 | [c d 0] |
| | 100 | [e f 1] |
| | 101 | That matrix should be premultiplied with the current matrix |
| | 102 | newCTM = input x oldCTM |
| | 103 | (8.3.4) |
| | 104 | */ |
| 50 | static int | 105 | static int |
| 51 | gsctm(Op *op, Page *p) | 106 | gsctm(Op *op, Page *p) |
| 52 | { | 107 | { |
| 53 | USED(op, p); | 108 | double input[6]; |
| 54 | return 0; | 109 | int i; |
| | 110 | for(i = 0; i < 6; i += 1) |
| | 111 | input[i] = arrayget(p->stack, i)->num.d; |
| | 112 | matmult(input, p->GSactive->CTM, p->GSactive->CTM); |
| | 113 | return flagless(op); |
| 55 | } | 114 | } |
| 56 | | 115 | |
| 57 | static int | 116 | static int |
| 58 | gswidth(Op *op, Page *p) | 117 | gswidth(Op *op, Page *p) |
| 59 | { | 118 | { |
| 60 | USED(op, p); | 119 | p->GSactive->LW = arrayget(p->stack, 0)->num.i; |
| 61 | return 0; | 120 | return flagless(op); |
| 62 | } | 121 | } |
| 63 | | 122 | |
| 64 | static int | 123 | static int |
| @@ -99,8 +158,8 @@ gsintent(Op *op, Page *p) · 34 unmodified lines |
| 99 | static int | 158 | static int |
| 100 | gsflatness(Op *op, Page *p) | 159 | gsflatness(Op *op, Page *p) |
| 101 | { | 160 | { |
| 102 | USED(op, p); | 161 | p->GSactive->FL = arrayget(p->stack, 0)->num.d; |
| 103 | return 0; | 162 | return flagless(op); |
| 104 | } | 163 | } |
| 105 | | 164 | |
| 106 | static int | 165 | static int |
| @@ -114,21 +173,21 @@ static int · 7 unmodified lines |
| 114 | pcmove(Op *op, Page *p) | 173 | pcmove(Op *op, Page *p) |
| 115 | { | 174 | { |
| 116 | USED(op, p); | 175 | USED(op, p); |
| 117 | return 0; | 176 | return 1; |
| 118 | } | 177 | } |
| 119 | | 178 | |
| 120 | static int | 179 | static int |
| 121 | pcline(Op *op, Page *p) | 180 | pcline(Op *op, Page *p) |
| 122 | { | 181 | { |
| 123 | USED(op, p); | 182 | USED(op, p); |
| 124 | return 0; | 183 | return 1; |
| 125 | } | 184 | } |
| 126 | | 185 | |
| 127 | static int | 186 | static int |
| 128 | pccurve(Op *op, Page *p) | 187 | pccurve(Op *op, Page *p) |
| 129 | { | 188 | { |
| 130 | USED(op, p); | 189 | USED(op, p); |
| 131 | return 0; | 190 | return 1; |
| 132 | } | 191 | } |
| 133 | | 192 | |
| 134 | static int | 193 | static int |
| @@ -149,7 +208,7 @@ static int · 14 unmodified lines |
| 149 | ppstroke(Op *op, Page *p) | 208 | ppstroke(Op *op, Page *p) |
| 150 | { | 209 | { |
| 151 | USED(op, p); | 210 | USED(op, p); |
| 152 | return 0; | 211 | return 1; |
| 153 | } | 212 | } |
| 154 | | 213 | |
| 155 | static int | 214 | static int |
| @@ -218,8 +277,21 @@ ccolour2(Op *op, Page *p) · 62 unmodified lines |
| 218 | static int | 277 | static int |
| 219 | cgray(Op *op, Page *p) | 278 | cgray(Op *op, Page *p) |
| 220 | { | 279 | { |
| 221 | USED(op, p); | 280 | int value = 255 * arrayget(p->stack, 0)->num.d; |
| 222 | return 0; | 281 | int i; |
| | 282 | u32int *color; |
| | 283 | if(op->flags & Nonstroking){ |
| | 284 | color = &p->GSactive->NSC; |
| | 285 | p->GSactive->NSCS = DeviceGray; |
| | 286 | } else{ |
| | 287 | color = &p->GSactive->SC; |
| | 288 | p->GSactive->SCS = DeviceGray; |
| | 289 | } |
| | 290 | *color = 0; |
| | 291 | for(i = 0; i < 3; i += 1) |
| | 292 | *color = (*color | value) << 8; |
| | 293 | *color |= 255; |
| | 294 | return 1; |
| 223 | } | 295 | } |
| 224 | | 296 | |
| 225 | static int | 297 | static int |
| @@ -295,21 +367,51 @@ tshscale(Op *op, Page *p) · 69 unmodified lines |
| 295 | static int | 367 | static int |
| 296 | tslead(Op *op, Page *p) | 368 | tslead(Op *op, Page *p) |
| 297 | { | 369 | { |
| 298 | int d = arrayget(p->stack, 0)->num.d / 20; | 370 | p->TS.TL = arrayget(p->stack, 0)->num.d; |
| 299 | while(d > 0){ | 371 | return flagless(op); |
| 300 | d -= 1; | 372 | } |
| 301 | if(bufput(&p->buf, (uchar*)"\n", 1) == -1) | 373 | |
| 302 | sysfatal("OOM"); | 374 | static int |
| | 375 | fontwidths(Page *p) |
| | 376 | { |
| | 377 | Object *o; |
| | 378 | int i; |
| | 379 | if(p->GSactive->Font.widths != nil) |
| | 380 | free(p->GSactive->Font.widths); |
| | 381 | o = dictget(p->GSactive->Font.font, "FirstChar"); |
| | 382 | if(o == nil) |
| | 383 | return 1; |
| | 384 | p->GSactive->Font.first = o->num.i; |
| | 385 | p->GSactive->Font.last = dictget(p->GSactive->Font.font, "LastChar")->num.i; |
| | 386 | p->GSactive->Font.widths = malloc(sizeof(int) * (p->GSactive->Font.last - p->GSactive->Font.first + 1)); |
| | 387 | if(p->GSactive->Font.widths == nil){ |
| | 388 | print("Failed to allocate for (%d, %d): %d\n", p->GSactive->Font.first, p->GSactive->Font.last, p->GSactive->Font.last - p->GSactive->Font.first + 1); |
| | 389 | return 1; |
| 303 | } | 390 | } |
| 304 | USED(op, p); | 391 | o = dictget(p->GSactive->Font.font, "Widths"); |
| 305 | return 0; | 392 | if(o == nil) |
| | 393 | return 0; |
| | 394 | for(i = 0; i < arraylen(o); i += 1) |
| | 395 | p->GSactive->Font.widths[i] = arrayget(o, i)->num.i; |
| | 396 | o = dictget(p->GSactive->Font.font, "FontDescriptor"); |
| | 397 | p->GSactive->Font.defwidth = dictget(o, "MissingWidth")->num.i; |
| | 398 | return 1; |
| 306 | } | 399 | } |
| 307 | | 400 | |
| 308 | static int | 401 | static int |
| 309 | tsfontsz(Op *op, Page *p) | 402 | tsfontsz(Op *op, Page *p) |
| 310 | { | 403 | { |
| 311 | USED(op, p); | 404 | char *name = arrayget(p->stack, 0)->name; |
| 312 | return 0; | 405 | p->GSactive->Font.font = dictget(dictget(dictget(p->obj, "Resources"), "Font"), name); |
| | 406 | if(p->GSactive->Font.font == nil){ |
| | 407 | werrstr("Font not found: '%s'", name); |
| | 408 | return 0; |
| | 409 | } |
| | 410 | p->GSactive->Font.enc = dictget(p->GSactive->Font.font, "Encoding"); |
| | 411 | if(p->GSactive->Font.enc) |
| | 412 | p->GSactive->Font.enc = dictget(p->GSactive->Font.enc, "Differences"); |
| | 413 | p->GSactive->Font.size = arrayget(p->stack, 1)->num.d; |
| | 414 | return fontwidths(p) && flagless(op); |
| 313 | } | 415 | } |
| 314 | | 416 | |
| 315 | static int | 417 | static int |
| @@ -329,15 +431,39 @@ tsrise(Op *op, Page *p) · 13 unmodified lines |
| 329 | static int | 431 | static int |
| 330 | tobegin(Op *op, Page *p) | 432 | tobegin(Op *op, Page *p) |
| 331 | { | 433 | { |
| 332 | USED(op, p); | 434 | if(p->TS.inobj){ |
| 333 | return 0; | 435 | werrstr("Text objects must not be nested"); |
| | 436 | return 0; |
| | 437 | } |
| | 438 | matidentity(p->TS.Tm); |
| | 439 | matidentity(p->TS.Tlm); |
| | 440 | p->TS.inobj = 1; |
| | 441 | p->GSactive->Font.font = nil; |
| | 442 | return flagless(op); |
| 334 | } | 443 | } |
| 335 | | 444 | |
| 336 | static int | 445 | static int |
| 337 | toend(Op *op, Page *p) | 446 | toend(Op *op, Page *p) |
| 338 | { | 447 | { |
| 339 | USED(op, p); | 448 | if(!p->TS.inobj){ |
| 340 | return 0; | 449 | werrstr("ET found without BT"); |
| | 450 | return 0; |
| | 451 | } |
| | 452 | p->TS.inobj = 0; |
| | 453 | return flagless(op); |
| | 454 | } |
| | 455 | |
| | 456 | static int |
| | 457 | tmove(Page *p, double x, double y, int tlm) |
| | 458 | { |
| | 459 | double shift[6] = {1, 0, 0, 1, x, y}; |
| | 460 | if(tlm){ |
| | 461 | matmult(shift, p->TS.Tlm, p->TS.Tlm); |
| | 462 | memcpy(p->TS.Tm, p->TS.Tlm, sizeof(double) * 6); |
| | 463 | } else{ |
| | 464 | matmult(shift, p->TS.Tm, p->TS.Tm); |
| | 465 | } |
| | 466 | return 1; |
| 341 | } | 467 | } |
| 342 | | 468 | |
| 343 | static int | 469 | static int |
| @@ -346,62 +472,141 @@ tpmove(Op *op, Page *p) · 2 unmodified lines |
| 346 | Object *x, *y; | 472 | Object *x, *y; |
| 347 | x = arrayget(p->stack, 0); | 473 | x = arrayget(p->stack, 0); |
| 348 | y = arrayget(p->stack, 1); | 474 | y = arrayget(p->stack, 1); |
| 349 | if(y->num.d != 0){ | 475 | if(op->flags & Leading) |
| 350 | if(bufput(&p->buf, (uchar*)"\n", 1) == -1) | 476 | p->TS.TL = -y->num.d; |
| 351 | sysfatal("OOM"); | 477 | return tmove(p, x->num.d, y->num.d, 1); |
| 352 | } | | |
| 353 | else if(x->num.d < 50) | | |
| 354 | if(bufput(&p->buf, (uchar*)" ", 1) == -1) | | |
| 355 | sysfatal("OOM"); | | |
| 356 | USED(op, p); | | |
| 357 | return 0; | | |
| 358 | } | 478 | } |
| 359 | | 479 | |
| 360 | static int | 480 | static int |
| 361 | tpmatrix(Op *op, Page *p) | 481 | tpmatrix(Op *op, Page *p) |
| 362 | { | 482 | { |
| 363 | USED(op, p); | 483 | int i; |
| 364 | return 0; | 484 | for(i = 0; i < 6; i += 1){ |
| | 485 | p->TS.Tm[i] = arrayget(p->stack, i)->num.d; |
| | 486 | p->TS.Tlm[i] = p->TS.Tm[i]; |
| | 487 | } |
| | 488 | return flagless(op); |
| 365 | } | 489 | } |
| 366 | | 490 | |
| 367 | static int | 491 | static int |
| 368 | tpmove0(Op *op, Page *p) | 492 | tpmove0(Op *op, Page *p) |
| 369 | { | 493 | { |
| 370 | USED(op, p); | 494 | return tmove(p, 0, 0 - p->TS.TL, 1) && flagless(op); |
| 371 | if(bufput(&p->buf, (uchar*)"\n", 1) == -1) | 495 | } |
| 372 | sysfatal("OOM"); | 496 | |
| 373 | return 0; | 497 | static int |
| | 498 | writepatched(Page *p, uchar c) |
| | 499 | { |
| | 500 | int i, len, d = 0; |
| | 501 | Object *o; |
| | 502 | if(p->GSactive->Font.enc != nil){ |
| | 503 | len = arraylen(p->GSactive->Font.enc); |
| | 504 | for(i = 0; i < len; i += 1){ |
| | 505 | o = arrayget(p->GSactive->Font.enc, i); |
| | 506 | if(o->type == Onum) |
| | 507 | d = o->num.i; |
| | 508 | else if(d == c){ |
| | 509 | if(strcmp(o->name, "endash") == 0) |
| | 510 | return bufput(&p->buf, (uchar*)"-", 1) == 1; |
| | 511 | if(strcmp(o->name, "fi") == 0) |
| | 512 | return bufput(&p->buf, (uchar*)"fi", 2) == 2; |
| | 513 | if(strcmp(o->name, "ff") == 0) |
| | 514 | return bufput(&p->buf, (uchar*)"ff", 2) == 2; |
| | 515 | if(strcmp(o->name, "ffi") == 0) |
| | 516 | return bufput(&p->buf, (uchar*)"ffi", 3) == 3; |
| | 517 | if(strcmp(o->name, "bullet") == 0) |
| | 518 | return bufput(&p->buf, (uchar*)"•", strlen("•")) == 3; |
| | 519 | if(strcmp(o->name, "quotedblleft") == 0) |
| | 520 | return bufput(&p->buf, (uchar*)"\"", 1) == 1; |
| | 521 | if(strcmp(o->name, "quotedblright") == 0) |
| | 522 | return bufput(&p->buf, (uchar*)"\"", 1) == 1; |
| | 523 | if(strcmp(o->name, "quoteleft") == 0) |
| | 524 | return bufput(&p->buf, (uchar*)"'", 1) == 1; |
| | 525 | if(strcmp(o->name, "quoteright") == 0) |
| | 526 | return bufput(&p->buf, (uchar*)"'", 1) == 1; |
| | 527 | fprint(2, "TODO: recognize glyph name '%s'\n", o->name); |
| | 528 | return 1; |
| | 529 | } else |
| | 530 | d += 1; |
| | 531 | } |
| | 532 | } |
| | 533 | return bufput(&p->buf, (uchar*)&c, 1) == 1; |
| | 534 | } |
| | 535 | |
| | 536 | /* Renders one character / glyph and updates the text state */ |
| | 537 | static int |
| | 538 | tchar(Page *p, ulong c) |
| | 539 | { |
| | 540 | double Trm[6] = {p->GSactive->Font.size, 0, 0, p->GSactive->Font.size, 0, 0}; |
| | 541 | double tx; |
| | 542 | int i; |
| | 543 | matmult(Trm, p->TS.Tm, Trm); |
| | 544 | matmult(Trm, p->GSactive->CTM, Trm); |
| | 545 | tx = p->GSactive->Font.size / 1000; |
| | 546 | if(c >= p->GSactive->Font.first && c <= p->GSactive->Font.last) |
| | 547 | tx = tx * (double)p->GSactive->Font.widths[c - p->GSactive->Font.first]; |
| | 548 | else |
| | 549 | tx = tx * (double)p->GSactive->Font.defwidth; |
| | 550 | // Check if whitespace is needed |
| | 551 | if(p->buf.sz > 1){ |
| | 552 | if(p->TS.y != Trm[5]){ |
| | 553 | for(i = 0; i < (int)((p->TS.y - Trm[5]) / p->GSactive->Font.size); i += 1) |
| | 554 | if(bufput(&p->buf, (uchar*)"\n", 1) != 1) |
| | 555 | return 0; |
| | 556 | } |
| | 557 | if(Trm[4] - p->TS.x > 2.5){ |
| | 558 | if(bufput(&p->buf, (uchar*)" ", 1) != 1) |
| | 559 | return 0; |
| | 560 | } |
| | 561 | } |
| | 562 | if(!writepatched(p, c) || !tmove(p, tx, 0, 0)) |
| | 563 | return 0; |
| | 564 | p->TS.x = Trm[4] + tx; |
| | 565 | p->TS.y = Trm[5]; |
| | 566 | return 1; |
| | 567 | } |
| | 568 | |
| | 569 | static int |
| | 570 | tstr(Page *p, char *str, ulong len) |
| | 571 | { |
| | 572 | ulong i; |
| | 573 | for(i = 0; i < len; i += 1) |
| | 574 | if(!tchar(p, str[i])) |
| | 575 | return 0; |
| | 576 | return 1; |
| 374 | } | 577 | } |
| 375 | | 578 | |
| 376 | static int | 579 | static int |
| 377 | thshow(Op *op, Page *p) | 580 | thshow(Op *op, Page *p) |
| 378 | { | 581 | { |
| | 582 | if(op->flags != 0){ |
| | 583 | fprint(2, "TODO: thshow != Tj\n"); |
| | 584 | return 0; |
| | 585 | } |
| 379 | Object *o = arrayget(p->stack, 0); | 586 | Object *o = arrayget(p->stack, 0); |
| 380 | if(bufput(&p->buf, (uchar*)o->str, o->len) == -1) | 587 | if(!tstr(p, o->str, o->len)) |
| 381 | sysfatal("OOM"); | 588 | return 0; |
| 382 | USED(op); | 589 | return 1; |
| 383 | return 0; | | |
| 384 | } | 590 | } |
| 385 | | 591 | |
| 386 | static int | 592 | static int |
| 387 | thshowarr(Op *op, Page *p) | 593 | thshowarr(Op *op, Page *p) |
| 388 | { | 594 | { |
| 389 | Object *arr = arrayget(p->stack, 0); | 595 | Object *o, *arr = arrayget(p->stack, 0); |
| 390 | Object *o; | | |
| 391 | int i; | 596 | int i; |
| 392 | for(i = 0; i < arraylen(arr); i += 1){ | 597 | for(i = 0; i < arraylen(arr); i += 1){ |
| 393 | o = arrayget(arr, i); | 598 | o = arrayget(arr, i); |
| 394 | if(o->type == Ostr){ | 599 | if(o->type == Ostr){ |
| 395 | if(bufput(&p->buf, (uchar*)o->str, o->len) == -1) | 600 | if(!tstr(p, o->str, o->len)) |
| 396 | sysfatal("OOM"); | 601 | return 0; |
| 397 | } | 602 | } |
| 398 | else if(o->num.d < -150){ | 603 | else{ |
| 399 | if(bufput(&p->buf, (uchar*)" ", 1) == -1) | 604 | double shift = 0 - (p->GSactive->Font.size * o->num.d / 1000); |
| 400 | sysfatal("OOM"); | 605 | if(!tmove(p, shift, 0, 0)) |
| | 606 | return 0; |
| 401 | } | 607 | } |
| 402 | } | 608 | } |
| 403 | USED(op); | 609 | return flagless(op); |
| 404 | return 0; | | |
| 405 | } | 610 | } |
| 406 | | 611 | |
| 407 | static int | 612 | static int |
| @@ -746,7 +951,7 @@ static int · 338 unmodified lines |
| 746 | opignore(Op *op, Page *p) | 951 | opignore(Op *op, Page *p) |
| 747 | { | 952 | { |
| 748 | USED(op, p); | 953 | USED(op, p); |
| 749 | return 1; | 954 | return 0; |
| 750 | } | 955 | } |
| 751 | | 956 | |
| 752 | static Op ops[] = { | 957 | static Op ops[] = { |
| @@ -833,8 +1038,8 @@ static Op ops[] = { · 80 unmodified lines |
| 833 | /* 9.4.2 Text position operators */ | 1038 | /* 9.4.2 Text position operators */ |
| 834 | {"Td", tpmove, 2,}, /* move, next line */ | 1039 | {"Td", tpmove, 2,}, /* move, next line */ |
| 835 | {"TD", tpmove, 2, Leading,}, /* move, next line, leading */ | 1040 | {"TD", tpmove, 2, Leading,}, /* move, next line, leading */ |
| 836 | {"Tm", tpmatrix, 6,}, /* (line) matrix */ | 1041 | {"Tm", tpmatrix, 6,}, /* set Tm and Tlm */ |
| 837 | {"T*", tpmove0, 0, Leading,}, /* move, next line, leading */ | 1042 | {"T*", tpmove0, 0,}, /* move, next line, leading */ |
| 838 | | 1043 | |
| 839 | /* 9.4.3 Text showing operators */ | 1044 | /* 9.4.3 Text showing operators */ |
| 840 | {"Tj", thshow, 1,}, /* show string */ | 1045 | {"Tj", thshow, 1,}, /* show string */ |
| @@ -902,56 +1107,92 @@ static Op ops[] = { · 61 unmodified lines |
| 902 | {nil, nil, 0,}, | 1107 | {nil, nil, 0,}, |
| 903 | }; | 1108 | }; |
| 904 | | 1109 | |
| 905 | // If an op is found at the current position in the stream, the associated Op is | | |
| 906 | // returned and the stream is advanced. Otherwise, nil is returned and the stream | | |
| 907 | // is left unchanged. | | |
| 908 | Op * | 1110 | Op * |
| 909 | opfind(Stream *s) | 1111 | opfind(char *name) |
| 910 | { | 1112 | { |
| 911 | int i; | 1113 | int i = 0; |
| 912 | uint len; | | |
| 913 | Op *op; | 1114 | Op *op; |
| 914 | char *b = (char*)s->buf.b + s->buf.off; | | |
| 915 | i = 0; | | |
| 916 | while(ops[i].s != nil){ | 1115 | while(ops[i].s != nil){ |
| 917 | op = &ops[i]; | 1116 | op = &ops[i]; |
| 918 | len = strlen(op->s); | 1117 | if(strcmp(op->s, name) == 0) |
| 919 | if(strncmp(op->s, b, len) == 0 && (isws(b[len]) || isdelim(b[len]))){ | | |
| 920 | s->buf.off += len; | | |
| 921 | return op; | 1118 | return op; |
| 922 | } | | |
| 923 | i += 1; | 1119 | i += 1; |
| 924 | } | 1120 | } |
| 925 | return nil; | 1121 | return nil; |
| 926 | } | 1122 | } |
| 927 | | 1123 | |
| 928 | void | 1124 | void |
| 929 | pageinit(Page *page) | 1125 | pageinit(Page *page, Object *o) |
| 930 | { | 1126 | { |
| 931 | bufinit(&page->buf, 0, 0); | 1127 | bufinit(&page->buf, 0, 0); |
| 932 | // Stack is per-content-stream, so we don't create it here | 1128 | // Stack is per-content-stream, so we don't create it here |
| 933 | page->stack = nil; | 1129 | page->stack = nil; |
| | 1130 | page->obj = o; |
| | 1131 | page->TS.inobj = 0; |
| | 1132 | page->TS.x = 0; |
| | 1133 | page->TS.y = 0; |
| 934 | } | 1134 | } |
| 935 | | 1135 | |
| 936 | void | 1136 | void |
| 937 | pagefree(Page *p) | 1137 | gsinit(Page *p, GS *gs) |
| | 1138 | { |
| | 1139 | USED(p); |
| | 1140 | /* todo: actually initialize the full state */ |
| | 1141 | /* CTM maps user coords to device coords. |
| | 1142 | TODO: use mediabox and screen info to init CTM |
| | 1143 | */ |
| | 1144 | matidentity(gs->CTM); |
| | 1145 | gs->LW = 1; |
| | 1146 | gs->LC = 0; |
| | 1147 | gs->LJ = 0; |
| | 1148 | gs->ML = 10; |
| | 1149 | gs->SCS = gs->NSCS = DeviceGray; |
| | 1150 | // Alpha is lowest byte; this is (0, 0, 0, 255) == black |
| | 1151 | gs->SC = gs->NSC = 255; |
| | 1152 | gs->Font.font = nil; |
| | 1153 | gs->Font.enc = nil; |
| | 1154 | gs->Font.widths = nil; |
| | 1155 | } |
| | 1156 | |
| | 1157 | void |
| | 1158 | gsfree(GS gs) |
| 938 | { | 1159 | { |
| 939 | buffree(&p->buf); | 1160 | free(gs.Font.widths); |
| 940 | pdfobjfree(p->stack); | 1161 | pdfobjfree(gs.Font.font); |
| | 1162 | gs.Font.font = nil; |
| | 1163 | gs.Font.enc = nil; |
| | 1164 | gs.Font.widths = nil; |
| 941 | } | 1165 | } |
| 942 | | 1166 | |
| 943 | static void | 1167 | void |
| 944 | stackreset(Object *stack) | 1168 | pagegsclean(Page *p) |
| 945 | { | 1169 | { |
| 946 | int i; | 1170 | int i; |
| 947 | for(i = 0; i < stack->array.ne; i += 1) | 1171 | p->GSactive = nil; |
| 948 | pdfobjfree(stack->array.e[i]); | 1172 | for(i = 0; i < p->nGS; i += 1) |
| 949 | stack->array.ne = 0; | 1173 | gsfree(p->GS[i]); |
| 950 | free(stack->array.e); | 1174 | free(p->GS); |
| 951 | stack->array.e = nil; | 1175 | p->GS = nil; |
| | 1176 | p->nGS = 0; |
| 952 | } | 1177 | } |
| 953 | | 1178 | |
| 954 | static void | 1179 | static int |
| | 1180 | stackreset(Page *p) |
| | 1181 | { |
| | 1182 | pdfobjfree(p->stack); |
| | 1183 | p->stack = arraynew(p->obj->pdf); |
| | 1184 | return p->stack != nil; |
| | 1185 | } |
| | 1186 | |
| | 1187 | void |
| | 1188 | pagefree(Page *p) |
| | 1189 | { |
| | 1190 | buffree(&p->buf); |
| | 1191 | pdfobjfree(p->stack); |
| | 1192 | pagegsclean(p); |
| | 1193 | } |
| | 1194 | |
| | 1195 | static int |
| 955 | pagerendercontent(Page *p, Object *content) | 1196 | pagerendercontent(Page *p, Object *content) |
| 956 | { | 1197 | { |
| 957 | Stream *s; | 1198 | Stream *s; |
| @@ -964,43 +1205,63 @@ pagerendercontent(Page *p, Object *content) · 6 unmodified lines |
| 964 | } | 1205 | } |
| 965 | p->stack = arraynew(content->pdf); | 1206 | p->stack = arraynew(content->pdf); |
| 966 | if(p->stack == nil) | 1207 | if(p->stack == nil) |
| 967 | return; | 1208 | return 0; |
| 968 | while(s->buf.off != s->buf.sz){ | 1209 | while(s->buf.off != s->buf.sz){ |
| 969 | while(isws(s->buf.b[s->buf.off]) && s->buf.off != s->buf.sz) | 1210 | while(isws(s->buf.b[s->buf.off]) && s->buf.off != s->buf.sz) |
| 970 | s->buf.off += 1; | 1211 | s->buf.off += 1; |
| 971 | if(s->buf.off == s->buf.sz) | 1212 | if(s->buf.off == s->buf.sz) |
| 972 | break; | 1213 | break; |
| 973 | op = opfind(s); | 1214 | o = pdfobj(content->pdf, s); |
| 974 | if(op != nil){ | 1215 | if(o == nil) |
| 975 | op->f(op, p); | 1216 | return 0; |
| 976 | stackreset(p->stack); | 1217 | if(o->type == Oop){ |
| 977 | } else{ | 1218 | op = opfind(o->str); |
| 978 | o = pdfobj(content->pdf, s); | 1219 | if(op == nil){ |
| 979 | if(o == nil){ | 1220 | fprint(2, "Unknown op: %s\n", o->str); |
| 980 | fprint(2, "failed to read operand: %r\n"); | 1221 | pdfobjfree(o); |
| 981 | break; | 1222 | return 0; |
| | 1223 | } |
| | 1224 | pdfobjfree(o); |
| | 1225 | if(!op->f(op, p)){ |
| | 1226 | fprint(2, "'%s' failed!\n", op->s); |
| | 1227 | return 0; |
| 982 | } | 1228 | } |
| | 1229 | if(!stackreset(p)) |
| | 1230 | return 0; |
| | 1231 | } else{ |
| 983 | if(!arrayadd(p->stack, o)){ | 1232 | if(!arrayadd(p->stack, o)){ |
| 984 | fprint(2, "Failed to push operand to stack: %r\n"); | 1233 | fprint(2, "Failed to push operand to stack: %r\n"); |
| 985 | break; | 1234 | return 0; |
| 986 | } | 1235 | } |
| 987 | } | 1236 | } |
| 988 | } | 1237 | } |
| 989 | if(bufput(&p->buf, (uchar*)"\n", 1) == -1) | 1238 | if(bufput(&p->buf, (uchar*)"\n\0", 2) != 2) |
| 990 | sysfatal("OOM"); | 1239 | return 0; |
| 991 | Sclose(s); | 1240 | Sclose(s); |
| | 1241 | return 1; |
| 992 | } | 1242 | } |
| 993 | | 1243 | |
| 994 | int | 1244 | int |
| 995 | pagerender(Page *p, Object *o) | 1245 | pagerender(Page *p) |
| 996 | { | 1246 | { |
| 997 | Object *content; | 1247 | Object *content; |
| 998 | int i; | 1248 | int i; |
| 999 | content = dictget(o, "Contents"); | 1249 | p->nGS = 1; |
| 1000 | if(content->type == Oarray) | 1250 | p->GS = malloc(sizeof(GS)); |
| | 1251 | if(p->GS == nil){ |
| | 1252 | werrstr("Out of memory"); |
| | 1253 | return 0; |
| | 1254 | } |
| | 1255 | gsinit(p, p->GS); |
| | 1256 | content = dictget(p->obj, "Contents"); |
| | 1257 | if(content->type == Oarray){ |
| 1001 | for(i = 0; i < arraylen(content); i += 1) | 1258 | for(i = 0; i < arraylen(content); i += 1) |
| 1002 | pagerendercontent(p, arrayget(content, i)); | 1259 | if(!pagerendercontent(p, arrayget(content, i))) |
| | 1260 | return 0; |
| | 1261 | } |
| 1003 | else if(content->type != Onull) | 1262 | else if(content->type != Onull) |
| 1004 | pagerendercontent(p, content); | 1263 | if(!pagerendercontent(p, content)) |
| | 1264 | return 0; |
| | 1265 | pagegsclean(p); |
| 1005 | return 1; | 1266 | return 1; |
| 1006 | } | 1267 | } |