Inkscape
Vector Graphics Editor
sp-object.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SPObject implementation.
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Stephen Silver <sasilver@users.sourceforge.net>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 * Adrian Boguszewski
12 *
13 * Copyright (C) 1999-2016 authors
14 * Copyright (C) 2001-2002 Ximian, Inc.
15 *
16 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
17 */
18
19#include <cstring>
20#include <string>
21#include <vector>
22#include <limits>
23#include <glibmm.h>
24
25#include <boost/range/adaptor/transformed.hpp>
26
27#include "helper/sp-marshal.h"
28#include "attributes.h"
29#include "attribute-rel-util.h"
30#include "color-profile.h"
31#include "document.h"
32#include "io/fix-broken-links.h"
33#include "preferences.h"
34#include "style.h"
36#include "sp-factory.h"
37#include "sp-font.h"
38#include "sp-paint-server.h"
39#include "sp-root.h"
40#include "sp-use.h"
41#include "sp-use-reference.h"
42#include "sp-style-elem.h"
43#include "sp-script.h"
44#include "streq.h"
45#include "strneq.h"
46#include "xml/node-fns.h"
48#include "debug/event-tracker.h"
49#include "debug/simple-event.h"
50#include "debug/demangle.h"
52#include "util/format.h"
54
55#define noSP_OBJECT_DEBUG_CASCADE
56
57#define noSP_OBJECT_DEBUG
58
59#ifdef SP_OBJECT_DEBUG
60# define debug(f, a...) { g_print("%s(%d) %s:", \
61 __FILE__,__LINE__,__FUNCTION__); \
62 g_print(f, ## a); \
63 g_print("\n"); \
64 }
65#else
66# define debug(f, a...) /* */
67#endif
68
69// Define to enable indented tracing of SPObject.
70//#define OBJECT_TRACE
71static unsigned indent_level = 0;
72
76class SPObjectImpl
77{
78public:
79
85 static void setIdNull( SPObject* obj ) {
86 if (obj) {
87 obj->id = nullptr;
88 }
89 }
90
97 static void setId( SPObject* obj, gchar const* id ) {
98 if (obj && (id != obj->id) ) {
99 if (obj->id) {
100 g_free(obj->id);
101 obj->id = nullptr;
102 }
103 if (id) {
104 obj->id = g_strdup(id);
105 }
106 }
107 }
108};
109
114 : cloned{0}
115 , uflags{0}
116 , mflags{0}
117{
118 debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance *)this));
119
120 SPObjectImpl::setIdNull(this);
121
122 // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
123 // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
124 // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
125 // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
126 style = new SPStyle(nullptr, this);
127 context_style = nullptr;
128}
129
134{
135 g_free(this->_label);
136 g_free(this->_default_label);
137
138 if (this->_successor) {
139 sp_object_unref(this->_successor, nullptr);
140 this->_successor = nullptr;
141 }
142 if (this->_tmpsuccessor) {
143 sp_object_unref(this->_tmpsuccessor, nullptr);
144 this->_tmpsuccessor = nullptr;
145 }
146 if (parent) {
147 parent->children.erase(parent->children.iterator_to(*this));
148 }
149
150 delete style;
151 this->document = nullptr;
152 this->repr = nullptr;
153}
154
155// CPPIFY: make pure virtual
157 //throw;
158}
159
160void SPObject::update(SPCtx* /*ctx*/, unsigned int /*flags*/) {
161 //throw;
162}
163
164void SPObject::modified(unsigned int /*flags*/) {
165#ifdef OBJECT_TRACE
166 objectTrace( "SPObject::modified (default) (empty function)" );
167 objectTrace( "SPObject::modified (default)", false );
168#endif
169 //throw;
170}
171
172namespace {
173
174namespace Debug = Inkscape::Debug;
175namespace Util = Inkscape::Util;
176
177typedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
178
179class RefCountEvent : public BaseRefCountEvent {
180public:
181 RefCountEvent(SPObject *object, int bias, char const *name)
182 : BaseRefCountEvent(name)
183 {
184 _addProperty("object", Util::format("%p", object).pointer());
185 _addProperty("class", Debug::demangle(typeid(*object).name()));
186 _addProperty("new-refcount", Util::format("%d", object->refCount + bias).pointer());
187 }
188};
189
190class RefEvent : public RefCountEvent {
191public:
192 RefEvent(SPObject *object)
193 : RefCountEvent(object, 1, "sp-object-ref")
194 {}
195};
196
197class UnrefEvent : public RefCountEvent {
198public:
199 UnrefEvent(SPObject *object)
200 : RefCountEvent(object, -1, "sp-object-unref")
201 {}
202};
203
204} // namespace
205
206gchar const* SPObject::getId() const {
207 return id;
208}
209
213void SPObject::getIds(std::set<std::string> &ret) const {
214 if (id) {
215 ret.insert(std::string(id));
216 }
217 for (auto &child : children) {
218 child.getIds(ret);
219 }
220}
221
225std::string SPObject::getUrl() const {
226 if (id) {
227 return std::string("url(#") + id + ")";
228 }
229 return "";
230}
231
233 return repr;
234}
235
237 return repr;
238}
239
240
242{
243 g_return_val_if_fail(object != nullptr, NULL);
244
246
247 object->refCount++;
248
249 return object;
250}
251
253{
254 g_return_val_if_fail(object != nullptr, NULL);
255
257
258 object->refCount--;
259
260 if (object->refCount <= 0) {
261 delete object;
262 }
263
264 return nullptr;
265}
266
268{
269 // if (owner) std::cout << " owner: " << *owner << std::endl;
270
271 // If owner is a clone, do not increase hrefcount, it's already href'ed by original.
272 if (!owner || !owner->cloned) {
273 hrefcount++;
275 }
276
277 if(owner)
278 hrefList.push_front(owner);
279}
280
282{
283 if (!owner || !owner->cloned) {
284 g_return_if_fail(hrefcount > 0);
285
286 hrefcount--;
288 }
289
290 if(owner)
291 hrefList.remove(owner);
292}
293
295 SPObject *topmost_collectable = nullptr;
296 for ( SPObject *iter = this ; iter ; iter = iter->parent ) {
297 iter->_total_hrefcount += increment;
298 if ( iter->_total_hrefcount < iter->hrefcount ) {
299 g_critical("HRefs overcounted");
300 }
301 if ( iter->_total_hrefcount == 0 &&
302 iter->_collection_policy != COLLECT_WITH_PARENT )
303 {
304 topmost_collectable = iter;
305 }
306 }
307 if (topmost_collectable) {
308 topmost_collectable->requestOrphanCollection();
309 }
310}
311
312void SPObject::getLinked(std::vector<SPObject *> &objects, LinkedObjectNature direction) const
313{
315 // href list is all back links
316 for (auto linked : hrefList) {
317 objects.push_back(linked);
318 }
319 }
320}
321
322bool SPObject::isAncestorOf(SPObject const *object) const
323{
324 g_return_val_if_fail(object != nullptr, false);
325 object = object->parent;
326 while (object) {
327 if ( object == this ) {
328 return true;
329 }
330 object = object->parent;
331 }
332 return false;
333}
334
336 g_return_val_if_fail(object != nullptr, NULL);
337
339 return nearest_common_ancestor<SPObject::ConstParentIterator>(this, object, nullptr);
340}
341
342static SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
343 SPObject const *result = nullptr;
344 if ( obj && ancestor ) {
345 if (obj->parent == ancestor) {
346 result = obj;
347 } else {
348 result = AncestorSon(obj->parent, ancestor);
349 }
350 }
351 return result;
352}
353
354int sp_object_compare_position(SPObject const *first, SPObject const *second)
355{
356 int result = 0;
357 if (first != second) {
358 SPObject const *ancestor = first->nearestCommonAncestor(second);
359 // Need a common ancestor to be able to compare
360 if ( ancestor ) {
361 // we have an object and its ancestor (should not happen when sorting selection)
362 if (ancestor == first) {
363 result = 1;
364 } else if (ancestor == second) {
365 result = -1;
366 } else {
367 SPObject const *to_first = AncestorSon(first, ancestor);
368 SPObject const *to_second = AncestorSon(second, ancestor);
369
370 g_assert(to_second->parent == to_first->parent);
371
372 result = sp_repr_compare_position(to_first->getRepr(), to_second->getRepr());
373 }
374 }
375 }
376 return result;
377}
378
379bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second){
380 return sp_object_compare_position(first,second)<0;
381}
382
384 if ( !cloned ) {
387 } else {
388 g_critical("Attempt to append repr as child of cloned object");
389 return nullptr;
390 }
391}
392
393void SPObject::setCSS(SPCSSAttr *css, gchar const *attr)
394{
395 g_assert(this->getRepr() != nullptr);
396 sp_repr_css_set(this->getRepr(), css, attr);
397}
398
399void SPObject::changeCSS(SPCSSAttr *css, gchar const *attr)
400{
401 g_assert(this->getRepr() != nullptr);
402 sp_repr_css_change(this->getRepr(), css, attr);
403}
404
405std::vector<SPObject*> SPObject::childList(bool add_ref, Action) {
406 std::vector<SPObject*> l;
407 for (auto& child: children) {
408 if (add_ref) {
410 }
411 l.push_back(&child);
412 }
413 return l;
414}
415
416std::vector<SPObject*> SPObject::ancestorList(bool root_to_tip)
417{
418 std::vector<SPObject *> ancestors;
419 for (SPObject::ParentIterator iter=parent ; iter ; ++iter) {
420 ancestors.push_back(iter);
421 }
422 if (root_to_tip) {
423 std::reverse(ancestors.begin(), ancestors.end());
424 }
425 return ancestors;
426}
427
428gchar const *SPObject::label() const {
429 return _label;
430}
431
432gchar const *SPObject::defaultLabel() const {
433 if (_label) {
434 return _label;
435 } else {
436 if (!_default_label) {
437 if (getId()) {
438 _default_label = g_strdup_printf("#%s", getId());
439 } else if (getRepr()) {
440 _default_label = g_strdup_printf("<%s>", getRepr()->name());
441 } else {
442 _default_label = g_strdup("Default label");
443 }
444 }
445 return _default_label;
446 }
447}
448
449void SPObject::setLabel(gchar const *label)
450{
451 getRepr()->setAttribute("inkscape:label", label);
452 // Update anything that's watching the object's label
453 _modified_signal.emit(this, SP_OBJECT_MODIFIED_FLAG);
454}
455
456
458 g_return_if_fail(document != nullptr);
460
461 // do not remove style or script elements (Bug #276244)
462 if (is<SPStyleElem>(this)) {
463 // leave it
464 } else if (is<SPScript>(this)) {
465 // leave it
466 } else if (is<SPFont>(this)) {
467 // leave it
468 } else if (!prefs->getBool("/options/cleanupswatches/value", false) && is<SPPaintServer>(this) && static_cast<SPPaintServer*>(this)->isSwatch()) {
469 // leave it
470 } else if (is<Inkscape::ColorProfile>(this)) {
471 // leave it
472 } else if (is<LivePathEffectObject>(this)) {
474 } else {
476
487 this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
488 }
489}
490
492 for (auto& child: children) {
493 child._delete_signal.emit(&child);
494 child._sendDeleteSignalRecursive();
495 }
496}
497
498void SPObject::deleteObject(bool propagate, bool propagate_descendants)
499{
500 sp_object_ref(this, nullptr);
501 if (is<SPLPEItem>(this)) {
502 cast<SPLPEItem>(this)->removeAllPathEffects(false, propagate_descendants);
503 }
504 if (propagate) {
505 _delete_signal.emit(this);
506 }
507 if (propagate_descendants) {
509 }
510
512 if (repr && repr->parent()) {
514 }
515
516 if (_successor) {
517 _successor->deleteObject(propagate, propagate_descendants);
518 }
519 sp_object_unref(this, nullptr);
520}
521
523{
524 std::vector<SPObject *> toDelete;
525 for (auto &child : children) {
526 if (is<SPItem>(&child)) {
527 if (child.isAncestorOf(except)) {
528 child.cropToObject(except);
529 } else if (&child != except) {
530 sp_object_ref(&child, nullptr);
531 toDelete.push_back(&child);
532 }
533 }
534 }
535 for (auto &i : toDelete) {
536 i->deleteObject(true, true);
537 sp_object_unref(i, nullptr);
538 }
539}
540
552void SPObject::cropToObjects(std::vector<SPObject *> except_objects)
553{
554 if (except_objects.empty()) {
555 return;
556 }
557 std::vector<SPObject *> links;
558 std::vector<SPObject *> toDelete;
559
560 // Get a list of all forward links so we don't delete them
561 for (auto item : except_objects) {
563 }
564
565 except_objects.insert(except_objects.end(), links.begin(), links.end());
566
567 // Collect a list of objects we expect to delete.
568 getObjectsExcept(toDelete, except_objects);
569
570 for (auto &i : toDelete) {
571 // Don't propergate the delete signal as we may delete clones later
572 i->deleteObject(false, false);
573 }
574}
575
576void SPObject::getObjectsExcept(std::vector<SPObject *> &objects, const std::vector<SPObject *> &excepts)
577{
578 for (auto &child : children) {
579 if (is<SPItem>(&child)) {
580 int child_flag = 1;
581 for (auto except : excepts) {
582 if (&child == except) {
583 child_flag = 0;
584 break;
585 }
586 if (child.isAncestorOf(except)) {
587 child_flag = 2;
588 }
589 }
590 if (child_flag == 1) {
591 objects.push_back(&child);
592 } else if (child_flag == 2) {
593 child.getObjectsExcept(objects, excepts);
594 }
595 }
596 }
597}
598
599void SPObject::getLinkedRecursive(std::vector<SPObject *> &objects, LinkedObjectNature direction) const
600{
601 // Recurse through multiple links
602 for (auto link : getLinked(direction)) {
603 // Make sure we never recurse objects multiple times.
604 if (std::find(objects.begin(), objects.end(), link) == objects.end()) {
605 objects.push_back(link);
606 link->getLinkedRecursive(objects, direction);
607 }
608 }
609
610 for (auto &child : children) {
611 if (is<SPItem>(&child)) {
612 // Recurse through all ancestors
613 child.getLinkedRecursive(objects, direction);
614 }
615 }
616}
617
619{
620 g_return_if_fail(object != nullptr);
621 g_return_if_fail(!prev || prev->parent == this);
622 g_return_if_fail(!object->parent);
623
624 sp_object_ref(object, this);
625 object->parent = this;
627
628 auto it = children.begin();
629 if (prev != nullptr) {
630 it = ++children.iterator_to(*prev);
631 }
632 children.insert(it, *object);
633
634 if (!object->xml_space.set)
635 object->xml_space.value = this->xml_space.value;
636}
637
639 g_return_if_fail(obj != nullptr);
640 g_return_if_fail(obj->parent);
641 g_return_if_fail(obj->parent == this);
642 g_return_if_fail(obj != prev);
643 g_return_if_fail(!prev || prev->parent == obj->parent);
644
645 auto it = children.begin();
646 if (prev != nullptr) {
647 it = ++children.iterator_to(*prev);
648 }
649
650 children.splice(it, children, children.iterator_to(*obj));
651}
652
654{
655 g_return_if_fail(object != nullptr);
656 g_return_if_fail(object->parent == this);
657
658 children.erase(children.iterator_to(*object));
659 object->releaseReferences();
660
661 object->parent = nullptr;
662
664 sp_object_unref(object, this);
665}
666
668{
669 g_return_val_if_fail(repr != nullptr, NULL);
670 SPObject *result = nullptr;
671
672 if (children.size() > 0 && children.back().getRepr() == repr) {
673 result = &children.back(); // optimization for common scenario
674 } else {
675 for (auto& child: children) {
676 if (child.getRepr() == repr) {
677 result = &child;
678 break;
679 }
680 }
681 }
682 return result;
683}
684
694{
695 for (; ref; ref = ref->prev()) {
696 // The most likely situation is that `ref` is indeed a child of `obj`,
697 // so try that first, before checking getObjectByRepr.
698 if (auto result = obj.get_child_by_repr(ref)) {
699 return result;
700 }
701
702 // Only continue if `ref` is not an SPObject, but e.g. an XML comment
703 if (obj.document->getObjectByRepr(ref)) {
704 break;
705 }
706 }
707
708 return nullptr;
709}
710
712 SPObject* object = this;
713
714 const std::string type_string = NodeTraits::get_type_string(*child);
715
716 SPObject* ochild = SPFactory::createObject(type_string);
717 if (ochild == nullptr) {
718 // Currently, there are many node types that do not have
719 // corresponding classes in the SPObject tree.
720 // (rdf:RDF, inkscape:clipboard, ...)
721 // Thus, simply ignore this case for now.
722 return;
723 }
724
725 SPObject *prev = get_closest_child_by_repr(*object, ref);
726 object->attach(ochild, prev);
727 sp_object_unref(ochild, nullptr);
728
729 ochild->invoke_build(object->document, child, object->cloned);
730}
731
733 SPObject* object = this;
734 debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
735
736 style->filter.clear();
737 style->fill.value.href.reset();
738 style->stroke.value.href.reset();
739 style->shape_inside.clear();
740 style->shape_subtract.clear();
741
742 auto tmp = children | boost::adaptors::transformed([](SPObject& obj){return &obj;});
743 std::vector<SPObject *> toRelease(tmp.begin(), tmp.end());
744
745 for (auto& p: toRelease) {
746 object->detach(p);
747 }
748}
749
751 debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
752
753 SPObject *ochild = this->get_child_by_repr(child);
754
755 // If the xml node has got a corresponding child in the object tree
756 if (ochild) {
757 this->detach(ochild);
758 }
759}
760
762 SPObject* object = this;
763
764 SPObject *ochild = object->get_child_by_repr(child);
765 g_return_if_fail(ochild != nullptr);
766 SPObject *prev = get_closest_child_by_repr(*object, new_ref);
767 object->reorder(ochild, prev);
768 ochild->_position_changed_signal.emit(ochild);
769}
770
771void SPObject::tag_name_changed(gchar const* oldname, gchar const* newname) {
772 g_warning("XML Element renamed from %s to %s!", oldname, newname);
773}
774
776
777#ifdef OBJECT_TRACE
778 objectTrace( "SPObject::build" );
779#endif
780 SPObject* object = this;
781
782 /* Nothing specific here */
783 debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
784
785 object->readAttr(SPAttr::XML_SPACE);
786 object->readAttr(SPAttr::LANG);
787 object->readAttr(SPAttr::XML_LANG); // "xml:lang" overrides "lang" per spec, read it last.
788 object->readAttr(SPAttr::INKSCAPE_LABEL);
789 object->readAttr(SPAttr::INKSCAPE_COLLECT);
790
791 // Inherit if not set
792 if (lang.empty() && object->parent) {
793 lang = object->parent->lang;
794 }
795
796 if(object->cloned && (repr->attribute("id")) ) // The cases where this happens are when the "original" has no id. This happens
797 // if it is a SPString (a TextNode, e.g. in a <title>), or when importing
798 // stuff externally modified to have no id.
799 object->clone_original = document->getObjectById(repr->attribute("id"));
800
801 for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != nullptr; rchild = rchild->next()) {
802 const std::string typeString = NodeTraits::get_type_string(*rchild);
803
805 if (child == nullptr) {
806 // Currently, there are many node types that do not have
807 // corresponding classes in the SPObject tree.
808 // (rdf:RDF, inkscape:clipboard, ...)
809 // Thus, simply ignore this case for now.
810 continue;
811 }
812
813 object->attach(child, object->lastChild());
814 sp_object_unref(child, nullptr);
815 child->invoke_build(document, rchild, object->cloned);
816 }
817
818#ifdef OBJECT_TRACE
819 objectTrace( "SPObject::build", false );
820#endif
821}
822
823void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
824{
825#ifdef OBJECT_TRACE
826 objectTrace( "SPObject::invoke_build" );
827#endif
828 debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
829
830 g_assert(document != nullptr);
831 g_assert(repr != nullptr);
832
833 g_assert(this->document == nullptr);
834 g_assert(this->repr == nullptr);
835 g_assert(this->getId() == nullptr);
836
837 /* Bookkeeping */
838
839 this->document = document;
840 this->repr = repr;
841 if (!cloned) {
843 }
844 this->cloned = cloned;
845
846 /* Invoke derived methods, if any */
847 this->build(document, repr);
848
849 if ( !cloned ) {
850 this->document->bindObjectToRepr(this->repr, this);
851
852 if (Inkscape::XML::id_permitted(this->repr)) {
853 /* If we are not cloned, and not seeking, force unique id */
854 gchar const *id = this->repr->attribute("id");
855 if (!document->isSeeking()) {
856 auto realid = generate_unique_id(id);
857 this->document->bindObjectToId(realid.c_str(), this);
858 SPObjectImpl::setId(this, realid.c_str());
859
860 /* Redefine ID, if required */
861 if (!id || std::strcmp(id, getId()) != 0) {
862 this->repr->setAttribute("id", getId());
863 }
864 } else if (id) {
865 // bind if id, but no conflict -- otherwise, we can expect
866 // a subsequent setting of the id attribute
867 if (!this->document->getObjectById(id)) {
868 this->document->bindObjectToId(id, this);
869 SPObjectImpl::setId(this, id);
870 }
871 }
872 }
873 } else {
874 g_assert(this->getId() == nullptr);
875 }
876
877 this->document->process_pending_resource_changes();
878
879 /* Signalling (should be connected AFTER processing derived methods */
880 repr->addObserver(*this);
881
882#ifdef OBJECT_TRACE
883 objectTrace( "SPObject::invoke_build", false );
884#endif
885}
886
887int SPObject::getIntAttribute(char const *key, int def)
888{
889 return getRepr()->getAttributeInt(key, def);
890}
891
893 g_assert(this->repr);
894
895 return repr->position();
896}
897
899 g_assert(this->repr);
900
901 repr->appendChild(child);
902}
903
904SPObject* SPObject::nthChild(unsigned index) {
905 g_assert(this->repr);
906 if (hasChildren()) {
907 std::vector<SPObject*> l;
908 unsigned counter = 0;
909 for (auto& child: children) {
910 if (counter == index) {
911 return &child;
912 }
913 counter++;
914 }
915 }
916 return nullptr;
917}
918
920{
921 g_assert(this->repr);
922
923 repr->addChild(child,prev);
924}
925
927 g_assert(this->document);
928 g_assert(this->repr);
929 g_assert(cloned || repr->_anchored_refcount() > 0);
930
931 repr->removeObserver(*this);
932
933 this->_release_signal.emit(this);
934
935 this->release();
936
937 /* all hrefs should be released by the "release" handlers */
938 g_assert(this->hrefcount == 0);
939
940 if (!cloned) {
941 if (this->id) {
942 this->document->bindObjectToId(this->id, nullptr);
943 }
944 g_free(this->id);
945 this->id = nullptr;
946
947 g_free(this->_default_label);
948 this->_default_label = nullptr;
949
950 this->document->bindObjectToRepr(this->repr, nullptr);
951
953 } else {
954 g_assert(!this->id);
955 }
956
957 this->document = nullptr;
958 this->repr = nullptr;
959}
960
962{
963 SPObject *prev = nullptr;
964 if (parent && !parent->children.empty() && &parent->children.front() != this) {
965 prev = &*(--parent->children.iterator_to(*this));
966 }
967 return prev;
968}
969
971{
972 SPObject *next = nullptr;
973 if (parent && !parent->children.empty() && &parent->children.back() != this) {
974 next = &*(++parent->children.iterator_to(*this));
975 }
976 return next;
977}
978
980{
982}
983
985{
987}
988
990 Inkscape::XML::Node *new_prev)
991{
992 order_changed(&child, old_prev, new_prev);
993}
994
996{
997 auto const oldname = g_quark_to_string(old_name);
998 auto const newname = g_quark_to_string(new_name);
999
1000 tag_name_changed(oldname, newname);
1001}
1002
1003void SPObject::set(SPAttr key, gchar const* value) {
1004#ifdef OBJECT_TRACE
1005 std::stringstream temp;
1006 temp << "SPObject::set: " << sp_attribute_name(key) << " " << (value?value:"null");
1007 objectTrace( temp.str() );
1008#endif
1009
1010 g_assert(key != SPAttr::INVALID);
1011
1012 SPObject* object = this;
1013
1014 switch (key) {
1015
1016 case SPAttr::ID:
1017
1018 //XML Tree being used here.
1019 if ( !object->cloned && object->getRepr()->type() == Inkscape::XML::NodeType::ELEMENT_NODE ) {
1020 SPDocument *document=object->document;
1021 SPObject *conflict=nullptr;
1022
1023 gchar const *new_id = value;
1024
1025 if (new_id) {
1026 conflict = document->getObjectById((char const *)new_id);
1027 }
1028
1029 if ( conflict && conflict != object ) {
1030 if (!document->isSeeking()) {
1031 sp_object_ref(conflict, nullptr);
1032 // give the conflicting object a new ID
1033 auto new_conflict_id = conflict->generate_unique_id();
1034 conflict->setAttribute("id", new_conflict_id);
1035 sp_object_unref(conflict, nullptr);
1036 } else {
1037 new_id = nullptr;
1038 }
1039 }
1040
1041 if (object->getId()) {
1042 document->bindObjectToId(object->getId(), nullptr);
1043 SPObjectImpl::setId(object, nullptr);
1044 }
1045
1046 if (new_id) {
1047 SPObjectImpl::setId(object, new_id);
1048 document->bindObjectToId(object->getId(), object);
1049 }
1050
1051 g_free(object->_default_label);
1052 object->_default_label = nullptr;
1053 }
1054 break;
1055
1057 g_free(object->_label);
1058 if (value) {
1059 object->_label = g_strdup(value);
1060 } else {
1061 object->_label = nullptr;
1062 }
1063 g_free(object->_default_label);
1064 object->_default_label = nullptr;
1065 break;
1066
1068 if ( value && !std::strcmp(value, "always") ) {
1069 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1070 } else {
1071 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1072 }
1073 break;
1074
1075 case SPAttr::XML_SPACE:
1076 if (value && !std::strcmp(value, "preserve")) {
1077 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1078 object->xml_space.set = TRUE;
1079 } else if (value && !std::strcmp(value, "default")) {
1080 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1081 object->xml_space.set = TRUE;
1082 } else if (object->parent) {
1084 parent = object->parent;
1086 }
1087 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1088 break;
1089
1090 case SPAttr::LANG:
1091 if (value) {
1092 lang = value;
1093 // To do: sanity check
1094 }
1095 break;
1096
1097 case SPAttr::XML_LANG:
1098 if (value) {
1099 lang = value;
1100 // To do: sanity check
1101 }
1102 break;
1103
1104 case SPAttr::STYLE:
1105 object->style->readFromObject( object );
1106 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1107 break;
1108
1109 default:
1110 break;
1111 }
1112
1113#ifdef OBJECT_TRACE
1114 objectTrace( "SPObject::set", false );
1115#endif
1116}
1117
1118void SPObject::setKeyValue(SPAttr key, gchar const *value)
1119{
1120 this->set(key, value);
1121}
1122
1124{
1125 if (keyid == SPAttr::XLINK_HREF) {
1126 auto value = Inkscape::getHrefAttribute(*getRepr()).second;
1127 setKeyValue(keyid, value);
1128 return;
1129 }
1130
1131 char const *key = sp_attribute_name(keyid);
1132
1133 assert(key != nullptr);
1134 assert(getRepr() != nullptr);
1135
1136 char const *value = getRepr()->attribute(key);
1137
1138 setKeyValue(keyid, value);
1139}
1140
1141void SPObject::readAttr(gchar const *key)
1142{
1143 g_assert(key != nullptr);
1144
1145 //XML Tree being used here.
1146 g_assert(this->getRepr() != nullptr);
1147
1148 auto keyid = sp_attribute_lookup(key);
1149 if (keyid != SPAttr::INVALID) {
1150 /* Retrieve the 'key' attribute from the object's XML representation */
1151 gchar const *value = getRepr()->attribute(key);
1152
1153 setKeyValue(keyid, value);
1154 }
1155}
1156
1158{
1159 auto const key = g_quark_to_string(key_);
1160 readAttr(key);
1161}
1162
1164{
1165 read_content();
1166}
1167
1171static gchar const *sp_xml_get_space_string(unsigned int space)
1172{
1173 switch (space) {
1175 return "default";
1177 return "preserve";
1178 default:
1179 return nullptr;
1180 }
1181}
1182
1184#ifdef OBJECT_TRACE
1185 objectTrace( "SPObject::write" );
1186#endif
1187
1188 if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1189 repr = this->getRepr()->duplicate(doc);
1190 if (!( flags & SP_OBJECT_WRITE_EXT )) {
1191 repr->removeAttribute("inkscape:collect");
1192 }
1193 } else if (repr) {
1194 repr->setAttribute("id", this->getId());
1195
1196 if (this->xml_space.set) {
1197 char const *xml_space;
1198 xml_space = sp_xml_get_space_string(this->xml_space.value);
1199 repr->setAttribute("xml:space", xml_space);
1200 }
1201
1202 if ( flags & SP_OBJECT_WRITE_EXT &&
1204 {
1205 repr->setAttribute("inkscape:collect", "always");
1206 } else {
1207 repr->removeAttribute("inkscape:collect");
1208 }
1209
1210 if (style) {
1211 // Write if property set by style attribute in this object
1212 Glib::ustring style_prop = style->write(SPStyleSrc::STYLE_PROP);
1213
1214 // Write style attributes (SPStyleSrc::ATTRIBUTE) back to xml object
1215 bool any_written = false;
1216 auto properties = style->properties();
1217 for (auto * prop : properties) {
1219 // WARNING: We don't know for sure if the css names are the same as the attribute names
1220 auto val = repr->attribute(prop->name().c_str());
1221 auto new_val = prop->get_value();
1222 if (new_val.empty() && !val || new_val != val) {
1223 repr->setAttributeOrRemoveIfEmpty(prop->name(), new_val);
1224 any_written = true;
1225 }
1226 }
1227 }
1228 if(any_written) {
1229 // We need to ask the object to update the style and keep things in sync
1230 // see `case SPAttr::STYLE` above for how the style attr itself does this.
1231 style->readFromObject(this);
1232 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1233 }
1234
1235 // Check for valid attributes. This may be time consuming.
1236 // It is useful, though, for debugging Inkscape code.
1238 if( prefs->getBool("/options/svgoutput/check_on_editing") ) {
1239
1240 unsigned int flags = sp_attribute_clean_get_prefs();
1241 style_prop = sp_attribute_clean_style(repr, style_prop.c_str(), flags);
1242 }
1243
1244 repr->setAttributeOrRemoveIfEmpty("style", style_prop);
1245 } else {
1253 char const *style_str = repr->attribute("style");
1254 if (!style_str) {
1255 style_str = "NULL";
1256 }
1257 g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1258 }
1259 }
1260
1261#ifdef OBJECT_TRACE
1262 objectTrace( "SPObject::write", false );
1263#endif
1264 return repr;
1265}
1266
1271void
1273 assert(tmpsuccessor != NULL);
1274 assert(_tmpsuccessor == NULL);
1275 assert(tmpsuccessor->_tmpsuccessor == NULL);
1276 sp_object_ref(tmpsuccessor, nullptr);
1277 _tmpsuccessor = tmpsuccessor;
1278 if (repr) {
1279 char const *linked_fill_id = getAttribute("inkscape:linked-fill");
1280 if (linked_fill_id && document) {
1281 SPObject *lfill = document->getObjectById(linked_fill_id);
1282 if (lfill && lfill->_tmpsuccessor) {
1283 lfill->_tmpsuccessor->setAttribute("inkscape:linked-fill",lfill->_tmpsuccessor->getId());
1284 }
1285 }
1286
1287 if (children.size() == _tmpsuccessor->children.size()) {
1288 for (auto &obj : children) {
1289 auto tmpsuccessorchild = _tmpsuccessor->nthChild(obj.getPosition());
1290 if (tmpsuccessorchild && !obj._tmpsuccessor) {
1291 obj.setTmpSuccessor(tmpsuccessorchild);
1292 }
1293 }
1294 }
1295 }
1296}
1297
1301void
1303 for (auto &obj : children) {
1304 obj.fixTmpSuccessors();
1305 }
1306 if (_tmpsuccessor) {
1307 char const *linked_fill_id = getAttribute("inkscape:linked-fill");
1308 if (linked_fill_id && document) {
1309 SPObject *lfill = document->getObjectById(linked_fill_id);
1310 if (lfill && lfill->_tmpsuccessor) {
1311 _tmpsuccessor->setAttribute("inkscape:linked-fill", lfill->_tmpsuccessor->getId());
1312 }
1313 }
1314 }
1315}
1316
1317void
1319 for (auto &object : children) {
1320 object.unsetTmpSuccessor();
1321 }
1322 if (_tmpsuccessor) {
1324 _tmpsuccessor = nullptr;
1325 }
1326}
1327
1332 auto group = cast<SPGroup>(parent);
1333 if (group && group->layerMode() != SPGroup::LAYER) {
1334 return group->getTopAncestorNonLayer();
1335 } else {
1336 return this;
1337 }
1338};
1339
1340
1342{
1343#ifdef OBJECT_TRACE
1344 objectTrace( "SPObject::updateRepr 1" );
1345#endif
1346
1347 if ( !cloned ) {
1349 if (repr) {
1350#ifdef OBJECT_TRACE
1351 objectTrace( "SPObject::updateRepr 1", false );
1352#endif
1353 return updateRepr(repr->document(), repr, flags);
1354 } else {
1355 g_critical("Attempt to update non-existent repr");
1356#ifdef OBJECT_TRACE
1357 objectTrace( "SPObject::updateRepr 1", false );
1358#endif
1359 return nullptr;
1360 }
1361 } else {
1362 /* cloned objects have no repr */
1363#ifdef OBJECT_TRACE
1364 objectTrace( "SPObject::updateRepr 1", false );
1365#endif
1366 return nullptr;
1367 }
1368}
1369
1371{
1372#ifdef OBJECT_TRACE
1373 objectTrace( "SPObject::updateRepr 2" );
1374#endif
1375
1376 g_assert(doc != nullptr);
1377
1378 if (cloned) {
1379 /* cloned objects have no repr */
1380#ifdef OBJECT_TRACE
1381 objectTrace( "SPObject::updateRepr 2", false );
1382#endif
1383 return nullptr;
1384 }
1385
1386 if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1387 repr = getRepr();
1388 }
1389
1390#ifdef OBJECT_TRACE
1391 Inkscape::XML::Node *node = write(doc, repr, flags);
1392 objectTrace( "SPObject::updateRepr 2", false );
1393 return node;
1394#else
1395 return this->write(doc, repr, flags);
1396#endif
1397
1398}
1399
1400/* Modification */
1401
1402void SPObject::requestDisplayUpdate(unsigned int flags)
1403{
1404 g_return_if_fail( this->document != nullptr );
1405
1406#ifndef NDEBUG
1407 // expect no nested update calls
1409 // observed with LPE on <rect>
1410 g_warning("WARNING: Requested update while update in progress, counter = %d", document->update_in_progress);
1411 }
1412#endif
1413
1414 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1415 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1416 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1417 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1418 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1419
1420#ifdef OBJECT_TRACE
1421 objectTrace( "SPObject::requestDisplayUpdate" );
1422#endif
1423
1424 bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1425 //https://stackoverflow.com/a/7841333
1426 if ((this->uflags & flags) != flags ) {
1427 this->uflags |= flags;
1428 }
1429 /* If requestModified has already been called on this object or one of its children, then we
1430 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1431 */
1432 if (already_propagated) {
1433 if(this->document) {
1434 if (parent) {
1435 parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1436 } else {
1437 this->document->requestModified();
1438 }
1439 }
1440 }
1441
1442#ifdef OBJECT_TRACE
1443 objectTrace( "SPObject::requestDisplayUpdate", false );
1444#endif
1445
1446}
1447
1448void SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1449{
1450 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1451
1452#ifdef OBJECT_TRACE
1453 objectTrace( "SPObject::updateDisplay" );
1454#endif
1455
1456 assert(++(document->update_in_progress));
1457
1458#ifdef SP_OBJECT_DEBUG_CASCADE
1459 g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
1460#endif
1461
1462 /* Get this flags */
1463 flags |= this->uflags;
1464 /* Copy flags to modified cascade for later processing */
1465 this->mflags |= this->uflags;
1466 /* We have to clear flags here to allow rescheduling update */
1467 this->uflags = 0;
1468
1469 // Merge style if we have good reasons to think that parent style is changed */
1475 if (style) {
1477 if ((flags & SP_OBJECT_STYLESHEET_MODIFIED_FLAG)) {
1478 style->readFromObject(this);
1479 } else if (parent && (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1480 style->cascade( this->parent->style );
1481 }
1483 }
1484
1485 try
1486 {
1487 this->update(ctx, flags);
1488 }
1489 catch(...)
1490 {
1496 g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1497 }
1498
1499 assert((document->update_in_progress)--);
1500
1501#ifdef OBJECT_TRACE
1502 objectTrace( "SPObject::updateDisplay", false );
1503#endif
1504}
1505
1506void SPObject::requestModified(unsigned int flags)
1507{
1508 g_return_if_fail( this->document != nullptr );
1509
1510 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1511 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1512 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1513 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1514 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1515
1516#ifdef OBJECT_TRACE
1517 objectTrace( "SPObject::requestModified" );
1518#endif
1519
1520 bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1521
1522 this->mflags |= flags;
1523
1524 /* If requestModified has already been called on this object or one of its children, then we
1525 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1526 */
1527 if (already_propagated) {
1528 if (parent) {
1529 parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1530 } else {
1532 }
1533 }
1534#ifdef OBJECT_TRACE
1535 objectTrace( "SPObject::requestModified", false );
1536#endif
1537}
1538
1539void SPObject::emitModified(unsigned int flags)
1540{
1541 /* only the MODIFIED_CASCADE flag is legal here */
1542 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1543
1544#ifdef OBJECT_TRACE
1545 objectTrace( "SPObject::emitModified", true, flags );
1546#endif
1547
1548#ifdef SP_OBJECT_DEBUG_CASCADE
1549 g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
1550#endif
1551
1552 flags |= this->mflags;
1553 /* We have to clear mflags beforehand, as signal handlers may
1554 * make changes and therefore queue new modification notifications
1555 * themselves. */
1556 this->mflags = 0;
1557
1558 sp_object_ref(this);
1559
1560 this->modified(flags);
1561
1562 _modified_signal.emit(this, flags);
1563 sp_object_unref(this);
1564
1565#ifdef OBJECT_TRACE
1566 objectTrace( "SPObject::emitModified", false );
1567#endif
1568}
1569
1570gchar const *SPObject::getTagName() const
1571{
1572 g_assert(repr != nullptr);
1573
1575 //XML Tree being used here.
1576 return getRepr()->name();
1577}
1578
1579gchar const *SPObject::getAttribute(gchar const *key) const
1580{
1581 g_assert(this->repr != nullptr);
1582
1584 //XML Tree being used here.
1585 return (gchar const *) getRepr()->attribute(key);
1586}
1587
1590{
1591 g_assert(this->repr != nullptr);
1592
1594 //XML Tree being used here.
1595 getRepr()->setAttribute(key, value);
1596}
1597
1600 os << value;
1601 setAttribute(key, os.str());
1602}
1603
1605{
1607 //XML Tree being used here.
1609}
1610
1611bool SPObject::storeAsDouble( gchar const *key, double *val ) const
1612{
1613 g_assert(this->getRepr()!= nullptr);
1614 double nan = std::numeric_limits<double>::quiet_NaN();
1615 double temp_val = ((Inkscape::XML::Node *)(this->getRepr()))->getAttributeDouble(key, nan);
1616 if (std::isnan(temp_val)) {
1617 return false;
1618 }
1619 *val = temp_val;
1620 return true;
1621}
1622
1623std::string SPObject::generate_unique_id(char const *default_id) const
1624{
1625 if (default_id && !document->getObjectById(default_id)) {
1626 return default_id;
1627 }
1628
1629 //XML Tree being used here.
1630 auto name = repr->name();
1631 g_assert(name);
1632
1633 if (auto local = std::strchr(name, ':')) {
1634 name = local + 1;
1635 }
1636
1637 return document->generate_unique_id(name);
1638}
1639
1641 for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1642 SPObject *object = iter;
1643 if (is<SPRoot>(object)) {
1644 auto root = cast<SPRoot>(object);
1645 if ( root->version.svg < version ) {
1646 root->version.svg = version;
1647 }
1648 }
1649 }
1650}
1651
1652// Titles and descriptions
1653
1654/* Note:
1655 Titles and descriptions are stored in 'title' and 'desc' child elements
1656 (see section 5.4 of the SVG 1.0 and 1.1 specifications). The spec allows
1657 an element to have more than one 'title' child element, but strongly
1658 recommends against this and requires using the first one if a choice must
1659 be made. The same applies to 'desc' elements. Therefore, these functions
1660 ignore all but the first 'title' child element and first 'desc' child
1661 element, except when deleting a title or description.
1662
1663 This will change in SVG 2, where multiple 'title' and 'desc' elements will
1664 be allowed with different localized strings.
1665*/
1666
1667gchar * SPObject::title() const
1668{
1669 return getTitleOrDesc("svg:title");
1670}
1671
1672bool SPObject::setTitle(gchar const *title, bool verbatim)
1673{
1674 return setTitleOrDesc(title, "svg:title", verbatim);
1675}
1676
1677gchar * SPObject::desc() const
1678{
1679 return getTitleOrDesc("svg:desc");
1680}
1681
1682bool SPObject::setDesc(gchar const *desc, bool verbatim)
1683{
1684 return setTitleOrDesc(desc, "svg:desc", verbatim);
1685}
1686
1687char * SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1688{
1689 char *result = nullptr;
1690 SPObject *elem = findFirstChild(svg_tagname);
1691 if ( elem ) {
1692 //This string copy could be avoided by changing
1693 //the return type of SPObject::getTitleOrDesc
1694 //to std::unique_ptr<Glib::ustring>
1695 result = g_strdup(elem->textualContent().c_str());
1696 }
1697 return result;
1698}
1699
1700bool SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1701{
1702 if (!verbatim) {
1703 // If the new title/description is just whitespace,
1704 // treat it as though it were NULL.
1705 if (value) {
1706 bool just_whitespace = true;
1707 for (const gchar *cp = value; *cp; ++cp) {
1708 if (!std::strchr("\r\n \t", *cp)) {
1709 just_whitespace = false;
1710 break;
1711 }
1712 }
1713 if (just_whitespace) {
1714 value = nullptr;
1715 }
1716 }
1717 // Don't stomp on mark-up if there is no real change.
1718 if (value) {
1719 gchar *current_value = getTitleOrDesc(svg_tagname);
1720 if (current_value) {
1721 bool different = std::strcmp(current_value, value);
1722 g_free(current_value);
1723 if (!different) {
1724 return false;
1725 }
1726 }
1727 }
1728 }
1729
1730 SPObject *elem = findFirstChild(svg_tagname);
1731
1732 if (value == nullptr) {
1733 if (elem == nullptr) {
1734 return false;
1735 }
1736 // delete the title/description(s)
1737 while (elem) {
1738 elem->deleteObject();
1739 elem = findFirstChild(svg_tagname);
1740 }
1741 return true;
1742 }
1743
1745
1746 if (elem == nullptr) {
1747 // create a new 'title' or 'desc' element, putting it at the
1748 // beginning (in accordance with the spec's recommendations)
1749 Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1750 repr->addChild(xml_elem, nullptr);
1751 elem = document->getObjectByRepr(xml_elem);
1752 Inkscape::GC::release(xml_elem);
1753 }
1754 else {
1755 // remove the current content of the 'text' or 'desc' element
1756 auto tmp = elem->children | boost::adaptors::transformed([](SPObject& obj) { return &obj; });
1757 std::vector<SPObject*> vec(tmp.begin(), tmp.end());
1758 for (auto &child: vec) {
1759 child->deleteObject();
1760 }
1761 }
1762
1763 // add the new content
1764 elem->appendChildRepr(xml_doc->createTextNode(value));
1765 return true;
1766}
1767
1768SPObject* SPObject::findFirstChild(gchar const *tagname) const
1769{
1770 for (auto& child: const_cast<SPObject*>(this)->children)
1771 {
1772 if (child.repr->type() == Inkscape::XML::NodeType::ELEMENT_NODE &&
1773 !std::strcmp(child.repr->name(), tagname)) {
1774 return &child;
1775 }
1776 }
1777 return nullptr;
1778}
1779
1780Glib::ustring SPObject::textualContent() const
1781{
1782 Glib::ustring text;
1783
1784 for (auto& child: children)
1785 {
1786 Inkscape::XML::NodeType child_type = child.repr->type();
1787
1788 if (child_type == Inkscape::XML::NodeType::ELEMENT_NODE) {
1789 text += child.textualContent();
1790 }
1791 else if (child_type == Inkscape::XML::NodeType::TEXT_NODE) {
1792 text += child.repr->content();
1793 }
1794 }
1795 return text;
1796}
1797
1798Glib::ustring SPObject::getExportFilename() const
1799{
1800 if (auto filename = repr->attribute("inkscape:export-filename")) {
1801 return Glib::ustring(filename);
1802 }
1803 return "";
1804}
1805
1806void SPObject::setExportFilename(Glib::ustring filename)
1807{
1808 // Is this svg has been saved before.
1809 const char *doc_filename = document->getDocumentFilename();
1810 std::string base = Glib::path_get_dirname(doc_filename ? doc_filename : filename.c_str());
1811
1812 filename = Inkscape::optimizePath(filename, base);
1813 repr->setAttributeOrRemoveIfEmpty("inkscape:export-filename", filename.c_str());
1814}
1815
1817{
1818 return Geom::Point(
1819 repr->getAttributeDouble("inkscape:export-xdpi", 0.0),
1820 repr->getAttributeDouble("inkscape:export-ydpi", 0.0));
1821}
1822
1824{
1825 if (!dpi.x() || !dpi.y()) {
1826 repr->removeAttribute("inkscape:export-xdpi");
1827 repr->removeAttribute("inkscape:export-ydpi");
1828 } else {
1829 repr->setAttributeSvgDouble("inkscape:export-xdpi", dpi.x());
1830 repr->setAttributeSvgDouble("inkscape:export-ydpi", dpi.y());
1831 }
1832}
1833
1834// For debugging: Print SP tree structure.
1835void SPObject::recursivePrintTree( unsigned level )
1836{
1837 if (level == 0) {
1838 std::cout << "SP Object Tree" << std::endl;
1839 }
1840 std::cout << "SP: ";
1841 for (unsigned i = 0; i < level; ++i) {
1842 std::cout << " ";
1843 }
1844 std::cout << (getId()?getId():"No object id")
1845 << " clone: " << std::boolalpha << (bool)cloned
1846 << " hrefcount: " << hrefcount << std::endl;
1847 for (auto& child: children) {
1848 child.recursivePrintTree(level + 1);
1849 }
1850}
1851
1852// Function to allow tracing of program flow through SPObject and derived classes.
1853// To trace function, add at entrance ('in' = true) and exit of function ('in' = false).
1854void SPObject::objectTrace( std::string const &text, bool in, unsigned flags ) {
1855 if( in ) {
1856 for (unsigned i = 0; i < indent_level; ++i) {
1857 std::cout << " ";
1858 }
1859 std::cout << text << ":"
1860 << " entrance: "
1861 << (id?id:"null")
1862 // << " uflags: " << uflags
1863 // << " mflags: " << mflags
1864 // << " flags: " << flags
1865 << std::endl;
1866 ++indent_level;
1867 } else {
1868 --indent_level;
1869 for (unsigned i = 0; i < indent_level; ++i) {
1870 std::cout << " ";
1871 }
1872 std::cout << text << ":"
1873 << " exit: "
1874 << (id?id:"null")
1875 // << " uflags: " << uflags
1876 // << " mflags: " << mflags
1877 // << " flags: " << flags
1878 << std::endl;
1879 }
1880}
1881
1882std::ostream &operator<<(std::ostream &out, const SPObject &o)
1883{
1884 out << (o.getId()?o.getId():"No ID")
1885 << " cloned: " << std::boolalpha << (bool)o.cloned
1886 << " ref: " << o.refCount
1887 << " href: " << o.hrefcount
1888 << " total href: " << o._total_hrefcount;
1889 return out;
1890}
1891/*
1892 Local Variables:
1893 mode:c++
1894 c-file-style:"stroustrup"
1895 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1896 indent-tabs-mode:nil
1897 fill-column:99
1898 End:
1899*/
1900// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
unsigned int sp_attribute_clean_get_prefs()
Get preferences.
void sp_attribute_clean_style(Node *repr, unsigned int flags)
Clean CSS style on an element.
Utility functions related to parsing and validation of XML attributes.
gchar const * sp_attribute_name(SPAttr id)
Get attribute name by id.
Definition: attributes.cpp:644
SPAttr sp_attribute_lookup(gchar const *key)
Get attribute id by name.
Definition: attributes.cpp:631
Lookup dictionary for attributes/properties.
SPAttr
Definition: attributes.h:27
@ XLINK_HREF
@ INKSCAPE_LABEL
@ INKSCAPE_COLLECT
@ INVALID
Must have value 0.
std::ostream & operator<<(std::ostream &out_file, const Geom::Affine &m)
Print out the Affine (for debugging).
Definition: affine.h:188
Two-dimensional point that doubles as a vector.
Definition: point.h:66
constexpr Coord y() const noexcept
Definition: point.h:106
constexpr Coord x() const noexcept
Definition: point.h:104
A thin wrapper around std::ostringstream, but writing floating point numbers in the format required b...
unsigned _anchored_refcount() const
Definition: gc-anchored.h:52
Preference storage class.
Definition: preferences.h:63
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
Definition: preferences.h:363
static Preferences * get()
Access the singleton Preferences object.
Definition: preferences.h:599
Non-owning reference to 'const char*' Main-purpose: avoid overloads of type f(char*,...
char const * pointer() const
Definition: share.h:33
Interface for refcounted XML nodes.
Definition: node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual Node * next()=0
Get the next sibling of this node.
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.
double getAttributeDouble(Util::const_char_ptr key, double default_value=0.0) const
Definition: node.cpp:76
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition: node.cpp:167
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 unsigned position() const =0
Get the index of this node in parent's child order.
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 addObserver(NodeObserver &observer)=0
Add an object that will be notified of the changes to this node.
virtual Document * document()=0
Get the node's associated document.
int getAttributeInt(Util::const_char_ptr key, int default_value=0) const
Definition: node.cpp:67
virtual NodeType type() const =0
Get the type of the node.
virtual void removeObserver(NodeObserver &observer)=0
Remove an object from the list of observers.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition: node.cpp:111
Typed SVG document implementation.
Definition: document.h:106
bool isSeeking() const
Definition: document.h:359
char const * getDocumentFilename() const
Definition: document.h:234
std::string generate_unique_id(char const *prefix)
Generate a document-wide unique id.
Definition: document.cpp:1327
unsigned update_in_progress
For sanity check in SPObject::requestDisplayUpdate.
Definition: document.h:110
SPObject * getObjectById(std::string const &id) const
Definition: document.cpp:1182
void process_pending_resource_changes()
Definition: document.cpp:1909
void requestModified()
Definition: document.cpp:1415
void bindObjectToId(char const *id, SPObject *object)
Definition: document.cpp:1156
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition: document.h:216
void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object)
Definition: document.cpp:1345
void queueForOrphanCollection(SPObject *object)
Definition: document.cpp:335
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Definition: document.cpp:1358
unsigned int value
Definition: sp-object.h:97
unsigned int set
Definition: sp-object.h:96
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition: sp-object.h:146
CollectionPolicy collectionPolicy() const
Set the policy under which this object will be orphan-collected.
Definition: sp-object.h:411
char const * label() const
Gets the author-visible label property for the object or a default if no label is defined.
Definition: sp-object.cpp:428
SPObject * getNext()
Definition: sp-object.cpp:970
int getIntAttribute(char const *key, int def)
Definition: sp-object.cpp:887
Inkscape::XML::Node * repr
Definition: sp-object.h:179
SPObject * _successor
Definition: sp-object.h:716
char * desc() const
Returns the description of this object, or NULL if there is none.
Definition: sp-object.cpp:1677
void requestOrphanCollection()
Requests a later automatic call to collectOrphan().
Definition: sp-object.cpp:457
unsigned int uflags
Definition: sp-object.h:168
void setAttributeDouble(Inkscape::Util::const_char_ptr key, double value)
Definition: sp-object.cpp:1598
void appendChild(Inkscape::XML::Node *child)
Definition: sp-object.cpp:898
void _updateTotalHRefCount(int increment)
Adds increment to _total_hrefcount of object and its parents.
Definition: sp-object.cpp:294
bool storeAsDouble(char const *key, double *val) const
Definition: sp-object.cpp:1611
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Definition: sp-object.cpp:1588
SPObject()
Constructor, sets all attributes to default values.
Definition: sp-object.cpp:113
void detach(SPObject *object)
Remove object from parent's children, release and unref it.
Definition: sp-object.cpp:653
void requestModified(unsigned int flags)
Requests that a modification notification signal be emitted later (e.g.
Definition: sp-object.cpp:1506
void removeAttribute(char const *key)
Definition: sp-object.cpp:1604
Glib::ustring getExportFilename() const
Get and set the exportable filename on this object.
Definition: sp-object.cpp:1798
Geom::Point getExportDpi() const
Get and set the exported DPI for this objet, if available.
Definition: sp-object.cpp:1816
void notifyChildOrderChanged(Inkscape::XML::Node &node, Inkscape::XML::Node &child, Inkscape::XML::Node *old_prev, Inkscape::XML::Node *new_prev) final
Child order change callback.
Definition: sp-object.cpp:989
void setKeyValue(SPAttr key, char const *value)
Call virtual set() function of object.
Definition: sp-object.cpp:1118
bool setDesc(char const *desc, bool verbatim=false)
Sets the description of this object.
Definition: sp-object.cpp:1682
void setExportDpi(Geom::Point dpi)
Definition: sp-object.cpp:1823
SPIXmlSpace xml_space
Definition: sp-object.h:170
void setExportFilename(Glib::ustring filename)
Definition: sp-object.cpp:1806
std::vector< SPObject * > childList(bool add_ref, Action action=ActionGeneral)
Retrieves the children as a std vector object, optionally ref'ing the children in the process,...
Definition: sp-object.cpp:405
void setTmpSuccessor(SPObject *tmpsuccessor)
Indicates that another object supercedes temporaty this one.
Definition: sp-object.cpp:1272
char * getTitleOrDesc(char const *svg_tagname) const
Returns the title or description of this object, or NULL if there is none.
Definition: sp-object.cpp:1687
void getObjectsExcept(std::vector< SPObject * > &objects, const std::vector< SPObject * > &except)
Get all child objects except for any in the list.
Definition: sp-object.cpp:576
SPObject * lastChild()
Definition: sp-object.h:304
SPObject * get_child_by_repr(Inkscape::XML::Node *repr)
Return object's child whose node pointer equals repr.
Definition: sp-object.cpp:667
Glib::ustring lang
Definition: sp-object.h:171
virtual void read_content()
Definition: sp-object.cpp:156
unsigned int mflags
Definition: sp-object.h:169
void invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
Definition: sp-object.cpp:823
void notifyAttributeChanged(Inkscape::XML::Node &node, GQuark key, Inkscape::Util::ptr_shared oldval, Inkscape::Util::ptr_shared newval) final
Attribute change callback.
Definition: sp-object.cpp:1157
void attach(SPObject *object, SPObject *prev)
Put object into object tree, under parent, and behind prev; also update object's XML space.
Definition: sp-object.cpp:618
SPDocument * document
Definition: sp-object.h:174
virtual void set(SPAttr key, const char *value)
Definition: sp-object.cpp:1003
char * id
Definition: sp-object.h:178
void updateDisplay(SPCtx *ctx, unsigned int flags)
Updates the object's display immediately.
Definition: sp-object.cpp:1448
SPObject * _tmpsuccessor
Definition: sp-object.h:717
virtual void remove_child(Inkscape::XML::Node *child)
Definition: sp-object.cpp:750
void emitModified(unsigned int flags)
Emits the MODIFIED signal with the object's flags.
Definition: sp-object.cpp:1539
void getLinkedRecursive(std::vector< SPObject * > &objects, LinkedObjectNature direction=LinkedObjectNature::ANY) const
Grows the input list with all linked items recursively in both child nodes and links of links.
Definition: sp-object.cpp:599
char const * getId() const
Returns the objects current ID string.
Definition: sp-object.cpp:206
bool setTitleOrDesc(char const *value, char const *svg_tagname, bool verbatim)
Sets or deletes the title or description of this object.
Definition: sp-object.cpp:1700
void changeCSS(SPCSSAttr *css, char const *attr)
Definition: sp-object.cpp:399
void notifyChildRemoved(Inkscape::XML::Node &node, Inkscape::XML::Node &child, Inkscape::XML::Node *prev) final
Child removal callback.
Definition: sp-object.cpp:984
void recursivePrintTree(unsigned level=0)
Definition: sp-object.cpp:1835
sigc::signal< void(SPObject *)> _position_changed_signal
Definition: sp-object.h:714
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition: sp-object.h:234
bool setTitle(char const *title, bool verbatim=false)
Sets the title of this object.
Definition: sp-object.cpp:1672
std::vector< SPObject * > ancestorList(bool root_to_tip)
Retrieves a list of ancestors of the object, as an easy to use vector.
Definition: sp-object.cpp:416
virtual Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
Definition: sp-object.cpp:1183
void notifyChildAdded(Inkscape::XML::Node &node, Inkscape::XML::Node &child, Inkscape::XML::Node *prev) final
Child addition callback.
Definition: sp-object.cpp:979
char * _label
Definition: sp-object.h:719
Glib::ustring textualContent() const
Return the full textual content of an element (typically all the content except the tags).
Definition: sp-object.cpp:1780
sigc::signal< void(SPObject *, unsigned int)> _modified_signal
Definition: sp-object.h:715
~SPObject() override
Destructor, frees the used memory and unreferences a potential successor of the object.
Definition: sp-object.cpp:133
SPObject * getPrev()
Returns previous object in sibling list or NULL.
Definition: sp-object.cpp:961
char const * defaultLabel() const
Returns a default label property for this object.
Definition: sp-object.cpp:432
SPObject * parent
Definition: sp-object.h:175
unsigned getPosition()
Definition: sp-object.cpp:892
void setLabel(char const *label)
Sets the author-visible label for this object.
Definition: sp-object.cpp:449
virtual void update(SPCtx *ctx, unsigned int flags)
Definition: sp-object.cpp:160
virtual void release()
Definition: sp-object.cpp:732
SPStyle * context_style
Represents the style that should be used to resolve 'context-fill' and 'context-stroke'.
Definition: sp-object.h:239
SPObject * findFirstChild(char const *tagname) const
Find the first child of this object with a given tag name, and return it.
Definition: sp-object.cpp:1768
void unhrefObject(SPObject *owner=nullptr)
Decrease weak refcount.
Definition: sp-object.cpp:281
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
Definition: sp-object.cpp:1341
void _requireSVGVersion(unsigned major, unsigned minor)
Definition: sp-object.h:705
@ COLLECT_WITH_PARENT
Definition: sp-object.h:150
@ ALWAYS_COLLECT
Definition: sp-object.h:151
virtual void modified(unsigned int flags)
Definition: sp-object.cpp:164
void readAttr(char const *key)
Read value of key attribute from XML node into object.
void _sendDeleteSignalRecursive()
Sends the delete signal to all children of this object recursively.
Definition: sp-object.cpp:491
void reorder(SPObject *obj, SPObject *prev)
In list of object's children, move object behind prev.
Definition: sp-object.cpp:638
sigc::signal< void(SPObject *)> _delete_signal
Definition: sp-object.h:713
bool hasChildren() const
Definition: sp-object.h:299
std::string generate_unique_id(char const *default_id=nullptr) const
Generate a document-wide unique id for this object.
Definition: sp-object.cpp:1623
void hrefObject(SPObject *owner=nullptr)
Increase weak refcount.
Definition: sp-object.cpp:267
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Definition: sp-object.cpp:498
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
Definition: sp-object.cpp:232
virtual void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
Definition: sp-object.cpp:711
char const * getAttribute(char const *name) const
Definition: sp-object.cpp:1579
void fixTmpSuccessors()
Fix temporary successors in duple stamp.
Definition: sp-object.cpp:1302
char * title() const
Returns the title of this object, or NULL if there is none.
Definition: sp-object.cpp:1667
void cropToObjects(std::vector< SPObject * > except_objects)
Removes objects which are not related to given list of objects.
Definition: sp-object.cpp:552
virtual void getLinked(std::vector< SPObject * > &objects, LinkedObjectNature direction=LinkedObjectNature::ANY) const
Get objects which are linked to this object as either a source or a target.
Definition: sp-object.cpp:312
SPObject * appendChildRepr(Inkscape::XML::Node *repr)
Append repr as child of this object.
Definition: sp-object.cpp:383
bool isAncestorOf(SPObject const *object) const
True if object is non-NULL and this is some in/direct parent of object.
Definition: sp-object.cpp:322
unsigned int cloned
Definition: sp-object.h:166
LinkedObjectNature
Definition: sp-object.h:154
std::string getUrl() const
Get the id in a URL format.
Definition: sp-object.cpp:225
void objectTrace(std::string const &, bool in=true, unsigned flags=0)
Definition: sp-object.cpp:1854
SPObject * nthChild(unsigned index)
Definition: sp-object.cpp:904
void cropToObject(SPObject *except)
Removes all children except for the given object, it's children and it's ancesstors.
Definition: sp-object.cpp:522
void addChild(Inkscape::XML::Node *child, Inkscape::XML::Node *prev=nullptr)
Definition: sp-object.cpp:919
std::list< SPObject * > hrefList
Definition: sp-object.h:183
char * _default_label
Definition: sp-object.h:720
virtual void build(SPDocument *doc, Inkscape::XML::Node *repr)
Definition: sp-object.cpp:775
SPObject const * nearestCommonAncestor(SPObject const *object) const
Returns youngest object being parent to this and object.
Definition: sp-object.cpp:335
void getIds(std::set< std::string > &ret) const
Accumulate this id and all it's descendants ids.
Definition: sp-object.cpp:213
void unsetTmpSuccessor()
Unset object supercedes.
Definition: sp-object.cpp:1318
virtual void order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node *old_repr, Inkscape::XML::Node *new_repr)
Definition: sp-object.cpp:761
char const * getTagName() const
Definition: sp-object.cpp:1570
ChildrenList children
Definition: sp-object.h:893
SPObject const * getTopAncestorNonLayer() const
Returns ancestor non layer.
Definition: sp-object.cpp:1331
int refCount
Definition: sp-object.h:182
void setCSS(SPCSSAttr *css, char const *attr)
Definition: sp-object.cpp:393
sigc::signal< void(SPObject *)> _release_signal
Definition: sp-object.h:712
void notifyContentChanged(Inkscape::XML::Node &node, Inkscape::Util::ptr_shared oldcontent, Inkscape::Util::ptr_shared newcontent) final
Content change callback.
Definition: sp-object.cpp:1163
void notifyElementNameChanged(Inkscape::XML::Node &node, GQuark old_name, GQuark new_name) final
Element name change callback.
Definition: sp-object.cpp:995
unsigned int hrefcount
Definition: sp-object.h:172
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
Definition: sp-object.cpp:1402
virtual void tag_name_changed(gchar const *oldname, gchar const *newname)
Definition: sp-object.cpp:771
void releaseReferences()
Cleans up an SPObject, releasing its references and requesting that references to it be released.
Definition: sp-object.cpp:926
unsigned int _total_hrefcount
Definition: sp-object.h:173
bool isSwatch() const
An SVG style object.
Definition: style.h:45
T< SPAttr::FILL, SPIPaint > fill
fill
Definition: style.h:240
bool block_filter_bbox_updates
(hack) Temporarily set to true to block filter changes from updating the object's bbox in situations ...
Definition: style.h:303
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition: style.h:247
void readFromObject(SPObject *object)
Read style properties from object's repr.
Definition: style.cpp:606
Glib::ustring write(unsigned flags, SPStyleSrc style_src_req, SPStyle const *base=nullptr) const
const std::vector< SPIBase * > properties()
Definition: style.cpp:494
T< SPAttr::SHAPE_INSIDE, SPIShapes > shape_inside
SVG2 Text Wrapping.
Definition: style.h:179
T< SPAttr::FILTER, SPIFilter > filter
Filter effect.
Definition: style.h:275
T< SPAttr::SHAPE_SUBTRACT, SPIShapes > shape_subtract
Definition: style.h:180
void cascade(SPStyle const *parent)
Sets computed values in style, which may involve inheriting from (or in some other way calculating fr...
Definition: style.cpp:815
TODO: insert short description here.
RootCluster root
Definition: containment.cpp:50
TODO: insert short description here.
std::shared_ptr< Css const > css
Css & result
SPItem * item
Definition: imagemagick.cpp:43
Inkscape::XML::Node * node
Definition: imagemagick.cpp:39
Bezier reverse(const Bezier &a)
Definition: bezier.h:342
ForwardIterator nearest_common_ancestor(ForwardIterator a, ForwardIterator b, ForwardIterator end)
Time costs:
Debugging utilities.
Definition: demangle.cpp:22
std::shared_ptr< std::string > demangle(char const *name)
Definition: demangle.cpp:37
static R & anchor(R &r)
Increments the reference count of a anchored object.
Definition: gc-anchored.h:92
static R & release(R &r)
Decrements the reference count of a anchored object.
Definition: gc-anchored.h:133
static Geom::Point direction(Geom::Point const &first, Geom::Point const &second)
Computes an unit vector of the direction from first to second control point.
Definition: node.cpp:152
Miscellaneous supporting code.
Definition: color-conv.cpp:9
ptr_shared format(char const *format,...) G_GNUC_PRINTF(1
Definition: format.h:33
bool id_permitted(Node const *node)
Definition: node-fns.cpp:52
NodeType
Enumeration containing all supported node types.
Definition: node.h:40
@ ELEMENT_NODE
Regular element node, e.g. <group />.
@ TEXT_NODE
Text node, e.g. "Some text" in <group>Some text</group> is represented by a text node.
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
std::string optimizePath(std::string const &path, std::string const &base, unsigned int parents)
Convert an absolute path into a relative one if possible to do in the given number of parent steps.
Helper functions for XML nodes.
static cairo_user_data_key_t key
Definition: nr-svgfonts.cpp:46
static gint counter
Definition: box3d.cpp:39
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition: quantize.cpp:33
Ocnode ** ref
Definition: quantize.cpp:32
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
Definition: repr-css.cpp:350
void sp_repr_css_set(Node *repr, SPCSSAttr *css, gchar const *attr)
Sets an attribute (e.g.
Definition: repr-css.cpp:257
int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
Works for different-parent objects, so long as they have a common ancestor.
Definition: repr-util.cpp:249
void sp_repr_unparent(Inkscape::XML::Node *repr)
Remove repr from children of its parent node.
Definition: repr.h:103
guint32 GQuark
Definition: sp-namedview.h:36
bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second)
Definition: sp-object.cpp:379
static SPObject * get_closest_child_by_repr(SPObject &obj, Inkscape::XML::Node *ref)
Get closest child to a reference representation.
Definition: sp-object.cpp:693
static SPObject const * AncestorSon(SPObject const *obj, SPObject const *ancestor)
Definition: sp-object.cpp:342
static unsigned indent_level
Definition: sp-object.cpp:71
int sp_object_compare_position(SPObject const *first, SPObject const *second)
Compares height of objects in tree.
Definition: sp-object.cpp:354
static gchar const * sp_xml_get_space_string(unsigned int space)
Return string representation of space value.
Definition: sp-object.cpp:1171
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
Definition: sp-object.cpp:252
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
Definition: sp-object.cpp:241
@ SP_XML_SPACE_PRESERVE
Definition: sp-object.h:87
@ SP_XML_SPACE_DEFAULT
Definition: sp-object.h:86
SPRoot: SVG <svg> implementation.
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
Interface for XML documents.
Definition: document.h:43
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
static std::string get_type_string(Inkscape::XML::Node const &node)
Definition: sp-factory.cpp:295
Unused.
Definition: sp-object.h:80
static SPObject * createObject(std::string const &id)
Definition: sp-factory.cpp:285
static const unsigned SP_STYLE_FLAG_IFSET(1<< 0)
static const unsigned SP_STYLE_FLAG_IFSRC(1<< 3)
SPStyle - a style object for SPItem objects.
pair< double, double > Point
Definition: parser.cpp:7