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 #define _XOPEN_SOURCE 500
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <strings.h>
16 #include <getopt.h>
17 #include <math.h>
18
19 #include "main.h"
20 #include "potracelib.h"
21 #include "backend_pdf.h"
22 #include "backend_eps.h"
23 #include "backend_pgm.h"
24 #include "backend_svg.h"
25 #include "backend_xfig.h"
26 #include "backend_dxf.h"
27 #include "backend_geojson.h"
28 #include "potracelib.h"
29 #include "bitmap_io.h"
30 #include "bitmap.h"
31 #include "platform.h"
32 #include "auxiliary.h"
33 #include "progress_bar.h"
34 #include "trans.h"
35
36 #ifndef M_PI
37 #define M_PI 3.14159265358979323846
38 #endif
39
40 #define UNDEF ((double)(1e30)) /* a value to represent "undefined" */
41
42 struct info_s info;
43
44 /* ---------------------------------------------------------------------- */
45 /* some data structures for option processing */
46
47 struct pageformat_s {
48 const char *name;
49 int w, h;
50 };
51 typedef struct pageformat_s pageformat_t;
52
53 /* dimensions of the various page formats, in postscript points */
54 static pageformat_t pageformat[] = {
55 { "a4", 595, 842 },
56 { "a3", 842, 1191 },
57 { "a5", 421, 595 },
58 { "b5", 516, 729 },
59 { "letter", 612, 792 },
60 { "legal", 612, 1008 },
61 { "tabloid", 792, 1224 },
62 { "statement", 396, 612 },
63 { "executive", 540, 720 },
64 { "folio", 612, 936 },
65 { "quarto", 610, 780 },
66 { "10x14", 720, 1008 },
67 { NULL, 0, 0 },
68 };
69
70 struct turnpolicy_s {
71 const char *name;
72 int n;
73 };
74 typedef struct turnpolicy_s turnpolicy_t;
75
76 /* names of turn policies */
77 static turnpolicy_t turnpolicy[] = {
78 {"black", POTRACE_TURNPOLICY_BLACK},
79 {"white", POTRACE_TURNPOLICY_WHITE},
80 {"left", POTRACE_TURNPOLICY_LEFT},
81 {"right", POTRACE_TURNPOLICY_RIGHT},
82 {"minority", POTRACE_TURNPOLICY_MINORITY},
83 {"majority", POTRACE_TURNPOLICY_MAJORITY},
84 {"random", POTRACE_TURNPOLICY_RANDOM},
85 {NULL, 0},
86 };
87
88 /* backends and their characteristics */
89 struct backend_s {
90 const char *name; /* name of this backend */
91 const char *ext; /* file extension */
92 int fixed; /* fixed page size backend? */
93 int pixel; /* pixel-based backend? */
94 int multi; /* multi-page backend? */
95 int (*init_f)(FILE *fout); /* initialization function */
96 int (*page_f)(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo);
97 /* per-bitmap function */
98 int (*term_f)(FILE *fout); /* finalization function */
99 int opticurve; /* opticurve capable (true Bezier curves?) */
100 };
101 typedef struct backend_s backend_t;
102
103 static backend_t backend[] = {
104 { "eps", ".eps", 0, 0, 0, NULL, page_eps, NULL, 1 },
105 { "postscript", ".ps", 1, 0, 1, init_ps, page_ps, term_ps, 1 },
106 { "ps", ".ps", 1, 0, 1, init_ps, page_ps, term_ps, 1 },
107 { "pdf", ".pdf", 0, 0, 1, init_pdf, page_pdf, term_pdf, 1 },
108 { "pdfpage", ".pdf", 1, 0, 1, init_pdf, page_pdfpage, term_pdf, 1 },
109 { "svg", ".svg", 0, 0, 0, NULL, page_svg, NULL, 1 },
110 { "dxf", ".dxf", 0, 1, 0, NULL, page_dxf, NULL, 1 },
111 { "geojson", ".json",0, 1, 0, NULL, page_geojson, NULL, 1 },
112 { "pgm", ".pgm", 0, 1, 1, NULL, page_pgm, NULL, 1 },
113 { "gimppath", ".svg", 0, 1, 0, NULL, page_gimp, NULL, 1 },
114 { "xfig", ".fig", 1, 0, 0, NULL, page_xfig, NULL, 0 },
115 { NULL, NULL, 0, 0, 0, NULL, NULL, NULL, 0 },
116 };
117
118 /* look up a backend by name. If found, return 0 and set *bp. If not
119 found leave *bp unchanged and return 1, or 2 on ambiguous
120 prefix. */
backend_lookup(const char * name,backend_t ** bp)121 static int backend_lookup(const char *name, backend_t **bp) {
122 int i;
123 int m=0; /* prefix matches */
124 backend_t *b = NULL;
125
126 for (i=0; backend[i].name; i++) {
127 if (strcasecmp(backend[i].name, name)==0) {
128 *bp = &backend[i];
129 return 0;
130 } else if (strncasecmp(backend[i].name, name, strlen(name))==0) {
131 m++;
132 b = &backend[i];
133 }
134 }
135 /* if there was no exact match, and exactly one prefix match, use that */
136 if (m==1) {
137 *bp = b;
138 return 0;
139 } else if (m) {
140 return 2;
141 } else {
142 return 1;
143 }
144 }
145
146 /* list all available backends by name, in a comma separated list.
147 Assume the cursor starts in column j, and break lines at length
148 linelen. Do not output any trailing punctuation. Return the column
149 the cursor is in. */
backend_list(FILE * fout,int j,int linelen)150 static int backend_list(FILE *fout, int j, int linelen) {
151 int i;
152
153 for (i=0; backend[i].name; i++) {
154 if (j + (int)strlen(backend[i].name) > linelen) {
155 fprintf(fout, "\n");
156 j = 0;
157 }
158 j += fprintf(fout, "%s", backend[i].name);
159 if (backend[i+1].name) {
160 j += fprintf(fout, ", ");
161 }
162 }
163 return j;
164 }
165
166 /* ---------------------------------------------------------------------- */
167 /* some info functions */
168
license(FILE * f)169 static void license(FILE *f) {
170 fprintf(f,
171 "This program is free software; you can redistribute it and/or modify\n"
172 "it under the terms of the GNU General Public License as published by\n"
173 "the Free Software Foundation; either version 2 of the License, or\n"
174 "(at your option) any later version.\n"
175 "\n"
176 "This program is distributed in the hope that it will be useful,\n"
177 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
178 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
179 "GNU General Public License for more details.\n"
180 "\n"
181 "You should have received a copy of the GNU General Public License\n"
182 "along with this program; if not, write to the Free Software Foundation\n"
183 "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
184 );
185 }
186
show_defaults(FILE * f)187 static void show_defaults(FILE *f) {
188 fprintf(f, "Default unit: " DEFAULT_DIM_NAME "\n");
189 fprintf(f, "Default page size: " DEFAULT_PAPERFORMAT "\n");
190 }
191
usage(FILE * f)192 static void usage(FILE *f) {
193 int j;
194
195 fprintf(f, "Usage: " POTRACE " [options] [filename...]\n");
196 fprintf(f, "General options:\n");
197 fprintf(f, " -h, --help - print this help message and exit\n");
198 fprintf(f, " -v, --version - print version info and exit\n");
199 fprintf(f, " -l, --license - print license info and exit\n");
200 fprintf(f, "File selection:\n");
201 fprintf(f, " <filename> - an input file\n");
202 fprintf(f, " -o, --output <filename> - write all output to this file\n");
203 fprintf(f, " -- - end of options; 0 or more input filenames follow\n");
204 fprintf(f, "Backend selection:\n");
205 fprintf(f, " -b, --backend <name> - select backend by name\n");
206 fprintf(f, " -e, --eps - EPS backend (encapsulated PostScript) (default)\n");
207 fprintf(f, " -p, --postscript - PostScript backend\n");
208 fprintf(f, " -s, --svg - SVG backend (scalable vector graphics)\n");
209 fprintf(f, " -g, --pgm - PGM backend (portable greymap)\n");
210 fprintf(f, " -b pdf - PDF backend (portable document format)\n");
211 fprintf(f, " -b pdfpage - fixed page-size PDF backend\n");
212 fprintf(f, " -b dxf - DXF backend (drawing interchange format)\n");
213 fprintf(f, " -b geojson - GeoJSON backend\n");
214 fprintf(f, " -b gimppath - Gimppath backend (GNU Gimp)\n");
215 fprintf(f, " -b xfig - XFig backend\n");
216 fprintf(f, "Algorithm options:\n");
217 fprintf(f, " -z, --turnpolicy <policy> - how to resolve ambiguities in path decomposition\n");
218 fprintf(f, " -t, --turdsize <n> - suppress speckles of up to this size (default 2)\n");
219 fprintf(f, " -a, --alphamax <n> - corner threshold parameter (default 1)\n");
220 fprintf(f, " -n, --longcurve - turn off curve optimization\n");
221 fprintf(f, " -O, --opttolerance <n> - curve optimization tolerance (default 0.2)\n");
222 fprintf(f, " -u, --unit <n> - quantize output to 1/unit pixels (default 10)\n");
223 fprintf(f, " -d, --debug <n> - produce debugging output of type n (n=1,2,3)\n");
224 fprintf(f, "Scaling and placement options:\n");
225 fprintf(f, " -P, --pagesize <format> - page size (default is " DEFAULT_PAPERFORMAT ")\n");
226 fprintf(f, " -W, --width <dim> - width of output image\n");
227 fprintf(f, " -H, --height <dim> - height of output image\n");
228 fprintf(f, " -r, --resolution <n>[x<n>] - resolution (in dpi) (dimension-based backends)\n");
229 fprintf(f, " -x, --scale <n>[x<n>] - scaling factor (pixel-based backends)\n");
230 fprintf(f, " -S, --stretch <n> - yresolution/xresolution\n");
231 fprintf(f, " -A, --rotate <angle> - rotate counterclockwise by angle\n");
232 fprintf(f, " -M, --margin <dim> - margin\n");
233 fprintf(f, " -L, --leftmargin <dim> - left margin\n");
234 fprintf(f, " -R, --rightmargin <dim> - right margin\n");
235 fprintf(f, " -T, --topmargin <dim> - top margin\n");
236 fprintf(f, " -B, --bottommargin <dim> - bottom margin\n");
237 fprintf(f, " --tight - remove whitespace around the input image\n");
238 fprintf(f, "Color options, supported by some backends:\n");
239 fprintf(f, " -C, --color #rrggbb - set foreground color (default black)\n");
240 fprintf(f, " --fillcolor #rrggbb - set fill color (default transparent)\n");
241 fprintf(f, " --opaque - make white shapes opaque\n");
242 fprintf(f, "SVG options:\n");
243 fprintf(f, " --group - group related paths together\n");
244 fprintf(f, " --flat - whole image as a single path\n");
245 fprintf(f, "Postscript/EPS/PDF options:\n");
246 fprintf(f, " -c, --cleartext - do not compress the output\n");
247 fprintf(f, " -2, --level2 - use postscript level 2 compression (default)\n");
248 #ifdef HAVE_ZLIB
249 fprintf(f, " -3, --level3 - use postscript level 3 compression\n");
250 #endif
251 fprintf(f, " -q, --longcoding - do not optimize for file size\n");
252 fprintf(f, "PGM options:\n");
253 fprintf(f, " -G, --gamma <n> - gamma value for anti-aliasing (default 2.2)\n");
254 fprintf(f, "Frontend options:\n");
255 fprintf(f, " -k, --blacklevel <n> - black/white cutoff in input file (default 0.5)\n");
256 fprintf(f, " -i, --invert - invert bitmap\n");
257 fprintf(f, "Progress bar options:\n");
258 fprintf(f, " --progress - show progress bar\n");
259 fprintf(f, " --tty <mode> - progress bar rendering: vt100 or dumb\n");
260 fprintf(f, "\n");
261 fprintf(f, "Dimensions can have optional units, e.g. 6.5in, 15cm, 100pt.\n");
262 fprintf(f, "Default is " DEFAULT_DIM_NAME " (or pixels for pgm, dxf, and gimppath backends).\n");
263 fprintf(f, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n");
264 j = fprintf(f, "Backends are: ");
265 backend_list(f, j, 78);
266 fprintf(f, ".\n");
267 }
268
269 /* ---------------------------------------------------------------------- */
270 /* auxiliary functions for parameter parsing */
271
272 /* parse a dimension of the kind "1.5in", "7cm", etc. Return result in
273 postscript points (=1/72 in). If endptr!=NULL, store pointer to
274 next character in *endptr in the manner of strtod(3). */
parse_dimension(char * s,char ** endptr)275 static dim_t parse_dimension(char *s, char **endptr) {
276 char *p;
277 dim_t res;
278
279 res.x = strtod(s, &p);
280 res.d = 0;
281 if (p!=s) {
282 if (!strncasecmp(p, "in", 2)) {
283 res.d = DIM_IN;
284 p += 2;
285 } else if (!strncasecmp(p, "cm", 2)) {
286 res.d = DIM_CM;
287 p += 2;
288 } else if (!strncasecmp(p, "mm", 2)) {
289 res.d = DIM_MM;
290 p += 2;
291 } else if (!strncasecmp(p, "pt", 2)) {
292 res.d = DIM_PT;
293 p += 2;
294 }
295 }
296 if (endptr!=NULL) {
297 *endptr = p;
298 }
299 return res;
300 }
301
302 /* parse a pair of dimensions, such as "8.5x11in", "30mmx4cm" */
parse_dimensions(char * s,char ** endptr,dim_t * dxp,dim_t * dyp)303 static void parse_dimensions(char *s, char **endptr, dim_t *dxp, dim_t *dyp) {
304 char *p, *q;
305 dim_t dx, dy;
306
307 dx = parse_dimension(s, &p);
308 if (p==s) {
309 goto fail;
310 }
311 if (*p != 'x') {
312 goto fail;
313 }
314 p++;
315 dy = parse_dimension(p, &q);
316 if (q==p) {
317 goto fail;
318 }
319 if (dx.d && !dy.d) {
320 dy.d = dx.d;
321 } else if (!dx.d && dy.d) {
322 dx.d = dy.d;
323 }
324 *dxp = dx;
325 *dyp = dy;
326 if (endptr != NULL) {
327 *endptr = q;
328 }
329 return;
330
331 fail:
332 dx.x = dx.d = dy.x = dy.d = 0;
333 *dxp = dx;
334 *dyp = dy;
335 if (endptr != NULL) {
336 *endptr = s;
337 }
338 return;
339 }
340
double_of_dim(dim_t d,double def)341 static inline double double_of_dim(dim_t d, double def) {
342 if (d.d) {
343 return d.x * d.d;
344 } else {
345 return d.x * def;
346 }
347 }
348
parse_color(char * s)349 static int parse_color(char *s) {
350 int i, d;
351 int col = 0;
352
353 if (s[0] != '#' || strlen(s) != 7) {
354 return -1;
355 }
356 for (i=0; i<6; i++) {
357 d = s[6-i];
358 if (d >= '0' && d <= '9') {
359 col |= (d-'0') << (4*i);
360 } else if (d >= 'a' && d <= 'f') {
361 col |= (d-'a'+10) << (4*i);
362 } else if (d >= 'A' && d <= 'F') {
363 col |= (d-'A'+10) << (4*i);
364 } else {
365 return -1;
366 }
367 }
368 return col;
369 }
370
371 /* ---------------------------------------------------------------------- */
372 /* option processing */
373
374 /* codes for options that don't have short form */
375 enum {
376 OPT_TIGHT = 300,
377 OPT_FILLCOLOR,
378 OPT_OPAQUE,
379 OPT_GROUP,
380 OPT_FLAT,
381 OPT_PROGRESS,
382 OPT_TTY
383 };
384
385 static struct option longopts[] = {
386 {"help", 0, 0, 'h'},
387 {"version", 0, 0, 'v'},
388 {"show-defaults", 0, 0, 'V'}, /* undocumented option for compatibility */
389 {"license", 0, 0, 'l'},
390 {"width", 1, 0, 'W'},
391 {"height", 1, 0, 'H'},
392 {"resolution", 1, 0, 'r'},
393 {"scale", 1, 0, 'x'},
394 {"stretch", 1, 0, 'S'},
395 {"margin", 1, 0, 'M'},
396 {"leftmargin", 1, 0, 'L'},
397 {"rightmargin", 1, 0, 'R'},
398 {"topmargin", 1, 0, 'T'},
399 {"bottommargin", 1, 0, 'B'},
400 {"tight", 0, 0, OPT_TIGHT},
401 {"rotate", 1, 0, 'A'},
402 {"pagesize", 1, 0, 'P'},
403 {"turdsize", 1, 0, 't'},
404 {"unit", 1, 0, 'u'},
405 {"cleartext", 0, 0, 'c'},
406 {"level2", 0, 0, '2'},
407 {"level3", 0, 0, '3'},
408 {"eps", 0, 0, 'e'},
409 {"postscript", 0, 0, 'p'},
410 {"svg", 0, 0, 's'},
411 {"pgm", 0, 0, 'g'},
412 {"backend", 1, 0, 'b'},
413 {"debug", 1, 0, 'd'},
414 {"color", 1, 0, 'C'},
415 {"fillcolor", 1, 0, OPT_FILLCOLOR},
416 {"turnpolicy", 1, 0, 'z'},
417 {"gamma", 1, 0, 'G'},
418 {"longcurve", 0, 0, 'n'},
419 {"longcoding", 0, 0, 'q'},
420 {"alphamax", 1, 0, 'a'},
421 {"opttolerance", 1, 0, 'O'},
422 {"output", 1, 0, 'o'},
423 {"blacklevel", 1, 0, 'k'},
424 {"invert", 0, 0, 'i'},
425 {"opaque", 0, 0, OPT_OPAQUE},
426 {"group", 0, 0, OPT_GROUP},
427 {"flat", 0, 0, OPT_FLAT},
428 {"progress", 0, 0, OPT_PROGRESS},
429 {"tty", 1, 0, OPT_TTY},
430
431 {0, 0, 0, 0}
432 };
433
434 static const char *shortopts = "hvVlW:H:r:x:S:M:L:R:T:B:A:P:t:u:c23epsgb:d:C:z:G:nqa:O:o:k:i";
435
dopts(int ac,char * av[])436 static void dopts(int ac, char *av[]) {
437 int c;
438 char *p;
439 int i, j, r;
440 dim_t dim, dimx, dimy;
441 int matches, bestmatch;
442
443 /* defaults */
444 backend_lookup("eps", &info.backend);
445 info.debug = 0;
446 info.width_d.x = UNDEF;
447 info.height_d.x = UNDEF;
448 info.rx = UNDEF;
449 info.ry = UNDEF;
450 info.sx = UNDEF;
451 info.sy = UNDEF;
452 info.stretch = 1;
453 info.lmar_d.x = UNDEF;
454 info.rmar_d.x = UNDEF;
455 info.tmar_d.x = UNDEF;
456 info.bmar_d.x = UNDEF;
457 info.angle = 0;
458 info.paperwidth = DEFAULT_PAPERWIDTH;
459 info.paperheight = DEFAULT_PAPERHEIGHT;
460 info.tight = 0;
461 info.unit = 10;
462 info.compress = 1;
463 info.pslevel = 2;
464 info.color = 0x000000;
465 info.gamma = 2.2;
466 info.param = potrace_param_default();
467 if (!info.param) {
468 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno));
469 exit(2);
470 }
471 info.longcoding = 0;
472 info.outfile = NULL;
473 info.blacklevel = 0.5;
474 info.invert = 0;
475 info.opaque = 0;
476 info.grouping = 1;
477 info.fillcolor = 0xffffff;
478 info.progress = 0;
479 info.progress_bar = DEFAULT_PROGRESS_BAR;
480
481 while ((c = getopt_long(ac, av, shortopts, longopts, NULL)) != -1) {
482 switch (c) {
483 case 'h':
484 fprintf(stdout, "" POTRACE " " VERSION ". Transforms bitmaps into vector graphics.\n\n");
485 usage(stdout);
486 exit(0);
487 break;
488 case 'v':
489 case 'V':
490 fprintf(stdout, "" POTRACE " " VERSION ". Copyright (C) 2001-2017 Peter Selinger.\n");
491 fprintf(stdout, "Library version: %s\n", potrace_version());
492 show_defaults(stdout);
493 exit(0);
494 break;
495 case 'l':
496 fprintf(stdout, "" POTRACE " " VERSION ". Copyright (C) 2001-2017 Peter Selinger.\n\n");
497 license(stdout);
498 exit(0);
499 break;
500 case 'W':
501 info.width_d = parse_dimension(optarg, &p);
502 if (*p) {
503 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg);
504 exit(1);
505 }
506 break;
507 case 'H':
508 info.height_d = parse_dimension(optarg, &p);
509 if (*p) {
510 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg);
511 exit(1);
512 }
513 break;
514 case 'r':
515 parse_dimensions(optarg, &p, &dimx, &dimy);
516 if (*p == 0 && dimx.d == 0 && dimy.d == 0 && dimx.x != 0.0 && dimy.x != 0.0) {
517 info.rx = dimx.x;
518 info.ry = dimy.x;
519 break;
520 }
521 dim = parse_dimension(optarg, &p);
522 if (*p == 0 && dim.d == 0 && dim.x != 0.0) {
523 info.rx = info.ry = dim.x;
524 break;
525 }
526 fprintf(stderr, "" POTRACE ": invalid resolution -- %s\n", optarg);
527 exit(1);
528 break;
529 case 'x':
530 parse_dimensions(optarg, &p, &dimx, &dimy);
531 if (*p == 0 && dimx.d == 0 && dimy.d == 0) {
532 info.sx = dimx.x;
533 info.sy = dimy.x;
534 break;
535 }
536 dim = parse_dimension(optarg, &p);
537 if (*p == 0 && dim.d == 0) {
538 info.sx = info.sy = dim.x;
539 break;
540 }
541 fprintf(stderr, "" POTRACE ": invalid scaling factor -- %s\n", optarg);
542 exit(1);
543 break;
544 case 'S':
545 info.stretch = atof(optarg);
546 break;
547 case 'M':
548 info.lmar_d = parse_dimension(optarg, &p);
549 if (*p) {
550 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg);
551 exit(1);
552 }
553 info.rmar_d = info.tmar_d = info.bmar_d = info.lmar_d;
554 break;
555 case 'L':
556 info.lmar_d = parse_dimension(optarg, &p);
557 if (*p) {
558 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg);
559 exit(1);
560 }
561 break;
562 case 'R':
563 info.rmar_d = parse_dimension(optarg, &p);
564 if (*p) {
565 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg);
566 exit(1);
567 }
568 break;
569 case 'T':
570 info.tmar_d = parse_dimension(optarg, &p);
571 if (*p) {
572 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg);
573 exit(1);
574 }
575 break;
576 case 'B':
577 info.bmar_d = parse_dimension(optarg, &p);
578 if (*p) {
579 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg);
580 exit(1);
581 }
582 break;
583 case OPT_TIGHT:
584 info.tight = 1;
585 break;
586 case 'A':
587 info.angle = strtod(optarg, &p);
588 if (*p) {
589 fprintf(stderr, "" POTRACE ": invalid angle -- %s\n", optarg);
590 exit(1);
591 }
592 break;
593 case 'P':
594 matches = 0;
595 bestmatch = 0;
596 for (i=0; pageformat[i].name!=NULL; i++) {
597 if (strcasecmp(pageformat[i].name, optarg)==0) {
598 matches = 1;
599 bestmatch = i;
600 break;
601 } else if (strncasecmp(pageformat[i].name, optarg, strlen(optarg))==0) {
602 /* don't allow partial match on "10x14" */
603 if (optarg[0] != '1') {
604 matches++;
605 bestmatch = i;
606 }
607 }
608 }
609 if (matches == 1) {
610 info.paperwidth = pageformat[bestmatch].w;
611 info.paperheight = pageformat[bestmatch].h;
612 break;
613 }
614 parse_dimensions(optarg, &p, &dimx, &dimy);
615 if (*p == 0) {
616 info.paperwidth = (int)rint(double_of_dim(dimx, DEFAULT_DIM));
617 info.paperheight = (int)rint(double_of_dim(dimy, DEFAULT_DIM));
618 break;
619 }
620 if (matches == 0) {
621 fprintf(stderr, "" POTRACE ": unrecognized page format -- %s\n", optarg);
622 } else {
623 fprintf(stderr, "" POTRACE ": ambiguous page format -- %s\n", optarg);
624 }
625 j = fprintf(stderr, "Use one of: ");
626 for (i=0; pageformat[i].name!=NULL; i++) {
627 if (j + strlen(pageformat[i].name) > 75) {
628 fprintf(stderr, "\n");
629 j = 0;
630 }
631 j += fprintf(stderr, "%s, ", pageformat[i].name);
632 }
633 fprintf(stderr, "or specify <dim>x<dim>.\n");
634 exit(1);
635 break;
636 case 't':
637 info.param->turdsize = atoi(optarg);
638 break;
639 case 'u':
640 info.unit = strtod(optarg, &p);
641 if (*p) {
642 fprintf(stderr, "" POTRACE ": invalid unit -- %s\n", optarg);
643 exit(1);
644 }
645 break;
646 case 'c':
647 info.pslevel = 2;
648 info.compress = 0;
649 break;
650 case '2':
651 info.pslevel = 2;
652 info.compress = 1;
653 break;
654 case '3':
655 #ifdef HAVE_ZLIB
656 info.pslevel = 3;
657 info.compress = 1;
658 #else
659 fprintf(stderr, "" POTRACE ": option -3 not supported, using -2 instead.\n");
660 fflush(stderr);
661 info.pslevel = 2;
662 info.compress = 1;
663 #endif
664 break;
665 case 'e':
666 backend_lookup("eps", &info.backend);
667 break;
668 case 'p':
669 backend_lookup("postscript", &info.backend);
670 break;
671 case 's':
672 backend_lookup("svg", &info.backend);
673 break;
674 case 'g':
675 backend_lookup("pgm", &info.backend);
676 break;
677 case 'b':
678 r = backend_lookup(optarg, &info.backend);
679 if (r==1 || r==2) {
680 if (r==1) {
681 fprintf(stderr, "" POTRACE ": unrecognized backend -- %s\n", optarg);
682 } else {
683 fprintf(stderr, "" POTRACE ": ambiguous backend -- %s\n", optarg);
684 }
685 j = fprintf(stderr, "Use one of: ");
686 backend_list(stderr, j, 70);
687 fprintf(stderr, ".\n");
688 exit(1);
689 }
690 break;
691 case 'd':
692 info.debug = atoi(optarg);
693 break;
694 case 'C':
695 info.color = parse_color(optarg);
696 if (info.color == -1) {
697 fprintf(stderr, "" POTRACE ": invalid color -- %s\n", optarg);
698 exit(1);
699 }
700 break;
701 case OPT_FILLCOLOR:
702 info.fillcolor = parse_color(optarg);
703 if (info.fillcolor == -1) {
704 fprintf(stderr, "" POTRACE ": invalid color -- %s\n", optarg);
705 exit(1);
706 }
707 info.opaque = 1;
708 break;
709 case 'z':
710 matches = 0;
711 bestmatch = 0;
712 for (i=0; turnpolicy[i].name!=NULL; i++) {
713 if (strcasecmp(turnpolicy[i].name, optarg)==0) {
714 matches = 1;
715 bestmatch = i;
716 break;
717 } else if (strncasecmp(turnpolicy[i].name, optarg, strlen(optarg))==0) {
718 matches++;
719 bestmatch = i;
720 }
721 }
722 if (matches == 1) {
723 info.param->turnpolicy = turnpolicy[bestmatch].n;
724 break;
725 }
726 if (matches == 0) {
727 fprintf(stderr, "" POTRACE ": unrecognized turnpolicy -- %s\n", optarg);
728 } else {
729 fprintf(stderr, "" POTRACE ": ambiguous turnpolicy -- %s\n", optarg);
730 }
731 j = fprintf(stderr, "Use one of: ");
732 for (i=0; turnpolicy[i].name!=NULL; i++) {
733 if (j + strlen(turnpolicy[i].name) > 75) {
734 fprintf(stderr, "\n");
735 j = 0;
736 }
737 j += fprintf(stderr, "%s%s", turnpolicy[i].name, turnpolicy[i+1].name ? ", " : "");
738 }
739 fprintf(stderr, ".\n");
740 exit(1);
741 break;
742 case 'G':
743 info.gamma = atof(optarg);
744 break;
745 case 'n':
746 info.param->opticurve = 0;
747 break;
748 case 'q':
749 info.longcoding = 1;
750 break;
751 case 'a':
752 info.param->alphamax = strtod(optarg, &p);
753 if (*p) {
754 fprintf(stderr, "" POTRACE ": invalid alphamax -- %s\n", optarg);
755 exit(1);
756 }
757 break;
758 case 'O':
759 info.param->opttolerance = strtod(optarg, &p);
760 if (*p) {
761 fprintf(stderr, "" POTRACE ": invalid opttolerance -- %s\n", optarg);
762 exit(1);
763 }
764 break;
765 case 'o':
766 free(info.outfile);
767 info.outfile = strdup(optarg);
768 if (!info.outfile) {
769 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno));
770 exit(2);
771 }
772 break;
773 case 'k':
774 info.blacklevel = strtod(optarg, &p);
775 if (*p) {
776 fprintf(stderr, "" POTRACE ": invalid blacklevel -- %s\n", optarg);
777 exit(1);
778 }
779 break;
780 case 'i':
781 info.invert = 1;
782 break;
783 case OPT_OPAQUE:
784 info.opaque = 1;
785 break;
786 case OPT_GROUP:
787 info.grouping = 2;
788 break;
789 case OPT_FLAT:
790 info.grouping = 0;
791 break;
792 case OPT_PROGRESS:
793 info.progress = 1;
794 break;
795 case OPT_TTY:
796 if (strcmp(optarg, "dumb") == 0) {
797 info.progress_bar = progress_bar_simplified;
798 } else if (strcmp(optarg, "vt100") == 0) {
799 info.progress_bar = progress_bar_vt100;
800 } else {
801 fprintf(stderr, "" POTRACE ": invalid tty mode -- %s. Try --help for more info\n", optarg);
802 exit(1);
803 }
804 break;
805 case '?':
806 fprintf(stderr, "Try --help for more info\n");
807 exit(1);
808 break;
809 default:
810 fprintf(stderr, "" POTRACE ": Unimplemented option -- %c\n", c);
811 exit(1);
812 }
813 }
814 info.infiles = &av[optind];
815 info.infilecount = ac-optind;
816 info.some_infiles = info.infilecount ? 1 : 0;
817
818 /* if "--" was used, even an empty list of filenames is considered
819 "some" filenames. */
820 if (strcmp(av[optind-1], "--") == 0) {
821 info.some_infiles = 1;
822 }
823 }
824
825 /* ---------------------------------------------------------------------- */
826 /* calculations with bitmap dimensions, positioning etc */
827
828 /* determine the dimensions of the output based on command line and
829 image dimensions, and optionally, based on the actual image outline. */
calc_dimensions(imginfo_t * imginfo,potrace_path_t * plist)830 static void calc_dimensions(imginfo_t *imginfo, potrace_path_t *plist) {
831 double dim_def;
832 double maxwidth, maxheight, sc;
833 int default_scaling = 0;
834
835 /* we take care of a special case: if one of the image dimensions is
836 0, we change it to 1. Such an image is empty anyway, so there
837 will be 0 paths in it. Changing the dimensions avoids division by
838 0 error in calculating scaling factors, bounding boxes and
839 such. This doesn't quite do the right thing in all cases, but it
840 is better than causing overflow errors or "nan" output in
841 backends. Human users don't tend to process images of size 0
842 anyway; they might occur in some pipelines. */
843 if (imginfo->pixwidth == 0) {
844 imginfo->pixwidth = 1;
845 }
846 if (imginfo->pixheight == 0) {
847 imginfo->pixheight = 1;
848 }
849
850 /* set the default dimension for width, height, margins */
851 if (info.backend->pixel) {
852 dim_def = DIM_PT;
853 } else {
854 dim_def = DEFAULT_DIM;
855 }
856
857 /* apply default dimension to width, height, margins */
858 imginfo->width = info.width_d.x == UNDEF ? UNDEF : double_of_dim(info.width_d, dim_def);
859 imginfo->height = info.height_d.x == UNDEF ? UNDEF : double_of_dim(info.height_d, dim_def);
860 imginfo->lmar = info.lmar_d.x == UNDEF ? UNDEF : double_of_dim(info.lmar_d, dim_def);
861 imginfo->rmar = info.rmar_d.x == UNDEF ? UNDEF : double_of_dim(info.rmar_d, dim_def);
862 imginfo->tmar = info.tmar_d.x == UNDEF ? UNDEF : double_of_dim(info.tmar_d, dim_def);
863 imginfo->bmar = info.bmar_d.x == UNDEF ? UNDEF : double_of_dim(info.bmar_d, dim_def);
864
865 /* start with a standard rectangle */
866 trans_from_rect(&imginfo->trans, imginfo->pixwidth, imginfo->pixheight);
867
868 /* if info.tight is set, tighten the bounding box */
869 if (info.tight) {
870 trans_tighten(&imginfo->trans, plist);
871 }
872
873 /* sx/rx is just an alternate way to specify width; sy/ry is just an
874 alternate way to specify height. */
875 if (info.backend->pixel) {
876 if (imginfo->width == UNDEF && info.sx != UNDEF) {
877 imginfo->width = imginfo->trans.bb[0] * info.sx;
878 }
879 if (imginfo->height == UNDEF && info.sy != UNDEF) {
880 imginfo->height = imginfo->trans.bb[1] * info.sy;
881 }
882 } else {
883 if (imginfo->width == UNDEF && info.rx != UNDEF) {
884 imginfo->width = imginfo->trans.bb[0] / info.rx * 72;
885 }
886 if (imginfo->height == UNDEF && info.ry != UNDEF) {
887 imginfo->height = imginfo->trans.bb[1] / info.ry * 72;
888 }
889 }
890
891 /* if one of width/height is specified, use stretch to determine the
892 other */
893 if (imginfo->width == UNDEF && imginfo->height != UNDEF) {
894 imginfo->width = imginfo->height / imginfo->trans.bb[1] * imginfo->trans.bb[0] / info.stretch;
895 } else if (imginfo->width != UNDEF && imginfo->height == UNDEF) {
896 imginfo->height = imginfo->width / imginfo->trans.bb[0] * imginfo->trans.bb[1] * info.stretch;
897 }
898
899 /* if width and height are still variable, tenatively use the
900 default scaling factor of 72dpi (for dimension-based backends) or
901 1 (for pixel-based backends). For fixed-size backends, this will
902 be adjusted later to fit the page. */
903 if (imginfo->width == UNDEF && imginfo->height == UNDEF) {
904 imginfo->width = imginfo->trans.bb[0];
905 imginfo->height = imginfo->trans.bb[1] * info.stretch;
906 default_scaling = 1;
907 }
908
909 /* apply scaling */
910 trans_scale_to_size(&imginfo->trans, imginfo->width, imginfo->height);
911
912 /* apply rotation, and tighten the bounding box again, if necessary */
913 if (info.angle != 0.0) {
914 trans_rotate(&imginfo->trans, info.angle);
915 if (info.tight) {
916 trans_tighten(&imginfo->trans, plist);
917 }
918 }
919
920 /* for fixed-size backends, if default scaling was in effect,
921 further adjust the scaling to be the "best fit" for the given
922 page size and margins. */
923 if (default_scaling && info.backend->fixed) {
924
925 /* try to squeeze it between margins */
926 maxwidth = UNDEF;
927 maxheight = UNDEF;
928
929 if (imginfo->lmar != UNDEF && imginfo->rmar != UNDEF) {
930 maxwidth = info.paperwidth - imginfo->lmar - imginfo->rmar;
931 }
932 if (imginfo->bmar != UNDEF && imginfo->tmar != UNDEF) {
933 maxheight = info.paperheight - imginfo->bmar - imginfo->tmar;
934 }
935 if (maxwidth == UNDEF && maxheight == UNDEF) {
936 maxwidth = max(info.paperwidth - 144, info.paperwidth * 0.75);
937 maxheight = max(info.paperheight - 144, info.paperheight * 0.75);
938 }
939
940 if (maxwidth == UNDEF) {
941 sc = maxheight / imginfo->trans.bb[1];
942 } else if (maxheight == UNDEF) {
943 sc = maxwidth / imginfo->trans.bb[0];
944 } else {
945 sc = min(maxwidth / imginfo->trans.bb[0], maxheight / imginfo->trans.bb[1]);
946 }
947
948 /* re-scale coordinate system */
949 imginfo->width *= sc;
950 imginfo->height *= sc;
951 trans_rescale(&imginfo->trans, sc);
952 }
953
954 /* adjust margins */
955 if (info.backend->fixed) {
956 if (imginfo->lmar == UNDEF && imginfo->rmar == UNDEF) {
957 imginfo->lmar = (info.paperwidth-imginfo->trans.bb[0])/2;
958 } else if (imginfo->lmar == UNDEF) {
959 imginfo->lmar = (info.paperwidth-imginfo->trans.bb[0]-imginfo->rmar);
960 } else if (imginfo->lmar != UNDEF && imginfo->rmar != UNDEF) {
961 imginfo->lmar += (info.paperwidth-imginfo->trans.bb[0]-imginfo->lmar-imginfo->rmar)/2;
962 }
963 if (imginfo->bmar == UNDEF && imginfo->tmar == UNDEF) {
964 imginfo->bmar = (info.paperheight-imginfo->trans.bb[1])/2;
965 } else if (imginfo->bmar == UNDEF) {
966 imginfo->bmar = (info.paperheight-imginfo->trans.bb[1]-imginfo->tmar);
967 } else if (imginfo->bmar != UNDEF && imginfo->tmar != UNDEF) {
968 imginfo->bmar += (info.paperheight-imginfo->trans.bb[1]-imginfo->bmar-imginfo->tmar)/2;
969 }
970 } else {
971 if (imginfo->lmar == UNDEF) {
972 imginfo->lmar = 0;
973 }
974 if (imginfo->rmar == UNDEF) {
975 imginfo->rmar = 0;
976 }
977 if (imginfo->bmar == UNDEF) {
978 imginfo->bmar = 0;
979 }
980 if (imginfo->tmar == UNDEF) {
981 imginfo->tmar = 0;
982 }
983 }
984 }
985
986 /* ---------------------------------------------------------------------- */
987 /* auxiliary functions for file handling */
988
989 /* open a file for reading. Return stdin if filename is NULL or "-" */
my_fopen_read(const char * filename)990 static FILE *my_fopen_read(const char *filename) {
991 if (filename == NULL || strcmp(filename, "-") == 0) {
992 return stdin;
993 }
994 return fopen(filename, "rb");
995 }
996
997 /* open a file for writing. Return stdout if filename is NULL or "-" */
my_fopen_write(const char * filename)998 static FILE *my_fopen_write(const char *filename) {
999 if (filename == NULL || strcmp(filename, "-") == 0) {
1000 return stdout;
1001 }
1002 return fopen(filename, "wb");
1003 }
1004
1005 /* close a file, but do nothing is filename is NULL or "-" */
my_fclose(FILE * f,const char * filename)1006 static void my_fclose(FILE *f, const char *filename) {
1007 if (filename == NULL || strcmp(filename, "-") == 0) {
1008 return;
1009 }
1010 fclose(f);
1011 }
1012
1013 /* make output filename from input filename. Return an allocated value. */
make_outfilename(const char * infile,const char * ext)1014 static char *make_outfilename(const char *infile, const char *ext) {
1015 char *outfile;
1016 char *p;
1017
1018 if (strcmp(infile, "-") == 0) {
1019 return strdup("-");
1020 }
1021
1022 outfile = (char *) malloc(strlen(infile)+strlen(ext)+5);
1023 if (!outfile) {
1024 return NULL;
1025 }
1026 strcpy(outfile, infile);
1027 p = strrchr(outfile, '.');
1028 if (p) {
1029 *p = 0;
1030 }
1031 strcat(outfile, ext);
1032
1033 /* check that input and output filenames are different */
1034 if (strcmp(infile, outfile) == 0) {
1035 strcpy(outfile, infile);
1036 strcat(outfile, "-out");
1037 }
1038
1039 return outfile;
1040 }
1041
1042 /* ---------------------------------------------------------------------- */
1043 /* Process one infile */
1044
1045 /* Process one or more bitmaps from fin, and write the results to fout
1046 using the page_f function of the appropriate backend. */
1047
process_file(backend_t * b,const char * infile,const char * outfile,FILE * fin,FILE * fout)1048 static void process_file(backend_t *b, const char *infile, const char *outfile, FILE *fin, FILE *fout) {
1049 int r;
1050 potrace_bitmap_t *bm = NULL;
1051 imginfo_t imginfo;
1052 int eof_flag = 0; /* to indicate premature eof */
1053 int count; /* number of bitmaps successfully processed, this file */
1054 potrace_state_t *st;
1055
1056 for (count=0; ; count++) {
1057 /* read a bitmap */
1058 r = bm_read(fin, info.blacklevel, &bm);
1059 switch (r) {
1060 case -1: /* system error */
1061 fprintf(stderr, "" POTRACE ": %s: %s\n", infile, strerror(errno));
1062 exit(2);
1063 case -2: /* corrupt file format */
1064 fprintf(stderr, "" POTRACE ": %s: file format error: %s\n", infile, bm_read_error);
1065 exit(2);
1066 case -3: /* empty file */
1067 if (count>0) { /* end of file */
1068 return;
1069 }
1070 fprintf(stderr, "" POTRACE ": %s: empty file\n", infile);
1071 exit(2);
1072 case -4: /* wrong magic */
1073 if (count>0) {
1074 fprintf(stderr, "" POTRACE ": %s: warning: junk at end of file\n", infile);
1075 return;
1076 }
1077 fprintf(stderr, "" POTRACE ": %s: file format not recognized\n", infile);
1078 fprintf(stderr, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n");
1079 exit(2);
1080 case 1: /* unexpected end of file */
1081 fprintf(stderr, "" POTRACE ": warning: %s: premature end of file\n", infile);
1082 eof_flag = 1;
1083 break;
1084 }
1085
1086 /* prepare progress bar, if requested */
1087 if (info.progress) {
1088 r = info.progress_bar->init(&info.param->progress, infile, count);
1089 if (r) {
1090 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno));
1091 exit(2);
1092 }
1093 } else {
1094 info.param->progress.callback = NULL;
1095 }
1096
1097 if (info.invert) {
1098 bm_invert(bm);
1099 }
1100
1101 /* process the image */
1102 st = potrace_trace(info.param, bm);
1103 if (!st || st->status != POTRACE_STATUS_OK) {
1104 fprintf(stderr, "" POTRACE ": %s: %s\n", infile, strerror(errno));
1105 exit(2);
1106 }
1107
1108 /* calculate image dimensions */
1109 imginfo.pixwidth = bm->w;
1110 imginfo.pixheight = bm->h;
1111 bm_free(bm);
1112
1113 calc_dimensions(&imginfo, st->plist);
1114
1115 r = b->page_f(fout, st->plist, &imginfo);
1116 if (r) {
1117 fprintf(stderr, "" POTRACE ": %s: %s\n", outfile, strerror(errno));
1118 exit(2);
1119 }
1120
1121 potrace_state_free(st);
1122
1123 if (info.progress) {
1124 info.progress_bar->term(&info.param->progress);
1125 }
1126
1127 if (eof_flag || !b->multi) {
1128 return;
1129 }
1130 }
1131 /* not reached */
1132 }
1133
1134 /* ---------------------------------------------------------------------- */
1135 /* main: handle file i/o */
1136
1137 #define TRY(x) if (x) goto try_error
1138
main(int ac,char * av[])1139 int main(int ac, char *av[]) {
1140 backend_t *b; /* backend info */
1141 FILE *fin, *fout;
1142 int i;
1143 char *outfile;
1144
1145 /* platform-specific initializations, e.g., set file i/o to binary */
1146 platform_init();
1147
1148 /* process options */
1149 dopts(ac, av);
1150
1151 b = info.backend;
1152 if (b==NULL) {
1153 fprintf(stderr, "" POTRACE ": internal error: selected backend not found\n");
1154 exit(1);
1155 }
1156
1157 /* fix some parameters */
1158 /* if backend cannot handle opticurve, disable it */
1159 if (b->opticurve == 0) {
1160 info.param->opticurve = 0;
1161 }
1162
1163 /* there are several ways to call us:
1164 potrace -- stdin to stdout
1165 potrace -o outfile -- stdin to outfile
1166 potrace file... -- encode each file and generate outfile names
1167 potrace -o outfile file... -- concatenate files and write to outfile
1168
1169 The latter form is only allowed one file for single-page
1170 backends. For multi-page backends, each file must contain 0 or
1171 more complete bitmaps.
1172 */
1173
1174 if (!info.some_infiles) { /* read from stdin */
1175
1176 fout = my_fopen_write(info.outfile);
1177 if (!fout) {
1178 fprintf(stderr, "" POTRACE ": %s: %s\n", info.outfile ? info.outfile : "stdout", strerror(errno));
1179 exit(2);
1180 }
1181 if (b->init_f) {
1182 TRY(b->init_f(fout));
1183 }
1184 process_file(b, "stdin", info.outfile ? info.outfile : "stdout", stdin, fout);
1185 if (b->term_f) {
1186 TRY(b->term_f(fout));
1187 }
1188 my_fclose(fout, info.outfile);
1189 free(info.outfile);
1190 potrace_param_free(info.param);
1191 return 0;
1192
1193 } else if (!info.outfile) { /* infiles -> multiple outfiles */
1194
1195 for (i=0; i<info.infilecount; i++) {
1196 outfile = make_outfilename(info.infiles[i], b->ext);
1197 if (!outfile) {
1198 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno));
1199 exit(2);
1200 }
1201 fin = my_fopen_read(info.infiles[i]);
1202 if (!fin) {
1203 fprintf(stderr, "" POTRACE ": %s: %s\n", info.infiles[i], strerror(errno));
1204 exit(2);
1205 }
1206 fout = my_fopen_write(outfile);
1207 if (!fout) {
1208 fprintf(stderr, "" POTRACE ": %s: %s\n", outfile, strerror(errno));
1209 exit(2);
1210 }
1211 if (b->init_f) {
1212 TRY(b->init_f(fout));
1213 }
1214 process_file(b, info.infiles[i], outfile, fin, fout);
1215 if (b->term_f) {
1216 TRY(b->term_f(fout));
1217 }
1218 my_fclose(fin, info.infiles[i]);
1219 my_fclose(fout, outfile);
1220 free(outfile);
1221 }
1222 potrace_param_free(info.param);
1223 return 0;
1224
1225 } else { /* infiles to single outfile */
1226
1227 if (!b->multi && info.infilecount >= 2) {
1228 fprintf(stderr, "" POTRACE ": cannot use multiple input files with -o in %s mode\n", b->name);
1229 exit(1);
1230 }
1231 if (info.infilecount == 0) {
1232 fprintf(stderr, "" POTRACE ": cannot use empty list of input files with -o\n");
1233 exit(1);
1234 }
1235
1236 fout = my_fopen_write(info.outfile);
1237 if (!fout) {
1238 fprintf(stderr, "" POTRACE ": %s: %s\n", info.outfile, strerror(errno));
1239 exit(2);
1240 }
1241 if (b->init_f) {
1242 TRY(b->init_f(fout));
1243 }
1244 for (i=0; i<info.infilecount; i++) {
1245 fin = my_fopen_read(info.infiles[i]);
1246 if (!fin) {
1247 fprintf(stderr, "" POTRACE ": %s: %s\n", info.infiles[i], strerror(errno));
1248 exit(2);
1249 }
1250 process_file(b, info.infiles[i], info.outfile, fin, fout);
1251 my_fclose(fin, info.infiles[i]);
1252 }
1253 if (b->term_f) {
1254 TRY(b->term_f(fout));
1255 }
1256 my_fclose(fout, info.outfile);
1257 free(info.outfile);
1258 potrace_param_free(info.param);
1259 return 0;
1260
1261 }
1262
1263 /* not reached */
1264
1265 try_error:
1266 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno));
1267 exit(2);
1268 }
1269