diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | src/main.cpp | 159 | ||||
-rw-r--r-- | src/vec_core.cpp | 2 | ||||
m--------- | upstream/subprocess.h | 0 |
5 files changed, 156 insertions, 11 deletions
diff --git a/.gitmodules b/.gitmodules index 36615a6..e42d3c4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "upstream/CavalierContours"] path = upstream/CavalierContours url = https://github.com/jbuckmccready/CavalierContours +[submodule "upstream/subprocess.h"] + path = upstream/subprocess.h + url = https://github.com/sheredom/subprocess.h @@ -26,9 +26,10 @@ POISSON_INCLUDES ?= -Iupstream/poisson-disk-sampling/thinks/poisson_disk_sampli BASE64_INCLUDES ?= -Iupstream/cpp-base64 ARGAGG_INCLUDES ?= -Iupstream/argagg/include/argagg CAVC_INCLUDES ?= -Iupstream/CavalierContours/include/cavc/ +SUBPROCESS_INCLUDES ?= -Iupstream/subprocess.h SOURCES += $(CLIPPER_SOURCES) -INCLUDES := -Iinclude -Isrc $(CLIPPER_INCLUDES) $(VORONOI_INCLUDES) $(POISSON_INCLUDES) $(BASE64_INCLUDES) $(ARGAGG_INCLUDES) $(CAVC_INCLUDES) +INCLUDES := -Iinclude -Isrc $(CLIPPER_INCLUDES) $(VORONOI_INCLUDES) $(POISSON_INCLUDES) $(BASE64_INCLUDES) $(ARGAGG_INCLUDES) $(CAVC_INCLUDES) $(SUBPROCESS_INCLUDES) CXXFLAGS := -std=c++2a -g -Wall -Wextra -O0 CXXFLAGS += $(shell $(PKG_CONFIG) --cflags pangocairo pugixml opencv4) diff --git a/src/main.cpp b/src/main.cpp index 93b6e8f..931ab74 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,16 @@ #include <cstdlib> +#include <cstdio> +#include <filesystem> #include <iostream> #include <fstream> #include <algorithm> #include <string> #include <argagg.hpp> +#include <subprocess.h> #include <gerbolyze.hpp> #include "vec_core.h" +#include <base64.h> using argagg::parser_results; using argagg::parser; @@ -33,6 +37,9 @@ int main(int argc, char **argv) { {"svg_dark_color", {"--dark-color"}, "SVG color to use for \"dark\" areas (default: black)", 1}, + {"min_feature_size", {"-d", "--trace-space"}, + "Minimum feature size of elements in vectorized graphics (trace/space) in mm. Default: 0.1mm.", + 1}, {"no_header", {"--no-header"}, "Do not export output format header/footer, only export the primitives themselves", 0}, @@ -48,6 +55,18 @@ int main(int argc, char **argv) { {"vectorizer_map", {"--vectorizer-map"}, "Map from image element id to vectorizer. Overrides --vectorizer. Format: id1=vectorizer,id2=vectorizer,...", 1}, + {"force_svg", {"--force-svg"}, + "Force SVG input irrespective of file name", + 0}, + {"force_png", {"--force-png"}, + "Force bitmap graphics input irrespective of file name", + 0}, + {"size", {"-s", "--size"}, + "Bitmap mode only: Physical size of output image in mm. Format: 12.34x56.78", + 1}, + {"skip_usvg", {"--no-usvg"}, + "Do not preprocess input using usvg (do not use unless you know *exactly* what you're doing)", + 0}, {"exclude_groups", {"-e", "--exclude-groups"}, "Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups.", 1}, @@ -119,13 +138,6 @@ int main(int argc, char **argv) { out_f = &out_f_file; } - - SVGDocument doc; - if (!doc.load(*in_f)) { - cerr << "Error loading input file \"" << in_f_name << "\", exiting." << endl; - return EXIT_FAILURE; - } - bool only_polys = args["no_header"]; int precision = 6; @@ -134,7 +146,7 @@ int main(int argc, char **argv) { } string fmt = args["ofmt"] ? args["ofmt"] : "gerber"; - transform(fmt.begin(), fmt.end(), fmt.begin(), [](unsigned char c){ return std::tolower(c); }); + transform(fmt.begin(), fmt.end(), fmt.begin(), [](unsigned char c){ return std::tolower(c); }); /* c++ yeah */ PolygonSink *sink = nullptr; PolygonSink *flattener = nullptr; @@ -182,13 +194,142 @@ int main(int argc, char **argv) { } delete vec; + /* + double min_feature_size_px = mm_to_doc_units(rset.m_minimum_feature_size_mm); + vec->vectorize_image(cr, node, clip_path, viewport_matrix, *polygon_sink, min_feature_size_px); + delete vec; + */ + double min_feature_size = 0.1; /* mm */ + if (args["min_feature_size"]) { + min_feature_size = args["min_feature_size"].as<double>(); + } + + string ending = ""; + auto idx = in_f_name.rfind("."); + if (idx != string::npos) { + ending = in_f_name.substr(idx); + transform(ending.begin(), ending.end(), ending.begin(), [](unsigned char c){ return std::tolower(c); }); /* c++ yeah */ + } + + filesystem::path barf = { filesystem::temp_directory_path() /= (std::tmpnam(nullptr) + string(".svg")) }; + filesystem::path frob = { filesystem::temp_directory_path() /= (std::tmpnam(nullptr) + string(".svg")) }; + + bool is_svg = args["force_svg"] || (ending == ".svg" && !args["force_png"]); + if (!is_svg) { + cerr << "writing bitmap into svg" << endl; + if (!args["size"]) { + cerr << "Error: --size must be given when using bitmap input." << endl; + argagg::fmt_ostream fmt(cerr); + fmt << usage.str() << argparser; + return EXIT_FAILURE; + } + + string sz = args["size"].as<string>(); + auto pos = sz.find_first_of("x*,"); + if (pos == string::npos) { + cerr << "Error: --size must be of form 12.34x56.78" << endl; + argagg::fmt_ostream fmt(cerr); + fmt << usage.str() << argparser; + return EXIT_FAILURE; + } + + string x_str = sz.substr(0, pos); + string y_str = sz.substr(pos+1); + + double width = std::strtod(x_str.c_str(), nullptr); + double height = std::strtod(y_str.c_str(), nullptr); + + if (width < 1 || height < 1) { + cerr << "Error: --size must be of form 12.34x56.78 and values must be positive floating-point numbers in mm" << endl; + argagg::fmt_ostream fmt(cerr); + fmt << usage.str() << argparser; + return EXIT_FAILURE; + } + + ofstream svg(barf.c_str()); + + svg << "<svg width=\"" << width << "mm\" height=\"" << height << "mm\" viewBox=\"0 0 " + << width << " " << height << "\" " + << "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << endl; + + svg << "<image width=\"" << width << "\" height=\"" << height << "\" x=\"0\" y=\"0\" xlink:href=\"data:image/png;base64,"; + + /* c++ has the best hacks */ + std::ostringstream sstr; + sstr << in_f->rdbuf(); + string le_data = sstr.str(); + + svg << base64_encode(le_data); + svg << "\"/>" << endl; + + svg << "</svg>" << endl; + svg.close(); + + } else { /* svg file */ + cerr << "copying svg input into temp svg" << endl; + + /* c++ has the best hacks */ + std::ostringstream sstr; + sstr << in_f->rdbuf(); + + ofstream tmp_out(barf.c_str()); + tmp_out << sstr.str(); + tmp_out.close(); + + } + + if (args["skip_usvg"]) { + cerr << "skippint usvg" << endl; + frob = barf; + + } else { + cerr << "calling usvg on " << barf << " and " << frob << endl; + const char *command_line[] = {"usvg", barf.c_str(), frob.c_str(), NULL}; + struct subprocess_s subprocess; + int rc = subprocess_create(command_line, subprocess_option_inherit_environment, &subprocess); + if (rc) { + cerr << "Error calling usvg!" << endl; + return EXIT_FAILURE; + } + + int usvg_rc = 0; + rc = subprocess_join(&subprocess, &usvg_rc); + if (rc) { + cerr << "Error calling usvg!" << endl; + return EXIT_FAILURE; + } + if (usvg_rc) { + cerr << "usvg returned an error code: " << usvg_rc << endl; + return EXIT_FAILURE; + } + + rc = subprocess_destroy(&subprocess); + if (rc) { + cerr << "Error calling usvg!" << endl; + return EXIT_FAILURE; + } + } + VectorizerSelectorizer vec_sel(vectorizer, args["vectorizer_map"] ? args["vectorizer_map"] : ""); RenderSettings rset { - 0.1, + min_feature_size, vec_sel, }; + SVGDocument doc; + cerr << "Loading temporary file " << frob << endl; + ifstream load_f(frob); + if (!doc.load(load_f)) { + cerr << "Error loading input file \"" << in_f_name << "\", exiting." << endl; + return EXIT_FAILURE; + } + doc.render(rset, flattener ? *flattener : *sink, &sel); + if (!is_svg) { + remove(frob.c_str()); + remove(barf.c_str()); + } return EXIT_SUCCESS; } + diff --git a/src/vec_core.cpp b/src/vec_core.cpp index 93c7a68..81a2a96 100644 --- a/src/vec_core.cpp +++ b/src/vec_core.cpp @@ -36,7 +36,7 @@ ImageVectorizer *gerbolyze::makeVectorizer(const std::string &name) { else if (name == "hex-grid") return new VoronoiVectorizer(HEXGRID, /* relax */ false); else if (name == "square-grid") - return new VoronoiVectorizer(SQUAREGRID, /* relax */ false); + return new VoronoiVectorizer(SQUAREGRID, /* relax */ true); else if (name == "binary-contours") return new OpenCVContoursVectorizer(); else if (name == "dev-null") diff --git a/upstream/subprocess.h b/upstream/subprocess.h new file mode 160000 +Subproject d4d5c01ba9ffbb19fd552f8f3664691b44ff0ea |