summaryrefslogtreecommitdiffstats
path: root/ksvg/core/KSVGReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ksvg/core/KSVGReader.cpp')
-rw-r--r--ksvg/core/KSVGReader.cpp504
1 files changed, 504 insertions, 0 deletions
diff --git a/ksvg/core/KSVGReader.cpp b/ksvg/core/KSVGReader.cpp
new file mode 100644
index 00000000..3fbbefd3
--- /dev/null
+++ b/ksvg/core/KSVGReader.cpp
@@ -0,0 +1,504 @@
+/*
+ Copyright (C) 2001-2003 KSVG Team
+ This file is part of the KDE project
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <dom/dom_exception.h>
+#include <kdebug.h>
+#include <tdelocale.h>
+#include <tqmap.h>
+#include <ksimpleconfig.h>
+#include <KSVGCanvas.h>
+#include "KSVGReader.moc"
+#include "SVGSVGElementImpl.h"
+#include "SVGViewSpecImpl.h"
+#include "SVGDocumentImpl.h"
+#include "SVGMatrixImpl.h"
+#include "SVGShapeImpl.h"
+#include "SVGLengthImpl.h"
+#include "SVGImageElementImpl.h"
+#include "SVGAnimatedLengthImpl.h"
+#include "SVGUseElementImpl.h"
+
+namespace KSVG
+{
+
+class Helper
+{
+public:
+ static Helper *self(KSVGReader *reader = 0);
+
+ void destroy();
+
+ void setFit(bool bFit = true) { m_bFit = bFit; }
+ bool fit() { return m_bFit; }
+
+ SVGDocumentImpl *doc() const { return m_reader->doc(); }
+ KSVGCanvas *canvas() const { return m_reader->canvas(); }
+
+ void addSVGElement(SVGSVGElementImpl *one, DOM::NodeImpl *two) { m_svgMap.insert(two, one); }
+ SVGSVGElementImpl *nextSVGElement(SVGElementImpl *elem);
+ SVGSVGElementImpl *nextSVGElement(DOM::Node elem);
+ void setFinished(bool error, const TQString &errorDesc = "") { m_reader->setFinished(error, errorDesc); }
+
+ // Error handling
+ void setErrorDescription(const TQString &err) { m_errorDesc = err; }
+ TQString errorDescription() { return m_errorDesc; }
+ bool hasError() const { return !m_errorDesc.isEmpty(); }
+
+ bool getURLMode() const { return m_getURLMode; }
+ void setGetURLMode(bool mode) { m_getURLMode = mode; }
+
+ TQString SVGFragmentId() const { return m_SVGFragmentId; }
+ void setSVGFragmentId(const TQString &SVGFragmentId) { m_SVGFragmentId = SVGFragmentId; }
+
+protected:
+ Helper(KSVGReader *reader);
+
+private:
+ Helper();
+ Helper(const Helper &rhs);
+ Helper &operator=(const Helper &rhs);
+
+ static Helper *m_instance;
+ TQMap<DOM::NodeImpl *, SVGSVGElementImpl *> m_svgMap;
+ KSVGReader *m_reader;
+ bool m_bFit;
+ bool m_getURLMode;
+ TQString m_errorDesc;
+ TQString m_SVGFragmentId;
+};
+
+class InputHandler : public TQXmlDefaultHandler
+{
+public:
+ virtual bool startDocument();
+ virtual bool endDocument();
+ virtual bool startElement(const TQString &namespaceURI,
+ const TQString &localName,
+ const TQString &qName,
+ const TQXmlAttributes &atts);
+ virtual bool endElement(const TQString &namespaceURI,
+ const TQString &localName,
+ const TQString &qName);
+ virtual bool characters(const TQString &ch);
+ virtual bool warning(const TQXmlParseException &e);
+ virtual bool error(const TQXmlParseException &e);
+ virtual bool fatalError(const TQXmlParseException &e);
+
+private:
+ DOM::Node *m_rootNode;
+ DOM::Node *m_currentNode;
+ DOM::Node m_parentNode;
+
+ bool m_noRendering, m_progressive;
+};
+
+}
+
+using namespace KSVG;
+
+Helper *Helper::m_instance = 0;
+
+Helper::Helper(KSVGReader *reader)
+{
+ m_reader = reader;
+}
+
+Helper *Helper::self(KSVGReader *reader)
+{
+ if(m_instance && reader != 0)
+ m_instance->m_reader = reader;
+ if(!m_instance)
+ {
+ Q_ASSERT(reader != 0);
+ m_instance = new Helper(reader);
+ }
+
+ return m_instance;
+}
+
+void Helper::destroy()
+{
+ m_svgMap.clear();
+}
+
+SVGSVGElementImpl *Helper::nextSVGElement(SVGElementImpl *elem)
+{
+ return nextSVGElement(*elem);
+}
+
+SVGSVGElementImpl *Helper::nextSVGElement(DOM::Node elem)
+{
+ DOM::Node foundSVG;
+ DOM::Node shape = elem.parentNode();
+
+ for(; !shape.isNull(); shape = shape.parentNode())
+ {
+ if(reinterpret_cast<DOM::Element &>(shape).nodeName() == "svg")
+ {
+ foundSVG = shape;
+ break;
+ }
+ }
+
+ SVGSVGElementImpl *svg = m_svgMap[foundSVG.handle()];
+ return svg;
+}
+
+bool InputHandler::startDocument()
+{
+ m_rootNode = 0;
+ m_currentNode = 0;
+ m_noRendering = false;
+
+ KSimpleConfig config("ksvgpluginrc");
+ config.setGroup("Rendering");
+ m_progressive = config.readBoolEntry("ProgressiveRendering", true);
+
+ if(Helper::self()->canvas())
+ Helper::self()->canvas()->setImmediateUpdate(m_progressive);
+
+ return true;
+}
+
+bool InputHandler::endDocument()
+{
+ Helper::self()->setFinished(false);
+ if (Helper::self()->canvas())
+ Helper::self()->canvas()->setImmediateUpdate(false);
+
+ return true;
+}
+
+bool InputHandler::characters(const TQString &ch)
+{
+ kdDebug(26001) << "InputHandler::characters, read " << ch << endl;
+
+ if(ch.simplifyWhiteSpace().isEmpty())
+ return true;
+
+ TQString t = ch;
+
+ SVGSVGElementImpl *root = Helper::self()->nextSVGElement(*m_currentNode);
+ if(root)
+ {
+ SVGElementImpl *element = root->ownerDoc()->getElementFromHandle(m_currentNode->handle());
+ SVGLangSpaceImpl *langSpace = dynamic_cast<SVGLangSpaceImpl *>(element);
+
+ if(langSpace)
+ t = langSpace->handleText(ch);
+ }
+
+ if(!t.isEmpty())
+ {
+ DOM::Text impl = static_cast<DOM::Document *>(Helper::self()->doc())->createTextNode(t);
+ m_currentNode->appendChild(impl);
+ }
+
+ return true;
+}
+
+bool InputHandler::startElement(const TQString &namespaceURI, const TQString &, const TQString &qName, const TQXmlAttributes &attrs)
+{
+ kdDebug(26001) << "InputHandler::startElement, namespaceURI " << namespaceURI << " qName " << qName << endl;
+
+ SVGElementImpl *newElement = 0;
+ SVGSVGElementImpl *svg = 0;
+
+ if(qName == "svg")
+ {
+ DOM::Element impl = static_cast<DOM::Document *>(Helper::self()->doc())->createElementNS(namespaceURI, qName);
+ newElement = SVGDocumentImpl::createElement(qName, impl, Helper::self()->doc());
+ svg = dynamic_cast<SVGSVGElementImpl *>(newElement);
+
+ Helper::self()->addSVGElement(svg, impl.handle());
+
+ // Need this before we can find our ownerSVGElement (AP)
+ if(m_currentNode != 0)
+ {
+ m_currentNode->appendChild(*svg);
+ }
+ else
+ {
+ if(Helper::self()->fit())
+ { // handle fitting of svg into small drawing area(thumb)
+ // TODO : aspectratio? and what about svgs that dont provide width and height?
+ if(attrs.value("viewBox").isEmpty())
+ {
+ SVGLengthImpl *width = SVGSVGElementImpl::createSVGLength();
+ SVGLengthImpl *height = SVGSVGElementImpl::createSVGLength();
+ width->setValueAsString(attrs.value("width"));
+ height->setValueAsString(attrs.value("height"));
+ TQString viewbox = TQString("0 0 %1 %2").arg(width->value()).arg(height->value());
+ //kdDebug(26001) << "VIEWBOX : " << viewbox.latin1() << endl;
+ // HACK
+ // Does the existing attribute need to be deleted before appending the new attribute?
+ const_cast<TQXmlAttributes&>(attrs).append("viewBox", TQString::null, "viewBox", viewbox);
+ width->deref();
+ height->deref();
+ }
+ // HACK
+ // Does the existing attribute need to be deleted before appending the new attribute?
+ const_cast<TQXmlAttributes&>(attrs).append("width", TQString::null, "width", TQString::number(Helper::self()->canvas()->width()));
+ const_cast<TQXmlAttributes&>(attrs).append("height", TQString::null, "height", TQString::number(Helper::self()->canvas()->height()));
+ }
+
+ if(!Helper::self()->SVGFragmentId().isEmpty())
+ {
+ if(svg->currentView()->parseViewSpec(Helper::self()->SVGFragmentId()))
+ {
+ svg->setUseCurrentView(true);
+ }
+ }
+ }
+
+ if(m_rootNode == 0)
+ {
+ Helper::self()->doc()->appendChild(*svg);
+ Helper::self()->doc()->setRootElement(svg);
+
+ m_rootNode = svg;
+ }
+ }
+ else
+ {
+ if(!m_rootNode && !Helper::self()->getURLMode())
+ {
+ Helper::self()->setErrorDescription(i18n("A legal svg document requires a <svg> root element"));
+ return false;
+ }
+
+ DOM::Element impl = static_cast<DOM::Document *>(Helper::self()->doc())->createElementNS(namespaceURI, qName);
+ newElement = SVGDocumentImpl::createElement(qName, impl, Helper::self()->doc());
+
+ // m_currentNode == 0 if we are dynamically extending the dom (parsexml...)
+ // and the file doesn't have a root element
+ if(m_currentNode != 0)
+ m_currentNode->appendChild(*newElement);
+ else
+ Helper::self()->doc()->appendChild(*newElement);
+
+ // Special logics:
+ if(qName == "switch" || qName == "pattern" || qName == "mask")
+ m_noRendering = true;
+ }
+
+ newElement->setOwnerSVGElement(Helper::self()->nextSVGElement(newElement));
+ newElement->setViewportElement(newElement->ownerSVGElement());
+
+ newElement->setAttributes(attrs);
+
+ if(svg && svg->ownerSVGElement() == 0)
+ {
+ SVGImageElementImpl *parentImage = Helper::self()->doc()->parentImage();
+
+ if(parentImage)
+ {
+ // We're being displayed in a document via an 'image' element. Set
+ // us up to fit into it's rectangle.
+ parentImage->setupSVGElement(svg);
+ }
+ }
+
+ SVGLocatableImpl *locatable = dynamic_cast<SVGLocatableImpl *>(newElement);
+
+ if(locatable)
+ {
+ // Set up the cached screenCTM
+ SVGLocatableImpl *locatableParent = 0;
+ DOM::Node parentNode = newElement->parentNode();
+
+ if(!parentNode.isNull())
+ {
+ SVGElementImpl *parent = Helper::self()->doc()->getElementFromHandle(parentNode.handle());
+
+ if(parent)
+ locatableParent = dynamic_cast<SVGLocatableImpl *>(parent);
+ }
+
+ SVGMatrixImpl *parentMatrix = 0;
+
+ if(locatableParent)
+ parentMatrix = locatableParent->getScreenCTM();
+ else
+ parentMatrix = SVGSVGElementImpl::createSVGMatrix();
+
+ locatable->updateCachedScreenCTM(parentMatrix);
+ parentMatrix->deref();
+ }
+
+ m_currentNode = newElement;
+ return !Helper::self()->hasError();
+}
+
+bool InputHandler::endElement(const TQString &, const TQString &, const TQString &qName)
+{
+ kdDebug(26001) << "InputHandler::endElement, qName " << qName << endl;
+
+ bool haveCanvas = Helper::self()->canvas();
+
+ SVGSVGElementImpl *root = Helper::self()->nextSVGElement(*m_currentNode);
+ SVGElementImpl *element = root ? root->ownerDoc()->getElementFromHandle(m_currentNode->handle()) : Helper::self()->doc()->getElementFromHandle(m_currentNode->handle());
+ SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(element);
+ SVGTestsImpl *tests = dynamic_cast<SVGTestsImpl *>(element);
+ SVGStylableImpl *style = dynamic_cast<SVGStylableImpl *>(element);
+
+ if(qName != "script" && !m_noRendering && !Helper::self()->getURLMode())
+ {
+ if(!root)
+ {
+ if(haveCanvas)
+ {
+ if(!m_progressive)
+ Helper::self()->canvas()->update();
+
+ Helper::self()->canvas()->blit();
+
+ TQValueList<SVGUseElementImpl *> forwardReferencingUseElements = Helper::self()->doc()->forwardReferencingUseElements();
+
+ if(!forwardReferencingUseElements.isEmpty())
+ {
+ // Create the elements again now that we have parsed the whole document.
+ TQValueList<SVGUseElementImpl *>::iterator it;
+
+ Helper::self()->canvas()->setImmediateUpdate(false);
+
+ for(it = forwardReferencingUseElements.begin(); it != forwardReferencingUseElements.end(); it++)
+ (*it)->createItem(Helper::self()->canvas());
+
+ // The newly created items will need to be moved into their correct z-order positions.
+ Helper::self()->doc()->resortZIndicesOnFinishedLoading();
+ }
+ }
+
+ return true; // we have reached the bottom
+ }
+
+ if(haveCanvas && (tests ? tests->ok() : true))
+ {
+ if((shape && !shape->isContainer()) || (!shape && element))
+ element->createItem();
+ }
+ }
+
+ // Special logics:
+ if(qName == "switch" || qName == "pattern" || qName == "mask")
+ {
+ m_noRendering = false;
+ bool ok = tests ? tests->ok() : true;
+
+ if((haveCanvas && element && style && ok && style->getDisplay() && style->getVisible() && (qName == "pattern")) || (shape && shape->directRender()))
+ element->createItem();
+ }
+
+ m_parentNode = m_currentNode->parentNode(); // this is needed since otherwise we get temporary vars
+ m_currentNode = &m_parentNode;
+
+ return true;
+}
+
+bool InputHandler::warning(const TQXmlParseException &e)
+{
+ kdDebug(26001) << "[" << e.lineNumber() << ":" << e.columnNumber() << "]: WARNING: " << e.message() << endl;
+ return true;
+}
+
+bool InputHandler::error(const TQXmlParseException &e)
+{
+ kdDebug(26001) << "[" << e.lineNumber() << ":" << e.columnNumber() << "]: ERROR: " << e.message() << endl;
+ return true;
+}
+
+bool InputHandler::fatalError(const TQXmlParseException &e)
+{
+ TQString error;
+
+ if(Helper::self()->hasError())
+ {
+ error = Helper::self()->errorDescription();
+ Helper::self()->setErrorDescription(TQString());
+ }
+ else
+ error = TQString("[%1:%2]: FATAL ERROR: %3").arg(e.lineNumber()).arg(e.columnNumber()).arg(e.message());
+
+ kdDebug(26001) << "InputHandler::fatalError, " << error << endl;
+
+ Helper::self()->setFinished(true, error);
+ return true;
+}
+
+struct KSVGReader::Private
+{
+ TQXmlSimpleReader *reader;
+ InputHandler *inputHandler;
+ SVGDocumentImpl *doc;
+ KSVGCanvas *canvas;
+};
+
+KSVGReader::KSVGReader(SVGDocumentImpl *doc, KSVGCanvas *canvas, ParsingArgs args) : TQObject(), d(new Private)
+{
+ d->doc = doc;
+ d->canvas = canvas;
+
+ d->reader = new TQXmlSimpleReader();
+ d->inputHandler = new InputHandler();
+
+ Helper::self(this);
+ Helper::self()->setFit(args.fit);
+ Helper::self()->setGetURLMode(args.getURLMode);
+ Helper::self()->setSVGFragmentId(args.SVGFragmentId);
+
+ d->reader->setContentHandler(d->inputHandler);
+ d->reader->setErrorHandler(d->inputHandler);
+}
+
+KSVGReader::~KSVGReader()
+{
+ Helper::self()->destroy();
+
+ delete d->reader;
+ delete d->inputHandler;
+ delete d;
+}
+
+void KSVGReader::parse(TQXmlInputSource *source)
+{
+ d->reader->parse(source);
+}
+
+void KSVGReader::finishParsing(bool, const TQString &errorDesc)
+{
+ Helper::self()->setErrorDescription(errorDesc);
+}
+
+void KSVGReader::setFinished(bool error, const TQString &errorDesc)
+{
+ kdDebug(26001) << "KSVGReader::setFinished" << endl;
+ emit finished(error, errorDesc);
+}
+
+SVGDocumentImpl *KSVGReader::doc()
+{
+ return d->doc;
+}
+
+KSVG::KSVGCanvas *KSVGReader::canvas()
+{
+ return d->canvas;
+}