xref: /potrace-1.14/src/backend_pdf.c (revision b3fce824)
1 /* Copyright (C) 2001-2017 Peter Selinger.
2    This file is part of Potrace. It is free software and it is covered
3    by the GNU General Public License. See the file COPYING for details. */
4 
5 
6 /* The PDF backend of Potrace. Stream compression is optionally
7 	supplied via the functions in flate.c. */
8 
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12 
13 #include <stdio.h>
14 #include <stdarg.h>
15 #include <string.h>
16 #include <math.h>
17 #include <stdlib.h>
18 
19 #include "main.h"
20 #include "backend_pdf.h"
21 #include "flate.h"
22 #include "lists.h"
23 #include "potracelib.h"
24 #include "auxiliary.h"
25 
26 typedef int color_t;
27 
28 #define TRY(x) if (x) goto try_error
29 
30 /* ---------------------------------------------------------------------- */
31 /* auxiliary: growing arrays */
32 
33 struct intarray_s {
34   int size;
35   int *data;
36 };
37 typedef struct intarray_s intarray_t;
38 
intarray_init(intarray_t * ar)39 static inline void intarray_init(intarray_t *ar) {
40   ar->size = 0;
41   ar->data = NULL;
42 }
43 
intarray_term(intarray_t * ar)44 static inline void intarray_term(intarray_t *ar) {
45   free(ar->data);
46   ar->size = 0;
47   ar->data = NULL;
48 }
49 
50 /* Set ar[n]=val. Expects n>=0. Grows array if necessary. Return 0 on
51    success and -1 on error with errno set. */
intarray_set(intarray_t * ar,int n,int val)52 static inline int intarray_set(intarray_t *ar, int n, int val) {
53   int *p;
54   int s;
55 
56   if (n >= ar->size) {
57     s = n+1024;
58     p = (int *)realloc(ar->data, s * sizeof(int));
59     if (!p) {
60       return -1;
61     }
62     ar->data = p;
63     ar->size = s;
64   }
65   ar->data[n] = val;
66   return 0;
67 }
68 
69 /* ---------------------------------------------------------------------- */
70 /* some global variables */
71 
72 static intarray_t xref;
73 static int nxref = 0;
74 static intarray_t pages;
75 static int npages;
76 static int streamofs;
77 static size_t outcount;  /* output file position */
78 
79 /* ---------------------------------------------------------------------- */
80 /* functions for interfacing with compression backend */
81 
82 /* xship: callback function that must be initialized before calling
83    any other functions of the "ship" family. xship_file must be
84    initialized too. */
85 
86 /* print the token to f, but filtered through a compression
87    filter in case filter!=0 */
88 static int (*xship)(FILE *f, int filter, const char *s, int len);
89 static FILE *xship_file;
90 
91 /* ship PDF code, filtered */
ship(const char * fmt,...)92 static int ship(const char *fmt, ...) {
93   va_list args;
94   static char buf[4096]; /* static string limit is okay here because
95 			    we only use constant format strings - for
96 			    the same reason, it is okay to use
97 			    vsprintf instead of vsnprintf below. */
98   va_start(args, fmt);
99   vsprintf(buf, fmt, args);
100   buf[4095] = 0;
101   va_end(args);
102 
103   outcount += xship(xship_file, 1, buf, strlen(buf));
104   return 0;
105 }
106 
107 /* ship PDF code, unfiltered */
shipclear(const char * fmt,...)108 static int shipclear(const char *fmt, ...) {
109   static char buf[4096];
110   va_list args;
111 
112   va_start(args, fmt);
113   vsprintf(buf, fmt, args);
114   buf[4095] = 0;
115   va_end(args);
116 
117   outcount += xship(xship_file, 0, buf, strlen(buf));
118   return 0;
119 }
120 
121 /* set all callback functions */
pdf_callbacks(FILE * fout)122 static void pdf_callbacks(FILE *fout) {
123 
124   if (info.compress) {
125     xship = pdf_xship;
126   } else {
127     xship = dummy_xship;
128   }
129   xship_file = fout;
130 }
131 
132 /* ---------------------------------------------------------------------- */
133 /* PDF path-drawing auxiliary functions */
134 
135 /* coordinate quantization */
unit(dpoint_t p)136 static inline point_t unit(dpoint_t p) {
137   point_t q;
138 
139   q.x = (long)(floor(p.x*info.unit+.5));
140   q.y = (long)(floor(p.y*info.unit+.5));
141   return q;
142 }
143 
pdf_coords(dpoint_t p)144 static void pdf_coords(dpoint_t p) {
145   point_t cur = unit(p);
146   ship("%ld %ld ", cur.x, cur.y);
147 }
148 
pdf_moveto(dpoint_t p)149 static void pdf_moveto(dpoint_t p) {
150   pdf_coords(p);
151   ship("m\n");
152 }
153 
pdf_lineto(dpoint_t p)154 static void pdf_lineto(dpoint_t p) {
155   pdf_coords(p);
156   ship("l\n");
157 }
158 
pdf_curveto(dpoint_t p1,dpoint_t p2,dpoint_t p3)159 static void pdf_curveto(dpoint_t p1, dpoint_t p2, dpoint_t p3) {
160   point_t q1, q2, q3;
161 
162   q1 = unit(p1);
163   q2 = unit(p2);
164   q3 = unit(p3);
165 
166   ship("%ld %ld %ld %ld %ld %ld c\n", q1.x, q1.y, q2.x, q2.y, q3.x, q3.y);
167 }
168 
169 /* this procedure returns a statically allocated string */
pdf_colorstring(const color_t col)170 static const char *pdf_colorstring(const color_t col) {
171   double r, g, b;
172   static char buf[100];
173 
174   r = (col & 0xff0000) >> 16;
175   g = (col & 0x00ff00) >> 8;
176   b = (col & 0x0000ff) >> 0;
177 
178   if (r==0 && g==0 && b==0) {
179     return "0 g";
180   } else if (r==255 && g==255 && b==255) {
181     return "1 g";
182   } else if (r == g && g == b) {
183     sprintf(buf, "%.3f g", r/255.0);
184     return buf;
185   } else {
186     sprintf(buf, "%.3f %.3f %.3f rg", r/255.0, g/255.0, b/255.0);
187     return buf;
188   }
189 }
190 
191 static color_t pdf_color = -1;
192 
pdf_setcolor(const color_t col)193 static void pdf_setcolor(const color_t col) {
194   if (col == pdf_color) {
195     return;
196   }
197   pdf_color = col;
198 
199   ship("%s\n", pdf_colorstring(col));
200 }
201 
202 /* explicit encoding, does not use special macros */
pdf_path(potrace_curve_t * curve)203 static int pdf_path(potrace_curve_t *curve) {
204   int i;
205   dpoint_t *c;
206   int m = curve->n;
207 
208   c = curve->c[m-1];
209   pdf_moveto(c[2]);
210 
211   for (i=0; i<m; i++) {
212     c = curve->c[i];
213     switch (curve->tag[i]) {
214     case POTRACE_CORNER:
215       pdf_lineto(c[1]);
216       pdf_lineto(c[2]);
217       break;
218     case POTRACE_CURVETO:
219       pdf_curveto(c[0], c[1], c[2]);
220       break;
221     }
222   }
223   return 0;
224 }
225 
226 /* ---------------------------------------------------------------------- */
227 /* Backends for various types of output. */
228 
229 /* Normal output: black on transparent */
render0(potrace_path_t * plist)230 static int render0(potrace_path_t *plist) {
231   potrace_path_t *p;
232 
233   pdf_setcolor(info.color);
234   list_forall (p, plist) {
235     pdf_path(&p->curve);
236     ship("h\n");
237     if (p->next == NULL || p->next->sign == '+') {
238       ship("f\n");
239     }
240   }
241   return 0;
242 }
243 
244 /* Opaque output: alternating black and white */
render0_opaque(potrace_path_t * plist)245 static int render0_opaque(potrace_path_t *plist) {
246   potrace_path_t *p;
247 
248   list_forall (p, plist) {
249     pdf_path(&p->curve);
250     ship("h\n");
251     pdf_setcolor(p->sign=='+' ? info.color : info.fillcolor);
252     ship("f\n");
253   }
254   return 0;
255 }
256 
257 /* select the appropriate rendering function from above */
pdf_render(potrace_path_t * plist)258 static int pdf_render(potrace_path_t *plist)
259 {
260   if (info.opaque) {
261     return render0_opaque(plist);
262   }
263   return render0(plist);
264 }
265 
266 /* ---------------------------------------------------------------------- */
267 /* PDF header and footer */
268 
init_pdf(FILE * fout)269 int init_pdf(FILE *fout)
270 {
271         intarray_init(&xref);
272 	intarray_init(&pages);
273 	nxref = 0;
274 	npages = 0;
275 
276 	/* set callback functions for shipping routines */
277 	pdf_callbacks(fout);
278 	outcount = 0;
279 
280 	shipclear("%%PDF-1.3\n");
281 
282 	TRY(intarray_set(&xref, nxref++, outcount));
283 	shipclear("1 0 obj\n<</Type/Catalog/Pages 3 0 R>>\nendobj\n");
284 
285 	TRY(intarray_set(&xref, nxref++, outcount));
286 	shipclear("2 0 obj\n"
287 		"<</Creator"
288 		"(" POTRACE " " VERSION ", written by Peter Selinger 2001-2017)>>\n"
289 		"endobj\n");
290 
291 	/* delay obj #3 (pages) until end */
292 	nxref++;
293 
294 	fflush(fout);
295 	return 0;
296 
297  try_error:
298         return 1;
299 }
300 
term_pdf(FILE * fout)301 int term_pdf(FILE *fout)
302 {
303 	int startxref;
304 	int i;
305 
306 	pdf_callbacks(fout);
307 
308 	TRY(intarray_set(&xref, 2, outcount));
309 	shipclear("3 0 obj\n"
310 		"<</Type/Pages/Count %d/Kids[\n", npages);
311 	for (i = 0; i < npages; i++)
312 		shipclear("%d 0 R\n", pages.data[i]);
313 	shipclear("]>>\nendobj\n");
314 
315 	startxref = outcount;
316 
317 	shipclear("xref\n0 %d\n", nxref + 1);
318 	shipclear("0000000000 65535 f \n");
319 	for (i = 0; i < nxref; i++)
320 		shipclear("%0.10d 00000 n \n", xref.data[i]);
321 
322 	shipclear("trailer\n<</Size %d/Root 1 0 R/Info 2 0 R>>\n", nxref + 1);
323 	shipclear("startxref\n%d\n%%%%EOF\n", startxref);
324 
325 	fflush(fout);
326 	intarray_term(&xref);
327 	intarray_term(&pages);
328 	return 0;
329 
330  try_error:
331         return 1;
332 }
333 
334 /* if largebbox is set, set bounding box to pagesize. Return 0 on
335    success or 1 on error with errno set. */
pdf_pageinit(imginfo_t * imginfo,int largebbox)336 static int pdf_pageinit(imginfo_t *imginfo, int largebbox)
337 {
338 	double origx = imginfo->trans.orig[0] + imginfo->lmar;
339 	double origy = imginfo->trans.orig[1] + imginfo->bmar;
340 	double dxx = imginfo->trans.x[0] / info.unit;
341 	double dxy = imginfo->trans.x[1] / info.unit;
342 	double dyx = imginfo->trans.y[0] / info.unit;
343 	double dyy = imginfo->trans.y[1] / info.unit;
344 
345 	double pagew = imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar;
346 	double pageh = imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar;
347 
348 	pdf_color = -1;
349 
350 	TRY(intarray_set(&xref, nxref++, outcount));
351 	shipclear("%d 0 obj\n", nxref);
352 	shipclear("<</Type/Page/Parent 3 0 R/Resources<</ProcSet[/PDF]>>");
353 	if (largebbox) {
354 	  shipclear("/MediaBox[0 0 %d %d]", info.paperwidth, info.paperheight);
355 	} else {
356 	  shipclear("/MediaBox[0 0 %f %f]", pagew, pageh);
357 	}
358 	shipclear("/Contents %d 0 R>>\n", nxref + 1);
359 	shipclear("endobj\n");
360 
361 	TRY(intarray_set(&pages, npages++, nxref));
362 
363 	TRY(intarray_set(&xref, nxref++, outcount));
364 	shipclear("%d 0 obj\n", nxref);
365 	if (info.compress)
366 		shipclear("<</Filter/FlateDecode/Length %d 0 R>>\n", nxref + 1);
367 	else
368 		shipclear("<</Length %d 0 R>>\n", nxref + 1);
369 	shipclear("stream\n");
370 
371 	streamofs = outcount;
372 
373 	ship("%f %f %f %f %f %f cm\n", dxx, dxy, dyx, dyy, origx, origy);
374         return 0;
375 
376        try_error:
377         return 1;
378 }
379 
380 /* Return 0 on success or 1 on error with errno set. */
pdf_pageterm(void)381 static int pdf_pageterm(void)
382 {
383 	int streamlen;
384 
385 	shipclear("");
386 
387 	streamlen = outcount - streamofs;
388 	shipclear("endstream\nendobj\n");
389 
390 	TRY(intarray_set(&xref, nxref++, outcount));
391 	shipclear("%d 0 obj\n%d\nendobj\n", nxref, streamlen);
392         return 0;
393 
394        try_error:
395         return 1;
396 }
397 
page_pdf(FILE * fout,potrace_path_t * plist,imginfo_t * imginfo)398 int page_pdf(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo)
399 {
400   int r;
401 
402   pdf_callbacks(fout);
403 
404   TRY(pdf_pageinit(imginfo, 0));
405 
406   r = pdf_render(plist);
407   if (r) {
408     return r;
409   }
410 
411   TRY(pdf_pageterm());
412 
413   fflush(fout);
414   return 0;
415 
416  try_error:
417   return 1;
418 }
419 
page_pdfpage(FILE * fout,potrace_path_t * plist,imginfo_t * imginfo)420 int page_pdfpage(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo)
421 {
422   int r;
423 
424   pdf_callbacks(fout);
425 
426   TRY(pdf_pageinit(imginfo, 1));
427 
428   r = pdf_render(plist);
429   if (r) {
430     return r;
431   }
432 
433   TRY(pdf_pageterm());
434 
435   fflush(fout);
436   return 0;
437 
438  try_error:
439   return 1;
440 }
441 
442