summaryrefslogtreecommitdiffstats
path: root/src/object/sp-root.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/object/sp-root.cpp')
-rw-r--r--src/object/sp-root.cpp393
1 files changed, 393 insertions, 0 deletions
diff --git a/src/object/sp-root.cpp b/src/object/sp-root.cpp
new file mode 100644
index 0000000..7980c0f
--- /dev/null
+++ b/src/object/sp-root.cpp
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * SVG \<svg\> implementation.
+ */
+/*
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <string>
+#include <2geom/transforms.h>
+
+#include "attributes.h"
+#include "print.h"
+#include "document.h"
+#include "inkscape-version.h"
+#include "sp-defs.h"
+#include "sp-root.h"
+#include "display/drawing-group.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "xml/repr.h"
+#include "util/units.h"
+
+SPRoot::SPRoot() : SPGroup(), SPViewBox()
+{
+ this->onload = nullptr;
+
+ static Inkscape::Version const zero_version(0, 0);
+
+ sp_version_from_string(SVG_VERSION, &this->original.svg);
+ this->version.svg = zero_version;
+ this->original.svg = zero_version;
+ this->version.inkscape = zero_version;
+ this->original.inkscape = zero_version;
+
+ this->unset_x_and_y();
+ this->width.unset(SVGLength::PERCENT, 1.0, 1.0);
+ this->height.unset(SVGLength::PERCENT, 1.0, 1.0);
+
+ this->defs = nullptr;
+}
+
+SPRoot::~SPRoot()
+= default;
+
+void SPRoot::unset_x_and_y()
+{
+ this->x.unset(SVGLength::PERCENT, 0.0, 0.0); // Ignored for root SVG element
+ this->y.unset(SVGLength::PERCENT, 0.0, 0.0);
+}
+
+void SPRoot::build(SPDocument *document, Inkscape::XML::Node *repr)
+{
+ //XML Tree being used directly here while it shouldn't be.
+ if (!this->getRepr()->attribute("version")) {
+ repr->setAttribute("version", SVG_VERSION);
+ }
+
+ this->readAttr("version");
+ this->readAttr("inkscape:version");
+ /* It is important to parse these here, so objects will have viewport build-time */
+ this->readAttr("x");
+ this->readAttr("y");
+ this->readAttr("width");
+ this->readAttr("height");
+ this->readAttr("viewBox");
+ this->readAttr("preserveAspectRatio");
+ this->readAttr("onload");
+
+ SPGroup::build(document, repr);
+
+ // Search for first <defs> node
+ for (auto& o: children) {
+ if (SP_IS_DEFS(&o)) {
+ this->defs = SP_DEFS(&o);
+ break;
+ }
+ }
+
+ // clear transform, if any was read in - SVG does not allow transform= on <svg>
+ SP_ITEM(this)->transform = Geom::identity();
+}
+
+void SPRoot::release()
+{
+ this->defs = nullptr;
+
+ SPGroup::release();
+}
+
+
+void SPRoot::set(SPAttributeEnum key, const gchar *value)
+{
+ switch (key) {
+ case SP_ATTR_VERSION:
+ if (!sp_version_from_string(value, &this->version.svg)) {
+ this->version.svg = this->original.svg;
+ }
+ break;
+
+ case SP_ATTR_INKSCAPE_VERSION:
+ if (!sp_version_from_string(value, &this->version.inkscape)) {
+ this->version.inkscape = this->original.inkscape;
+ }
+ break;
+
+ case SP_ATTR_X:
+ /* Valid for non-root SVG elements; ex, em not handled correctly. */
+ if (!this->x.read(value)) {
+ this->x.unset(SVGLength::PERCENT, 0.0, 0.0);
+ }
+
+ /* fixme: I am almost sure these do not require viewport flag (Lauris) */
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_Y:
+ /* Valid for non-root SVG elements; ex, em not handled correctly. */
+ if (!this->y.read(value)) {
+ this->y.unset(SVGLength::PERCENT, 0.0, 0.0);
+ }
+
+ /* fixme: I am almost sure these do not require viewport flag (Lauris) */
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_WIDTH:
+ if (!this->width.read(value) || !(this->width.computed > 0.0)) {
+ this->width.unset(SVGLength::PERCENT, 1.0, 1.0);
+ }
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_HEIGHT:
+ if (!this->height.read(value) || !(this->height.computed > 0.0)) {
+ this->height.unset(SVGLength::PERCENT, 1.0, 1.0);
+ }
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_VIEWBOX:
+ set_viewBox( value );
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_PRESERVEASPECTRATIO:
+ set_preserveAspectRatio( value );
+ this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
+ break;
+
+ case SP_ATTR_ONLOAD:
+ this->onload = (char *) value;
+ break;
+
+ default:
+ /* Pass the set event to the parent */
+ SPGroup::set(key, value);
+ break;
+ }
+}
+
+void SPRoot::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
+{
+ SPGroup::child_added(child, ref);
+
+ SPObject *co = this->document->getObjectByRepr(child);
+ // NOTE: some XML nodes do not have corresponding SP objects,
+ // for instance inkscape:clipboard used in the clipboard code.
+ // See LP bug #1227827
+ //g_assert (co != NULL || !strcmp("comment", child->name())); // comment repr node has no object
+
+ if (co && SP_IS_DEFS(co)) {
+ // We search for first <defs> node - it is not beautiful, but works
+ for (auto& c: children) {
+ if (SP_IS_DEFS(&c)) {
+ this->defs = SP_DEFS(&c);
+ break;
+ }
+ }
+ }
+}
+
+void SPRoot::remove_child(Inkscape::XML::Node *child)
+{
+ if (this->defs && (this->defs->getRepr() == child)) {
+ SPObject *iter = nullptr;
+
+ // We search for first remaining <defs> node - it is not beautiful, but works
+ for (auto& child: children) {
+ iter = &child;
+ if (SP_IS_DEFS(iter) && (SPDefs *)iter != this->defs) {
+ this->defs = (SPDefs *)iter;
+ break;
+ }
+ }
+
+ if (!iter) {
+ /* we should probably create a new <defs> here? */
+ this->defs = nullptr;
+ }
+ }
+
+ SPGroup::remove_child(child);
+}
+
+void SPRoot::setRootDimensions()
+{
+ /*
+ * This is the root SVG element:
+ *
+ * x, y, width, and height apply to positioning the SVG element inside a parent.
+ * For the root SVG in Inkscape there is no parent, thus special rules apply:
+ * If width, height not set, width = 100%, height = 100% (as always).
+ * If width and height are in percent, they are percent of viewBox width/height.
+ * If width, height, and viewBox are not set... pick "random" width/height.
+ * x, y are ignored.
+ * initial viewport = (0 0 width height)
+ */
+ if( this->viewBox_set ) {
+
+ if( this->width._set ) {
+ // Check if this is necessary
+ if (this->width.unit == SVGLength::PERCENT) {
+ this->width.computed = this->width.value * this->viewBox.width();
+ }
+ } else {
+ this->width.set( SVGLength::PX, this->viewBox.width(), this->viewBox.width() );
+ }
+
+ if( this->height._set ) {
+ if (this->height.unit == SVGLength::PERCENT) {
+ this->height.computed = this->height.value * this->viewBox.height();
+ }
+ } else {
+ this->height.set(SVGLength::PX, this->viewBox.height(), this->viewBox.height() );
+ }
+
+ } else {
+
+ if( !this->width._set || this->width.unit == SVGLength::PERCENT) {
+ this->width.set( SVGLength::PX, 300, 300 ); // CSS/SVG default
+ }
+
+ if( !this->height._set || this->height.unit == SVGLength::PERCENT) {
+ this->height.set( SVGLength::PX, 150, 150 ); // CSS/SVG default
+ }
+ }
+
+ // Ignore x, y values for root element
+ this->unset_x_and_y();
+}
+
+void SPRoot::update(SPCtx *ctx, guint flags)
+{
+ SPItemCtx const *ictx = (SPItemCtx const *) ctx;
+
+ if( !this->parent ) {
+ this->setRootDimensions();
+ }
+
+ // Calculate x, y, width, height from parent/initial viewport
+ this->calcDimsFromParentViewport(ictx);
+
+ // std::cout << "SPRoot::update: final:"
+ // << " x: " << x.computed
+ // << " y: " << y.computed
+ // << " width: " << width.computed
+ // << " height: " << height.computed << std::endl;
+
+ // Calculate new viewport
+ SPItemCtx rctx = *ictx;
+ rctx.viewport = Geom::Rect::from_xywh( this->x.computed, this->y.computed,
+ this->width.computed, this->height.computed );
+ rctx = get_rctx( &rctx, Inkscape::Util::Quantity::convert(1, this->document->getDisplayUnit(), "px") );
+
+ /* And invoke parent method */
+ SPGroup::update((SPCtx *) &rctx, flags);
+
+ /* As last step set additional transform of drawing group */
+ for (SPItemView *v = this->display; v != nullptr; v = v->next) {
+ Inkscape::DrawingGroup *g = dynamic_cast<Inkscape::DrawingGroup *>(v->arenaitem);
+ g->setChildTransform(this->c2p);
+ }
+}
+
+void SPRoot::modified(unsigned int flags)
+{
+ SPGroup::modified(flags);
+
+ /* fixme: (Lauris) */
+ if (!this->parent && (flags & SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
+ this->document->emitResizedSignal(this->width.computed, this->height.computed);
+ }
+}
+
+
+Inkscape::XML::Node *SPRoot::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+{
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = xml_doc->createElement("svg:svg");
+ }
+
+ /* Only update version string on successful write to file. This is handled by 'file_save()'.
+ * if (flags & SP_OBJECT_WRITE_EXT) {
+ * repr->setAttribute("inkscape:version", Inkscape::version_string);
+ * }
+ */
+
+ if (!repr->attribute("version")) {
+ gchar *myversion = sp_version_to_string(this->version.svg);
+ repr->setAttribute("version", myversion);
+ g_free(myversion);
+ }
+
+ if (fabs(this->x.computed) > 1e-9) {
+ sp_repr_set_svg_double(repr, "x", this->x.computed);
+ }
+
+ if (fabs(this->y.computed) > 1e-9) {
+ sp_repr_set_svg_double(repr, "y", this->y.computed);
+ }
+
+ /* Unlike all other SPObject, here we want to preserve absolute units too (and only here,
+ * according to the recommendation in http://www.w3.org/TR/SVG11/coords.html#Units).
+ */
+ repr->setAttribute("width", sp_svg_length_write_with_units(this->width));
+ repr->setAttribute("height", sp_svg_length_write_with_units(this->height));
+
+ if (this->viewBox_set) {
+ Inkscape::SVGOStringStream os;
+ os << this->viewBox.left() << " " << this->viewBox.top() << " "
+ << this->viewBox.width() << " " << this->viewBox.height();
+
+ repr->setAttribute("viewBox", os.str());
+ }
+
+ SPGroup::write(xml_doc, repr, flags);
+
+ return repr;
+}
+
+Inkscape::DrawingItem *SPRoot::show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags)
+{
+ Inkscape::DrawingItem *ai = SPGroup::show(drawing, key, flags);
+
+ if (ai) {
+ Inkscape::DrawingGroup *g = dynamic_cast<Inkscape::DrawingGroup *>(ai);
+ g->setChildTransform(this->c2p);
+ }
+
+ // Uncomment to print out XML tree
+ // getRepr()->recursivePrintTree(0);
+
+ // Uncomment to print out SP Object tree
+ // recursivePrintTree(0);
+
+ // Uncomment to print out Display Item tree
+ // ai->recursivePrintTree(0);
+
+ return ai;
+}
+
+void SPRoot::print(SPPrintContext *ctx)
+{
+ ctx->bind(this->c2p, 1.0);
+
+ SPGroup::print(ctx);
+
+ ctx->release();
+}
+
+const char *SPRoot::displayName() const {
+ return "SVG"; // Do not translate
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :