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 (_("P_age size:"), 1.0, 0.5));
194 label_size->set_use_underline();
195 hbox_size->pack_start (*label_size, false, false, 0);
196 _omenu_size = manage (new Gtk::OptionMenu);
197 label_size->set_mnemonic_widget (*_omenu_size);
198 hbox_size->pack_start (*_omenu_size, true, true, 0);
199 Gtk::Menu *menu_size = manage (new Gtk::Menu);
201 for (PaperSize const *paper = inkscape_papers; paper->name; paper++) {
202 SizeMenuItem *item = manage (new SizeMenuItem (paper, this));
203 menu_size->append (*item);
204 }
205 SizeMenuItem *item = manage (new SizeMenuItem (0, 0));
206 menu_size->prepend (*item);
207 _omenu_size->set_menu (*menu_size);
208 }
210 PageSizer::~PageSizer()
211 {
212 _portrait_connection.disconnect();
213 _landscape_connection.disconnect();
214 _changedw_connection.disconnect();
215 _changedh_connection.disconnect();
216 }
218 void
219 PageSizer::init (Registry& reg)
220 {
221 Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
222 pack_start (*hbox_ori, false, false, 0);
223 Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5));
224 hbox_ori->pack_start (*label_ori, false, false, 0);
225 _rb_land = manage (new Gtk::RadioButton (_("_Landscape"), true));
226 Gtk::RadioButton::Group group = _rb_land->get_group();
227 hbox_ori->pack_end (*_rb_land, false, false, 5);
228 _rb_port = manage (new Gtk::RadioButton (_("_Portrait"), true));
229 hbox_ori->pack_end (*_rb_port, false, false, 5);
230 _rb_port->set_group (group);
231 _rb_port->set_active (true);
233 /* Custom paper frame */
234 Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
235 pack_start (*frame, false, false, 0);
236 Gtk::Table *table = manage (new Gtk::Table (4, 2, false));
237 table->set_border_width (4);
238 table->set_row_spacings (4);
239 table->set_col_spacings (4);
240 frame->add (*table);
242 _wr = ®
244 _rum.init (_("U_nits:"), "units", *_wr);
245 _rusw.init (_("_Width:"), _("Width of paper"), "width", _rum, *_wr);
246 _rush.init (_("_Height:"), _("Height of paper"), "height", _rum, *_wr);
248 table->attach (*_rum._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
249 table->attach (*_rum._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
250 table->attach (*_rusw.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
251 table->attach (*_rush.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
253 _landscape_connection = _rb_land->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
254 _portrait_connection = _rb_port->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
255 _changedw_connection = _rusw.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
256 _changedh_connection = _rush.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
258 show_all_children();
259 }
261 /**
262 * \param w, h given in px
263 */
264 void
265 PageSizer::setDim (double w, double h, bool update)
266 {
267 _landscape = w>h;
268 _rb_land->set_active (_landscape ? true : false);
269 _rb_port->set_active (_landscape ? false : true);
270 _omenu_size->set_history (1 + find_paper_size (w, h));
272 Unit const& unit = _rum._sel->getUnit();
273 if (!update) _changedw_connection.block();
274 _rusw.setValue (w / unit.factor);
275 if (!update) _changedw_connection.unblock();
276 if (!update) _changedh_connection.block();
277 _rush.setValue (h / unit.factor);
278 if (!update) _changedh_connection.unblock();
279 }
281 void
282 PageSizer::setDoc (double w, double h)
283 {
284 setDim (w, h);
285 if (!SP_ACTIVE_DESKTOP || _wr->isUpdating())
286 return;
288 SPDocument *doc = SP_DT_DOCUMENT(SP_ACTIVE_DESKTOP);
289 sp_document_set_width (doc, _rusw.getSU()->getValue("px"), &_px_unit);
290 sp_document_set_height (doc, _rush.getSU()->getValue("px"), &_px_unit);
291 sp_document_done (doc);
292 }
294 /**
295 * Returns an index into inkscape_papers of a paper of the specified
296 * size (specified in px), or -1 if there's no such paper.
297 */
298 int
299 PageSizer::find_paper_size (double w, double h) const
300 {
301 double given[2];
302 if ( w < h ) {
303 given[0] = w; given[1] = h;
304 } else {
305 given[0] = h; given[1] = w;
306 }
307 g_return_val_if_fail(given[0] <= given[1], -1);
308 for (unsigned i = 0; i < G_N_ELEMENTS(inkscape_papers) - 1; ++i) {
309 SPUnit const &i_unit = sp_unit_get_by_id(inkscape_papers[i].unit);
310 double const i_sizes[2] = { sp_units_get_pixels(inkscape_papers[i].smaller, i_unit),
311 sp_units_get_pixels(inkscape_papers[i].larger, i_unit) };
312 g_return_val_if_fail(i_sizes[0] <= i_sizes[1], -1);
313 if ((std::abs(given[0] - i_sizes[0]) <= .1) &&
314 (std::abs(given[1] - i_sizes[1]) <= .1) )
315 {
316 return (int) i;
317 }
318 }
319 return -1;
320 }
322 void
323 PageSizer::on_portrait()
324 {
325 double w = _rusw.getSU()->getValue ("px");
326 double h = _rush.getSU()->getValue ("px");
327 if (h<w) setDoc (h, w);
328 }
330 void
331 PageSizer::on_landscape()
332 {
333 double w = _rusw.getSU()->getValue ("px");
334 double h = _rush.getSU()->getValue ("px");
335 if (w<h) setDoc (h, w);
336 }
338 void
339 PageSizer::on_value_changed()
340 {
341 if (_wr->isUpdating()) return;
343 setDoc (_rusw.getSU()->getValue("px"), _rush.getSU()->getValue("px"));
344 }
346 } // namespace Widget
347 } // namespace UI
348 } // namespace Inkscape
350 /*
351 Local Variables:
352 mode:c++
353 c-file-style:"stroustrup"
354 c-file-offsets:((innamespace . 0)(inline-open . 0))
355 indent-tabs-mode:nil
356 fill-column:99
357 End:
358 */
359 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :