20#include <boost/algorithm/string.hpp>
21#include <giomm/file.h>
22#include <glibmm/convert.h>
23#include <glibmm/fileutils.h>
24#include <glibmm/miscutils.h>
25#include <glibmm/regex.h>
49namespace filesystem = std::filesystem;
76 std::string export_type_filename;
77 std::vector<Glib::ustring> export_type_list;
83 Glib::RefPtr<Gio::File> gfile = Gio::File::create_for_parse_name(
export_filename);
91 if (!fn.has_extension()) {
93 std::cerr <<
"InkFileExportCmd::do_export: No export type specified. "
94 <<
"Append a supported file extension to filename provided with --export-filename or "
95 <<
"provide one or more extensions separately using --export-type" << std::endl;
102 export_type_filename = fn.extension().string().substr(1);
103 boost::algorithm::to_lower(export_type_filename);
110 export_type_list = Glib::Regex::split_simple(
"[,;]",
export_type);
118 std::cerr <<
"InkFileExportCmd::do_export: "
119 <<
"--export-use-hints can only be used with --export-id or --export-area-drawing." << std::endl;
122 if (export_type_list.size() > 1 || (export_type_list.size() == 1 && export_type_list[0] !=
"png")) {
123 std::cerr <<
"InkFileExportCmd::do_export: --export-use-hints can only be used with PNG export! "
124 <<
"Ignoring --export-type=" <<
export_type.raw() <<
"." << std::endl;
127 std::cerr <<
"InkFileExportCmd::do_export: --export-filename is ignored when using --export-use-hints!" << std::endl;
129 export_type_list.clear();
130 export_type_list.emplace_back(
"png");
131 }
else if (export_type_list.empty()) {
132 if (!export_type_filename.empty()) {
133 export_type_list.emplace_back(export_type_filename);
139 export_type_list.emplace_back(std::string(ext->get_extension()).substr(1));
141 std::cerr <<
"InkFileExportCmd::do_export: "
142 <<
"The supplied --export-extension was not found. Specify a file extension "
143 <<
"to get a list of available extensions for this file type.";
147 export_type_list.emplace_back(
"svg");
153 <<
"InkFileExportCmd::do_export: You may only specify one export type if --export-extension is supplied";
162 auto path_out = Glib::path_get_dirname(filename_out);
164 g_mkdir_with_parents(path_out.c_str(), 0755);
168 std::cerr <<
"InkFileExportCmd::do_export: "
169 <<
"file directory doesn't exist for export: "
170 << path_out.c_str() << std::endl;
174 for (
auto const &Type : export_type_list) {
176 auto type = Type.lowercase();
177 g_info(
"exporting '%s' to type '%s'", filename_in.c_str(), type.c_str());
182 if (!export_type_filename.empty() && type.raw() != export_type_filename) {
183 std::cerr <<
"InkFileExportCmd::do_export: "
184 <<
"Ignoring extension of export filename (" << export_type_filename <<
") "
185 <<
"as it does not match the current export type (" << type.raw() <<
")." << std::endl;
190 if (!export_extension_forced) {
193 std::cerr <<
"InkFileExportCmd::do_export: "
194 <<
"The parameter --export-extension is invalid for PNG export" << std::endl;
201 if (type ==
"svg" && !export_extension_forced) {
206 bool extension_for_fn_exists =
false;
207 bool exported =
false;
210 std::list<std::string> filetypes({
".svg",
".png",
".ps",
".eps",
".pdf"});
211 std::list<std::string> exts_for_fn;
212 for (
auto const &oext : extension_list) {
213 if (oext->deactivated()) {
216 auto name = Glib::ustring(oext->get_extension()).lowercase();
217 filetypes.emplace_back(
name);
218 if (
name ==
"." + type) {
219 extension_for_fn_exists =
true;
220 exts_for_fn.emplace_back(oext->get_id());
221 if (!export_extension_forced ||
225 }
else if (type ==
"ps") {
227 }
else if (type ==
"eps") {
229 }
else if (type ==
"pdf") {
240 if (export_extension_forced && extension_for_fn_exists) {
242 std::cerr <<
"InkFileExportCmd::do_export: "
244 <<
") does not match any of the extensions "
245 <<
"available for this file type." << std::endl
246 <<
"Supported IDs for this file type: [";
247 copy(exts_for_fn.begin(), exts_for_fn.end(), std::ostream_iterator<std::string>(std::cerr,
", "));
248 std::cerr <<
"\b\b]" << std::endl;
250 std::cerr <<
"InkFileExportCmd::do_export: Unknown export type: " << type.raw() <<
". Allowed values: [";
253 copy(filetypes.begin(), filetypes.end(), std::ostream_iterator<std::string>(std::cerr,
", "));
254 std::cerr <<
"\b\b]" << std::endl;
274 auto cmp =
"." + export_type_current_native;
279 if (filename_in ==
"-") {
284 auto extension_pos = filename_in.find_last_of(
'.');
285 if (extension_pos == std::string::npos) {
286 std::cerr <<
"InkFileExportCmd::get_filename_out: cannot determine input file type from filename extension: " << filename_in << std::endl;
287 return (std::string());
290 std::string extension = filename_in.substr(extension_pos+1);
295 if (export_type_current_native == extension) {
298 if (!object_id.empty()) {
299 tag =
"_" + object_id;
301 return filename_in.substr(0, extension_pos) + tag +
"." + export_type_current_native;
364 std::string ext =
"svg";
367 auto extension_pos = tmp_out.find_last_of(
'.');
368 if (extension_pos != std::string::npos) {
369 base = tmp_out.substr(0, extension_pos);
370 ext = tmp_out.substr(extension_pos+1);
374 for (
auto page_num : pages) {
376 std::string filename_out = base + (pages.size() > 1 ?
"_p" + std::to_string(page_num) :
"") +
"." + ext;
378 auto copy_doc = doc->
copy();
379 copy_doc->prunePages(std::to_string(page_num),
true);
380 copy_doc->ensureUpToDate();
381 copy_doc->vacuumDocument();
388 std::cerr <<
"InkFileExportCmd::do_export_vector: Failed to save " << (
export_plain_svg ?
"" :
"Inkscape")
389 <<
" file to: " << filename_out << std::endl;
397 std::vector<Glib::ustring> objects = Glib::Regex::split_simple(
"\\s*;\\s*",
export_id);
398 if (objects.empty()) {
399 objects.emplace_back();
402 for (
auto const &
object : objects) {
403 auto copy_doc = doc->
copy();
406 if (filename_out.empty()) {
410 if(!
object.empty()) {
411 copy_doc->ensureUpToDate();
414 SPObject *obj = copy_doc->getObjectById(
object);
415 if (obj ==
nullptr) {
416 std::cerr <<
"InkFileExportCmd::do_export_vector: Object " <<
object.raw() <<
" not found in document, nothing to export." << std::endl;
432 filename_out.c_str(),
false,
false,
436 std::cerr <<
"InkFileExportCmd::do_export_vector: Failed to save " << (
export_plain_svg ?
"" :
"Inkscape")
437 <<
" file to: " << filename_out << std::endl;
459 if (
auto c = Color::parse(nv->
attribute(
"pagecolor"))) {
475 if (nv && nv->
attribute(
"inkscape:pageopacity")){
491 bool filename_from_hint =
false;
495 bool old_dither = prefs->getBool(
"/options/dithering/value",
true);
499 std::vector<Glib::ustring> objects = Glib::Regex::split_simple(
"\\s*;\\s*",
export_id);
501 std::vector<SPItem const *>
items;
502 std::vector<Glib::ustring> objects_found;
503 for (
auto const &object_id : objects) {
508 std::cerr <<
"InkFileExport::do_export_png: "
509 <<
"Object with id=\"" << object_id.raw()
510 <<
"\" was not found in the document. Skipping." << std::endl;
514 if (!is<SPItem>(
object)) {
515 std::cerr <<
"InkFileExportCmd::do_export_png: "
516 <<
"Object with id=\"" << object_id.raw()
517 <<
"\" is not a visible item. Skipping." << std::endl;
521 items.push_back(cast<SPItem>(
object));
522 objects_found.push_back(object_id);
531 if (extension_pos != std::string::npos)
535 for (
auto page_num : pages) {
538 std::string filename_out = base + (pages.size() > 1 ?
"_p" + std::to_string(page_num) :
"") +
".png";
539 if (
auto page = pm.getPage(page_num - 1)) {
546 if (objects.empty()) {
547 objects_found.emplace_back();
550 for (
auto const &object_id : objects_found) {
552 if (!object_id.empty()) {
559 std::cerr <<
"Exporting only object with id=\""
560 << object_id.raw() <<
"\"; all other objects hidden." << std::endl;
567 const gchar *fn_hint =
object->getRepr()->attribute(
"inkscape:export-filename");
569 filename_out = fn_hint;
570 filename_from_hint =
true;
572 std::cerr <<
"InkFileExport::do_export_png: "
573 <<
"Export filename hint not found for object " << object_id.raw() <<
". Skipping." << std::endl;
578 const gchar *dpi_hint =
object->getRepr()->attribute(
"inkscape:export-xdpi");
581 std::cerr <<
"InkFileExport::do_export_png: "
582 <<
"Using bitmap dimensions from the command line "
583 <<
"(--export-dpi, --export-width, or --export-height). "
584 <<
"DPI hint " << dpi_hint <<
" is ignored." << std::endl;
586 dpi = g_ascii_strtod(dpi_hint,
nullptr);
589 std::cerr <<
"InkFileExport::do_export_png: "
590 <<
"Export DPI hint not found for the object." << std::endl;
597 if (filename_out.empty()) {
598 std::cerr <<
"InkFileExport::do_export_png: "
599 <<
"No valid export filename given and no filename hint. Skipping." << std::endl;
604 if (filename_from_hint && !Glib::path_is_absolute(filename_out) && doc->
getDocumentFilename()) {
606 if (!dirname.empty()) {
607 filename_out = Glib::build_filename(dirname, filename_out);
612 std::string directory = Glib::path_get_dirname(filename_out);
613 if (!Glib::file_test(directory, Glib::FileTest::IS_DIR)) {
614 std::cerr <<
"File path " << filename_out <<
" includes directory that doesn't exist. Skipping." << std::endl;
625 if (object_id.empty()) {
634 std::cerr <<
"ExportAreaType::not_set should be handled before" << std::endl;
646 gdouble x0, y0, x1, y1;
647 if (sscanf(
export_area.c_str(),
"%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) != 4) {
648 g_warning(
"Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.",
661 std::cerr <<
"InkFileExport::do_export_png: "
662 <<
"Unable to determine a valid bounding box. Skipping." << std::endl;
676 prefs->setBool(
"/options/dithering/value", old_dither);
692 if ((dpi < 0.1) || (dpi > 10000.0)) {
693 std::cerr <<
"InkFileExport::do_export_png: "
695 <<
" out of range [0.1 - 10000.0]. Skipping.";
707 unsigned long int width = 0;
708 unsigned long int height = 0;
715 std::cerr <<
"InkFileExport::do_export_png: "
716 <<
"Export height " <<
height <<
" out of range (1 to " << PNG_UINT_31_MAX <<
")" << std::endl;
726 if ((
width < 1) || (
width > PNG_UINT_31_MAX)) {
727 std::cerr <<
"InkFileExport::do_export_png: "
728 <<
"Export width " <<
width <<
" out of range (1 to " << PNG_UINT_31_MAX <<
")." << std::endl;
745 std::cerr <<
"InkFileExport::do_export_png: Dimensions " <<
width <<
"x" <<
height <<
" are out of range (1 to " << PNG_UINT_31_MAX <<
")." << std::endl;
752 int color_type = PNG_COLOR_TYPE_RGB_ALPHA;
756 const std::map<std::string, std::pair<int, int>> color_modes = {
757 {
"Gray_1", {PNG_COLOR_TYPE_GRAY, 1}},
758 {
"Gray_2", {PNG_COLOR_TYPE_GRAY, 2}},
759 {
"Gray_4", {PNG_COLOR_TYPE_GRAY, 4}},
760 {
"Gray_8", {PNG_COLOR_TYPE_GRAY, 8}},
761 {
"Gray_16", {PNG_COLOR_TYPE_GRAY, 16}},
762 {
"RGB_8", {PNG_COLOR_TYPE_RGB, 8}},
763 {
"RGB_16", {PNG_COLOR_TYPE_RGB, 16}},
764 {
"GrayAlpha_8", {PNG_COLOR_TYPE_GRAY_ALPHA, 8}},
765 {
"GrayAlpha_16", {PNG_COLOR_TYPE_GRAY_ALPHA, 16}},
766 {
"RGBA_8", {PNG_COLOR_TYPE_RGB_ALPHA, 8}},
767 {
"RGBA_16", {PNG_COLOR_TYPE_RGB_ALPHA, 16}},
770 if (it == color_modes.end()) {
771 std::cerr <<
"InkFileExport::do_export_png: "
772 <<
"Color mode " <<
export_png_color_mode.raw() <<
" is invalid. It must be one of Gray_1/Gray_2/Gray_4/Gray_8/Gray_16/RGB_8/RGB_16/GrayAlpha_8/GrayAlpha_16/RGBA_8/RGBA_16." << std::endl;
775 std::tie(color_type, bit_depth) = it->second;
782 std::cerr <<
"Background RRGGBBAA: " << std::hex << bgcolor << std::dec << std::endl;
786 <<
width <<
" x " <<
height <<
" pixels (" << dpi <<
" dpi)" << std::endl;
791 if (export_png_compression < 0 || export_png_compression > 9) {
792 std::cerr <<
"InkFileExport::do_export_png: "
794 <<
" out of range [0 - 9]. Skipping.";
800 if (export_png_antialias < 0 || export_png_antialias > 3) {
801 std::cerr <<
"InkFileExport::do_export_png: "
803 <<
" out of range [0 - 3]. Skipping.";
811 std::cerr <<
"InkFileExport::do_export_png: Failed to export to " << filename_out << std::endl;
829 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
830 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime_type.c_str() ) != 0) {
835 std::cerr <<
"InkFileExportCmd::do_export_ps_pdf: Could not find an extension to export to MIME type: " << mime_type << std::endl;
853 assert(std::string(extension.
get_mimetype()) == mime_type);
871 if ((dpi < 1) || (dpi > 10000.0)) {
872 g_warning(
"DPI value %lf out of range [1 - 10000]. Using 96 dpi instead.",
export_dpi);
881 if (mime_type ==
"application/pdf") {
882 bool set_export_pdf_version_fail =
true;
883 const gchar *pdfver_param_name =
"PDFversion";
886 std::string version_gui_string = std::string(
"PDF-") +
export_pdf_level.raw();
891 set_export_pdf_version_fail =
false;
893 g_warning(
"Desired PDF export version \"%s\" not supported! Hint: input one of the versions found in the pdf export dialog e.g. \"1.4\".",
900 g_warning(
"Parameter or Enum \"%s\" might not exist", pdfver_param_name);
905 if(set_export_pdf_version_fail) {
910 if (mime_type ==
"image/x-postscript" || mime_type ==
"image/x-e-postscript") {
911 if ( export_ps_level < 2 || export_ps_level > 3 ) {
912 g_warning(
"Only supported PostScript levels are 2 and 3."
913 " Defaulting to 2.");
938 extension->
save(doc, filename_out.c_str());
941 std::cerr << __PRETTY_FUNCTION__ <<
": Failed to save " << extension->
get_id()
942 <<
" to: " << filename_out << std::endl;
953 return "--export-area";
955 return "--export-area-page";
957 return "--export-area-drawing";
966 std::cerr <<
"Warning: multiple export area types have been set, overriding "
void export_use_hints(const Glib::VariantBase &value, InkscapeApplication *app)
void export_dpi(const Glib::VariantBase &value, InkscapeApplication *app)
void export_ignore_filters(const Glib::VariantBase &value, InkscapeApplication *app)
void export_png_compression(const Glib::VariantBase &value, InkscapeApplication *app)
void export_pdf_level(const Glib::VariantBase &value, InkscapeApplication *app)
void export_png_antialias(const Glib::VariantBase &value, InkscapeApplication *app)
void export_ps_level(const Glib::VariantBase &value, InkscapeApplication *app)
void export_area_snap(const Glib::VariantBase &value, InkscapeApplication *app)
void export_latex(const Glib::VariantBase &value, InkscapeApplication *app)
void export_plain_svg(const Glib::VariantBase &value, InkscapeApplication *app)
void export_text_to_path(const Glib::VariantBase &value, InkscapeApplication *app)
void export_margin(const Glib::VariantBase &value, InkscapeApplication *app)
void export_height(const Glib::VariantBase &value, InkscapeApplication *app)
void export_background_opacity(const Glib::VariantBase &value, InkscapeApplication *app)
void export_width(const Glib::VariantBase &value, InkscapeApplication *app)
void export_overwrite(const Glib::VariantBase &value, InkscapeApplication *app)
void export_filename(const Glib::VariantBase &value, InkscapeApplication *app)
void export_id_only(const Glib::VariantBase &value, InkscapeApplication *app)
static bool cmp(std::pair< Glib::ustring, Glib::ustring > const &a, std::pair< Glib::ustring, Glib::ustring > const &b)
Compare function.
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
Range of real numbers that is never empty.
Axis-aligned rectangle that can be empty.
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
IntRect roundOutwards() const
Return the smallest integer rectangle which contains this one.
Glib::ustring export_area
int do_export_ps_pdf(SPDocument *doc, std::string const &filename_in, std::string const &mime_type)
Perform a PDF/PS/EPS export.
std::string get_filename_out(std::string filename_in="", std::string object_id="")
void set_export_area(const Glib::ustring &area)
ExportAreaType export_area_type
int do_export_vector(SPDocument *doc, std::string const &filename_in, Inkscape::Extension::Output &extension)
Perform a vector file export (SVG, PDF, or PS)
void set_export_area_type(ExportAreaType type)
Glib::ustring export_pdf_level
int do_export_png(SPDocument *doc, std::string const &filename_in)
Perform a PNG export.
Glib::ustring export_page
Glib::ustring export_type_current
bool export_png_use_dithering
int do_export_svg(SPDocument *doc, std::string const &filename_in)
Perform an SVG export.
guint32 get_bgcolor(SPDocument *doc)
Glib::ustring export_type
double export_background_opacity
void do_export_png_now(SPDocument *doc, std::string const &filename_out, Geom::Rect area, double dpi_in, const std::vector< SPItem const * > &items)
int do_export_extension(SPDocument *doc, std::string const &filename_in, Inkscape::Extension::Output *extension)
Export a document using an export extension.
Glib::ustring export_png_color_mode
void do_export(SPDocument *doc, std::string filename_in="")
int export_png_compression
Glib::ustring export_background
std::string export_filename
Glib::ustring export_extension
bool export_ignore_filters
bool addOpacity(double opacity=1.0)
uint32_t toRGBA(double opacity=1.0) const
Return an sRGB conversion of the color in RGBA int32 format.
std::list< Output * > OutputList
OutputList & get_output_list(OutputList &ou_list)
Creates a list of all the Output extensions.
Extension * get(const gchar *key) const
This function looks up a Inkscape::Extension::Extension by using its unique id. It then returns a ref...
The object that is the basis for the Extension system.
@ STATE_LOADED
The extension has been loaded successfully.
bool get_param_optiongroup_contains(char const *name, char const *value) const
This is useful to find out, if a given string value is selectable in a optiongroup named \cname.
bool set_param_bool(char const *name, bool value)
Sets a parameter identified by name with the boolean in the parameter value.
int set_param_int(char const *name, int value)
Sets a parameter identified by name with the integer in the parameter value.
char const * get_id() const
Get the ID of this extension - not a copy don't delete!
void set_state(state_t in_state)
A function to set whether the extension should be loaded or unloaded.
char const * set_param_optiongroup(char const *name, char const *value)
Sets a parameter identified by name with the string in the parameter value.
Generic failure for an undescribed reason.
gchar const * get_mimetype() const
Get the mime-type that describes this extension.
void save(SPDocument *doc, gchar const *filename, bool detachbase=false)
Save a document as a file.
void set(SPObject *object, bool persist_selection_context=false)
Set the selection to a single specific object.
bool fitCanvas(bool with_margins, bool skip_undo=false)
static Preferences * get()
Access the singleton Preferences object.
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Interface for refcounted XML nodes.
double getAttributeDouble(Util::const_char_ptr key, double default_value=0.0) const
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Typed SVG document implementation.
SPRoot * getRoot()
Returns our SPRoot.
std::unique_ptr< SPDocument > copy() const
Create a copy of the document, useful for modifying during save & export.
char const * getDocumentFilename() const
SPObject * getObjectById(std::string const &id) const
void fitToRect(Geom::Rect const &rect, bool with_margins=false)
Given a Geom::Rect that may, for example, correspond to the bbox of an object, this function fits the...
Geom::Point getDimensions() const
Geom::OptRect preferredBounds() const
Inkscape::PageManager & getPageManager()
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPNamedView * getNamedView()
Get the namedview for this document, creates it if it's not found.
Inkscape::XML::Node * getReprNamedView()
Base class for visual SVG elements.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
void cropToObject(SPObject *except)
Removes all children except for the given object, it's children and it's ancesstors.
Inkscape::Extension::Extension: Frontend to certain, possibly pluggable, actions.
std::string export_area_type_string(ExportAreaType type)
auto floor(Geom::Rect const &rect)
DB db
This is the actual database object.
void save(Extension *key, SPDocument *doc, gchar const *filename, bool check_overwrite, bool official, Inkscape::Extension::FileSaveMethod save_method)
This is a generic function to use the save function of a module (including Autodetect)
@ FILE_SAVE_METHOD_SAVE_COPY
@ FILE_SAVE_METHOD_INKSCAPE_SVG
Glib::ustring get_file_extension(Glib::ustring const &path)
bool file_test(char const *utf8name, GFileTest test)
std::set< unsigned > parseIntRange(std::string const &input, unsigned start, unsigned end)
Parse integer ranges out of a string.
void convert_text_to_curves(SPDocument *)
Convert all text in the document to path, in-place.
ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, double x0, double y0, double x1, double y1, unsigned long int width, unsigned long int height, double xdpi, double ydpi, unsigned long bgcolor, unsigned int(*status)(float, void *), void *data, bool force_overwrite, const std::vector< SPItem const * > &items_only, bool interlace, int color_type, int bit_depth, int zlib, int antialiasing)
Export the given document as a Portable Network Graphics (PNG) file.
Singleton class to access the preferences file in a convenient way.
bool fit_canvas_to_drawing(SPDocument *doc, bool with_margins)
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
SPRoot: SVG <svg> implementation.