Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
print.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Authors:
7 * Kees Cook <kees@outflux.net>
8 * Abhishek Sharma
9 * Patrick McDermott
10 *
11 * Copyright (C) 2007 Kees Cook
12 * Copyright (C) 2017 Patrick McDermott
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include "preferences.h"
17#include "print.h"
18
21#include "object/sp-page.h"
22
23#include "util/units.h"
24#include "helper/png-write.h"
25
26#include <glibmm/i18n.h>
27
28
29namespace Inkscape {
30namespace UI {
31namespace Dialog {
32
33Glib::RefPtr<Gtk::PrintSettings> &get_printer_settings()
34{
35 static Glib::RefPtr<Gtk::PrintSettings> printer_settings;
36 return printer_settings;
37}
38
40 _doc (doc),
41 _base (base)
42{
43 g_assert (_doc);
44 g_assert (_base);
45
46 _printop = Gtk::PrintOperation::create();
47
48 // set up dialog title, based on document name
49 const Glib::ustring jobname = _doc->getDocumentName() ? _doc->getDocumentName() : _("SVG Document");
50 Glib::ustring title = _("Print");
51 title += " ";
52 title += jobname;
53 _printop->set_job_name(title);
54
55 _printop->set_unit(Gtk::Unit::POINTS);
56 Glib::RefPtr<Gtk::PageSetup> page_setup = Gtk::PageSetup::create();
57
58 // Default to a custom paper size, in case we can't find a more specific size
59 set_paper_size(page_setup, _doc->getWidth().value("pt"), _doc->getHeight().value("pt"));
60 _printop->set_default_page_setup(page_setup);
61 _printop->set_use_full_page(true);
62 _printop->set_n_pages(1);
63
64 // Now process actual multi-page setup.
65 auto &pm = _doc->getPageManager();
66 if (pm.hasPages()) {
67 // This appears to be limiting which pages get rendered
68 _printop->set_n_pages(pm.getPageCount());
69 _printop->set_current_page(pm.getSelectedPageIndex());
70 _printop->signal_request_page_setup().connect(sigc::mem_fun(*this, &Print::setup_page));
71 }
72
73 // set up signals
77 _printop->signal_create_custom_widget().connect(sigc::mem_fun(*this, &Print::create_custom_widget), true);
78 _printop->signal_begin_print().connect(sigc::mem_fun(*this, &Print::begin_print));
79 _printop->signal_draw_page().connect(sigc::mem_fun(*this, &Print::draw_page));
80
81 // build custom preferences tab
82 _printop->set_custom_tab_label(_("Rendering"));
83}
84
89void Print::setup_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_nr,
90 const Glib::RefPtr<Gtk::PageSetup> &setup)
91{
92 auto &pm = _workaround._doc->getPageManager();
93 if (auto page = pm.getPage(page_nr)) {
94 auto rect = page->getDesktopRect();
95 auto width = Inkscape::Util::Quantity::convert(rect.width(), "px", "pt");
96 auto height = Inkscape::Util::Quantity::convert(rect.height(), "px", "pt");
98 }
99}
100
104void Print::set_paper_size(const Glib::RefPtr<Gtk::PageSetup> &page_setup, double page_width, double page_height)
105{
106 auto p_size = Gtk::PaperSize("custom", "custom", page_width, page_height, Gtk::Unit::POINTS);
107 Gtk::PageOrientation orientation = Gtk::PageOrientation::PORTRAIT;
108
109 // Some print drivers, like the EPSON's ESC/P-R CUPS driver, don't accept custom
110 // page sizes, so we'll try to find a known page size.
111 // GTK+'s known paper sizes always have a longer height than width, so we'll rotate
112 // the page and set its orientation to landscape as necessary in order to match a paper size.
113 // Unfortunately, some printers, like Epilog laser cutters, don't understand landscape
114 // mode.
115 // As a compromise, we'll only rotate the page if we actually find a matching paper size,
116 // since laser cutter beds tend to be custom sizes.
117 Gtk::PageOrientation search_orientation = Gtk::PageOrientation::PORTRAIT;
118 if (page_width > page_height) {
119 search_orientation = Gtk::PageOrientation::LANDSCAPE;
120 std::swap(page_width, page_height);
121 }
122
123 // attempt to match document size against known paper sizes
124 std::vector<Gtk::PaperSize> known_sizes = Gtk::PaperSize::get_paper_sizes(false);
125 for (auto& size : known_sizes) {
126 if (fabs(size.get_width(Gtk::Unit::POINTS) - page_width) >= 1.0) {
127 // width (short edge) doesn't match
128 continue;
129 }
130 if (fabs(size.get_height(Gtk::Unit::POINTS) - page_height) >= 1.0) {
131 // height (short edge) doesn't match
132 continue;
133 }
134 // size matches
135 p_size = size;
136 orientation = search_orientation;
137 break;
138 }
139 page_setup->set_paper_size(p_size);
140 page_setup->set_orientation(orientation);
141}
142
143void Print::draw_page(const Glib::RefPtr<Gtk::PrintContext>& context, int page_nr)
144{
145 // TODO: If the user prints multiple copies we render the whole page for each copy
146 // It would be more efficient to render the page once (e.g. in "begin_print")
147 // and simply print this result as often as necessary
148
150 //printf("%s %d\n",__FUNCTION__, page_nr);
151
152 auto &pm = _workaround._doc->getPageManager();
153 auto page = pm.getPage(page_nr); // nullptr when no pages.
154
155 if (_workaround._tab->as_bitmap()) {
156 // Render as exported PNG
157 prefs->setBool("/dialogs/printing/asbitmap", true);
158 gdouble dpi = _workaround._tab->bitmap_dpi();
159 prefs->setDouble("/dialogs/printing/dpi", dpi);
160
161 auto rect = *(_workaround._doc->preferredBounds());
162 if (page) {
163 rect = page->getDesktopRect();
164 }
165
167 std::string tmp_png;
168 std::string tmp_base = "inkscape-print-png-XXXXXX";
169
170 int tmp_fd;
171 if ( (tmp_fd = Glib::file_open_tmp(tmp_png, tmp_base)) >= 0) {
172 close(tmp_fd);
173
174 guint32 bgcolor = 0x00000000;
176 if (nv && nv->attribute("pagecolor")){
177 if (auto c = Colors::Color::parse(nv->attribute("pagecolor"))) {
178 // TODO allow page color to be any color space, not just RGB
179 bgcolor = c->toRGBA();
180 }
181 }
182 if (nv && nv->attribute("inkscape:pageopacity")){
183 double opacity = nv->getAttributeDouble("inkscape:pageopacity", 1.0);
184 bgcolor |= SP_COLOR_F_TO_U(opacity);
185 }
186
187 sp_export_png_file(_workaround._doc, Glib::filename_to_utf8(tmp_png).c_str(), rect,
188 (unsigned long)(Inkscape::Util::Quantity::convert(rect.width(), "px", "in") * dpi),
189 (unsigned long)(Inkscape::Util::Quantity::convert(rect.height(), "px", "in") * dpi),
190 dpi, dpi, bgcolor, nullptr, nullptr, true, {});
191
192 // This doesn't seem to work:
193 //context->set_cairo_context ( Cairo::Context::create (Cairo::ImageSurface::create_from_png (tmp_png) ), dpi, dpi );
194 //
195 // so we'll use a surface pattern blat instead...
196 //
197 // but the C++ interface isn't implemented in cairomm:
198 //context->get_cairo_context ()->set_source_surface(Cairo::ImageSurface::create_from_png (tmp_png) );
199 //
200 // so do it in C:
201 {
202 auto png = Cairo::ImageSurface::create_from_png(tmp_png);
203 auto pattern = Cairo::SurfacePattern::create(png);
204 auto cr = context->get_cairo_context();
205 auto m = cr->get_matrix();
206 cr->scale(Inkscape::Util::Quantity::convert(1, "in", "pt") / dpi,
207 Inkscape::Util::Quantity::convert(1, "in", "pt") / dpi);
208 // FIXME: why is the origin offset??
209 cr->set_source(pattern);
210 cr->paint();
211 cr->set_matrix(m);
212 }
213
214 // Clean up
215 unlink (tmp_png.c_str());
216 }
217 else {
218 g_warning("%s", _("Could not open temporary PNG for bitmap printing"));
219 }
220 } else {
221 // Render as vectors
222 prefs->setBool("/dialogs/printing/asbitmap", false);
224 auto ctx = renderer.createContext();
225
226 ctx.setTextToPath(false);
227 ctx.setFilterToBitmap(true);
228 ctx.setBitmapResolution(72);
229
230 auto cr = context->get_cairo_context();
231 auto surface = cr->get_target();
232 auto ctm = cr->get_matrix();
233
234 bool ret = ctx.setSurfaceTarget(surface->cobj(), true, &ctm);
235 if (ret) {
236 ret = renderer.setupDocument(&ctx, _workaround._doc);
237 if (ret) {
238 if (auto page = pm.getPage(page_nr)) {
239 renderer.renderPage(&ctx, _workaround._doc, page, false);
240 } else {
241 renderer.renderItem(&ctx, _workaround._base);
242 }
243 ctx.finish(false); // do not finish the cairo_surface_t - it's owned by our GtkPrintContext!
244 } else {
245 g_warning("%s", _("Could not set up Document"));
246 }
247 } else {
248 g_warning("%s", _("Failed to set CairoRenderContext"));
249 }
250 }
251}
252
254{
255 return &_tab;
256}
257
258void Print::begin_print(const Glib::RefPtr<Gtk::PrintContext>&)
259{
260 // Could change which pages get printed here, but nothing to do.
261}
262
263Gtk::PrintOperation::Result Print::run(Gtk::PrintOperation::Action, Gtk::Window &parent_window)
264{
265 // Remember to restore the previous print settings
266 _printop->set_print_settings(get_printer_settings());
267
268 try {
269 Gtk::PrintOperation::Result res = _printop->run(Gtk::PrintOperation::Action::PRINT_DIALOG, parent_window);
270
271 // Save printer settings (but only on success)
272 if (res == Gtk::PrintOperation::Result::APPLY) {
273 get_printer_settings() = _printop->get_print_settings();
274 }
275
276 return res;
277 } catch (const Glib::Error &e) {
278 g_warning("Failed to print '%s': %s", _doc->getDocumentName(), e.what());
279 }
280
281 return Gtk::PrintOperation::Result::ERROR;
282}
283
284
285} // namespace Dialog
286} // namespace UI
287} // namespace Inkscape
288
289/*
290 Local Variables:
291 mode:c++
292 c-file-style:"stroustrup"
293 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
294 indent-tabs-mode:nil
295 fill-column:99
296 End:
297*/
298// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Declaration of CairoRenderContext, a class used for rendering with Cairo.
Declaration of CairoRenderer, a class used for rendering via a CairoRenderContext.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition canvas.cpp:137
uint64_t page
Definition canvas.cpp:171
bool renderPage(CairoRenderContext *ctx, SPDocument *doc, SPPage const *page, bool stretch_to_fit)
void renderItem(CairoRenderContext *ctx, SPItem const *item, SPItem const *origin=nullptr, SPPage const *page=nullptr)
Traverses the object tree and invokes the render methods.
bool setupDocument(CairoRenderContext *ctx, SPDocument *doc, SPItem const *base=nullptr)
Initializes the CairoRenderContext according to the specified SPDocument.
SPPage * getPage(int index) const
Get the page at the given position or return nullptr if out of range.
Geom::OptRect getDesktopRect() const
Returns the total area of all the pages in desktop units.
Preference storage class.
Definition preferences.h:61
static Preferences * get()
Access the singleton Preferences object.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
void draw_page(const Glib::RefPtr< Gtk::PrintContext > &context, int page_nr)
Definition print.cpp:143
Gtk::PrintOperation::Result run(Gtk::PrintOperation::Action, Gtk::Window &parent_window)
Definition print.cpp:263
struct workaround_gtkmm _workaround
Definition print.h:56
Inkscape::UI::Widget::RenderingOptions _tab
Definition print.h:54
void setup_page(const Glib::RefPtr< Gtk::PrintContext > &context, int page_nr, const Glib::RefPtr< Gtk::PageSetup > &setup)
Return the required page setup, only connected for multi-page documents and only required where there...
Definition print.cpp:89
Print(SPDocument *doc, SPItem *base)
Definition print.cpp:39
Gtk::Widget * create_custom_widget()
Definition print.cpp:253
void set_paper_size(const Glib::RefPtr< Gtk::PageSetup > &, double width, double height)
Set the paper size with correct orientation.
Definition print.cpp:104
void begin_print(const Glib::RefPtr< Gtk::PrintContext > &)
Definition print.cpp:258
Glib::RefPtr< Gtk::PrintOperation > _printop
Definition print.h:51
double value(Unit const *u) const
Return the quantity's value in the specified unit.
Definition units.cpp:565
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.
Typed SVG document implementation.
Definition document.h:101
Inkscape::Util::Quantity getWidth() const
Definition document.cpp:848
char const * getDocumentName() const
basename or other human-readable label for the document.
Definition document.h:236
Geom::OptRect preferredBounds() const
Definition document.cpp:969
Inkscape::PageManager & getPageManager()
Definition document.h:162
Inkscape::Util::Quantity getHeight() const
Definition document.cpp:887
Inkscape::XML::Node * getReprNamedView()
Definition document.cpp:225
Base class for visual SVG elements.
Definition sp-item.h:109
Inkscape::XML::Document * _doc
Reference to the clipboard's Inkscape::XML::Document.
constexpr uint32_t SP_COLOR_F_TO_U(double v)
Definition utils.h:23
double c[8][4]
unsigned int guint32
Glib::RefPtr< Gtk::PrintSettings > & get_printer_settings()
Definition print.cpp:33
static constexpr int height
Helper class to stream background task notifications as a series of messages.
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.
SPPage – a page object.
SPItem * _base
Definition print.h:33
SPDocument * _doc
Definition print.h:32
Inkscape::UI::Widget::RenderingOptions * _tab
Definition print.h:34
double width