File indexing completed on 2024-12-22 04:04:13
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 #ifdef HAVE_CONFIG_H 0006 #include <config.h> 0007 #endif 0008 0009 #include <stdio.h> 0010 #include <stdlib.h> 0011 #include <errno.h> 0012 #include <string.h> 0013 #include <strings.h> 0014 #include <getopt.h> 0015 #include <math.h> 0016 0017 #include "main.h" 0018 #include "potracelib.h" 0019 #include "backend_pdf.h" 0020 #include "backend_eps.h" 0021 #include "backend_pgm.h" 0022 #include "backend_svg.h" 0023 #include "backend_xfig.h" 0024 #include "backend_dxf.h" 0025 #include "backend_geojson.h" 0026 #include "potracelib.h" 0027 #include "bitmap_io.h" 0028 #include "bitmap.h" 0029 #include "platform.h" 0030 #include "auxiliary.h" 0031 #include "progress_bar.h" 0032 #include "trans.h" 0033 0034 #ifndef M_PI 0035 #define M_PI 3.14159265358979323846 0036 #endif 0037 0038 #define UNDEF ((double)(1e30)) /* a value to represent "undefined" */ 0039 0040 struct info_s info; 0041 0042 /* ---------------------------------------------------------------------- */ 0043 /* some data structures for option processing */ 0044 0045 struct pageformat_s { 0046 const char *name; 0047 int w, h; 0048 }; 0049 typedef struct pageformat_s pageformat_t; 0050 0051 /* dimensions of the various page formats, in postscript points */ 0052 static pageformat_t pageformat[] = { 0053 { "a4", 595, 842 }, 0054 { "a3", 842, 1191 }, 0055 { "a5", 421, 595 }, 0056 { "b5", 516, 729 }, 0057 { "letter", 612, 792 }, 0058 { "legal", 612, 1008 }, 0059 { "tabloid", 792, 1224 }, 0060 { "statement", 396, 612 }, 0061 { "executive", 540, 720 }, 0062 { "folio", 612, 936 }, 0063 { "quarto", 610, 780 }, 0064 { "10x14", 720, 1008 }, 0065 { NULL, 0, 0 }, 0066 }; 0067 0068 struct turnpolicy_s { 0069 const char *name; 0070 int n; 0071 }; 0072 typedef struct turnpolicy_s turnpolicy_t; 0073 0074 /* names of turn policies */ 0075 static turnpolicy_t turnpolicy[] = { 0076 {"black", POTRACE_TURNPOLICY_BLACK}, 0077 {"white", POTRACE_TURNPOLICY_WHITE}, 0078 {"left", POTRACE_TURNPOLICY_LEFT}, 0079 {"right", POTRACE_TURNPOLICY_RIGHT}, 0080 {"minority", POTRACE_TURNPOLICY_MINORITY}, 0081 {"majority", POTRACE_TURNPOLICY_MAJORITY}, 0082 {"random", POTRACE_TURNPOLICY_RANDOM}, 0083 {NULL, 0}, 0084 }; 0085 0086 /* backends and their characteristics */ 0087 struct backend_s { 0088 const char *name; /* name of this backend */ 0089 const char *ext; /* file extension */ 0090 int fixed; /* fixed page size backend? */ 0091 int pixel; /* pixel-based backend? */ 0092 int multi; /* multi-page backend? */ 0093 int (*init_f)(FILE *fout); /* initialization function */ 0094 int (*page_f)(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo); 0095 /* per-bitmap function */ 0096 int (*term_f)(FILE *fout); /* finalization function */ 0097 int opticurve; /* opticurve capable (true Bezier curves?) */ 0098 }; 0099 typedef struct backend_s backend_t; 0100 0101 static backend_t backend[] = { 0102 { "svg", ".svg", 0, 0, 0, NULL, page_svg, NULL, 1 }, 0103 { "pdf", ".pdf", 0, 0, 1, init_pdf, page_pdf, term_pdf, 1 }, 0104 { "pdfpage", ".pdf", 1, 0, 1, init_pdf, page_pdfpage, term_pdf, 1 }, 0105 { "eps", ".eps", 0, 0, 0, NULL, page_eps, NULL, 1 }, 0106 { "postscript", ".ps", 1, 0, 1, init_ps, page_ps, term_ps, 1 }, 0107 { "ps", ".ps", 1, 0, 1, init_ps, page_ps, term_ps, 1 }, 0108 { "dxf", ".dxf", 0, 1, 0, NULL, page_dxf, NULL, 1 }, 0109 { "geojson", ".json",0, 1, 0, NULL, page_geojson, NULL, 1 }, 0110 { "pgm", ".pgm", 0, 1, 1, NULL, page_pgm, NULL, 1 }, 0111 { "gimppath", ".svg", 0, 1, 0, NULL, page_gimp, NULL, 1 }, 0112 { "xfig", ".fig", 1, 0, 0, NULL, page_xfig, NULL, 0 }, 0113 { NULL, NULL, 0, 0, 0, NULL, NULL, NULL, 0 }, 0114 }; 0115 0116 /* look up a backend by name. If found, return 0 and set *bp. If not 0117 found leave *bp unchanged and return 1, or 2 on ambiguous 0118 prefix. */ 0119 static int backend_lookup(const char *name, backend_t **bp) { 0120 int i; 0121 int m=0; /* prefix matches */ 0122 backend_t *b = NULL; 0123 0124 for (i=0; backend[i].name; i++) { 0125 if (strcasecmp(backend[i].name, name)==0) { 0126 *bp = &backend[i]; 0127 return 0; 0128 } else if (strncasecmp(backend[i].name, name, strlen(name))==0) { 0129 m++; 0130 b = &backend[i]; 0131 } 0132 } 0133 /* if there was no exact match, and exactly one prefix match, use that */ 0134 if (m==1) { 0135 *bp = b; 0136 return 0; 0137 } else if (m) { 0138 return 2; 0139 } else { 0140 return 1; 0141 } 0142 } 0143 0144 /* list all available backends by name, in a comma separated list. 0145 Assume the cursor starts in column j, and break lines at length 0146 linelen. Do not output any trailing punctuation. Return the column 0147 the cursor is in. */ 0148 static int backend_list(FILE *fout, int j, int linelen) { 0149 int i; 0150 0151 for (i=0; backend[i].name; i++) { 0152 if (j + (int)strlen(backend[i].name) > linelen) { 0153 fprintf(fout, "\n"); 0154 j = 0; 0155 } 0156 j += fprintf(fout, "%s", backend[i].name); 0157 if (backend[i+1].name) { 0158 j += fprintf(fout, ", "); 0159 } 0160 } 0161 return j; 0162 } 0163 0164 /* ---------------------------------------------------------------------- */ 0165 /* some info functions */ 0166 0167 static void license(FILE *f) { 0168 fprintf(f, 0169 "This program is free software; you can redistribute it and/or modify\n" 0170 "it under the terms of the GNU General Public License as published by\n" 0171 "the Free Software Foundation; either version 2 of the License, or\n" 0172 "(at your option) any later version.\n" 0173 "\n" 0174 "This program is distributed in the hope that it will be useful,\n" 0175 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" 0176 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" 0177 "GNU General Public License for more details.\n" 0178 "\n" 0179 "You should have received a copy of the GNU General Public License\n" 0180 "along with this program; if not, write to the Free Software Foundation\n" 0181 "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" 0182 ); 0183 } 0184 0185 static void show_defaults(FILE *f) { 0186 fprintf(f, "Default unit: " DEFAULT_DIM_NAME "\n"); 0187 fprintf(f, "Default page size: " DEFAULT_PAPERFORMAT "\n"); 0188 } 0189 0190 static void usage(FILE *f) { 0191 int j; 0192 0193 fprintf(f, "Usage: " POTRACE " [options] [filename...]\n"); 0194 fprintf(f, "General options:\n"); 0195 fprintf(f, " -h, --help - print this help message and exit\n"); 0196 fprintf(f, " -v, --version - print version info and exit\n"); 0197 fprintf(f, " -l, --license - print license info and exit\n"); 0198 fprintf(f, "File selection:\n"); 0199 fprintf(f, " <filename> - an input file\n"); 0200 fprintf(f, " -o, --output <filename> - write all output to this file\n"); 0201 fprintf(f, " -- - end of options; 0 or more input filenames follow\n"); 0202 fprintf(f, "Backend selection:\n"); 0203 fprintf(f, " -b, --backend <name> - select backend by name\n"); 0204 fprintf(f, " -b svg, -s, --svg - SVG backend (scalable vector graphics)\n"); 0205 fprintf(f, " -b pdf - PDF backend (portable document format)\n"); 0206 fprintf(f, " -b pdfpage - fixed page-size PDF backend\n"); 0207 fprintf(f, " -b eps, -e, --eps - EPS backend (encapsulated PostScript) (default)\n"); 0208 fprintf(f, " -b ps, -p, --postscript - PostScript backend\n"); 0209 fprintf(f, " -b pgm, -g, --pgm - PGM backend (portable greymap)\n"); 0210 fprintf(f, " -b dxf - DXF backend (drawing interchange format)\n"); 0211 fprintf(f, " -b geojson - GeoJSON backend\n"); 0212 fprintf(f, " -b gimppath - Gimppath backend (GNU Gimp)\n"); 0213 fprintf(f, " -b xfig - XFig backend\n"); 0214 fprintf(f, "Algorithm options:\n"); 0215 fprintf(f, " -z, --turnpolicy <policy> - how to resolve ambiguities in path decomposition\n"); 0216 fprintf(f, " -t, --turdsize <n> - suppress speckles of up to this size (default 2)\n"); 0217 fprintf(f, " -a, --alphamax <n> - corner threshold parameter (default 1)\n"); 0218 fprintf(f, " -n, --longcurve - turn off curve optimization\n"); 0219 fprintf(f, " -O, --opttolerance <n> - curve optimization tolerance (default 0.2)\n"); 0220 fprintf(f, " -u, --unit <n> - quantize output to 1/unit pixels (default 10)\n"); 0221 fprintf(f, " -d, --debug <n> - produce debugging output of type n (n=1,2,3)\n"); 0222 fprintf(f, "Scaling and placement options:\n"); 0223 fprintf(f, " -P, --pagesize <format> - page size (default is " DEFAULT_PAPERFORMAT ")\n"); 0224 fprintf(f, " -W, --width <dim> - width of output image\n"); 0225 fprintf(f, " -H, --height <dim> - height of output image\n"); 0226 fprintf(f, " -r, --resolution <n>[x<n>] - resolution (in dpi) (dimension-based backends)\n"); 0227 fprintf(f, " -x, --scale <n>[x<n>] - scaling factor (pixel-based backends)\n"); 0228 fprintf(f, " -S, --stretch <n> - yresolution/xresolution\n"); 0229 fprintf(f, " -A, --rotate <angle> - rotate counterclockwise by angle\n"); 0230 fprintf(f, " -M, --margin <dim> - margin\n"); 0231 fprintf(f, " -L, --leftmargin <dim> - left margin\n"); 0232 fprintf(f, " -R, --rightmargin <dim> - right margin\n"); 0233 fprintf(f, " -T, --topmargin <dim> - top margin\n"); 0234 fprintf(f, " -B, --bottommargin <dim> - bottom margin\n"); 0235 fprintf(f, " --tight - remove whitespace around the input image\n"); 0236 fprintf(f, "Color options, supported by some backends:\n"); 0237 fprintf(f, " -C, --color #rrggbb - set foreground color (default black)\n"); 0238 fprintf(f, " --fillcolor #rrggbb - set fill color (default transparent)\n"); 0239 fprintf(f, " --opaque - make white shapes opaque\n"); 0240 fprintf(f, "SVG options:\n"); 0241 fprintf(f, " --group - group related paths together\n"); 0242 fprintf(f, " --flat - whole image as a single path\n"); 0243 fprintf(f, "Postscript/EPS/PDF options:\n"); 0244 fprintf(f, " -c, --cleartext - do not compress the output\n"); 0245 fprintf(f, " -2, --level2 - use postscript level 2 compression (default)\n"); 0246 #ifdef HAVE_ZLIB 0247 fprintf(f, " -3, --level3 - use postscript level 3 compression\n"); 0248 #endif 0249 fprintf(f, " -q, --longcoding - do not optimize for file size\n"); 0250 fprintf(f, "PGM options:\n"); 0251 fprintf(f, " -G, --gamma <n> - gamma value for anti-aliasing (default 2.2)\n"); 0252 fprintf(f, "Frontend options:\n"); 0253 fprintf(f, " -k, --blacklevel <n> - black/white cutoff in input file (default 0.5)\n"); 0254 fprintf(f, " -i, --invert - invert bitmap\n"); 0255 fprintf(f, "Progress bar options:\n"); 0256 fprintf(f, " --progress - show progress bar\n"); 0257 fprintf(f, " --tty <mode> - progress bar rendering: vt100 or dumb\n"); 0258 fprintf(f, "\n"); 0259 fprintf(f, "Dimensions can have optional units, e.g. 6.5in, 15cm, 100pt.\n"); 0260 fprintf(f, "Default is " DEFAULT_DIM_NAME " (or pixels for pgm, dxf, and gimppath backends).\n"); 0261 fprintf(f, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n"); 0262 j = fprintf(f, "Backends are: "); 0263 backend_list(f, j, 78); 0264 fprintf(f, ".\n"); 0265 } 0266 0267 /* ---------------------------------------------------------------------- */ 0268 /* auxiliary functions for parameter parsing */ 0269 0270 /* parse a dimension of the kind "1.5in", "7cm", etc. Return result in 0271 postscript points (=1/72 in). If endptr!=NULL, store pointer to 0272 next character in *endptr in the manner of strtod(3). */ 0273 static dim_t parse_dimension(char *s, char **endptr) { 0274 char *p; 0275 dim_t res; 0276 0277 res.x = strtod(s, &p); 0278 res.d = 0; 0279 if (p!=s) { 0280 if (!strncasecmp(p, "in", 2)) { 0281 res.d = DIM_IN; 0282 p += 2; 0283 } else if (!strncasecmp(p, "cm", 2)) { 0284 res.d = DIM_CM; 0285 p += 2; 0286 } else if (!strncasecmp(p, "mm", 2)) { 0287 res.d = DIM_MM; 0288 p += 2; 0289 } else if (!strncasecmp(p, "pt", 2)) { 0290 res.d = DIM_PT; 0291 p += 2; 0292 } 0293 } 0294 if (endptr!=NULL) { 0295 *endptr = p; 0296 } 0297 return res; 0298 } 0299 0300 /* parse a pair of dimensions, such as "8.5x11in", "30mmx4cm" */ 0301 static void parse_dimensions(char *s, char **endptr, dim_t *dxp, dim_t *dyp) { 0302 char *p, *q; 0303 dim_t dx, dy; 0304 0305 dx = parse_dimension(s, &p); 0306 if (p==s) { 0307 goto fail; 0308 } 0309 if (*p != 'x') { 0310 goto fail; 0311 } 0312 p++; 0313 dy = parse_dimension(p, &q); 0314 if (q==p) { 0315 goto fail; 0316 } 0317 if (dx.d && !dy.d) { 0318 dy.d = dx.d; 0319 } else if (!dx.d && dy.d) { 0320 dx.d = dy.d; 0321 } 0322 *dxp = dx; 0323 *dyp = dy; 0324 if (endptr != NULL) { 0325 *endptr = q; 0326 } 0327 return; 0328 0329 fail: 0330 dx.x = dx.d = dy.x = dy.d = 0; 0331 *dxp = dx; 0332 *dyp = dy; 0333 if (endptr != NULL) { 0334 *endptr = s; 0335 } 0336 return; 0337 } 0338 0339 static inline double double_of_dim(dim_t d, double def) { 0340 if (d.d) { 0341 return d.x * d.d; 0342 } else { 0343 return d.x * def; 0344 } 0345 } 0346 0347 static int parse_color(char *s) { 0348 int i, d; 0349 int col = 0; 0350 0351 if (s[0] != '#' || strlen(s) != 7) { 0352 return -1; 0353 } 0354 for (i=0; i<6; i++) { 0355 d = s[6-i]; 0356 if (d >= '0' && d <= '9') { 0357 col |= (d-'0') << (4*i); 0358 } else if (d >= 'a' && d <= 'f') { 0359 col |= (d-'a'+10) << (4*i); 0360 } else if (d >= 'A' && d <= 'F') { 0361 col |= (d-'A'+10) << (4*i); 0362 } else { 0363 return -1; 0364 } 0365 } 0366 return col; 0367 } 0368 0369 /* ---------------------------------------------------------------------- */ 0370 /* option processing */ 0371 0372 /* codes for options that don't have short form */ 0373 enum { 0374 OPT_TIGHT = 300, 0375 OPT_FILLCOLOR, 0376 OPT_OPAQUE, 0377 OPT_GROUP, 0378 OPT_FLAT, 0379 OPT_PROGRESS, 0380 OPT_TTY 0381 }; 0382 0383 static struct option longopts[] = { 0384 {"help", 0, 0, 'h'}, 0385 {"version", 0, 0, 'v'}, 0386 {"show-defaults", 0, 0, 'V'}, /* undocumented option for compatibility */ 0387 {"license", 0, 0, 'l'}, 0388 {"width", 1, 0, 'W'}, 0389 {"height", 1, 0, 'H'}, 0390 {"resolution", 1, 0, 'r'}, 0391 {"scale", 1, 0, 'x'}, 0392 {"stretch", 1, 0, 'S'}, 0393 {"margin", 1, 0, 'M'}, 0394 {"leftmargin", 1, 0, 'L'}, 0395 {"rightmargin", 1, 0, 'R'}, 0396 {"topmargin", 1, 0, 'T'}, 0397 {"bottommargin", 1, 0, 'B'}, 0398 {"tight", 0, 0, OPT_TIGHT}, 0399 {"rotate", 1, 0, 'A'}, 0400 {"pagesize", 1, 0, 'P'}, 0401 {"turdsize", 1, 0, 't'}, 0402 {"unit", 1, 0, 'u'}, 0403 {"cleartext", 0, 0, 'c'}, 0404 {"level2", 0, 0, '2'}, 0405 {"level3", 0, 0, '3'}, 0406 {"eps", 0, 0, 'e'}, 0407 {"postscript", 0, 0, 'p'}, 0408 {"svg", 0, 0, 's'}, 0409 {"pgm", 0, 0, 'g'}, 0410 {"backend", 1, 0, 'b'}, 0411 {"debug", 1, 0, 'd'}, 0412 {"color", 1, 0, 'C'}, 0413 {"fillcolor", 1, 0, OPT_FILLCOLOR}, 0414 {"turnpolicy", 1, 0, 'z'}, 0415 {"gamma", 1, 0, 'G'}, 0416 {"longcurve", 0, 0, 'n'}, 0417 {"longcoding", 0, 0, 'q'}, 0418 {"alphamax", 1, 0, 'a'}, 0419 {"opttolerance", 1, 0, 'O'}, 0420 {"output", 1, 0, 'o'}, 0421 {"blacklevel", 1, 0, 'k'}, 0422 {"invert", 0, 0, 'i'}, 0423 {"opaque", 0, 0, OPT_OPAQUE}, 0424 {"group", 0, 0, OPT_GROUP}, 0425 {"flat", 0, 0, OPT_FLAT}, 0426 {"progress", 0, 0, OPT_PROGRESS}, 0427 {"tty", 1, 0, OPT_TTY}, 0428 0429 {0, 0, 0, 0} 0430 }; 0431 0432 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"; 0433 0434 static void dopts(int ac, char *av[]) { 0435 int c; 0436 char *p; 0437 int i, j, r; 0438 dim_t dim, dimx, dimy; 0439 int matches, bestmatch; 0440 0441 /* defaults */ 0442 backend_lookup("eps", &info.backend); 0443 info.debug = 0; 0444 info.width_d.x = UNDEF; 0445 info.height_d.x = UNDEF; 0446 info.rx = UNDEF; 0447 info.ry = UNDEF; 0448 info.sx = UNDEF; 0449 info.sy = UNDEF; 0450 info.stretch = 1; 0451 info.lmar_d.x = UNDEF; 0452 info.rmar_d.x = UNDEF; 0453 info.tmar_d.x = UNDEF; 0454 info.bmar_d.x = UNDEF; 0455 info.angle = 0; 0456 info.paperwidth = DEFAULT_PAPERWIDTH; 0457 info.paperheight = DEFAULT_PAPERHEIGHT; 0458 info.tight = 0; 0459 info.unit = 10; 0460 info.compress = 1; 0461 info.pslevel = 2; 0462 info.color = 0x000000; 0463 info.gamma = 2.2; 0464 info.param = potrace_param_default(); 0465 if (!info.param) { 0466 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); 0467 exit(2); 0468 } 0469 info.longcoding = 0; 0470 info.outfile = NULL; 0471 info.blacklevel = 0.5; 0472 info.invert = 0; 0473 info.opaque = 0; 0474 info.grouping = 1; 0475 info.fillcolor = 0xffffff; 0476 info.progress = 0; 0477 info.progress_bar = DEFAULT_PROGRESS_BAR; 0478 0479 while ((c = getopt_long(ac, av, shortopts, longopts, NULL)) != -1) { 0480 switch (c) { 0481 case 'h': 0482 fprintf(stdout, "" POTRACE " " VERSION ". Transforms bitmaps into vector graphics.\n\n"); 0483 usage(stdout); 0484 exit(0); 0485 break; 0486 case 'v': 0487 case 'V': 0488 fprintf(stdout, "" POTRACE " " VERSION ". Copyright (C) 2001-2019 Peter Selinger.\n"); 0489 fprintf(stdout, "Library version: %s\n", potrace_version()); 0490 show_defaults(stdout); 0491 exit(0); 0492 break; 0493 case 'l': 0494 fprintf(stdout, "" POTRACE " " VERSION ". Copyright (C) 2001-2019 Peter Selinger.\n\n"); 0495 license(stdout); 0496 exit(0); 0497 break; 0498 case 'W': 0499 info.width_d = parse_dimension(optarg, &p); 0500 if (*p) { 0501 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); 0502 exit(1); 0503 } 0504 break; 0505 case 'H': 0506 info.height_d = parse_dimension(optarg, &p); 0507 if (*p) { 0508 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); 0509 exit(1); 0510 } 0511 break; 0512 case 'r': 0513 parse_dimensions(optarg, &p, &dimx, &dimy); 0514 if (*p == 0 && dimx.d == 0 && dimy.d == 0 && dimx.x != 0.0 && dimy.x != 0.0) { 0515 info.rx = dimx.x; 0516 info.ry = dimy.x; 0517 break; 0518 } 0519 dim = parse_dimension(optarg, &p); 0520 if (*p == 0 && dim.d == 0 && dim.x != 0.0) { 0521 info.rx = info.ry = dim.x; 0522 break; 0523 } 0524 fprintf(stderr, "" POTRACE ": invalid resolution -- %s\n", optarg); 0525 exit(1); 0526 break; 0527 case 'x': 0528 parse_dimensions(optarg, &p, &dimx, &dimy); 0529 if (*p == 0 && dimx.d == 0 && dimy.d == 0) { 0530 info.sx = dimx.x; 0531 info.sy = dimy.x; 0532 break; 0533 } 0534 dim = parse_dimension(optarg, &p); 0535 if (*p == 0 && dim.d == 0) { 0536 info.sx = info.sy = dim.x; 0537 break; 0538 } 0539 fprintf(stderr, "" POTRACE ": invalid scaling factor -- %s\n", optarg); 0540 exit(1); 0541 break; 0542 case 'S': 0543 info.stretch = atof(optarg); 0544 break; 0545 case 'M': 0546 info.lmar_d = parse_dimension(optarg, &p); 0547 if (*p) { 0548 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); 0549 exit(1); 0550 } 0551 info.rmar_d = info.tmar_d = info.bmar_d = info.lmar_d; 0552 break; 0553 case 'L': 0554 info.lmar_d = parse_dimension(optarg, &p); 0555 if (*p) { 0556 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); 0557 exit(1); 0558 } 0559 break; 0560 case 'R': 0561 info.rmar_d = parse_dimension(optarg, &p); 0562 if (*p) { 0563 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); 0564 exit(1); 0565 } 0566 break; 0567 case 'T': 0568 info.tmar_d = parse_dimension(optarg, &p); 0569 if (*p) { 0570 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); 0571 exit(1); 0572 } 0573 break; 0574 case 'B': 0575 info.bmar_d = parse_dimension(optarg, &p); 0576 if (*p) { 0577 fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); 0578 exit(1); 0579 } 0580 break; 0581 case OPT_TIGHT: 0582 info.tight = 1; 0583 break; 0584 case 'A': 0585 info.angle = strtod(optarg, &p); 0586 if (*p) { 0587 fprintf(stderr, "" POTRACE ": invalid angle -- %s\n", optarg); 0588 exit(1); 0589 } 0590 if (info.angle <= -180 || info.angle > 180) { 0591 info.angle -= 360 * ceil(info.angle / 360 - 0.5); 0592 } 0593 break; 0594 case 'P': 0595 matches = 0; 0596 bestmatch = 0; 0597 for (i=0; pageformat[i].name!=NULL; i++) { 0598 if (strcasecmp(pageformat[i].name, optarg)==0) { 0599 matches = 1; 0600 bestmatch = i; 0601 break; 0602 } else if (strncasecmp(pageformat[i].name, optarg, strlen(optarg))==0) { 0603 /* don't allow partial match on "10x14" */ 0604 if (optarg[0] != '1') { 0605 matches++; 0606 bestmatch = i; 0607 } 0608 } 0609 } 0610 if (matches == 1) { 0611 info.paperwidth = pageformat[bestmatch].w; 0612 info.paperheight = pageformat[bestmatch].h; 0613 break; 0614 } 0615 parse_dimensions(optarg, &p, &dimx, &dimy); 0616 if (*p == 0) { 0617 info.paperwidth = (int)round(double_of_dim(dimx, DEFAULT_DIM)); 0618 info.paperheight = (int)round(double_of_dim(dimy, DEFAULT_DIM)); 0619 break; 0620 } 0621 if (matches == 0) { 0622 fprintf(stderr, "" POTRACE ": unrecognized page format -- %s\n", optarg); 0623 } else { 0624 fprintf(stderr, "" POTRACE ": ambiguous page format -- %s\n", optarg); 0625 } 0626 j = fprintf(stderr, "Use one of: "); 0627 for (i=0; pageformat[i].name!=NULL; i++) { 0628 if (j + strlen(pageformat[i].name) > 75) { 0629 fprintf(stderr, "\n"); 0630 j = 0; 0631 } 0632 j += fprintf(stderr, "%s, ", pageformat[i].name); 0633 } 0634 fprintf(stderr, "or specify <dim>x<dim>.\n"); 0635 exit(1); 0636 break; 0637 case 't': 0638 info.param->turdsize = atoi(optarg); 0639 break; 0640 case 'u': 0641 info.unit = strtod(optarg, &p); 0642 if (*p) { 0643 fprintf(stderr, "" POTRACE ": invalid unit -- %s\n", optarg); 0644 exit(1); 0645 } 0646 break; 0647 case 'c': 0648 info.pslevel = 2; 0649 info.compress = 0; 0650 break; 0651 case '2': 0652 info.pslevel = 2; 0653 info.compress = 1; 0654 break; 0655 case '3': 0656 #ifdef HAVE_ZLIB 0657 info.pslevel = 3; 0658 info.compress = 1; 0659 #else 0660 fprintf(stderr, "" POTRACE ": option -3 not supported, using -2 instead.\n"); 0661 fflush(stderr); 0662 info.pslevel = 2; 0663 info.compress = 1; 0664 #endif 0665 break; 0666 case 'e': 0667 backend_lookup("eps", &info.backend); 0668 break; 0669 case 'p': 0670 backend_lookup("postscript", &info.backend); 0671 break; 0672 case 's': 0673 backend_lookup("svg", &info.backend); 0674 break; 0675 case 'g': 0676 backend_lookup("pgm", &info.backend); 0677 break; 0678 case 'b': 0679 r = backend_lookup(optarg, &info.backend); 0680 if (r==1 || r==2) { 0681 if (r==1) { 0682 fprintf(stderr, "" POTRACE ": unrecognized backend -- %s\n", optarg); 0683 } else { 0684 fprintf(stderr, "" POTRACE ": ambiguous backend -- %s\n", optarg); 0685 } 0686 j = fprintf(stderr, "Use one of: "); 0687 backend_list(stderr, j, 70); 0688 fprintf(stderr, ".\n"); 0689 exit(1); 0690 } 0691 break; 0692 case 'd': 0693 info.debug = atoi(optarg); 0694 break; 0695 case 'C': 0696 info.color = parse_color(optarg); 0697 if (info.color == -1) { 0698 fprintf(stderr, "" POTRACE ": invalid color -- %s\n", optarg); 0699 exit(1); 0700 } 0701 break; 0702 case OPT_FILLCOLOR: 0703 info.fillcolor = parse_color(optarg); 0704 if (info.fillcolor == -1) { 0705 fprintf(stderr, "" POTRACE ": invalid color -- %s\n", optarg); 0706 exit(1); 0707 } 0708 info.opaque = 1; 0709 break; 0710 case 'z': 0711 matches = 0; 0712 bestmatch = 0; 0713 for (i=0; turnpolicy[i].name!=NULL; i++) { 0714 if (strcasecmp(turnpolicy[i].name, optarg)==0) { 0715 matches = 1; 0716 bestmatch = i; 0717 break; 0718 } else if (strncasecmp(turnpolicy[i].name, optarg, strlen(optarg))==0) { 0719 matches++; 0720 bestmatch = i; 0721 } 0722 } 0723 if (matches == 1) { 0724 info.param->turnpolicy = turnpolicy[bestmatch].n; 0725 break; 0726 } 0727 if (matches == 0) { 0728 fprintf(stderr, "" POTRACE ": unrecognized turnpolicy -- %s\n", optarg); 0729 } else { 0730 fprintf(stderr, "" POTRACE ": ambiguous turnpolicy -- %s\n", optarg); 0731 } 0732 j = fprintf(stderr, "Use one of: "); 0733 for (i=0; turnpolicy[i].name!=NULL; i++) { 0734 if (j + strlen(turnpolicy[i].name) > 75) { 0735 fprintf(stderr, "\n"); 0736 j = 0; 0737 } 0738 j += fprintf(stderr, "%s%s", turnpolicy[i].name, turnpolicy[i+1].name ? ", " : ""); 0739 } 0740 fprintf(stderr, ".\n"); 0741 exit(1); 0742 break; 0743 case 'G': 0744 info.gamma = atof(optarg); 0745 break; 0746 case 'n': 0747 info.param->opticurve = 0; 0748 break; 0749 case 'q': 0750 info.longcoding = 1; 0751 break; 0752 case 'a': 0753 info.param->alphamax = strtod(optarg, &p); 0754 if (*p) { 0755 fprintf(stderr, "" POTRACE ": invalid alphamax -- %s\n", optarg); 0756 exit(1); 0757 } 0758 break; 0759 case 'O': 0760 info.param->opttolerance = strtod(optarg, &p); 0761 if (*p) { 0762 fprintf(stderr, "" POTRACE ": invalid opttolerance -- %s\n", optarg); 0763 exit(1); 0764 } 0765 break; 0766 case 'o': 0767 free(info.outfile); 0768 info.outfile = strdup(optarg); 0769 if (!info.outfile) { 0770 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); 0771 exit(2); 0772 } 0773 break; 0774 case 'k': 0775 info.blacklevel = strtod(optarg, &p); 0776 if (*p) { 0777 fprintf(stderr, "" POTRACE ": invalid blacklevel -- %s\n", optarg); 0778 exit(1); 0779 } 0780 break; 0781 case 'i': 0782 info.invert = 1; 0783 break; 0784 case OPT_OPAQUE: 0785 info.opaque = 1; 0786 break; 0787 case OPT_GROUP: 0788 info.grouping = 2; 0789 break; 0790 case OPT_FLAT: 0791 info.grouping = 0; 0792 break; 0793 case OPT_PROGRESS: 0794 info.progress = 1; 0795 break; 0796 case OPT_TTY: 0797 if (strcmp(optarg, "dumb") == 0) { 0798 info.progress_bar = progress_bar_simplified; 0799 } else if (strcmp(optarg, "vt100") == 0) { 0800 info.progress_bar = progress_bar_vt100; 0801 } else { 0802 fprintf(stderr, "" POTRACE ": invalid tty mode -- %s. Try --help for more info\n", optarg); 0803 exit(1); 0804 } 0805 break; 0806 case '?': 0807 fprintf(stderr, "Try --help for more info\n"); 0808 exit(1); 0809 break; 0810 default: 0811 fprintf(stderr, "" POTRACE ": Unimplemented option -- %c\n", c); 0812 exit(1); 0813 } 0814 } 0815 info.infiles = &av[optind]; 0816 info.infilecount = ac-optind; 0817 info.some_infiles = info.infilecount ? 1 : 0; 0818 0819 /* if "--" was used, even an empty list of filenames is considered 0820 "some" filenames. */ 0821 if (strcmp(av[optind-1], "--") == 0) { 0822 info.some_infiles = 1; 0823 } 0824 } 0825 0826 /* ---------------------------------------------------------------------- */ 0827 /* calculations with bitmap dimensions, positioning etc */ 0828 0829 /* determine the dimensions of the output based on command line and 0830 image dimensions, and optionally, based on the actual image outline. */ 0831 static void calc_dimensions(imginfo_t *imginfo, potrace_path_t *plist) { 0832 double dim_def; 0833 double maxwidth, maxheight, sc; 0834 int default_scaling = 0; 0835 0836 /* we take care of a special case: if one of the image dimensions is 0837 0, we change it to 1. Such an image is empty anyway, so there 0838 will be 0 paths in it. Changing the dimensions avoids division by 0839 0 error in calculating scaling factors, bounding boxes and 0840 such. This doesn't quite do the right thing in all cases, but it 0841 is better than causing overflow errors or "nan" output in 0842 backends. Human users don't tend to process images of size 0 0843 anyway; they might occur in some pipelines. */ 0844 if (imginfo->pixwidth == 0) { 0845 imginfo->pixwidth = 1; 0846 } 0847 if (imginfo->pixheight == 0) { 0848 imginfo->pixheight = 1; 0849 } 0850 0851 /* set the default dimension for width, height, margins */ 0852 if (info.backend->pixel) { 0853 dim_def = DIM_PT; 0854 } else { 0855 dim_def = DEFAULT_DIM; 0856 } 0857 0858 /* apply default dimension to width, height, margins */ 0859 imginfo->width = info.width_d.x == UNDEF ? UNDEF : double_of_dim(info.width_d, dim_def); 0860 imginfo->height = info.height_d.x == UNDEF ? UNDEF : double_of_dim(info.height_d, dim_def); 0861 imginfo->lmar = info.lmar_d.x == UNDEF ? UNDEF : double_of_dim(info.lmar_d, dim_def); 0862 imginfo->rmar = info.rmar_d.x == UNDEF ? UNDEF : double_of_dim(info.rmar_d, dim_def); 0863 imginfo->tmar = info.tmar_d.x == UNDEF ? UNDEF : double_of_dim(info.tmar_d, dim_def); 0864 imginfo->bmar = info.bmar_d.x == UNDEF ? UNDEF : double_of_dim(info.bmar_d, dim_def); 0865 0866 /* start with a standard rectangle */ 0867 trans_from_rect(&imginfo->trans, imginfo->pixwidth, imginfo->pixheight); 0868 0869 /* if info.tight is set, tighten the bounding box */ 0870 if (info.tight) { 0871 trans_tighten(&imginfo->trans, plist); 0872 } 0873 0874 /* sx/rx is just an alternate way to specify width; sy/ry is just an 0875 alternate way to specify height. */ 0876 if (info.backend->pixel) { 0877 if (imginfo->width == UNDEF && info.sx != UNDEF) { 0878 imginfo->width = imginfo->trans.bb[0] * info.sx; 0879 } 0880 if (imginfo->height == UNDEF && info.sy != UNDEF) { 0881 imginfo->height = imginfo->trans.bb[1] * info.sy; 0882 } 0883 } else { 0884 if (imginfo->width == UNDEF && info.rx != UNDEF) { 0885 imginfo->width = imginfo->trans.bb[0] / info.rx * 72; 0886 } 0887 if (imginfo->height == UNDEF && info.ry != UNDEF) { 0888 imginfo->height = imginfo->trans.bb[1] / info.ry * 72; 0889 } 0890 } 0891 0892 /* if one of width/height is specified, use stretch to determine the 0893 other */ 0894 if (imginfo->width == UNDEF && imginfo->height != UNDEF) { 0895 imginfo->width = imginfo->height / imginfo->trans.bb[1] * imginfo->trans.bb[0] / info.stretch; 0896 } else if (imginfo->width != UNDEF && imginfo->height == UNDEF) { 0897 imginfo->height = imginfo->width / imginfo->trans.bb[0] * imginfo->trans.bb[1] * info.stretch; 0898 } 0899 0900 /* if width and height are still variable, tenatively use the 0901 default scaling factor of 72dpi (for dimension-based backends) or 0902 1 (for pixel-based backends). For fixed-size backends, this will 0903 be adjusted later to fit the page. */ 0904 if (imginfo->width == UNDEF && imginfo->height == UNDEF) { 0905 imginfo->width = imginfo->trans.bb[0]; 0906 imginfo->height = imginfo->trans.bb[1] * info.stretch; 0907 default_scaling = 1; 0908 } 0909 0910 /* apply scaling */ 0911 trans_scale_to_size(&imginfo->trans, imginfo->width, imginfo->height); 0912 0913 /* apply rotation, and tighten the bounding box again, if necessary */ 0914 if (info.angle != 0.0) { 0915 trans_rotate(&imginfo->trans, info.angle); 0916 if (info.tight) { 0917 trans_tighten(&imginfo->trans, plist); 0918 } 0919 } 0920 0921 /* for fixed-size backends, if default scaling was in effect, 0922 further adjust the scaling to be the "best fit" for the given 0923 page size and margins. */ 0924 if (default_scaling && info.backend->fixed) { 0925 0926 /* try to squeeze it between margins */ 0927 maxwidth = UNDEF; 0928 maxheight = UNDEF; 0929 0930 if (imginfo->lmar != UNDEF && imginfo->rmar != UNDEF) { 0931 maxwidth = info.paperwidth - imginfo->lmar - imginfo->rmar; 0932 } 0933 if (imginfo->bmar != UNDEF && imginfo->tmar != UNDEF) { 0934 maxheight = info.paperheight - imginfo->bmar - imginfo->tmar; 0935 } 0936 if (maxwidth == UNDEF && maxheight == UNDEF) { 0937 maxwidth = max(info.paperwidth - 144, info.paperwidth * 0.75); 0938 maxheight = max(info.paperheight - 144, info.paperheight * 0.75); 0939 } 0940 0941 if (maxwidth == UNDEF) { 0942 sc = maxheight / imginfo->trans.bb[1]; 0943 } else if (maxheight == UNDEF) { 0944 sc = maxwidth / imginfo->trans.bb[0]; 0945 } else { 0946 sc = min(maxwidth / imginfo->trans.bb[0], maxheight / imginfo->trans.bb[1]); 0947 } 0948 0949 /* re-scale coordinate system */ 0950 imginfo->width *= sc; 0951 imginfo->height *= sc; 0952 trans_rescale(&imginfo->trans, sc); 0953 } 0954 0955 /* adjust margins */ 0956 if (info.backend->fixed) { 0957 if (imginfo->lmar == UNDEF && imginfo->rmar == UNDEF) { 0958 imginfo->lmar = (info.paperwidth-imginfo->trans.bb[0])/2; 0959 } else if (imginfo->lmar == UNDEF) { 0960 imginfo->lmar = (info.paperwidth-imginfo->trans.bb[0]-imginfo->rmar); 0961 } else if (imginfo->lmar != UNDEF && imginfo->rmar != UNDEF) { 0962 imginfo->lmar += (info.paperwidth-imginfo->trans.bb[0]-imginfo->lmar-imginfo->rmar)/2; 0963 } 0964 if (imginfo->bmar == UNDEF && imginfo->tmar == UNDEF) { 0965 imginfo->bmar = (info.paperheight-imginfo->trans.bb[1])/2; 0966 } else if (imginfo->bmar == UNDEF) { 0967 imginfo->bmar = (info.paperheight-imginfo->trans.bb[1]-imginfo->tmar); 0968 } else if (imginfo->bmar != UNDEF && imginfo->tmar != UNDEF) { 0969 imginfo->bmar += (info.paperheight-imginfo->trans.bb[1]-imginfo->bmar-imginfo->tmar)/2; 0970 } 0971 } else { 0972 if (imginfo->lmar == UNDEF) { 0973 imginfo->lmar = 0; 0974 } 0975 if (imginfo->rmar == UNDEF) { 0976 imginfo->rmar = 0; 0977 } 0978 if (imginfo->bmar == UNDEF) { 0979 imginfo->bmar = 0; 0980 } 0981 if (imginfo->tmar == UNDEF) { 0982 imginfo->tmar = 0; 0983 } 0984 } 0985 } 0986 0987 /* ---------------------------------------------------------------------- */ 0988 /* auxiliary functions for file handling */ 0989 0990 /* open a file for reading. Return stdin if filename is NULL or "-" */ 0991 static FILE *my_fopen_read(const char *filename) { 0992 if (filename == NULL || strcmp(filename, "-") == 0) { 0993 return stdin; 0994 } 0995 return fopen(filename, "rb"); 0996 } 0997 0998 /* open a file for writing. Return stdout if filename is NULL or "-" */ 0999 static FILE *my_fopen_write(const char *filename) { 1000 if (filename == NULL || strcmp(filename, "-") == 0) { 1001 return stdout; 1002 } 1003 return fopen(filename, "wb"); 1004 } 1005 1006 /* close a file, but do nothing is filename is NULL or "-" */ 1007 static void my_fclose(FILE *f, const char *filename) { 1008 if (filename == NULL || strcmp(filename, "-") == 0) { 1009 return; 1010 } 1011 fclose(f); 1012 } 1013 1014 /* make output filename from input filename. Return an allocated value. */ 1015 static char *make_outfilename(const char *infile, const char *ext) { 1016 char *outfile; 1017 char *p; 1018 1019 if (strcmp(infile, "-") == 0) { 1020 return strdup("-"); 1021 } 1022 1023 outfile = (char *) malloc(strlen(infile)+strlen(ext)+5); 1024 if (!outfile) { 1025 return NULL; 1026 } 1027 strcpy(outfile, infile); 1028 p = strrchr(outfile, '.'); 1029 if (p) { 1030 *p = 0; 1031 } 1032 strcat(outfile, ext); 1033 1034 /* check that input and output filenames are different */ 1035 if (strcmp(infile, outfile) == 0) { 1036 strcpy(outfile, infile); 1037 strcat(outfile, "-out"); 1038 } 1039 1040 return outfile; 1041 } 1042 1043 /* ---------------------------------------------------------------------- */ 1044 /* Process one infile */ 1045 1046 /* Process one or more bitmaps from fin, and write the results to fout 1047 using the page_f function of the appropriate backend. */ 1048 1049 static void process_file(backend_t *b, const char *infile, const char *outfile, FILE *fin, FILE *fout) { 1050 int r; 1051 potrace_bitmap_t *bm = NULL; 1052 imginfo_t imginfo; 1053 int eof_flag = 0; /* to indicate premature eof */ 1054 int count; /* number of bitmaps successfully processed, this file */ 1055 potrace_state_t *st; 1056 1057 for (count=0; ; count++) { 1058 /* read a bitmap */ 1059 r = bm_read(fin, info.blacklevel, &bm); 1060 switch (r) { 1061 case -1: /* system error */ 1062 fprintf(stderr, "" POTRACE ": %s: %s\n", infile, strerror(errno)); 1063 exit(2); 1064 case -2: /* corrupt file format */ 1065 fprintf(stderr, "" POTRACE ": %s: file format error: %s\n", infile, bm_read_error); 1066 exit(2); 1067 case -3: /* empty file */ 1068 if (count>0) { /* end of file */ 1069 return; 1070 } 1071 fprintf(stderr, "" POTRACE ": %s: empty file\n", infile); 1072 exit(2); 1073 case -4: /* wrong magic */ 1074 if (count>0) { 1075 fprintf(stderr, "" POTRACE ": %s: warning: junk at end of file\n", infile); 1076 return; 1077 } 1078 fprintf(stderr, "" POTRACE ": %s: file format not recognized\n", infile); 1079 fprintf(stderr, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n"); 1080 exit(2); 1081 case 1: /* unexpected end of file */ 1082 fprintf(stderr, "" POTRACE ": warning: %s: premature end of file\n", infile); 1083 eof_flag = 1; 1084 break; 1085 } 1086 1087 /* prepare progress bar, if requested */ 1088 if (info.progress) { 1089 r = info.progress_bar->init(&info.param->progress, infile, count); 1090 if (r) { 1091 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); 1092 exit(2); 1093 } 1094 } else { 1095 info.param->progress.callback = NULL; 1096 } 1097 1098 if (info.invert) { 1099 bm_invert(bm); 1100 } 1101 1102 /* process the image */ 1103 st = potrace_trace(info.param, bm); 1104 if (!st || st->status != POTRACE_STATUS_OK) { 1105 fprintf(stderr, "" POTRACE ": %s: %s\n", infile, strerror(errno)); 1106 exit(2); 1107 } 1108 1109 /* calculate image dimensions */ 1110 imginfo.pixwidth = bm->w; 1111 imginfo.pixheight = bm->h; 1112 bm_free(bm); 1113 1114 calc_dimensions(&imginfo, st->plist); 1115 1116 r = b->page_f(fout, st->plist, &imginfo); 1117 if (r) { 1118 fprintf(stderr, "" POTRACE ": %s: %s\n", outfile, strerror(errno)); 1119 exit(2); 1120 } 1121 1122 potrace_state_free(st); 1123 1124 if (info.progress) { 1125 info.progress_bar->term(&info.param->progress); 1126 } 1127 1128 if (eof_flag || !b->multi) { 1129 return; 1130 } 1131 } 1132 /* not reached */ 1133 } 1134 1135 /* ---------------------------------------------------------------------- */ 1136 /* main: handle file i/o */ 1137 1138 #define TRY(x) if (x) goto try_error 1139 1140 int main(int ac, char *av[]) { 1141 backend_t *b; /* backend info */ 1142 FILE *fin, *fout; 1143 int i; 1144 char *outfile; 1145 1146 /* platform-specific initializations, e.g., set file i/o to binary */ 1147 platform_init(); 1148 1149 /* process options */ 1150 dopts(ac, av); 1151 1152 b = info.backend; 1153 if (b==NULL) { 1154 fprintf(stderr, "" POTRACE ": internal error: selected backend not found\n"); 1155 exit(1); 1156 } 1157 1158 /* fix some parameters */ 1159 /* if backend cannot handle opticurve, disable it */ 1160 if (b->opticurve == 0) { 1161 info.param->opticurve = 0; 1162 } 1163 1164 /* there are several ways to call us: 1165 potrace -- stdin to stdout 1166 potrace -o outfile -- stdin to outfile 1167 potrace file... -- encode each file and generate outfile names 1168 potrace -o outfile file... -- concatenate files and write to outfile 1169 1170 The latter form is only allowed one file for single-page 1171 backends. For multi-page backends, each file must contain 0 or 1172 more complete bitmaps. 1173 */ 1174 1175 if (!info.some_infiles) { /* read from stdin */ 1176 1177 fout = my_fopen_write(info.outfile); 1178 if (!fout) { 1179 fprintf(stderr, "" POTRACE ": %s: %s\n", info.outfile ? info.outfile : "stdout", strerror(errno)); 1180 exit(2); 1181 } 1182 if (b->init_f) { 1183 TRY(b->init_f(fout)); 1184 } 1185 process_file(b, "stdin", info.outfile ? info.outfile : "stdout", stdin, fout); 1186 if (b->term_f) { 1187 TRY(b->term_f(fout)); 1188 } 1189 my_fclose(fout, info.outfile); 1190 free(info.outfile); 1191 potrace_param_free(info.param); 1192 return 0; 1193 1194 } else if (!info.outfile) { /* infiles -> multiple outfiles */ 1195 1196 for (i=0; i<info.infilecount; i++) { 1197 outfile = make_outfilename(info.infiles[i], b->ext); 1198 if (!outfile) { 1199 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); 1200 exit(2); 1201 } 1202 fin = my_fopen_read(info.infiles[i]); 1203 if (!fin) { 1204 fprintf(stderr, "" POTRACE ": %s: %s\n", info.infiles[i], strerror(errno)); 1205 exit(2); 1206 } 1207 fout = my_fopen_write(outfile); 1208 if (!fout) { 1209 fprintf(stderr, "" POTRACE ": %s: %s\n", outfile, strerror(errno)); 1210 exit(2); 1211 } 1212 if (b->init_f) { 1213 TRY(b->init_f(fout)); 1214 } 1215 process_file(b, info.infiles[i], outfile, fin, fout); 1216 if (b->term_f) { 1217 TRY(b->term_f(fout)); 1218 } 1219 my_fclose(fin, info.infiles[i]); 1220 my_fclose(fout, outfile); 1221 free(outfile); 1222 } 1223 potrace_param_free(info.param); 1224 return 0; 1225 1226 } else { /* infiles to single outfile */ 1227 1228 if (!b->multi && info.infilecount >= 2) { 1229 fprintf(stderr, "" POTRACE ": cannot use multiple input files with -o in %s mode\n", b->name); 1230 exit(1); 1231 } 1232 if (info.infilecount == 0) { 1233 fprintf(stderr, "" POTRACE ": cannot use empty list of input files with -o\n"); 1234 exit(1); 1235 } 1236 1237 fout = my_fopen_write(info.outfile); 1238 if (!fout) { 1239 fprintf(stderr, "" POTRACE ": %s: %s\n", info.outfile, strerror(errno)); 1240 exit(2); 1241 } 1242 if (b->init_f) { 1243 TRY(b->init_f(fout)); 1244 } 1245 for (i=0; i<info.infilecount; i++) { 1246 fin = my_fopen_read(info.infiles[i]); 1247 if (!fin) { 1248 fprintf(stderr, "" POTRACE ": %s: %s\n", info.infiles[i], strerror(errno)); 1249 exit(2); 1250 } 1251 process_file(b, info.infiles[i], info.outfile, fin, fout); 1252 my_fclose(fin, info.infiles[i]); 1253 } 1254 if (b->term_f) { 1255 TRY(b->term_f(fout)); 1256 } 1257 my_fclose(fout, info.outfile); 1258 free(info.outfile); 1259 potrace_param_free(info.param); 1260 return 0; 1261 1262 } 1263 1264 /* not reached */ 1265 1266 try_error: 1267 fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); 1268 exit(2); 1269 }