Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
file-export-cmd.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * File export from the command line. This code, greatly modified, use to be in main.cpp. It should
4 * be replaced by code shared with the file dialog (Gio::Actions?).
5 *
6 * Copyright (C) 2018 Tavmjong Bah
7 *
8 * Git blame shows that bulia byak is the main author of the original export code from
9 * main.cpp. Other authors of note include Nicolas Dufour, Vinicius dos Santos Oliveira, and Bob
10 * Jamison; none of whom bothered to add their names to the copyright of main.cc.
11 *
12 * The contents of this file may be used under the GNU General Public License Version 2 or later.
13 */
14
15#include "file-export-cmd.h"
16
17#include <filesystem>
18#include <iostream>
19#include <string>
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>
26#include <png.h> // PNG export
27
28#include "colors/color.h"
29#include "colors/manager.h"
30#include "document.h"
31#include "extension/db.h"
32#include "extension/extension.h"
33#include "extension/output.h"
34#include "extension/system.h"
35#include "helper/png-write.h" // PNG Export
36#include "object/object-set.h"
37#include "object/sp-item.h"
38#include "object/sp-namedview.h"
39#include "object/sp-page.h"
40#include "object/sp-root.h"
41#include "page-manager.h"
42#include "path-chemistry.h" // sp_item_list_to_curves
43#include "preferences.h"
44#include "selection-chemistry.h" // fit_canvas_to_drawing
45#include "util/units.h"
47#include "io/sys.h"
48
49namespace filesystem = std::filesystem;
50
52 : export_overwrite(false)
53 , export_margin(0)
54 , export_area_snap(false)
55 , export_use_hints(false)
56 , export_width(0)
57 , export_height(0)
58 , export_dpi(0)
60 , export_text_to_path(false)
62 , export_pdf_level("1.7")
63 , export_latex(false)
64 , export_id_only(false)
65 , export_background_opacity(-1) // default is unset != actively set to 0
66 , export_plain_svg(false)
69 , make_paths(false)
70{
71}
72
73void
74InkFileExportCmd::do_export(SPDocument* doc, std::string filename_in)
75{
76 std::string export_type_filename;
77 std::vector<Glib::ustring> export_type_list;
78
79 // Get export type from filename supplied with --export-filename
80 if (!export_filename.empty() && export_filename != "-") {
81
82 // Attempt to result variable and home path use in export filenames.
83 Glib::RefPtr<Gio::File> gfile = Gio::File::create_for_parse_name(export_filename);
84 export_filename = gfile->get_parse_name();
85
86#ifdef G_OS_WIN32
87 auto fn = filesystem::u8path(export_filename);
88#else
89 auto fn = filesystem::path(export_filename);
90#endif
91 if (!fn.has_extension()) {
92 if (export_type.empty() && export_extension.empty()) {
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;
96 return;
97 } else {
98 // no extension is fine if --export-type is given
99 // explicitly stated extensions are handled later
100 }
101 } else {
102 export_type_filename = fn.extension().string().substr(1);
103 boost::algorithm::to_lower(export_type_filename);
104 export_filename = (fn.parent_path() / fn.stem()).string();
105 }
106 }
107
108 // Get export type(s) from string supplied with --export-type
109 if (!export_type.empty()) {
110 export_type_list = Glib::Regex::split_simple("[,;]", export_type);
111 }
112
113 // Determine actual type(s) for export.
114 if (export_use_hints) {
115 // Override type if --export-use-hints is used (hints presume PNG export for now)
116 // TODO: There's actually no reason to presume. We could allow to export to any format using hints!
118 std::cerr << "InkFileExportCmd::do_export: "
119 << "--export-use-hints can only be used with --export-id or --export-area-drawing." << std::endl;
120 return;
121 }
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;
125 }
126 if (!export_filename.empty()) {
127 std::cerr << "InkFileExportCmd::do_export: --export-filename is ignored when using --export-use-hints!" << std::endl;
128 }
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); // use extension from filename
134 } else if (!export_extension.empty()) {
135 // guess export type from extension
136 auto ext =
138 if (ext) {
139 export_type_list.emplace_back(std::string(ext->get_extension()).substr(1));
140 } else {
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.";
144 return;
145 }
146 } else {
147 export_type_list.emplace_back("svg"); // fall-back to SVG by default
148 }
149 }
150 // check if multiple export files are requested, but --export_extension was supplied
151 if (!export_extension.empty() && export_type_list.size() != 1) {
152 std::cerr
153 << "InkFileExportCmd::do_export: You may only specify one export type if --export-extension is supplied";
154 return;
155 }
158
159 // Export filename should be used when specified as the output file
160 auto const filename_out = !export_filename.empty() ? export_filename : filename_in;
161
162 auto path_out = Glib::path_get_dirname(filename_out);
163 if (make_paths && !Inkscape::IO::file_test(path_out.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
164 g_mkdir_with_parents(path_out.c_str(), 0755);
165 }
166
167 if (!Inkscape::IO::file_test(path_out.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
168 std::cerr << "InkFileExportCmd::do_export: "
169 << "file directory doesn't exist for export: "
170 << path_out.c_str() << std::endl;
171 return;
172 }
173
174 for (auto const &Type : export_type_list) {
175 // use lowercase type for following comparisons
176 auto type = Type.lowercase();
177 g_info("exporting '%s' to type '%s'", filename_in.c_str(), type.c_str());
178
179 export_type_current = type;
180
181 // Check for consistency between extension of --export-filename and --export-type if both are given
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;
186 }
187 bool export_extension_forced = !export_extension.empty();
188 // For PNG export, there is no extension, so the method below can not be used.
189 if (type == "png") {
190 if (!export_extension_forced) {
191 do_export_png(doc, filename_out);
192 } else {
193 std::cerr << "InkFileExportCmd::do_export: "
194 << "The parameter --export-extension is invalid for PNG export" << std::endl;
195 }
196 continue;
197 }
198 // for SVG export, we let the do_export_svg function handle the selection of the extension, unless
199 // an extension ID was explicitly given. This makes handling of --export-plain-svg easier (which
200 // should also work when multiple file types are given, unlike --export-extension)
201 if (type == "svg" && !export_extension_forced) {
202 do_export_svg(doc, filename_out);
203 continue;
204 }
205
206 bool extension_for_fn_exists = false;
207 bool exported = false;
208 // if no extension is found, the entire list of extensions is walked through,
209 // so we can use the same loop to construct the list of available formats for the error message
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()) {
214 continue;
215 }
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 ||
222 (export_extension == Glib::ustring(oext->get_id()).lowercase())) {
223 if (type == "svg") {
224 do_export_vector(doc, filename_out, *oext);
225 } else if (type == "ps") {
226 do_export_ps_pdf(doc, filename_out, "image/x-postscript", *oext);
227 } else if (type == "eps") {
228 do_export_ps_pdf(doc, filename_out, "image/x-e-postscript", *oext);
229 } else if (type == "pdf") {
230 do_export_ps_pdf(doc, filename_out, "application/pdf", *oext);
231 } else {
232 do_export_extension(doc, filename_out, oext);
233 }
234 exported = true;
235 break;
236 }
237 }
238 }
239 if (!exported) {
240 if (export_extension_forced && extension_for_fn_exists) {
241 // the located extension for this file type did not match the provided --export-extension parameter
242 std::cerr << "InkFileExportCmd::do_export: "
243 << "The supplied extension ID (" << export_extension
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;
249 } else {
250 std::cerr << "InkFileExportCmd::do_export: Unknown export type: " << type.raw() << ". Allowed values: [";
251 filetypes.sort();
252 filetypes.unique();
253 copy(filetypes.begin(), filetypes.end(), std::ostream_iterator<std::string>(std::cerr, ", "));
254 std::cerr << "\b\b]" << std::endl;
255 }
256 }
257 }
258}
259
260// File names use std::string. HTML5 and presumably SVG 2 allows UTF-8 characters. Do we need to convert "object_id" here?
261std::string
262InkFileExportCmd::get_filename_out(std::string filename_in, std::string object_id)
263{
264 // Pipe out
265 if (export_filename == "-") {
266 return "-";
267 }
268
269 auto const export_type_current_native = Glib::filename_from_utf8(export_type_current);
270
271 // Use filename provided with --export-filename if given
272 if (!export_filename.empty()) {
274 auto cmp = "." + export_type_current_native;
275 return export_filename + (ext == cmp ? "" : cmp);
276 }
277
278 // Check for pipe
279 if (filename_in == "-") {
280 return "-";
281 }
282
283 // Construct output filename from input filename and export_type.
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());
288 }
289
290 std::string extension = filename_in.substr(extension_pos+1);
291 if (export_overwrite && export_type_current_native == extension) {
292 return filename_in;
293 } else {
294 std::string tag;
295 if (export_type_current_native == extension) {
296 tag = "_out";
297 }
298 if (!object_id.empty()) {
299 tag = "_" + object_id;
300 }
301 return filename_in.substr(0, extension_pos) + tag + "." + export_type_current_native;
302 }
303}
304
312{
314 if (export_plain_svg) {
315 oext =
316 dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get("org.inkscape.output.svg.plain"));
317 } else {
318 oext = dynamic_cast<Inkscape::Extension::Output *>(
319 Inkscape::Extension::db.get("org.inkscape.output.svg.inkscape"));
320 }
321 return do_export_vector(doc, export_filename, *oext);
322}
332{
333 // Start with options that are once per document.
336 }
337
338 if (export_margin != 0) {
339 gdouble margin = export_margin;
340 doc->ensureUpToDate();
341 SPNamedView *nv;
342 Inkscape::XML::Node *nv_repr;
343 if ((nv = doc->getNamedView()) && (nv_repr = nv->getRepr())) {
344 nv_repr->setAttributeSvgDouble("fit-margin-top", margin);
345 nv_repr->setAttributeSvgDouble("fit-margin-left", margin);
346 nv_repr->setAttributeSvgDouble("fit-margin-right", margin);
347 nv_repr->setAttributeSvgDouble("fit-margin-bottom", margin);
348 }
349 }
350
352 fit_canvas_to_drawing(doc, export_margin != 0 ? true : false);
353 } else if (export_area_type == ExportAreaType::Page || export_id.empty()) {
354 if (export_margin) {
355 doc->ensureUpToDate();
356 doc->fitToRect(*(doc->preferredBounds()), export_margin);
357 }
358 }
359
360 // Export pages instead of objects
361 if (!export_page.empty()) {
362 auto &pm = doc->getPageManager();
363 std::string base = export_filename;
364 std::string ext = "svg";
365 // Strip any possible extension
366 std::string tmp_out = get_filename_out(export_filename, "");
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);
371 }
372
373 auto pages = Inkscape::parseIntRange(export_page, 1, pm.getPageCount());
374 for (auto page_num : pages) {
375 // And if only one page is selected then we assume the user knows the filename they intended.
376 std::string filename_out = base + (pages.size() > 1 ? "_p" + std::to_string(page_num) : "") + "." + ext;
377
378 auto copy_doc = doc->copy();
379 copy_doc->prunePages(std::to_string(page_num), true);
380 copy_doc->ensureUpToDate();
381 copy_doc->vacuumDocument();
382
383 try {
384 extension.set_gui(false);
385 Inkscape::Extension::save(dynamic_cast<Inkscape::Extension::Extension *>(&extension), copy_doc.get(),
386 filename_out.c_str(), false, false, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_COPY);
388 std::cerr << "InkFileExportCmd::do_export_vector: Failed to save " << (export_plain_svg ? "" : "Inkscape")
389 << " file to: " << filename_out << std::endl;
390 return 1;
391 }
392 }
393 return 0;
394 }
395
396 // Export each object in list (or root if empty). Use ';' so in future it could be possible to selected multiple objects to export together.
397 std::vector<Glib::ustring> objects = Glib::Regex::split_simple("\\s*;\\s*", export_id);
398 if (objects.empty()) {
399 objects.emplace_back(); // So we do loop at least once for root.
400 }
401
402 for (auto const &object : objects) {
403 auto copy_doc = doc->copy();
404
405 std::string filename_out = get_filename_out(export_filename, Glib::filename_from_utf8(object));
406 if (filename_out.empty()) {
407 return 1;
408 }
409
410 if(!object.empty()) {
411 copy_doc->ensureUpToDate();
412
413 // "crop" the document to the specified object, cleaning as we go.
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;
417 return 1;
418 }
419 if (export_id_only) {
420 // If -j then remove all other objects to complete the "crop"
421 copy_doc->getRoot()->cropToObject(obj);
422 }
424 Inkscape::ObjectSet s(copy_doc.get());
425 s.set(obj);
426 s.fitCanvas((bool)export_margin);
427 }
428 }
429 try {
430 extension.set_gui(false);
431 Inkscape::Extension::save(dynamic_cast<Inkscape::Extension::Extension *>(&extension), copy_doc.get(),
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;
438 return 1;
439 }
440 }
441 return 0;
442}
443
445 Inkscape::Colors::Color bgcolor(0xffffffff);
446 if (!export_background.empty()) {
447 // override the page color
448 if (auto c = Color::parse(export_background)) {
449 bgcolor = *c;
450 }
451 // default is opaque if a color is given on commandline
452 if (export_background_opacity < -.5 ) {
454 }
455 } else {
456 // read from namedview
458 if (nv && nv->attribute("pagecolor")){
459 if (auto c = Color::parse(nv->attribute("pagecolor"))) {
460 bgcolor = *c;
461 }
462 }
463 }
464
465 if (export_background_opacity > -.5) { // if the value is manually set
466 if (export_background_opacity > 1.0) {
467 float value = CLAMP (export_background_opacity, 1.0f, 255.0f);
468 bgcolor.addOpacity(floor(value) / 255.0);
469 } else {
470 float value = CLAMP (export_background_opacity, 0.0f, 1.0f);
471 bgcolor.addOpacity(value);
472 }
473 } else {
475 if (nv && nv->attribute("inkscape:pageopacity")){
476 bgcolor.addOpacity(nv->getAttributeDouble("inkscape:pageopacity", 1.0));
477 } // else it's transparent
478 }
479 return bgcolor.toRGBA();
480}
481
488int
490{
491 bool filename_from_hint = false;
492 gdouble dpi = 0.0;
493
494 auto prefs = Inkscape::Preferences::get();
495 bool old_dither = prefs->getBool("/options/dithering/value", true);
496 prefs->setBool("/options/dithering/value", export_png_use_dithering);
497
498 // Export each object in list (or root if empty). Use ';' so in future it could be possible to selected multiple objects to export together.
499 std::vector<Glib::ustring> objects = Glib::Regex::split_simple("\\s*;\\s*", export_id);
500
501 std::vector<SPItem const *> items;
502 std::vector<Glib::ustring> objects_found;
503 for (auto const &object_id : objects) {
504 // Find export object. (Either root or object with specified id.)
505 auto object = doc->getObjectById(object_id);
506
507 if (!object) {
508 std::cerr << "InkFileExport::do_export_png: "
509 << "Object with id=\"" << object_id.raw()
510 << "\" was not found in the document. Skipping." << std::endl;
511 continue;
512 }
513
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;
518 continue;
519 }
520
521 items.push_back(cast<SPItem>(object));
522 objects_found.push_back(object_id);
523 }
524
525 // Export pages instead of objects
526 if (!export_page.empty()) {
527 auto &pm = doc->getPageManager();
528 std::string base = export_filename;
529 // Strip any possible extension
530 auto extension_pos = export_filename.find_last_of('.');
531 if (extension_pos != std::string::npos)
532 base = export_filename.substr(0, extension_pos);
533
534 auto pages = Inkscape::parseIntRange(export_page, 1, pm.getPageCount());
535 for (auto page_num : pages) {
536 // We always use the png extension and ignore the extension given by the user
537 // And if only one page is selected then we assume the user knows the filename they intended.
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)) {
540 do_export_png_now(doc, filename_out, page->getDesktopRect(), dpi, items);
541 }
542 }
543 return 0;
544 }
545
546 if (objects.empty()) {
547 objects_found.emplace_back(); // So we do loop at least once for root.
548 }
549
550 for (auto const &object_id : objects_found) {
551 SPObject *object = doc->getRoot();
552 if (!object_id.empty()) {
553 object = doc->getObjectById(object_id);
554 }
555
556 std::string filename_out = get_filename_out(export_filename, Glib::filename_from_utf8(object_id));
557
558 if (export_id_only) {
559 std::cerr << "Exporting only object with id=\""
560 << object_id.raw() << "\"; all other objects hidden." << std::endl;
561 }
562
563 // Find file name and dpi from hints.
564 if (export_use_hints) {
565
566 // Retrieve export filename hint.
567 const gchar *fn_hint = object->getRepr()->attribute("inkscape:export-filename");
568 if (fn_hint) {
569 filename_out = fn_hint;
570 filename_from_hint = true;
571 } else {
572 std::cerr << "InkFileExport::do_export_png: "
573 << "Export filename hint not found for object " << object_id.raw() << ". Skipping." << std::endl;
574 continue;
575 }
576
577 // Retrieve export dpi hint. Only xdpi as ydpi is always the same now.
578 const gchar *dpi_hint = object->getRepr()->attribute("inkscape:export-xdpi");
579 if (dpi_hint) {
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;
585 } else {
586 dpi = g_ascii_strtod(dpi_hint, nullptr);
587 }
588 } else {
589 std::cerr << "InkFileExport::do_export_png: "
590 << "Export DPI hint not found for the object." << std::endl;
591 }
592 }
593
594 // ------------------------- File name -------------------------
595
596 // Check we have a filename.
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;
600 continue;
601 }
602
603 //Make relative paths go from the document location, if possible:
604 if (filename_from_hint && !Glib::path_is_absolute(filename_out) && doc->getDocumentFilename()) {
605 std::string dirname = Glib::path_get_dirname(doc->getDocumentFilename());
606 if (!dirname.empty()) {
607 filename_out = Glib::build_filename(dirname, filename_out);
608 }
609 }
610
611 // Check if directory exists
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;
615 continue;
616 }
617
618 // ------------------------- Area -------------------------------
619
620 Geom::Rect area;
621 doc->ensureUpToDate();
622
624 // Default to drawing if has object, otherwise export page
625 if (object_id.empty()) {
627 } else {
629 }
630 }
631 // Three choices: 1. Command-line export_area 2. Page area 3. Drawing area
632 switch (export_area_type) {
634 std::cerr << "ExportAreaType::not_set should be handled before" << std::endl;
635 return 1;
637 // Export area page (explicit or if no object is given).
639 area = Geom::Rect(origin, origin + doc->getDimensions());
640 break;
641 }
643 // Export area command-line
644
645 /* Try to parse area (given in SVG pixels) */
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.",
649 export_area.c_str());
650 return 1; // If it fails once, it will fail for all objects.
651 }
652 area = Geom::Rect(Geom::Interval(x0, x1), Geom::Interval(y0, y1));
653 break;
654 }
656 // Export area drawing (explicit or if object is given).
657 Geom::OptRect areaMaybe = static_cast<SPItem *>(object)->documentVisualBounds();
658 if (areaMaybe) {
659 area = *areaMaybe;
660 } else {
661 std::cerr << "InkFileExport::do_export_png: "
662 << "Unable to determine a valid bounding box. Skipping." << std::endl;
663 continue;
664 }
665 break;
666 }
667 }
668
669 if (export_area_snap) {
670 area = area.roundOutwards();
671 }
672 // End finding area.
673 do_export_png_now(doc, filename_out, area, dpi, items);
674
675 } // End loop over objects.
676 prefs->setBool("/options/dithering/value", old_dither);
677 return 0;
678}
679
683void
684InkFileExportCmd::do_export_png_now(SPDocument *doc, std::string const &filename_out, Geom::Rect area, double dpi_in, const std::vector<SPItem const *> &items)
685{
686 // -------------------------- DPI -------------------------------
687
688 double dpi = dpi_in;
689
690 if (export_dpi != 0.0 && dpi == 0.0) {
691 dpi = export_dpi;
692 if ((dpi < 0.1) || (dpi > 10000.0)) {
693 std::cerr << "InkFileExport::do_export_png: "
694 << "DPI value " << export_dpi
695 << " out of range [0.1 - 10000.0]. Skipping.";
696 return;
697 }
698 }
699
700 // default dpi
701 if (dpi == 0.0) {
702 dpi = Inkscape::Util::Quantity::convert(1, "in", "px");
703 }
704
705 // -------------------------- Width and Height ---------------------------------
706
707 unsigned long int width = 0;
708 unsigned long int height = 0;
709 double xdpi = dpi;
710 double ydpi = dpi;
711
712 if (export_height != 0) {
714 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
715 std::cerr << "InkFileExport::do_export_png: "
716 << "Export height " << height << " out of range (1 to " << PNG_UINT_31_MAX << ")" << std::endl;
717 return;
718 }
719 ydpi = Inkscape::Util::Quantity::convert(height, "in", "px") / area.height();
720 xdpi = ydpi;
721 dpi = ydpi;
722 }
723
724 if (export_width != 0) {
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;
729 return;
730 }
731 xdpi = Inkscape::Util::Quantity::convert(width, "in", "px") / area.width();
732 ydpi = export_height ? ydpi : xdpi;
733 dpi = xdpi;
734 }
735
736 if (width == 0) {
737 width = (unsigned long int) (Inkscape::Util::Quantity::convert(area.width(), "px", "in") * dpi + 0.5);
738 }
739
740 if (height == 0) {
741 height = (unsigned long int) (Inkscape::Util::Quantity::convert(area.height(), "px", "in") * dpi + 0.5);
742 }
743
744 if ((width < 1) || (height < 1) || (width > PNG_UINT_31_MAX) || (height > PNG_UINT_31_MAX)) {
745 std::cerr << "InkFileExport::do_export_png: Dimensions " << width << "x" << height << " are out of range (1 to " << PNG_UINT_31_MAX << ")." << std::endl;
746 return;
747 }
748
749 // -------------------------- Bit Depth and Color Type --------------------
750
751 int bit_depth = 8; // default of sp_export_png_file function
752 int color_type = PNG_COLOR_TYPE_RGB_ALPHA; // default of sp_export_png_file function
753
754 if (!export_png_color_mode.empty()) {
755 // data as in ui/dialog/export.cpp:
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}},
768 };
769 auto it = color_modes.find(export_png_color_mode);
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;
773 return;
774 } else {
775 std::tie(color_type, bit_depth) = it->second;
776 }
777 }
778
779 guint32 bgcolor = get_bgcolor(doc);
780 // ---------------------- Generate the PNG -------------------------------
781#ifdef DEBUG
782 std::cerr << "Background RRGGBBAA: " << std::hex << bgcolor << std::dec << std::endl;
783 std::cerr << "Area "
784 << area[Geom::X][0] << ":" << area[Geom::Y][0] << ":"
785 << area[Geom::X][1] << ":" << area[Geom::Y][1] << " exported to "
786 << width << " x " << height << " pixels (" << dpi << " dpi)" << std::endl;
787#endif
788
789 // -------------------------- Compression level ---------------------------
790
791 if (export_png_compression < 0 || export_png_compression > 9) {
792 std::cerr << "InkFileExport::do_export_png: "
793 << "Compression level " << export_png_compression
794 << " out of range [0 - 9]. Skipping.";
795 return;
796 }
797
798 // ---------------------------- Antialias level ---------------------------
799
800 if (export_png_antialias < 0 || export_png_antialias > 3) {
801 std::cerr << "InkFileExport::do_export_png: "
802 << "Antialias level " << export_png_antialias
803 << " out of range [0 - 3]. Skipping.";
804 return;
805 }
806
807 if( sp_export_png_file(doc, filename_out.c_str(), area, width, height, xdpi, ydpi,
808 bgcolor, nullptr, nullptr, true, export_id_only ? items : std::vector<SPItem const *>(),
809 false, color_type, bit_depth, export_png_compression, export_png_antialias) == 1 ) {
810 } else {
811 std::cerr << "InkFileExport::do_export_png: Failed to export to " << filename_out << std::endl;
812 }
813}
814
815
823int
824InkFileExportCmd::do_export_ps_pdf(SPDocument* doc, std::string const &filename_in, std::string const & mime_type)
825{
826 // Check if we support mime type.
829 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
830 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime_type.c_str() ) != 0) {
831 ++i;
832 }
833
834 if (i == o.end()) {
835 std::cerr << "InkFileExportCmd::do_export_ps_pdf: Could not find an extension to export to MIME type: " << mime_type << std::endl;
836 return 1;
837 }
838 return do_export_ps_pdf(doc, filename_in, mime_type, *dynamic_cast<Inkscape::Extension::Output *>(*i));
839}
840
849int InkFileExportCmd::do_export_ps_pdf(SPDocument *doc, std::string const &filename_in, std::string const & mime_type,
851{
852 // check if the passed extension conforms to the mime type.
853 assert(std::string(extension.get_mimetype()) == mime_type);
854 // Start with options that are once per document.
855
856 // Set export options.
858 extension.set_param_optiongroup("textToPath", "paths");
859 } else if (export_latex) {
860 extension.set_param_optiongroup("textToPath", "LaTeX");
861 }
862
864 extension.set_param_bool("blurToBitmap", false);
865 } else {
866 extension.set_param_bool("blurToBitmap", true);
867
868 gdouble dpi = 96.0;
869 if (export_dpi) {
870 dpi = export_dpi;
871 if ((dpi < 1) || (dpi > 10000.0)) {
872 g_warning("DPI value %lf out of range [1 - 10000]. Using 96 dpi instead.", export_dpi);
873 dpi = 96;
874 }
875 }
876
877 extension.set_param_int("resolution", (int)dpi);
878 }
879
880 // handle --export-pdf-version
881 if (mime_type == "application/pdf") {
882 bool set_export_pdf_version_fail = true;
883 const gchar *pdfver_param_name = "PDFversion";
884 if (!export_pdf_level.empty()) {
885 // combine "PDF " and the given command line
886 std::string version_gui_string = std::string("PDF-") + export_pdf_level.raw();
887 try {
888 // first, check if the given pdf version is selectable in the ComboBox
889 if (extension.get_param_optiongroup_contains("PDFversion", version_gui_string.c_str())) {
890 extension.set_param_optiongroup(pdfver_param_name, version_gui_string.c_str());
891 set_export_pdf_version_fail = false;
892 } else {
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\".",
894 export_pdf_level.c_str());
895 }
896 } catch (...) {
897 // can be thrown along the way:
898 // throw Extension::param_not_exist();
899 // throw Extension::param_not_enum_param();
900 g_warning("Parameter or Enum \"%s\" might not exist", pdfver_param_name);
901 }
902 }
903
904 // set default pdf export version to 1.4, also if something went wrong
905 if(set_export_pdf_version_fail) {
906 extension.set_param_optiongroup(pdfver_param_name, "PDF-1.4");
907 }
908 }
909
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.");
914 export_ps_level = 2;
915 }
916
917 extension.set_param_optiongroup("PSlevel", (export_ps_level == 3) ? "PS3" : "PS2");
918 }
919
920 return do_export_vector(doc, filename_in, extension);
921}
922
930int InkFileExportCmd::do_export_extension(SPDocument *doc, std::string const &filename_in,
932{
933 std::string filename_out = get_filename_out(filename_in);
934 if (extension) {
936 try {
937 extension->set_gui(false);
938 extension->save(doc, filename_out.c_str());
940
941 std::cerr << __PRETTY_FUNCTION__ << ": Failed to save " << extension->get_id()
942 << " to: " << filename_out << std::endl;
943 return 1;
944 }
945 }
946 return 0;
947}
948
950{
951 switch (type) {
953 return "--export-area";
955 return "--export-area-page";
957 return "--export-area-drawing";
958 default:
959 return "default";
960 }
961}
962
964{
966 std::cerr << "Warning: multiple export area types have been set, overriding "
968 << std::endl;
969 }
970 export_area_type = type;
971}
972
973void InkFileExportCmd::set_export_area(const Glib::ustring &area)
974{
975 export_area = area;
977}
978
979/*
980 Local Variables:
981 mode:c++
982 c-file-style:"stroustrup"
983 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
984 indent-tabs-mode:nil
985 fill-column:99
986 End:
987*/
988// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Point origin
Definition aa.cpp:227
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.
uint64_t page
Definition canvas.cpp:171
int margin
Definition canvas.cpp:166
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.
Definition interval.h:59
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
IntRect roundOutwards() const
Return the smallest integer rectangle which contains this one.
Definition rect.h:141
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.
Glib::ustring export_id
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
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="")
Glib::ustring export_background
std::string export_filename
Glib::ustring export_extension
bool addOpacity(double opacity=1.0)
Definition color.h:56
uint32_t toRGBA(double opacity=1.0) const
Return an sRGB conversion of the color in RGBA int32 format.
Definition color.cpp:117
std::list< Output * > OutputList
Definition db.h:58
OutputList & get_output_list(OutputList &ou_list)
Creates a list of all the Output extensions.
Definition db.cpp:256
Extension * get(const gchar *key) const
This function looks up a Inkscape::Extension::Extension by using its unique id. It then returns a ref...
Definition db.cpp:101
The object that is the basis for the Extension system.
Definition extension.h:133
@ STATE_LOADED
The extension has been loaded successfully.
Definition extension.h:137
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.
Definition output.h:34
gchar const * get_mimetype() const
Get the mime-type that describes this extension.
Definition output.cpp:149
void save(SPDocument *doc, gchar const *filename, bool detachbase=false)
Save a document as a file.
Definition output.cpp:215
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.
Definition units.cpp:588
Interface for refcounted XML nodes.
Definition node.h:80
double getAttributeDouble(Util::const_char_ptr key, double default_value=0.0) const
Definition node.cpp:76
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.
Definition node.cpp:111
SVGLength y
SVGLength x
Typed SVG document implementation.
Definition document.h:101
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
std::unique_ptr< SPDocument > copy() const
Create a copy of the document, useful for modifying during save & export.
Definition document.cpp:515
char const * getDocumentFilename() const
Definition document.h:229
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...
Definition document.cpp:992
Geom::Point getDimensions() const
Definition document.cpp:964
Geom::OptRect preferredBounds() const
Definition document.cpp:969
Inkscape::PageManager & getPageManager()
Definition document.h:162
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.
Definition document.cpp:235
Inkscape::XML::Node * getReprNamedView()
Definition document.cpp:225
Base class for visual SVG elements.
Definition sp-item.h:109
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
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.
float computed
Definition svg-length.h:50
double c[8][4]
unsigned int guint32
Inkscape::Extension::Extension: Frontend to certain, possibly pluggable, actions.
std::string export_area_type_string(ExportAreaType type)
ExportAreaType
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
auto floor(Geom::Rect const &rect)
Definition geom.h:131
DB db
This is the actual database object.
Definition db.cpp:32
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)
Definition system.cpp:162
@ FILE_SAVE_METHOD_SAVE_COPY
Definition system.h:36
@ FILE_SAVE_METHOD_INKSCAPE_SVG
Definition system.h:40
Glib::ustring get_file_extension(Glib::ustring const &path)
Definition sys.cpp:214
bool file_test(char const *utf8name, GFileTest test)
Definition sys.cpp:116
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.
GList * items
bool fit_canvas_to_drawing(SPDocument *doc, bool with_margins)
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
SPPage – a page object.
SPRoot: SVG <svg> implementation.
double height
double width
Glib::ustring name
Definition toolbars.cpp:55