1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3 * @file
4 * Print dialog.
5 */
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 <cmath>
17
18 #include <gtkmm.h>
19
20 #include "inkscape.h"
21 #include "preferences.h"
22 #include "print.h"
23
24 #include "extension/internal/cairo-render-context.h"
25 #include "extension/internal/cairo-renderer.h"
26 #include "document.h"
27
28 #include "util/units.h"
29 #include "helper/png-write.h"
30 #include "svg/svg-color.h"
31
32 #include <glibmm/i18n.h>
33
34
35 namespace Inkscape {
36 namespace UI {
37 namespace Dialog {
38
Print(SPDocument * doc,SPItem * base)39 Print::Print(SPDocument *doc, SPItem *base) :
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 gdouble doc_width = _doc->getWidth().value("pt");
60 gdouble doc_height = _doc->getHeight().value("pt");
61 page_setup->set_paper_size(
62 Gtk::PaperSize("custom", "custom", doc_width, doc_height, Gtk::UNIT_POINTS));
63
64 // Some print drivers, like the EPSON's ESC/P-R CUPS driver, don't accept custom
65 // page sizes, so we'll try to find a known page size.
66 // GTK+'s known paper sizes always have a longer height than width, so we'll rotate
67 // the page and set its orientation to landscape as necessary in order to match a paper size.
68 // Unfortunately, some printers, like Epilog laser cutters, don't understand landscape
69 // mode.
70 // As a compromise, we'll only rotate the page if we actually find a matching paper size,
71 // since laser cutter beds tend to be custom sizes.
72 Gtk::PageOrientation orientation = Gtk::PAGE_ORIENTATION_PORTRAIT;
73 if (_doc->getWidth().value("pt") > _doc->getHeight().value("pt")) {
74 orientation = Gtk::PAGE_ORIENTATION_REVERSE_LANDSCAPE;
75 std::swap(doc_width, doc_height);
76 }
77
78 // attempt to match document size against known paper sizes
79 std::vector<Gtk::PaperSize> known_sizes = Gtk::PaperSize::get_paper_sizes(false);
80 for (auto& size : known_sizes) {
81 if (fabs(size.get_width(Gtk::UNIT_POINTS) - doc_width) >= 1.0) {
82 // width (short edge) doesn't match
83 continue;
84 }
85 if (fabs(size.get_height(Gtk::UNIT_POINTS) - doc_height) >= 1.0) {
86 // height (short edge) doesn't match
87 continue;
88 }
89 // size matches
90 page_setup->set_paper_size(size);
91 page_setup->set_orientation(orientation);
92 break;
93 }
94
95 _printop->set_default_page_setup(page_setup);
96 _printop->set_use_full_page(true);
97
98 // set up signals
99 _workaround._doc = _doc;
100 _workaround._base = _base;
101 _workaround._tab = &_tab;
102 _printop->signal_create_custom_widget().connect(sigc::mem_fun(*this, &Print::create_custom_widget));
103 _printop->signal_begin_print().connect(sigc::mem_fun(*this, &Print::begin_print));
104 _printop->signal_draw_page().connect(sigc::mem_fun(*this, &Print::draw_page));
105
106 // build custom preferences tab
107 _printop->set_custom_tab_label(_("Rendering"));
108 }
109
draw_page(const Glib::RefPtr<Gtk::PrintContext> & context,int)110 void Print::draw_page(const Glib::RefPtr<Gtk::PrintContext>& context, int /*page_nr*/)
111 {
112 // TODO: If the user prints multiple copies we render the whole page for each copy
113 // It would be more efficient to render the page once (e.g. in "begin_print")
114 // and simply print this result as often as necessary
115
116 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
117 //printf("%s %d\n",__FUNCTION__, page_nr);
118
119 if (_workaround._tab->as_bitmap()) {
120 // Render as exported PNG
121 prefs->setBool("/dialogs/printing/asbitmap", true);
122 gdouble width = (_workaround._doc)->getWidth().value("px");
123 gdouble height = (_workaround._doc)->getHeight().value("px");
124 gdouble dpi = _workaround._tab->bitmap_dpi();
125 prefs->setDouble("/dialogs/printing/dpi", dpi);
126
127 std::string tmp_png;
128 std::string tmp_base = "inkscape-print-png-XXXXXX";
129
130 int tmp_fd;
131 if ( (tmp_fd = Glib::file_open_tmp(tmp_png, tmp_base)) >= 0) {
132 close(tmp_fd);
133
134 guint32 bgcolor = 0x00000000;
135 Inkscape::XML::Node *nv = _workaround._doc->getReprNamedView();
136 if (nv && nv->attribute("pagecolor")){
137 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
138 }
139 if (nv && nv->attribute("inkscape:pageopacity")){
140 double opacity = 1.0;
141 sp_repr_get_double (nv, "inkscape:pageopacity", &opacity);
142 bgcolor |= SP_COLOR_F_TO_U(opacity);
143 }
144
145 sp_export_png_file(_workaround._doc, tmp_png.c_str(), 0.0, 0.0,
146 width, height,
147 (unsigned long)(Inkscape::Util::Quantity::convert(width, "px", "in") * dpi),
148 (unsigned long)(Inkscape::Util::Quantity::convert(height, "px", "in") * dpi),
149 dpi, dpi, bgcolor, nullptr, nullptr, true, std::vector<SPItem*>());
150
151 // This doesn't seem to work:
152 //context->set_cairo_context ( Cairo::Context::create (Cairo::ImageSurface::create_from_png (tmp_png) ), dpi, dpi );
153 //
154 // so we'll use a surface pattern blat instead...
155 //
156 // but the C++ interface isn't implemented in cairomm:
157 //context->get_cairo_context ()->set_source_surface(Cairo::ImageSurface::create_from_png (tmp_png) );
158 //
159 // so do it in C:
160 {
161 auto png = Cairo::ImageSurface::create_from_png(tmp_png);
162 auto pattern = Cairo::SurfacePattern::create(png);
163 auto cr = context->get_cairo_context();
164 auto m = cr->get_matrix();
165 cr->scale(Inkscape::Util::Quantity::convert(1, "in", "pt") / dpi,
166 Inkscape::Util::Quantity::convert(1, "in", "pt") / dpi);
167 // FIXME: why is the origin offset??
168 cr->set_source(pattern);
169 cr->paint();
170 cr->set_matrix(m);
171 }
172
173 // Clean up
174 unlink (tmp_png.c_str());
175 }
176 else {
177 g_warning("%s", _("Could not open temporary PNG for bitmap printing"));
178 }
179 }
180 else {
181 // Render as vectors
182 prefs->setBool("/dialogs/printing/asbitmap", false);
183 Inkscape::Extension::Internal::CairoRenderer renderer;
184 Inkscape::Extension::Internal::CairoRenderContext *ctx = renderer.createContext();
185
186 // ctx->setPSLevel(CAIRO_PS_LEVEL_3);
187 ctx->setTextToPath(false);
188 ctx->setFilterToBitmap(true);
189 ctx->setBitmapResolution(72);
190
191 auto cr = context->get_cairo_context();
192 auto surface = cr->get_target();
193 auto ctm = cr->get_matrix();
194
195 bool ret = ctx->setSurfaceTarget(surface->cobj(), true, &ctm);
196 if (ret) {
197 ret = renderer.setupDocument (ctx, _workaround._doc, TRUE, 0., nullptr);
198 if (ret) {
199 renderer.renderItem(ctx, _workaround._base);
200 ctx->finish(false); // do not finish the cairo_surface_t - it's owned by our GtkPrintContext!
201 }
202 else {
203 g_warning("%s", _("Could not set up Document"));
204 }
205 }
206 else {
207 g_warning("%s", _("Failed to set CairoRenderContext"));
208 }
209
210 // Clean up
211 renderer.destroyContext(ctx);
212 }
213
214 }
215
create_custom_widget()216 Gtk::Widget *Print::create_custom_widget()
217 {
218 //printf("%s\n",__FUNCTION__);
219 return &_tab;
220 }
221
begin_print(const Glib::RefPtr<Gtk::PrintContext> &)222 void Print::begin_print(const Glib::RefPtr<Gtk::PrintContext>&)
223 {
224 //printf("%s\n",__FUNCTION__);
225 _printop->set_n_pages(1);
226 }
227
run(Gtk::PrintOperationAction,Gtk::Window & parent_window)228 Gtk::PrintOperationResult Print::run(Gtk::PrintOperationAction, Gtk::Window &parent_window)
229 {
230 // Remember to restore the previous print settings
231 _printop->set_print_settings(SP_ACTIVE_DESKTOP->printer_settings._gtk_print_settings);
232
233 try {
234 Gtk::PrintOperationResult res = _printop->run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, parent_window);
235
236 // Save printer settings (but only on success)
237 if (res == Gtk::PRINT_OPERATION_RESULT_APPLY) {
238 SP_ACTIVE_DESKTOP->printer_settings._gtk_print_settings = _printop->get_print_settings();
239 }
240
241 return res;
242 } catch (const Glib::Error &e) {
243 g_warning("Failed to print '%s': %s", _doc->getDocumentName(), e.what().c_str());
244 }
245
246 return Gtk::PRINT_OPERATION_RESULT_ERROR;
247 }
248
249
250 } // namespace Dialog
251 } // namespace UI
252 } // namespace Inkscape
253
254 /*
255 Local Variables:
256 mode:c++
257 c-file-style:"stroustrup"
258 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
259 indent-tabs-mode:nil
260 fill-column:99
261 End:
262 */
263 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
264