06b167e6581a9aca77eb619289653e32e8717141
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 /** \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;
85 double const smaller;
86 double const larger;
87 SPUnitId const unit;
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 },
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 //===================================================
164 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
167 /*
168 class SizeMenuItem : public Gtk::MenuItem {
169 public:
170 SizeMenuItem (PaperSizeRec const * paper, PageSizer * widget)
171 : Gtk::MenuItem (paper ? paper->name : _("Custom")),
172 _paper(paper),
173 _parent(widget)
174 {}
175 protected:
176 PaperSizeRec const * _paper;
177 PageSizer *_parent;
178 void on_activate();
179 };
181 void
182 SizeMenuItem::on_activate()
183 {
184 if (_parent == 0) // handle Custom entry
185 return;
187 double w = _paper->smaller, h = _paper->larger;
188 SPUnit const &src_unit = sp_unit_get_by_id (_paper->unit);
189 sp_convert_distance (&w, &src_unit, &_px_unit);
190 sp_convert_distance (&h, &src_unit, &_px_unit);
191 if (_parent->_landscape)
192 _parent->setDim (h, w);
193 else
194 _parent->setDim (w, h);
195 }
196 */
198 //---------------------------------------------------
204 PageSizer::PageSizer()
205 : Gtk::VBox(false,4)
206 {
207 Gtk::HBox *hbox_size = manage (new Gtk::HBox (false, 4));
208 pack_start (*hbox_size, false, false, 0);
209 Gtk::Label *label_size = manage (new Gtk::Label (_("P_age size:"), 1.0, 0.5));
210 label_size->set_use_underline();
211 hbox_size->pack_start (*label_size, false, false, 0);
212 label_size->set_mnemonic_widget (_paperSizeList);
213 hbox_size->pack_start (_paperSizeList, true, true, 0);
215 //# Set up the Paper Size combo box
217 for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
218 {
219 Glib::ustring name = p->name;
220 PaperSize paper(name, p->smaller, p->larger, p->unit);
221 paperSizeTable[name] = paper;
222 _paperSizeList.append_text(name);
223 }
225 }
227 PageSizer::~PageSizer()
228 {
229 _paper_size_list_connection.disconnect();
230 _portrait_connection.disconnect();
231 _landscape_connection.disconnect();
232 _changedw_connection.disconnect();
233 _changedh_connection.disconnect();
234 }
239 void
240 PageSizer::init (Registry& reg)
241 {
242 Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
243 pack_start (*hbox_ori, false, false, 0);
244 Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5));
245 hbox_ori->pack_start (*label_ori, false, false, 0);
247 _landscapeButton.set_label(_("_Landscape"));
248 _landscapeButton.set_active(true);
249 Gtk::RadioButton::Group group = _landscapeButton.get_group();
250 hbox_ori->pack_end (_landscapeButton, false, false, 5);
251 _portraitButton.set_label(_("_Portrait"));
252 _portraitButton.set_active(true);
253 hbox_ori->pack_end (_portraitButton, false, false, 5);
254 _portraitButton.set_group (group);
255 _portraitButton.set_active (true);
257 /* Custom paper frame */
258 Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
259 pack_start (*frame, false, false, 0);
260 Gtk::Table *table = manage (new Gtk::Table (5, 2, false));
261 table->set_border_width (4);
262 table->set_row_spacings (4);
263 table->set_col_spacings (4);
265 Inkscape::UI::Widget::Button* fit_canv =
266 manage(new Inkscape::UI::Widget::Button(_("_Fit page to selection"),
267 _("Resize the page to fit the current selection, or the entire drawing if there is no selection")));
269 // prevent fit_canv from expanding
270 Gtk::Alignment *fit_canv_cont = manage(new Gtk::Alignment(1.0,0.5,0.0,0.0));
271 fit_canv_cont->add(*fit_canv);
273 frame->add (*table);
275 _wr = ®
277 _rum.init (_("U_nits:"), "units", *_wr);
278 _rusw.init (_("_Width:"), _("Width of paper"), "width", _rum, *_wr);
279 _rush.init (_("_Height:"), _("Height of paper"), "height", _rum, *_wr);
281 table->attach (*_rum._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND,
282 (Gtk::AttachOptions)0,0,0);
283 table->attach (*_rum._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND,
284 (Gtk::AttachOptions)0,0,0);
285 table->attach (*_rusw.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND,
286 (Gtk::AttachOptions)0,0,0);
287 table->attach (*_rush.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND,
288 (Gtk::AttachOptions)0,0,0);
289 table->attach (*fit_canv_cont, 0,2,3,4, Gtk::FILL|Gtk::EXPAND,
290 (Gtk::AttachOptions)0,0,0);
292 _paper_size_list_connection = _paperSizeList.signal_changed().connect (
293 sigc::mem_fun (*this, &PageSizer::on_paper_size_list_changed));
295 _landscape_connection = _landscapeButton.signal_toggled().connect (
296 sigc::mem_fun (*this, &PageSizer::on_landscape));
297 _portrait_connection = _portraitButton.signal_toggled().connect (
298 sigc::mem_fun (*this, &PageSizer::on_portrait));
299 _changedw_connection = _rusw.getSU()->signal_value_changed().connect (
300 sigc::mem_fun (*this, &PageSizer::on_value_changed));
301 _changedh_connection = _rush.getSU()->signal_value_changed().connect (
302 sigc::mem_fun (*this, &PageSizer::on_value_changed));
303 fit_canv->signal_clicked().connect(
304 sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
306 show_all_children();
307 }
310 /**
311 * Set document dimensions (if not called by Doc prop's update()) and
312 * set the PageSizer's widgets and text entries accordingly. This is
313 * somewhat slow, is there something done too often invisibly?
314 *
315 * \param w, h given in px
316 */
317 void
318 PageSizer::setDim (double w, double h, bool changeList)
319 {
320 static bool _called = false;
321 if (_called)
322 return;
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 && !_wr->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,
337 /* TODO: annotate */ "page-sizer.cpp:301");
338 }
340 _landscape = w>h;
341 _landscapeButton.set_active(_landscape ? true : false);
342 _portraitButton.set_active (_landscape ? false : true);
344 if (changeList)
345 _paperSizeList.set_active (find_paper_size (w, h));
347 Unit const& unit = _rum._sel->getUnit();
348 _rusw.setValue (w / unit.factor);
349 _rush.setValue (h / unit.factor);
351 _paper_size_list_connection.unblock();
352 _landscape_connection.unblock();
353 _portrait_connection.unblock();
354 _changedw_connection.unblock();
355 _changedh_connection.unblock();
357 _called = false;
358 }
360 /**
361 * Returns an index into inkscape_papers of a paper of the specified
362 * size (specified in px), or -1 if there's no such paper.
363 */
364 int
365 PageSizer::find_paper_size (double w, double h) const
366 {
367 double smaller = w;
368 double larger = h;
369 if ( h < w ) {
370 smaller = h; larger = w;
371 }
373 g_return_val_if_fail(smaller <= larger, -1);
375 int index = 0;
376 std::map<Glib::ustring, PaperSize>::const_iterator iter;
377 for (iter = paperSizeTable.begin() ; iter != paperSizeTable.end() ; iter++) {
378 PaperSize paper = iter->second;
379 SPUnit const &i_unit = sp_unit_get_by_id(paper.unit);
380 double smallX = sp_units_get_pixels(paper.smaller, i_unit);
381 double largeX = sp_units_get_pixels(paper.larger, i_unit);
383 g_return_val_if_fail(smallX <= largeX, -1);
385 if ((std::abs(smaller - smallX) <= 0.1) &&
386 (std::abs(larger - largeX) <= 0.1) )
387 return index;
389 index++;
390 }
391 return -1;
392 }
394 void
395 PageSizer::fire_fit_canvas_to_selection_or_drawing() {
396 SPDesktop *dt = SP_ACTIVE_DESKTOP;
397 if (!dt) return;
398 Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
399 if (verb) {
400 SPAction *action = verb->get_action(dt);
401 if (action) {
402 sp_action_perform(action, NULL);
403 }
404 }
405 }
407 void
408 PageSizer::on_paper_size_list_changed()
409 {
410 Glib::ustring name = _paperSizeList.get_active_text();
411 std::map<Glib::ustring, PaperSize>::const_iterator iter =
412 paperSizeTable.find(name);
413 if (iter == paperSizeTable.end()) {
414 g_warning("paper size '%s' not found in table", name.c_str());
415 return;
416 }
417 PaperSize paper = iter->second;
418 double w = paper.smaller;
419 double h = paper.larger;
420 SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
421 sp_convert_distance (&w, &src_unit, &_px_unit);
422 sp_convert_distance (&h, &src_unit, &_px_unit);
423 if (_landscape)
424 setDim (h, w, false);
425 else
426 setDim (w, h, false);
428 }
430 void
431 PageSizer::on_portrait()
432 {
433 if (!_portraitButton.get_active())
434 return;
435 double w = _rusw.getSU()->getValue ("px");
436 double h = _rush.getSU()->getValue ("px");
437 if (h<w)
438 setDim (h, w);
439 }
441 void
442 PageSizer::on_landscape()
443 {
444 if (!_landscapeButton.get_active())
445 return;
446 double w = _rusw.getSU()->getValue ("px");
447 double h = _rush.getSU()->getValue ("px");
448 if (w<h)
449 setDim (h, w);
450 }
452 void
453 PageSizer::on_value_changed()
454 {
455 if (_wr->isUpdating()) return;
457 setDim (_rusw.getSU()->getValue("px"),
458 _rush.getSU()->getValue("px"));
459 }
461 } // namespace Widget
462 } // namespace UI
463 } // namespace Inkscape
465 /*
466 Local Variables:
467 mode:c++
468 c-file-style:"stroustrup"
469 c-file-offsets:((innamespace . 0)(inline-open . 0))
470 indent-tabs-mode:nil
471 fill-column:99
472 End:
473 */
474 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :