1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** @file
3 * TODO: insert short description here
4 *//*
5 * Authors: see git history
6 *
7 * Copyright (C) 2018 Authors
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
10 /**
11 * @file-update
12 * Operations to bump files from the pre-0.92 era into the 0.92+ era
13 * (90dpi vs 96dpi, line height problems, and related bugs)
14 */
15 /* Authors:
16 * Tavmjong Bah
17 * Marc Jeanmougin
18 * su_v
19 */
20 #include <clocale>
21 #include <string>
22 #include <vector>
23
24 #include <gtkmm.h>
25
26 #include "desktop.h"
27 #include "document-undo.h"
28 #include "document.h"
29 #include "file.h"
30 #include "inkscape.h"
31 #include "message-stack.h"
32 #include "message.h"
33 #include "preferences.h"
34 #include "print.h"
35 #include "proj_pt.h"
36 #include "selection-chemistry.h"
37 #include "text-editing.h"
38 #include "verbs.h"
39
40 #include "display/control/canvas-grid.h"
41
42 #include "extension/effect.h"
43 #include "extension/db.h"
44 #include "extension/input.h"
45 #include "extension/output.h"
46 #include "extension/system.h"
47
48 #include "io/dir-util.h"
49 #include "io/sys.h"
50
51 #include "object/persp3d.h"
52 #include "object/sp-defs.h"
53 #include "object/sp-flowdiv.h"
54 #include "object/sp-flowtext.h"
55 #include "object/sp-guide.h"
56 #include "object/sp-item.h"
57 #include "object/sp-namedview.h"
58 #include "object/sp-object.h"
59 #include "object/sp-root.h"
60 #include "object/sp-text.h"
61 #include "object/sp-tspan.h"
62 #include "style.h"
63
64 #include "ui/shape-editor.h"
65
66
67 using Inkscape::DocumentUndo;
68
69 int sp_file_convert_dpi_method_commandline = -1; // Unset
70
is_line(SPObject * i)71 bool is_line(SPObject *i)
72 {
73 if (!(i->getAttribute("sodipodi:role")))
74 return false;
75 return !strcmp(i->getAttribute("sodipodi:role"), "line");
76 }
77
78
fix_blank_line(SPObject * o)79 void fix_blank_line(SPObject *o)
80 {
81 if (SP_IS_TEXT(o))
82 ((SPText *)o)->rebuildLayout();
83 else if (SP_IS_FLOWTEXT(o))
84 ((SPFlowtext *)o)->rebuildLayout();
85
86 SPIFontSize fontsize = o->style->font_size;
87 SPILengthOrNormal lineheight = o->style->line_height;
88 std::vector<SPObject *> cl = o->childList(false);
89 bool beginning = true;
90 for (std::vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) {
91 SPObject *i = *ci;
92 if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) {
93 if (sp_text_get_length((SPItem *)i) <= 1) { // empty line
94 Inkscape::Text::Layout::iterator pos = te_get_layout((SPItem*)(o))->charIndexToIterator(
95 ((SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i))?0:((ci==cl.begin())?0:1)) + sp_text_get_length_upto(o,i) );
96 sp_te_insert((SPItem *)o, pos, "\u00a0"); //"\u00a0"
97 gchar *l = g_strdup_printf("%f", lineheight.value);
98 gchar *f = g_strdup_printf("%f", fontsize.value);
99 i->style->line_height.readIfUnset(l);
100 if (!beginning)
101 i->style->font_size.read(f);
102 else
103 i->style->font_size.readIfUnset(f);
104 g_free(l);
105 g_free(f);
106 } else {
107 beginning = false;
108 fontsize = i->style->font_size;
109 lineheight = o->style->line_height;
110 }
111 }
112 }
113 }
114
fix_line_spacing(SPObject * o)115 void fix_line_spacing(SPObject *o)
116 {
117 SPILengthOrNormal lineheight = o->style->line_height;
118 bool inner = false;
119 std::vector<SPObject *> cl = o->childList(false);
120 for (auto i : cl) {
121 if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) {
122 // if no line-height attribute, set it
123 gchar *l = g_strdup_printf("%f", lineheight.value);
124 i->style->line_height.readIfUnset(l);
125 g_free(l);
126 }
127 inner = true;
128 }
129 if (inner) {
130 if (SP_IS_TEXT(o)) {
131 o->style->line_height.read("0.00%");
132 } else {
133 o->style->line_height.read("0.01%");
134 }
135 }
136 }
137
fix_font_name(SPObject * o)138 void fix_font_name(SPObject *o)
139 {
140 std::vector<SPObject *> cl = o->childList(false);
141 for (auto ci : cl)
142 fix_font_name(ci);
143 std::string prev = o->style->font_family.value();
144 if (prev == "Sans")
145 o->style->font_family.read("sans-serif");
146 else if (prev == "Serif")
147 o->style->font_family.read("serif");
148 else if (prev == "Monospace")
149 o->style->font_family.read("monospace");
150 }
151
152
fix_font_size(SPObject * o)153 void fix_font_size(SPObject *o)
154 {
155 SPIFontSize fontsize = o->style->font_size;
156 if (!fontsize.set)
157 return;
158 bool inner = false;
159 std::vector<SPObject *> cl = o->childList(false);
160 for (auto i : cl) {
161 fix_font_size(i);
162 if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) {
163 inner = true;
164 gchar *s = g_strdup_printf("%f", fontsize.value);
165 if (fontsize.set)
166 i->style->font_size.readIfUnset(s);
167 g_free(s);
168 }
169 }
170 if (inner && (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o)))
171 o->style->font_size.clear();
172 }
173
174
fix_osb(SPObject * i)175 void fix_osb(SPObject *i)
176 {
177 if (auto a = i->getAttribute("osb:paint") ){
178 i->setAttribute("inkscape:swatch", a);
179 i->setAttribute("osb:paint", nullptr);
180 i->updateRepr();
181 }
182 }
183
184
185 // helper function
sp_file_text_run_recursive(void (* f)(SPObject *),SPObject * o)186 void sp_file_text_run_recursive(void (*f)(SPObject *), SPObject *o)
187 {
188 if (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o))
189 f(o);
190 else {
191 std::vector<SPObject *> cl = o->childList(false);
192 for (auto ci : cl)
193 sp_file_text_run_recursive(f, ci);
194 }
195 }
196
fix_update(SPObject * o)197 void fix_update(SPObject *o) {
198 o->style->write();
199 o->updateRepr();
200 }
201
sp_file_convert_text_baseline_spacing(SPDocument * doc)202 void sp_file_convert_text_baseline_spacing(SPDocument *doc)
203 {
204 char *oldlocale = g_strdup(setlocale(LC_NUMERIC, nullptr));
205 setlocale(LC_NUMERIC,"C");
206 sp_file_text_run_recursive(fix_blank_line, doc->getRoot());
207 sp_file_text_run_recursive(fix_line_spacing, doc->getRoot());
208 sp_file_text_run_recursive(fix_font_size, doc->getRoot());
209 setlocale(LC_NUMERIC, oldlocale);
210 g_free(oldlocale);
211
212 sp_file_text_run_recursive(fix_update, doc->getRoot());
213 }
214
sp_file_fix_osb(SPObject * o)215 void sp_file_fix_osb(SPObject *o)
216 {
217 fix_osb(o);
218 std::vector<SPObject *> cl = o->childList(false);
219 for (auto ci : cl)
220 sp_file_fix_osb(ci);
221 }
222
223
224 /**
225 * Implements a fix for https://gitlab.com/inkscape/inkscape/-/issues/45
226 * Line spacing for empty lines was handled differently before 1.0
227 * and in particular with the first empty lines or with how style attributes
228 * are processed in empty lines (line = tspan with sodipodi:role="line")
229 *
230 * This function "fixes" a text element in a old document by removing the
231 * first empty lines and style attrs on other empty lines.
232 *
233 * */
_fix_pre_v1_empty_lines(SPObject * o)234 void _fix_pre_v1_empty_lines(SPObject *o)
235 {
236 std::vector<SPObject *> cl = o->childList(false);
237 bool begin = true;
238 std::string cur_y = "";
239 for (auto ci : cl) {
240 if (!SP_IS_TSPAN(ci))
241 continue;
242 if (!is_line(ci))
243 continue;
244 if (!ci->childList(false).empty()) {
245 if (begin)
246 cur_y = ci->getAttribute("y") ? ci->getAttribute("y") : cur_y;
247 begin = false;
248 } else {
249 ci->removeAttribute("style");
250 ci->updateRepr();
251 if (begin) {
252 ci->deleteObject();
253 }
254 }
255 if (cur_y != "")
256 o->setAttribute("y", cur_y);
257 }
258 }
259
260
261
sp_file_fix_empty_lines(SPDocument * doc)262 void sp_file_fix_empty_lines(SPDocument *doc)
263 {
264 sp_file_text_run_recursive(_fix_pre_v1_empty_lines, doc->getRoot());
265 sp_file_text_run_recursive(fix_update, doc->getRoot());
266 }
267
268
269
sp_file_convert_font_name(SPDocument * doc)270 void sp_file_convert_font_name(SPDocument *doc)
271 {
272 sp_file_text_run_recursive(fix_font_name, doc->getRoot());
273 sp_file_text_run_recursive(fix_update, doc->getRoot());
274 }
275
276
277 // Quick and dirty internal backup function
sp_file_save_backup(Glib::ustring uri)278 bool sp_file_save_backup( Glib::ustring uri ) {
279
280 Glib::ustring out = uri;
281 out.insert(out.find(".svg"),"_backup");
282
283 FILE *filein = Inkscape::IO::fopen_utf8name(uri.c_str(), "rb");
284 if (!filein) {
285 std::cerr << "sp_file_save_backup: failed to open: " << uri << std::endl;
286 return false;
287 }
288
289 FILE *fileout = Inkscape::IO::fopen_utf8name(out.c_str(), "wb");
290 if (!fileout) {
291 std::cerr << "sp_file_save_backup: failed to open: " << out << std::endl;
292 fclose( filein );
293 return false;
294 }
295
296 int ch;
297 while ((ch = fgetc(filein)) != EOF) {
298 fputc(ch, fileout);
299 }
300 fflush(fileout);
301
302 bool return_value = true;
303 if (ferror(fileout)) {
304 std::cerr << "sp_file_save_backup: error when writing to: " << out << std::endl;
305 return_value = false;
306 }
307
308 fclose(filein);
309 fclose(fileout);
310
311 return return_value;
312 }
313
314
sp_file_convert_dpi(SPDocument * doc)315 void sp_file_convert_dpi(SPDocument *doc)
316 {
317 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
318 SPRoot *root = doc->getRoot();
319
320 // See if we need to offer the user a fix for the 90->96 px per inch change.
321 // std::cout << "SPFileOpen:" << std::endl;
322 // std::cout << " Version: " << sp_version_to_string(root->version.inkscape) << std::endl;
323 // std::cout << " SVG file from old Inkscape version detected: "
324 // << sp_version_to_string(root->version.inkscape) << std::endl;
325 static const double ratio = 90.0/96.0;
326
327 bool need_fix_viewbox = false;
328 bool need_fix_units = false;
329 bool need_fix_guides = false;
330 bool need_fix_grid_mm = false;
331 bool need_fix_box3d = false;
332 bool did_scaling = false;
333
334 // Check if potentially need viewbox or unit fix
335 switch (root->width.unit) {
336 case SVGLength::PC:
337 case SVGLength::PT:
338 case SVGLength::MM:
339 case SVGLength::CM:
340 case SVGLength::INCH:
341 need_fix_viewbox = true;
342 break;
343 case SVGLength::NONE:
344 case SVGLength::PX:
345 need_fix_units = true;
346 break;
347 case SVGLength::EM:
348 case SVGLength::EX:
349 case SVGLength::PERCENT:
350 // OK
351 break;
352 default:
353 std::cerr << "sp_file_convert_dpi: Unhandled width unit!" << std::endl;
354 }
355
356 switch (root->height.unit) {
357 case SVGLength::PC:
358 case SVGLength::PT:
359 case SVGLength::MM:
360 case SVGLength::CM:
361 case SVGLength::INCH:
362 need_fix_viewbox = true;
363 break;
364 case SVGLength::NONE:
365 case SVGLength::PX:
366 need_fix_units = true;
367 break;
368 case SVGLength::EM:
369 case SVGLength::EX:
370 case SVGLength::PERCENT:
371 // OK
372 break;
373 default:
374 std::cerr << "sp_file_convert_dpi: Unhandled height unit!" << std::endl;
375 }
376
377 if (need_fix_units && need_fix_viewbox) {
378 std::cerr << "Different units in document size !" << std::endl;
379 if (root->viewBox_set)
380 need_fix_viewbox = false;
381 else
382 need_fix_units = false;
383 }
384
385 // std::cout << "Absolute SVG units in root? " << (need_fix_viewbox?"true":"false") << std::endl;
386 // std::cout << "User units in root? " << (need_fix_units ?"true":"false") << std::endl;
387
388 if ((!root->viewBox_set && need_fix_viewbox) || need_fix_units) {
389 int response = FILE_DPI_UNCHANGED; // default
390
391 /******** UI *******/
392 bool backup = prefs->getBool("/options/dpifixbackup", true);
393 if (INKSCAPE.use_gui() && sp_file_convert_dpi_method_commandline == -1) {
394 Gtk::Dialog scale_dialog(_("Convert legacy Inkscape file"));
395 scale_dialog.set_transient_for( *(INKSCAPE.active_desktop()->getToplevel()) );
396 scale_dialog.set_border_width(10);
397 scale_dialog.set_resizable(false);
398 Gtk::Label explanation;
399 explanation.set_markup(Glib::ustring("<b>") + doc->getDocumentName() + "</b>\n" +
400 _("was created in an older version of Inkscape (90 DPI) and we need "
401 "to make it compatible with newer versions (96 DPI). Tell us about this file:\n"));
402 explanation.set_line_wrap(true);
403 explanation.set_size_request(600,-1);
404 Gtk::RadioButton::Group c1, c2;
405
406 Gtk::Label choice1_label;
407 choice1_label.set_markup(
408 _("This file contains digital artwork for screen display. <b>(Choose if unsure.)</b>"));
409 Gtk::RadioButton choice1(c1);
410 choice1.add(choice1_label);
411 Gtk::RadioButton choice2(c1, _("This file is intended for physical output, such as paper or 3D prints."));
412 Gtk::Label choice2_1_label;
413 choice2_1_label.set_markup(_("The appearance of elements such as clips, masks, filters, and clones\n"
414 "is most important. <b>(Choose if unsure.)</b>"));
415 Gtk::RadioButton choice2_1(c2);
416 choice2_1.add(choice2_1_label);
417 Gtk::RadioButton choice2_2(c2, _("The accuracy of the physical unit size and position values of objects\n"
418 "in the file is most important. (Experimental.)"));
419 Gtk::CheckButton backup_button(_("Create a backup file in same directory."));
420 Gtk::Expander moreinfo(_("More details..."));
421 Gtk::Label moreinfo_text("", Gtk::ALIGN_START);
422 moreinfo_text.set_markup(
423 // TRANSLATORS: Please don't translate link unless the page exists in your language. Add your language
424 // code to the link this way: https://inkscape.org/[lang]/learn/faq#dpi_change
425 _("<small>We've updated Inkscape to follow the CSS standard of 96 DPI for "
426 "better browser compatibility; we used to use 90 DPI. Digital artwork for screen\n"
427 "display will be converted to 96 DPI without scaling and should be unaffected.\n"
428 "Artwork drawn at 90 DPI for a specific physical size will be too small if "
429 "converted to 96 DPI without any scaling. There are two scaling methods:\n\n"
430 "<b>Scaling the whole document:</b> The least error-prone method, this preserves "
431 "the appearance of the artwork, including filters and the position of masks, etc. \n"
432 "The scale of the artwork relative to the document size may not be accurate.\n\n"
433 "<b>Scaling individual elements in the artwork:</b> This method is less reliable "
434 "and can result in a changed appearance, \nbut is better for physical output that "
435 "relies on accurate sizes and positions (for example, for 3D printing.)\n\n"
436 "More information about this change are available in the <a "
437 "href='https://inkscape.org/en/learn/faq#dpi_change'>Inkscape FAQ</a>"
438 "</small>"));
439 moreinfo_text.set_line_wrap(true);
440 moreinfo_text.set_margin_bottom(20);
441 moreinfo_text.set_margin_top(20);
442 moreinfo_text.set_margin_start(30);
443 moreinfo_text.set_margin_end(15);
444
445 Gtk::Box b;
446 b.set_border_width(0);
447
448 b.pack_start(choice2_1, false, false, 4);
449 b.pack_start(choice2_2, false, false, 4);
450 choice2_1.show();
451 choice2_2.show();
452
453 b.set_halign(Gtk::ALIGN_START);
454 b.set_valign(Gtk::ALIGN_START);
455 b.set_hexpand(false);
456 b.set_vexpand(false);
457 b.set_margin_start(30);
458
459 Gtk::Box *content = scale_dialog.get_content_area();
460 Gtk::Button *ok_button = scale_dialog.add_button(_("OK"), GTK_RESPONSE_ACCEPT);
461 backup_button.set_active(backup);
462 content->pack_start(explanation, false, false, 5);
463 content->pack_start(choice1, false, false, 5);
464 content->pack_start(choice2, false, false, 5);
465 content->pack_start(b, false, false, 5);
466 content->pack_start(backup_button, false, false, 5);
467 content->pack_start(moreinfo, false, false, 5);
468 moreinfo.add(moreinfo_text);
469 scale_dialog.show_all_children();
470 b.hide();
471 choice1.signal_clicked().connect(sigc::mem_fun(b, &Gtk::Box::hide));
472 choice2.signal_clicked().connect(sigc::mem_fun(b, &Gtk::Box::show));
473
474 response = prefs->getInt("/options/dpiupdatemethod", FILE_DPI_UNCHANGED);
475 if ( response != FILE_DPI_UNCHANGED ) {
476 choice2.set_active();
477 b.show();
478 if ( response == FILE_DPI_DOCUMENT_SCALED)
479 choice2_2.set_active();
480 }
481 ok_button->grab_focus();
482
483 int status = scale_dialog.run();
484 if ( status == GTK_RESPONSE_ACCEPT ) {
485 backup = backup_button.get_active();
486 prefs->setBool("/options/dpifixbackup", backup);
487 response = choice1.get_active() ? FILE_DPI_UNCHANGED : choice2_1.get_active() ? FILE_DPI_VIEWBOX_SCALED : FILE_DPI_DOCUMENT_SCALED;
488 prefs->setInt("/options/dpiupdatemethod", response);
489 } else if (sp_file_convert_dpi_method_commandline != -1) {
490 response = sp_file_convert_dpi_method_commandline;
491 } else {
492 response = FILE_DPI_UNCHANGED;
493 }
494 } else { // GUI with explicit option
495 response = FILE_DPI_UNCHANGED;
496 }
497
498 if (backup && (response != FILE_DPI_UNCHANGED)) {
499 const char* uri = doc->getDocumentURI();
500 if (uri) {
501 sp_file_save_backup(Glib::ustring(uri));
502 }
503 }
504
505 if (!(response == FILE_DPI_UNCHANGED && need_fix_units)) {
506 need_fix_guides = true; // Only fix guides if drawing scaled
507 need_fix_box3d = true;
508 }
509
510 if (response == FILE_DPI_VIEWBOX_SCALED) {
511 double ratio_viewbox = need_fix_units ? 1.0 : ratio;
512
513 doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value("px") * ratio_viewbox,
514 doc->getHeight().value("px") * ratio_viewbox));
515 Inkscape::Util::Quantity width = // maybe set it to mm ?
516 Inkscape::Util::Quantity(doc->getWidth().value("px") / ratio, "px");
517 Inkscape::Util::Quantity height = Inkscape::Util::Quantity(doc->getHeight().value("px") / ratio, "px");
518 if (need_fix_units)
519 doc->setWidthAndHeight(width, height, false);
520
521 } else if (response == FILE_DPI_DOCUMENT_SCALED) {
522
523 Inkscape::Util::Quantity width = // maybe set it to mm ?
524 Inkscape::Util::Quantity(doc->getWidth().value("px") / ratio, "px");
525 Inkscape::Util::Quantity height = Inkscape::Util::Quantity(doc->getHeight().value("px") / ratio, "px");
526 if (need_fix_units)
527 doc->setWidthAndHeight(width, height, false);
528
529 if (!root->viewBox_set) {
530 // Save preferences
531 bool transform_stroke = prefs->getBool("/options/transform/stroke", true);
532 bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true);
533 bool transform_pattern = prefs->getBool("/options/transform/pattern", true);
534 bool transform_gradient = prefs->getBool("/options/transform/gradient", true);
535
536 prefs->setBool("/options/transform/stroke", true);
537 prefs->setBool("/options/transform/rectcorners", true);
538 prefs->setBool("/options/transform/pattern", true);
539 prefs->setBool("/options/transform/gradient", true);
540
541 Inkscape::UI::ShapeEditor::blockSetItem(true);
542 doc->getRoot()->scaleChildItemsRec(Geom::Scale(1 / ratio), Geom::Point(0, 0), false);
543 Inkscape::UI::ShapeEditor::blockSetItem(false);
544
545 // Restore preferences
546 prefs->setBool("/options/transform/stroke", transform_stroke);
547 prefs->setBool("/options/transform/rectcorners", transform_rectcorners);
548 prefs->setBool("/options/transform/pattern", transform_pattern);
549 prefs->setBool("/options/transform/gradient", transform_gradient);
550
551 did_scaling = true;
552 }
553
554 } else { // FILE_DPI_UNCHANGED
555 if (need_fix_units)
556 need_fix_grid_mm = true;
557 }
558 }
559
560 // Fix guides and grids and perspective
561 for (SPObject *child = root->firstChild(); child; child = child->getNext()) {
562 SPNamedView *nv = dynamic_cast<SPNamedView *>(child);
563 if (nv) {
564 if (need_fix_guides) {
565 // std::cout << "Fixing guides" << std::endl;
566 for (SPObject *child2 = nv->firstChild(); child2; child2 = child2->getNext()) {
567 SPGuide *gd = dynamic_cast<SPGuide *>(child2);
568 if (gd) {
569 gd->moveto(gd->getPoint() / ratio, true);
570 }
571 }
572 }
573
574 for (auto grid : nv->grids) {
575 Inkscape::CanvasXYGrid *xy = dynamic_cast<Inkscape::CanvasXYGrid *>(grid);
576 if (xy) {
577 // std::cout << "A grid: " << xy->getSVGName() << std::endl;
578 // std::cout << " Origin: " << xy->origin
579 // << " Spacing: " << xy->spacing << std::endl;
580 // std::cout << (xy->isLegacy()?" Legacy":" Not Legacy") << std::endl;
581 Geom::Scale scale = doc->getDocumentScale();
582 if (xy->isLegacy()) {
583 if (xy->isPixel()) {
584 if (need_fix_grid_mm) {
585 xy->Scale(Geom::Scale(1, 1)); // See note below
586 } else {
587 scale *= Geom::Scale(ratio, ratio);
588 xy->Scale(scale.inverse()); /* *** */
589 }
590 } else {
591 if (need_fix_grid_mm) {
592 xy->Scale(Geom::Scale(ratio, ratio));
593 } else {
594 xy->Scale(scale.inverse()); /* *** */
595 }
596 }
597 } else {
598 if (need_fix_guides) {
599 if (did_scaling) {
600 xy->Scale(Geom::Scale(ratio, ratio).inverse());
601 } else {
602 // HACK: Scaling the document does not seem to cause
603 // grids defined in document units to be updated.
604 // This forces an update.
605 xy->Scale(Geom::Scale(1, 1));
606 }
607 }
608 }
609 }
610 }
611 } // If SPNamedView
612
613 SPDefs *defs = dynamic_cast<SPDefs *>(child);
614 if (defs && need_fix_box3d) {
615 for (SPObject *child = defs->firstChild(); child; child = child->getNext()) {
616 Persp3D *persp3d = dynamic_cast<Persp3D *>(child);
617 if (persp3d) {
618 std::vector<Glib::ustring> tokens;
619
620 const gchar *vp_x = persp3d->getAttribute("inkscape:vp_x");
621 const gchar *vp_y = persp3d->getAttribute("inkscape:vp_y");
622 const gchar *vp_z = persp3d->getAttribute("inkscape:vp_z");
623 const gchar *vp_o = persp3d->getAttribute("inkscape:persp3d-origin");
624 // std::cout << "Found Persp3d: "
625 // << " vp_x: " << vp_x
626 // << " vp_y: " << vp_y
627 // << " vp_z: " << vp_z << std::endl;
628 Proj::Pt2 pt_x(vp_x);
629 Proj::Pt2 pt_y(vp_y);
630 Proj::Pt2 pt_z(vp_z);
631 Proj::Pt2 pt_o(vp_o);
632 pt_x = pt_x * (1.0 / ratio);
633 pt_y = pt_y * (1.0 / ratio);
634 pt_z = pt_z * (1.0 / ratio);
635 pt_o = pt_o * (1.0 / ratio);
636 persp3d->setAttribute("inkscape:vp_x", pt_x.coord_string());
637 persp3d->setAttribute("inkscape:vp_y", pt_y.coord_string());
638 persp3d->setAttribute("inkscape:vp_z", pt_z.coord_string());
639 persp3d->setAttribute("inkscape:persp3d-origin", pt_o.coord_string());
640 }
641 }
642 }
643 } // Look for SPNamedView and SPDefs loop
644
645 // desktop->getDocument()->ensureUpToDate(); // Does not update box3d!
646 DocumentUndo::done(doc, SP_VERB_NONE, _("Update Document"));
647 }
648
649
650
651 /*
652 Local Variables:
653 mode:c++
654 c-file-style:"stroustrup"
655 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
656 indent-tabs-mode:nil
657 fill-column:99
658 End:
659 */
660 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
661