From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- src/object/sp-root.cpp | 393 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 src/object/sp-root.cpp (limited to 'src/object/sp-root.cpp') diff --git a/src/object/sp-root.cpp b/src/object/sp-root.cpp new file mode 100644 index 0000000..fd0c93c --- /dev/null +++ b/src/object/sp-root.cpp @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * SVG \ implementation. + */ +/* + * Authors: + * Lauris Kaplinski + * Jon A. Cruz + * 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 +#include <2geom/transforms.h> + +#include "attributes.h" +#include "print.h" +#include "document.h" +#include "inkscape-version.h" +#include "sp-defs.h" +#include "sp-namedview.h" +#include "sp-root.h" +#include "sp-use.h" +#include "display/drawing-group.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(SPAttr::VERSION); + this->readAttr(SPAttr::INKSCAPE_VERSION); + /* It is important to parse these here, so objects will have viewport build-time */ + this->readAttr(SPAttr::X); + this->readAttr(SPAttr::Y); + this->readAttr(SPAttr::WIDTH); + this->readAttr(SPAttr::HEIGHT); + this->readAttr(SPAttr::VIEWBOX); + this->readAttr(SPAttr::PRESERVEASPECTRATIO); + this->readAttr(SPAttr::ONLOAD); + + SPGroup::build(document, repr); + + // Search for first 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 + this->transform = Geom::identity(); +} + +void SPRoot::release() +{ + this->defs = nullptr; + + SPGroup::release(); +} + + +void SPRoot::set(SPAttr key, const gchar *value) +{ + switch (key) { + case SPAttr::VERSION: + if (!sp_version_from_string(value, &this->version.svg)) { + this->version.svg = this->original.svg; + } + break; + + case SPAttr::INKSCAPE_VERSION: + if (!sp_version_from_string(value, &this->version.inkscape)) { + this->version.inkscape = this->original.inkscape; + } + break; + + case SPAttr::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 SPAttr::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 SPAttr::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 SPAttr::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 SPAttr::VIEWBOX: + set_viewBox( value ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; + + case SPAttr::PRESERVEASPECTRATIO: + set_preserveAspectRatio( value ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; + + case SPAttr::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 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 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 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, false, cloned ? dynamic_cast(parent) : nullptr); + + // 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(v->arenaitem); + g->setChildTransform(this->c2p); + } +} + +void SPRoot::modified(unsigned int flags) +{ + SPGroup::modified(flags); + + if (!this->parent && (flags & SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + // Size of viewport has changed. + document->getNamedView()->updateViewPort(); + } +} + + +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) { + repr->setAttributeSvgDouble("x", this->x.computed); + } + + if (fabs(this->y.computed) > 1e-9) { + repr->setAttributeSvgDouble("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)); + + this->write_viewBox(repr); + this->write_preserveAspectRatio(repr); + + 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(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::typeName() const { + return "image"; +} + +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 : -- cgit v1.2.3