summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/print.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/print.cpp')
-rw-r--r--src/ui/dialog/print.cpp308
1 files changed, 308 insertions, 0 deletions
diff --git a/src/ui/dialog/print.cpp b/src/ui/dialog/print.cpp
new file mode 100644
index 0000000..da35219
--- /dev/null
+++ b/src/ui/dialog/print.cpp
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Print dialog.
+ */
+/* Authors:
+ * Kees Cook <kees@outflux.net>
+ * Abhishek Sharma
+ * Patrick McDermott
+ *
+ * Copyright (C) 2007 Kees Cook
+ * Copyright (C) 2017 Patrick McDermott
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cmath>
+
+#include <gtkmm.h>
+
+#include "inkscape.h"
+#include "preferences.h"
+#include "print.h"
+
+#include "extension/internal/cairo-render-context.h"
+#include "extension/internal/cairo-renderer.h"
+#include "document.h"
+#include "object/sp-page.h"
+
+#include "util/units.h"
+#include "helper/png-write.h"
+#include "page-manager.h"
+#include "svg/svg-color.h"
+
+#include <glibmm/i18n.h>
+
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+Glib::RefPtr<Gtk::PrintSettings> &get_printer_settings()
+{
+ static Glib::RefPtr<Gtk::PrintSettings> printer_settings;
+ return printer_settings;
+}
+
+Print::Print(SPDocument *doc, SPItem *base) :
+ _doc (doc),
+ _base (base)
+{
+ g_assert (_doc);
+ g_assert (_base);
+
+ _printop = Gtk::PrintOperation::create();
+
+ // set up dialog title, based on document name
+ const Glib::ustring jobname = _doc->getDocumentName() ? _doc->getDocumentName() : _("SVG Document");
+ Glib::ustring title = _("Print");
+ title += " ";
+ title += jobname;
+ _printop->set_job_name(title);
+
+ _printop->set_unit(Gtk::UNIT_POINTS);
+ Glib::RefPtr<Gtk::PageSetup> page_setup = Gtk::PageSetup::create();
+
+ // Default to a custom paper size, in case we can't find a more specific size
+ set_paper_size(page_setup, _doc->getWidth().value("pt"), _doc->getHeight().value("pt"));
+ _printop->set_default_page_setup(page_setup);
+ _printop->set_use_full_page(true);
+ _printop->set_n_pages(1);
+
+ // Now process actual multi-page setup.
+ auto &pm = _doc->getPageManager();
+ if (pm.hasPages()) {
+ // This appears to be limiting which pages get rendered
+ _printop->set_n_pages(pm.getPageCount());
+ _printop->set_current_page(pm.getSelectedPageIndex());
+ _printop->signal_request_page_setup().connect(sigc::mem_fun(*this, &Print::setup_page));
+ }
+
+ // set up signals
+ _workaround._doc = _doc;
+ _workaround._base = _base;
+ _workaround._tab = &_tab;
+ _printop->signal_create_custom_widget().connect(sigc::mem_fun(*this, &Print::create_custom_widget));
+ _printop->signal_begin_print().connect(sigc::mem_fun(*this, &Print::begin_print));
+ _printop->signal_draw_page().connect(sigc::mem_fun(*this, &Print::draw_page));
+
+ // build custom preferences tab
+ _printop->set_custom_tab_label(_("Rendering"));
+}
+
+/**
+ * Return the required page setup, only connected for multi-page documents
+ * and only required where there are pages of different sizes.
+ */
+void Print::setup_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_nr,
+ const Glib::RefPtr<Gtk::PageSetup> &setup)
+{
+ auto &pm = _workaround._doc->getPageManager();
+ if (auto page = pm.getPage(page_nr)) {
+ auto rect = page->getDesktopRect();
+ auto width = Inkscape::Util::Quantity::convert(rect.width(), "px", "pt");
+ auto height = Inkscape::Util::Quantity::convert(rect.height(), "px", "pt");
+ set_paper_size(setup, width, height);
+ }
+}
+
+/**
+ * Set the paper size with correct orientation.
+ */
+void Print::set_paper_size(const Glib::RefPtr<Gtk::PageSetup> &page_setup, double page_width, double page_height)
+{
+ auto p_size = Gtk::PaperSize("custom", "custom", page_width, page_height, Gtk::UNIT_POINTS);
+
+ // Some print drivers, like the EPSON's ESC/P-R CUPS driver, don't accept custom
+ // page sizes, so we'll try to find a known page size.
+ // GTK+'s known paper sizes always have a longer height than width, so we'll rotate
+ // the page and set its orientation to landscape as necessary in order to match a paper size.
+ // Unfortunately, some printers, like Epilog laser cutters, don't understand landscape
+ // mode.
+ // As a compromise, we'll only rotate the page if we actually find a matching paper size,
+ // since laser cutter beds tend to be custom sizes.
+ Gtk::PageOrientation orientation = Gtk::PAGE_ORIENTATION_PORTRAIT;
+ if (page_width > page_height) {
+ orientation = Gtk::PAGE_ORIENTATION_REVERSE_LANDSCAPE;
+ std::swap(page_width, page_height);
+ }
+
+ // attempt to match document size against known paper sizes
+ std::vector<Gtk::PaperSize> known_sizes = Gtk::PaperSize::get_paper_sizes(false);
+ for (auto& size : known_sizes) {
+ if (fabs(size.get_width(Gtk::UNIT_POINTS) - page_width) >= 1.0) {
+ // width (short edge) doesn't match
+ continue;
+ }
+ if (fabs(size.get_height(Gtk::UNIT_POINTS) - page_height) >= 1.0) {
+ // height (short edge) doesn't match
+ continue;
+ }
+ // size matches
+ p_size = size;
+ break;
+ }
+ page_setup->set_paper_size(p_size);
+ page_setup->set_orientation(orientation);
+}
+
+void Print::draw_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_nr)
+{
+ // TODO: If the user prints multiple copies we render the whole page for each copy
+ // It would be more efficient to render the page once (e.g. in "begin_print")
+ // and simply print this result as often as necessary
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ //printf("%s %d\n",__FUNCTION__, page_nr);
+
+ auto &pm = _workaround._doc->getPageManager();
+ auto page = pm.getPage(page_nr); // nullptr when no pages.
+
+ if (_workaround._tab->as_bitmap()) {
+ // Render as exported PNG
+ prefs->setBool("/dialogs/printing/asbitmap", true);
+ gdouble dpi = _workaround._tab->bitmap_dpi();
+ prefs->setDouble("/dialogs/printing/dpi", dpi);
+
+ auto rect = *(_workaround._doc->preferredBounds());
+ if (page) {
+ rect = page->getDesktopRect();
+ }
+
+ std::string tmp_png;
+ std::string tmp_base = "inkscape-print-png-XXXXXX";
+
+ int tmp_fd;
+ if ( (tmp_fd = Glib::file_open_tmp(tmp_png, tmp_base)) >= 0) {
+ close(tmp_fd);
+
+ guint32 bgcolor = 0x00000000;
+ Inkscape::XML::Node *nv = _workaround._doc->getReprNamedView();
+ if (nv && nv->attribute("pagecolor")){
+ bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
+ }
+ if (nv && nv->attribute("inkscape:pageopacity")){
+ double opacity = nv->getAttributeDouble("inkscape:pageopacity", 1.0);
+ bgcolor |= SP_COLOR_F_TO_U(opacity);
+ }
+
+ sp_export_png_file(_workaround._doc, tmp_png.c_str(), rect,
+ (unsigned long)(Inkscape::Util::Quantity::convert(rect.width(), "px", "in") * dpi),
+ (unsigned long)(Inkscape::Util::Quantity::convert(rect.height(), "px", "in") * dpi),
+ dpi, dpi, bgcolor, nullptr, nullptr, true, std::vector<SPItem*>());
+
+ // This doesn't seem to work:
+ //context->set_cairo_context ( Cairo::Context::create (Cairo::ImageSurface::create_from_png (tmp_png) ), dpi, dpi );
+ //
+ // so we'll use a surface pattern blat instead...
+ //
+ // but the C++ interface isn't implemented in cairomm:
+ //context->get_cairo_context ()->set_source_surface(Cairo::ImageSurface::create_from_png (tmp_png) );
+ //
+ // so do it in C:
+ {
+ auto png = Cairo::ImageSurface::create_from_png(tmp_png);
+ auto pattern = Cairo::SurfacePattern::create(png);
+ auto cr = context->get_cairo_context();
+ auto m = cr->get_matrix();
+ cr->scale(Inkscape::Util::Quantity::convert(1, "in", "pt") / dpi,
+ Inkscape::Util::Quantity::convert(1, "in", "pt") / dpi);
+ // FIXME: why is the origin offset??
+ cr->set_source(pattern);
+ cr->paint();
+ cr->set_matrix(m);
+ }
+
+ // Clean up
+ unlink (tmp_png.c_str());
+ }
+ else {
+ g_warning("%s", _("Could not open temporary PNG for bitmap printing"));
+ }
+ }
+ else {
+ // Render as vectors
+ prefs->setBool("/dialogs/printing/asbitmap", false);
+ Inkscape::Extension::Internal::CairoRenderer renderer;
+ Inkscape::Extension::Internal::CairoRenderContext *ctx = renderer.createContext();
+
+ // ctx->setPSLevel(CAIRO_PS_LEVEL_3);
+ ctx->setTextToPath(false);
+ ctx->setFilterToBitmap(true);
+ ctx->setBitmapResolution(72);
+
+ auto cr = context->get_cairo_context();
+ auto surface = cr->get_target();
+ auto ctm = cr->get_matrix();
+
+ bool ret = ctx->setSurfaceTarget(surface->cobj(), true, &ctm);
+ if (ret) {
+ ret = renderer.setupDocument (ctx, _workaround._doc);
+ if (ret) {
+ if (auto page = pm.getPage(page_nr)) {
+ renderer.renderPage(ctx, _workaround._doc, page, false);
+ } else {
+ renderer.renderItem(ctx, _workaround._base);
+ }
+ ctx->finish(false); // do not finish the cairo_surface_t - it's owned by our GtkPrintContext!
+ }
+ else {
+ g_warning("%s", _("Could not set up Document"));
+ }
+ }
+ else {
+ g_warning("%s", _("Failed to set CairoRenderContext"));
+ }
+
+ // Clean up
+ renderer.destroyContext(ctx);
+ }
+
+}
+
+Gtk::Widget *Print::create_custom_widget()
+{
+ return &_tab;
+}
+
+void Print::begin_print(const Glib::RefPtr<Gtk::PrintContext>&)
+{
+ // Could change which pages get printed here, but nothing to do.
+}
+
+Gtk::PrintOperationResult Print::run(Gtk::PrintOperationAction, Gtk::Window &parent_window)
+{
+ // Remember to restore the previous print settings
+ _printop->set_print_settings(get_printer_settings());
+
+ try {
+ Gtk::PrintOperationResult res = _printop->run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, parent_window);
+
+ // Save printer settings (but only on success)
+ if (res == Gtk::PRINT_OPERATION_RESULT_APPLY) {
+ get_printer_settings() = _printop->get_print_settings();
+ }
+
+ return res;
+ } catch (const Glib::Error &e) {
+ g_warning("Failed to print '%s': %s", _doc->getDocumentName(), e.what().c_str());
+ }
+
+ return Gtk::PRINT_OPERATION_RESULT_ERROR;
+}
+
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :