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