5882d465118bd9e0bc656a2b881a2c24fca5ef83
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.h>
22 #include <gtkmm/optionmenu.h>
23 #include <gtkmm/frame.h>
24 #include <gtkmm/table.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 struct PaperSize {
45 char const * const name;
46 double const smaller;
47 double const larger;
48 SPUnitId const unit;
49 };
51 /** \note
52 * The ISO page sizes in the table below differ from ghostscript's idea of page sizes (by
53 * less than 1pt). Being off by <1pt should be OK for most purposes, but may cause fuzziness
54 * (antialiasing) problems when printing to 72dpi or 144dpi printers or bitmap files due to
55 * postscript's different coordinate system (y=0 meaning bottom of page in postscript and top
56 * of page in SVG). I haven't looked into whether this does in fact cause fuzziness, I merely
57 * note the possibility. Rounding done by extension/internal/ps.cpp (e.g. floor/ceil calls)
58 * will also affect whether fuzziness occurs.
59 *
60 * The remainder of this comment discusses the origin of the numbers used for ISO page sizes in
61 * this table and in ghostscript.
62 *
63 * The versions here, in mm, are the official sizes according to
64 * <a href="http://en.wikipedia.org/wiki/Paper_sizes">http://en.wikipedia.org/wiki/Paper_sizes</a>
65 * at 2005-01-25. (The ISO entries in the below table
66 * were produced mechanically from the table on that page.)
67 *
68 * (The rule seems to be that A0, B0, ..., D0. sizes are rounded to the nearest number of mm
69 * from the "theoretical size" (i.e. 1000 * sqrt(2) or pow(2.0, .25) or the like), whereas
70 * going from e.g. A0 to A1 always take the floor of halving -- which by chance coincides
71 * exactly with flooring the "theoretical size" for n != 0 instead of the rounding to nearest
72 * done for n==0.)
73 *
74 * Ghostscript paper sizes are given in gs_statd.ps according to gs(1). gs_statd.ps always
75 * uses an integer number of pt: sometimes gs_statd.ps rounds to nearest (e.g. a1), sometimes
76 * floors (e.g. a10), sometimes ceils (e.g. a8).
77 *
78 * I'm not sure how ghostscript's gs_statd.ps was calculated: it isn't just rounding the
79 * "theoretical size" of each page to pt (see a0), nor is it rounding the a0 size times an
80 * appropriate power of two (see a1). Possibly it was prepared manually, with a human applying
81 * inconsistent rounding rules when converting from mm to pt.
82 */
83 /** \todo
84 * Should we include the JIS B series (used in Japan)
85 * (JIS B0 is sometimes called JB0, and similarly for JB1 etc)?
86 * Should we exclude B7--B10 and A7--10 to make the list smaller ?
87 * Should we include any of the ISO C, D and E series (see below) ?
88 */
90 static PaperSize 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 },
117 #if 0 /* Whether to include or exclude these depends on how big we mind our page size menu
118 becoming. C series is used for envelopes; don't know what D and E series are used for. */
119 { "C0", 917, 1297, SP_UNIT_MM },
120 { "C1", 648, 917, SP_UNIT_MM },
121 { "C2", 458, 648, SP_UNIT_MM },
122 { "C3", 324, 458, SP_UNIT_MM },
123 { "C4", 229, 324, SP_UNIT_MM },
124 { "C5", 162, 229, SP_UNIT_MM },
125 { "C6", 114, 162, SP_UNIT_MM },
126 { "C7", 81, 114, SP_UNIT_MM },
127 { "C8", 57, 81, SP_UNIT_MM },
128 { "C9", 40, 57, SP_UNIT_MM },
129 { "C10", 28, 40, SP_UNIT_MM },
130 { "D1", 545, 771, SP_UNIT_MM },
131 { "D2", 385, 545, SP_UNIT_MM },
132 { "D3", 272, 385, SP_UNIT_MM },
133 { "D4", 192, 272, SP_UNIT_MM },
134 { "D5", 136, 192, SP_UNIT_MM },
135 { "D6", 96, 136, SP_UNIT_MM },
136 { "D7", 68, 96, SP_UNIT_MM },
137 { "E3", 400, 560, SP_UNIT_MM },
138 { "E4", 280, 400, SP_UNIT_MM },
139 { "E5", 200, 280, SP_UNIT_MM },
140 { "E6", 140, 200, SP_UNIT_MM },
141 #endif
143 { "CSE", 462, 649, SP_UNIT_PT },
144 { "US #10 Envelope", 4.125, 9.5, SP_UNIT_IN }, // TODO: Select landscape by default.
145 /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
146 sizes. */
147 { "DL Envelope", 110, 220, SP_UNIT_MM }, // TODO: Select landscape by default.
148 { "Ledger/Tabloid", 11, 17, SP_UNIT_IN },
149 /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
150 means different sizes to different people: different people may expect the width to be
151 either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
152 restricting our interpretation to foolscap folio. If you wish to introduce a folio-like
153 page size to the list, then please consider using a name more specific than just `Folio' or
154 `Foolscap Folio'. */
155 { "Banner 468x60", 60, 468, SP_UNIT_PX }, // TODO: Select landscape by default.
156 { "Icon 16x16", 16, 16, SP_UNIT_PX },
157 { "Icon 32x32", 32, 32, SP_UNIT_PX },
158 { NULL, 0, 0, SP_UNIT_PX },
159 };
161 //===================================================
162 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
164 class SizeMenuItem : public Gtk::MenuItem {
165 public:
166 SizeMenuItem (PaperSize const * paper, PageSizer * widget)
167 : Gtk::MenuItem (paper ? paper->name : _("Custom")),
168 _paper(paper), _parent(widget) {}
169 protected:
170 PaperSize const * _paper;
171 PageSizer *_parent;
172 void on_activate();
173 };
175 void
176 SizeMenuItem::on_activate()
177 {
178 if (_parent == 0) // handle Custom entry
179 return;
181 double w = _paper->smaller, h = _paper->larger;
182 SPUnit const &src_unit = sp_unit_get_by_id (_paper->unit);
183 sp_convert_distance (&w, &src_unit, &_px_unit);
184 sp_convert_distance (&h, &src_unit, &_px_unit);
185 if (_parent->_landscape)
186 _parent->setDim (h, w);
187 else
188 _parent->setDim (w, h);
189 }
191 //---------------------------------------------------
193 PageSizer::PageSizer()
194 : Gtk::VBox(false,4)
195 {
196 Gtk::HBox *hbox_size = manage (new Gtk::HBox (false, 4));
197 pack_start (*hbox_size, false, false, 0);
198 Gtk::Label *label_size = manage (new Gtk::Label (_("P_age size:"), 1.0, 0.5));
199 label_size->set_use_underline();
200 hbox_size->pack_start (*label_size, false, false, 0);
201 _omenu_size = manage (new Gtk::OptionMenu);
202 label_size->set_mnemonic_widget (*_omenu_size);
203 hbox_size->pack_start (*_omenu_size, true, true, 0);
204 Gtk::Menu *menu_size = manage (new Gtk::Menu);
206 for (PaperSize const *paper = inkscape_papers; paper->name; paper++) {
207 SizeMenuItem *item = manage (new SizeMenuItem (paper, this));
208 menu_size->append (*item);
209 }
210 SizeMenuItem *item = manage (new SizeMenuItem (0, 0));
211 menu_size->prepend (*item);
212 _omenu_size->set_menu (*menu_size);
213 }
215 PageSizer::~PageSizer()
216 {
217 _portrait_connection.disconnect();
218 _landscape_connection.disconnect();
219 _changedw_connection.disconnect();
220 _changedh_connection.disconnect();
221 }
223 void
224 PageSizer::init (Registry& reg)
225 {
226 Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
227 pack_start (*hbox_ori, false, false, 0);
228 Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5));
229 hbox_ori->pack_start (*label_ori, false, false, 0);
230 _rb_land = manage (new Gtk::RadioButton (_("_Landscape"), true));
231 Gtk::RadioButton::Group group = _rb_land->get_group();
232 hbox_ori->pack_end (*_rb_land, false, false, 5);
233 _rb_port = manage (new Gtk::RadioButton (_("_Portrait"), true));
234 hbox_ori->pack_end (*_rb_port, false, false, 5);
235 _rb_port->set_group (group);
236 _rb_port->set_active (true);
238 /* Custom paper frame */
239 Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
240 pack_start (*frame, false, false, 0);
241 Gtk::Table *table = manage (new Gtk::Table (5, 2, false));
242 table->set_border_width (4);
243 table->set_row_spacings (4);
244 table->set_col_spacings (4);
246 Inkscape::UI::Widget::Button* fit_canv = manage(new Inkscape::UI::Widget::Button(_("_Fit page to selection"),
247 _("Resize the page to fit the current selection, or the entire drawing if there is no selection")));
248 // prevent fit_canv from expanding
249 Gtk::Alignment *fit_canv_cont = manage(new Gtk::Alignment(1.0,0.5,0.0,0.0));
250 fit_canv_cont->add(*fit_canv);
252 frame->add (*table);
254 _wr = ®
256 _rum.init (_("U_nits:"), "units", *_wr);
257 _rusw.init (_("_Width:"), _("Width of paper"), "width", _rum, *_wr);
258 _rush.init (_("_Height:"), _("Height of paper"), "height", _rum, *_wr);
260 table->attach (*_rum._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
261 table->attach (*_rum._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
262 table->attach (*_rusw.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
263 table->attach (*_rush.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
264 table->attach (*fit_canv_cont, 0,2,3,4, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
266 _landscape_connection = _rb_land->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
267 _portrait_connection = _rb_port->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
268 _changedw_connection = _rusw.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
269 _changedh_connection = _rush.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
270 fit_canv->signal_clicked().connect(sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
272 show_all_children();
273 }
276 /**
277 * Set document dimensions (if not called by Doc prop's update()) and
278 * set the PageSizer's widgets and text entries accordingly. This is
279 * somewhat slow, is there something done too often invisibly?
280 *
281 * \param w, h given in px
282 */
283 void
284 PageSizer::setDim (double w, double h)
285 {
286 static bool _called = false;
287 if (_called) return;
289 _called = true;
291 _landscape_connection.block();
292 _portrait_connection.block();
293 _changedw_connection.block();
294 _changedh_connection.block();
296 if (SP_ACTIVE_DESKTOP && !_wr->isUpdating()) {
297 SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
298 sp_document_set_width (doc, w, &_px_unit);
299 sp_document_set_height (doc, h, &_px_unit);
300 sp_document_done (doc, SP_VERB_NONE,
301 /* TODO: annotate */ "page-sizer.cpp:301");
302 }
304 _landscape = w>h;
305 _rb_land->set_active (_landscape ? true : false);
306 _rb_port->set_active (_landscape ? false : true);
308 _omenu_size->set_history (1 + find_paper_size (w, h));
310 Unit const& unit = _rum._sel->getUnit();
311 _rusw.setValue (w / unit.factor);
312 _rush.setValue (h / unit.factor);
314 _landscape_connection.unblock();
315 _portrait_connection.unblock();
316 _changedw_connection.unblock();
317 _changedh_connection.unblock();
319 _called = false;
320 }
322 /**
323 * Returns an index into inkscape_papers of a paper of the specified
324 * size (specified in px), or -1 if there's no such paper.
325 */
326 int
327 PageSizer::find_paper_size (double w, double h) const
328 {
329 double given[2];
330 if ( w < h ) {
331 given[0] = w; given[1] = h;
332 } else {
333 given[0] = h; given[1] = w;
334 }
335 g_return_val_if_fail(given[0] <= given[1], -1);
336 for (unsigned i = 0; i < G_N_ELEMENTS(inkscape_papers) - 1; ++i) {
337 SPUnit const &i_unit = sp_unit_get_by_id(inkscape_papers[i].unit);
338 double const i_sizes[2] = { sp_units_get_pixels(inkscape_papers[i].smaller, i_unit),
339 sp_units_get_pixels(inkscape_papers[i].larger, i_unit) };
340 g_return_val_if_fail(i_sizes[0] <= i_sizes[1], -1);
341 if ((std::abs(given[0] - i_sizes[0]) <= .1) &&
342 (std::abs(given[1] - i_sizes[1]) <= .1) )
343 {
344 return (int) i;
345 }
346 }
347 return -1;
348 }
350 void
351 PageSizer::fire_fit_canvas_to_selection_or_drawing() {
352 SPDesktop *dt = SP_ACTIVE_DESKTOP;
353 if (!dt) return;
354 Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
355 if (verb) {
356 SPAction *action = verb->get_action(dt);
357 if (action) {
358 sp_action_perform(action, NULL);
359 }
360 }
361 }
363 void
364 PageSizer::on_portrait()
365 {
366 if (!_rb_port->get_active())
367 return;
368 double w = _rusw.getSU()->getValue ("px");
369 double h = _rush.getSU()->getValue ("px");
370 if (h<w) setDim (h, w);
371 }
373 void
374 PageSizer::on_landscape()
375 {
376 if (!_rb_land->get_active())
377 return;
378 double w = _rusw.getSU()->getValue ("px");
379 double h = _rush.getSU()->getValue ("px");
380 if (w<h) setDim (h, w);
381 }
383 void
384 PageSizer::on_value_changed()
385 {
386 if (_wr->isUpdating()) return;
388 setDim (_rusw.getSU()->getValue("px"), _rush.getSU()->getValue("px"));
389 }
391 } // namespace Widget
392 } // namespace UI
393 } // namespace Inkscape
395 /*
396 Local Variables:
397 mode:c++
398 c-file-style:"stroustrup"
399 c-file-offsets:((innamespace . 0)(inline-open . 0))
400 indent-tabs-mode:nil
401 fill-column:99
402 End:
403 */
404 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :