summaryrefslogtreecommitdiffstats
path: root/ksvg/impl/SVGDocumentImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ksvg/impl/SVGDocumentImpl.cpp')
-rw-r--r--ksvg/impl/SVGDocumentImpl.cpp703
1 files changed, 703 insertions, 0 deletions
diff --git a/ksvg/impl/SVGDocumentImpl.cpp b/ksvg/impl/SVGDocumentImpl.cpp
new file mode 100644
index 00000000..ce6de16e
--- /dev/null
+++ b/ksvg/impl/SVGDocumentImpl.cpp
@@ -0,0 +1,703 @@
+/*
+ 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 <kdebug.h>
+
+#define USE_VALGRIND 0
+
+#if USE_VALGRIND
+#include <valgrind/calltree.h>
+#endif
+
+#include "SVGEvent.h"
+#include "SVGMatrixImpl.h"
+#include "SVGWindowImpl.h"
+#include "SVGElementImpl.h"
+#include "SVGDocumentImpl.moc"
+#include "SVGSVGElementImpl.h"
+#include "SVGImageElementImpl.h"
+#include "SVGScriptElementImpl.h"
+#include "SVGTitleElementImpl.h"
+#include "SVGAnimationElementImpl.h"
+
+#include "KSVGReader.h"
+#include "KSVGLoader.h"
+#include "KSVGCanvas.h"
+#include "CanvasItem.h"
+
+#include <tqpaintdevicemetrics.h>
+
+using namespace KSVG;
+
+#include "SVGDocumentImpl.lut.h"
+#include "ksvg_scriptinterpreter.h"
+#include "ksvg_bridge.h"
+#include "ksvg_ecma.h"
+
+// A sequence of prime numbers that sets the m_elemDict's hash table size as the
+// number of elements in the dictionary passes each level. This keeps the lookup
+// performance high as the number of elements grows. See the TQDict documentation.
+unsigned int SVGDocumentImpl::elemDictHashSizes [] =
+{
+ 101,
+ 211,
+ 401,
+ 809,
+ 1601,
+ 3203,
+ 6421,
+ 12809,
+ 25601,
+ 51203,
+ 102407,
+ 204803,
+ 409609,
+ 819229
+};
+
+const int SVGDocumentImpl::numElemDictHashSizes = sizeof(elemDictHashSizes) / sizeof(elemDictHashSizes[0]);
+
+SVGDocumentImpl::SVGDocumentImpl(bool anim, bool fit, SVGImageElementImpl *parentImage) : TQObject(), DOM::DomShared(), DOM::Document(), SVGDOMNodeBridge(static_cast<DOM::Node>(*this))
+{
+ m_animations = anim;
+
+ m_reader = 0;
+ m_loader = 0;
+ m_canvas = 0;
+ m_rootElement = 0;
+ m_lastTarget = 0;
+ m_window = 0;
+
+ m_elemDictHashSizeIndex = 0;
+ m_elemDict.resize(elemDictHashSizes[m_elemDictHashSizeIndex]);
+
+ m_timeScheduler = new SVGTimeScheduler(this);
+ m_ecmaEngine = new KSVGEcma(this);
+ m_ecmaEngine->setup();
+
+ m_finishedParsing = false;
+ m_finishedLoading = false;
+ m_resortZIndicesOnFinishedLoading = false;
+ m_fit = fit;
+
+ m_parentImage = parentImage;
+ if(m_parentImage)
+ m_parentImage->ref();
+}
+
+SVGDocumentImpl::~SVGDocumentImpl()
+{
+ if(rootElement() && rootElement()->hasEventListener(SVGEvent::UNLOAD_EVENT, true))
+ rootElement()->dispatchEvent(SVGEvent::UNLOAD_EVENT, false, false);
+
+ TQPtrList<SVGShapeImpl> killList;
+
+ DOM::Node node = firstChild();
+ for(; !node.isNull(); node = node.nextSibling())
+ {
+ SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(getElementFromHandle(node.handle()));
+ if(shape)
+ killList.append(shape);
+ }
+
+ SVGShapeImpl *rend = 0;
+ for(rend = killList.first(); rend; rend = killList.next())
+ delete rend;
+
+ delete m_timeScheduler;
+ delete m_ecmaEngine;
+ delete m_reader;
+ delete m_loader;
+
+ if(m_window)
+ m_window->deref();
+
+ if(m_parentImage)
+ m_parentImage->deref();
+}
+
+SVGWindowImpl *SVGDocumentImpl::window()
+{
+ if(!m_window)
+ {
+ m_window = new SVGWindowImpl(const_cast<SVGDocumentImpl *>(this));
+ m_window->ref();
+ }
+
+ return m_window;
+}
+
+float SVGDocumentImpl::screenPixelsPerMillimeterX() const
+{
+ if(canvas() && canvas()->drawWindow())
+ {
+ TQPaintDeviceMetrics metrics(canvas()->drawWindow());
+ return float(metrics.width()) / float(metrics.widthMM());
+ }
+ else
+ return 90.0;
+}
+
+float SVGDocumentImpl::screenPixelsPerMillimeterY() const
+{
+ if(canvas() && canvas()->drawWindow())
+ {
+ TQPaintDeviceMetrics metrics(canvas()->drawWindow());
+ return float(metrics.height()) / float(metrics.heightMM());
+ }
+ else
+ return 90.0;
+}
+
+DOM::DOMString SVGDocumentImpl::title() const
+{
+ DOM::Node n;
+ for(n = rootElement()->firstChild(); !n.isNull(); n = n.nextSibling())
+ {
+ SVGElementImpl *elem = getElementFromHandle(n.handle());
+ if(dynamic_cast<SVGTitleElementImpl *>(elem))
+ return elem->collectText();
+ }
+ return "";
+}
+
+DOM::DOMString SVGDocumentImpl::referrer() const
+{
+ return m_referrer;
+}
+
+DOM::DOMString SVGDocumentImpl::domain() const
+{
+ return m_baseURL.host();
+}
+
+DOM::DOMString SVGDocumentImpl::URL() const
+{
+ return m_baseURL.prettyURL();
+}
+
+void SVGDocumentImpl::setReferrer(const DOM::DOMString &referrer)
+{
+ // TODO : better may be to request for referrer instead of storing it
+ m_referrer = referrer;
+}
+
+void SVGDocumentImpl::setRootElement(SVGSVGElementImpl *elem)
+{
+ m_rootElement = elem;
+}
+
+SVGSVGElementImpl *SVGDocumentImpl::rootElement() const
+{
+ return m_rootElement;
+}
+
+SVGElementImpl *SVGDocumentImpl::createElement(const DOM::DOMString &name, DOM::Element impl, SVGDocumentImpl *doc)
+{
+ DOM::ElementImpl *handle = reinterpret_cast<DOM::ElementImpl *>(impl.handle());
+ SVGElementImpl *element = SVGElementImpl::Factory::self()->create(std::string(name.string().latin1()), handle);
+
+ if(!element)
+ element = new SVGElementImpl(handle);
+
+ element->setOwnerDoc(doc);
+ element->ref();
+ return element;
+}
+
+bool SVGDocumentImpl::open(const ::KURL &url)
+{
+ if(!url.prettyURL().isEmpty())
+ {
+ m_baseURL = url;
+
+ if(!m_loader)
+ m_loader = new KSVGLoader();
+
+ connect(m_loader, TQT_SIGNAL(gotResult(TQIODevice *)), this, TQT_SLOT(slotSVGContent(TQIODevice *)));
+ m_loader->getSVGContent(url);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+void SVGDocumentImpl::slotSVGContent(TQIODevice *dev)
+{
+ TQXmlInputSource *inputSource = new TQXmlInputSource(dev);
+
+ if(m_reader)
+ delete m_reader;
+
+ KSVGReader::ParsingArgs args;
+ args.fit = m_fit;
+ args.getURLMode = false;
+
+ TQString url = m_baseURL.prettyURL();
+ int pos = url.find('#'); // url can become like this.svg#svgView(viewBox(63,226,74,74)), get part after '#'
+ if(pos > -1)
+ args.SVGFragmentId = url.mid(pos + 1);
+
+ m_reader = new KSVGReader(this, m_canvas, args);
+ connect(m_reader, TQT_SIGNAL(finished(bool, const TQString &)), this, TQT_SLOT(slotFinishedParsing(bool, const TQString &)));
+ m_t.start();
+
+#if USE_VALGRIND
+ CALLTREE_ZERO_STATS();
+#endif
+
+ m_reader->parse(inputSource);
+ delete dev;
+}
+
+void SVGDocumentImpl::parseSVG(TQXmlInputSource *inputSource, bool getURLMode)
+{
+ if(m_reader)
+ delete m_reader;
+
+ KSVGReader::ParsingArgs args;
+ args.fit = m_fit;
+ args.getURLMode = getURLMode;
+ m_reader = new KSVGReader(this, 0, args);
+ connect(m_reader, TQT_SIGNAL(finished(bool, const TQString &)), this, TQT_SLOT(slotFinishedParsing(bool, const TQString &)));
+
+#if USE_VALGRIND
+ CALLTREE_ZERO_STATS();
+#endif
+
+ m_reader->parse(inputSource);
+}
+
+void SVGDocumentImpl::finishParsing(bool error, const TQString &errorDesc)
+{
+ if(m_reader)
+ m_reader->finishParsing(error, errorDesc);
+}
+
+void SVGDocumentImpl::slotFinishedParsing(bool error, const TQString &errorDesc)
+{
+ kdDebug(26000) << k_funcinfo << "total time : " << m_t.elapsed() << endl;
+
+#if USE_VALGRIND
+ CALLTREE_DUMP_STATS();
+#endif
+
+ if(m_animations)
+ m_timeScheduler->startAnimations();
+
+ if(m_canvas && !error && rootElement())
+ executeScripts();
+
+ m_finishedParsing = true;
+
+ emit finishedParsing(error, errorDesc);
+ if(!error)
+ emit finishedRendering();
+
+ checkFinishedLoading();
+}
+
+void SVGDocumentImpl::newImageJob(SVGImageElementImpl *image)
+{
+ kdDebug(26002) << "SVGDocumentImpl::newImageJob, " << image << endl;
+ m_loader->newImageJob(image, m_baseURL);
+}
+
+void SVGDocumentImpl::notifyImageLoading(SVGImageElementImpl *image)
+{
+ m_imagesLoading.append(image);
+}
+
+void SVGDocumentImpl::notifyImageLoaded(SVGImageElementImpl *image)
+{
+ m_imagesLoading.remove(image);
+
+ if(m_imagesLoading.isEmpty())
+ checkFinishedLoading();
+}
+
+void SVGDocumentImpl::checkFinishedLoading()
+{
+ if(m_finishedParsing && m_imagesLoading.isEmpty())
+ {
+ m_finishedLoading = true;
+
+ if(m_resortZIndicesOnFinishedLoading)
+ {
+ // Only resort if we're the 'outermost' document, i.e. we're not an svg image
+ // inside another document. We could resort as each image finishes loading, but it
+ // slows down the parsing phase.
+ if(m_parentImage == 0 && m_canvas && m_rootElement)
+ {
+ m_canvas->setElementItemZIndexRecursive(m_rootElement, 0);
+ m_canvas->update();
+ }
+ }
+
+ emit finishedLoading();
+ }
+}
+
+void SVGDocumentImpl::addForwardReferencingUseElement(SVGUseElementImpl *use)
+{
+ if(!m_forwardReferencingUseElements.contains(use))
+ m_forwardReferencingUseElements.append(use);
+}
+
+void SVGDocumentImpl::slotPaint()
+{
+ rerender();
+}
+
+void SVGDocumentImpl::rerender()
+{
+ m_canvas->update();
+ emit finishedRendering();
+}
+
+void SVGDocumentImpl::attach(KSVG::KSVGCanvas *c)
+{
+ m_canvas = c;
+}
+
+void SVGDocumentImpl::detach()
+{
+ m_canvas = 0;
+}
+
+KSVG::KSVGCanvas *SVGDocumentImpl::canvas() const
+{
+ return m_canvas;
+}
+
+void SVGDocumentImpl::syncCachedMatrices()
+{
+ if(rootElement())
+ {
+ SVGMatrixImpl *parentMatrix = SVGSVGElementImpl::createSVGMatrix();
+ rootElement()->checkCachedScreenCTM(parentMatrix);
+ parentMatrix->deref();
+ }
+}
+
+void SVGDocumentImpl::executeScriptsRecursive(DOM::Node start)
+{
+ DOM::Node node = start.firstChild();
+
+ for(; !node.isNull(); node = node.nextSibling())
+ {
+ SVGElementImpl *element = getElementFromHandle(node.handle());
+
+ SVGContainerImpl *container = dynamic_cast<SVGContainerImpl *>(element);
+ if(container)
+ executeScriptsRecursive(node);
+
+ SVGScriptElementImpl *script = dynamic_cast<SVGScriptElementImpl *>(element);
+
+ if(script)
+ script->executeScript(DOM::Node());
+ }
+}
+
+bool SVGDocumentImpl::executeScriptsRecursiveCheck(DOM::Node start)
+{
+ bool test = true;
+
+ DOM::Node node = start.firstChild();
+
+ for(; !node.isNull(); node = node.nextSibling())
+ {
+ SVGElementImpl *element = getElementFromHandle(node.handle());
+
+ SVGContainerImpl *container = dynamic_cast<SVGContainerImpl *>(element);
+ if(container)
+ {
+ if(!executeScriptsRecursiveCheck(node))
+ return false;
+ }
+
+ SVGScriptElementImpl *script = dynamic_cast<SVGScriptElementImpl *>(element);
+
+ if(script)
+ {
+ if(!script->canExecuteScript())
+ {
+ test = false;
+ break;
+ }
+ }
+ }
+
+ return test;
+}
+
+void SVGDocumentImpl::executeScripts()
+{
+ bool test = executeScriptsRecursiveCheck(*rootElement());
+
+ if(!test)
+ TQTimer::singleShot(50, this, TQT_SLOT(executeScripts()));
+ else
+ {
+ executeScriptsRecursive(*rootElement());
+
+ // mop: only rerender if an loadevent has been found
+ if(dispatchRecursiveEvent(SVGEvent::LOAD_EVENT, lastChild()))
+ m_canvas->update();
+ }
+}
+
+// Dispatches a non-cancelable, non-bubbles event to every child
+bool SVGDocumentImpl::dispatchRecursiveEvent(SVGEvent::EventId id, DOM::Node start)
+{
+ bool eventExecuted = false;
+
+ // Iterate the tree, backwards, and dispatch the event to every child
+ DOM::Node node = start;
+ for(; !node.isNull(); node = node.previousSibling())
+ {
+ SVGElementImpl *element = getElementFromHandle(node.handle());
+
+ if(element && element->hasChildNodes())
+ {
+ // Dispatch to all children
+ eventExecuted = dispatchRecursiveEvent(id, element->lastChild()) ? true : eventExecuted;
+
+ // Dispatch, locally
+ if(element->hasEventListener(id, true))
+ {
+ element->dispatchEvent(id, false, false);
+ eventExecuted = true;
+ }
+ }
+ else if(element && element->hasEventListener(id, true))
+ {
+ element->dispatchEvent(id, false, false);
+ eventExecuted = true;
+ }
+ }
+
+ return eventExecuted;
+}
+
+SVGEventListener *SVGDocumentImpl::createEventListener(DOM::DOMString type)
+{
+ return m_ecmaEngine->createEventListener(type);
+}
+
+SVGElementImpl *SVGDocumentImpl::recursiveSearch(DOM::Node start, const DOM::DOMString &id)
+{
+ DOM::Node node = start.firstChild();
+ for(; !node.isNull(); node = node.nextSibling())
+ {
+ SVGElementImpl *test = getElementFromHandle(node.handle());
+
+ // Look in containers
+ SVGContainerImpl *container = dynamic_cast<SVGContainerImpl *>(test);
+ if(container)
+ {
+ SVGElementImpl *found = recursiveSearch(node, id);
+ if(found)
+ return found;
+ }
+
+ // Look in SVGSVGElementImpl's
+ SVGSVGElementImpl *svgtest = dynamic_cast<SVGSVGElementImpl *>(test);
+ if(svgtest)
+ {
+ SVGElementImpl *element = svgtest->getElementById(id);
+ if(element)
+ return element;
+ }
+ }
+
+ return 0;
+}
+
+/*
+@namespace KSVG
+@begin SVGDocumentImpl::s_hashTable 9
+ title SVGDocumentImpl::Title DontDelete|ReadOnly
+ referrer SVGDocumentImpl::Referrer DontDelete|ReadOnly
+ domain SVGDocumentImpl::Domain DontDelete|ReadOnly
+ URL SVGDocumentImpl::Url DontDelete|ReadOnly
+ doctype SVGDocumentImpl::DocType DontDelete|ReadOnly
+ implementation SVGDocumentImpl::Implementation DontDelete|ReadOnly
+ rootElement SVGDocumentImpl::RootElement DontDelete|ReadOnly
+ documentElement SVGDocumentImpl::DocumentElement DontDelete|ReadOnly
+@end
+@namespace KSVG
+@begin SVGDocumentImplProto::s_hashTable 7
+ createTextNode SVGDocumentImpl::CreateTextNode DontDelete|Function 1
+ createElement SVGDocumentImpl::CreateElement DontDelete|Function 1
+ createElementNS SVGDocumentImpl::CreateElementNS DontDelete|Function 2
+ getElementById SVGDocumentImpl::GetElementById DontDelete|Function 1
+ getElementsByTagName SVGDocumentImpl::GetElementsByTagName DontDelete|Function 1
+ getElementsByTagNameNS SVGDocumentImpl::GetElementsByTagNameNS DontDelete|Function 2
+@end
+*/
+
+KSVG_IMPLEMENT_PROTOTYPE("SVGDocument", SVGDocumentImplProto, SVGDocumentImplProtoFunc)
+
+Value SVGDocumentImpl::getValueProperty(ExecState *exec, int token) const
+{
+ switch(token)
+ {
+ case Title:
+ return String(title().string());
+ case Referrer:
+ return String(referrer().string());
+ case Domain:
+ return String(domain().string());
+ case Url:
+ return String(URL().string());
+ case DocType:
+ return getDOMNode(exec, doctype());
+ case Implementation:
+ return (new SVGDOMDOMImplementationBridge(implementation()))->cache(exec);
+ case RootElement:
+ case DocumentElement:
+ return m_rootElement->cache(exec);
+ default:
+ kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl;
+ return Undefined();
+ }
+}
+
+Value SVGDocumentImplProtoFunc::call(ExecState *exec, Object &thisObj, const List &args)
+{
+ KSVG_CHECK_THIS(SVGDocumentImpl)
+
+ switch(id)
+ {
+ case SVGDocumentImpl::CreateTextNode:
+ return getDOMNode(exec, obj->createTextNode(args[0].toString(exec).string()));
+ case SVGDocumentImpl::CreateElement:
+ case SVGDocumentImpl::CreateElementNS:
+ {
+ SVGElementImpl *newElement = 0;
+
+ if(id == SVGDocumentImpl::CreateElement)
+ newElement = obj->createElement(args[0].toString(exec).qstring(), static_cast<DOM::Document *>(obj)->createElement(args[0].toString(exec).string()), obj);
+ else if(id == SVGDocumentImpl::CreateElementNS)
+ newElement = obj->createElement(args[1].toString(exec).qstring(), static_cast<DOM::Document *>(obj)->createElementNS(args[0].toString(exec).string(), args[1].toString(exec).string()), obj);
+
+ newElement->setOwnerSVGElement(obj->rootElement()); // FIXME: Correct in all situations?
+ newElement->setViewportElement(obj->rootElement());
+ newElement->setAttributes();
+
+ return getDOMNode(exec, *newElement);
+ }
+ case SVGDocumentImpl::GetElementById:
+ {
+ Value ret;
+
+ SVGElementImpl *element = obj->rootElement()->getElementById(args[0].toString(exec).string());
+
+ if(element)
+ ret = getDOMNode(exec, *element);
+ else
+ {
+ element = obj->recursiveSearch(*obj, args[0].toString(exec).string());
+ if(!element)
+ return Null();
+
+ ret = getDOMNode(exec, *element);
+ }
+
+ return ret;
+ }
+ case SVGDocumentImpl::GetElementsByTagName:
+ return (new SVGDOMNodeListBridge(obj->getElementsByTagName(args[0].toString(exec).string())))->cache(exec);
+ case SVGDocumentImpl::GetElementsByTagNameNS:
+ return (new SVGDOMNodeListBridge(obj->getElementsByTagNameNS(args[0].toString(exec).string(), args[1].toString(exec).string())))->cache(exec);
+ default:
+ break;
+ }
+
+ return KJS::Undefined();
+}
+
+SVGElementImpl *SVGDocumentImpl::getElementFromHandle(DOM::NodeImpl *handle) const
+{
+ return m_elemDict[handle];
+}
+
+void SVGDocumentImpl::addToElemDict(DOM::NodeImpl *handle, SVGElementImpl *obj)
+{
+ m_elemDict.insert(handle, obj);
+
+ if(m_elemDict.count()>m_elemDict.size() && m_elemDictHashSizeIndex<numElemDictHashSizes-1)
+ {
+ // Increase the hash table size to maintain good lookup speed.
+ m_elemDictHashSizeIndex++;
+ m_elemDict.resize(elemDictHashSizes[m_elemDictHashSizeIndex]);
+ }
+}
+
+void SVGDocumentImpl::removeFromElemDict(DOM::NodeImpl *handle)
+{
+ m_elemDict.remove(handle);
+}
+
+SVGDocumentImpl *SVGDocumentImpl::getDocumentFromHandle(DOM::NodeImpl *handle) const
+{
+ return m_documentDict[handle];
+}
+
+void SVGDocumentImpl::addToDocumentDict(DOM::NodeImpl *handle, SVGDocumentImpl *obj)
+{
+ m_documentDict.insert(handle, obj);
+}
+
+SVGElementImpl *SVGDocumentImpl::getElementByIdRecursive(SVGSVGElementImpl *start, const DOM::DOMString &elementId, bool dontSearch)
+{
+ SVGElementImpl *element = 0;
+
+ // #1 Look in passed SVGSVGElementImpl
+ if(start)
+ {
+ element = start->getElementById(elementId);
+
+ if(element)
+ return element;
+ }
+
+ // #2 Search in all child SVGSVGElementImpl's
+ element = recursiveSearch(*this, elementId);
+
+ if(element)
+ return element;
+
+ // #3 Search in other documents
+ if(!dontSearch)
+ {
+ TQPtrDictIterator<SVGDocumentImpl> it(m_documentDict);
+ for(; it.current(); ++it)
+ {
+ SVGElementImpl *temp = it.current()->getElementByIdRecursive(0, elementId, true);
+ if(temp)
+ return temp;
+ }
+ }
+
+ return element;
+}