File indexing completed on 2024-12-22 04:04:09

0001 /* Copyright (C) 2001-2019 Peter Selinger.
0002    This file is part of Potrace. It is free software and it is covered
0003    by the GNU General Public License. See the file COPYING for details. */
0004 
0005 
0006 /* The PDF backend of Potrace. Stream compression is optionally
0007     supplied via the functions in flate.c. */
0008 
0009 #ifdef HAVE_CONFIG_H
0010 #include <config.h>
0011 #endif
0012 
0013 #include <stdio.h>
0014 #include <stdarg.h>
0015 #include <string.h>
0016 #include <math.h>
0017 #include <stdlib.h>
0018 
0019 #include "main.h"
0020 #include "backend_pdf.h"
0021 #include "flate.h"
0022 #include "lists.h"
0023 #include "potracelib.h"
0024 #include "auxiliary.h"
0025 
0026 typedef int color_t;
0027 
0028 #define TRY(x) if (x) goto try_error
0029 
0030 /* ---------------------------------------------------------------------- */
0031 /* auxiliary: growing arrays */
0032 
0033 struct intarray_s {
0034   int size;
0035   int *data;
0036 };
0037 typedef struct intarray_s intarray_t;
0038 
0039 static inline void intarray_init(intarray_t *ar) {
0040   ar->size = 0;
0041   ar->data = NULL;
0042 }
0043 
0044 static inline void intarray_term(intarray_t *ar) {
0045   free(ar->data);
0046   ar->size = 0;
0047   ar->data = NULL;
0048 }
0049 
0050 /* Set ar[n]=val. Expects n>=0. Grows array if necessary. Return 0 on
0051    success and -1 on error with errno set. */
0052 static inline int intarray_set(intarray_t *ar, int n, int val) {
0053   int *p;
0054   int s;
0055 
0056   if (n >= ar->size) {
0057     s = n+1024;
0058     p = (int *)realloc(ar->data, s * sizeof(int));
0059     if (!p) {
0060       return -1;
0061     }
0062     ar->data = p;
0063     ar->size = s;
0064   }
0065   ar->data[n] = val;
0066   return 0;
0067 }
0068 
0069 /* ---------------------------------------------------------------------- */
0070 /* some global variables */
0071 
0072 static intarray_t xref;
0073 static int nxref = 0;
0074 static intarray_t pages;
0075 static int npages;
0076 static int streamofs;
0077 static size_t outcount;  /* output file position */
0078 
0079 /* ---------------------------------------------------------------------- */
0080 /* functions for interfacing with compression backend */
0081 
0082 /* xship: callback function that must be initialized before calling
0083    any other functions of the "ship" family. xship_file must be
0084    initialized too. */
0085 
0086 /* print the token to f, but filtered through a compression
0087    filter in case filter!=0 */
0088 static int (*xship)(FILE *f, int filter, const char *s, int len);
0089 static FILE *xship_file;
0090 
0091 /* ship PDF code, filtered */
0092 static int ship(const char *fmt, ...) {
0093   va_list args;
0094   static char buf[4096]; /* static string limit is okay here because
0095                 we only use constant format strings - for
0096                 the same reason, it is okay to use
0097                 vsprintf instead of vsnprintf below. */
0098   va_start(args, fmt);
0099   vsprintf(buf, fmt, args);
0100   buf[4095] = 0;
0101   va_end(args);
0102 
0103   outcount += xship(xship_file, 1, buf, strlen(buf));
0104   return 0;
0105 }  
0106 
0107 /* ship PDF code, unfiltered */
0108 static int shipclear(const char *fmt, ...) {
0109   static char buf[4096];
0110   va_list args;
0111 
0112   va_start(args, fmt);
0113   vsprintf(buf, fmt, args);
0114   buf[4095] = 0;
0115   va_end(args);
0116 
0117   outcount += xship(xship_file, 0, buf, strlen(buf));
0118   return 0;
0119 }
0120 
0121 /* set all callback functions */
0122 static void pdf_callbacks(FILE *fout) {
0123 
0124   if (info.compress) {
0125     xship = pdf_xship;
0126   } else {
0127     xship = dummy_xship;
0128   }
0129   xship_file = fout;
0130 }  
0131 
0132 /* ---------------------------------------------------------------------- */
0133 /* PDF path-drawing auxiliary functions */
0134 
0135 /* coordinate quantization */
0136 static inline point_t unit(dpoint_t p) {
0137   point_t q;
0138 
0139   q.x = (long)(floor(p.x*info.unit+.5));
0140   q.y = (long)(floor(p.y*info.unit+.5));
0141   return q;
0142 }
0143 
0144 static void pdf_coords(dpoint_t p) {
0145   point_t cur = unit(p);
0146   ship("%ld %ld ", cur.x, cur.y);
0147 }
0148 
0149 static void pdf_moveto(dpoint_t p) {
0150   pdf_coords(p);
0151   ship("m\n");
0152 }
0153 
0154 static void pdf_lineto(dpoint_t p) {
0155   pdf_coords(p);
0156   ship("l\n");
0157 }
0158 
0159 static void pdf_curveto(dpoint_t p1, dpoint_t p2, dpoint_t p3) {
0160   point_t q1, q2, q3;
0161 
0162   q1 = unit(p1);
0163   q2 = unit(p2);
0164   q3 = unit(p3);
0165 
0166   ship("%ld %ld %ld %ld %ld %ld c\n", q1.x, q1.y, q2.x, q2.y, q3.x, q3.y);
0167 }
0168 
0169 /* this procedure returns a statically allocated string */
0170 static const char *pdf_colorstring(const color_t col) {
0171   double r, g, b;
0172   static char buf[100];
0173 
0174   r = (col & 0xff0000) >> 16;
0175   g = (col & 0x00ff00) >> 8;
0176   b = (col & 0x0000ff) >> 0;
0177 
0178   if (r==0 && g==0 && b==0) {
0179     return "0 g";
0180   } else if (r==255 && g==255 && b==255) {
0181     return "1 g";
0182   } else if (r == g && g == b) {
0183     sprintf(buf, "%.3f g", r/255.0);
0184     return buf;
0185   } else {
0186     sprintf(buf, "%.3f %.3f %.3f rg", r/255.0, g/255.0, b/255.0);
0187     return buf;
0188   }
0189 }
0190 
0191 static color_t pdf_color = -1;
0192 
0193 static void pdf_setcolor(const color_t col) {
0194   if (col == pdf_color) {
0195     return;
0196   }
0197   pdf_color = col;
0198 
0199   ship("%s\n", pdf_colorstring(col));
0200 }
0201 
0202 /* explicit encoding, does not use special macros */
0203 static int pdf_path(potrace_curve_t *curve) {
0204   int i;
0205   dpoint_t *c;
0206   int m = curve->n;
0207 
0208   c = curve->c[m-1];
0209   pdf_moveto(c[2]);
0210 
0211   for (i=0; i<m; i++) {
0212     c = curve->c[i];
0213     switch (curve->tag[i]) {
0214     case POTRACE_CORNER:
0215       pdf_lineto(c[1]);
0216       pdf_lineto(c[2]);
0217       break;
0218     case POTRACE_CURVETO:
0219       pdf_curveto(c[0], c[1], c[2]);
0220       break;
0221     }
0222   }
0223   return 0;
0224 }
0225 
0226 /* ---------------------------------------------------------------------- */
0227 /* Backends for various types of output. */
0228 
0229 /* Normal output: black on transparent */
0230 static int render0(potrace_path_t *plist) {
0231   potrace_path_t *p;
0232 
0233   pdf_setcolor(info.color);
0234   list_forall (p, plist) {
0235     pdf_path(&p->curve);
0236     ship("h\n");
0237     if (p->next == NULL || p->next->sign == '+') {
0238       ship("f\n");
0239     }
0240   }
0241   return 0;
0242 }
0243 
0244 /* Opaque output: alternating black and white */
0245 static int render0_opaque(potrace_path_t *plist) {
0246   potrace_path_t *p;
0247   
0248   list_forall (p, plist) {
0249     pdf_path(&p->curve);
0250     ship("h\n");
0251     pdf_setcolor(p->sign=='+' ? info.color : info.fillcolor);
0252     ship("f\n");
0253   }
0254   return 0;
0255 }
0256 
0257 /* select the appropriate rendering function from above */
0258 static int pdf_render(potrace_path_t *plist)
0259 {
0260   if (info.opaque) {
0261     return render0_opaque(plist);
0262   }
0263   return render0(plist);
0264 }  
0265 
0266 /* ---------------------------------------------------------------------- */
0267 /* PDF header and footer */
0268 
0269 int init_pdf(FILE *fout)
0270 {
0271         intarray_init(&xref);
0272     intarray_init(&pages);
0273     nxref = 0;
0274     npages = 0;
0275 
0276     /* set callback functions for shipping routines */
0277     pdf_callbacks(fout);
0278     outcount = 0;
0279 
0280     shipclear("%%PDF-1.3\n");
0281 
0282     TRY(intarray_set(&xref, nxref++, outcount));
0283     shipclear("1 0 obj\n<</Type/Catalog/Pages 3 0 R>>\nendobj\n");
0284 
0285     TRY(intarray_set(&xref, nxref++, outcount));
0286     shipclear("2 0 obj\n"
0287         "<</Creator"
0288         "(" POTRACE " " VERSION ", written by Peter Selinger 2001-2019)>>\n"
0289         "endobj\n");
0290 
0291     /* delay obj #3 (pages) until end */
0292     nxref++;
0293 
0294     fflush(fout);
0295     return 0;
0296 
0297  try_error:
0298         return 1;
0299 }
0300 
0301 int term_pdf(FILE *fout)
0302 {
0303     int startxref;
0304     int i;
0305 
0306     pdf_callbacks(fout);
0307 
0308     TRY(intarray_set(&xref, 2, outcount));
0309     shipclear("3 0 obj\n"
0310         "<</Type/Pages/Count %d/Kids[\n", npages);
0311     for (i = 0; i < npages; i++)
0312         shipclear("%d 0 R\n", pages.data[i]);
0313     shipclear("]>>\nendobj\n");
0314 
0315     startxref = outcount;
0316 
0317     shipclear("xref\n0 %d\n", nxref + 1);
0318     shipclear("0000000000 65535 f \n");
0319     for (i = 0; i < nxref; i++)
0320         shipclear("%0.10d 00000 n \n", xref.data[i]);
0321 
0322     shipclear("trailer\n<</Size %d/Root 1 0 R/Info 2 0 R>>\n", nxref + 1);
0323     shipclear("startxref\n%d\n%%%%EOF\n", startxref);
0324 
0325     fflush(fout);
0326     intarray_term(&xref);
0327     intarray_term(&pages);
0328     return 0;
0329 
0330  try_error:
0331         return 1;
0332 }
0333 
0334 /* if largebbox is set, set bounding box to pagesize. Return 0 on
0335    success or 1 on error with errno set. */
0336 static int pdf_pageinit(imginfo_t *imginfo, int largebbox)
0337 {
0338     double origx = imginfo->trans.orig[0] + imginfo->lmar;
0339     double origy = imginfo->trans.orig[1] + imginfo->bmar;
0340     double dxx = imginfo->trans.x[0] / info.unit;
0341     double dxy = imginfo->trans.x[1] / info.unit;
0342     double dyx = imginfo->trans.y[0] / info.unit;
0343     double dyy = imginfo->trans.y[1] / info.unit;
0344 
0345     double pagew = imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar;
0346     double pageh = imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar;
0347 
0348     pdf_color = -1;
0349 
0350     TRY(intarray_set(&xref, nxref++, outcount));
0351     shipclear("%d 0 obj\n", nxref);
0352     shipclear("<</Type/Page/Parent 3 0 R/Resources<</ProcSet[/PDF]>>");
0353     if (largebbox) {
0354       shipclear("/MediaBox[0 0 %d %d]", info.paperwidth, info.paperheight);
0355     } else {
0356       shipclear("/MediaBox[0 0 %f %f]", pagew, pageh);
0357     }
0358     shipclear("/Contents %d 0 R>>\n", nxref + 1);
0359     shipclear("endobj\n");
0360 
0361     TRY(intarray_set(&pages, npages++, nxref));
0362 
0363     TRY(intarray_set(&xref, nxref++, outcount));
0364     shipclear("%d 0 obj\n", nxref);
0365     if (info.compress)
0366         shipclear("<</Filter/FlateDecode/Length %d 0 R>>\n", nxref + 1);
0367     else
0368         shipclear("<</Length %d 0 R>>\n", nxref + 1);
0369     shipclear("stream\n");
0370 
0371     streamofs = outcount;
0372 
0373     ship("%f %f %f %f %f %f cm\n", dxx, dxy, dyx, dyy, origx, origy);
0374         return 0;
0375 
0376        try_error:
0377         return 1;
0378 }
0379 
0380 /* Return 0 on success or 1 on error with errno set. */
0381 static int pdf_pageterm(void)
0382 {
0383     int streamlen;
0384 
0385     shipclear("");
0386 
0387     streamlen = outcount - streamofs;
0388     shipclear("endstream\nendobj\n");
0389     
0390     TRY(intarray_set(&xref, nxref++, outcount));
0391     shipclear("%d 0 obj\n%d\nendobj\n", nxref, streamlen);
0392         return 0;
0393 
0394        try_error:
0395         return 1;
0396 }
0397 
0398 int page_pdf(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo)
0399 {
0400   int r;
0401 
0402   pdf_callbacks(fout);
0403 
0404   TRY(pdf_pageinit(imginfo, 0));
0405 
0406   r = pdf_render(plist);
0407   if (r) {
0408     return r;
0409   }
0410 
0411   TRY(pdf_pageterm());
0412 
0413   fflush(fout);
0414   return 0;
0415 
0416  try_error:
0417   return 1;
0418 }
0419 
0420 int page_pdfpage(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo)
0421 {
0422   int r;
0423 
0424   pdf_callbacks(fout);
0425 
0426   TRY(pdf_pageinit(imginfo, 1));
0427 
0428   r = pdf_render(plist);
0429   if (r) {
0430     return r;
0431   }
0432 
0433   TRY(pdf_pageterm());
0434 
0435   fflush(fout);
0436   return 0;
0437 
0438  try_error:
0439   return 1;
0440 }
0441