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