Inkscape
Vector Graphics Editor
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages Concepts
document.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SPDocument manipulation
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * MenTaLguY <mental@rydia.net>
8 * bulia byak <buliabyak@users.sf.net>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 * Tavmjong Bah <tavmjong@free.fr>
12 *
13 * Copyright (C) 2004-2005 MenTaLguY
14 * Copyright (C) 1999-2002 Lauris Kaplinski
15 * Copyright (C) 2000-2001 Ximian, Inc.
16 * Copyright (C) 2012 Tavmjong Bah
17 *
18 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
19 */
20
36#include "document.h"
37
38#include <vector>
39#include <string>
40#include <cstring>
41
42#include <boost/range/adaptor/reversed.hpp>
43#include <glibmm/main.h>
44#include <glibmm/miscutils.h>
45
46#include <2geom/transforms.h>
47
48#include "desktop.h"
49#include "document-undo.h"
50#include "event-log.h"
51#include "file.h"
52#include "id-clash.h"
53#include "inkscape-window.h"
54#include "inkscape.h"
55#include "layer-manager.h"
56#include "page-manager.h"
57#include "colors/document-cms.h"
58#include "rdf.h"
59#include "selection.h"
60
70#include "display/drawing.h"
71#include "io/dir-util.h"
73#include "object/persp3d.h"
74#include "object/sp-defs.h"
75#include "object/sp-factory.h"
77#include "object/sp-lpe-item.h"
78#include "object/sp-namedview.h"
79#include "object/sp-page.h"
80#include "object/sp-root.h"
81#include "object/sp-symbol.h"
82#include "ui/widget/canvas.h"
84#include "util/units.h"
86#include "xml/rebase-hrefs.h"
87#include "xml/simple-document.h"
88
91
92// Higher number means lower priority.
93constexpr auto SP_DOCUMENT_UPDATE_PRIORITY = G_PRIORITY_HIGH_IDLE - 2;
94
95// Should have a lower priority than SP_DOCUMENT_UPDATE_PRIORITY,
96// since we want it to happen when there are no more updates.
97constexpr auto SP_DOCUMENT_REROUTING_PRIORITY = G_PRIORITY_HIGH_IDLE - 1;
98
100
101static int doc_count = 0;
102static int doc_mem_count = 0;
103
104static unsigned long next_serial = 0;
105
107 keepalive(false),
108 virgin(true),
109 rdoc(nullptr),
110 rroot(nullptr),
111 root(nullptr),
112 style_cascade(cr_cascade_new(nullptr, nullptr, nullptr)),
113 document_filename(nullptr),
114 document_base(nullptr),
115 document_name(nullptr),
116 actionkey(),
117 object_id_counter(1),
118 _router(std::make_unique<Avoid::Router>(Avoid::PolyLineRouting|Avoid::OrthogonalRouting)),
119 current_persp3d(nullptr),
120 current_persp3d_impl(nullptr),
121 _activexmltree(nullptr)
122{
123 // This is kept here so that members are not accessed before they are initialized
124
125 _event_log = std::make_unique<Inkscape::EventLog>(this);
126 _selection = std::make_unique<Inkscape::Selection>(this);
127
128 _desktop_activated_connection = INKSCAPE.signal_activate_desktop.connect(
129 sigc::hide(sigc::bind(
130 sigc::ptr_fun(&DocumentUndo::resetKey), this)));
131
132 // Penalise libavoid for choosing paths with needless extra segments.
133 // This results in much better looking orthogonal connector paths.
134 _router->setRoutingPenalty(Avoid::segmentPenalty);
135
137
138 sensitive = false;
139 partial = nullptr;
140 seeking = false;
141
142 // For undo/redo
144
145 // XXX only for testing!
147
148 // Actions
149 action_group = Gio::SimpleActionGroup::create();
151 add_actions_pages(this);
155
156 _page_manager = std::make_unique<Inkscape::PageManager>(this);
157 _cms_manager = std::make_unique<Inkscape::Colors::DocumentCMS>(this);
158}
159
161 destroySignal.emit();
162
163 // kill/unhook this first
165
166 if (partial) {
168 partial = nullptr;
169 }
170
171 DocumentUndo::clearRedo(this);
172 DocumentUndo::clearUndo(this);
173
174 if (root) {
177 root = nullptr;
178 }
179
181
182 /* Free resources */
183 resources.clear();
184
185 // This also destroys all attached stylesheets
187 style_cascade = nullptr;
188
189 if (document_name) {
190 g_free(document_name);
191 document_name = nullptr;
192 }
193 if (document_base) {
194 g_free(document_base);
195 document_base = nullptr;
196 }
197 if (document_filename) {
198 g_free(document_filename);
199 document_filename = nullptr;
200 }
201
202 modified_connection.disconnect();
203 rerouting_connection.disconnect();
204
205 if (keepalive) {
206 inkscape_unref(INKSCAPE);
207 keepalive = false;
208 }
209
210 if (this->current_persp3d_impl)
211 delete this->current_persp3d_impl;
212 this->current_persp3d_impl = nullptr;
213
214 // This is at the end of the destructor, because preceding code adds new orphans to the queue
216}
217
219{
220 return ++doc_count;
221}
222
224{
225 return sp_repr_lookup_name (rroot, "sodipodi:namedview");
226}
227
234{
235 auto xml = getReprNamedView();
236 if (!xml) {
237 xml = rdoc->createElement("sodipodi:namedview");
238 rroot->addChildAtPos(xml, 0);
240 }
241 auto nv = getObjectByRepr(xml);
242 return cast<SPNamedView>(nv);
243}
244
246{
247 if (!root) {
248 return nullptr;
249 }
250 return root->defs;
251}
252
254 // Check if current_persp3d is still valid
255 std::vector<Persp3D*> plist;
257 for (auto & i : plist) {
258 if (current_persp3d == i)
259 return current_persp3d;
260 }
261
262 // If not, return the first perspective in defs (which may be NULL of none exists)
264
265 return current_persp3d;
266}
267
269 current_persp3d = persp;
270 //current_persp3d_impl = persp->perspective_impl;
271}
272
273void SPDocument::getPerspectivesInDefs(std::vector<Persp3D*> &list) const
274{
275 for (auto &c : root->defs->children) {
276 if (auto p = cast<Persp3D>(&c)) {
277 list.emplace_back(p);
278 }
279 }
280}
281
295void SPDocument::setPages(bool enabled)
296{
297 if (enabled) {
298 _page_manager->enablePages();
299 } else {
300 _page_manager->disablePages();
301 }
302}
303
310void SPDocument::prunePages(const std::string &page_nums, bool invert)
311{
312 auto pages = _page_manager->getPages(page_nums, invert);
313 for (auto page : pages) {
314 if (page->getId()) {
316 _page_manager->deletePage(page, true);
317 }
318 }
319}
320
322 g_return_if_fail(object != nullptr);
323 g_return_if_fail(object->document == this);
324
325 sp_object_ref(object, nullptr);
326 _collection_queue.push_back(object);
327}
328
330 while (!_collection_queue.empty()) {
331 std::vector<SPObject *> objects(_collection_queue);
332 _collection_queue.clear();
333 for (auto object : objects) {
334 object->collectOrphan();
335 sp_object_unref(object, nullptr);
336 }
337 }
338}
339
340std::unique_ptr<SPDocument> SPDocument::createDoc(
342 char const *filename,
343 char const *document_base,
344 char const *document_name,
345 bool keepalive,
347{
348 auto document = std::make_unique<SPDocument>();
349
351
352 document->keepalive = keepalive;
353
354 document->rdoc = rdoc;
355 document->rroot = rroot;
356 document->_parent_document = parent;
357
358 if (document->document_filename){
359 g_free(document->document_filename);
360 document->document_filename = nullptr;
361 }
362 if (document->document_base){
363 g_free(document->document_base);
364 document->document_base = nullptr;
365 }
366 if (document->document_name){
367 g_free(document->document_name);
368 document->document_name = nullptr;
369 }
370#ifndef _WIN32
371 document->document_filename = prepend_current_dir_if_relative(filename);
372#else
373 // FIXME: it may be that prepend_current_dir_if_relative works OK on windows too, test!
374 document->document_filename = filename? g_strdup(filename) : NULL;
375#endif
376
377 // base is simply the part of the path before filename; e.g. when running "inkscape ../file.svg" the base is "../"
378 // which is why we use g_get_current_dir() in calculating the abs path above
379 //This is NULL for a new document
380 if (document_base) {
381 document->document_base = g_strdup(document_base);
382 } else {
383 document->document_base = nullptr;
384 }
385 document->document_name = g_strdup(document_name);
386
387 // Create SPRoot element
388 const std::string typeString = NodeTraits::get_type_string(*rroot);
389 SPObject* rootObj = SPFactory::createObject(typeString);
390 document->root = cast<SPRoot>(rootObj);
391
392 if (document->root == nullptr) {
393 // Node is not a valid root element
394 delete rootObj;
395
396 // fixme: what to do here?
397 throw;
398 }
399
400 // Recursively build object tree
401 document->root->invoke_build(document.get(), rroot, false);
402
403 /* Eliminate obsolete sodipodi:docbase, for privacy reasons */
404 rroot->removeAttribute("sodipodi:docbase");
405
406 /* Eliminate any claim to adhere to a profile, as we don't try to */
407 rroot->removeAttribute("baseProfile");
408
409 // loading or creating namedview.
410 auto nv = document->getNamedView();
411
412 // Set each of the defaults in new or existing namedview (allows for per-attr overriding)
413 nv->setDefaultAttribute("pagecolor", "/template/base/pagecolor", "#ffffff");
414 nv->setDefaultAttribute("bordercolor", "/template/base/bordercolor", "");
415 nv->setDefaultAttribute("borderopacity", "/template/base/borderopacity", "");
416 nv->setDefaultAttribute("inkscape:showpageshadow", "/template/base/pageshadow", "2");
417 nv->setDefaultAttribute("inkscape:pageopacity", "/template/base/pageopacity", "0.0");
418 nv->setDefaultAttribute("inkscape:pagecheckerboard", "/template/base/pagecheckerboard", "0");
419 nv->setDefaultAttribute("inkscape:deskcolor", "/template/base/deskcolor", "#d1d1d1");
420
421 // If no units are set in the document, try and guess them from the width/height
422 // XXX Replace these calls with nv->setDocumentUnit(document->root->width.getUnit());
423 if (document->root->width.isAbsolute()) {
424 nv->setDefaultAttribute("inkscape:document-units", "", document->root->width.getUnit());
425 } else if (document->root->height.isAbsolute()) {
426 nv->setDefaultAttribute("inkscape:document-units", "", document->root->height.getUnit());
427 }
428
429 // Defs
430 if (!document->root->defs) {
431 Inkscape::XML::Node *r = rdoc->createElement("svg:defs");
432 rroot->addChild(r, nullptr);
434 g_assert(document->root->defs);
435 }
436
437 /* Default RDF */
438 rdf_set_defaults(document.get());
439
440 if (keepalive) {
441 inkscape_ref(INKSCAPE);
442 }
443
444 // Check if the document already has a perspective (e.g., when opening an existing
445 // document). If not, create a new one and set it as the current perspective.
446 document->setCurrentPersp3D(Persp3D::document_first_persp(document.get()));
447 if (!document->getCurrentPersp3D()) {
448 //document->setCurrentPersp3D(Persp3D::create_xml_element (document));
449 Persp3DImpl *persp_impl = new Persp3DImpl();
450 document->setCurrentPersp3DImpl(persp_impl);
451 }
452
453 DocumentUndo::setUndoSensitive(document.get(), true);
454
455 // ************* Fix Document **************
456 // Move to separate function?
457
459 Inkscape::Version const lowest_version{0, 1};
461 document->root->inkscape.getVersion().isInsideRangeInclusive(lowest_version, {0, 92})) {
463 }
464
466 if (document->root->inkscape.getVersion().isInsideRangeInclusive(lowest_version, {0, 92})) {
467 sp_file_convert_font_name(document.get());
468 }
469
471 if (document->root->inkscape.getVersion().isInsideRangeInclusive(lowest_version, {1, 0})) {
472 sp_file_fix_empty_lines(document.get());
473 }
474
476 if (document->root->inkscape.getVersion().isInsideRangeInclusive(lowest_version, {1, 1})) {
477 sp_file_fix_osb(document->getRoot());
478 }
479
481 if (document->root->inkscape.getVersion().isInsideRangeInclusive(lowest_version, {1, 2})) {
482 sp_file_fix_feComposite(document->getRoot());
483 }
484
486 std::string version = document->root->inkscape.getVersion().str();
487 if (version.size() > 4) {
488 version.erase(5);
489 if (version == "1.3.1") {
490 document->getRoot()->updateRepr(SP_OBJECT_CHILD_MODIFIED_FLAG);
491 }
492 }
494 if (!(INKSCAPE.use_gui()) &&
495 document->root->inkscape.getVersion().isInsideRangeInclusive(lowest_version, {0, 92})) {
496 sp_file_convert_dpi(document.get());
497 }
498
499 // Update document level action settings
500 // -- none available so far --
501
502 return document;
503}
504
508std::unique_ptr<SPDocument> SPDocument::copy() const
509{
510 // New SimpleDocument object where we will put all the same data
512
513 // Duplicate the svg root node AND any PI and COMMENT nodes, this should be put
514 // into xml/simple-document.h at some point to fix it's duplicate implementation.
515 for (Inkscape::XML::Node *child = rdoc->firstChild(); child; child = child->next()) {
516 if (child) {
517 // Get a new xml repr for the svg root node
518 Inkscape::XML::Node *new_child = child->duplicate(new_rdoc);
519
520 // Add the duplicated svg node as the document's rdoc
521 new_rdoc->appendChild(new_child);
522 Inkscape::GC::release(new_child);
523 }
524 }
525
527 doc->_original_document = this;
528
529 return doc;
530}
531
532/*
533 Rebase the document with a new XMLDoc.
534 passing the same file is like revert but keep history
535*/
536void SPDocument::rebase(const gchar * file, bool keep_namedview)
537{
538 if (file == nullptr)
539 {
540 g_warning("Error on rebase_doc: no file.");
541 return;
542 }
543 Inkscape::XML::Document *new_xmldoc = sp_repr_read_file(file, SP_SVG_NS_URI);
544
545 if (new_xmldoc) {
546 rebase(new_xmldoc, keep_namedview);
547 } else {
548 g_warning("Error on rebase_doc: The file could not be parsed.");
549 }
550}
551
552/*
553 Rebase the document with de a new XMLDoc.
554 \brief A function to replace all the elements in a document
555 by those from a new XML::Document.
556 document and repinserts them into an emptied old document.
557 \param new_xmldoc The root node to inject into.
558
559 This function first deletes all the root attributes in the old document followed
560 by copying all the root attributes from the new document to the old document.
561
562 Then, it copies all the element in the new XML::Document into the root of document.
563 keep a diferent approach for namedview to not erase it and merge new value
564*/
565void SPDocument::rebase(Inkscape::XML::Document * new_xmldoc, bool keep_namedview)
566{
567 if (new_xmldoc == nullptr)
568 {
569 g_warning("Error on rebase_doc: NULL pointer input.");
570 return;
571 }
573 Inkscape::XML::Document * origin_xmldoc = getReprDoc();
574 Inkscape::XML::Node *namedview = nullptr;
575 for ( Inkscape::XML::Node *child = origin_xmldoc->root()->lastChild() ; child != nullptr ;)
576 {
577 Inkscape::XML::Node *prevchild = child->prev();
578 if (!g_strcmp0(child->name(),"sodipodi:namedview") && keep_namedview) {
579 namedview = child;
580 } else {
581 origin_xmldoc->root()->removeChild(child);
582 }
583 child = prevchild;
584 }
585 for ( Inkscape::XML::Node *child = new_xmldoc->root()->firstChild() ; child != nullptr ; child = child->next() )
586 {
587 if (!g_strcmp0(child->name(),"sodipodi:namedview") && keep_namedview) {
588 namedview->mergeFrom(child, "id", true, true);
589 } else {
590 Inkscape::XML::Node *new_child = child->duplicate(origin_xmldoc);
591 origin_xmldoc->root()->appendChild(new_child);
592 Inkscape::GC::release(new_child);
593 }
594 }
595 // Copy svg root attributes
596 for (const auto & iter : new_xmldoc->root()->attributeList()) {
597 origin_xmldoc->root()->setAttribute(g_quark_to_string(iter.key), iter.value);
598 }
600 new_xmldoc->release();
601}
602
603/*
604 Rebase the document from data in disk
605*/
606void SPDocument::rebase(bool keep_namedview)
607{
608 if (document_filename == nullptr)
609 {
610 g_warning("Error on rebase_doc: NULL file");
611 return;
612 }
613 rebase(document_filename, keep_namedview);
614}
615
619SPDocument *SPDocument::createChildDoc(std::string const &filename)
620{
621 SPDocument *avoid = nullptr;
622 // Walk up the parent chain, starting from this document.
623 for (auto doc = this; doc; doc = doc->_parent_document) {
624 // Check doc and its children for matching filename, avoiding previously searched child.
625 if (auto ret = doc->_searchForChild(filename, avoid)) {
626 return ret;
627 }
628 avoid = doc;
629 }
630
631 // Load a fresh document from the svg source.
632 auto const path = Glib::path_is_absolute(filename)
633 ? filename
634 : document_base + filename;
635
636 auto doc = createNewDoc(path.c_str(), false, false, this);
637 return _child_documents.emplace_back(std::move(doc)).get();
638}
639
640SPDocument *SPDocument::_searchForChild(std::string const &filename, SPDocument const *avoid)
641{
642 if (this == avoid) {
643 return nullptr;
644 }
645
646 if (document_filename && document_filename == filename) {
647 return this;
648 }
649
650 for (auto &c : _child_documents) {
651 if (auto ret = c->_searchForChild(filename, avoid)) {
652 return ret;
653 }
654 }
655
656 return nullptr;
657}
658
663
668std::unique_ptr<SPDocument> SPDocument::createNewDoc(char const *filename, bool keepalive, bool make_new, SPDocument *parent)
669{
670 Inkscape::XML::Document *rdoc = nullptr;
671 gchar *document_base = nullptr;
672 gchar *document_name = nullptr;
673
674 if (filename) {
676 /* Try to fetch repr from file */
677 rdoc = sp_repr_read_file(filename, SP_SVG_NS_URI);
678 /* If file cannot be loaded, return NULL without warning */
679 if (rdoc == nullptr) return nullptr;
680 rroot = rdoc->root();
681 /* If xml file is not svg, return NULL without warning */
682 /* fixme: destroy document */
683 if (strcmp(rroot->name(), "svg:svg") != 0) return nullptr;
684
685 // Opening a template that points to a sister file should still work
686 // this also includes tutorials which point to png files.
687 document_base = g_path_get_dirname(filename);
688
689 if (make_new) {
690 filename = nullptr;
691 document_name = g_strdup_printf(_("New document %d"), ++doc_count);
692 } else {
693 document_name = g_path_get_basename(filename);
694 if (strcmp(document_base, ".") == 0) {
695 g_free(document_base);
696 document_base = nullptr;
697 }
698 }
699 } else {
700 if (make_new) {
701 document_name = g_strdup_printf(_("Memory document %d"), ++doc_mem_count);
702 }
703
704 rdoc = sp_repr_document_new("svg:svg");
705 }
706
707 //# These should be set by now
708 g_assert(document_name);
709
710 auto doc = createDoc(rdoc, filename, document_base, document_name, keepalive, parent);
711
712 g_free(document_base);
713 g_free(document_name);
714
715 return doc;
716}
717
718std::unique_ptr<SPDocument> SPDocument::createNewDocFromMem(std::span<char const> buffer, bool keepalive, std::string const &filename)
719{
720 auto rdoc = sp_repr_read_mem(buffer.data(), buffer.size(), SP_SVG_NS_URI);
721 if (!rdoc) {
722 return {};
723 }
724
725 if (std::strcmp(rdoc->root()->name(), "svg:svg") != 0) {
727 return {};
728 }
729
730 auto document_base = Glib::path_get_dirname(filename);
731 if (document_base == ".") {
732 document_base = "";
733 }
734
735 auto document_name = Glib::ustring::compose(_("Memory document %1"), ++doc_mem_count);
736
737 return createDoc(rdoc, filename.c_str(), document_base.c_str(), document_name.c_str(), keepalive);
738}
739
742{
743 if (auto nv = getNamedView()) {
744 return nv->getDisplayUnit();
745 }
746 return UnitTable::get().getUnit("px");
747}
748
750void SPDocument::setDocumentScale(double scaleX, double scaleY) {
751 if (scaleX <= 0 || scaleY <= 0) {
752 g_warning("%s: Invalid scale, has to be positive: %f, %f", __func__, scaleX, scaleY);
753 return;
754 }
755
756 // since scale is doc size / viewbox size, then it follows that viewbox size is doc size / scale
758 root->viewBox.left(),
759 root->viewBox.top(),
760 root->width.computed / scaleX,
761 root->height.computed / scaleY);
762 root->viewBox_set = true;
763 root->updateRepr();
764}
765
770
774{
776 if( root->viewBox_set ) {
777 double scale_x = 1.0;
778 double scale_y = 1.0;
779 if( root->viewBox.width() > 0.0 ) {
780 scale_x = (computed ? root->width.computed : root->width.value) / root->viewBox.width();
781 }
782 if( root->viewBox.height() > 0.0 ) {
783 scale_y = (computed ? root->height.computed : root->height.value) / root->viewBox.height();
784 }
785 scale = Geom::Scale(scale_x, scale_y);
786 }
787 // std::cout << "SPDocument::getDocumentScale():\n" << scale << std::endl;
788 return scale;
789}
790
796{
798 bool transform_stroke = prefs->getBool("/options/transform/stroke", true);
799 bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true);
800 bool transform_pattern = prefs->getBool("/options/transform/pattern", true);
801 bool transform_gradient = prefs->getBool("/options/transform/gradient", true);
802
803 prefs->setBool("/options/transform/stroke", true);
804 prefs->setBool("/options/transform/rectcorners", true);
805 prefs->setBool("/options/transform/pattern", true);
806 prefs->setBool("/options/transform/gradient", true);
807
809
810 // Restore preferences
811 prefs->setBool("/options/transform/stroke", transform_stroke);
812 prefs->setBool("/options/transform/rectcorners", transform_rectcorners);
813 prefs->setBool("/options/transform/pattern", transform_pattern);
814 prefs->setBool("/options/transform/gradient", transform_gradient);
815}
816
817// Avoid calling root->updateRepr() twice by combining setting width and height.
818// (As done on every delete as clipboard calls this via fitToRect())
820{
821 auto const &unit_table = UnitTable::get();
822 Inkscape::Util::Unit const *old_width_units = unit_table.getUnit("px");
823 if (root->width.unit)
824 old_width_units = unit_table.getUnit(root->width.unit);
825 gdouble old_width_converted; // old width converted to new units
827 old_width_converted = Inkscape::Util::Quantity::convert(root->width.computed, "px", width.unit);
828 else
829 old_width_converted = Inkscape::Util::Quantity::convert(root->width.value, old_width_units, width.unit);
830
831 root->width.computed = width.value("px");
832 root->width.value = width.quantity;
833 root->width.unit = (SVGLength::Unit) width.unit->svgUnit();
834
835 Inkscape::Util::Unit const *old_height_units = unit_table.getUnit("px");
836 if (root->height.unit)
837 old_height_units = unit_table.getUnit(root->height.unit);
838 gdouble old_height_converted; // old height converted to new units
840 old_height_converted = Inkscape::Util::Quantity::convert(root->height.computed, "px", height.unit);
841 else
842 old_height_converted = Inkscape::Util::Quantity::convert(root->height.value, old_height_units, height.unit);
843
844 root->height.computed = height.value("px");
845 root->height.value = height.quantity;
846 root->height.unit = (SVGLength::Unit) height.unit->svgUnit();
847
848 // viewBox scaled by relative change in page size (maintains document scale).
849 if (root->viewBox_set && changeSize) {
851 root->viewBox.left() + (root->width.value / old_width_converted ) * root->viewBox.width(),
852 root->viewBox.top() + (root->height.value / old_height_converted) * root->viewBox.height()));
853 }
854 root->updateRepr();
855}
856
858{
859 auto const &unit_table = UnitTable::get();
860 g_return_val_if_fail(this->root != nullptr, Inkscape::Util::Quantity(0.0, unit_table.getUnit("")));
861
862 gdouble result = root->width.value;
866 u = SVGLength::PX;
867 }
868 if (u == SVGLength::NONE) {
869 u = SVGLength::PX;
870 }
871 return Inkscape::Util::Quantity(result, unit_table.getUnit(u));
872}
873
875{
876 auto const &unit_table = UnitTable::get();
877 Inkscape::Util::Unit const *old_width_units = unit_table.getUnit("px");
878 if (root->width.unit)
879 old_width_units = unit_table.getUnit(root->width.unit);
880 gdouble old_width_converted; // old width converted to new units
882 old_width_converted = Inkscape::Util::Quantity::convert(root->width.computed, "px", width.unit);
883 else
884 old_width_converted = Inkscape::Util::Quantity::convert(root->width.value, old_width_units, width.unit);
885
886 root->width.computed = width.value("px");
887 root->width.value = width.quantity;
888 root->width.unit = (SVGLength::Unit) width.unit->svgUnit();
889
890 if (root->viewBox_set && changeSize)
891 root->viewBox.setMax(Geom::Point(root->viewBox.left() + (root->width.value / old_width_converted) * root->viewBox.width(), root->viewBox.bottom()));
892
893 root->updateRepr();
894}
895
897{
898 auto const &unit_table = UnitTable::get();
899 g_return_val_if_fail(this->root != nullptr, Inkscape::Util::Quantity(0.0, unit_table.getUnit("")));
900
901 gdouble result = root->height.value;
905 u = SVGLength::PX;
906 }
907 if (u == SVGLength::NONE) {
908 u = SVGLength::PX;
909 }
910 return Inkscape::Util::Quantity(result, unit_table.getUnit(u));
911}
912
914{
915 auto const &unit_table = UnitTable::get();
916 Inkscape::Util::Unit const *old_height_units = unit_table.getUnit("px");
917 if (root->height.unit)
918 old_height_units = unit_table.getUnit(root->height.unit);
919 gdouble old_height_converted; // old height converted to new units
921 old_height_converted = Inkscape::Util::Quantity::convert(root->height.computed, "px", height.unit);
922 else
923 old_height_converted = Inkscape::Util::Quantity::convert(root->height.value, old_height_units, height.unit);
924
925 root->height.computed = height.value("px");
926 root->height.value = height.quantity;
927 root->height.unit = (SVGLength::Unit) height.unit->svgUnit();
928
929 if (root->viewBox_set && changeSize)
930 root->viewBox.setMax(Geom::Point(root->viewBox.right(), root->viewBox.top() + (root->height.value / old_height_converted) * root->viewBox.height()));
931
932 root->updateRepr();
933}
934
936{
937 if (root && !is_yaxisdown()) {
939 }
940
941 return _doc2dt;
942}
943
945{
946 Geom::Rect viewBox;
947 if (root->viewBox_set) {
948 viewBox = root->viewBox;
949 } else {
950 viewBox = *preferredBounds();
951 }
952 return viewBox;
953}
954
959{
961 0,
962 getWidth().value(getDisplayUnit()),
963 getHeight().value(getDisplayUnit())));
964}
965
967{
968 root->viewBox_set = true;
969 root->viewBox = viewBox;
970 root->updateRepr();
971}
972
974{
975 return Geom::Point(getWidth().value("px"), getHeight().value("px"));
976}
977
982
987{
988 if (auto page = _page_manager->getSelected()) {
989 return page->getDesktopRect();
990 }
991 return preferredBounds();
992}
993
1001void SPDocument::fitToRect(Geom::Rect const &rect, bool)
1002{
1003 using namespace Inkscape::Util;
1004
1005 auto const &unit_table = UnitTable::get();
1006 Unit const *nv_units = unit_table.getUnit("px");
1007
1009 nv_units = unit_table.getUnit(root->height.unit);
1010 }
1011
1012 // 1. Calculate geometric transformations that must be applied to the drawing,
1013 // pages, grids and guidelines to compensate for the changed origin.
1014 bool y_down = is_yaxisdown();
1015 double const old_height = root->height.computed;
1016 double const tr_x = -rect[Geom::X].min();
1017 double const tr_y_items = -rect[Geom::Y].min() * yaxisdir();
1018 double const tr_y_gadgets = y_down ? -rect[Geom::Y].min() : rect[Geom::Y].max() - old_height;
1019
1020 // Item translation (in desktop coordinates)
1021 auto const item_translation = Geom::Translate(tr_x, tr_y_items);
1022 // Translation of grids and guides (in document coordinates)
1023 auto const gadget_translation = Geom::Translate(tr_x, tr_y_gadgets);
1024
1025 // 2. Translate the guides.
1026 auto *nv = getNamedView();
1027 if (nv) {
1028 // It's important to do it BEFORE the document is resized, in order to ensure
1029 // the correct undo sequence. During undo, the document height will be restored
1030 // first, so the guides can then correctly recalculate their own position.
1031 // See https://gitlab.com/inkscape/inkscape/-/issues/615
1032 nv->translateGuides(gadget_translation);
1033 }
1034
1035 // 3. Resize the document. This changes the SVG origin relative to the drawing.
1036 setWidthAndHeight(Quantity(Quantity::convert(rect.width(), "px", nv_units), nv_units),
1037 Quantity(Quantity::convert(rect.height(), "px", nv_units), nv_units));
1038
1039 // 4. Translate everything to cancel out the change in the origin position.
1040 root->translateChildItems(item_translation);
1041 if (nv) {
1042 nv->translateGrids(gadget_translation);
1043 _page_manager->movePages(item_translation);
1044
1045 // FIXME: The scroll state isn't restored during undo.
1046 nv->scrollAllDesktops(-tr_x, -tr_y_gadgets * yaxisdir());
1047 }
1048}
1049
1050void SPDocument::setDocumentBase( gchar const* document_base )
1051{
1052 if (this->document_base) {
1053 g_free(this->document_base);
1054 this->document_base = nullptr;
1055 }
1056 if (document_base) {
1057 this->document_base = g_strdup(document_base);
1058 }
1059}
1060
1061void SPDocument::do_change_filename(gchar const *const filename, bool const rebase)
1062{
1063 gchar *new_document_base = nullptr;
1064 gchar *new_document_name = nullptr;
1065 gchar *new_document_filename = nullptr;
1066 if (filename) {
1067
1068#ifndef _WIN32
1069 new_document_filename = prepend_current_dir_if_relative(filename);
1070#else
1071 // FIXME: it may be that prepend_current_dir_if_relative works OK on windows too, test!
1072 new_document_filename = g_strdup(filename);
1073#endif
1074
1075 new_document_base = g_path_get_dirname(new_document_filename);
1076 new_document_name = g_path_get_basename(new_document_filename);
1077 } else {
1078 new_document_name = g_strdup_printf(_("Unnamed document %d"), ++doc_count);
1079 new_document_base = nullptr;
1080 new_document_filename = nullptr;
1081 }
1082
1083 // Update saveable repr attributes.
1085
1086 // Changing filename in the document repr must not be not undoable.
1087 {
1088 DocumentUndo::ScopedInsensitive _no_undo(this);
1089
1090 if (rebase) {
1092 bool use_sodipodi_absref = prefs->getBool("/options/svgoutput/usesodipodiabsref", false);
1093 Inkscape::XML::rebase_hrefs(this, new_document_base, use_sodipodi_absref);
1094 }
1095
1096 if (strncmp(new_document_name, "ink_ext_XXXXXX", 14)) // do not use temporary filenames
1097 repr->setAttribute("sodipodi:docname", new_document_name);
1098 }
1099
1100 g_free(this->document_name);
1101 g_free(this->document_base);
1102 g_free(this->document_filename);
1103 this->document_name = new_document_name;
1104 this->document_base = new_document_base;
1105 this->document_filename = new_document_filename;
1106
1107 // In case of new document the filename is nullptr
1108 gchar *new_filename = this->document_filename ? this->document_filename : this->document_name;
1109 this->filename_set_signal.emit(new_filename);
1110}
1111
1119void SPDocument::setDocumentFilename(gchar const *filename)
1120{
1121 do_change_filename(filename, false);
1122}
1123
1128void SPDocument::changeFilenameAndHrefs(gchar const *filename)
1129{
1130 do_change_filename(filename, true);
1131}
1132
1133void SPDocument::bindObjectToId(char const *id, SPObject *object)
1134{
1135 GQuark idq = g_quark_from_string(id);
1136
1137 if (object) {
1138 if(object->getId()) {
1139 iddef.erase(object->getId());
1140 }
1141 auto ret = iddef.emplace(id, object);
1142 g_assert(ret.second);
1143 } else {
1144 auto it = iddef.find(id);
1145 g_assert(it != iddef.end());
1146 iddef.erase(it);
1147 }
1148
1149 auto pos = id_changed_signals.find(idq);
1150 if (pos != id_changed_signals.end()) {
1151 if (!pos->second.empty()) {
1152 pos->second.emit(object);
1153 } else { // discard unused signal
1154 id_changed_signals.erase(pos);
1155 }
1156 }
1157}
1158
1159SPObject *SPDocument::getObjectById(std::string const &id) const
1160{
1161 if (iddef.empty()) return nullptr;
1162
1163 if (auto rv = iddef.find(id); rv != iddef.end()) {
1164 return rv->second;
1165 } else if (_parent_document) {
1166 return _parent_document->getObjectById(id);
1167 } else if (_ref_document) {
1168 return _ref_document->getObjectById(id);
1169 }
1170
1171 return nullptr;
1172}
1173
1175{
1176 if (!id || iddef.empty()) return nullptr;
1177
1178 if (auto rv = iddef.find(id); rv != iddef.end()) {
1179 return rv->second;
1180 } else if (_parent_document) {
1181 return _parent_document->getObjectById(id);
1182 } else if (_ref_document) {
1183 return _ref_document->getObjectById(id);
1184 }
1185
1186 return nullptr;
1187}
1188
1189SPObject *SPDocument::getObjectByHref(std::string const &href) const
1190{
1191 if (iddef.empty()) return nullptr;
1192 auto id = href.substr(1);
1193 return getObjectById(id);
1194}
1195
1197{
1198 if (!href || href[0] == '\0') return nullptr;
1199 auto id = href + 1;
1200 return getObjectById(id);
1201}
1202
1203static void _getObjectsByClassRecursive(Glib::ustring const &klass, SPObject *parent, std::vector<SPObject*> &objects)
1204{
1205 if (!parent) return;
1206
1207 if (auto const temp = parent->getAttribute("class")) {
1208 std::istringstream classes(temp);
1209 Glib::ustring token;
1210 while (classes >> token) {
1211 // we can have multiple class
1212 if (classes.str() == " ") {
1213 token = "";
1214 continue;
1215 }
1216 if (token == klass) {
1217 objects.emplace_back(parent);
1218 break;
1219 }
1220 }
1221 }
1222
1223 // Check children
1224 for (auto &child : parent->children) {
1225 _getObjectsByClassRecursive(klass, &child, objects);
1226 }
1227}
1228
1229std::vector<SPObject*> SPDocument::getObjectsByClass(Glib::ustring const &klass) const
1230{
1231 if (klass.empty()) return {};
1232 std::vector<SPObject*> objects;
1233 _getObjectsByClassRecursive(klass, root, objects);
1234 return objects;
1235}
1236
1237static void _getObjectsByElementRecursive(Glib::ustring const &element,
1239 std::vector<SPObject*> &objects,
1240 bool custom)
1241{
1242 if (!parent) return;
1243
1244 Glib::ustring prefixed = custom ? "inkscape:" : "svg:";
1245 prefixed += element;
1246 if (parent->getRepr()->name() == prefixed) {
1247 objects.emplace_back(parent);
1248 }
1249
1250 // Check children
1251 for (auto &child : parent->children) {
1252 _getObjectsByElementRecursive(element, &child, objects, custom);
1253 }
1254}
1255
1256std::vector<SPObject*> SPDocument::getObjectsByElement(Glib::ustring const &element, bool custom) const
1257{
1258 if (element.empty()) return {};
1259 std::vector<SPObject*> objects;
1260 _getObjectsByElementRecursive(element, root, objects, custom);
1261 return objects;
1262}
1263
1265 CRSelEng *sel_eng, CRSimpleSel *simple_sel,
1266 std::vector<SPObject*> &objects)
1267{
1268 if (parent) {
1269 gboolean result = false;
1270 cr_sel_eng_matches_node(sel_eng, simple_sel, parent->getRepr(), &result);
1271 if (result) {
1272 objects.push_back(parent);
1273 }
1274
1275 // Check children
1276 for (auto &child : parent->children) {
1277 _getObjectsBySelectorRecursive(&child, sel_eng, simple_sel, objects);
1278 }
1279 }
1280}
1281
1282std::vector<SPObject*> SPDocument::getObjectsBySelector(Glib::ustring const &selector) const
1283{
1284 if (selector.empty()) return {};
1285
1286 static CRSelEng *sel_eng = nullptr;
1287 if (!sel_eng) {
1289 }
1290
1291 auto cr_selector = cr_selector_parse_from_buf(reinterpret_cast<guchar const*>(selector.c_str()), CR_UTF_8);
1292
1293 std::vector<SPObject*> objects;
1294 for (auto cur = cr_selector; cur; cur = cur->next) {
1295 if (cur->simple_sel) {
1296 _getObjectsBySelectorRecursive(root, sel_eng, cur->simple_sel, objects);
1297 }
1298 }
1299 cr_selector_destroy(cr_selector);
1300 return objects;
1301}
1302
1303// Note: Despite appearances, this implementation is allocation-free thanks to SSO.
1304std::string SPDocument::generate_unique_id(char const *prefix)
1305{
1306 auto result = std::string(prefix);
1307 auto const prefix_len = result.size();
1308
1309 while (true) {
1310 result.replace(prefix_len, std::string::npos, std::to_string(object_id_counter));
1311
1312 if (!getObjectById(result)) {
1313 break;
1314 }
1315
1317 }
1318
1319 return result;
1320}
1321
1323{
1324 if (object) {
1325 auto ret = reprdef.emplace(repr, object);
1326 g_assert(ret.second);
1327 } else {
1328 auto it = reprdef.find(repr);
1329 g_assert(it != reprdef.end());
1330 reprdef.erase(it);
1331 }
1333}
1334
1336{
1337 if (!repr) return nullptr;
1338 auto it = reprdef.find(repr);
1339 return it == reprdef.end() ? nullptr : it->second;
1340}
1341
1348std::vector<Glib::ustring> SPDocument::getLanguages() const
1349{
1350 std::vector<Glib::ustring> document_languages;
1351
1352 // get language from RDF
1353 gchar const *rdf_language = rdf_get_work_entity(this, rdf_find_entity("language"));
1354 if (rdf_language) {
1355 gchar *rdf_language_stripped = g_strstrip(g_strdup(rdf_language));
1356 if (strcmp(rdf_language_stripped, "") != 0) {
1357 document_languages.emplace_back(rdf_language_stripped);
1358 }
1359 g_free(rdf_language_stripped);
1360 }
1361
1362 // add languages from parent document
1363 if (_parent_document) {
1364 auto parent_languages = _parent_document->getLanguages();
1365
1366 // return parent languages directly if we aren't contributing any
1367 if (document_languages.empty()) {
1368 return parent_languages;
1369 }
1370
1371 // otherwise append parent's languages to what we already have
1372 std::move(parent_languages.begin(), parent_languages.end(),
1373 std::back_insert_iterator(document_languages));
1374
1375 // don't add languages from locale; parent already did that
1376 return document_languages;
1377 }
1378
1379 // get language from system locale (will also match the interface language preference as we set LANG accordingly)
1380 // TODO: This includes locales with encodings like "de_DE.UTF-8" - is this useful or should we skip these?
1381 // TODO: This includes the default "C" locale - is this useful or should we skip it?
1382 const gchar * const * names = g_get_language_names();
1383 for (int i=0; names[i]; i++) {
1384 document_languages.emplace_back(names[i]);
1385 }
1386
1387 return document_languages;
1388}
1389
1390/* Object modification root handler */
1391
1393{
1394 if (modified_connection.empty()) {
1396 Glib::signal_idle().connect(sigc::mem_fun(*this, &SPDocument::idle_handler),
1398 }
1399
1400 if (rerouting_connection.empty()) {
1402 Glib::signal_idle().connect(sigc::mem_fun(*this, &SPDocument::rerouting_handler),
1404 }
1405}
1406
1408{
1409 ctx->flags = 0;
1410 ctx->i2doc = Geom::identity();
1411 // Set up viewport in case svg has it defined as percentages
1412 if (root->viewBox_set) { // if set, take from viewBox
1413 ctx->viewport = root->viewBox;
1414 } else { // as a last resort, set size to A4
1416 }
1417 ctx->i2vp = Geom::identity();
1418}
1419
1421 if (!root) return false;
1422
1423 // detect Y-axis orientation change
1424 if (auto nv = getNamedView(); nv && is_yaxisdown() != nv->is_y_axis_down()) {
1425 return true;
1426 }
1427
1428 return false;
1429}
1430
1432 if (!root) return 0;
1433
1434 auto nv = getNamedView();
1435 auto shift = _doc2dt[5];
1436 if (nv->is_y_axis_down()) {
1437 _doc2dt[3] = 1;
1438 _doc2dt[5] = 0;
1439 }
1440 else {
1441 _doc2dt[3] = -1;
1443 }
1444
1445 return shift - _doc2dt[5];
1446}
1447
1453bool SPDocument::_updateDocument(int update_flags, unsigned int object_modified_tag)
1454{
1457
1458 // fix elements that rely on Y-axis orientation having certain value
1459 auto nv = getNamedView();
1460 nv->fix_guidelines();
1461 nv->updateViewPort();
1462 // repaint pages
1463 for (auto& child : nv->children) {
1464 child.requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1465 }
1466 // refresh display
1467 _y_axis_flipped.emit(shift);
1468 }
1469
1470 /* Process updates */
1471 if (root->uflags || root->mflags) {
1472 if (root->uflags) {
1473 SPItemCtx ctx;
1474 setupViewport(&ctx);
1475
1476 DocumentUndo::ScopedInsensitive _no_undo(this);
1477
1478 root->updateDisplay(&ctx, update_flags);
1479 }
1480 _emitModified(object_modified_tag);
1481 }
1482
1483 return !(root->uflags || root->mflags);
1484}
1485
1495gint SPDocument::ensureUpToDate(unsigned int object_modified_tag)
1496{
1497 // Bring the document up-to-date, specifically via the following:
1498 // 1a) Process all document updates.
1499 // 1b) When completed, process connector routing changes.
1500 // 2a) Process any updates resulting from connector reroutings.
1501 int counter = 32;
1502 for (unsigned int pass = 1; pass <= 2; ++pass) {
1503 // Process document updates.
1504 while (!_updateDocument(0, object_modified_tag)) {
1505 if (counter == 0) {
1506 g_warning("More than 32 iteration while updating document '%s'", document_filename);
1507 break;
1508 }
1509 counter--;
1510 }
1511 if (counter == 0)
1512 {
1513 break;
1514 }
1515
1516 // After updates on the first pass we get libavoid to process all the
1517 // changed objects and provide new routings. This may cause some objects
1518 // to be modified, hence the second update pass.
1519 if (pass == 1) {
1520 _router->processTransaction();
1521 }
1522 }
1523
1524 // Remove handlers
1525 modified_connection.disconnect();
1526 rerouting_connection.disconnect();
1527
1528 return (counter > 0);
1529}
1530
1535bool
1537{
1538 bool status = !_updateDocument(0); // method TRUE if it does NOT need further modification, so invert
1539 if (!status) {
1540 modified_connection.disconnect();
1541 }
1542 return status;
1543}
1544
1548bool
1550{
1551 // Process any queued movement actions and determine new routings for
1552 // object-avoiding connectors. Callbacks will be used to update and
1553 // redraw affected connectors.
1554 _router->processTransaction();
1555
1556 // We don't need to handle rerouting again until there are further
1557 // diagram updates.
1558 return false;
1559}
1560
1561static bool is_within(Geom::Rect const &area, Geom::Rect const &box)
1562{
1563 return area.contains(box);
1564}
1565
1566static bool overlaps(Geom::Rect const &area, Geom::Rect const &box)
1567{
1568 return area.intersects(box);
1569}
1570
1585static std::vector<SPItem*> &find_items_in_area(std::vector<SPItem*> &s,
1586 SPGroup *group, unsigned int dkey,
1587 Geom::Rect const &area,
1588 bool (*test)(Geom::Rect const &, Geom::Rect const &),
1589 bool take_hidden = false,
1590 bool take_insensitive = false,
1591 bool take_groups = true,
1592 bool enter_groups = false,
1593 bool enter_layers = true)
1594{
1595 g_return_val_if_fail(group, s);
1596
1597 for (auto& o: group->children) {
1598 if (auto item = cast<SPItem>(&o)) {
1599 if (!take_insensitive && item->isLocked()) {
1600 continue;
1601 }
1602
1603 if (!take_hidden && item->isHidden()) {
1604 continue;
1605 }
1606
1607 if (auto childgroup = cast<SPGroup>(item)) {
1608 bool is_layer = childgroup->effectiveLayerMode(dkey) == SPGroup::LAYER;
1609 if ((enter_layers && is_layer) || (enter_groups)) {
1610 s = find_items_in_area(s, childgroup, dkey, area, test, take_hidden, take_insensitive, take_groups, enter_groups, enter_layers);
1611 }
1612 if (!take_groups || (enter_layers && is_layer)) {
1613 continue;
1614 }
1615 }
1617 if (box && test(area, *box)) {
1618 s.push_back(item);
1619 }
1620 }
1621 }
1622 return s;
1623}
1624
1625SPItem *SPDocument::getItemFromListAtPointBottom(unsigned dkey, SPGroup *group, std::vector<SPItem*> const &list, Geom::Point const &p, bool take_insensitive)
1626{
1627 if (!group) {
1628 return nullptr;
1629 }
1630
1631 double const delta = Inkscape::Preferences::get()->getDouble("/options/cursortolerance/value", 1.0);
1632 std::optional<bool> outline;
1633
1634 for (auto &c: group->children) {
1635 if (auto item = cast<SPItem>(&c)) {
1636 if (auto di = item->get_arenaitem(dkey)) {
1637 if (!outline) {
1638 if (auto cid = di->drawing().getCanvasItemDrawing()) {
1639 auto canvas = cid->get_canvas();
1640 outline = canvas->canvas_point_in_outline_zone(p - canvas->get_pos());
1641 }
1642 }
1643 if (di->pick(p, delta, Inkscape::DrawingItem::PICK_STICKY | outline.value_or(false) * Inkscape::DrawingItem::PICK_OUTLINE) && (take_insensitive || item->isVisibleAndUnlocked(dkey))) {
1644 if (std::find(list.begin(), list.end(), item) != list.end()) {
1645 return item;
1646 }
1647 }
1648 }
1649
1650 if (auto group = cast<SPGroup>(item)) {
1651 if (auto ret = getItemFromListAtPointBottom(dkey, group, list, p, take_insensitive)) {
1652 return ret;
1653 }
1654 }
1655 }
1656 }
1657
1658 return nullptr;
1659}
1660
1661void _build_flat_item_list(std::deque<SPItem*> &cache, SPGroup *group, unsigned int dkey, bool into_groups, bool active_only)
1662{
1663 for (auto& o: group->children) {
1664 if (!is<SPItem>(&o)) {
1665 continue;
1666 }
1667
1668 if (is<SPGroup>(&o) && (cast<SPGroup>(&o)->effectiveLayerMode(dkey) == SPGroup::LAYER || into_groups)) {
1669 _build_flat_item_list(cache, cast<SPGroup>(&o), dkey, into_groups, active_only);
1670 } else {
1671 auto child = cast<SPItem>(&o);
1672 if (!active_only || child->isVisibleAndUnlocked(dkey)) {
1673 cache.push_front(child);
1674 }
1675 }
1676 }
1677}
1678
1683std::deque<SPItem*> const &SPDocument::get_flat_item_list(unsigned int dkey, bool into_groups, bool active_only) const
1684{
1685 // Build a caching key from our inputs
1686 using key_t = decltype(_node_cache)::key_type;
1687 auto const key = (key_t{dkey} << 2) | (into_groups << 1) | active_only;
1688
1689 auto const [it, inserted] = _node_cache.try_emplace(key);
1690 if (inserted) {
1691 _build_flat_item_list(it->second, root, dkey, into_groups, active_only);
1692 }
1693 return it->second;
1694}
1695
1705static std::vector<SPItem*> find_items_at_point(std::deque<SPItem*> const &nodes, unsigned dkey,
1706 Geom::Point const &p, int items_count = 0, SPItem *upto = nullptr)
1707{
1708 double const delta = Inkscape::Preferences::get()->getDouble("/options/cursortolerance/value", 1.0);
1709 std::optional<bool> outline;
1710
1711 std::vector<SPItem*> result;
1712
1713 bool seen_upto = !upto;
1714 for (auto node : nodes) {
1715 if (!seen_upto) {
1716 if (node == upto) {
1717 seen_upto = true;
1718 }
1719 continue;
1720 }
1721 if (auto di = node->get_arenaitem(dkey)) {
1722 if (!outline) {
1723 if (auto cid = di->drawing().getCanvasItemDrawing()) {
1724 auto canvas = cid->get_canvas();
1725 outline = canvas->canvas_point_in_outline_zone(p - canvas->get_pos());
1726 }
1727 }
1728 if (di->pick(p, delta, Inkscape::DrawingItem::PICK_STICKY | outline.value_or(false) * Inkscape::DrawingItem::PICK_OUTLINE)) {
1729 result.emplace_back(node);
1730 if (--items_count == 0) {
1731 break;
1732 }
1733 }
1734 }
1735 }
1736
1737 return result;
1738}
1739
1740static SPItem *find_item_at_point(std::deque<SPItem*> const &nodes, unsigned dkey, Geom::Point const &p, SPItem *upto = nullptr)
1741{
1742 auto items = find_items_at_point(nodes, dkey, p, 1, upto);
1743 if (items.empty()) {
1744 return nullptr;
1745 }
1746 return items.back();
1747}
1748
1753static SPItem *find_group_at_point(unsigned dkey, SPGroup *group, Geom::Point const &p)
1754{
1755 double const delta = Inkscape::Preferences::get()->getDouble("/options/cursortolerance/value", 1.0);
1756 std::optional<bool> outline;
1757
1758 for (auto &c : boost::adaptors::reverse(group->children)) {
1759 if (auto group = cast<SPGroup>(&c)) {
1760 if (group->effectiveLayerMode(dkey) == SPGroup::LAYER) {
1761 if (auto ret = find_group_at_point(dkey, group, p)) {
1762 return ret;
1763 }
1764 } else if (auto di = group->get_arenaitem(dkey)) {
1765 if (!outline) {
1766 if (auto cid = di->drawing().getCanvasItemDrawing()) {
1767 auto canvas = cid->get_canvas();
1768 outline = canvas->canvas_point_in_outline_zone(p - canvas->get_pos());
1769 }
1770 }
1771 if (di->pick(p, delta, Inkscape::DrawingItem::PICK_STICKY | outline.value_or(false) * Inkscape::DrawingItem::PICK_OUTLINE)) {
1772 return group;
1773 }
1774 }
1775 }
1776 }
1777
1778 return nullptr;
1779}
1780
1787std::vector<SPItem*> SPDocument::getItemsInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden, bool take_insensitive, bool take_groups, bool enter_groups, bool enter_layers) const
1788{
1789 std::vector<SPItem*> x;
1790 return find_items_in_area(x, this->root, dkey, box, is_within, take_hidden, take_insensitive, take_groups, enter_groups, enter_layers);
1791}
1792
1804std::vector<SPItem*> SPDocument::getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden, bool take_insensitive, bool take_groups, bool enter_groups, bool enter_layers) const
1805{
1806 std::vector<SPItem*> x;
1807 return find_items_in_area(x, this->root, dkey, box, overlaps, take_hidden, take_insensitive, take_groups, enter_groups, enter_layers);
1808}
1809
1810std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points, bool all_layers, bool topmost_only, size_t limit, bool active_only) const
1811{
1812 std::vector<SPItem*> result;
1814
1815 // When picking along the path, we don't want small objects close together
1816 // (such as hatching strokes) to obscure each other by their deltas,
1817 // so we temporarily set delta to a small value
1818 gdouble saved_delta = prefs->getDouble("/options/cursortolerance/value", 1.0);
1819 prefs->setDouble("/options/cursortolerance/value", 0.25);
1820
1821 auto &node_cache = get_flat_item_list(key, true, active_only);
1822
1823 SPObject *current_layer = nullptr;
1824 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1825 if(desktop){
1826 current_layer = desktop->layerManager().currentLayer();
1827 }
1828 size_t item_counter = 0;
1829 for(auto point : points) {
1830 std::vector<SPItem*> items = find_items_at_point(node_cache, key, point, topmost_only);
1831 for (SPItem *item : items) {
1832 if (item && result.end()==find(result.begin(), result.end(), item))
1833 if(all_layers || (desktop && desktop->layerManager().layerForObject(item) == current_layer)){
1834 result.push_back(item);
1835 item_counter++;
1836 //limit 0 = no limit
1837 if(item_counter == limit){
1838 prefs->setDouble("/options/cursortolerance/value", saved_delta);
1839 return result;
1840 }
1841 }
1842 }
1843 }
1844
1845 // and now we restore it back
1846 prefs->setDouble("/options/cursortolerance/value", saved_delta);
1847
1848 return result;
1849}
1850
1852 bool const into_groups, SPItem *upto) const
1853{
1854 return find_item_at_point(get_flat_item_list(key, into_groups, true), key, p, upto);
1855}
1856
1858{
1859 return find_group_at_point(key, this->root, p);
1860}
1861
1862// Resource management
1863
1864bool SPDocument::addResource(gchar const *key, SPObject *object)
1865{
1866 g_return_val_if_fail(key != nullptr, false);
1867 g_return_val_if_fail(*key != '\0', false);
1868 g_return_val_if_fail(object != nullptr, false);
1869
1870 bool result = false;
1871
1872 if ( !object->cloned ) {
1873 std::vector<SPObject *> rlist = resources[key];
1874 g_return_val_if_fail(std::find(rlist.begin(),rlist.end(),object) == rlist.end(), false);
1875 resources[key].insert(resources[key].begin(),object);
1876
1877 GQuark q = g_quark_from_string(key);
1878
1879 /*in general, do not send signal if the object has no id (yet),
1880 it means the object is not completely built.
1881 (happens when pasting swatches across documents, cf bug 1495106)
1882 [this check should be more generally presend on emit() calls since
1883 the backtrace is unusable with crashed from this cause]
1884 */
1885 if (object->getId() || is<SPGroup>(object) || is<SPPage>(object)) {
1886 resources_changed_signals[q].emit();
1887 } else {
1888 pending_resource_changes.emplace(q);
1889 }
1890
1891 result = true;
1892 }
1893
1894 return result;
1895}
1896
1897bool SPDocument::removeResource(gchar const *key, SPObject *object)
1898{
1899 g_return_val_if_fail(key != nullptr, false);
1900 g_return_val_if_fail(*key != '\0', false);
1901 g_return_val_if_fail(object != nullptr, false);
1902
1903 bool result = false;
1904
1905 if ( !object->cloned ) {
1906 std::vector<SPObject *> rlist = resources[key];
1907 g_return_val_if_fail(!rlist.empty(), false);
1908 std::vector<SPObject*>::iterator it = std::find(resources[key].begin(),resources[key].end(),object);
1909 g_return_val_if_fail(it != rlist.end(), false);
1910 resources[key].erase(it);
1911
1912 GQuark q = g_quark_from_string(key);
1913 resources_changed_signals[q].emit();
1914
1915 result = true;
1916 }
1917
1918 return result;
1919}
1920
1921std::vector<SPObject *> const SPDocument::getResourceList(gchar const *key)
1922{
1923 std::vector<SPObject *> emptyset;
1924 g_return_val_if_fail(key != nullptr, emptyset);
1925 g_return_val_if_fail(*key != '\0', emptyset);
1926
1927 return resources[key];
1928}
1929
1931{
1932 while (!pending_resource_changes.empty()) {
1933 auto q = pending_resource_changes.front();
1935 resources_changed_signals[q].emit();
1936 }
1937}
1938
1939/* Helpers */
1940
1941static unsigned int count_objects_recursive(SPObject *obj, unsigned int count)
1942{
1943 count++; // obj itself
1944
1945 for (auto& i: obj->children) {
1946 count = count_objects_recursive(&i, count);
1947 }
1948
1949 return count;
1950}
1951
1958static unsigned int objects_in_document(SPDocument *document)
1959{
1960 return count_objects_recursive(document->getRoot(), 0);
1961}
1962
1969{
1970 if (is<SPDefs>(obj)) {
1971 for (auto& def: obj->children) {
1972 // fixme: some inkscape-internal nodes in the future might not be collectable
1973 def.requestOrphanCollection();
1974 }
1975 } else {
1976 for (auto& i: obj->children) {
1978 }
1979 }
1980}
1981
1988{
1989 unsigned int start = objects_in_document(this);
1990 unsigned int end;
1991 unsigned int newend = start;
1992
1993 unsigned int iterations = 0;
1994
1995 do {
1996 end = newend;
1997
1999 this->collectOrphans();
2000 iterations++;
2001
2002 newend = objects_in_document(this);
2003
2004 } while (iterations < 100 && newend < end);
2005 // We stop if vacuum_document_recursive doesn't remove any more objects or after 100 iterations, whichever occurs first.
2006
2007 return start - newend;
2008}
2009
2016{
2017 modified_since_save = modified;
2018 modified_since_autosave = modified;
2020}
2021
2028{
2030 Inkscape::XML::Node *target_defs = this->getDefs()->getRepr();
2031 std::vector<Inkscape::XML::Node const *> defsNodes = sp_repr_lookup_name_many(root, "svg:defs");
2032
2033 prevent_id_clashes(source, this);
2034
2035 for (auto & defsNode : defsNodes) {
2036 _importDefsNode(source, const_cast<Inkscape::XML::Node *>(defsNode), target_defs);
2037 }
2038}
2039
2041{
2042 int stagger=0;
2043
2044 /* Note, "clipboard" throughout the comments means "the document that is either the clipboard
2045 or an imported document", as importDefs is called in both contexts.
2046
2047 The order of the records in the clipboard is unpredictable and there may be both
2048 forward and backwards references to other records within it. There may be definitions in
2049 the clipboard that duplicate definitions in the present document OR that duplicate other
2050 definitions in the clipboard. (Inkscape will not have created these, but they may be read
2051 in from other SVG sources.)
2052
2053 There are 3 passes to clean this up:
2054
2055 In the first find and mark definitions in the clipboard that are duplicates of those in the
2056 present document. Change the ID to "RESERVED_FOR_INKSCAPE_DUPLICATE_DEF_XXXXXXXXX".
2057 (Inkscape will not reuse an ID, and the XXXXXXXXX keeps it from automatically creating new ones.)
2058 References in the clipboard to the old clipboard name are converted to the name used
2059 in the current document.
2060
2061 In the second find and mark definitions in the clipboard that are duplicates of earlier
2062 definitions in the clipbard. Unfortunately this is O(n^2) and could be very slow for a large
2063 SVG with thousands of definitions. As before, references are adjusted to reflect the name
2064 going forward.
2065
2066 In the final cycle copy over those records not marked with that ID.
2067
2068 If an SVG file uses the special ID it will cause problems!
2069
2070 If this function is called because of the paste of a true clipboard the caller will have passed in a
2071 COPY of the clipboard items. That is good, because this routine modifies that document. If the calling
2072 behavior ever changes, so that the same document is passed in on multiple pastes, this routine will break
2073 as in the following example:
2074 1. Paste clipboard containing B same as A into document containing A. Result, B is dropped
2075 and all references to it will point to A.
2076 2. Paste same clipboard into a new document. It will not contain A, so there will be unsatisfied
2077 references in that window.
2078 */
2079
2080 std::string DuplicateDefString = "RESERVED_FOR_INKSCAPE_DUPLICATE_DEF";
2081
2082 /* First pass: remove duplicates in clipboard of definitions in document */
2083 for (Inkscape::XML::Node *def = defs->firstChild() ; def ; def = def->next()) {
2084 if(def->type() != Inkscape::XML::NodeType::ELEMENT_NODE)continue;
2085 /* If this clipboard has been pasted into one document, and is now being pasted into another,
2086 or pasted again into the same, it will already have been processed. If we detect that then
2087 skip the rest of this pass. */
2088 Glib::ustring defid = def->attribute("id");
2089 if( defid.find( DuplicateDefString ) != Glib::ustring::npos )break;
2090
2091 SPObject *src = source->getObjectByRepr(def);
2092
2093 // Prevent duplicates of solid swatches by checking if equivalent swatch already exists
2094 auto s_gr = cast<SPGradient>(src);
2095 auto s_lpeobj = cast<LivePathEffectObject>(src);
2096 if (src && (s_gr || s_lpeobj)) {
2097 for (auto& trg: getDefs()->children) {
2098 auto t_gr = cast<SPGradient>(&trg);
2099 if (src != &trg && s_gr && t_gr) {
2100 if (s_gr->isEquivalent(t_gr)) {
2101 // Change object references to the existing equivalent gradient
2102 Glib::ustring newid = trg.getId();
2103 if (newid != defid) { // id could be the same if it is a second paste into the same document
2104 change_def_references(src, &trg);
2105 }
2106 gchar *longid = g_strdup_printf("%s_%9.9d", DuplicateDefString.c_str(), stagger++);
2107 def->setAttribute("id", longid);
2108 g_free(longid);
2109 // do NOT break here, there could be more than 1 duplicate!
2110 }
2111 }
2112 auto t_lpeobj = cast<LivePathEffectObject>(&trg);
2113 if (src != &trg && s_lpeobj && t_lpeobj) {
2114 if (t_lpeobj->is_similar(s_lpeobj)) {
2115 // Change object references to the existing equivalent gradient
2116 Glib::ustring newid = trg.getId();
2117 if (newid != defid) { // id could be the same if it is a second paste into the same document
2118 change_def_references(src, &trg);
2119 }
2120 gchar *longid = g_strdup_printf("%s_%9.9d", DuplicateDefString.c_str(), stagger++);
2121 def->setAttribute("id", longid);
2122 g_free(longid);
2123 // do NOT break here, there could be more than 1 duplicate!
2124 }
2125 }
2126 }
2127 }
2128 }
2129
2130 /* Second pass: remove duplicates in clipboard of earlier definitions in clipboard */
2131 for (Inkscape::XML::Node *def = defs->firstChild() ; def ; def = def->next()) {
2132 if(def->type() != Inkscape::XML::NodeType::ELEMENT_NODE)continue;
2133 Glib::ustring defid = def->attribute("id");
2134 if( defid.find( DuplicateDefString ) != Glib::ustring::npos )continue; // this one already handled
2135 SPObject *src = source->getObjectByRepr(def);
2136 auto s_lpeobj = cast<LivePathEffectObject>(src);
2137 auto s_gr = cast<SPGradient>(src);
2138 if (src && (s_gr || s_lpeobj)) {
2139 for (Inkscape::XML::Node *laterDef = def->next() ; laterDef ; laterDef = laterDef->next()) {
2140 SPObject *trg = source->getObjectByRepr(laterDef);
2141 auto t_gr = cast<SPGradient>(trg);
2142 if (trg && (src != trg) && s_gr && t_gr) {
2143 Glib::ustring newid = trg->getId();
2144 if (newid.find(DuplicateDefString) != Glib::ustring::npos)
2145 continue; // this one already handled
2146 if (t_gr && s_gr->isEquivalent(t_gr)) {
2147 // Change object references to the existing equivalent gradient
2148 // two id's in the clipboard should never be the same, so always change references
2149 change_def_references(trg, src);
2150 gchar *longid = g_strdup_printf("%s_%9.9d", DuplicateDefString.c_str(), stagger++);
2151 laterDef->setAttribute("id", longid);
2152 g_free(longid);
2153 // do NOT break here, there could be more than 1 duplicate!
2154 }
2155 }
2156 auto t_lpeobj = cast<LivePathEffectObject>(trg);
2157 if (trg && (src != trg) && s_lpeobj && t_lpeobj) {
2158 Glib::ustring newid = trg->getId();
2159 if (newid.find(DuplicateDefString) != Glib::ustring::npos)
2160 continue; // this one already handled
2161 if (t_lpeobj->is_similar(s_lpeobj)) {
2162 // Change object references to the existing equivalent gradient
2163 // two id's in the clipboard should never be the same, so always change references
2164 change_def_references(trg, src);
2165 gchar *longid = g_strdup_printf("%s_%9.9d", DuplicateDefString.c_str(), stagger++);
2166 laterDef->setAttribute("id", longid);
2167 g_free(longid);
2168 // do NOT break here, there could be more than 1 duplicate!
2169 }
2170 }
2171 }
2172 }
2173 }
2174
2175 /* Final pass: copy over those parts which are not duplicates */
2176 for (Inkscape::XML::Node *def = defs->firstChild() ; def ; def = def->next()) {
2177 if(def->type() != Inkscape::XML::NodeType::ELEMENT_NODE)continue;
2178
2179 /* Ignore duplicate defs marked in the first pass */
2180 Glib::ustring defid = def->attribute("id");
2181 if( defid.find( DuplicateDefString ) != Glib::ustring::npos )continue;
2182
2183 bool duplicate = false;
2184 SPObject *src = source->getObjectByRepr(def);
2185
2186 // Prevent duplication of symbols... could be more clever.
2187 // The tag "_inkscape_duplicate" is added to "id" by ClipboardManagerImpl::copySymbol().
2188 // We assume that symbols are in defs section (not required by SVG spec).
2189 if (src && is<SPSymbol>(src)) {
2190
2191 Glib::ustring id = src->getRepr()->attribute("id");
2192 size_t pos = id.find( "_inkscape_duplicate" );
2193 if( pos != Glib::ustring::npos ) {
2194
2195 // This is our symbol, now get rid of tag
2196 id.erase( pos );
2197
2198 // Check that it really is a duplicate
2199 for (auto& trg: getDefs()->children) {
2200 if (is<SPSymbol>(&trg) && src != &trg) {
2201 Glib::ustring id2 = trg.getRepr()->attribute("id");
2202
2203 if( !id.compare( id2 ) ) {
2204 duplicate = true;
2205 break;
2206 }
2207 }
2208 }
2209 if ( !duplicate ) {
2210 src->setAttribute("id", id);
2211 }
2212 }
2213 }
2214
2215 if (!duplicate) {
2216 Inkscape::XML::Node * dup = def->duplicate(this->getReprDoc());
2217 target_defs->appendChild(dup);
2219 }
2220 }
2221}
2222
2223// Signals ------------------------------
2224
2225void
2230
2231void
2236
2237sigc::connection SPDocument::connectDestroy(sigc::signal<void ()>::slot_type slot)
2238{
2239 return destroySignal.connect(slot);
2240}
2241
2242sigc::connection SPDocument::connectModified(SPDocument::ModifiedSignal::slot_type slot)
2243{
2244 return modified_signal.connect(slot);
2245}
2246
2247sigc::connection SPDocument::connectFilenameSet(SPDocument::FilenameSetSignal::slot_type slot)
2248{
2249 return filename_set_signal.connect(slot);
2250}
2251
2252sigc::connection SPDocument::connectCommit(SPDocument::CommitSignal::slot_type slot)
2253{
2254 return commit_signal.connect(slot);
2255}
2256
2257sigc::connection SPDocument::connectBeforeCommit(SPDocument::BeforeCommitSignal::slot_type slot)
2258{
2259 return before_commit_signal.connect(slot);
2260}
2261
2262sigc::connection SPDocument::connectIdChanged(gchar const *id,
2263 SPDocument::IDChangedSignal::slot_type slot)
2264{
2265 return id_changed_signals[g_quark_from_string(id)].connect(slot);
2266}
2267
2268sigc::connection SPDocument::connectResourcesChanged(gchar const *key,
2269 SPDocument::ResourcesChangedSignal::slot_type slot)
2270{
2271 GQuark q = g_quark_from_string(key);
2272 return resources_changed_signals[q].connect(slot);
2273}
2274
2275sigc::connection SPDocument::connectReconstructionStart(SPDocument::ReconstructionStart::slot_type slot, bool first)
2276{
2277 if (first)
2278 return _reconstruction_start_signal.connect_first(slot);
2279 return _reconstruction_start_signal.connect(slot);
2280}
2281
2282sigc::connection
2283SPDocument::connectReconstructionFinish(SPDocument::ReconstructionFinish::slot_type slot)
2284{
2285 return _reconstruction_finish_signal.connect(slot);
2286}
2287
2288sigc::connection SPDocument::connectSavedOrModified(sigc::slot<void ()> &&slot)
2289{
2290 return _saved_or_modified_signal.connect(std::move(slot));
2291}
2292
2293void SPDocument::_emitModified(unsigned int object_modified_tag) {
2294 static guint const flags = SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG;
2295 root->emitModified(object_modified_tag);
2296 modified_signal.emit(flags);
2298}
2299
2300void
2302{
2303 // printf("Starting Reconstruction\n");
2305}
2306
2307void
2309{
2310 // printf("Finishing Reconstruction\n");
2312 // indicates that gradients are reloaded (to rebuild the Auto palette)
2313 resources_changed_signals[g_quark_from_string("gradient")].emit();
2314 resources_changed_signals[g_quark_from_string("filter")].emit();
2315
2320}
2321
2323 _ref_document = document;
2324}
2325
2329
2331 g_assert(inject_into);
2332 _parent = inject_into;
2333 _parent->set_reference_document(reference);
2334}
2335
2337 _parent->set_reference_document(nullptr);
2338}
2339
2341 if (auto nv = getNamedView()) {
2342 return nv->get_origin_follows_page();
2343 }
2344 // named view not ready yet during document build; sp-grid may ask for origin correction
2345 return true;
2346}
2347
2351
2352/*
2353 Local Variables:
2354 mode:c++
2355 c-file-style:"stroustrup"
2356 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2357 indent-tabs-mode:nil
2358 fill-column:99
2359 End:
2360*/
2361// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
int test()
Definition 2junctions.cpp:5
double scale
Definition aa.cpp:228
void add_actions_edit_document(SPDocument *document)
Authors: Sushant A A sushant.co19@gmail.com
void add_document_actions_effect(SPDocument *doc)
Authors: Sushant A A sushant.co19@gmail.com
void add_actions_pages(SPDocument *doc)
void add_actions_processing(SPDocument *doc)
void add_actions_undo_document(SPDocument *document)
Actions for Undo/Redo tied to document.
uint64_t page
Definition canvas.cpp:171
Inkscape canvas widget.
3x3 matrix representing an affine transformation.
Definition affine.h:70
static CRect from_xywh(Coord x, Coord y, Coord w, Coord h)
Create rectangle from origin and dimensions.
C right() const
Return rightmost coordinate of the rectangle (+X is to the right).
bool contains(GenericRect< C > const &r) const
Check whether the rectangle includes all points in the given rectangle.
bool intersects(GenericRect< C > const &r) const
Check whether the rectangles have any common points.
C top() const
Return top coordinate of the rectangle (+Y is downwards).
void setMax(CPoint const &p)
Set the lower right point of the rectangle.
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
C bottom() const
Return bottom coordinate of the rectangle (+Y is downwards).
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
void add(UndoStackObserver &observer)
Add an UndoStackObserver.
void remove(UndoStackObserver &observer)
Remove an UndoStackObserver.
RAII-style mechanism for creating a temporary undo-insensitive context.
SPObject * layerForObject(SPObject *object)
Return layer that contains object.
SPGroup * currentLayer() const
Returns current top layer.
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
static Preferences * get()
Access the singleton Preferences object.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
Observes changes made to the undo and redo stacks.
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:525
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * next()=0
Get the next sibling of this node.
virtual Node * prev()=0
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
virtual char const * name() const =0
Get the name of the element node.
virtual const AttributeVector & attributeList() const =0
Get a list of the node's attributes.
void addChildAtPos(Node *child, unsigned pos)
Insert another node as a child of this node.
Definition node.h:430
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual Node * firstChild()=0
Get the first child of this node.
virtual void mergeFrom(Node const *src, char const *key, bool extension=false, bool clean=false)=0
Merge all children of another node with the current.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
virtual void removeChild(Node *child)=0
Remove a child of this node.
virtual Node * lastChild()=0
Get the last child of this node.
virtual Node * root()=0
Get the root node of this node's document.
static Persp3D * document_first_persp(SPDocument *document)
Definition persp3d.cpp:248
To do: update description of desktop.
Definition desktop.h:149
Inkscape::LayerManager & layerManager()
Definition desktop.h:287
SVGLength height
SVGLength width
Typed SVG document implementation.
Definition document.h:102
bool get_origin_follows_page()
std::vector< SPObject * > _collection_queue
Orphans.
Definition document.h:448
void scaleContentBy(Geom::Scale const &delta)
Scale the content, used by file-update and document properties when modifying the the document's view...
Definition document.cpp:795
void setDocumentFilename(char const *filename)
A filename, or NULL.
CRCascade * style_cascade
Definition document.h:405
static std::unique_ptr< SPDocument > createDoc(Inkscape::XML::Document *rdoc, char const *filename, char const *base, char const *name, bool keepalive, SPDocument *parent=nullptr)
Definition document.cpp:340
void collectOrphans()
Definition document.cpp:329
std::vector< SPObject * > getObjectsByClass(Glib::ustring const &klass) const
void prunePages(const std::string &page_nums, bool invert=false)
Remove pages in bulk using the integer range format "1,2,3-4" etc.
Definition document.cpp:310
std::vector< std::unique_ptr< SPDocument > > _child_documents
Definition document.h:396
void setCurrentPersp3D(Persp3D *const persp)
Definition document.cpp:268
Inkscape::XML::Document * rdoc
Our Inkscape::XML::Document.
Definition document.h:390
const Geom::Affine & doc2dt() const
Document to desktop coordinate transformation.
Definition document.cpp:935
void importDefs(SPDocument *source)
Paste SVG defs from the document retrieved from the clipboard or imported document into the active do...
bool removeResource(char const *key, SPObject *object)
Inkscape::XML::Event * partial
Definition document.h:432
SPDocument::ModifiedSignal modified_signal
Definition document.h:471
std::vector< SPItem * > getItemsAtPoints(unsigned const key, std::vector< Geom::Point > points, bool all_layers=true, bool topmost_only=true, size_t limit=0, bool active_only=true) const
Glib::RefPtr< Gio::SimpleActionGroup > action_group
Definition document.h:451
std::map< std::string, std::vector< SPObject * > > resources
Definition document.h:505
static int get_new_doc_number()
Definition document.cpp:218
void _emitModified(unsigned int object_modified_tag=0)
static std::unique_ptr< SPDocument > createNewDocFromMem(std::span< char const > buffer, bool keepalive, std::string const &filename="")
Definition document.cpp:718
void setWidthAndHeight(const Inkscape::Util::Quantity &width, const Inkscape::Util::Quantity &height, bool changeSize=true)
Definition document.cpp:819
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:201
static SPItem * getItemFromListAtPointBottom(unsigned int dkey, SPGroup *group, const std::vector< SPItem * > &list, Geom::Point const &p, bool take_insensitive=false)
Returns the bottommost item from the list which is at the point, or NULL if none.
SPDocument::CommitSignal commit_signal
Definition document.h:475
double update_desktop_affine()
Update desktop transform after Y-axis orientation change.
std::unique_ptr< SPDocument > copy() const
Create a copy of the document, useful for modifying during save & export.
Definition document.cpp:508
SPDocument * get_reference_document()
sigc::connection connectResourcesChanged(char const *key, SPDocument::ResourcesChangedSignal::slot_type slot)
void update_lpobjs()
Definition document.cpp:659
std::string generate_unique_id(char const *prefix)
Generate a document-wide unique id.
bool addResource(char const *key, SPObject *object)
bool idle_handler()
An idle handler to update the document.
std::unique_ptr< Inkscape::Colors::DocumentCMS > _cms_manager
Definition document.h:174
SPObject * getObjectById(std::string const &id) const
sigc::signal< void()> destroySignal
Definition document.h:480
unsigned long _serial
Definition document.h:442
void setWidth(const Inkscape::Util::Quantity &width, bool changeSize=true)
Definition document.cpp:874
void _importDefsNode(SPDocument *source, Inkscape::XML::Node *defs, Inkscape::XML::Node *target_defs)
char * document_base
To be used for resolving relative hrefs.
Definition document.h:412
SPDocument * _parent_document
Definition document.h:398
SPDocument::FilenameSetSignal filename_set_signal
Definition document.h:472
Inkscape::CompositeUndoStackObserver undoStackObservers
Definition document.h:436
void setModifiedSinceSave(bool const modified=true)
Indicate to the user if the document has been modified since the last save by displaying a "*" in fro...
void setupViewport(SPItemCtx *ctx)
void fitToRect(Geom::Rect const &rect, bool with_margins=false)
Given a Geom::Rect that may, for example, correspond to the bbox of an object, this function fits the...
Inkscape::Util::Quantity getWidth() const
Definition document.cpp:857
void process_pending_resource_changes()
void addUndoObserver(Inkscape::UndoStackObserver &observer)
Add the observer to the document's undo listener The caller is in charge of freeing any memory alloca...
sigc::connection connectSavedOrModified(sigc::slot< void()> &&slot)
std::deque< SPItem * > const & get_flat_item_list(unsigned int dkey, bool into_groups, bool active_only) const
Turn the SVG DOM into a cached flat list of nodes that can be searched from top-down.
std::unique_ptr< Inkscape::PageManager > _page_manager
Definition document.h:173
std::vector< SPObject * > const getResourceList(char const *key)
bool seeking
Definition document.h:441
bool sensitive
Definition document.h:431
Geom::Affine _doc2dt
Definition document.h:408
Geom::Point getDimensions() const
Definition document.cpp:973
bool modified_since_save
Definition document.h:384
sigc::connection _desktop_activated_connection
Definition document.h:478
SPRoot * root
Our SPRoot.
Definition document.h:393
bool rerouting_handler()
An idle handler to reroute connectors in the document.
Persp3D * current_persp3d
Currently 'active' perspective (to which, e.g., newly created boxes are attached)
Definition document.h:423
sigc::connection connectBeforeCommit(BeforeCommitSignal::slot_type slot)
void requestModified()
Geom::Rect getViewBox() const
Definition document.cpp:944
std::vector< SPItem * > getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden=false, bool take_insensitive=false, bool take_groups=true, bool enter_groups=false, bool enter_layers=true) const
Get items whose bounding box overlaps with given area.
Inkscape::XML::Node * getReprRoot()
Definition document.h:207
Geom::OptRect preferredBounds() const
Definition document.cpp:978
std::unique_ptr< Inkscape::Selection > _selection
Definition document.h:378
Geom::OptRect pageBounds()
Returns the position of the selected page or the preferredBounds()
Definition document.cpp:986
SPDocument::BeforeCommitSignal before_commit_signal
Definition document.h:476
SPDocument::ReconstructionStart _reconstruction_start_signal
Definition document.h:473
sigc::connection connectModified(ModifiedSignal::slot_type slot)
sigc::signal< void(double)> _y_axis_flipped
Definition document.h:482
unsigned int vacuumDocument()
Remove unused definitions etc.
void setHeight(const Inkscape::Util::Quantity &height, bool changeSize=true)
Definition document.cpp:913
sigc::connection modified_connection
Definition document.h:386
void getPerspectivesInDefs(std::vector< Persp3D * > &list) const
Definition document.cpp:273
void setDocumentBase(char const *document_base)
To be used for resolving relative hrefs.
ResourcesChangedSignalMap resources_changed_signals
Definition document.h:506
void emitReconstructionFinish()
bool has_yaxis_orientation_changed()
Detect Y-axis orientation change.
double yaxisdir() const
"1" if the desktop Y-axis points down, "-1" if it points up.
Definition document.h:277
std::vector< SPItem * > getItemsInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden=false, bool take_insensitive=false, bool take_groups=true, bool enter_groups=false, bool enter_layers=true) const
Return list of items, contained in box.
void bindObjectToId(char const *id, SPObject *object)
std::queue< GQuark > pending_resource_changes
Definition document.h:176
bool modified_since_autosave
Definition document.h:385
void removeUndoObserver(Inkscape::UndoStackObserver &observer)
bool _updateDocument(int flags, unsigned int object_modified_tag=0)
Tries to update the document state based on the modified and "update required" flags,...
bool keepalive
false if temporary document (e.g. to generate a PNG for display in a dialog).
Definition document.h:382
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:245
SPDocument * createChildDoc(std::string const &filename)
Fetches a document and attaches it to the current document as a child href.
Definition document.cpp:619
std::map< unsigned long, std::deque< SPItem * > > _node_cache
Definition document.h:420
void clearNodeCache()
Definition document.h:190
void set_origin_follows_page(bool on)
std::vector< SPObject * > getObjectsByElement(Glib::ustring const &element, bool custom=false) const
char * document_filename
A filename, or NULL.
Definition document.h:411
static std::unique_ptr< SPDocument > createNewDoc(char const *filename, bool keepalive, bool make_new=false, SPDocument *parent=nullptr)
Fetches document from filename, or creates new, if NULL; public document appears in document list.
Definition document.cpp:668
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:212
sigc::connection connectReconstructionFinish(ReconstructionFinish::slot_type slot)
SPDocument * _searchForChild(std::string const &filename, SPDocument const *avoid=nullptr)
Definition document.cpp:640
sigc::signal< void()> _saved_or_modified_signal
Definition document.h:481
unsigned long object_id_counter
Definition document.h:444
void rebase(Inkscape::XML::Document *new_xmldoc, bool keep_namedview=true)
Definition document.cpp:565
SPDocument::ReconstructionFinish _reconstruction_finish_signal
Definition document.h:474
Inkscape::ConsoleOutputUndoObserver console_output_undo_observer
Definition document.h:439
void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object)
void setDocumentScale(const double scaleX, const double scaleY)
Sets document scale (by changing viewBox)
Definition document.cpp:750
char * document_name
basename or other human-readable label for the document.
Definition document.h:413
std::vector< Glib::ustring > getLanguages() const
Returns preferred document languages (from most to least preferred)
std::unique_ptr< Inkscape::EventLog > _event_log
Definition document.h:428
void queueForOrphanCollection(SPObject *object)
Definition document.cpp:321
sigc::connection connectFilenameSet(FilenameSetSignal::slot_type slot)
void do_change_filename(char const *const filename, bool const rebase)
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
std::unique_ptr< Avoid::Router > _router
Definition document.h:377
SPDocument * _ref_document
Definition document.h:402
bool is_yaxisdown() const
True if the desktop Y-axis points down, false if it points up.
Definition document.h:275
SPObject * getObjectByHref(std::string const &href) const
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
void set_reference_document(SPDocument *document)
Set the reference document object.
IDChangedSignalMap id_changed_signals
Dictionary of signals for id changes.
Definition document.h:469
sigc::connection connectIdChanged(const char *id, IDChangedSignal::slot_type slot)
sigc::connection connectReconstructionStart(ReconstructionStart::slot_type slot, bool first=false)
SPNamedView * getNamedView()
Get the namedview for this document, creates it if it's not found.
Definition document.cpp:233
void changeFilenameAndHrefs(char const *filename)
Changes the base, name and filename members of document, and updates any relative hrefs in the docume...
Persp3D * getCurrentPersp3D()
Definition document.cpp:253
Geom::Scale getDocumentScale(bool computed=true) const
Returns document scale as defined by width/height (in pixels) and viewBox (real world to user-units).
Definition document.cpp:773
void setViewBox()
Set default viewbox calculated from document properties.
Definition document.cpp:958
SPItem * getGroupAtPoint(unsigned int key, Geom::Point const &p) const
Inkscape::Util::Quantity getHeight() const
Definition document.cpp:896
void setPages(bool enabled)
void SPDocument::initialize_current_persp3d() { this->current_persp3d = Persp3D::document_first_persp...
Definition document.cpp:295
void emitReconstructionStart()
std::map< std::string, SPObject * > iddef
Definition document.h:416
sigc::connection connectCommit(CommitSignal::slot_type slot)
Inkscape::XML::Node * rroot
Root element of Inkscape::XML::Document.
Definition document.h:391
sigc::connection connectDestroy(sigc::signal< void()>::slot_type slot)
Inkscape::XML::Node * getReprNamedView()
Definition document.cpp:223
SPItem * getItemAtPoint(unsigned int key, Geom::Point const &p, bool into_groups, SPItem *upto=nullptr) const
sigc::connection rerouting_connection
Definition document.h:387
Inkscape::Util::Unit const * getDisplayUnit()
guaranteed not to return nullptr
Definition document.cpp:741
Persp3DImpl * current_persp3d_impl
Definition document.h:424
std::vector< SPObject * > getObjectsBySelector(Glib::ustring const &selector) const
std::map< Inkscape::XML::Node *, SPObject * > reprdef
Definition document.h:417
void scaleChildItemsRec(Geom::Scale const &sc, Geom::Point const &p, bool noRecurse)
void translateChildItems(Geom::Translate const &tr)
LayerMode effectiveLayerMode(unsigned int display_key) const
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::OptRect documentVisualBounds() const
Get item's visual bbox in document coordinate system.
Definition sp-item.cpp:1005
Inkscape::DrawingItem * get_arenaitem(unsigned key) const
Return the arenaitem corresponding to the given item in the display with the given key.
Definition sp-item.cpp:1835
bool isHidden() const
Definition sp-item.cpp:222
bool isVisibleAndUnlocked() const
Definition sp-item.cpp:197
bool isLocked() const
Definition sp-item.cpp:205
void set_origin_follows_page(bool on)
bool is_y_axis_down() const
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
unsigned int uflags
Definition sp-object.h:182
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
unsigned int mflags
Definition sp-object.h:183
void invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
SPDocument * document
Definition sp-object.h:188
void updateDisplay(SPCtx *ctx, unsigned int flags)
Updates the object's display immediately.
void emitModified(unsigned int flags)
Emits the MODIFIED signal with the object's flags.
char const * getId() const
Returns the objects current ID string.
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
unsigned int cloned
Definition sp-object.h:180
ChildrenList children
Definition sp-object.h:907
void releaseReferences()
Cleans up an SPObject, releasing its references and requesting that references to it be released.
SPDefs * defs
Primary <defs> element where we put new defs (patterns, gradients etc.).
Definition sp-root.h:54
Geom::Rect viewBox
Definition viewbox.h:35
bool viewBox_set
Definition viewbox.h:34
float value
Definition svg-length.h:47
Unit unit
Definition svg-length.h:44
float computed
Definition svg-length.h:50
NodeObserver const * observer
RootCluster root
void cr_cascade_unref(CRCascade *a_this)
CRCascade * cr_cascade_new(CRStyleSheet *a_author_sheet, CRStyleSheet *a_user_sheet, CRStyleSheet *a_ua_sheet)
The declaration of the CRSelEng class.
typedefG_BEGIN_DECLS struct _CRSelEng CRSelEng
Definition cr-sel-eng.h:43
CRSelEng * cr_sel_eng_new(CRNodeIface const *)
enum CRStatus cr_sel_eng_matches_node(CRSelEng *a_this, CRSimpleSel *a_sel, CRXMLNodePtr a_node, gboolean *a_result)
The declaration file of the CRSelector file.
CRSelector * cr_selector_parse_from_buf(const guchar *a_char_buf, enum CREncoding a_enc)
void cr_selector_destroy(CRSelector *a_this)
@ CR_UTF_8
Definition cr-utils.h:89
TODO: insert short description here.
Css & result
double c[8][4]
bool custom
A class to hold:
Editable view implementation.
static char const *const parent
Definition dir-util.cpp:70
char * prepend_current_dir_if_relative(gchar const *uri)
Definition dir-util.cpp:229
TODO: insert short description here.
TODO: insert short description here.
static std::vector< SPItem * > & find_items_in_area(std::vector< SPItem * > &s, SPGroup *group, unsigned int dkey, Geom::Rect const &area, bool(*test)(Geom::Rect const &, Geom::Rect const &), bool take_hidden=false, bool take_insensitive=false, bool take_groups=true, bool enter_groups=false, bool enter_layers=true)
Return a vector list of items in a given area.
static unsigned int count_objects_recursive(SPObject *obj, unsigned int count)
static void _getObjectsByElementRecursive(Glib::ustring const &element, SPObject *parent, std::vector< SPObject * > &objects, bool custom)
static void _getObjectsBySelectorRecursive(SPObject *parent, CRSelEng *sel_eng, CRSimpleSel *simple_sel, std::vector< SPObject * > &objects)
static unsigned long next_serial
Definition document.cpp:104
constexpr auto SP_DOCUMENT_UPDATE_PRIORITY
Definition document.cpp:93
static void _getObjectsByClassRecursive(Glib::ustring const &klass, SPObject *parent, std::vector< SPObject * > &objects)
static void vacuum_document_recursive(SPObject *obj)
Remove unused definitions etc.
static bool is_within(Geom::Rect const &area, Geom::Rect const &box)
bool sp_no_convert_text_baseline_spacing
Definition document.cpp:99
constexpr auto SP_DOCUMENT_REROUTING_PRIORITY
Definition document.cpp:97
static std::vector< SPItem * > find_items_at_point(std::deque< SPItem * > const &nodes, unsigned dkey, Geom::Point const &p, int items_count=0, SPItem *upto=nullptr)
Returns the items from the descendants of group (recursively) which are at the point p,...
static bool overlaps(Geom::Rect const &area, Geom::Rect const &box)
static int doc_count
Definition document.cpp:101
void _build_flat_item_list(std::deque< SPItem * > &cache, SPGroup *group, unsigned int dkey, bool into_groups, bool active_only)
static SPItem * find_group_at_point(unsigned dkey, SPGroup *group, Geom::Point const &p)
Returns the topmost non-layer group from the descendants of group which is at point p,...
static SPItem * find_item_at_point(std::deque< SPItem * > const &nodes, unsigned dkey, Geom::Point const &p, SPItem *upto=nullptr)
static unsigned int objects_in_document(SPDocument *document)
Count the number of objects in a given document recursively using the count_objects_recursive helper ...
static int doc_mem_count
Definition document.cpp:102
bool sp_no_convert_text_baseline_spacing
Definition document.cpp:99
SVG drawing for display.
void sp_repr_free_log(Inkscape::XML::Event *log)
Definition event.cpp:285
void sp_file_fix_feComposite(SPObject *o)
void sp_file_convert_text_baseline_spacing(SPDocument *doc)
void sp_file_convert_font_name(SPDocument *doc)
void sp_file_convert_dpi(SPDocument *doc)
void sp_file_fix_osb(SPObject *o)
void sp_file_fix_empty_lines(SPDocument *doc)
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
void change_def_references(SPObject *from_obj, SPObject *to_obj)
Definition id-clash.cpp:479
void prevent_id_clashes(SPDocument *imported_doc, SPDocument *current_doc, bool from_clipboard)
This function resolves ID clashes between the document being imported and the current open document: ...
Definition id-clash.cpp:464
TODO: insert short description here.
std::string cache
SPItem * item
Inkscape::XML::Node * node
Inkscape - An SVG editor.
void inkscape_unref(Inkscape::Application &in)
Definition inkscape.cpp:121
void inkscape_ref(Inkscape::Application &in)
Definition inkscape.cpp:116
void shift(T &a, T &b, T const &c)
Geom::Point start
Geom::Point end
libavoid: Object-avoiding orthogonal and polyline connector routing library.
@ segmentPenalty
This penalty is applied for each segment in the connector path beyond the first.
Definition router.h:97
Affine identity()
Create an identity matrix.
Definition affine.h:210
static R & release(R &r)
Decrements the reference count of a anchored object.
Miscellaneous supporting code.
Definition document.h:94
CRNodeIface const croco_node_iface
Interface for XML nodes used by libcroco.
void rebase_hrefs(Inkscape::XML::Node *rootxml, gchar const *const old_base, gchar const *const new_base, bool const spns)
Change relative hrefs in current root XML node (faster than full document generation)
@ ELEMENT_NODE
Regular element node, e.g. <group />.
STL namespace.
static cairo_user_data_key_t key
static gint counter
Definition box3d.cpp:39
Ocnode * child[8]
Definition quantize.cpp:33
struct rdf_work_entity_t * rdf_find_entity(gchar const *name)
Retrieves a known RDF/Work entity by name.
Definition rdf.cpp:364
void rdf_set_defaults(SPDocument *doc)
Definition rdf.cpp:1187
const gchar * rdf_get_work_entity(SPDocument const *doc, struct rdf_work_entity_t *entity)
Retrieves a known RDF/Work entity's contents from the document XML by name.
Definition rdf.cpp:885
headers for RDF types
TODO: insert short description here.
Document * sp_repr_read_file(const gchar *filename, const gchar *default_ns, bool xinclude)
Reads XML from a file, and returns the Document.
Definition repr-io.cpp:274
Document * sp_repr_read_mem(const gchar *buffer, gint length, const gchar *default_ns)
Reads and parses XML from a buffer, returning it as an Document.
Definition repr-io.cpp:324
std::vector< Inkscape::XML::Node const * > sp_repr_lookup_name_many(Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth)
Inkscape::XML::Node const * sp_repr_lookup_name(Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth)
Inkscape::XML::Document * sp_repr_document_new(char const *rootname)
Returns new document having as first child a node named rootname.
Definition repr.cpp:32
Contains the interface for the Router class.
GList * items
Inkscape::XML::SimpleDocument - generic XML document implementation.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
Base class for live path effect items.
void invert(const double v[16], double alpha[16])
guint32 GQuark
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
SPPage – a page object.
SPRoot: SVG <svg> implementation.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
static std::string get_type_string(Inkscape::XML::Node const &node)
unsigned int flags
Definition sp-object.h:95
install_reference_document(SPDocument *inject_into, SPDocument *reference)
static SPObject * createObject(std::string const &id)
Contains transformations to document/viewport and the viewport size.
Definition sp-item.h:92
Geom::Affine i2doc
Item to document transformation.
Definition sp-item.h:94
Geom::Affine i2vp
Item to viewport transformation.
Definition sp-item.h:100
Geom::Rect viewport
Viewport size.
Definition sp-item.h:97
The abstraction of a css2 simple selection list as defined by the right part of the "selector" produc...
int delta
SPDesktop * desktop
double height
double width
Affine transformation classes.