/* Copyright (C) 2001-2007 Peter Selinger. This file is part of Potrace. It is free software and it is covered by the GNU General Public License. See the file COPYING for details. */ /* $Id: backend_pdf.c 147 2007-04-09 00:44:09Z selinger $ */ /* The PDF backend of Potrace. Stream compression is optionally supplied via the functions in flate.c. */ #include #include #include #include #include #include "main.h" #include "backend_pdf.h" #include "flate.h" #include "lists.h" #include "potracelib.h" #include "auxiliary.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif typedef int color_t; #define black 0x000000 #define red 0xff0000 #define green 0x008000 #define blue 0x0000ff /* ---------------------------------------------------------------------- */ /* auxiliary: growing arrays */ struct intarray_s { int size; int *data; }; typedef struct intarray_s intarray_t; static inline void intarray_init(intarray_t *ar) { ar->size = 0; ar->data = NULL; } static inline void intarray_term(intarray_t *ar) { free(ar->data); ar->size = 0; ar->data = NULL; } /* Set ar[n]=val. Expects n>=0. Grows array if necessary. Return 0 on success and -1 on error with errno set. */ static inline int intarray_set(intarray_t *ar, int n, int val) { int *p; int s; if (n >= ar->size) { s = n+1024; p = (int *)realloc(ar->data, s * sizeof(int)); if (!p) { return -1; } ar->data = p; ar->size = s; } ar->data[n] = val; return 0; } /* ---------------------------------------------------------------------- */ /* some global variables */ static intarray_t xref; static int nxref = 0; static intarray_t pages; static int npages; static int streamofs; static size_t outcount; /* output file position */ /* ---------------------------------------------------------------------- */ /* functions for interfacing with compression backend */ /* xship: callback function that must be initialized before calling any other functions of the "ship" family. xship_file must be initialized too. */ /* print the token to f, but filtered through a compression filter in case filter!=0 */ static int (*xship)(FILE *f, int filter, char *s, int len); static FILE *xship_file; /* ship PDF code, filtered */ static int ship(const char *fmt, ...) { va_list args; static char buf[4096]; /* static string limit is okay here because we only use constant format strings - for the same reason, it is okay to use vsprintf instead of vsnprintf below. */ va_start(args, fmt); vsprintf(buf, fmt, args); buf[4095] = 0; va_end(args); outcount += xship(xship_file, 1, buf, strlen(buf)); return 0; } /* ship PDF code, unfiltered */ static int shipclear(char *fmt, ...) { static char buf[4096]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); buf[4095] = 0; va_end(args); outcount += xship(xship_file, 0, buf, strlen(buf)); return 0; } /* set all callback functions */ static void pdf_callbacks(FILE *fout) { if (info.compress) { xship = pdf_xship; } else { xship = dummy_xship; } xship_file = fout; } /* ---------------------------------------------------------------------- */ /* PDF path-drawing auxiliary functions */ /* coordinate quantization */ static inline point_t unit(dpoint_t p) { point_t q; q.x = (long)(floor(p.x*info.unit+.5)); q.y = (long)(floor(p.y*info.unit+.5)); return q; } static void pdf_coords(dpoint_t p) { point_t cur = unit(p); ship("%ld %ld ", cur.x, cur.y); } static void pdf_moveto(dpoint_t p) { pdf_coords(p); ship("m\n"); } static void pdf_lineto(dpoint_t p) { pdf_coords(p); ship("l\n"); } static void pdf_curveto(dpoint_t p1, dpoint_t p2, dpoint_t p3) { point_t q1, q2, q3; q1 = unit(p1); q2 = unit(p2); q3 = unit(p3); ship("%ld %ld %ld %ld %ld %ld c\n", q1.x, q1.y, q2.x, q2.y, q3.x, q3.y); } /* this procedure returns a statically allocated string */ static char *pdf_colorstring(const color_t col) { double r, g, b; static char buf[100]; r = (col & 0xff0000) >> 16; g = (col & 0x00ff00) >> 8; b = (col & 0x0000ff) >> 0; if (r==0 && g==0 && b==0) { return "0 g"; } else if (r==255 && g==255 && b==255) { return "1 g"; } else if (r == g && g == b) { sprintf(buf, "%.3f g", r/255.0); return buf; } else { sprintf(buf, "%.3f %.3f %.3f rg", r/255.0, g/255.0, b/255.0); return buf; } } static color_t pdf_color = -1; static double pdf_width = -1; static void pdf_setcolor(const color_t col) { if (col == pdf_color) { return; } pdf_color = col; ship("%s\n", pdf_colorstring(col)); } /* explicit encoding, does not use special macros */ static int pdf_path(potrace_curve_t *curve) { int i; dpoint_t *c; int m = curve->n; c = curve->c[m-1]; pdf_moveto(c[2]); for (i=0; ic[i]; switch (curve->tag[i]) { case POTRACE_CORNER: pdf_lineto(c[1]); pdf_lineto(c[2]); break; case POTRACE_CURVETO: pdf_curveto(c[0], c[1], c[2]); break; } } return 0; } /* ---------------------------------------------------------------------- */ /* Backends for various types of output. */ /* Normal output: black on transparent */ static int render0(potrace_path_t *plist) { potrace_path_t *p; pdf_setcolor(info.color); list_forall (p, plist) { pdf_path(&p->curve); ship("h\n"); if (p->next == NULL || p->next->sign == '+') { ship("f\n"); } } return 0; } /* Opaque output: alternating black and white */ static int render0_opaque(potrace_path_t *plist) { potrace_path_t *p; list_forall (p, plist) { pdf_path(&p->curve); ship("h\n"); pdf_setcolor(p->sign=='+' ? info.color : info.fillcolor); ship("f\n"); } return 0; } /* select the appropriate rendering function from above */ static int pdf_render(potrace_path_t *plist) { if (info.opaque) { return render0_opaque(plist); } return render0(plist); } /* ---------------------------------------------------------------------- */ /* PDF header and footer */ int init_pdf(FILE *fout) { intarray_init(&xref); intarray_init(&pages); nxref = 0; npages = 0; /* set callback functions for shipping routines */ pdf_callbacks(fout); outcount = 0; shipclear("%%PDF-1.3\n"); intarray_set(&xref, nxref++, outcount); shipclear("1 0 obj\n<>\nendobj\n"); intarray_set(&xref, nxref++, outcount); shipclear("2 0 obj\n" "<>\n" "endobj\n"); /* delay obj #3 (pages) until end */ nxref++; fflush(fout); return 0; } int term_pdf(FILE *fout) { int startxref; int i; pdf_callbacks(fout); intarray_set(&xref, 2, outcount); shipclear("3 0 obj\n" "<>\nendobj\n"); startxref = outcount; shipclear("xref\n0 %d\n", nxref + 1); shipclear("0000000000 65535 f \n"); for (i = 0; i < nxref; i++) shipclear("%0.10d 00000 n \n", xref.data[i]); shipclear("trailer\n<>\n", nxref + 1); shipclear("startxref\n%d\n%%%%EOF\n", startxref); fflush(fout); intarray_term(&xref); intarray_term(&pages); return 0; } static void pdf_pageinit(imginfo_t *imginfo) { double s, c; double origx = imginfo->trans.orig[0] + imginfo->lmar; double origy = imginfo->trans.orig[1] + imginfo->bmar; double scalex = imginfo->width / imginfo->pixwidth / info.unit; double scaley = imginfo->height / imginfo->pixheight / info.unit; int pagew = (int)ceil(imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar); int pageh = (int)ceil(imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar); pdf_color = -1; pdf_width = -1; intarray_set(&xref, nxref++, outcount); shipclear("%d 0 obj\n", nxref); shipclear("<>"); shipclear("/MediaBox[0 0 %d %d]", pagew, pageh); shipclear("/Contents %d 0 R>>\n", nxref + 1); shipclear("endobj\n"); intarray_set(&pages, npages++, nxref); intarray_set(&xref, nxref++, outcount); shipclear("%d 0 obj\n", nxref); if (info.compress) shipclear("<>\n", nxref + 1); else shipclear("<>\n", nxref + 1); shipclear("stream\n"); streamofs = outcount; s = sin(info.angle * M_PI / 180.0); c = cos(info.angle * M_PI / 180.0); ship("%f %f %f %f %.0f %.0f cm\n", scalex*c, scalex*s, -scaley*s, scaley*c, origx, origy); } static void pdf_pageterm(void) { int streamlen; shipclear(""); streamlen = outcount - streamofs; shipclear("endstream\nendobj\n"); intarray_set(&xref, nxref++, outcount); shipclear("%d 0 obj\n%d\nendobj\n", nxref, streamlen); } int page_pdf(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo) { int r; pdf_callbacks(fout); pdf_pageinit(imginfo); r = pdf_render(plist); if (r) { return r; } pdf_pageterm(); fflush(fout); return 0; }