Code

Fix for Bug #166678 (Paper size or orientation are not used when printing) by Adrian...
[inkscape.git] / src / ui / widget / page-sizer.cpp
1 /** \file
2  *
3  * Paper-size widget and helper functions
4  *
5  * Authors:
6  *   bulia byak <buliabyak@users.sf.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Jon Phillips <jon@rejon.org>
9  *   Ralf Stephan <ralf@ark.in-berlin.de> (Gtkmm)
10  *   Bob Jamison <ishmal@users.sf.net>
11  *
12  * Copyright (C) 2000 - 2006 Authors
13  *
14  * Released under GNU GPL.  Read the file 'COPYING' for more information
15  */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <string.h>
22 #include <vector>
23 #include <string>
25 #include <cmath>
26 #include <gtkmm.h>
27 #include "ui/widget/button.h"
29 #include "ui/widget/scalar-unit.h"
31 #include "helper/units.h"
32 #include "inkscape.h"
33 #include "verbs.h"
34 #include "desktop-handles.h"
35 #include "document.h"
36 #include "desktop.h"
37 #include "page-sizer.h"
38 #include "helper/action.h"
39 #include "sp-root.h"
41 using std::pair;
43 namespace Inkscape {
44 namespace UI {
45 namespace Widget {
47     /** \note
48      * The ISO page sizes in the table below differ from ghostscript's idea of page sizes (by
49      * less than 1pt).  Being off by <1pt should be OK for most purposes, but may cause fuzziness
50      * (antialiasing) problems when printing to 72dpi or 144dpi printers or bitmap files due to
51      * postscript's different coordinate system (y=0 meaning bottom of page in postscript and top
52      * of page in SVG).  I haven't looked into whether this does in fact cause fuzziness, I merely
53      * note the possibility.  Rounding done by extension/internal/ps.cpp (e.g. floor/ceil calls)
54      * will also affect whether fuzziness occurs.
55      *
56      * The remainder of this comment discusses the origin of the numbers used for ISO page sizes in
57      * this table and in ghostscript.
58      *
59      * The versions here, in mm, are the official sizes according to
60      * <a href="http://en.wikipedia.org/wiki/Paper_sizes">http://en.wikipedia.org/wiki/Paper_sizes</a>
61      * at 2005-01-25.  (The ISO entries in the below table
62      * were produced mechanically from the table on that page.)
63      *
64      * (The rule seems to be that A0, B0, ..., D0. sizes are rounded to the nearest number of mm
65      * from the "theoretical size" (i.e. 1000 * sqrt(2) or pow(2.0, .25) or the like), whereas
66      * going from e.g. A0 to A1 always take the floor of halving -- which by chance coincides
67      * exactly with flooring the "theoretical size" for n != 0 instead of the rounding to nearest
68      * done for n==0.)
69      *
70      * Ghostscript paper sizes are given in gs_statd.ps according to gs(1).  gs_statd.ps always
71      * uses an integer number of pt: sometimes gs_statd.ps rounds to nearest (e.g. a1), sometimes
72      * floors (e.g. a10), sometimes ceils (e.g. a8).
73      *
74      * I'm not sure how ghostscript's gs_statd.ps was calculated: it isn't just rounding the
75      * "theoretical size" of each page to pt (see a0), nor is it rounding the a0 size times an
76      * appropriate power of two (see a1).  Possibly it was prepared manually, with a human applying
77      * inconsistent rounding rules when converting from mm to pt.
78      */
79     /** \todo
80      * Should we include the JIS B series (used in Japan)
81      * (JIS B0 is sometimes called JB0, and similarly for JB1 etc)?
82      * Should we exclude B7--B10 and A7--10 to make the list smaller ?
83      * Should we include any of the ISO C, D and E series (see below) ?
84      */
86 struct PaperSizeRec {
87     char const * const name;  //name
88     double const smaller;     //lesser dimension
89     double const larger;      //greater dimension
90     SPUnitId const unit;      //units
91 };
93 // list of page formats that should be in landscape automatically
94 static std::vector<std::string> lscape_papers;
96 static void
97 fill_landscape_papers() {
98     lscape_papers.push_back("US #10 Envelope");
99     lscape_papers.push_back("DL Envelope");
100     lscape_papers.push_back("Banner 468x60");
101     lscape_papers.push_back("Business Card (ISO 7810)");
102     lscape_papers.push_back("Business Card (US)");
103     lscape_papers.push_back("Business Card (Europe)");
104     lscape_papers.push_back("Business Card (Aus/NZ)");
107 static PaperSizeRec const inkscape_papers[] = {
108     { "A4",                210,  297, SP_UNIT_MM },
109     { "US Letter",         8.5,   11, SP_UNIT_IN },
110     { "US Legal",          8.5,   14, SP_UNIT_IN },
111     { "US Executive",     7.25, 10.5, SP_UNIT_IN },
112     { "A0",                841, 1189, SP_UNIT_MM },
113     { "A1",                594,  841, SP_UNIT_MM },
114     { "A2",                420,  594, SP_UNIT_MM },
115     { "A3",                297,  420, SP_UNIT_MM },
116     { "A5",                148,  210, SP_UNIT_MM },
117     { "A6",                105,  148, SP_UNIT_MM },
118     { "A7",                 74,  105, SP_UNIT_MM },
119     { "A8",                 52,   74, SP_UNIT_MM },
120     { "A9",                 37,   52, SP_UNIT_MM },
121     { "A10",                26,   37, SP_UNIT_MM },
122     { "B0",               1000, 1414, SP_UNIT_MM },
123     { "B1",                707, 1000, SP_UNIT_MM },
124     { "B2",                500,  707, SP_UNIT_MM },
125     { "B3",                353,  500, SP_UNIT_MM },
126     { "B4",                250,  353, SP_UNIT_MM },
127     { "B5",                176,  250, SP_UNIT_MM },
128     { "B6",                125,  176, SP_UNIT_MM },
129     { "B7",                 88,  125, SP_UNIT_MM },
130     { "B8",                 62,   88, SP_UNIT_MM },
131     { "B9",                 44,   62, SP_UNIT_MM },
132     { "B10",                31,   44, SP_UNIT_MM },
136 //#if 0
137          /*
138          Whether to include or exclude these depends on how
139          big we mind our page size menu
140          becoming.  C series is used for envelopes;
141          don't know what D and E series are used for.
142          */
144     { "C0",                917, 1297, SP_UNIT_MM },
145     { "C1",                648,  917, SP_UNIT_MM },
146     { "C2",                458,  648, SP_UNIT_MM },
147     { "C3",                324,  458, SP_UNIT_MM },
148     { "C4",                229,  324, SP_UNIT_MM },
149     { "C5",                162,  229, SP_UNIT_MM },
150     { "C6",                114,  162, SP_UNIT_MM },
151     { "C7",                 81,  114, SP_UNIT_MM },
152     { "C8",                 57,   81, SP_UNIT_MM },
153     { "C9",                 40,   57, SP_UNIT_MM },
154     { "C10",                28,   40, SP_UNIT_MM },
155     { "D1",                545,  771, SP_UNIT_MM },
156     { "D2",                385,  545, SP_UNIT_MM },
157     { "D3",                272,  385, SP_UNIT_MM },
158     { "D4",                192,  272, SP_UNIT_MM },
159     { "D5",                136,  192, SP_UNIT_MM },
160     { "D6",                 96,  136, SP_UNIT_MM },
161     { "D7",                 68,   96, SP_UNIT_MM },
162     { "E3",                400,  560, SP_UNIT_MM },
163     { "E4",                280,  400, SP_UNIT_MM },
164     { "E5",                200,  280, SP_UNIT_MM },
165     { "E6",                140,  200, SP_UNIT_MM },
166 //#endif
170     { "CSE",               462,  649, SP_UNIT_PT },
171     { "US #10 Envelope", 4.125,  9.5, SP_UNIT_IN },
172     /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
173        sizes. */
174     { "DL Envelope",       110,  220, SP_UNIT_MM },
175     { "Ledger/Tabloid",     11,   17, SP_UNIT_IN },
176     /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
177        means different sizes to different people: different people may expect the width to be
178        either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
179        restricting our interpretation to foolscap folio.  If you wish to introduce a folio-like
180        page size to the list, then please consider using a name more specific than just `Folio' or
181        `Foolscap Folio'. */
182     { "Banner 468x60",      60,  468, SP_UNIT_PX },
183     { "Icon 16x16",         16,   16, SP_UNIT_PX },
184     { "Icon 32x32",         32,   32, SP_UNIT_PX },
185     { "Icon 48x48",         48,   48, SP_UNIT_PX },
186     /* business cards */
187     { "Business Card (ISO 7810)", 53.98, 85.60, SP_UNIT_MM },
188     { "Business Card (US)",             2,     3.5,  SP_UNIT_IN },
189     { "Business Card (Europe)",        55,    85,    SP_UNIT_MM },
190     { "Business Card (Aus/NZ)",        55,    90,    SP_UNIT_MM },
191     { NULL,                     0,    0, SP_UNIT_PX },
192 };
196 //########################################################################
197 //# P A G E    S I Z E R
198 //########################################################################
200 //The default unit for this widget and its calculations
201 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
204 /**
205  * Constructor
206  */
207 PageSizer::PageSizer(Registry & _wr)
208     : Gtk::VBox(false,4),
209       _dimensionUnits( _("U_nits:"), "units", _wr ),
210       _dimensionWidth( _("_Width:"), _("Width of paper"), "width", _dimensionUnits, _wr ),
211       _dimensionHeight( _("_Height:"), _("Height of paper"), "height", _dimensionUnits, _wr ),
212       _widgetRegistry(&_wr)
214     //# Set up the Paper Size combo box
215     _paperSizeListStore = Gtk::ListStore::create(_paperSizeListColumns);
216     _paperSizeList.set_model(_paperSizeListStore);
217     _paperSizeList.append_column(_("Name"),
218                                  _paperSizeListColumns.nameColumn);
219     _paperSizeList.append_column(_("Description"),
220                                  _paperSizeListColumns.descColumn);
221     _paperSizeList.set_headers_visible(false);
222     _paperSizeListSelection = _paperSizeList.get_selection();
223     _paper_size_list_connection =
224         _paperSizeListSelection->signal_changed().connect (
225             sigc::mem_fun (*this, &PageSizer::on_paper_size_list_changed));
226     _paperSizeListScroller.add(_paperSizeList);
227     _paperSizeListScroller.set_shadow_type(Gtk::SHADOW_IN);
228     _paperSizeListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
229     _paperSizeListScroller.set_size_request(-1, 90);
231     fill_landscape_papers();
233     for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
234     {
235         Glib::ustring name = p->name;
236         char formatBuf[80];
237         snprintf(formatBuf, 79, "%0.1f x %0.1f", p->smaller, p->larger);
238         Glib::ustring desc = formatBuf;
239         if (p->unit == SP_UNIT_IN)
240             desc.append(" in");
241         else if (p->unit == SP_UNIT_MM)
242              desc.append(" mm");
243         else if (p->unit == SP_UNIT_PX)
244             desc.append(" px");
245         PaperSize paper(name, p->smaller, p->larger, p->unit);
246         _paperSizeTable[name] = paper;
247         Gtk::TreeModel::Row row = *(_paperSizeListStore->append());
248         row[_paperSizeListColumns.nameColumn] = name;
249         row[_paperSizeListColumns.descColumn] = desc;
250         }
251     //Gtk::TreeModel::iterator iter = _paperSizeListStore->children().begin();
252     //if (iter)
253     //    _paperSizeListSelection->select(iter);
256     pack_start (_paperSizeListBox, true, true, 0);
257     _paperSizeListLabel.set_label(_("P_age size:"));
258     _paperSizeListLabel.set_use_underline();
259     _paperSizeListBox.pack_start (_paperSizeListLabel, false, false, 0);
260     _paperSizeListLabel.set_mnemonic_widget (_paperSizeList);
261     _paperSizeListBox.pack_start (_paperSizeListScroller, true, true, 0);
263     //## Set up orientation radio buttons
264     pack_start (_orientationBox, false, false, 0);
265     _orientationLabel.set_label(_("Page orientation:"));
266     _orientationBox.pack_start(_orientationLabel, false, false, 0);
267     _landscapeButton.set_use_underline();
268     _landscapeButton.set_label(_("_Landscape"));
269     _landscapeButton.set_active(true);
270     Gtk::RadioButton::Group group = _landscapeButton.get_group();
271     _orientationBox.pack_end (_landscapeButton, false, false, 5);
272     _portraitButton.set_use_underline();
273     _portraitButton.set_label(_("_Portrait"));
274     _portraitButton.set_active(true);
275     _orientationBox.pack_end (_portraitButton, false, false, 5);
276     _portraitButton.set_group (group);
277     _portraitButton.set_active (true);
279     //## Set up custom size frame
280     _customFrame.set_label(_("Custom size"));
281     pack_start (_customFrame, false, false, 0);
282     _customTable.resize(2, 2);
283     _customTable.set_border_width (4);
284     _customTable.set_row_spacings (4);
285     _customTable.set_col_spacings (4);
286     _customTable.attach(_dimensionWidth, 0,1,0,1);
287     _customTable.attach(_dimensionUnits, 1,2,0,1);
288     _customTable.attach(_dimensionHeight, 0,1,1,2);
289     _customTable.attach(_fitPageButton,              1,2,1,2);
290     _customFrame.add(_customTable);
292     _fitPageButton.set_use_underline();
293     _fitPageButton.set_label(_("_Fit page to selection"));
294     _tips.set_tip(_fitPageButton, _("Resize the page to fit the current selection, or the entire drawing if there is no selection"));
298 /**
299  * Destructor
300  */
301 PageSizer::~PageSizer()
307 /**
308  * Initialize or reset this widget
309  */
310 void
311 PageSizer::init ()
313     _landscape_connection = _landscapeButton.signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
314     _portrait_connection = _portraitButton.signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
315     _changedw_connection = _dimensionWidth.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
316     _changedh_connection = _dimensionHeight.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
317     _fitPageButton.signal_clicked().connect(sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
319     show_all_children();
323 /**
324  * Set document dimensions (if not called by Doc prop's update()) and
325  * set the PageSizer's widgets and text entries accordingly. If
326  * 'chageList' is true, then adjust the paperSizeList to show the closest
327  * standard page size.
328  *
329  * \param w, h given in px
330  * \param changeList whether to modify the paper size list
331  */
332 void
333 PageSizer::setDim (double w, double h, bool changeList)
335     static bool _called = false;
336     if (_called) {
337         return;
338     }
340     _called = true;
342     _paper_size_list_connection.block();
343     _landscape_connection.block();
344     _portrait_connection.block();
345     _changedw_connection.block();
346     _changedh_connection.block();
348     if ( w != h ) {
349         _landscapeButton.set_sensitive(true);
350         _portraitButton.set_sensitive (true);
351         _landscape = ( w > h );
352         _landscapeButton.set_active(_landscape ? true : false);
353         _portraitButton.set_active (_landscape ? false : true);
354     } else {
355         _landscapeButton.set_sensitive(false);
356         _portraitButton.set_sensitive (false);
357     }
359     if (SP_ACTIVE_DESKTOP && !_widgetRegistry->isUpdating()) {
360         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
361         double const old_height = sp_document_height(doc);
362         sp_document_set_width (doc, w, &_px_unit);
363         sp_document_set_height (doc, h, &_px_unit);
364         sp_document_set_landscape (doc, _landscape);
365         // The origin for the user is in the lower left corner; this point should remain stationary when
366         // changing the page size. The SVG's origin however is in the upper left corner, so we must compensate for this
367         Geom::Translate const vert_offset(Geom::Point(0, (old_height - h)));
368                 SP_GROUP(SP_ROOT(doc->root))->translateChildItems(vert_offset);
369         sp_document_done (doc, SP_VERB_NONE, _("Set page size"));
370     }
372     if (changeList)
373         {
374         Gtk::TreeModel::Row row = (*find_paper_size(w, h));
375         if (row)
376             _paperSizeListSelection->select(row);
377         }
379     Unit const& unit = _dimensionUnits.getUnit();
380     _dimensionWidth.setValue (w / unit.factor);
381     _dimensionHeight.setValue (h / unit.factor);
383     _paper_size_list_connection.unblock();
384     _landscape_connection.unblock();
385     _portrait_connection.unblock();
386     _changedw_connection.unblock();
387     _changedh_connection.unblock();
389     _called = false;
393 /**
394  * Returns an iterator pointing to a row in paperSizeListStore which
395  * contains a paper of the specified size (specified in px), or
396  * paperSizeListStore->children().end() if no such paper exists.
397  */
398 Gtk::ListStore::iterator
399 PageSizer::find_paper_size (double w, double h) const
401     double smaller = w;
402     double larger  = h;
403     if ( h < w ) {
404         smaller = h; larger = w;
405     }
407     g_return_val_if_fail(smaller <= larger, _paperSizeListStore->children().end());
409     std::map<Glib::ustring, PaperSize>::const_iterator iter;
410     for (iter = _paperSizeTable.begin() ;
411          iter != _paperSizeTable.end() ; iter++) {
412         PaperSize paper = iter->second;
413         SPUnit const &i_unit = sp_unit_get_by_id(paper.unit);
414         double smallX = sp_units_get_pixels(paper.smaller, i_unit);
415         double largeX = sp_units_get_pixels(paper.larger,  i_unit);
417         g_return_val_if_fail(smallX <= largeX, _paperSizeListStore->children().end());
419         if ((std::abs(smaller - smallX) <= 0.1) &&
420             (std::abs(larger  - largeX) <= 0.1)   ) {
421             Gtk::ListStore::iterator p;
422             // We need to search paperSizeListStore explicitly for the
423             // specified paper size because it is sorted in a different
424             // way than paperSizeTable (which is sorted alphabetically)
425             for (p = _paperSizeListStore->children().begin(); p != _paperSizeListStore->children().end(); p++) {
426                 if ((*p)[_paperSizeListColumns.nameColumn] == paper.name) {
427                     return p;
428                 }
429             }
430         }
431     }
432     return _paperSizeListStore->children().end();
437 /**
438  * Tell the desktop to change the page size
439  */
440 void
441 PageSizer::fire_fit_canvas_to_selection_or_drawing()
443     SPDesktop *dt = SP_ACTIVE_DESKTOP;
444     if (!dt) {
445         return;
446     }
447     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
448     if (verb) {
449         SPAction *action = verb->get_action(dt);
450         if (action)
451             sp_action_perform(action, NULL);
452     }
457 /**
458  * Paper Size list callback for when a user changes the selection
459  */
460 void
461 PageSizer::on_paper_size_list_changed()
463     //Glib::ustring name = _paperSizeList.get_active_text();
464     Gtk::TreeModel::iterator miter = _paperSizeListSelection->get_selected();
465     if(!miter)
466         {
467         //error?
468         return;
469         }
470     Gtk::TreeModel::Row row = *miter;
471     Glib::ustring name = row[_paperSizeListColumns.nameColumn];
472     std::map<Glib::ustring, PaperSize>::const_iterator piter =
473                     _paperSizeTable.find(name);
474     if (piter == _paperSizeTable.end()) {
475         g_warning("paper size '%s' not found in table", name.c_str());
476         return;
477     }
478     PaperSize paper = piter->second;
479     double w = paper.smaller;
480     double h = paper.larger;
482     if (std::find(lscape_papers.begin(), lscape_papers.end(), paper.name.c_str()) != lscape_papers.end()) {
483         // enforce landscape mode if this is desired for the given page format
484         _landscape = true;
485     } else {
486         // otherwise we keep the current mode
487         _landscape = _landscapeButton.get_active();
488     }
490     SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
491     sp_convert_distance (&w, &src_unit, &_px_unit);
492     sp_convert_distance (&h, &src_unit, &_px_unit);
494     if (_landscape)
495         setDim (h, w, false);
496     else
497         setDim (w, h, false);
502 /**
503  * Portrait button callback
504  */
505 void
506 PageSizer::on_portrait()
508     if (!_portraitButton.get_active())
509         return;
510     double w = _dimensionWidth.getValue ("px");
511     double h = _dimensionHeight.getValue ("px");
512     if (h < w) {
513         setDim (h, w);
514     }
518 /**
519  * Landscape button callback
520  */
521 void
522 PageSizer::on_landscape()
524     if (!_landscapeButton.get_active())
525         return;
526     double w = _dimensionWidth.getValue ("px");
527     double h = _dimensionHeight.getValue ("px");
528     if (w < h) {
529         setDim (h, w);
530     }
533 /**
534  * Callback for the dimension widgets
535  */
536 void
537 PageSizer::on_value_changed()
539     if (_widgetRegistry->isUpdating()) return;
541     setDim (_dimensionWidth.getValue("px"),
542             _dimensionHeight.getValue("px"));
546 } // namespace Widget
547 } // namespace UI
548 } // namespace Inkscape
550 /*
551   Local Variables:
552   mode:c++
553   c-file-style:"stroustrup"
554   c-file-offsets:((innamespace . 0)(inline-open . 0))
555   indent-tabs-mode:nil
556   fill-column:99
557   End:
558 */
559 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :