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