/* Copyright (C) 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevpsu.c,v 1.7.2.2.2.1 2003/01/17 00:49:01 giles Exp $ */ /* PostScript-writing utilities */ #include "math_.h" #include "time_.h" #include "gx.h" #include "gscdefs.h" #include "gxdevice.h" #include "gdevpsu.h" #include "spprint.h" #include "stream.h" /* ---------------- Low level ---------------- */ /* Write a 0-terminated array of strings as lines. */ void psw_print_lines(FILE *f, const char *const lines[]) { int i; for (i = 0; lines[i] != 0; ++i) fprintf(f, "%s\n", lines[i]); } /* Write the ProcSet name. */ private void psw_put_procset_name(stream *s, const gx_device *dev, const gx_device_pswrite_common_t *pdpc) { pprints1(s, "GS_%s", dev->dname); pprintd3(s, "_%d_%d_%d", (int)pdpc->LanguageLevel, (int)(pdpc->LanguageLevel * 10 + 0.5) % 10, pdpc->ProcSet_version); } private void psw_print_procset_name(FILE *f, const gx_device *dev, const gx_device_pswrite_common_t *pdpc) { byte buf[100]; /* arbitrary */ stream s; swrite_file(&s, f, buf, sizeof(buf)); psw_put_procset_name(&s, dev, pdpc); sflush(&s); } /* Write a bounding box. */ private void psw_print_bbox(FILE *f, const gs_rect *pbbox) { fprintf(f, "%%%%BoundingBox: %d %d %d %d\n", (int)floor(pbbox->p.x), (int)floor(pbbox->p.y), (int)ceil(pbbox->q.x), (int)ceil(pbbox->q.y)); fprintf(f, "%%%%HiResBoundingBox: %f %f %f %f\n", pbbox->p.x, pbbox->p.y, pbbox->q.x, pbbox->q.y); } /* ---------------- File level ---------------- */ private const char *const psw_ps_header[] = { "%!PS-Adobe-3.0", "%%Pages: (atend)", 0 }; private const char *const psw_eps_header[] = { "%!PS-Adobe-3.0 EPSF-3.0", 0 }; private const char *const psw_begin_prolog[] = { "%%EndComments", "%%BeginProlog", "% This copyright applies to everything between here and the %%EndProlog:", /* copyright */ /* begin ProcSet */ 0 }; /* * To achieve page independence, every page must in the general case * set page parameters. To preserve duplexing the page cannot set page * parameters. The following code checks the current page size and sets * it only if it is necessary. */ private const char *const psw_ps_procset[] = { /* setpagesize - */ "/PageSize 2 array def" "/setpagesize" /* x y /a4 -> - */ "{ PageSize aload pop " /* x y /a4 x0 y0 */ "3 index eq exch", /* x y /a4 bool x0 */ "4 index eq and" /* x y /a4 bool */ "{ pop pop pop" "}" "{ PageSize dup 1", /* x y /a4 [ ] [ ] 1 */ "5 -1 roll put 0 " /* x /a4 [ y] 0 */ "4 -1 roll put " /* /a4 */ "dup null eq {false} {dup where} ifelse" "{ exch get exec" /* - */ "}", "{ pop" /* - */ "/setpagedevice where", "{ pop 1 dict dup /PageSize PageSize put setpagedevice" "}", "{ /setpage where" "{ pop PageSize aload pop pageparams 3 {exch pop} repeat", "setpage" "}" "if" "}" "ifelse" "}" "ifelse" "}" "ifelse" "} bind def", 0 }; private const char *const psw_end_prolog[] = { "end readonly def", "%%EndResource", /* ProcSet */ "/pagesave null def", /* establish binding */ "%%EndProlog", 0 }; /* * Write the file header, up through the BeginProlog. This must write to a * file, not a stream, because it may be called during finalization. */ void psw_begin_file_header(FILE *f, const gx_device *dev, const gs_rect *pbbox, gx_device_pswrite_common_t *pdpc, bool ascii) { psw_print_lines(f, (pdpc->ProduceEPS ? psw_eps_header : psw_ps_header)); if (pbbox) { psw_print_bbox(f, pbbox); pdpc->bbox_position = 0; } else if (ftell(f) < 0) { /* File is not seekable. */ pdpc->bbox_position = -1; fputs("%%BoundingBox: (atend)\n", f); fputs("%%HiResBoundingBox: (atend)\n", f); } else { /* File is seekable, leave room to rewrite bbox. */ pdpc->bbox_position = ftell(f); fputs("%...............................................................\n", f); fputs("%...............................................................\n", f); } fprintf(f, "%%%%Creator: %s %ld (%s)\n", gs_product, (long)gs_revision, dev->dname); { time_t t; struct tm tms; time(&t); tms = *localtime(&t); fprintf(f, "%%%%CreationDate: %d/%02d/%02d %02d:%02d:%02d\n", tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec); } if (ascii) fputs("%%DocumentData: Clean7Bit\n", f); if (pdpc->LanguageLevel >= 2.0) fprintf(f, "%%%%LanguageLevel: %d\n", (int)pdpc->LanguageLevel); else if (pdpc->LanguageLevel == 1.5) fputs("%%Extensions: CMYK\n", f); psw_print_lines(f, psw_begin_prolog); fprintf(f, "%% %s\n", gs_copyright); fputs("%%BeginResource: procset ", f); psw_print_procset_name(f, dev, pdpc); fputs("\n/", f); psw_print_procset_name(f, dev, pdpc); fputs(" 80 dict dup begin\n", f); psw_print_lines(f, psw_ps_procset); } /* * End the file header. */ void psw_end_file_header(FILE *f) { psw_print_lines(f, psw_end_prolog); } /* * End the file. */ void psw_end_file(FILE *f, const gx_device *dev, const gx_device_pswrite_common_t *pdpc, const gs_rect *pbbox, int page_count) { if (f == NULL) return; /* clients should be more careful */ fprintf(f, "%%%%Trailer\n%%%%Pages: %ld\n", page_count); if (dev->PageCount > 0 && pdpc->bbox_position != 0) { if (pdpc->bbox_position >= 0) { long save_pos = ftell(f); fseek(f, pdpc->bbox_position, SEEK_SET); psw_print_bbox(f, pbbox); fputc('%', f); fseek(f, save_pos, SEEK_SET); } else psw_print_bbox(f, pbbox); } if (!pdpc->ProduceEPS) fputs("%%EOF\n", f); } /* ---------------- Page level ---------------- */ /* * Write the page header. */ void psw_write_page_header(stream *s, const gx_device *dev, const gx_device_pswrite_common_t *pdpc, bool do_scale, long page_ord) { long page = dev->PageCount + 1; pprintld2(s, "%%%%Page: %ld %ld\n%%%%BeginPageSetup\n", page, page_ord); /* * Adobe's documentation says that page setup must be placed outside the * save/restore that encapsulates the page contents, and that the * showpage must be placed after the restore. This means that to * achieve page independence, *every* page's setup code must include a * setpagedevice that sets *every* page device parameter that is changed * on *any* page. Currently, the only such parameter relevant to this * driver is page size, but there might be more in the future. */ psw_put_procset_name(s, dev, pdpc); stream_puts(s, " begin\n"); if (!pdpc->ProduceEPS) { int width = (int)(dev->width * 72.0 / dev->HWResolution[0] + 0.5); int height = (int)(dev->height * 72.0 / dev->HWResolution[1] + 0.5); typedef struct ps_ { const char *size_name; int width, height; } page_size; static const page_size sizes[] = { {"/11x17", 792, 1224}, {"/a3", 842, 1190}, {"/a4", 595, 842}, {"/b5", 501, 709}, {"/ledger", 1224, 792}, {"/legal", 612, 1008}, {"/letter", 612, 792}, {"null", 0, 0} }; const page_size *p = sizes; while (p->size_name[0] == '/' && (p->width != width || p->height != height)) ++p; pprintd2(s, "%d %d ", width, height); pprints1(s, "%s setpagesize\n", p->size_name); } stream_puts(s, "/pagesave save store 100 dict begin\n"); if (do_scale) pprintg2(s, "%g %g scale\n", 72.0 / dev->HWResolution[0], 72.0 / dev->HWResolution[1]); stream_puts(s, "%%EndPageSetup\ngsave mark\n"); } /* * Write the page trailer. We do this directly to the file, rather than to * the stream, because we may have to do it during finalization. */ void psw_write_page_trailer(FILE *f, int num_copies, int flush) { if (num_copies != 1) fprintf(f, "userdict /#copies %d put\n", num_copies); fprintf(f, "cleartomark end end pagesave restore %s\n%%%%PageTrailer\n", (flush ? "showpage" : "copypage")); }