diff options
Diffstat (limited to 'Build/source/libs/potrace/potrace-src/src/main.c')
-rw-r--r-- | Build/source/libs/potrace/potrace-src/src/main.c | 1269 |
1 files changed, 1269 insertions, 0 deletions
diff --git a/Build/source/libs/potrace/potrace-src/src/main.c b/Build/source/libs/potrace/potrace-src/src/main.c new file mode 100644 index 00000000000..2785b19bb6e --- /dev/null +++ b/Build/source/libs/potrace/potrace-src/src/main.c @@ -0,0 +1,1269 @@ +/* Copyright (C) 2001-2019 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <strings.h> +#include <getopt.h> +#include <math.h> + +#include "main.h" +#include "potracelib.h" +#include "backend_pdf.h" +#include "backend_eps.h" +#include "backend_pgm.h" +#include "backend_svg.h" +#include "backend_xfig.h" +#include "backend_dxf.h" +#include "backend_geojson.h" +#include "potracelib.h" +#include "bitmap_io.h" +#include "bitmap.h" +#include "platform.h" +#include "auxiliary.h" +#include "progress_bar.h" +#include "trans.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define UNDEF ((double)(1e30)) /* a value to represent "undefined" */ + +struct info_s info; + +/* ---------------------------------------------------------------------- */ +/* some data structures for option processing */ + +struct pageformat_s { + const char *name; + int w, h; +}; +typedef struct pageformat_s pageformat_t; + +/* dimensions of the various page formats, in postscript points */ +static pageformat_t pageformat[] = { + { "a4", 595, 842 }, + { "a3", 842, 1191 }, + { "a5", 421, 595 }, + { "b5", 516, 729 }, + { "letter", 612, 792 }, + { "legal", 612, 1008 }, + { "tabloid", 792, 1224 }, + { "statement", 396, 612 }, + { "executive", 540, 720 }, + { "folio", 612, 936 }, + { "quarto", 610, 780 }, + { "10x14", 720, 1008 }, + { NULL, 0, 0 }, +}; + +struct turnpolicy_s { + const char *name; + int n; +}; +typedef struct turnpolicy_s turnpolicy_t; + +/* names of turn policies */ +static turnpolicy_t turnpolicy[] = { + {"black", POTRACE_TURNPOLICY_BLACK}, + {"white", POTRACE_TURNPOLICY_WHITE}, + {"left", POTRACE_TURNPOLICY_LEFT}, + {"right", POTRACE_TURNPOLICY_RIGHT}, + {"minority", POTRACE_TURNPOLICY_MINORITY}, + {"majority", POTRACE_TURNPOLICY_MAJORITY}, + {"random", POTRACE_TURNPOLICY_RANDOM}, + {NULL, 0}, +}; + +/* backends and their characteristics */ +struct backend_s { + const char *name; /* name of this backend */ + const char *ext; /* file extension */ + int fixed; /* fixed page size backend? */ + int pixel; /* pixel-based backend? */ + int multi; /* multi-page backend? */ + int (*init_f)(FILE *fout); /* initialization function */ + int (*page_f)(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo); + /* per-bitmap function */ + int (*term_f)(FILE *fout); /* finalization function */ + int opticurve; /* opticurve capable (true Bezier curves?) */ +}; +typedef struct backend_s backend_t; + +static backend_t backend[] = { + { "svg", ".svg", 0, 0, 0, NULL, page_svg, NULL, 1 }, + { "pdf", ".pdf", 0, 0, 1, init_pdf, page_pdf, term_pdf, 1 }, + { "pdfpage", ".pdf", 1, 0, 1, init_pdf, page_pdfpage, term_pdf, 1 }, + { "eps", ".eps", 0, 0, 0, NULL, page_eps, NULL, 1 }, + { "postscript", ".ps", 1, 0, 1, init_ps, page_ps, term_ps, 1 }, + { "ps", ".ps", 1, 0, 1, init_ps, page_ps, term_ps, 1 }, + { "dxf", ".dxf", 0, 1, 0, NULL, page_dxf, NULL, 1 }, + { "geojson", ".json",0, 1, 0, NULL, page_geojson, NULL, 1 }, + { "pgm", ".pgm", 0, 1, 1, NULL, page_pgm, NULL, 1 }, + { "gimppath", ".svg", 0, 1, 0, NULL, page_gimp, NULL, 1 }, + { "xfig", ".fig", 1, 0, 0, NULL, page_xfig, NULL, 0 }, + { NULL, NULL, 0, 0, 0, NULL, NULL, NULL, 0 }, +}; + +/* look up a backend by name. If found, return 0 and set *bp. If not + found leave *bp unchanged and return 1, or 2 on ambiguous + prefix. */ +static int backend_lookup(const char *name, backend_t **bp) { + int i; + int m=0; /* prefix matches */ + backend_t *b = NULL; + + for (i=0; backend[i].name; i++) { + if (strcasecmp(backend[i].name, name)==0) { + *bp = &backend[i]; + return 0; + } else if (strncasecmp(backend[i].name, name, strlen(name))==0) { + m++; + b = &backend[i]; + } + } + /* if there was no exact match, and exactly one prefix match, use that */ + if (m==1) { + *bp = b; + return 0; + } else if (m) { + return 2; + } else { + return 1; + } +} + +/* list all available backends by name, in a comma separated list. + Assume the cursor starts in column j, and break lines at length + linelen. Do not output any trailing punctuation. Return the column + the cursor is in. */ +static int backend_list(FILE *fout, int j, int linelen) { + int i; + + for (i=0; backend[i].name; i++) { + if (j + (int)strlen(backend[i].name) > linelen) { + fprintf(fout, "\n"); + j = 0; + } + j += fprintf(fout, "%s", backend[i].name); + if (backend[i+1].name) { + j += fprintf(fout, ", "); + } + } + return j; +} + +/* ---------------------------------------------------------------------- */ +/* some info functions */ + +static void license(FILE *f) { + fprintf(f, + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software Foundation\n" + "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + ); +} + +static void show_defaults(FILE *f) { + fprintf(f, "Default unit: " DEFAULT_DIM_NAME "\n"); + fprintf(f, "Default page size: " DEFAULT_PAPERFORMAT "\n"); +} + +static void usage(FILE *f) { + int j; + + fprintf(f, "Usage: " POTRACE " [options] [filename...]\n"); + fprintf(f, "General options:\n"); + fprintf(f, " -h, --help - print this help message and exit\n"); + fprintf(f, " -v, --version - print version info and exit\n"); + fprintf(f, " -l, --license - print license info and exit\n"); + fprintf(f, "File selection:\n"); + fprintf(f, " <filename> - an input file\n"); + fprintf(f, " -o, --output <filename> - write all output to this file\n"); + fprintf(f, " -- - end of options; 0 or more input filenames follow\n"); + fprintf(f, "Backend selection:\n"); + fprintf(f, " -b, --backend <name> - select backend by name\n"); + fprintf(f, " -b svg, -s, --svg - SVG backend (scalable vector graphics)\n"); + fprintf(f, " -b pdf - PDF backend (portable document format)\n"); + fprintf(f, " -b pdfpage - fixed page-size PDF backend\n"); + fprintf(f, " -b eps, -e, --eps - EPS backend (encapsulated PostScript) (default)\n"); + fprintf(f, " -b ps, -p, --postscript - PostScript backend\n"); + fprintf(f, " -b pgm, -g, --pgm - PGM backend (portable greymap)\n"); + fprintf(f, " -b dxf - DXF backend (drawing interchange format)\n"); + fprintf(f, " -b geojson - GeoJSON backend\n"); + fprintf(f, " -b gimppath - Gimppath backend (GNU Gimp)\n"); + fprintf(f, " -b xfig - XFig backend\n"); + fprintf(f, "Algorithm options:\n"); + fprintf(f, " -z, --turnpolicy <policy> - how to resolve ambiguities in path decomposition\n"); + fprintf(f, " -t, --turdsize <n> - suppress speckles of up to this size (default 2)\n"); + fprintf(f, " -a, --alphamax <n> - corner threshold parameter (default 1)\n"); + fprintf(f, " -n, --longcurve - turn off curve optimization\n"); + fprintf(f, " -O, --opttolerance <n> - curve optimization tolerance (default 0.2)\n"); + fprintf(f, " -u, --unit <n> - quantize output to 1/unit pixels (default 10)\n"); + fprintf(f, " -d, --debug <n> - produce debugging output of type n (n=1,2,3)\n"); + fprintf(f, "Scaling and placement options:\n"); + fprintf(f, " -P, --pagesize <format> - page size (default is " DEFAULT_PAPERFORMAT ")\n"); + fprintf(f, " -W, --width <dim> - width of output image\n"); + fprintf(f, " -H, --height <dim> - height of output image\n"); + fprintf(f, " -r, --resolution <n>[x<n>] - resolution (in dpi) (dimension-based backends)\n"); + fprintf(f, " -x, --scale <n>[x<n>] - scaling factor (pixel-based backends)\n"); + fprintf(f, " -S, --stretch <n> - yresolution/xresolution\n"); + fprintf(f, " -A, --rotate <angle> - rotate counterclockwise by angle\n"); + fprintf(f, " -M, --margin <dim> - margin\n"); + fprintf(f, " -L, --leftmargin <dim> - left margin\n"); + fprintf(f, " -R, --rightmargin <dim> - right margin\n"); + fprintf(f, " -T, --topmargin <dim> - top margin\n"); + fprintf(f, " -B, --bottommargin <dim> - bottom margin\n"); + fprintf(f, " --tight - remove whitespace around the input image\n"); + fprintf(f, "Color options, supported by some backends:\n"); + fprintf(f, " -C, --color #rrggbb - set foreground color (default black)\n"); + fprintf(f, " --fillcolor #rrggbb - set fill color (default transparent)\n"); + fprintf(f, " --opaque - make white shapes opaque\n"); + fprintf(f, "SVG options:\n"); + fprintf(f, " --group - group related paths together\n"); + fprintf(f, " --flat - whole image as a single path\n"); + fprintf(f, "Postscript/EPS/PDF options:\n"); + fprintf(f, " -c, --cleartext - do not compress the output\n"); + fprintf(f, " -2, --level2 - use postscript level 2 compression (default)\n"); +#ifdef HAVE_ZLIB + fprintf(f, " -3, --level3 - use postscript level 3 compression\n"); +#endif + fprintf(f, " -q, --longcoding - do not optimize for file size\n"); + fprintf(f, "PGM options:\n"); + fprintf(f, " -G, --gamma <n> - gamma value for anti-aliasing (default 2.2)\n"); + fprintf(f, "Frontend options:\n"); + fprintf(f, " -k, --blacklevel <n> - black/white cutoff in input file (default 0.5)\n"); + fprintf(f, " -i, --invert - invert bitmap\n"); + fprintf(f, "Progress bar options:\n"); + fprintf(f, " --progress - show progress bar\n"); + fprintf(f, " --tty <mode> - progress bar rendering: vt100 or dumb\n"); + fprintf(f, "\n"); + fprintf(f, "Dimensions can have optional units, e.g. 6.5in, 15cm, 100pt.\n"); + fprintf(f, "Default is " DEFAULT_DIM_NAME " (or pixels for pgm, dxf, and gimppath backends).\n"); + fprintf(f, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n"); + j = fprintf(f, "Backends are: "); + backend_list(f, j, 78); + fprintf(f, ".\n"); +} + +/* ---------------------------------------------------------------------- */ +/* auxiliary functions for parameter parsing */ + +/* parse a dimension of the kind "1.5in", "7cm", etc. Return result in + postscript points (=1/72 in). If endptr!=NULL, store pointer to + next character in *endptr in the manner of strtod(3). */ +static dim_t parse_dimension(char *s, char **endptr) { + char *p; + dim_t res; + + res.x = strtod(s, &p); + res.d = 0; + if (p!=s) { + if (!strncasecmp(p, "in", 2)) { + res.d = DIM_IN; + p += 2; + } else if (!strncasecmp(p, "cm", 2)) { + res.d = DIM_CM; + p += 2; + } else if (!strncasecmp(p, "mm", 2)) { + res.d = DIM_MM; + p += 2; + } else if (!strncasecmp(p, "pt", 2)) { + res.d = DIM_PT; + p += 2; + } + } + if (endptr!=NULL) { + *endptr = p; + } + return res; +} + +/* parse a pair of dimensions, such as "8.5x11in", "30mmx4cm" */ +static void parse_dimensions(char *s, char **endptr, dim_t *dxp, dim_t *dyp) { + char *p, *q; + dim_t dx, dy; + + dx = parse_dimension(s, &p); + if (p==s) { + goto fail; + } + if (*p != 'x') { + goto fail; + } + p++; + dy = parse_dimension(p, &q); + if (q==p) { + goto fail; + } + if (dx.d && !dy.d) { + dy.d = dx.d; + } else if (!dx.d && dy.d) { + dx.d = dy.d; + } + *dxp = dx; + *dyp = dy; + if (endptr != NULL) { + *endptr = q; + } + return; + + fail: + dx.x = dx.d = dy.x = dy.d = 0; + *dxp = dx; + *dyp = dy; + if (endptr != NULL) { + *endptr = s; + } + return; +} + +static inline double double_of_dim(dim_t d, double def) { + if (d.d) { + return d.x * d.d; + } else { + return d.x * def; + } +} + +static int parse_color(char *s) { + int i, d; + int col = 0; + + if (s[0] != '#' || strlen(s) != 7) { + return -1; + } + for (i=0; i<6; i++) { + d = s[6-i]; + if (d >= '0' && d <= '9') { + col |= (d-'0') << (4*i); + } else if (d >= 'a' && d <= 'f') { + col |= (d-'a'+10) << (4*i); + } else if (d >= 'A' && d <= 'F') { + col |= (d-'A'+10) << (4*i); + } else { + return -1; + } + } + return col; +} + +/* ---------------------------------------------------------------------- */ +/* option processing */ + +/* codes for options that don't have short form */ +enum { + OPT_TIGHT = 300, + OPT_FILLCOLOR, + OPT_OPAQUE, + OPT_GROUP, + OPT_FLAT, + OPT_PROGRESS, + OPT_TTY +}; + +static struct option longopts[] = { + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {"show-defaults", 0, 0, 'V'}, /* undocumented option for compatibility */ + {"license", 0, 0, 'l'}, + {"width", 1, 0, 'W'}, + {"height", 1, 0, 'H'}, + {"resolution", 1, 0, 'r'}, + {"scale", 1, 0, 'x'}, + {"stretch", 1, 0, 'S'}, + {"margin", 1, 0, 'M'}, + {"leftmargin", 1, 0, 'L'}, + {"rightmargin", 1, 0, 'R'}, + {"topmargin", 1, 0, 'T'}, + {"bottommargin", 1, 0, 'B'}, + {"tight", 0, 0, OPT_TIGHT}, + {"rotate", 1, 0, 'A'}, + {"pagesize", 1, 0, 'P'}, + {"turdsize", 1, 0, 't'}, + {"unit", 1, 0, 'u'}, + {"cleartext", 0, 0, 'c'}, + {"level2", 0, 0, '2'}, + {"level3", 0, 0, '3'}, + {"eps", 0, 0, 'e'}, + {"postscript", 0, 0, 'p'}, + {"svg", 0, 0, 's'}, + {"pgm", 0, 0, 'g'}, + {"backend", 1, 0, 'b'}, + {"debug", 1, 0, 'd'}, + {"color", 1, 0, 'C'}, + {"fillcolor", 1, 0, OPT_FILLCOLOR}, + {"turnpolicy", 1, 0, 'z'}, + {"gamma", 1, 0, 'G'}, + {"longcurve", 0, 0, 'n'}, + {"longcoding", 0, 0, 'q'}, + {"alphamax", 1, 0, 'a'}, + {"opttolerance", 1, 0, 'O'}, + {"output", 1, 0, 'o'}, + {"blacklevel", 1, 0, 'k'}, + {"invert", 0, 0, 'i'}, + {"opaque", 0, 0, OPT_OPAQUE}, + {"group", 0, 0, OPT_GROUP}, + {"flat", 0, 0, OPT_FLAT}, + {"progress", 0, 0, OPT_PROGRESS}, + {"tty", 1, 0, OPT_TTY}, + + {0, 0, 0, 0} +}; + +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"; + +static void dopts(int ac, char *av[]) { + int c; + char *p; + int i, j, r; + dim_t dim, dimx, dimy; + int matches, bestmatch; + + /* defaults */ + backend_lookup("eps", &info.backend); + info.debug = 0; + info.width_d.x = UNDEF; + info.height_d.x = UNDEF; + info.rx = UNDEF; + info.ry = UNDEF; + info.sx = UNDEF; + info.sy = UNDEF; + info.stretch = 1; + info.lmar_d.x = UNDEF; + info.rmar_d.x = UNDEF; + info.tmar_d.x = UNDEF; + info.bmar_d.x = UNDEF; + info.angle = 0; + info.paperwidth = DEFAULT_PAPERWIDTH; + info.paperheight = DEFAULT_PAPERHEIGHT; + info.tight = 0; + info.unit = 10; + info.compress = 1; + info.pslevel = 2; + info.color = 0x000000; + info.gamma = 2.2; + info.param = potrace_param_default(); + if (!info.param) { + fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); + exit(2); + } + info.longcoding = 0; + info.outfile = NULL; + info.blacklevel = 0.5; + info.invert = 0; + info.opaque = 0; + info.grouping = 1; + info.fillcolor = 0xffffff; + info.progress = 0; + info.progress_bar = DEFAULT_PROGRESS_BAR; + + while ((c = getopt_long(ac, av, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'h': + fprintf(stdout, "" POTRACE " " VERSION ". Transforms bitmaps into vector graphics.\n\n"); + usage(stdout); + exit(0); + break; + case 'v': + case 'V': + fprintf(stdout, "" POTRACE " " VERSION ". Copyright (C) 2001-2019 Peter Selinger.\n"); + fprintf(stdout, "Library version: %s\n", potrace_version()); + show_defaults(stdout); + exit(0); + break; + case 'l': + fprintf(stdout, "" POTRACE " " VERSION ". Copyright (C) 2001-2019 Peter Selinger.\n\n"); + license(stdout); + exit(0); + break; + case 'W': + info.width_d = parse_dimension(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); + exit(1); + } + break; + case 'H': + info.height_d = parse_dimension(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); + exit(1); + } + break; + case 'r': + parse_dimensions(optarg, &p, &dimx, &dimy); + if (*p == 0 && dimx.d == 0 && dimy.d == 0 && dimx.x != 0.0 && dimy.x != 0.0) { + info.rx = dimx.x; + info.ry = dimy.x; + break; + } + dim = parse_dimension(optarg, &p); + if (*p == 0 && dim.d == 0 && dim.x != 0.0) { + info.rx = info.ry = dim.x; + break; + } + fprintf(stderr, "" POTRACE ": invalid resolution -- %s\n", optarg); + exit(1); + break; + case 'x': + parse_dimensions(optarg, &p, &dimx, &dimy); + if (*p == 0 && dimx.d == 0 && dimy.d == 0) { + info.sx = dimx.x; + info.sy = dimy.x; + break; + } + dim = parse_dimension(optarg, &p); + if (*p == 0 && dim.d == 0) { + info.sx = info.sy = dim.x; + break; + } + fprintf(stderr, "" POTRACE ": invalid scaling factor -- %s\n", optarg); + exit(1); + break; + case 'S': + info.stretch = atof(optarg); + break; + case 'M': + info.lmar_d = parse_dimension(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); + exit(1); + } + info.rmar_d = info.tmar_d = info.bmar_d = info.lmar_d; + break; + case 'L': + info.lmar_d = parse_dimension(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); + exit(1); + } + break; + case 'R': + info.rmar_d = parse_dimension(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); + exit(1); + } + break; + case 'T': + info.tmar_d = parse_dimension(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); + exit(1); + } + break; + case 'B': + info.bmar_d = parse_dimension(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid dimension -- %s\n", optarg); + exit(1); + } + break; + case OPT_TIGHT: + info.tight = 1; + break; + case 'A': + info.angle = strtod(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid angle -- %s\n", optarg); + exit(1); + } + if (info.angle <= -180 || info.angle > 180) { + info.angle -= 360 * ceil(info.angle / 360 - 0.5); + } + break; + case 'P': + matches = 0; + bestmatch = 0; + for (i=0; pageformat[i].name!=NULL; i++) { + if (strcasecmp(pageformat[i].name, optarg)==0) { + matches = 1; + bestmatch = i; + break; + } else if (strncasecmp(pageformat[i].name, optarg, strlen(optarg))==0) { + /* don't allow partial match on "10x14" */ + if (optarg[0] != '1') { + matches++; + bestmatch = i; + } + } + } + if (matches == 1) { + info.paperwidth = pageformat[bestmatch].w; + info.paperheight = pageformat[bestmatch].h; + break; + } + parse_dimensions(optarg, &p, &dimx, &dimy); + if (*p == 0) { + info.paperwidth = (int)round(double_of_dim(dimx, DEFAULT_DIM)); + info.paperheight = (int)round(double_of_dim(dimy, DEFAULT_DIM)); + break; + } + if (matches == 0) { + fprintf(stderr, "" POTRACE ": unrecognized page format -- %s\n", optarg); + } else { + fprintf(stderr, "" POTRACE ": ambiguous page format -- %s\n", optarg); + } + j = fprintf(stderr, "Use one of: "); + for (i=0; pageformat[i].name!=NULL; i++) { + if (j + strlen(pageformat[i].name) > 75) { + fprintf(stderr, "\n"); + j = 0; + } + j += fprintf(stderr, "%s, ", pageformat[i].name); + } + fprintf(stderr, "or specify <dim>x<dim>.\n"); + exit(1); + break; + case 't': + info.param->turdsize = atoi(optarg); + break; + case 'u': + info.unit = strtod(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid unit -- %s\n", optarg); + exit(1); + } + break; + case 'c': + info.pslevel = 2; + info.compress = 0; + break; + case '2': + info.pslevel = 2; + info.compress = 1; + break; + case '3': +#ifdef HAVE_ZLIB + info.pslevel = 3; + info.compress = 1; +#else + fprintf(stderr, "" POTRACE ": option -3 not supported, using -2 instead.\n"); + fflush(stderr); + info.pslevel = 2; + info.compress = 1; +#endif + break; + case 'e': + backend_lookup("eps", &info.backend); + break; + case 'p': + backend_lookup("postscript", &info.backend); + break; + case 's': + backend_lookup("svg", &info.backend); + break; + case 'g': + backend_lookup("pgm", &info.backend); + break; + case 'b': + r = backend_lookup(optarg, &info.backend); + if (r==1 || r==2) { + if (r==1) { + fprintf(stderr, "" POTRACE ": unrecognized backend -- %s\n", optarg); + } else { + fprintf(stderr, "" POTRACE ": ambiguous backend -- %s\n", optarg); + } + j = fprintf(stderr, "Use one of: "); + backend_list(stderr, j, 70); + fprintf(stderr, ".\n"); + exit(1); + } + break; + case 'd': + info.debug = atoi(optarg); + break; + case 'C': + info.color = parse_color(optarg); + if (info.color == -1) { + fprintf(stderr, "" POTRACE ": invalid color -- %s\n", optarg); + exit(1); + } + break; + case OPT_FILLCOLOR: + info.fillcolor = parse_color(optarg); + if (info.fillcolor == -1) { + fprintf(stderr, "" POTRACE ": invalid color -- %s\n", optarg); + exit(1); + } + info.opaque = 1; + break; + case 'z': + matches = 0; + bestmatch = 0; + for (i=0; turnpolicy[i].name!=NULL; i++) { + if (strcasecmp(turnpolicy[i].name, optarg)==0) { + matches = 1; + bestmatch = i; + break; + } else if (strncasecmp(turnpolicy[i].name, optarg, strlen(optarg))==0) { + matches++; + bestmatch = i; + } + } + if (matches == 1) { + info.param->turnpolicy = turnpolicy[bestmatch].n; + break; + } + if (matches == 0) { + fprintf(stderr, "" POTRACE ": unrecognized turnpolicy -- %s\n", optarg); + } else { + fprintf(stderr, "" POTRACE ": ambiguous turnpolicy -- %s\n", optarg); + } + j = fprintf(stderr, "Use one of: "); + for (i=0; turnpolicy[i].name!=NULL; i++) { + if (j + strlen(turnpolicy[i].name) > 75) { + fprintf(stderr, "\n"); + j = 0; + } + j += fprintf(stderr, "%s%s", turnpolicy[i].name, turnpolicy[i+1].name ? ", " : ""); + } + fprintf(stderr, ".\n"); + exit(1); + break; + case 'G': + info.gamma = atof(optarg); + break; + case 'n': + info.param->opticurve = 0; + break; + case 'q': + info.longcoding = 1; + break; + case 'a': + info.param->alphamax = strtod(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid alphamax -- %s\n", optarg); + exit(1); + } + break; + case 'O': + info.param->opttolerance = strtod(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid opttolerance -- %s\n", optarg); + exit(1); + } + break; + case 'o': + free(info.outfile); + info.outfile = strdup(optarg); + if (!info.outfile) { + fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); + exit(2); + } + break; + case 'k': + info.blacklevel = strtod(optarg, &p); + if (*p) { + fprintf(stderr, "" POTRACE ": invalid blacklevel -- %s\n", optarg); + exit(1); + } + break; + case 'i': + info.invert = 1; + break; + case OPT_OPAQUE: + info.opaque = 1; + break; + case OPT_GROUP: + info.grouping = 2; + break; + case OPT_FLAT: + info.grouping = 0; + break; + case OPT_PROGRESS: + info.progress = 1; + break; + case OPT_TTY: + if (strcmp(optarg, "dumb") == 0) { + info.progress_bar = progress_bar_simplified; + } else if (strcmp(optarg, "vt100") == 0) { + info.progress_bar = progress_bar_vt100; + } else { + fprintf(stderr, "" POTRACE ": invalid tty mode -- %s. Try --help for more info\n", optarg); + exit(1); + } + break; + case '?': + fprintf(stderr, "Try --help for more info\n"); + exit(1); + break; + default: + fprintf(stderr, "" POTRACE ": Unimplemented option -- %c\n", c); + exit(1); + } + } + info.infiles = &av[optind]; + info.infilecount = ac-optind; + info.some_infiles = info.infilecount ? 1 : 0; + + /* if "--" was used, even an empty list of filenames is considered + "some" filenames. */ + if (strcmp(av[optind-1], "--") == 0) { + info.some_infiles = 1; + } +} + +/* ---------------------------------------------------------------------- */ +/* calculations with bitmap dimensions, positioning etc */ + +/* determine the dimensions of the output based on command line and + image dimensions, and optionally, based on the actual image outline. */ +static void calc_dimensions(imginfo_t *imginfo, potrace_path_t *plist) { + double dim_def; + double maxwidth, maxheight, sc; + int default_scaling = 0; + + /* we take care of a special case: if one of the image dimensions is + 0, we change it to 1. Such an image is empty anyway, so there + will be 0 paths in it. Changing the dimensions avoids division by + 0 error in calculating scaling factors, bounding boxes and + such. This doesn't quite do the right thing in all cases, but it + is better than causing overflow errors or "nan" output in + backends. Human users don't tend to process images of size 0 + anyway; they might occur in some pipelines. */ + if (imginfo->pixwidth == 0) { + imginfo->pixwidth = 1; + } + if (imginfo->pixheight == 0) { + imginfo->pixheight = 1; + } + + /* set the default dimension for width, height, margins */ + if (info.backend->pixel) { + dim_def = DIM_PT; + } else { + dim_def = DEFAULT_DIM; + } + + /* apply default dimension to width, height, margins */ + imginfo->width = info.width_d.x == UNDEF ? UNDEF : double_of_dim(info.width_d, dim_def); + imginfo->height = info.height_d.x == UNDEF ? UNDEF : double_of_dim(info.height_d, dim_def); + imginfo->lmar = info.lmar_d.x == UNDEF ? UNDEF : double_of_dim(info.lmar_d, dim_def); + imginfo->rmar = info.rmar_d.x == UNDEF ? UNDEF : double_of_dim(info.rmar_d, dim_def); + imginfo->tmar = info.tmar_d.x == UNDEF ? UNDEF : double_of_dim(info.tmar_d, dim_def); + imginfo->bmar = info.bmar_d.x == UNDEF ? UNDEF : double_of_dim(info.bmar_d, dim_def); + + /* start with a standard rectangle */ + trans_from_rect(&imginfo->trans, imginfo->pixwidth, imginfo->pixheight); + + /* if info.tight is set, tighten the bounding box */ + if (info.tight) { + trans_tighten(&imginfo->trans, plist); + } + + /* sx/rx is just an alternate way to specify width; sy/ry is just an + alternate way to specify height. */ + if (info.backend->pixel) { + if (imginfo->width == UNDEF && info.sx != UNDEF) { + imginfo->width = imginfo->trans.bb[0] * info.sx; + } + if (imginfo->height == UNDEF && info.sy != UNDEF) { + imginfo->height = imginfo->trans.bb[1] * info.sy; + } + } else { + if (imginfo->width == UNDEF && info.rx != UNDEF) { + imginfo->width = imginfo->trans.bb[0] / info.rx * 72; + } + if (imginfo->height == UNDEF && info.ry != UNDEF) { + imginfo->height = imginfo->trans.bb[1] / info.ry * 72; + } + } + + /* if one of width/height is specified, use stretch to determine the + other */ + if (imginfo->width == UNDEF && imginfo->height != UNDEF) { + imginfo->width = imginfo->height / imginfo->trans.bb[1] * imginfo->trans.bb[0] / info.stretch; + } else if (imginfo->width != UNDEF && imginfo->height == UNDEF) { + imginfo->height = imginfo->width / imginfo->trans.bb[0] * imginfo->trans.bb[1] * info.stretch; + } + + /* if width and height are still variable, tenatively use the + default scaling factor of 72dpi (for dimension-based backends) or + 1 (for pixel-based backends). For fixed-size backends, this will + be adjusted later to fit the page. */ + if (imginfo->width == UNDEF && imginfo->height == UNDEF) { + imginfo->width = imginfo->trans.bb[0]; + imginfo->height = imginfo->trans.bb[1] * info.stretch; + default_scaling = 1; + } + + /* apply scaling */ + trans_scale_to_size(&imginfo->trans, imginfo->width, imginfo->height); + + /* apply rotation, and tighten the bounding box again, if necessary */ + if (info.angle != 0.0) { + trans_rotate(&imginfo->trans, info.angle); + if (info.tight) { + trans_tighten(&imginfo->trans, plist); + } + } + + /* for fixed-size backends, if default scaling was in effect, + further adjust the scaling to be the "best fit" for the given + page size and margins. */ + if (default_scaling && info.backend->fixed) { + + /* try to squeeze it between margins */ + maxwidth = UNDEF; + maxheight = UNDEF; + + if (imginfo->lmar != UNDEF && imginfo->rmar != UNDEF) { + maxwidth = info.paperwidth - imginfo->lmar - imginfo->rmar; + } + if (imginfo->bmar != UNDEF && imginfo->tmar != UNDEF) { + maxheight = info.paperheight - imginfo->bmar - imginfo->tmar; + } + if (maxwidth == UNDEF && maxheight == UNDEF) { + maxwidth = max(info.paperwidth - 144, info.paperwidth * 0.75); + maxheight = max(info.paperheight - 144, info.paperheight * 0.75); + } + + if (maxwidth == UNDEF) { + sc = maxheight / imginfo->trans.bb[1]; + } else if (maxheight == UNDEF) { + sc = maxwidth / imginfo->trans.bb[0]; + } else { + sc = min(maxwidth / imginfo->trans.bb[0], maxheight / imginfo->trans.bb[1]); + } + + /* re-scale coordinate system */ + imginfo->width *= sc; + imginfo->height *= sc; + trans_rescale(&imginfo->trans, sc); + } + + /* adjust margins */ + if (info.backend->fixed) { + if (imginfo->lmar == UNDEF && imginfo->rmar == UNDEF) { + imginfo->lmar = (info.paperwidth-imginfo->trans.bb[0])/2; + } else if (imginfo->lmar == UNDEF) { + imginfo->lmar = (info.paperwidth-imginfo->trans.bb[0]-imginfo->rmar); + } else if (imginfo->lmar != UNDEF && imginfo->rmar != UNDEF) { + imginfo->lmar += (info.paperwidth-imginfo->trans.bb[0]-imginfo->lmar-imginfo->rmar)/2; + } + if (imginfo->bmar == UNDEF && imginfo->tmar == UNDEF) { + imginfo->bmar = (info.paperheight-imginfo->trans.bb[1])/2; + } else if (imginfo->bmar == UNDEF) { + imginfo->bmar = (info.paperheight-imginfo->trans.bb[1]-imginfo->tmar); + } else if (imginfo->bmar != UNDEF && imginfo->tmar != UNDEF) { + imginfo->bmar += (info.paperheight-imginfo->trans.bb[1]-imginfo->bmar-imginfo->tmar)/2; + } + } else { + if (imginfo->lmar == UNDEF) { + imginfo->lmar = 0; + } + if (imginfo->rmar == UNDEF) { + imginfo->rmar = 0; + } + if (imginfo->bmar == UNDEF) { + imginfo->bmar = 0; + } + if (imginfo->tmar == UNDEF) { + imginfo->tmar = 0; + } + } +} + +/* ---------------------------------------------------------------------- */ +/* auxiliary functions for file handling */ + +/* open a file for reading. Return stdin if filename is NULL or "-" */ +static FILE *my_fopen_read(const char *filename) { + if (filename == NULL || strcmp(filename, "-") == 0) { + return stdin; + } + return fopen(filename, "rb"); +} + +/* open a file for writing. Return stdout if filename is NULL or "-" */ +static FILE *my_fopen_write(const char *filename) { + if (filename == NULL || strcmp(filename, "-") == 0) { + return stdout; + } + return fopen(filename, "wb"); +} + +/* close a file, but do nothing is filename is NULL or "-" */ +static void my_fclose(FILE *f, const char *filename) { + if (filename == NULL || strcmp(filename, "-") == 0) { + return; + } + fclose(f); +} + +/* make output filename from input filename. Return an allocated value. */ +static char *make_outfilename(const char *infile, const char *ext) { + char *outfile; + char *p; + + if (strcmp(infile, "-") == 0) { + return strdup("-"); + } + + outfile = (char *) malloc(strlen(infile)+strlen(ext)+5); + if (!outfile) { + return NULL; + } + strcpy(outfile, infile); + p = strrchr(outfile, '.'); + if (p) { + *p = 0; + } + strcat(outfile, ext); + + /* check that input and output filenames are different */ + if (strcmp(infile, outfile) == 0) { + strcpy(outfile, infile); + strcat(outfile, "-out"); + } + + return outfile; +} + +/* ---------------------------------------------------------------------- */ +/* Process one infile */ + +/* Process one or more bitmaps from fin, and write the results to fout + using the page_f function of the appropriate backend. */ + +static void process_file(backend_t *b, const char *infile, const char *outfile, FILE *fin, FILE *fout) { + int r; + potrace_bitmap_t *bm = NULL; + imginfo_t imginfo; + int eof_flag = 0; /* to indicate premature eof */ + int count; /* number of bitmaps successfully processed, this file */ + potrace_state_t *st; + + for (count=0; ; count++) { + /* read a bitmap */ + r = bm_read(fin, info.blacklevel, &bm); + switch (r) { + case -1: /* system error */ + fprintf(stderr, "" POTRACE ": %s: %s\n", infile, strerror(errno)); + exit(2); + case -2: /* corrupt file format */ + fprintf(stderr, "" POTRACE ": %s: file format error: %s\n", infile, bm_read_error); + exit(2); + case -3: /* empty file */ + if (count>0) { /* end of file */ + return; + } + fprintf(stderr, "" POTRACE ": %s: empty file\n", infile); + exit(2); + case -4: /* wrong magic */ + if (count>0) { + fprintf(stderr, "" POTRACE ": %s: warning: junk at end of file\n", infile); + return; + } + fprintf(stderr, "" POTRACE ": %s: file format not recognized\n", infile); + fprintf(stderr, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n"); + exit(2); + case 1: /* unexpected end of file */ + fprintf(stderr, "" POTRACE ": warning: %s: premature end of file\n", infile); + eof_flag = 1; + break; + } + + /* prepare progress bar, if requested */ + if (info.progress) { + r = info.progress_bar->init(&info.param->progress, infile, count); + if (r) { + fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); + exit(2); + } + } else { + info.param->progress.callback = NULL; + } + + if (info.invert) { + bm_invert(bm); + } + + /* process the image */ + st = potrace_trace(info.param, bm); + if (!st || st->status != POTRACE_STATUS_OK) { + fprintf(stderr, "" POTRACE ": %s: %s\n", infile, strerror(errno)); + exit(2); + } + + /* calculate image dimensions */ + imginfo.pixwidth = bm->w; + imginfo.pixheight = bm->h; + bm_free(bm); + + calc_dimensions(&imginfo, st->plist); + + r = b->page_f(fout, st->plist, &imginfo); + if (r) { + fprintf(stderr, "" POTRACE ": %s: %s\n", outfile, strerror(errno)); + exit(2); + } + + potrace_state_free(st); + + if (info.progress) { + info.progress_bar->term(&info.param->progress); + } + + if (eof_flag || !b->multi) { + return; + } + } + /* not reached */ +} + +/* ---------------------------------------------------------------------- */ +/* main: handle file i/o */ + +#define TRY(x) if (x) goto try_error + +int main(int ac, char *av[]) { + backend_t *b; /* backend info */ + FILE *fin, *fout; + int i; + char *outfile; + + /* platform-specific initializations, e.g., set file i/o to binary */ + platform_init(); + + /* process options */ + dopts(ac, av); + + b = info.backend; + if (b==NULL) { + fprintf(stderr, "" POTRACE ": internal error: selected backend not found\n"); + exit(1); + } + + /* fix some parameters */ + /* if backend cannot handle opticurve, disable it */ + if (b->opticurve == 0) { + info.param->opticurve = 0; + } + + /* there are several ways to call us: + potrace -- stdin to stdout + potrace -o outfile -- stdin to outfile + potrace file... -- encode each file and generate outfile names + potrace -o outfile file... -- concatenate files and write to outfile + + The latter form is only allowed one file for single-page + backends. For multi-page backends, each file must contain 0 or + more complete bitmaps. + */ + + if (!info.some_infiles) { /* read from stdin */ + + fout = my_fopen_write(info.outfile); + if (!fout) { + fprintf(stderr, "" POTRACE ": %s: %s\n", info.outfile ? info.outfile : "stdout", strerror(errno)); + exit(2); + } + if (b->init_f) { + TRY(b->init_f(fout)); + } + process_file(b, "stdin", info.outfile ? info.outfile : "stdout", stdin, fout); + if (b->term_f) { + TRY(b->term_f(fout)); + } + my_fclose(fout, info.outfile); + free(info.outfile); + potrace_param_free(info.param); + return 0; + + } else if (!info.outfile) { /* infiles -> multiple outfiles */ + + for (i=0; i<info.infilecount; i++) { + outfile = make_outfilename(info.infiles[i], b->ext); + if (!outfile) { + fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); + exit(2); + } + fin = my_fopen_read(info.infiles[i]); + if (!fin) { + fprintf(stderr, "" POTRACE ": %s: %s\n", info.infiles[i], strerror(errno)); + exit(2); + } + fout = my_fopen_write(outfile); + if (!fout) { + fprintf(stderr, "" POTRACE ": %s: %s\n", outfile, strerror(errno)); + exit(2); + } + if (b->init_f) { + TRY(b->init_f(fout)); + } + process_file(b, info.infiles[i], outfile, fin, fout); + if (b->term_f) { + TRY(b->term_f(fout)); + } + my_fclose(fin, info.infiles[i]); + my_fclose(fout, outfile); + free(outfile); + } + potrace_param_free(info.param); + return 0; + + } else { /* infiles to single outfile */ + + if (!b->multi && info.infilecount >= 2) { + fprintf(stderr, "" POTRACE ": cannot use multiple input files with -o in %s mode\n", b->name); + exit(1); + } + if (info.infilecount == 0) { + fprintf(stderr, "" POTRACE ": cannot use empty list of input files with -o\n"); + exit(1); + } + + fout = my_fopen_write(info.outfile); + if (!fout) { + fprintf(stderr, "" POTRACE ": %s: %s\n", info.outfile, strerror(errno)); + exit(2); + } + if (b->init_f) { + TRY(b->init_f(fout)); + } + for (i=0; i<info.infilecount; i++) { + fin = my_fopen_read(info.infiles[i]); + if (!fin) { + fprintf(stderr, "" POTRACE ": %s: %s\n", info.infiles[i], strerror(errno)); + exit(2); + } + process_file(b, info.infiles[i], info.outfile, fin, fout); + my_fclose(fin, info.infiles[i]); + } + if (b->term_f) { + TRY(b->term_f(fout)); + } + my_fclose(fout, info.outfile); + free(info.outfile); + potrace_param_free(info.param); + return 0; + + } + + /* not reached */ + + try_error: + fprintf(stderr, "" POTRACE ": %s\n", strerror(errno)); + exit(2); +} |