1/*---------------------------------------------------------------------------* 2 | PDFlib - A library for generating PDF on the fly | 3 +---------------------------------------------------------------------------+ 4 | Copyright (c) 1997-2004 Thomas Merz and PDFlib GmbH. All rights reserved. | 5 +---------------------------------------------------------------------------+ 6 | | 7 | This software is subject to the PDFlib license. It is NOT in the | 8 | public domain. Extended versions and commercial licenses are | 9 | available, please check http://www.pdflib.com. | 10 | | 11 *---------------------------------------------------------------------------*/ 12 13/* $Id: p_gstate.c 14574 2005-10-29 16:27:43Z bonefish $ 14 * 15 * PDFlib routines dealing with the graphics states 16 * 17 */ 18 19#include "p_intern.h" 20 21/* ---------------------- matrix functions ----------------------------- */ 22 23void 24pdf_concat_raw(PDF *p, pdc_matrix *m) 25{ 26 if (pdc_is_identity_matrix(m)) 27 return; 28 29 pdf_end_text(p); 30 31 pdc_printf(p->out, "%f %f %f %f %f %f cm\n", 32 m->a, m->b, m->c, m->d, m->e, m->f); 33 34 pdc_multiply_matrix(m, &p->gstate[p->sl].ctm); 35} 36 37void 38pdf_concat_raw_ob(PDF *p, pdc_matrix *m, pdc_bool blind) 39{ 40 if (!blind) 41 pdf_concat_raw(p, m); 42 else 43 pdc_multiply_matrix(m, &p->gstate[p->sl].ctm); 44} 45 46void 47pdf_set_topdownsystem(PDF *p, float height) 48{ 49 if (p->ydirection < (float) 0.0) 50 { 51 pdc_matrix m; 52 pdc_translation_matrix(0, height, &m); 53 pdf_concat_raw(p, &m); 54 pdc_scale_matrix(1, -1, &m); 55 pdf_concat_raw(p, &m); 56 pdf_set_horiz_scaling(p, 100); 57 } 58} 59 60/* -------------------- Special graphics state ---------------------------- */ 61 62void 63pdf_init_gstate(PDF *p) 64{ 65 pdf_gstate *gs = &p->gstate[p->sl]; 66 67 gs->ctm.a = (float) 1; 68 gs->ctm.b = (float) 0; 69 gs->ctm.c = (float) 0; 70 gs->ctm.d = (float) 1; 71 gs->ctm.e = (float) 0.0; 72 gs->ctm.f = (float) 0.0; 73 74 gs->x = (float) 0.0; 75 gs->y = (float) 0.0; 76 77 p->fillrule = pdf_fill_winding; 78 79 gs->lwidth = (float) 1; 80 gs->lcap = 0; 81 gs->ljoin = 0; 82 gs->miter = (float) 10; 83 gs->flatness = (float) -1; /* -1 means "has not been set" */ 84 gs->dashed = pdc_false; 85} 86 87void 88pdf__save(PDF *p) 89{ 90 if (p->sl == PDF_MAX_SAVE_LEVEL - 1) 91 pdc_error(p->pdc, PDF_E_GSTATE_SAVELEVEL, 92 pdc_errprintf(p->pdc, "%d", PDF_MAX_SAVE_LEVEL - 1), 0, 0, 0); 93 94 pdf_end_text(p); 95 96 pdc_puts(p->out, "q\n"); 97 98 /* propagate states to next level */ 99 p->sl++; 100 memcpy(&p->gstate[p->sl], &p->gstate[p->sl - 1], sizeof(pdf_gstate)); 101 memcpy(&p->tstate[p->sl], &p->tstate[p->sl - 1], sizeof(pdf_tstate)); 102 memcpy(&p->cstate[p->sl], &p->cstate[p->sl - 1], sizeof(pdf_cstate)); 103} 104 105 106void 107pdf__restore(PDF *p) 108{ 109 if (p->sl == 0) 110 pdc_error(p->pdc, PDF_E_GSTATE_RESTORE, 0, 0, 0, 0); 111 112 pdf_end_text(p); 113 114 pdc_puts(p->out, "Q\n"); 115 116 p->sl--; 117} 118 119PDFLIB_API void PDFLIB_CALL 120PDF_save(PDF *p) 121{ 122 static const char fn[] = "PDF_save"; 123 124 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p])\n", (void *) p)) 125 return; 126 127 pdf__save(p); 128} 129 130PDFLIB_API void PDFLIB_CALL 131PDF_restore(PDF *p) 132{ 133 static const char fn[] = "PDF_restore"; 134 135 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p])\n", (void *) p)) 136 return; 137 138 pdf__restore(p); 139} 140 141PDFLIB_API void PDFLIB_CALL 142PDF_translate(PDF *p, float tx, float ty) 143{ 144 static const char fn[] = "PDF_translate"; 145 pdc_matrix m; 146 147 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n", 148 (void *) p, tx, ty)) 149 return; 150 151 if (tx == (float) 0 && ty == (float) 0) 152 return; 153 154 pdc_translation_matrix(tx, ty, &m); 155 156 pdf_concat_raw(p, &m); 157} 158 159PDFLIB_API void PDFLIB_CALL 160PDF_scale(PDF *p, float sx, float sy) 161{ 162 static const char fn[] = "PDF_scale"; 163 pdc_matrix m; 164 165 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n", 166 (void *) p, sx, sy)) 167 return; 168 169 if (sx == (float) 0) 170 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, "sx", "0", 0, 0); 171 172 if (sy == (float) 0) 173 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, "sy", "0", 0, 0); 174 175 if (sx == (float) 1 && sy == (float) 1) 176 return; 177 178 pdc_scale_matrix(sx, sy, &m); 179 180 pdf_concat_raw(p, &m); 181} 182 183PDFLIB_API void PDFLIB_CALL 184PDF_rotate(PDF *p, float phi) 185{ 186 static const char fn[] = "PDF_rotate"; 187 pdc_matrix m; 188 189 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n", 190 (void *) p, phi)) 191 return; 192 193 if (phi == (float) 0) 194 return; 195 196 pdc_rotation_matrix(p->ydirection * phi, &m); 197 198 pdf_concat_raw(p, &m); 199} 200 201PDFLIB_API void PDFLIB_CALL 202PDF_skew(PDF *p, float alpha, float beta) 203{ 204 static const char fn[] = "PDF_skew"; 205 pdc_matrix m; 206 207 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n", 208 (void *) p, alpha, beta)) 209 return; 210 211 if (alpha == (float) 0 && beta == (float) 0) 212 return; 213 214 if (alpha > (float) 360 || alpha < (float) -360 || 215 alpha == (float) -90 || alpha == (float) -270 || 216 alpha == (float) 90 || alpha == (float) 270) { 217 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 218 "alpha", pdc_errprintf(p->pdc, "%f", alpha), 0, 0); 219 } 220 221 if (beta > (float) 360 || beta < (float) -360 || 222 beta == (float) -90 || beta == (float) -270 || 223 beta == (float) 90 || beta == (float) 270) { 224 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 225 "beta", pdc_errprintf(p->pdc, "%f", beta), 0, 0); 226 } 227 228 pdc_skew_matrix(p->ydirection * alpha, p->ydirection * beta, &m); 229 230 pdf_concat_raw(p, &m); 231} 232 233PDFLIB_API void PDFLIB_CALL 234PDF_concat(PDF *p, float a, float b, float c, float d, float e, float f) 235{ 236 static const char fn[] = "PDF_concat"; 237 pdc_matrix m; 238 float det = a * d - b * c; 239 240 if (!pdf_enter_api(p, fn, pdf_state_content, 241 "(p[%p], %g, %g, %g, %g, %g, %g)\n", (void *) p, a, b, c, d, e, f)) 242 { 243 return; 244 } 245 246 if (fabs(det) < (float) PDF_SMALLREAL) 247 pdc_error(p->pdc, PDC_E_ILLARG_MATRIX, 248 pdc_errprintf(p->pdc, "%f %f %f %f %f %f", a, b, c, d, e, f), 249 0, 0, 0); 250 251 m.a = (float) a; 252 m.b = (float) b; 253 m.c = (float) c; 254 m.d = (float) d; 255 m.e = (float) e; 256 m.f = (float) f; 257 258 pdf_concat_raw(p, &m); 259} 260 261void 262pdf__setmatrix(PDF *p, pdc_matrix *n) 263{ 264 pdc_matrix m; 265 float det = n->a * n->d - n->b * n->c; 266 267 if (fabs(det) < (float) PDF_SMALLREAL) 268 pdc_error(p->pdc, PDC_E_ILLARG_MATRIX, 269 pdc_errprintf(p->pdc, "%f %f %f %f %f %f", 270 n->a, n->b, n->c, n->d, n->e, n->f), 271 0, 0, 0); 272 273 pdc_invert_matrix(p->pdc, &m, &p->gstate[p->sl].ctm); 274 pdf_concat_raw(p, &m); 275 pdf_concat_raw(p, n); 276} 277 278PDFLIB_API void PDFLIB_CALL 279PDF_setmatrix(PDF *p, float a, float b, float c, float d, float e, float f) 280{ 281 static const char fn[] = "PDF_setmatrix"; 282 pdc_matrix m; 283 float det = a * d - b * c; 284 285 if (!pdf_enter_api(p, fn, pdf_state_content, 286 "(p[%p], %g, %g, %g, %g, %g, %g)\n", (void *) p, a, b, c, d, e, f)) 287 { 288 return; 289 } 290 291 if (fabs(det) < (float) PDF_SMALLREAL) 292 pdc_error(p->pdc, PDC_E_ILLARG_MATRIX, 293 pdc_errprintf(p->pdc, "%f %f %f %f %f %f", a, b, c, d, e, f), 294 0, 0, 0); 295 296 pdc_invert_matrix(p->pdc, &m, &p->gstate[p->sl].ctm); 297 pdf_concat_raw(p, &m); 298 299 m.a = (float) a; 300 m.b = (float) b; 301 m.c = (float) c; 302 m.d = (float) d; 303 m.e = (float) e; 304 m.f = (float) f; 305 306 pdf_concat_raw(p, &m); 307} 308 309/* -------------------- General graphics state ---------------------------- */ 310 311/* definitions of dash options */ 312static const pdc_defopt pdf_dashoptions[] = 313{ 314 {"dasharray", pdc_floatlist, 0, 2, MAX_DASH_LENGTH, 315 PDC_FLOAT_PREC, PDC_FLOAT_MAX, NULL}, 316 317 {"dashphase", pdc_floatlist, 0, 1, 1, 0.0, PDC_FLOAT_MAX, NULL}, 318 319 PDC_OPT_TERMINATE 320}; 321 322static void 323pdf__setdashpattern(PDF *p, float *darray, int length, float phase) 324{ 325 pdf_gstate *gs = &p->gstate[p->sl]; 326 327 /* length == 0 or 1 means solid line */ 328 if (length < 2) 329 { 330 if (gs->dashed || PDF_FORCE_OUTPUT()) 331 { 332 pdc_puts(p->out, "[] 0 d\n"); 333 gs->dashed = pdc_false; 334 } 335 } 336 else 337 { 338 int i; 339 340 pdc_puts(p->out, "["); 341 for (i = 0; i < length; i++) 342 { 343 pdc_printf(p->out, "%f ", darray[i]); 344 } 345 pdc_printf(p->out, "] %f d\n", phase); 346 gs->dashed = pdc_true; 347 } 348} 349 350void 351pdf__setdash(PDF *p, float b, float w) 352{ 353 float darray[2]; 354 int length = 2; 355 356 /* both zero means solid line */ 357 if (b == 0.0 && w == 0.0) 358 { 359 length = 0; 360 } 361 else 362 { 363 darray[0] = b; 364 darray[1] = w; 365 } 366 pdf__setdashpattern(p, darray, length, (float) 0.0); 367} 368 369void 370pdf__setflat(PDF *p, float flat) 371{ 372 pdf_gstate *gs = &p->gstate[p->sl]; 373 374 if (flat != gs->flatness || PDF_FORCE_OUTPUT()) 375 { 376 gs->flatness = flat; 377 pdc_printf(p->out, "%f i\n", flat); 378 } 379} 380 381void 382pdf__setlinejoin(PDF *p, int join) 383{ 384 pdf_gstate *gs = &p->gstate[p->sl]; 385 386 if (join != gs->ljoin || PDF_FORCE_OUTPUT()) 387 { 388 gs->ljoin = join; 389 pdc_printf(p->out, "%d j\n", join); 390 } 391} 392 393void 394pdf__setlinecap(PDF *p, int cap) 395{ 396 pdf_gstate *gs = &p->gstate[p->sl]; 397 398 if (cap != gs->lcap || PDF_FORCE_OUTPUT()) 399 { 400 gs->lcap = cap; 401 pdc_printf(p->out, "%d J\n", cap); 402 } 403} 404 405void 406pdf__setlinewidth(PDF *p, float width) 407{ 408 pdf_gstate *gs = &p->gstate[p->sl]; 409 410 if (width != gs->lwidth || PDF_FORCE_OUTPUT()) 411 { 412 gs->lwidth = width; 413 pdc_printf(p->out, "%f w\n", width); 414 } 415} 416 417void 418pdf__setmiterlimit(PDF *p, float miter) 419{ 420 pdf_gstate *gs = &p->gstate[p->sl]; 421 422 if (miter != gs->miter || PDF_FORCE_OUTPUT()) 423 { 424 gs->miter = miter; 425 pdc_printf(p->out, "%f M\n", miter); 426 } 427} 428 429 430PDFLIB_API void PDFLIB_CALL 431PDF_setdash(PDF *p, float b, float w) 432{ 433 static const char fn[] = "PDF_setdash"; 434 435 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n", 436 (void *) p, b, w)) 437 return; 438 439 if (b < (float) 0.0) 440 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 441 "b", pdc_errprintf(p->pdc, "%f", b), 0, 0); 442 443 if (w < (float) 0.0) 444 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 445 "w", pdc_errprintf(p->pdc, "%f", w), 0, 0); 446 447 pdf__setdash(p, b, w); 448} 449 450PDFLIB_API void PDFLIB_CALL 451PDF_setpolydash(PDF *p, float *darray, int length) 452{ 453 static const char fn[] = "PDF_setpolydash"; 454 455 int i; 456 457 for (i = 0; i < length; i++) 458 pdc_trace(p->pdc, "*(darray+%d) = %g;\n", i, darray[i]); 459 460 if (!pdf_enter_api(p, fn, pdf_state_content, 461 "(p[%p], darray[%p], %d)\n", (void *) p, (void *) darray, length)) 462 { 463 return; 464 } 465 466 if (length > 1) 467 { 468 /* sanity checks */ 469 if (!darray) 470 pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "darray", 0, 0, 0); 471 472 if (length < 0 || length > MAX_DASH_LENGTH) 473 pdc_error(p->pdc, PDC_E_ILLARG_INT, 474 "length", pdc_errprintf(p->pdc, "%d", length), 0, 0); 475 476 for (i = 0; i < length; i++) { 477 if (darray[i] < (float) 0.0) 478 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 479 "darray[i]", pdc_errprintf(p->pdc, "%f", darray[i]), 0, 0); 480 } 481 } 482 483 pdf__setdashpattern(p, darray, length, (float) 0.0); 484} 485 486PDFLIB_API void PDFLIB_CALL 487PDF_setdashpattern(PDF *p, const char *optlist) 488{ 489 static const char fn[] = "PDF_setdashpattern"; 490 pdc_resopt *results; 491 float *darray, phase; 492 int length; 493 494 if (!pdf_enter_api(p, fn, pdf_state_content, 495 "(p[%p], \"%s\")\n", (void *) p, optlist)) 496 return; 497 498 /* parsing optlist */ 499 results = pdc_parse_optionlist(p->pdc, optlist, pdf_dashoptions, NULL, 500 pdc_true); 501 502 length = pdc_get_optvalues(p->pdc, "dasharray", results, 503 NULL, (void **) &darray); 504 505 phase = (float) 0.0; 506 (void) pdc_get_optvalues(p->pdc, "dashphase", results, &phase, NULL); 507 508 pdc_cleanup_optionlist(p->pdc, results); 509 510 pdf__setdashpattern(p, darray, length, phase); 511 512 if (darray) 513 pdc_free(p->pdc, darray); 514} 515 516PDFLIB_API void PDFLIB_CALL 517PDF_setflat(PDF *p, float flat) 518{ 519 static const char fn[] = "PDF_setflat"; 520 521 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n", 522 (void *) p, flat)) 523 return; 524 525 if (flat < 0.0 || flat > 100.0) 526 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 527 "flat", pdc_errprintf(p->pdc, "%f", flat), 0, 0); 528 529 pdf__setflat(p, flat); 530} 531 532 533PDFLIB_API void PDFLIB_CALL 534PDF_setlinejoin(PDF *p, int join) 535{ 536 static const char fn[] = "PDF_setlinejoin"; 537 const int LAST_JOIN = 2; 538 539 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %d)\n", 540 (void *) p, join)) 541 return; 542 543 if (join < 0 || join > LAST_JOIN) 544 pdc_error(p->pdc, PDC_E_ILLARG_INT, 545 "join", pdc_errprintf(p->pdc, "%d", join), 0, 0); 546 547 pdf__setlinejoin(p, join); 548} 549 550 551PDFLIB_API void PDFLIB_CALL 552PDF_setlinecap(PDF *p, int cap) 553{ 554 static const char fn[] = "PDF_setlinecap"; 555 const int LAST_CAP = 2; 556 557 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %d)\n", 558 (void *) p, cap)) 559 return; 560 561 if (cap < 0 || cap > LAST_CAP) 562 pdc_error(p->pdc, PDC_E_ILLARG_INT, 563 "cap", pdc_errprintf(p->pdc, "%d", cap), 0, 0); 564 565 pdf__setlinecap(p, cap); 566} 567 568PDFLIB_API void PDFLIB_CALL 569PDF_setmiterlimit(PDF *p, float miter) 570{ 571 static const char fn[] = "PDF_setmiterlimit"; 572 573 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n", 574 (void *) p, miter)) 575 return; 576 577 if (miter < (float) 1.0) 578 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 579 "miter", pdc_errprintf(p->pdc, "%f", miter), 0, 0); 580 581 pdf__setmiterlimit(p, miter); 582} 583 584PDFLIB_API void PDFLIB_CALL 585PDF_setlinewidth(PDF *p, float width) 586{ 587 static const char fn[] = "PDF_setlinewidth"; 588 589 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g)\n", 590 (void *) p, width)) 591 return; 592 593 if (width <= (float) 0.0) 594 pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, 595 "width", pdc_errprintf(p->pdc, "%f", width), 0, 0); 596 597 pdf__setlinewidth(p, width); 598} 599 600/* reset all gstate parameters except CTM 601*/ 602void 603pdf_reset_gstate(PDF *p) 604{ 605 pdf_gstate *gs = &p->gstate[p->sl]; 606 607 608 pdf__setcolor(p, "fillstroke", "gray", 609 (float) 0, (float) 0, (float) 0, (float) 0); 610 611 612 pdf__setlinewidth(p, 1); 613 pdf__setlinecap(p, 0); 614 pdf__setlinejoin(p, 0); 615 pdf__setmiterlimit(p, 10); 616 pdf__setdash(p, 0, 0); 617 618 if (gs->flatness != (float) -1) 619 pdf__setflat(p, (float) 1.0); 620} 621 622void 623pdf__initgraphics(PDF *p) 624{ 625 pdc_matrix inv_ctm; 626 627 pdf_reset_gstate(p); 628 629 pdc_invert_matrix(p->pdc, &inv_ctm, &p->gstate[p->sl].ctm); 630 pdf_concat_raw(p, &inv_ctm); 631 632 /* This also resets the CTM which guards against rounding artifacts. */ 633 pdf_init_gstate(p); 634} 635 636PDFLIB_API void PDFLIB_CALL 637PDF_initgraphics(PDF *p) 638{ 639 static const char fn[] = "PDF_initgraphics"; 640 641 if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p])\n", (void *) p)) 642 return; 643 644 pdf__initgraphics(p); 645} 646