Code

Add page sizes for various business card formats as per request in LP #172225
[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>
23 #include <cmath>
24 #include <gtkmm.h>
25 #include "ui/widget/button.h"
27 #include "ui/widget/scalar-unit.h"
29 #include "helper/units.h"
30 #include "inkscape.h"
31 #include "verbs.h"
32 #include "desktop-handles.h"
33 #include "document.h"
34 #include "desktop.h"
35 #include "page-sizer.h"
36 #include "helper/action.h"
38 using std::pair;
40 namespace Inkscape {
41 namespace UI {
42 namespace Widget {
44     /** \note
45      * The ISO page sizes in the table below differ from ghostscript's idea of page sizes (by
46      * less than 1pt).  Being off by <1pt should be OK for most purposes, but may cause fuzziness
47      * (antialiasing) problems when printing to 72dpi or 144dpi printers or bitmap files due to
48      * postscript's different coordinate system (y=0 meaning bottom of page in postscript and top
49      * of page in SVG).  I haven't looked into whether this does in fact cause fuzziness, I merely
50      * note the possibility.  Rounding done by extension/internal/ps.cpp (e.g. floor/ceil calls)
51      * will also affect whether fuzziness occurs.
52      *
53      * The remainder of this comment discusses the origin of the numbers used for ISO page sizes in
54      * this table and in ghostscript.
55      *
56      * The versions here, in mm, are the official sizes according to
57      * <a href="http://en.wikipedia.org/wiki/Paper_sizes">http://en.wikipedia.org/wiki/Paper_sizes</a>
58      * at 2005-01-25.  (The ISO entries in the below table
59      * were produced mechanically from the table on that page.)
60      *
61      * (The rule seems to be that A0, B0, ..., D0. sizes are rounded to the nearest number of mm
62      * from the "theoretical size" (i.e. 1000 * sqrt(2) or pow(2.0, .25) or the like), whereas
63      * going from e.g. A0 to A1 always take the floor of halving -- which by chance coincides
64      * exactly with flooring the "theoretical size" for n != 0 instead of the rounding to nearest
65      * done for n==0.)
66      *
67      * Ghostscript paper sizes are given in gs_statd.ps according to gs(1).  gs_statd.ps always
68      * uses an integer number of pt: sometimes gs_statd.ps rounds to nearest (e.g. a1), sometimes
69      * floors (e.g. a10), sometimes ceils (e.g. a8).
70      *
71      * I'm not sure how ghostscript's gs_statd.ps was calculated: it isn't just rounding the
72      * "theoretical size" of each page to pt (see a0), nor is it rounding the a0 size times an
73      * appropriate power of two (see a1).  Possibly it was prepared manually, with a human applying
74      * inconsistent rounding rules when converting from mm to pt.
75      */
76     /** \todo
77      * Should we include the JIS B series (used in Japan)
78      * (JIS B0 is sometimes called JB0, and similarly for JB1 etc)?
79      * Should we exclude B7--B10 and A7--10 to make the list smaller ?
80      * Should we include any of the ISO C, D and E series (see below) ?
81      */
83 struct PaperSizeRec {
84     char const * const name;  //name
85     double const smaller;     //lesser dimension
86     double const larger;      //greater dimension
87     SPUnitId const unit;      //units
88 };
90 static PaperSizeRec const inkscape_papers[] = {
91     { "A4",                210,  297, SP_UNIT_MM },
92     { "US Letter",         8.5,   11, SP_UNIT_IN },
93     { "US Legal",          8.5,   14, SP_UNIT_IN },
94     { "US Executive",     7.25, 10.5, SP_UNIT_IN },
95     { "A0",                841, 1189, SP_UNIT_MM },
96     { "A1",                594,  841, SP_UNIT_MM },
97     { "A2",                420,  594, SP_UNIT_MM },
98     { "A3",                297,  420, SP_UNIT_MM },
99     { "A5",                148,  210, SP_UNIT_MM },
100     { "A6",                105,  148, SP_UNIT_MM },
101     { "A7",                 74,  105, SP_UNIT_MM },
102     { "A8",                 52,   74, SP_UNIT_MM },
103     { "A9",                 37,   52, SP_UNIT_MM },
104     { "A10",                26,   37, SP_UNIT_MM },
105     { "B0",               1000, 1414, SP_UNIT_MM },
106     { "B1",                707, 1000, SP_UNIT_MM },
107     { "B2",                500,  707, SP_UNIT_MM },
108     { "B3",                353,  500, SP_UNIT_MM },
109     { "B4",                250,  353, SP_UNIT_MM },
110     { "B5",                176,  250, SP_UNIT_MM },
111     { "B6",                125,  176, SP_UNIT_MM },
112     { "B7",                 88,  125, SP_UNIT_MM },
113     { "B8",                 62,   88, SP_UNIT_MM },
114     { "B9",                 44,   62, SP_UNIT_MM },
115     { "B10",                31,   44, SP_UNIT_MM },
119 //#if 0
120          /*
121          Whether to include or exclude these depends on how
122          big we mind our page size menu
123          becoming.  C series is used for envelopes;
124          don't know what D and E series are used for.
125          */
127     { "C0",                917, 1297, SP_UNIT_MM },
128     { "C1",                648,  917, SP_UNIT_MM },
129     { "C2",                458,  648, SP_UNIT_MM },
130     { "C3",                324,  458, SP_UNIT_MM },
131     { "C4",                229,  324, SP_UNIT_MM },
132     { "C5",                162,  229, SP_UNIT_MM },
133     { "C6",                114,  162, SP_UNIT_MM },
134     { "C7",                 81,  114, SP_UNIT_MM },
135     { "C8",                 57,   81, SP_UNIT_MM },
136     { "C9",                 40,   57, SP_UNIT_MM },
137     { "C10",                28,   40, SP_UNIT_MM },
138     { "D1",                545,  771, SP_UNIT_MM },
139     { "D2",                385,  545, SP_UNIT_MM },
140     { "D3",                272,  385, SP_UNIT_MM },
141     { "D4",                192,  272, SP_UNIT_MM },
142     { "D5",                136,  192, SP_UNIT_MM },
143     { "D6",                 96,  136, SP_UNIT_MM },
144     { "D7",                 68,   96, SP_UNIT_MM },
145     { "E3",                400,  560, SP_UNIT_MM },
146     { "E4",                280,  400, SP_UNIT_MM },
147     { "E5",                200,  280, SP_UNIT_MM },
148     { "E6",                140,  200, SP_UNIT_MM },
149 //#endif
153     { "CSE",               462,  649, SP_UNIT_PT },
154     { "US #10 Envelope", 4.125,  9.5, SP_UNIT_IN },
155     // TODO: Select landscape by default.
156     /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
157        sizes. */
158     { "DL Envelope",       110,  220, SP_UNIT_MM },
159     // TODO: Select landscape by default.
160     { "Ledger/Tabloid",     11,   17, SP_UNIT_IN },
161     /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
162        means different sizes to different people: different people may expect the width to be
163        either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
164        restricting our interpretation to foolscap folio.  If you wish to introduce a folio-like
165        page size to the list, then please consider using a name more specific than just `Folio' or
166        `Foolscap Folio'. */
167     { "Banner 468x60",      60,  468, SP_UNIT_PX },
168     // TODO: Select landscape by default.
169     { "Icon 16x16",         16,   16, SP_UNIT_PX },
170     { "Icon 32x32",         32,   32, SP_UNIT_PX },
171     { "Icon 48x48",         48,   48, SP_UNIT_PX },
172     /* business cards; TODO: select landscape by default */
173     { "Business Card (ISO 7810 ID-1)",         53.98, 85.60, SP_UNIT_MM },
174     { "Business Card (US)",                     2,     3.5,  SP_UNIT_IN },
175     { "Business Card (Europe)",                55,    85,    SP_UNIT_MM },
176     { "Business Card (Australia/New Zealand)", 55,    90,    SP_UNIT_MM },
177     { NULL,                  0,    0, SP_UNIT_PX },
178 };
182 //########################################################################
183 //# P A G E    S I Z E R
184 //########################################################################
186 //The default unit for this widget and its calculations
187 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
190 /**
191  * Constructor
192  */
193 PageSizer::PageSizer(Registry & _wr)
194     : Gtk::VBox(false,4),
195       _dimensionUnits( _("U_nits:"), "units", _wr ),
196       _dimensionWidth( _("_Width:"), _("Width of paper"), "width", _dimensionUnits, _wr ),
197       _dimensionHeight( _("_Height:"), _("Height of paper"), "height", _dimensionUnits, _wr ),
198       _widgetRegistry(&_wr)
200     //# Set up the Paper Size combo box
201     _paperSizeListStore = Gtk::ListStore::create(_paperSizeListColumns);
202     _paperSizeList.set_model(_paperSizeListStore);
203     _paperSizeList.append_column(_("Name"),
204                                  _paperSizeListColumns.nameColumn);
205     _paperSizeList.append_column(_("Description"),
206                                  _paperSizeListColumns.descColumn);
207     _paperSizeList.set_headers_visible(false);
208     _paperSizeListSelection = _paperSizeList.get_selection();
209     _paper_size_list_connection =
210         _paperSizeListSelection->signal_changed().connect (
211             sigc::mem_fun (*this, &PageSizer::on_paper_size_list_changed));
212     _paperSizeListScroller.add(_paperSizeList);
213     _paperSizeListScroller.set_shadow_type(Gtk::SHADOW_IN);
214     _paperSizeListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
215     _paperSizeListScroller.set_size_request(-1, 90);
217     for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
218     {
219         Glib::ustring name = p->name;
220         char formatBuf[80];
221         snprintf(formatBuf, 79, "%0.1f x %0.1f", p->smaller, p->larger);
222         Glib::ustring desc = formatBuf;
223         if (p->unit == SP_UNIT_IN)
224             desc.append(" in");
225         else if (p->unit == SP_UNIT_MM)
226              desc.append(" mm");
227         else if (p->unit == SP_UNIT_PX)
228             desc.append(" px");
229         PaperSize paper(name, p->smaller, p->larger, p->unit);
230         _paperSizeTable[name] = paper;
231         Gtk::TreeModel::Row row = *(_paperSizeListStore->append());
232         row[_paperSizeListColumns.nameColumn] = name;
233         row[_paperSizeListColumns.descColumn] = desc;
234         }
235     //Gtk::TreeModel::iterator iter = _paperSizeListStore->children().begin();
236     //if (iter)
237     //    _paperSizeListSelection->select(iter);
240     pack_start (_paperSizeListBox, true, true, 0);
241     _paperSizeListLabel.set_label(_("P_age size:"));
242     _paperSizeListLabel.set_use_underline();
243     _paperSizeListBox.pack_start (_paperSizeListLabel, false, false, 0);
244     _paperSizeListLabel.set_mnemonic_widget (_paperSizeList);
245     _paperSizeListBox.pack_start (_paperSizeListScroller, true, true, 0);
247     //## Set up orientation radio buttons
248     pack_start (_orientationBox, false, false, 0);
249     _orientationLabel.set_label(_("Page orientation:"));
250     _orientationBox.pack_start(_orientationLabel, false, false, 0);
251     _landscapeButton.set_use_underline();
252     _landscapeButton.set_label(_("_Landscape"));
253     _landscapeButton.set_active(true);
254     Gtk::RadioButton::Group group = _landscapeButton.get_group();
255     _orientationBox.pack_end (_landscapeButton, false, false, 5);
256     _portraitButton.set_use_underline();
257     _portraitButton.set_label(_("_Portrait"));
258     _portraitButton.set_active(true);
259     _orientationBox.pack_end (_portraitButton, false, false, 5);
260     _portraitButton.set_group (group);
261     _portraitButton.set_active (true);
263     //## Set up custom size frame
264     _customFrame.set_label(_("Custom size"));
265     pack_start (_customFrame, false, false, 0);
266     _customTable.resize(2, 2);
267     _customTable.set_border_width (4);
268     _customTable.set_row_spacings (4);
269     _customTable.set_col_spacings (4);
270     _customTable.attach(_dimensionWidth, 0,1,0,1);
271     _customTable.attach(_dimensionUnits, 1,2,0,1);
272     _customTable.attach(_dimensionHeight, 0,1,1,2);
273     _customTable.attach(_fitPageButton,              1,2,1,2);
274     _customFrame.add(_customTable);
276     _fitPageButton.set_use_underline();
277     _fitPageButton.set_label(_("_Fit page to selection"));
278     _tips.set_tip(_fitPageButton, _("Resize the page to fit the current selection, or the entire drawing if there is no selection"));
282 /**
283  * Destructor
284  */
285 PageSizer::~PageSizer()
291 /**
292  * Initialize or reset this widget
293  */
294 void
295 PageSizer::init ()
297     _landscape_connection = _landscapeButton.signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
298     _portrait_connection = _portraitButton.signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
299     _changedw_connection = _dimensionWidth.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
300     _changedh_connection = _dimensionHeight.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
301     _fitPageButton.signal_clicked().connect(sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
303     show_all_children();
307 /**
308  * Set document dimensions (if not called by Doc prop's update()) and
309  * set the PageSizer's widgets and text entries accordingly. If
310  * 'chageList' is true, then adjust the paperSizeList to show the closest
311  * standard page size.
312  *
313  * \param w, h given in px
314  * \param changeList whether to modify the paper size list
315  */
316 void
317 PageSizer::setDim (double w, double h, bool changeList)
319     static bool _called = false;
320     if (_called) {
321         return;
322     }
324     _called = true;
326     _paper_size_list_connection.block();
327     _landscape_connection.block();
328     _portrait_connection.block();
329     _changedw_connection.block();
330     _changedh_connection.block();
332     if (SP_ACTIVE_DESKTOP && !_widgetRegistry->isUpdating()) {
333         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
334         sp_document_set_width (doc, w, &_px_unit);
335         sp_document_set_height (doc, h, &_px_unit);
336         sp_document_done (doc, SP_VERB_NONE, _("Set page size"));
337     }
339     _landscape = ( w > h );
340     _landscapeButton.set_active(_landscape ? true : false);
341     _portraitButton.set_active (_landscape ? false : true);
343     if (changeList)
344         {
345         Gtk::TreeModel::Row row = (*find_paper_size(w, h));
346         if (row)
347             _paperSizeListSelection->select(row);
348         }
350     Unit const& unit = _dimensionUnits.getUnit();
351     _dimensionWidth.setValue (w / unit.factor);
352     _dimensionHeight.setValue (h / unit.factor);
354     _paper_size_list_connection.unblock();
355     _landscape_connection.unblock();
356     _portrait_connection.unblock();
357     _changedw_connection.unblock();
358     _changedh_connection.unblock();
360     _called = false;
364 /**
365  * Returns an iterator pointing to a row in paperSizeListStore which
366  * contains a paper of the specified size (specified in px), or
367  * paperSizeListStore->children().end() if no such paper exists.
368  */
369 Gtk::ListStore::iterator
370 PageSizer::find_paper_size (double w, double h) const
372     double smaller = w;
373     double larger  = h;
374     if ( h < w ) {
375         smaller = h; larger = w;
376     }
378     g_return_val_if_fail(smaller <= larger, _paperSizeListStore->children().end());
380     std::map<Glib::ustring, PaperSize>::const_iterator iter;
381     for (iter = _paperSizeTable.begin() ;
382          iter != _paperSizeTable.end() ; iter++) {
383         PaperSize paper = iter->second;
384         SPUnit const &i_unit = sp_unit_get_by_id(paper.unit);
385         double smallX = sp_units_get_pixels(paper.smaller, i_unit);
386         double largeX = sp_units_get_pixels(paper.larger,  i_unit);
388         g_return_val_if_fail(smallX <= largeX, _paperSizeListStore->children().end());
390         if ((std::abs(smaller - smallX) <= 0.1) &&
391             (std::abs(larger  - largeX) <= 0.1)   ) {
392             Gtk::ListStore::iterator p;
393             // We need to search paperSizeListStore explicitly for the
394             // specified paper size because it is sorted in a different
395             // way than paperSizeTable (which is sorted alphabetically)
396             for (p = _paperSizeListStore->children().begin(); p != _paperSizeListStore->children().end(); p++) {
397                 if ((*p)[_paperSizeListColumns.nameColumn] == paper.name) {
398                     return p;
399                 }
400             }
401         }
402     }
403     return _paperSizeListStore->children().end();
408 /**
409  * Tell the desktop to change the page size
410  */
411 void
412 PageSizer::fire_fit_canvas_to_selection_or_drawing()
414     SPDesktop *dt = SP_ACTIVE_DESKTOP;
415     if (!dt) {
416         return;
417     }
418     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
419     if (verb) {
420         SPAction *action = verb->get_action(dt);
421         if (action)
422             sp_action_perform(action, NULL);
423     }
428 /**
429  * Paper Size list callback for when a user changes the selection
430  */
431 void
432 PageSizer::on_paper_size_list_changed()
434     //Glib::ustring name = _paperSizeList.get_active_text();
435     Gtk::TreeModel::iterator miter = _paperSizeListSelection->get_selected();
436     if(!miter)
437         {
438         //error?
439         return;
440         }
441     Gtk::TreeModel::Row row = *miter;
442     Glib::ustring name = row[_paperSizeListColumns.nameColumn];
443     std::map<Glib::ustring, PaperSize>::const_iterator piter =
444                     _paperSizeTable.find(name);
445     if (piter == _paperSizeTable.end()) {
446         g_warning("paper size '%s' not found in table", name.c_str());
447         return;
448     }
449     PaperSize paper = piter->second;
450     double w = paper.smaller;
451     double h = paper.larger;
452     SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
453     sp_convert_distance (&w, &src_unit, &_px_unit);
454     sp_convert_distance (&h, &src_unit, &_px_unit);
456     if (_landscape)
457         setDim (h, w, false);
458     else
459         setDim (w, h, false);
464 /**
465  * Portrait button callback
466  */
467 void
468 PageSizer::on_portrait()
470     if (!_portraitButton.get_active())
471         return;
472     double w = _dimensionWidth.getValue ("px");
473     double h = _dimensionHeight.getValue ("px");
474     if (h < w) {
475         setDim (h, w);
476     }
480 /**
481  * Landscape button callback
482  */
483 void
484 PageSizer::on_landscape()
486     if (!_landscapeButton.get_active())
487         return;
488     double w = _dimensionWidth.getValue ("px");
489     double h = _dimensionHeight.getValue ("px");
490     if (w < h) {
491         setDim (h, w);
492     }
495 /**
496  * Callback for the dimension widgets
497  */
498 void
499 PageSizer::on_value_changed()
501     if (_widgetRegistry->isUpdating()) return;
503     setDim (_dimensionWidth.getValue("px"),
504             _dimensionHeight.getValue("px"));
508 } // namespace Widget
509 } // namespace UI
510 } // namespace Inkscape
512 /*
513   Local Variables:
514   mode:c++
515   c-file-style:"stroustrup"
516   c-file-offsets:((innamespace . 0)(inline-open . 0))
517   indent-tabs-mode:nil
518   fill-column:99
519   End:
520 */
521 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :