1/*********************************************************************** 2 * * 3 * $Id: hpgsdevices.c 373 2007-01-24 13:43:04Z softadm $ 4 * * 5 * hpgs - HPGl Script, a hpgl/2 interpreter, which uses a Postscript * 6 * API for rendering a scene and thus renders to a variety of * 7 * devices and fileformats. * 8 * * 9 * (C) 2004-2006 ev-i Informationstechnologie GmbH http://www.ev-i.at * 10 * * 11 * Author: Wolfgang Glas * 12 * * 13 * hpgs is free software; you can redistribute it and/or * 14 * modify it under the terms of the GNU Lesser General Public * 15 * License as published by the Free Software Foundation; either * 16 * version 2.1 of the License, or (at your option) any later version. * 17 * * 18 * hpgs is distributed in the hope that it will be useful, * 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 21 * Lesser General Public License for more details. * 22 * * 23 * You should have received a copy of the GNU Lesser General Public * 24 * License along with this library; if not, write to the * 25 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * 26 * Boston, MA 02111-1307 USA * 27 * * 28 *********************************************************************** 29 * * 30 * The implementations of the simple plotsize and eps devices. * 31 * * 32 ***********************************************************************/ 33 34#include <hpgsdevices.h> 35#include <math.h> 36#include <string.h> 37#include <errno.h> 38#ifdef WIN32 39#include<windows.h> 40#else 41#include <dlfcn.h> 42#endif 43#if defined ( __MINGW32__ ) || defined ( _MSC_VER ) 44#include<malloc.h> 45#include<io.h> 46#else 47#include<alloca.h> 48#include<unistd.h> 49#endif 50 51//#define HPGS_EPS_DEBUG_ROP3 52 53/*! \defgroup device Basic vector graphics devices. 54 55 This group contains the definitions for the abstract vector graphics device 56 \c hpgs_device as well as the implementations of the two very basic 57 vector devices \c hpgs_plotsize_device ans \c hpgs_eps_device. 58 */ 59 60/*! 61 Returns the name of the device for use with RTTI, runtime type information. 62 */ 63const char *hpgs_device_rtti(hpgs_device *_this) 64{ 65 return _this->vtable->rtti; 66} 67 68/*! Sets the raster operation for the given device. 69 Raster operations and source/pattern transparency are described in 70 71 PCL 5 Comparison Guide, Edition 2, 6/2003, Hewlett Packard 72 (May be downloaded as bpl13206.pdf from http://www.hp.com) 73 74 The function returns -1, if an invalid raster operation is specified. 75 If the device is not capable of raster operations, the function succeeds 76 anyways. 77*/ 78int hpgs_setrop3 (hpgs_device *_this, int rop, 79 hpgs_bool src_transparency, hpgs_bool pattern_transparency) 80{ 81 if (!_this->vtable->setrop3) return 0; 82 83 return _this->vtable->setrop3(_this,rop, 84 src_transparency,pattern_transparency); 85} 86 87/*! Sets the patter ncolor applied using the raster operation specified 88 in \c hpgs_setrop3. 89*/ 90int hpgs_setpatcol (hpgs_device *_this, const hpgs_color *rgb) 91{ 92 if (!_this->vtable->setpatcol) return 0; 93 94 return _this->vtable->setpatcol(_this,rgb); 95} 96 97/*! Draw an image to the device. 98 The arguments \c ll, \c lr and \c ur are the 99 lower left, lower right and upper right corner of the drawn image 100 in world coordinates. 101 102 The function returns 0 on success. 103 -1 is returned upon failure. 104*/ 105int hpgs_drawimage(hpgs_device *_this, const hpgs_image *img, 106 const hpgs_point *ll, const hpgs_point *lr, 107 const hpgs_point *ur) 108{ 109 if (!_this->vtable->drawimage) return 0; 110 111 return _this->vtable->drawimage(_this,img,ll,lr,ur); 112} 113 114/*! Report a HPGL PS command to the device. If the function returns 2, 115 the interpretation of the file is interrupted immediately without error. 116 117 For devices with the \c HPGS_DEVICE_CAP_MULTISIZE capability, this function 118 may be called immediately after a showpage command. 119*/ 120int hpgs_setplotsize (hpgs_device *_this, const hpgs_bbox *bb) 121{ 122 if (!_this->vtable->setplotsize) 123 return 0; 124 125 return _this->vtable->setplotsize(_this,bb); 126} 127 128/*! Gets the plotsize of the given page number \c i or the overall bounding box, 129 if \c i is zero. 130 131 The function returns 0 on success. 132 133 If the function returns 1, the overall bounding box is returned, 134 because the plotsize of page \c i is not known. 135 136 -1 is returned, if the funtion is unimplemented for the given device. 137*/ 138int hpgs_getplotsize (hpgs_device *_this, int i, hpgs_bbox *bb) 139{ 140 if (!_this->vtable->getplotsize) 141 return -1; 142 143 return _this->vtable->getplotsize(_this,i,bb); 144} 145 146/*! Finishes the output of a page to the device. If the function returns 2, 147 the interpretation of the file is interrupted immediately without error. 148 This is the case for device, which are not capable of displaying multiple 149 pages. 150 151 The integer argument is the number of the page begin finished. 152 This argument is intended as a hint for devices, which write a file 153 for each page. 154 155 If this argument is less than or equal to 0, this is the only page 156 written to the device. In this case, devices which write a file for 157 each page may omit a page counter from the filename of the written 158 file. 159*/ 160int hpgs_showpage (hpgs_device *_this, int i) 161{ 162 if (!_this->vtable->showpage) 163 return 0; 164 165 return _this->vtable->showpage(_this,i); 166} 167 168/*! Finishes the output of a document to the device. 169 Implementations of device should discard all output, 170 which has been undertaken since the past showpage call. 171*/ 172int hpgs_device_finish (hpgs_device *_this) 173{ 174 if (!_this->vtable->finish) 175 return 0; 176 177 return _this->vtable->finish(_this); 178} 179 180 181/*! Destroys the given device and frees all allocated resources by this device. 182*/ 183void hpgs_device_destroy (hpgs_device *_this) 184{ 185 _this->vtable->destroy(_this); 186 free(_this); 187} 188 189static int eps_expand_media_sizes(hpgs_eps_device *eps) 190{ 191 int n = eps->media_sizes_alloc_size*2; 192 hpgs_ps_media_size *nnms = realloc(eps->media_sizes,sizeof(hpgs_bbox)*n); 193 194 if (!nnms) 195 return hpgs_set_error(hpgs_i18n("Out of memory expanding page size stack.")); 196 197 eps->media_sizes=nnms; 198 eps->media_sizes_alloc_size=n; 199 return 0; 200} 201 202#define HPGS_MEDIA_SIZE_HASH(w,h) (size_t)((419430343*((size_t)(w)))^((size_t)(h))) 203 204static int eps_get_papersize (hpgs_eps_device *eps, 205 char *name, size_t name_len, 206 double w, double h) 207{ 208 int paper_width = (int)ceil(w); 209 int paper_height = (int)ceil(h); 210 size_t hash = HPGS_MEDIA_SIZE_HASH(paper_width,paper_height); 211 int i0 = 0; 212 int i1 = eps->n_media_sizes; 213 214 // binary search for hash; 215 while (i1>i0) 216 { 217 int i = i0+(i1-i0)/2; 218 219 if (eps->media_sizes[i].hash < hash) 220 i0 = i+1; 221 else 222 i1 = i; 223 } 224 225 i1 = 0; 226 227 // search all media sizes with equal hash 228 while (i0 < eps->n_media_sizes && 229 eps->media_sizes[i0].hash == hash) 230 { 231 if (eps->media_sizes[i0].width == paper_width && 232 eps->media_sizes[i0].height == paper_height ) 233 { i1 = 1; break; } 234 235 ++i0; 236 } 237 238 if (i1) 239 { 240 ++eps->media_sizes[i0].usage; 241 } 242 else 243 { 244 // no page size found -> make a new one 245 if (eps->n_media_sizes>=eps->media_sizes_alloc_size && 246 eps_expand_media_sizes(eps)) 247 return -1; 248 249 if (i0 < eps->n_media_sizes) 250 memmove (eps->media_sizes+i0+1,eps->media_sizes+i0, 251 sizeof(hpgs_ps_media_size) * (eps->n_media_sizes-i0)); 252 253 eps->media_sizes[i0].width = paper_width; 254 eps->media_sizes[i0].height = paper_height; 255 eps->media_sizes[i0].name = 0; 256 eps->media_sizes[i0].usage = 1; 257 eps->media_sizes[i0].hash = hash; 258 } 259 260 // fill in media size. 261 if (eps->media_sizes[i0].name) 262 { 263 strcpy(name,eps->media_sizes[i0].name); 264 } 265 else 266 { 267#ifdef WIN32 268 _snprintf(name,name_len,"Custom%dx%d", 269 paper_width,paper_height); 270#else 271 snprintf(name,name_len,"Custom%dx%d", 272 paper_width,paper_height); 273#endif 274 } 275 return 0; 276} 277 278 279static int eps_startpage (hpgs_eps_device *eps) 280{ 281 if (eps->n_pages < 0) 282 { 283 // EPS page file start 284 eps->out = hpgs_new_file_ostream (eps->filename); 285 286 if (!eps->out) 287 return hpgs_set_error(hpgs_i18n("Error opening file %s: %s"), 288 eps->filename,strerror(errno)); 289 290 if (hpgs_ostream_printf (eps->out, 291 "%%!PS-Adobe-3.0 EPSF-3.0\n" 292 "%%%%Creator: hpgs-%s\n" 293 "%%%%Title: %s\n" 294 "%%%%BoundingBox: %d %d %d %d\n" 295 "%%%%HiResBoundingBox: %g %g %g %g\n" 296 "/hpgsdict 20 dict def\n" 297 "hpgsdict begin\n" 298 "/B{bind def} bind def\n" 299 "/r{setrgbcolor}B/w{setlinewidth}B/j{setlinejoin}B/J{setlinecap}B/M{setmiterlimit}B\n" 300 "/d{setdash}B/n{newpath}B/h{closepath}B/m{moveto}B/l{lineto}B/c{curveto}B\n" 301 "/s{stroke}B/f{fill}B/g{eofill}B/x{clip}B/y{eoclip}B/cs{gsave}B\n" 302 "/cr{currentrgbcolor currentlinewidth currentlinecap currentlinejoin currentmiterlimit currentdash grestore setdash setmiterlimit setlinejoin setlinecap setlinewidth setrgbcolor}B\n" 303 "end\n" 304 "%%EndComments\n" 305 "%%EndProlog\n" 306 "hpgsdict begin\n" 307 "gsave\n", 308 HPGS_VERSION, 309 eps->filename ? eps->filename : "(stdout)", 310 (int)floor(eps->page_bb.llx),(int)floor(eps->page_bb.lly), 311 (int)ceil(eps->page_bb.urx),(int)ceil(eps->page_bb.ury), 312 eps->page_bb.llx,eps->page_bb.lly, 313 eps->page_bb.urx,eps->page_bb.ury ) < 0) 314 { 315 hpgs_ostream_close(eps->out); 316 eps->out = 0; 317 return hpgs_set_error(hpgs_i18n("Error writing header of file %s: %s"), 318 eps->filename?eps->filename:"(stdout)", 319 strerror(errno)); 320 } 321 eps->page_setup = HPGS_TRUE; 322 } 323 else 324 { 325 hpgs_bbox bb; 326 char paper_name[32]; 327 double paper_width = eps->page_bb.urx; 328 double paper_height = eps->page_bb.ury; 329 330 hpgs_bool landscape = paper_width > paper_height; 331 332 if (eps_get_papersize(eps,paper_name,sizeof(paper_name), 333 landscape ? paper_height : paper_width, 334 landscape ? paper_width : paper_height)) 335 return -1; 336 337 if (landscape) 338 { 339 if (hpgs_ostream_printf (eps->out, 340 "%%%%Page: %d %d\n" 341 "%%%%PageMedia: %s\n" 342 "%%%%PageOrientation: Landscape\n" 343 "hpgsdict begin\n" 344 "gsave\n" 345 "%.8f %.8f translate\n" 346 "90 rotate\n", 347 eps->n_pages+1,eps->n_pages+1,paper_name, 348 paper_height,0.0 ) < 0) 349 return hpgs_set_error(hpgs_i18n("Error writing header of file %s: %s"), 350 eps->filename?eps->filename:"(stdout)", 351 strerror(errno)); 352 353 354 // caclulate the bounding box on the page. 355 bb.llx = 0.0; 356 bb.lly = 0.0; 357 bb.urx = paper_height; 358 bb.ury = paper_width; 359 } 360 else 361 { 362 if (hpgs_ostream_printf (eps->out, 363 "%%%%Page: %d %d\n" 364 "%%%%PageMedia: %s\n" 365 "%%%%PageOrientation: Portrait\n" 366 "gsave\n", 367 eps->n_pages+1,eps->n_pages+1,paper_name ) < 0) 368 return hpgs_set_error(hpgs_i18n("Error writing header of file %s: %s"), 369 eps->filename?eps->filename:"(stdout)", 370 strerror(errno)); 371 372 // calculate the bounding box on the page. 373 bb = eps->page_bb; 374 } 375 376 // expand document bounding box. 377 if (eps->n_pages) 378 hpgs_bbox_expand(&eps->doc_bb,&bb); 379 else 380 eps->doc_bb = bb; 381 382 ++eps->n_pages; 383 eps->page_setup = HPGS_TRUE; 384 } 385 386 return 0; 387} 388 389 390static int eps_moveto (hpgs_device *_this, const hpgs_point *p) 391{ 392 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 393 394 if (!eps->page_setup && eps_startpage(eps)) return -1; 395 396 if (hpgs_ostream_printf(eps->out,"%g %g m\n",p->x,p->y) < 0) 397 return hpgs_set_error("moveto: %s",strerror(errno)); 398 return 0; 399} 400 401static int eps_lineto (hpgs_device *_this, const hpgs_point *p) 402{ 403 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 404 405 if (!eps->page_setup && eps_startpage(eps)) return -1; 406 407 if (hpgs_ostream_printf(eps->out,"%g %g l\n",p->x,p->y) < 0) 408 return hpgs_set_error("lineto: %s",strerror(errno)); 409 return 0; 410} 411 412static int eps_curveto (hpgs_device *_this, const hpgs_point *p1, 413 const hpgs_point *p2, const hpgs_point *p3 ) 414{ 415 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 416 417 if (!eps->page_setup && eps_startpage(eps)) return -1; 418 419 if (hpgs_ostream_printf(eps->out,"%g %g %g %g %g %g c\n", 420 p1->x,p1->y,p2->x,p2->y,p3->x,p3->y) < 0) 421 return hpgs_set_error("curveto: %s",strerror(errno)); 422 return 0; 423} 424 425static int eps_newpath (hpgs_device *_this) 426{ 427 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 428 429 if (!eps->page_setup && eps_startpage(eps)) return -1; 430 431 if (hpgs_ostream_printf(eps->out,"n\n") < 0) 432 return hpgs_set_error("newpath: %s",strerror(errno)); 433 return 0; 434} 435 436static int eps_closepath (hpgs_device *_this) 437{ 438 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 439 440 if (!eps->page_setup && eps_startpage(eps)) return -1; 441 442 if (hpgs_ostream_printf(eps->out,"h\n") < 0) 443 return hpgs_set_error("closepath: %s",strerror(errno)); 444 return 0; 445} 446 447static int eps_stroke (hpgs_device *_this) 448{ 449 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 450 451 if (!eps->page_setup && eps_startpage(eps)) return -1; 452 453 if (hpgs_ostream_printf(eps->out,"s\n") < 0) 454 return hpgs_set_error("stroke: %s",strerror(errno)); 455 return 0; 456} 457 458static int eps_fill (hpgs_device *_this, hpgs_bool winding) 459{ 460 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 461 462 if (!eps->page_setup && eps_startpage(eps)) return -1; 463 464 if (hpgs_ostream_printf(eps->out,winding ? "f\n" : "g\n") < 0) 465 return hpgs_set_error("fill: %s",strerror(errno)); 466 return 0; 467} 468 469static int eps_clip (hpgs_device *_this, hpgs_bool winding) 470{ 471 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 472 473 if (!eps->page_setup && eps_startpage(eps)) return -1; 474 475 if (hpgs_ostream_printf(eps->out,winding ? "x\n" : "y\n") < 0) 476 return hpgs_set_error("clip: %s",strerror(errno)); 477 return 0; 478} 479 480static int eps_clipsave (hpgs_device *_this) 481{ 482 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 483 484 if (!eps->page_setup && eps_startpage(eps)) return -1; 485 486 if (hpgs_ostream_printf(eps->out,"cs\n") < 0) 487 return hpgs_set_error("clipsave: %s",strerror(errno)); 488 return 0; 489} 490 491static int eps_cliprestore (hpgs_device *_this) 492{ 493 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 494 495 if (!eps->page_setup && eps_startpage(eps)) return -1; 496 497 if (hpgs_ostream_printf(eps->out,"cr\n") < 0) 498 return hpgs_set_error("cliprestore: %s",strerror(errno)); 499 return 0; 500} 501 502static int eps_setrgbcolor (hpgs_device *_this, const hpgs_color *rgb) 503{ 504 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 505 506 if (!eps->page_setup && eps_startpage(eps)) return -1; 507 508 if (hpgs_ostream_printf(eps->out,"%g %g %g r\n",rgb->r,rgb->g,rgb->b) < 0) 509 return hpgs_set_error("setrgbcolor: %s",strerror(errno)); 510 511 eps->color = *rgb; 512 return 0; 513} 514 515static int eps_setdash (hpgs_device *_this, const float *segs, 516 unsigned n, double d) 517{ 518 unsigned i; 519 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 520 521 if (!eps->page_setup && eps_startpage(eps)) return -1; 522 523 if (hpgs_ostream_printf(eps->out,"[ ")<0) return 1; 524 525 for (i=0;i<n;++i) 526 if (hpgs_ostream_printf(eps->out,"%f ",(double)segs[i])<0) 527 return hpgs_set_error("setdash: %s",strerror(errno)); 528 529 if (hpgs_ostream_printf(eps->out,"] %g d\n",d)<0) 530 return hpgs_set_error("setdash: %s",strerror(errno)); 531 return 0; 532} 533 534static int eps_setlinewidth(hpgs_device *_this, double lw) 535{ 536 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 537 538 if (!eps->page_setup && eps_startpage(eps)) return -1; 539 540 if (hpgs_ostream_printf(eps->out,"%g w\n",lw) < 0) 541 return hpgs_set_error("setlinewidth: %s",strerror(errno)); 542 return 0; 543} 544 545static int eps_setlinecap (hpgs_device *_this, hpgs_line_cap lc) 546{ 547 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 548 549 if (!eps->page_setup && eps_startpage(eps)) return -1; 550 551 if (hpgs_ostream_printf(eps->out,"%d J\n",(int)lc) < 0) 552 return hpgs_set_error("setlinecap: %s",strerror(errno)); 553 return 0; 554} 555 556static int eps_setlinejoin (hpgs_device *_this, hpgs_line_join lj) 557{ 558 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 559 560 if (!eps->page_setup && eps_startpage(eps)) return -1; 561 562 if (hpgs_ostream_printf(eps->out,"%d j\n",(int)lj) < 0) 563 return hpgs_set_error("setlinejoin: %s",strerror(errno)); 564 return 0; 565} 566 567static int eps_setmiterlimit (hpgs_device *_this, double l) 568{ 569 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 570 571 if (!eps->page_setup && eps_startpage(eps)) return -1; 572 573 if (hpgs_ostream_printf(eps->out,"%g M\n",l) < 0) 574 return hpgs_set_error("setmiterlimit: %s",strerror(errno)); 575 return 0; 576} 577 578static int eps_setrop3 (hpgs_device *_this, int rop, 579 hpgs_bool src_transparency, hpgs_bool pattern_transparency) 580{ 581 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 582 hpgs_xrop3_func_t rop3; 583 584 if (!eps->rop3) return 0; 585 586#ifdef HPGS_EPS_DEBUG_ROP3 587 hpgs_log("setrop3: rop,src_trans,pat_trans=%d,%d,%d.\n", 588 rop,src_transparency,pattern_transparency); 589#endif 590 rop3 = hpgs_xrop3_func(rop,src_transparency,pattern_transparency); 591 592 if (!rop3) 593 return hpgs_set_error("setrop3: Invalid ROP3 %d specified",rop); 594 595 eps->rop3 = rop3; 596 return 0; 597} 598 599static int eps_setpatcol(hpgs_device *_this, const hpgs_color *rgb) 600{ 601 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 602 603 eps->pattern_color.r = (unsigned char)(rgb->r * 255.0); 604 eps->pattern_color.g = (unsigned char)(rgb->g * 255.0); 605 eps->pattern_color.b = (unsigned char)(rgb->b * 255.0); 606 607#ifdef HPGS_EPS_DEBUG_ROP3 608 hpgs_log("setpatcol: patcol=%g,%g,%g.\n",rgb->r,rgb->g,rgb->b); 609#endif 610 611 return 0; 612} 613 614static int eps_drawimage(hpgs_device *_this, const hpgs_image *img, 615 const hpgs_point *ll, const hpgs_point *lr, 616 const hpgs_point *ur) 617{ 618 static const char *hex_4_bit = "0123456789abcdef"; 619 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 620 int i,j; 621 hpgs_point ul; 622 int ret = -1; 623 624 if (!img) 625 return hpgs_set_error("drawimage: Null image specified."); 626 627 if (!eps->page_setup && eps_startpage(eps)) return -1; 628 629 ul.x = ll->x + (ur->x - lr->x); 630 ul.y = ll->y + (ur->y - lr->y); 631 632 hpgs_palette_color *data = (hpgs_palette_color *) 633 malloc(img->width*img->height*sizeof(hpgs_palette_color)); 634 635 int have_clip = 0; 636 637 // data aqusition. 638 if (eps->rop3) 639 { 640 hpgs_color rgb = eps->color; 641 642 switch (hpgs_image_rop3_clip(_this,data,img, 643 ll,lr,ur, 644 &eps->pattern_color,eps->rop3)) 645 { 646 case 0: 647 // invisible. 648 ret = 0; 649 goto cleanup; 650 case 1: 651 // clipped. 652 have_clip = 1; 653 case 2: 654 // totally visible. 655 break; 656 case 3: 657 // optimized to a single color. 658 eps->color = rgb; 659 if (hpgs_ostream_printf(eps->out,"%g %g %g r\n", 660 eps->color.r,eps->color.g,eps->color.b) >= 0) 661 ret = 0; 662 goto cleanup; 663 default: 664 goto cleanup; 665 } 666 } 667 else 668 { 669 hpgs_palette_color *d = data; 670 671 for (j=0; j<img->height;++j) 672 { 673 hpgs_paint_color c; 674 675 for (i=0; i<img->width;++i,++d) 676 { 677 hpgs_image_get_pixel(img,i,j,&c,(double *)0/*alpha*/); 678 d->r = c.r; 679 d->g = c.g; 680 d->b = c.b; 681 } 682 } 683 } 684 685 if (!have_clip) 686 hpgs_ostream_printf(eps->out, 687 "gsave\n"); 688 689 if (hpgs_ostream_printf(eps->out, 690 "[ %g %g %g %g %g %g ] concat\n" 691 "%d %d 8\n" 692 "[ %d 0 0 %d 0 0 ]\n" 693 "{ currentfile %d string readhexstring pop }\n" 694 "dup dup true 3 colorimage\n", 695 lr->x-ll->x,lr->y-ll->y,lr->x-ur->x,lr->y-ur->y,ul.x,ul.y, 696 img->width,img->height, 697 img->width,img->height,img->width) < 0) 698 { hpgs_set_error("drawimage: %s",strerror(errno)); goto cleanup; } 699 700 // write data 701 for (j=0; j<img->height; ++j) 702 { 703 hpgs_palette_color *scanline = data + j * img->width; 704 705 for (i=0; i<img->width; i++) 706 { 707 hpgs_ostream_putc(hex_4_bit[scanline[i].r>>4],eps->out); 708 hpgs_ostream_putc(hex_4_bit[scanline[i].r&0xf],eps->out); 709 } 710 for (i=0; i<img->width; i++) 711 { 712 hpgs_ostream_putc(hex_4_bit[scanline[i].g>>4],eps->out); 713 hpgs_ostream_putc(hex_4_bit[scanline[i].g&0xf],eps->out); 714 } 715 for (i=0; i<img->width; i++) 716 { 717 hpgs_ostream_putc(hex_4_bit[scanline[i].b>>4],eps->out); 718 hpgs_ostream_putc(hex_4_bit[scanline[i].b&0xf],eps->out); 719 } 720 } 721 722 if (hpgs_ostream_printf(eps->out,"\ngrestore\n")<0) 723 { hpgs_set_error("drawimage: %s",strerror(errno)); goto cleanup; } 724 725 ret = 0; 726 727 cleanup: 728 free(data); 729 730 return ret; 731} 732 733static int eps_showpage (hpgs_device *_this, int i) 734{ 735 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 736 737 if (!eps->out) return 0; 738 739 if (hpgs_ostream_printf(eps->out,"grestore\nshowpage\nend\n") < 0) 740 return hpgs_set_error("showpage: %s",strerror(errno)); 741 742 // write an eps file, if we are not in multipage mode. 743 if (eps->n_pages<0) 744 { 745 if (hpgs_ostream_printf(eps->out,"%%%%EOF\n")<0 || 746 hpgs_ostream_close(eps->out) < 0) 747 return hpgs_set_error("showpage: %s",strerror(errno)); 748 749 eps->out = 0; 750 751 if (i > 0 && eps->filename) 752 { 753 int l = strlen(eps->filename); 754 char *fn = hpgs_alloca(l+20); 755 char *dot = strrchr(eps->filename,'.'); 756 int pos = dot ? dot-eps->filename : l; 757 758#ifdef WIN32 759 _snprintf(fn,l+20,"%.*s%4.4d%s", 760 pos,eps->filename,i,eps->filename+pos); 761#else 762 snprintf(fn,l+20,"%.*s%4.4d%s", 763 pos,eps->filename,i,eps->filename+pos); 764#endif 765 l = rename(eps->filename,fn); 766 767 if (l<0) 768 return hpgs_set_error(hpgs_i18n("Error moving file %s to %.*s%4.4d%s.eps: %s"), 769 eps->filename, 770 pos,eps->filename,i,eps->filename+pos, 771 strerror(errno)); 772 } 773 } 774 775 eps->page_setup = HPGS_FALSE; 776 777 return 0; 778} 779 780static int eps_setplotsize (hpgs_device *_this, const hpgs_bbox *bb) 781{ 782 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 783 784 eps->page_bb = *bb; 785 786 return 0; 787} 788 789static int eps_capabilities (hpgs_device *_this) 790{ 791 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 792 793 int ret = 794 HPGS_DEVICE_CAP_VECTOR | 795 HPGS_DEVICE_CAP_MULTIPAGE | 796 HPGS_DEVICE_CAP_MULTISIZE | 797 HPGS_DEVICE_CAP_DRAWIMAGE; 798 799 if (eps->n_pages >= 0) 800 ret |= HPGS_DEVICE_CAP_PAGECOLLATION; 801 802 if (eps->rop3) 803 ret |= HPGS_DEVICE_CAP_ROP3; 804 805 return ret; 806} 807 808static int eps_finish (hpgs_device *_this) 809{ 810 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 811 812 if (!eps->out) return 0; 813 814 if (eps->n_pages < 0) 815 { 816 // discard trailing output in EPS mode. 817 if (hpgs_ostream_close(eps->out) < 0) 818 { 819 eps->out = 0; 820 return hpgs_set_error("finish: %s",strerror(errno)); 821 } 822 823 eps->out = 0; 824 825 if (eps->filename && unlink(eps->filename)<0) 826 return hpgs_set_error(hpgs_i18n("Error removing file %s: %s"), 827 eps->filename,strerror(errno)); 828 } 829 else 830 { 831 // now really write the postscript file 832 int i,i_media=0; 833 hpgs_istream *buffer; 834 835 hpgs_ostream *out = hpgs_new_file_ostream (eps->filename); 836 837 if (!out) 838 return hpgs_set_error(hpgs_i18n("Error opening file %s: %s"), 839 eps->filename,strerror(errno)); 840 841 if (hpgs_ostream_flush(eps->out) <0 || 842 (buffer =hpgs_ostream_getbuf(eps->out)) == 0) 843 return hpgs_set_error(hpgs_i18n("Error getting page stream data")); 844 845 hpgs_ostream_printf (out, 846 "%%!PS-Adobe-3.0\n" 847 "%%%%Creator: hpgs-%s\n" 848 "%%%%Title: %s\n" 849 "%%%%Pages: %d\n" 850 "%%%%BoundingBox: %d %d %d %d\n" 851 "%%%%HiResBoundingBox: %.3f %.3f %.3f %.3f\n" 852 "%%%%DocumentMedia:", 853 HPGS_VERSION, 854 eps->filename ? eps->filename : "(stdout)", 855 eps->n_pages, 856 (int)floor(eps->doc_bb.llx),(int)floor(eps->doc_bb.lly), 857 (int)ceil(eps->doc_bb.urx),(int)ceil(eps->doc_bb.ury), 858 eps->doc_bb.llx,eps->doc_bb.lly, 859 eps->doc_bb.urx,eps->doc_bb.ury); 860 861 // write paper sizes. 862 for (i=0;i<eps->n_media_sizes;++i) 863 if (eps->media_sizes[i].usage) 864 { 865 if (eps->media_sizes[i].name) 866 hpgs_ostream_printf (out,"%s %s %d %d 75 white()\n", 867 i_media ? "%%+" : "", 868 eps->media_sizes[i].name, 869 eps->media_sizes[i].width, 870 eps->media_sizes[i].height ); 871 else 872 hpgs_ostream_printf (out,"%s Custom%dx%d %d %d 75 white()\n", 873 i_media ? "%%+" : "", 874 eps->media_sizes[i].width, 875 eps->media_sizes[i].height, 876 eps->media_sizes[i].width, 877 eps->media_sizes[i].height ); 878 879 ++i_media; 880 } 881 882 hpgs_ostream_printf (out, 883 "%%%%EndComments\n" 884 "/hpgsdict 20 dict def\n" 885 "hpgsdict begin\n" 886 "/B{bind def} bind def\n" 887 "/r{setrgbcolor}B/w{setlinewidth}B/j{setlinejoin}B/J{setlinecap}B/M{setmiterlimit}B\n" 888 "/d{setdash}B/n{newpath}B/h{closepath}B/m{moveto}B/l{lineto}B/c{curveto}B\n" 889 "/s{stroke}B/f{fill}B/g{eofill}B/x{clip}B/y{eoclip}B/cs{gsave}B\n" 890 "/cr{currentrgbcolor currentlinewidth currentlinecap currentlinejoin currentmiterlimit currentdash grestore setdash setmiterlimit setlinejoin setlinecap setlinewidth setrgbcolor}B\n" 891 "end\n" 892 "%%%%EndProlog\n"); 893 894 hpgs_copy_streams (out,buffer); 895 hpgs_istream_close(buffer); 896 897 hpgs_ostream_printf (out,"%%%%EOF\n"); 898 899 if (hpgs_ostream_iserror(out)) 900 { 901 if (eps->filename) 902 hpgs_ostream_close(out); 903 return hpgs_set_error(hpgs_i18n("Error writing file %s: %s"), 904 eps->filename?eps->filename:"(stdout)", 905 strerror(errno)); 906 } 907 908 hpgs_ostream_close(out); 909 } 910 911 return 0; 912} 913 914static void eps_destroy (hpgs_device *_this) 915{ 916 hpgs_eps_device *eps = (hpgs_eps_device *)_this; 917 if (eps->out) 918 hpgs_ostream_close(eps->out); 919 920 if (eps->filename) 921 free(eps->filename); 922} 923 924static hpgs_device_vtable eps_vtable = 925 { 926 "hpgs_eps_device", 927 eps_moveto, 928 eps_lineto, 929 eps_curveto, 930 eps_newpath, 931 eps_closepath, 932 eps_stroke, 933 eps_fill, 934 eps_clip, 935 eps_clipsave, 936 eps_cliprestore, 937 eps_setrgbcolor, 938 eps_setdash, 939 eps_setlinewidth, 940 eps_setlinecap, 941 eps_setlinejoin, 942 eps_setmiterlimit, 943 eps_setrop3, 944 eps_setpatcol, 945 eps_drawimage, 946 eps_setplotsize, 947 0 /* eps_getplotsize */, 948 eps_showpage, 949 eps_finish, 950 eps_capabilities, 951 eps_destroy 952 }; 953 954/*! Retrieves the pointer to a new \c hpgs_eps_device on the heap, 955 which writes to the file with the gieven \c filename. 956 957 The bounding box in the eps files is passed to this functions. 958 959 If the file cannot be opened or the system is out of memory, 960 a null pointer is returned. 961*/ 962hpgs_eps_device *hpgs_new_eps_device(const char *filename, 963 const hpgs_bbox *bb, 964 hpgs_bool do_rop3) 965{ 966 hpgs_eps_device *ret = (hpgs_eps_device *)malloc(sizeof(hpgs_eps_device)); 967 968 if (!ret) 969 return 0; 970 971 ret->out = 0; 972 ret->n_pages = -1; 973 ret->page_setup = HPGS_FALSE; 974 975 if (filename) 976 { 977 ret->filename = strdup(filename); 978 979 if (!ret->filename) 980 { 981 free(ret); 982 return 0; 983 } 984 } 985 else 986 ret->filename = 0; 987 988 ret->pattern_color.r = 0; 989 ret->pattern_color.g = 0; 990 ret->pattern_color.b = 0; 991 992 if (do_rop3) 993 ret->rop3 = hpgs_xrop3_func(252,HPGS_TRUE,HPGS_TRUE); 994 else 995 ret->rop3 = 0; 996 997 ret->doc_bb = *bb; 998 ret->page_bb = *bb; 999 1000 ret->media_sizes_alloc_size = 0; 1001 ret->n_media_sizes = 0; 1002 ret->media_sizes = 0; 1003 1004 ret->color.r = 0.0; 1005 ret->color.g = 0.0; 1006 ret->color.b = 0.0; 1007 1008 ret->inherited.vtable=&eps_vtable; 1009 1010 return ret; 1011} 1012 1013#define HPGS_STD_MEDIA_SIZE(w,h,n) {w,h,n,0,HPGS_MEDIA_SIZE_HASH(w,h)} 1014 1015static hpgs_ps_media_size ps_std_media_sizes[]= 1016 { 1017 HPGS_STD_MEDIA_SIZE(596 ,843 ,"A4"), 1018 HPGS_STD_MEDIA_SIZE(843 ,1192,"A3"), 1019 HPGS_STD_MEDIA_SIZE(1192,1686,"A2"), 1020 HPGS_STD_MEDIA_SIZE(1686,2384,"A1"), 1021 HPGS_STD_MEDIA_SIZE(2384,3371,"A0") 1022 }; 1023 1024/* A helper for qsort */ 1025static int compare_media_hashes(const void *a, const void *b) 1026{ 1027 const hpgs_ps_media_size *m1 = (const hpgs_ps_media_size *)a; 1028 const hpgs_ps_media_size *m2 = (const hpgs_ps_media_size *)b; 1029 1030 if (m1->hash < m2->hash) return -1; 1031 if (m1->hash > m2->hash) return 1; 1032 return 0; 1033} 1034 1035 1036/*! Retrieves the pointer to a new \c hpgs_eps_device on the heap, 1037 which writes to a multipage PostScript file with the given \c filename. 1038 1039 The overall document bounding box for the PostScript file is 1040 passed as \c bb. 1041 1042 If \c paper_width and \c paper_height are greater than zero, 1043 the content of each page is scaled to this fixed paper format. 1044 Otherwise, the paper size of each page adpats to the page 1045 bounding box. 1046 1047 The given \c border is used in order to place the contents on the 1048 page. 1049 1050 If the file cannot be opened or the system is out of memory, 1051 a null pointer is returned. 1052*/ 1053hpgs_eps_device *hpgs_new_ps_device(const char *filename, 1054 const hpgs_bbox *bb, 1055 hpgs_bool do_rop3) 1056{ 1057 hpgs_eps_device *ret = (hpgs_eps_device *)malloc(sizeof(hpgs_eps_device)); 1058 1059 if (!ret) 1060 return 0; 1061 1062 ret->out = hpgs_new_mem_ostream(1024*1024); 1063 1064 if (!ret->out) 1065 { 1066 free(ret); 1067 return 0; 1068 } 1069 1070 ret->n_pages = 0; 1071 ret->page_setup = HPGS_FALSE; 1072 1073 if (filename) 1074 { 1075 ret->filename = strdup(filename); 1076 1077 if (!ret->filename) 1078 { 1079 hpgs_ostream_close(ret->out); 1080 free(ret); 1081 return 0; 1082 } 1083 } 1084 else 1085 ret->filename = 0; 1086 1087 ret->pattern_color.r = 0; 1088 ret->pattern_color.g = 0; 1089 ret->pattern_color.b = 0; 1090 1091 if (do_rop3) 1092 ret->rop3 = hpgs_xrop3_func(252,HPGS_TRUE,HPGS_TRUE); 1093 else 1094 ret->rop3 = 0; 1095 1096 ret->doc_bb = *bb; 1097 ret->page_bb = *bb; 1098 1099 ret->media_sizes_alloc_size = 32; 1100 ret->media_sizes = 1101 (hpgs_ps_media_size*)malloc(sizeof(hpgs_ps_media_size)*ret->media_sizes_alloc_size); 1102 1103 if (!ret->media_sizes) 1104 { 1105 hpgs_ostream_close(ret->out); 1106 free(ret->filename); 1107 free(ret); 1108 return 0; 1109 } 1110 1111 // fill in std media sizes. 1112 ret->n_media_sizes = sizeof(ps_std_media_sizes)/sizeof(hpgs_ps_media_size); 1113 memcpy(ret->media_sizes,ps_std_media_sizes,sizeof(ps_std_media_sizes)); 1114 1115 qsort(ret->media_sizes,ret->n_media_sizes, 1116 sizeof(hpgs_ps_media_size),compare_media_hashes); 1117 1118 ret->color.r = 0.0; 1119 ret->color.g = 0.0; 1120 ret->color.b = 0.0; 1121 1122 ret->inherited.vtable=&eps_vtable; 1123 1124 return ret; 1125} 1126 1127/*! 1128 Plotsize device. 1129*/ 1130static int pls_expand_pages(hpgs_plotsize_device *pls) 1131{ 1132 int i,n = pls->page_bbs_alloc_size*2; 1133 hpgs_bbox *nbbs; 1134 1135 if (pls->n_page_bbs > n) n=pls->n_page_bbs; 1136 1137 nbbs = realloc(pls->page_bbs,sizeof(hpgs_bbox)*n); 1138 1139 if (!nbbs) 1140 return hpgs_set_error(hpgs_i18n("Out of memory expanding page size stack.")); 1141 1142 pls->page_bbs=nbbs; 1143 1144 for (i=pls->page_bbs_alloc_size;i<n;++i) 1145 hpgs_bbox_null(pls->page_bbs+i); 1146 1147 pls->page_bbs_alloc_size=n; 1148 return 0; 1149} 1150 1151static int pls_moveto (hpgs_device *_this, const hpgs_point *p) 1152{ 1153 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1154 1155 pls->moveto = *p; 1156 pls->deferred_moveto = 1; 1157 1158 return 0; 1159} 1160 1161static int pls_lineto (hpgs_device *_this, const hpgs_point *p) 1162{ 1163 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1164 1165 if (pls->deferred_moveto) 1166 { 1167 hpgs_bbox_add(&pls->path_bb,&pls->moveto); 1168 pls->deferred_moveto = 0; 1169 } 1170 1171 hpgs_bbox_add(&pls->path_bb,p); 1172 pls->moveto = *p; 1173 1174 return 0; 1175} 1176 1177static void add_bezier(hpgs_plotsize_device *pls, 1178 const hpgs_point *p0, const hpgs_point *p1, 1179 const hpgs_point *p2, const hpgs_point *p3, 1180 int depth ) 1181{ 1182 hpgs_point ll = { HPGS_MIN(p0->x,p3->x),HPGS_MIN(p0->y,p3->y) }; 1183 hpgs_point ur = { HPGS_MAX(p0->x,p3->x),HPGS_MAX(p0->y,p3->y) }; 1184 1185 // p1 and p2 inside bbox of p0 and p3. 1186 if (depth > 2 || 1187 p1->x < ll.x || p2->x < ll.x || 1188 p1->y < ll.y || p2->y < ll.y || 1189 p1->x > ur.x || p2->x > ur.x || 1190 p1->y > ur.y || p2->y > ur.y ) 1191 { 1192 hpgs_bbox_add(&pls->path_bb,p3); 1193 } 1194 else 1195 { 1196 // split spline 1197 hpgs_point p1l = { 0.5 * (p0->x + p1->x), 0.5 * (p0->y + p1->y) }; 1198 hpgs_point p2l = { 0.25 * (p0->x + p2->x) + 0.5 * p1->x, 0.25 * (p0->y + p2->y) + 0.5 * p1->y }; 1199 hpgs_point pm = { (p0->x + p3->x) * 0.125 + 0.375 * (p1->x + p2->x), (p0->y + p3->y) * 0.125 + 0.375 * (p1->y + p2->y) }; 1200 hpgs_point p1u = { 0.25 * (p1->x + p3->x) + 0.5 * p2->x, 0.25 * (p1->y + p3->y) + 0.5 * p2->y }; 1201 hpgs_point p2u = { 0.5 * (p2->x + p3->x), 0.5 * (p2->y + p3->y) }; 1202 1203 add_bezier(pls,p0,&p1l,&p2l,&pm,depth+1); 1204 add_bezier(pls,&pm,&p1u,&p2u,p3,depth+1); 1205 } 1206} 1207 1208static int pls_curveto (hpgs_device *_this, const hpgs_point *p1, 1209 const hpgs_point *p2, const hpgs_point *p3 ) 1210{ 1211 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1212 1213 if (pls->deferred_moveto) 1214 { 1215 hpgs_bbox_add(&pls->path_bb,&pls->moveto); 1216 pls->deferred_moveto = 0; 1217 } 1218 1219 add_bezier(pls,&pls->moveto,p1,p2,p3,0); 1220 1221 pls->moveto = *p3; 1222 1223 return 0; 1224} 1225 1226static int pls_addpath (hpgs_device *_this, hpgs_bool do_linewidth) 1227{ 1228 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1229 1230 if (do_linewidth) 1231 { 1232 hpgs_bbox_addborder(&pls->path_bb,0.5 * pls->linewidth); 1233 } 1234 1235 if (pls->clip_depth >= 0) 1236 { 1237 hpgs_bbox_intersect(&pls->path_bb,pls->clip_bbs+pls->clip_depth); 1238 1239 // check for null intersection 1240 if (hpgs_bbox_isnull(&pls->path_bb)) 1241 goto get_out; 1242 } 1243 1244 hpgs_bbox_expand(&pls->page_bb,&pls->path_bb); 1245 hpgs_bbox_expand(&pls->global_bb,&pls->path_bb); 1246 1247 get_out: 1248 hpgs_bbox_null(&pls->path_bb); 1249 1250 pls->moveto.x = 0.0; 1251 pls->moveto.y = 0.0; 1252 pls->deferred_moveto = 0; 1253 1254 return 0; 1255} 1256 1257static int pls_stroke (hpgs_device *_this) 1258{ 1259 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1260 1261 return pls_addpath (_this,pls->do_linewidth); 1262} 1263 1264static int pls_fill (hpgs_device *_this, hpgs_bool winding) 1265{ 1266 return pls_addpath (_this,HPGS_FALSE); 1267} 1268 1269static int pls_closepath (hpgs_device *_this) 1270{ 1271 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1272 1273 pls->deferred_moveto = 1; 1274 1275 return 0; 1276} 1277 1278static int pls_newpath (hpgs_device *_this) 1279{ 1280 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1281 1282 hpgs_bbox_null(&pls->path_bb); 1283 1284 pls->moveto.x = 0.0; 1285 pls->moveto.y = 0.0; 1286 pls->deferred_moveto = 0; 1287 1288 return 0; 1289} 1290 1291static int pls_clip (hpgs_device *_this, hpgs_bool winding) 1292{ 1293 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1294 1295 hpgs_bbox_intersect(pls->clip_bbs+pls->clip_depth,&pls->path_bb); 1296 1297 return 0; 1298} 1299 1300static int pls_clipsave (hpgs_device *_this) 1301{ 1302 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1303 1304 if (pls->clip_depth+1 >= HPGS_PLOTSIZE_MAX_CLIP_DEPTH) 1305 return hpgs_set_error(hpgs_i18n("Maximum clip depth %d exceeded."), 1306 HPGS_PLOTSIZE_MAX_CLIP_DEPTH); 1307 1308 pls->clip_bbs[pls->clip_depth+1] = pls->clip_bbs[pls->clip_depth] ; 1309 1310 ++pls->clip_depth; 1311 1312 return 0; 1313} 1314 1315static int pls_cliprestore (hpgs_device *_this) 1316{ 1317 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1318 --pls->clip_depth; 1319 1320 if (pls->clip_depth < 0) 1321 return hpgs_set_error("cliprestore: clip stack underflow."); 1322 1323 return 0; 1324} 1325 1326static int pls_setlinewidth(hpgs_device *_this, double lw) 1327{ 1328 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1329 1330 pls->linewidth = lw; 1331 return 0; 1332} 1333 1334static int pls_drawimage(hpgs_device *_this, const hpgs_image *img, 1335 const hpgs_point *ll, const hpgs_point *lr, 1336 const hpgs_point *ur) 1337{ 1338 hpgs_point ul; 1339 1340 ul.x = ll->x + (ur->x - lr->x); 1341 ul.y = ll->y + (ur->y - lr->y); 1342 1343 pls_newpath(_this); 1344 pls_moveto(_this,ll); 1345 pls_lineto(_this,lr); 1346 pls_lineto(_this,ur); 1347 pls_lineto(_this,&ul); 1348 pls_addpath(_this,HPGS_FALSE); 1349 1350 return 0; 1351} 1352 1353static int pls_showpage (hpgs_device *_this, int i) 1354{ 1355 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1356 1357 if (i>0) 1358 { 1359 if (pls->page_bb.urx < pls->page_bb.llx) 1360 pls->page_bb.urx = pls->page_bb.llx = 0.0; 1361 1362 if (pls->page_bb.ury < pls->page_bb.lly) 1363 pls->page_bb.ury = pls->page_bb.lly = 0.0; 1364 1365 if (i > pls->n_page_bbs) pls->n_page_bbs=i; 1366 1367 if (pls->n_page_bbs > pls->page_bbs_alloc_size && 1368 pls_expand_pages(pls)) 1369 return -1; 1370 1371 pls->page_bbs[i-1] = pls->page_bb; 1372 } 1373 1374 hpgs_bbox_null(&pls->page_bb); 1375 1376 return 0; 1377} 1378 1379static int pls_finish (hpgs_device *_this) 1380{ 1381 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1382 1383 if (pls->global_bb.urx < pls->global_bb.llx) 1384 pls->global_bb.urx = pls->global_bb.llx = 0.0; 1385 1386 if (pls->global_bb.ury < pls->global_bb.lly) 1387 pls->global_bb.ury = pls->global_bb.lly = 0.0; 1388 1389 return 0; 1390} 1391 1392static int pls_capabilities (hpgs_device *_this) 1393{ 1394 return 1395 HPGS_DEVICE_CAP_PLOTSIZE | 1396 HPGS_DEVICE_CAP_MULTIPAGE | 1397 HPGS_DEVICE_CAP_MULTISIZE | 1398 HPGS_DEVICE_CAP_NULLIMAGE; 1399} 1400 1401static void pls_destroy (hpgs_device *_this) 1402{ 1403 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1404 1405 if (pls->page_bbs) 1406 free (pls->page_bbs); 1407} 1408 1409static int pls_setplotsize (hpgs_device *_this, const hpgs_bbox *bb) 1410{ 1411 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1412 1413 if (pls->ignore_ps) 1414 return 0; 1415 1416 pls->global_bb = *bb; 1417 pls->page_bb = *bb; 1418 1419 return 2; 1420} 1421 1422static int pls_getplotsize (hpgs_device *_this, int i, hpgs_bbox *bb) 1423{ 1424 hpgs_plotsize_device *pls = (hpgs_plotsize_device *)_this; 1425 1426 int ret = 0; 1427 1428 if (i<=0 || i > pls->n_page_bbs) 1429 { 1430 *bb = pls->global_bb; 1431 if (i>0) ret = 1; 1432 } 1433 else 1434 *bb = pls->page_bbs[i-1]; 1435 1436 return ret; 1437} 1438 1439static hpgs_device_vtable pls_vtable = 1440 { 1441 "hpgs_plotsize_device", 1442 pls_moveto, 1443 pls_lineto, 1444 pls_curveto, 1445 pls_newpath, 1446 pls_closepath, 1447 pls_stroke, 1448 pls_fill, 1449 pls_clip, 1450 pls_clipsave, 1451 pls_cliprestore, 1452 0 /* pls_setrgbcolor */, 1453 0 /* pls_setdash */, 1454 pls_setlinewidth, 1455 0 /* pls_setlinecap */, 1456 0 /* pls_setlinejoin */, 1457 0 /* pls_setmiterlimit */, 1458 0 /* pls_setrop3 */, 1459 0 /* pls_setpatcol */, 1460 pls_drawimage, 1461 pls_setplotsize, 1462 pls_getplotsize, 1463 pls_showpage, 1464 pls_finish, 1465 pls_capabilities, 1466 pls_destroy 1467 }; 1468 1469/*! Retrieves the pointer to a new \c hpgs_plotsize_device on the heap. 1470 1471 If \c ignore_ps is \c HPGS_TRUE, a HPGL PS statement is ignored an the 1472 plotsize is calculated from the vector graphics contents. 1473 1474 If \c do_linewidth is \c HPGS_TRUE, the current linewidth is 1475 taken into account in the plotsize calculation. 1476 1477 If the system is out of memory, a null pointer is returned. 1478*/ 1479hpgs_plotsize_device *hpgs_new_plotsize_device(hpgs_bool ignore_ps, 1480 hpgs_bool do_linewidth) 1481{ 1482 hpgs_plotsize_device *ret = 1483 (hpgs_plotsize_device *)malloc(sizeof(hpgs_plotsize_device)); 1484 1485 if (ret) 1486 { 1487 ret->n_page_bbs = 0; 1488 ret->page_bbs_alloc_size = 32; 1489 ret->page_bbs = malloc(sizeof(hpgs_bbox)*ret->page_bbs_alloc_size); 1490 1491 if (!ret->page_bbs) 1492 { 1493 free(ret); 1494 return 0; 1495 } 1496 1497 hpgs_bbox_null(&ret->path_bb); 1498 hpgs_bbox_null(&ret->page_bb); 1499 hpgs_bbox_null(&ret->global_bb); 1500 ret->moveto.x = 0.0; 1501 ret->moveto.y = 0.0; 1502 ret->deferred_moveto = 0; 1503 ret->clip_bbs[0].llx = -1.0e20; 1504 ret->clip_bbs[0].lly = -1.0e20; 1505 ret->clip_bbs[0].urx = 1.0e20; 1506 ret->clip_bbs[0].ury = 1.0e20; 1507 ret->clip_depth = 0; 1508 ret->linewidth = 1.0; 1509 ret->ignore_ps = ignore_ps; 1510 ret->do_linewidth = do_linewidth; 1511 ret->inherited.vtable=&pls_vtable; 1512 } 1513 1514 return ret; 1515} 1516 1517/* 1518 Custom device from plugin. 1519*/ 1520typedef int (*hpgs_new_device_func_t)(hpgs_device **device, 1521 void **page_asset_ctxt, 1522 hpgs_reader_asset_func_t *page_asset_func, 1523 void **frame_asset_ctxt, 1524 hpgs_reader_asset_func_t *frame_asset_func, 1525 const char *dev_name, 1526 const char *filename, 1527 const hpgs_bbox *bb, 1528 double xres, double yres, 1529 hpgs_bool do_rop3, 1530 int argc, const char *argv[]); 1531 1532typedef void (*hpgs_plugin_version_func_t)(int *major, int *minor); 1533 1534typedef void (*hpgs_plugin_init_func_t)(); 1535typedef void (*hpgs_plugin_cleanup_func_t)(); 1536 1537typedef struct hpgs_plugin_ref_st hpgs_plugin_ref; 1538 1539#define HPGS_PLUGIN_NAME_LEN 8 1540 1541struct hpgs_plugin_ref_st 1542{ 1543 char name[HPGS_PLUGIN_NAME_LEN]; 1544#ifdef WIN32 1545 HMODULE handle; 1546#else 1547 void *handle; 1548#endif 1549 hpgs_new_device_func_t new_dev_func; 1550 hpgs_plugin_version_func_t version_func; 1551 hpgs_plugin_init_func_t init_func; 1552 hpgs_plugin_init_func_t cleanup_func; 1553}; 1554 1555static hpgs_plugin_ref plugins[4] = 1556 { { "",0,0,0}, { "",0,0,0}, { "",0,0,0}, { "",0,0,0} }; 1557 1558static void hpgs_cleanup_plugin(hpgs_plugin_ref *plugin) 1559{ 1560 if (strlen(plugin->name) == 0) 1561 return; 1562 1563 if (plugin->cleanup_func) plugin->cleanup_func(); 1564 1565#ifdef WIN32 1566 if (!FreeLibrary(plugin->handle)) 1567 hpgs_log("hpgs_cleanup_plugin_devices: unable to close plugin %s.\n", 1568 plugin->name); 1569#else 1570 if (dlclose(plugin->handle)) 1571 hpgs_log("hpgs_cleanup_plugin_devices: unable to close plugin %s: %s.\n", 1572 plugin->name,dlerror()); 1573#endif 1574 1575 plugin->name[0] = '\0'; 1576 plugin->handle = 0; 1577 plugin->new_dev_func = 0; 1578 plugin->version_func = 0; 1579 plugin->init_func = 0; 1580 plugin->cleanup_func = 0; 1581} 1582 1583void hpgs_cleanup_plugin_devices() 1584{ 1585 int i; 1586 1587 for (i=0;i<sizeof(plugins)/sizeof(hpgs_plugin_ref);++i) 1588 { 1589 hpgs_cleanup_plugin(&plugins[i]); 1590 } 1591} 1592 1593int hpgs_new_plugin_device( hpgs_device **device, 1594 void **page_asset_ctxt, 1595 hpgs_reader_asset_func_t *page_asset_func, 1596 void **frame_asset_ctxt, 1597 hpgs_reader_asset_func_t *frame_asset_func, 1598 const char *dev_name, 1599 const char *filename, 1600 const hpgs_bbox *bb, 1601 double xres, double yres, 1602 hpgs_bool do_rop3, 1603 int argc, const char *argv[]) 1604{ 1605 char *underscore=strchr(dev_name,'_'); 1606 int i,l = underscore ? underscore-dev_name : strlen(dev_name); 1607 hpgs_plugin_ref *plugin=0; 1608 1609 if (l >= HPGS_PLUGIN_NAME_LEN) l = HPGS_PLUGIN_NAME_LEN-1; 1610 1611 for (i=0;i<sizeof(plugins)/sizeof(hpgs_plugin_ref);++i) 1612 { 1613 if (strlen(plugins[i].name) == 0) 1614 break; 1615 1616 if (strlen(plugins[i].name) == l && 1617 strncmp(plugins[i].name,dev_name,l) == 0) 1618 { plugin = plugins+i; break; } 1619 } 1620 1621 if (i>=sizeof(plugins)/sizeof(hpgs_plugin_ref)) 1622 { 1623 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: too much plugins registered (max %u).\n"), 1624 (unsigned)(sizeof(plugins)/sizeof(hpgs_plugin_ref))); 1625 return 0; 1626 } 1627 1628 if (!plugin) 1629 { 1630 char plugin_name[1024]; 1631 plugin = plugins+i; 1632 1633#ifdef WIN32 1634 _snprintf(plugin_name,sizeof(plugin_name),"%s\\lib\\hpgs\\hpgs%.*splugin.%d.%d.dll", 1635 hpgs_get_prefix(), 1636 l,dev_name, 1637 HPGS_MAJOR_VERSION,HPGS_MINOR_VERSION); 1638 plugin->handle =LoadLibraryA(plugin_name); 1639 1640 if (!plugin->handle) 1641 { 1642 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: unable to open plugin %s.\n"), 1643 plugin_name); 1644 return 0; 1645 } 1646#else 1647 1648#ifndef HPGS_LIBSFX 1649#define HPGS_LIBSFX "lib" 1650#endif 1651 snprintf(plugin_name,sizeof(plugin_name),"%s/" HPGS_LIBSFX "/hpgs/hpgs%.*splugin.so.%d.%d", 1652 hpgs_get_prefix(), 1653 l,dev_name, 1654 HPGS_MAJOR_VERSION,HPGS_MINOR_VERSION); 1655 1656 plugin->handle = dlopen(plugin_name,RTLD_LAZY); 1657 1658 if (!plugin->handle) 1659 { 1660 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: unable to open plugin %s: %s.\n"), 1661 plugin_name,dlerror()); 1662 return 0; 1663 } 1664#endif 1665 memcpy(plugin->name,dev_name,l); 1666 plugin->name[l]='\0'; 1667 1668 plugin->version_func=0; 1669 plugin->init_func=0; 1670 plugin->cleanup_func=0; 1671 plugin->new_dev_func=0; 1672 } 1673 1674 1675 if (! plugin->version_func) 1676 { 1677 int minor = 0; 1678 int major = 0; 1679#ifdef WIN32 1680 plugin->version_func = 1681 (hpgs_plugin_version_func_t)GetProcAddress(plugin->handle, 1682 "hpgs_plugin_version"); 1683 if (!plugin->version_func) 1684 { 1685 hpgs_cleanup_plugin(plugin); 1686 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: unable to resolve function hpgs_plugin_version.\n")); 1687 return 0; 1688 } 1689#else 1690 plugin->version_func = 1691 (hpgs_plugin_version_func_t)dlsym(plugin->handle, 1692 "hpgs_plugin_version"); 1693 if (!plugin->version_func) 1694 { 1695 hpgs_cleanup_plugin(plugin); 1696 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: unable to resolve function hpgs_plugin_version: %s.\n"), 1697 dlerror()); 1698 return 0; 1699 } 1700#endif 1701 1702 plugin->version_func(&major,&minor); 1703 1704 if (major != HPGS_MAJOR_VERSION || 1705 minor != HPGS_MINOR_VERSION ) 1706 { 1707 hpgs_cleanup_plugin(plugin); 1708 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: Plugin version %d.%d does not match application version %d.%d.\n"), 1709 major,minor,HPGS_MAJOR_VERSION,HPGS_MINOR_VERSION); 1710 1711 return 0; 1712 } 1713 1714#ifdef WIN32 1715 plugin->init_func = 1716 (hpgs_plugin_init_func_t)GetProcAddress(plugin->handle, 1717 "hpgs_plugin_init"); 1718 plugin->cleanup_func = 1719 (hpgs_plugin_cleanup_func_t)GetProcAddress(plugin->handle, 1720 "hpgs_plugin_cleanup"); 1721#else 1722 plugin->init_func = 1723 (hpgs_plugin_init_func_t)dlsym(plugin->handle, 1724 "hpgs_plugin_init"); 1725 plugin->cleanup_func = 1726 (hpgs_plugin_cleanup_func_t)dlsym(plugin->handle, 1727 "hpgs_plugin_cleanup"); 1728#endif 1729 1730 if (plugin->init_func) plugin->init_func(); 1731 } 1732 1733 1734 if (!plugin->new_dev_func) 1735 { 1736#ifdef WIN32 1737 plugin->new_dev_func = 1738 (hpgs_new_device_func_t)GetProcAddress(plugin->handle, 1739 "hpgs_plugin_new_device"); 1740 if (!plugin->new_dev_func) 1741 { 1742 hpgs_cleanup_plugin(plugin); 1743 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: unable to resolve function hpgs_plugin_new_device.\n")); 1744 return 0; 1745 } 1746#else 1747 plugin->new_dev_func = 1748 (hpgs_new_device_func_t)dlsym(plugin->handle, 1749 "hpgs_plugin_new_device"); 1750 1751 if (!plugin->new_dev_func) 1752 { 1753 hpgs_cleanup_plugin(plugin); 1754 hpgs_set_error(hpgs_i18n("hpgs_new_plugin_device: unable to resolve function hpgs_plugin_new_device: %s.\n"), 1755 dlerror()); 1756 return 0; 1757 } 1758#endif 1759 } 1760 1761 return plugin->new_dev_func(device, 1762 page_asset_ctxt,page_asset_func, 1763 frame_asset_ctxt,frame_asset_func, 1764 dev_name,filename,bb,xres,yres,do_rop3,argc,argv); 1765} 1766