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 }