diff options
Diffstat (limited to 'ksvg/core/KSVGReader.cpp')
-rw-r--r-- | ksvg/core/KSVGReader.cpp | 504 |
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; +} |