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 *
11 * Copyright (C) 2000 - 2005 Authors
12 *
13 * Released under GNU GPL. Read the file 'COPYING' for more information
14 */
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
20 #include <cmath>
21 #include <gtkmm/optionmenu.h>
22 #include <gtkmm/frame.h>
23 #include <gtkmm/table.h>
25 #include "ui/widget/scalar-unit.h"
27 #include "helper/units.h"
28 #include "inkscape.h"
29 #include "desktop-handles.h"
30 #include "document.h"
31 #include "page-sizer.h"
33 using std::pair;
35 namespace Inkscape {
36 namespace UI {
37 namespace Widget {
39 struct PaperSize {
40 char const * const name;
41 double const smaller;
42 double const larger;
43 SPUnitId const unit;
44 };
46 /** \note
47 * The ISO page sizes in the table below differ from ghostscript's idea of page sizes (by
48 * less than 1pt). Being off by <1pt should be OK for most purposes, but may cause fuzziness
49 * (antialiasing) problems when printing to 72dpi or 144dpi printers or bitmap files due to
50 * postscript's different coordinate system (y=0 meaning bottom of page in postscript and top
51 * of page in SVG). I haven't looked into whether this does in fact cause fuzziness, I merely
52 * note the possibility. Rounding done by extension/internal/ps.cpp (e.g. floor/ceil calls)
53 * will also affect whether fuzziness occurs.
54 *
55 * The remainder of this comment discusses the origin of the numbers used for ISO page sizes in
56 * this table and in ghostscript.
57 *
58 * The versions here, in mm, are the official sizes according to
59 * <a href="http://en.wikipedia.org/wiki/Paper_sizes">http://en.wikipedia.org/wiki/Paper_sizes</a>
60 * at 2005-01-25. (The ISO entries in the below table
61 * were produced mechanically from the table on that page.)
62 *
63 * (The rule seems to be that A0, B0, ..., D0. sizes are rounded to the nearest number of mm
64 * from the "theoretical size" (i.e. 1000 * sqrt(2) or pow(2.0, .25) or the like), whereas
65 * going from e.g. A0 to A1 always take the floor of halving -- which by chance coincides
66 * exactly with flooring the "theoretical size" for n != 0 instead of the rounding to nearest
67 * done for n==0.)
68 *
69 * Ghostscript paper sizes are given in gs_statd.ps according to gs(1). gs_statd.ps always
70 * uses an integer number of pt: sometimes gs_statd.ps rounds to nearest (e.g. a1), sometimes
71 * floors (e.g. a10), sometimes ceils (e.g. a8).
72 *
73 * I'm not sure how ghostscript's gs_statd.ps was calculated: it isn't just rounding the
74 * "theoretical size" of each page to pt (see a0), nor is it rounding the a0 size times an
75 * appropriate power of two (see a1). Possibly it was prepared manually, with a human applying
76 * inconsistent rounding rules when converting from mm to pt.
77 */
78 /** \todo
79 * Should we include the JIS B series (used in Japan)
80 * (JIS B0 is sometimes called JB0, and similarly for JB1 etc)?
81 * Should we exclude B7--B10 and A7--10 to make the list smaller ?
82 * Should we include any of the ISO C, D and E series (see below) ?
83 */
85 static PaperSize const inkscape_papers[] = {
86 { "A4", 210, 297, SP_UNIT_MM },
87 { "US Letter", 8.5, 11, SP_UNIT_IN },
88 { "US Legal", 8.5, 14, SP_UNIT_IN },
89 { "US Executive", 7.25, 10.5, SP_UNIT_IN },
90 { "A0", 841, 1189, SP_UNIT_MM },
91 { "A1", 594, 841, SP_UNIT_MM },
92 { "A2", 420, 594, SP_UNIT_MM },
93 { "A3", 297, 420, SP_UNIT_MM },
94 { "A5", 148, 210, SP_UNIT_MM },
95 { "A6", 105, 148, SP_UNIT_MM },
96 { "A7", 74, 105, SP_UNIT_MM },
97 { "A8", 52, 74, SP_UNIT_MM },
98 { "A9", 37, 52, SP_UNIT_MM },
99 { "A10", 26, 37, SP_UNIT_MM },
100 { "B0", 1000, 1414, SP_UNIT_MM },
101 { "B1", 707, 1000, SP_UNIT_MM },
102 { "B2", 500, 707, SP_UNIT_MM },
103 { "B3", 353, 500, SP_UNIT_MM },
104 { "B4", 250, 353, SP_UNIT_MM },
105 { "B5", 176, 250, SP_UNIT_MM },
106 { "B6", 125, 176, SP_UNIT_MM },
107 { "B7", 88, 125, SP_UNIT_MM },
108 { "B8", 62, 88, SP_UNIT_MM },
109 { "B9", 44, 62, SP_UNIT_MM },
110 { "B10", 31, 44, SP_UNIT_MM },
112 #if 0 /* Whether to include or exclude these depends on how big we mind our page size menu
113 becoming. C series is used for envelopes; don't know what D and E series are used for. */
114 { "C0", 917, 1297, SP_UNIT_MM },
115 { "C1", 648, 917, SP_UNIT_MM },
116 { "C2", 458, 648, SP_UNIT_MM },
117 { "C3", 324, 458, SP_UNIT_MM },
118 { "C4", 229, 324, SP_UNIT_MM },
119 { "C5", 162, 229, SP_UNIT_MM },
120 { "C6", 114, 162, SP_UNIT_MM },
121 { "C7", 81, 114, SP_UNIT_MM },
122 { "C8", 57, 81, SP_UNIT_MM },
123 { "C9", 40, 57, SP_UNIT_MM },
124 { "C10", 28, 40, SP_UNIT_MM },
125 { "D1", 545, 771, SP_UNIT_MM },
126 { "D2", 385, 545, SP_UNIT_MM },
127 { "D3", 272, 385, SP_UNIT_MM },
128 { "D4", 192, 272, SP_UNIT_MM },
129 { "D5", 136, 192, SP_UNIT_MM },
130 { "D6", 96, 136, SP_UNIT_MM },
131 { "D7", 68, 96, SP_UNIT_MM },
132 { "E3", 400, 560, SP_UNIT_MM },
133 { "E4", 280, 400, SP_UNIT_MM },
134 { "E5", 200, 280, SP_UNIT_MM },
135 { "E6", 140, 200, SP_UNIT_MM },
136 #endif
138 { "CSE", 462, 649, SP_UNIT_PT },
139 { "US #10 Envelope", 4.125, 9.5, SP_UNIT_IN }, // TODO: Select landscape by default.
140 /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
141 sizes. */
142 { "DL Envelope", 110, 220, SP_UNIT_MM }, // TODO: Select landscape by default.
143 { "Ledger/Tabloid", 11, 17, SP_UNIT_IN },
144 /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
145 means different sizes to different people: different people may expect the width to be
146 either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
147 restricting our interpretation to foolscap folio. If you wish to introduce a folio-like
148 page size to the list, then please consider using a name more specific than just `Folio' or
149 `Foolscap Folio'. */
150 { "Banner 468x60", 60, 468, SP_UNIT_PX }, // TODO: Select landscape by default.
151 { "Icon 16x16", 16, 16, SP_UNIT_PX },
152 { "Icon 32x32", 32, 32, SP_UNIT_PX },
153 { NULL, 0, 0, SP_UNIT_PX },
154 };
156 //===================================================
157 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
159 class SizeMenuItem : public Gtk::MenuItem {
160 public:
161 SizeMenuItem (PaperSize const * paper, PageSizer * widget)
162 : Gtk::MenuItem (paper ? paper->name : _("Custom")),
163 _paper(paper), _parent(widget) {}
164 protected:
165 PaperSize const * _paper;
166 PageSizer *_parent;
167 void on_activate();
168 };
170 void
171 SizeMenuItem::on_activate()
172 {
173 if (_parent == 0) // handle Custom entry
174 return;
176 double w = _paper->smaller, h = _paper->larger;
177 SPUnit const &src_unit = sp_unit_get_by_id (_paper->unit);
178 sp_convert_distance (&w, &src_unit, &_px_unit);
179 sp_convert_distance (&h, &src_unit, &_px_unit);
180 if (_parent->_landscape)
181 _parent->setDim (h, w, true);
182 else
183 _parent->setDim (w, h, true);
184 }
186 //---------------------------------------------------
188 PageSizer::PageSizer()
189 : Gtk::VBox(false,4)
190 {
191 Gtk::HBox *hbox_size = manage (new Gtk::HBox (false, 4));
192 pack_start (*hbox_size, false, false, 0);
193 Gtk::Label *label_size = manage (new Gtk::Label (_("Page size:"), 1.0, 0.5));
194 hbox_size->pack_start (*label_size, false, false, 0);
195 _omenu_size = manage (new Gtk::OptionMenu);
196 hbox_size->pack_start (*_omenu_size, true, true, 0);
197 Gtk::Menu *menu_size = manage (new Gtk::Menu);
199 for (PaperSize const *paper = inkscape_papers; paper->name; paper++) {
200 SizeMenuItem *item = manage (new SizeMenuItem (paper, this));
201 menu_size->append (*item);
202 }
203 SizeMenuItem *item = manage (new SizeMenuItem (0, 0));
204 menu_size->prepend (*item);
205 _omenu_size->set_menu (*menu_size);
206 }
208 PageSizer::~PageSizer()
209 {
210 _portrait_connection.disconnect();
211 _landscape_connection.disconnect();
212 _changedw_connection.disconnect();
213 _changedh_connection.disconnect();
214 }
216 void
217 PageSizer::init (Registry& reg)
218 {
219 Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
220 pack_start (*hbox_ori, false, false, 0);
221 Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5));
222 hbox_ori->pack_start (*label_ori, false, false, 0);
223 _rb_land = manage (new Gtk::RadioButton (_("Landscape")));
224 Gtk::RadioButton::Group group = _rb_land->get_group();
225 hbox_ori->pack_end (*_rb_land, false, false, 5);
226 _rb_port = manage (new Gtk::RadioButton (_("Portrait")));
227 hbox_ori->pack_end (*_rb_port, false, false, 5);
228 _rb_port->set_group (group);
229 _rb_port->set_active (true);
231 /* Custom paper frame */
232 Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
233 pack_start (*frame, false, false, 0);
234 Gtk::Table *table = manage (new Gtk::Table (4, 2, false));
235 table->set_border_width (4);
236 table->set_row_spacings (4);
237 table->set_col_spacings (4);
238 frame->add (*table);
240 _wr = ®
242 _rum.init (_("Units:"), "units", *_wr);
243 _rusw.init (_("Width:"), _("Width of paper"), "width", _rum, *_wr);
244 _rush.init (_("Height:"), _("Height of paper"), "height", _rum, *_wr);
246 table->attach (*_rum._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
247 table->attach (*_rum._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
248 table->attach (*_rusw.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
249 table->attach (*_rush.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
251 _landscape_connection = _rb_land->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
252 _portrait_connection = _rb_port->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
253 _changedw_connection = _rusw.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
254 _changedh_connection = _rush.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
256 show_all_children();
257 }
259 /**
260 * \param w, h given in px
261 */
262 void
263 PageSizer::setDim (double w, double h, bool update)
264 {
265 _landscape = w>h;
266 _rb_land->set_active (_landscape ? true : false);
267 _rb_port->set_active (_landscape ? false : true);
268 _omenu_size->set_history (1 + find_paper_size (w, h));
270 Unit const& unit = _rum._sel->getUnit();
271 if (!update) _changedw_connection.block();
272 _rusw.setValue (w / unit.factor);
273 if (!update) _changedw_connection.unblock();
274 if (!update) _changedh_connection.block();
275 _rush.setValue (h / unit.factor);
276 if (!update) _changedh_connection.unblock();
277 }
279 void
280 PageSizer::setDoc (double w, double h)
281 {
282 setDim (w, h);
283 if (!SP_ACTIVE_DESKTOP || _wr->isUpdating())
284 return;
286 SPDocument *doc = SP_DT_DOCUMENT(SP_ACTIVE_DESKTOP);
287 sp_document_set_width (doc, _rusw.getSU()->getValue("px"), &_px_unit);
288 sp_document_set_height (doc, _rush.getSU()->getValue("px"), &_px_unit);
289 sp_document_done (doc);
290 }
292 /**
293 * Returns an index into inkscape_papers of a paper of the specified
294 * size (specified in px), or -1 if there's no such paper.
295 */
296 int
297 PageSizer::find_paper_size (double w, double h) const
298 {
299 double given[2];
300 if ( w < h ) {
301 given[0] = w; given[1] = h;
302 } else {
303 given[0] = h; given[1] = w;
304 }
305 g_return_val_if_fail(given[0] <= given[1], -1);
306 for (unsigned i = 0; i < G_N_ELEMENTS(inkscape_papers) - 1; ++i) {
307 SPUnit const &i_unit = sp_unit_get_by_id(inkscape_papers[i].unit);
308 double const i_sizes[2] = { sp_units_get_pixels(inkscape_papers[i].smaller, i_unit),
309 sp_units_get_pixels(inkscape_papers[i].larger, i_unit) };
310 g_return_val_if_fail(i_sizes[0] <= i_sizes[1], -1);
311 if ((std::abs(given[0] - i_sizes[0]) <= .1) &&
312 (std::abs(given[1] - i_sizes[1]) <= .1) )
313 {
314 return (int) i;
315 }
316 }
317 return -1;
318 }
320 void
321 PageSizer::on_portrait()
322 {
323 double w = _rusw.getSU()->getValue ("px");
324 double h = _rush.getSU()->getValue ("px");
325 if (h<w) setDoc (h, w);
326 }
328 void
329 PageSizer::on_landscape()
330 {
331 double w = _rusw.getSU()->getValue ("px");
332 double h = _rush.getSU()->getValue ("px");
333 if (w<h) setDoc (h, w);
334 }
336 void
337 PageSizer::on_value_changed()
338 {
339 if (_wr->isUpdating()) return;
341 setDoc (_rusw.getSU()->getValue("px"), _rush.getSU()->getValue("px"));
342 }
344 } // namespace Widget
345 } // namespace UI
346 } // namespace Inkscape
348 /*
349 Local Variables:
350 mode:c++
351 c-file-style:"stroustrup"
352 c-file-offsets:((innamespace . 0)(inline-open . 0))
353 indent-tabs-mode:nil
354 fill-column:99
355 End:
356 */
357 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :