diff options
Diffstat (limited to 'ksvg/core')
-rw-r--r-- | ksvg/core/CanvasFactory.cpp | 176 | ||||
-rw-r--r-- | ksvg/core/CanvasFactory.h | 69 | ||||
-rw-r--r-- | ksvg/core/CanvasItem.h | 154 | ||||
-rw-r--r-- | ksvg/core/CanvasItems.cpp | 509 | ||||
-rw-r--r-- | ksvg/core/CanvasItems.h | 133 | ||||
-rw-r--r-- | ksvg/core/DocumentFactory.cpp | 110 | ||||
-rw-r--r-- | ksvg/core/DocumentFactory.h | 63 | ||||
-rw-r--r-- | ksvg/core/KSVGCanvas.cpp | 786 | ||||
-rw-r--r-- | ksvg/core/KSVGCanvas.h | 190 | ||||
-rw-r--r-- | ksvg/core/KSVGHelper.cpp | 92 | ||||
-rw-r--r-- | ksvg/core/KSVGHelper.h | 144 | ||||
-rw-r--r-- | ksvg/core/KSVGLoader.cpp | 449 | ||||
-rw-r--r-- | ksvg/core/KSVGLoader.h | 92 | ||||
-rw-r--r-- | ksvg/core/KSVGReader.cc | 500 | ||||
-rw-r--r-- | ksvg/core/KSVGReader.h | 67 | ||||
-rw-r--r-- | ksvg/core/KSVGTextChunk.cpp | 69 | ||||
-rw-r--r-- | ksvg/core/KSVGTextChunk.h | 54 | ||||
-rw-r--r-- | ksvg/core/Makefile.am | 16 | ||||
-rw-r--r-- | ksvg/core/ksvgrenderer.desktop | 56 |
19 files changed, 3729 insertions, 0 deletions
diff --git a/ksvg/core/CanvasFactory.cpp b/ksvg/core/CanvasFactory.cpp new file mode 100644 index 00000000..ca2822b2 --- /dev/null +++ b/ksvg/core/CanvasFactory.cpp @@ -0,0 +1,176 @@ +/* + Copyright (C) 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 + aint 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 <qfile.h> + +#include <kdebug.h> +#include <ksimpleconfig.h> +#include <kparts/componentfactory.h> + +#include "KSVGCanvas.h" +#include "CanvasFactory.h" + +using namespace KSVG; + +CanvasFactory *CanvasFactory::s_factory = 0; + +CanvasFactory::CanvasFactory() +{ + m_canvasList.setAutoDelete(true); +} + +CanvasFactory::~CanvasFactory() +{ +} + +CanvasFactory *CanvasFactory::self() +{ + if(!s_factory) + s_factory = new CanvasFactory(); + + return s_factory; +} + +void CanvasFactory::cleanup() +{ + m_canvasList.clear(); +} + +void CanvasFactory::queryCanvas() +{ + m_canvasList.clear(); + + QValueList<KService::Ptr> traderList = KTrader::self()->query("KSVG/Renderer", "(Type == 'Service')"); + KTrader::OfferList::Iterator it(traderList.begin()); + for( ; it != traderList.end(); ++it) + { + KService::Ptr ptr = (*it); + + QString name = ptr->property("Name").toString(); + QString internal = ptr->property("X-KSVG-InternalName").toString(); + if(name.isEmpty() || internal.isEmpty()) + continue; + + CanvasInfo *cinfo = new CanvasInfo(); + cinfo->service = ptr; + cinfo->canvas = 0; + cinfo->name = name; + cinfo->internal = internal; + + m_canvasList.append(cinfo); + } + + if(m_canvasList.isEmpty()) + { + kdError(26001) << "Couldn't load any canvas!!! FATAL ERROR." << endl; + return; + } +} + +KSVGCanvas *CanvasFactory::loadCanvas(int width, int height) +{ + queryCanvas(); + + KSimpleConfig *config = new KSimpleConfig("ksvgpluginrc", false); + config->setGroup("Canvas"); + QString load = config->readEntry("ActiveCanvas", "libart"); + delete config; + + QPtrListIterator<CanvasInfo> it(m_canvasList); + CanvasInfo *info = it.current(); + while((info = it.current()) != 0) + { + if(info->internal == load) + { + QStringList args; + args.prepend(QString::number(width)); + args.prepend(QString::number(height)); + + info->canvas = KParts::ComponentFactory::createInstanceFromLibrary<KSVGCanvas>(QFile::encodeName(info->service->library()), 0, 0, args); + + if(info->canvas) + return info->canvas; + else + { + kdError(26001) << "Failed to load canvas: " << load << " FATAL ERROR." << endl; + break; + } + } + + ++it; + } + + return 0; +} + +int CanvasFactory::itemInList(KSVGCanvas *canvas) +{ + QPtrListIterator<CanvasInfo> it(m_canvasList); + CanvasInfo *info = it.current(); + unsigned int i = 0; + while((info = it.current()) != 0) + { + if(info->canvas == canvas) + return i; + + i++; + ++it; + } + + return 0; +} + +QString CanvasFactory::internalNameFor(const QString &name) +{ + QPtrListIterator<CanvasInfo> it(m_canvasList); + CanvasInfo *info = it.current(); + while((info = it.current()) != 0) + { + if(info->name == name) + return info->internal; + + ++it; + } + + return QString::null; +} + +void CanvasFactory::deleteCanvas(KSVGCanvas *canvas) +{ + QPtrListIterator<CanvasInfo> it(m_canvasList); + CanvasInfo *info = it.current(); + while((info = it.current()) != 0) + { + if(info->canvas == canvas) + { + delete info->canvas; + info->canvas = 0; + } + + ++it; + } +} + +QPtrList<CanvasInfo> CanvasFactory::canvasList() +{ + return m_canvasList; +} + +// vim:ts=4:noet diff --git a/ksvg/core/CanvasFactory.h b/ksvg/core/CanvasFactory.h new file mode 100644 index 00000000..dc272700 --- /dev/null +++ b/ksvg/core/CanvasFactory.h @@ -0,0 +1,69 @@ +/* + Copyright (C) 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 + aint 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. +*/ + +#ifndef CANVASFACTORY_H +#define CANVASFACTORY_H + +#include <ktrader.h> + +#include <qstring.h> +#include <qptrlist.h> + +namespace KSVG +{ + +class KSVGCanvas; +class CanvasInfo +{ +public: + KService::Ptr service; + KSVGCanvas *canvas; + QString name, internal; +}; + +class CanvasFactory +{ +public: + CanvasFactory(); + ~CanvasFactory(); + + static CanvasFactory *self(); + + void cleanup(); + KSVGCanvas *loadCanvas(int width, int height); + + int itemInList(KSVGCanvas *canvas); + QString internalNameFor(const QString &name); + void deleteCanvas(KSVGCanvas *canvas); + + QPtrList<CanvasInfo> canvasList(); + +private: + void queryCanvas(); + + static CanvasFactory *s_factory; + QPtrList<CanvasInfo> m_canvasList; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/CanvasItem.h b/ksvg/core/CanvasItem.h new file mode 100644 index 00000000..7ffc7ad9 --- /dev/null +++ b/ksvg/core/CanvasItem.h @@ -0,0 +1,154 @@ +/* + Copyright (C) 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 + aint 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. +*/ + +#ifndef CANVASITEM_H +#define CANVASITEM_H + +#include <qrect.h> +#include <qpoint.h> +#include <qvaluelist.h> + +#define CHUNK_SIZE_HORIZONTAL 32 +#define CHUNK_SIZE_VERTICAL 32 + +namespace KSVG +{ + +class SVGElementImpl; + +enum RenderContext +{ + /* NONE = for initializing */ + NONE = 0, + /* NORMAL = use baseVal()'s for calculation, and fillRule */ + NORMAL = 1, + /* CLIPPING = use baseVal()'s for calculation, and clipRule */ + CLIPPING = 2, + /* ANIMATION = use animVal()'s for calculation, and fillRule */ + ANIMATION = 4 +}; + +enum CanvasItemUpdate +{ + UPDATE_STYLE, + UPDATE_LINEWIDTH, + UPDATE_TRANSFORM, + UPDATE_ZOOM, + UPDATE_PAN +}; + +class CanvasItem +{ +public: + CanvasItem() { m_zIndex = 0; m_referenced = false; } + virtual ~CanvasItem() { } + + virtual QRect bbox() const = 0; + virtual bool fillContains(const QPoint &) = 0; + virtual bool strokeContains(const QPoint &) = 0; + virtual void update(CanvasItemUpdate reason, int param1 = 0, int param2 = 0) = 0; + virtual void draw() = 0; + virtual bool isVisible() = 0; + + void setZIndex(unsigned int zIndex) { m_zIndex = zIndex; } + unsigned int zIndex() const { return m_zIndex; } + + void setReferenced(bool referenced) { m_referenced = referenced; } + bool referenced() const { return m_referenced; } + + virtual SVGElementImpl *element() const { return 0; } + +protected: + unsigned int m_zIndex; + bool m_referenced; +}; + +class CanvasItemPtr +{ +public: + CanvasItemPtr() : ptr(0) { } + CanvasItemPtr(CanvasItem *p) : ptr(p) { } + + bool operator<=(const CanvasItemPtr& that) const + { + // Order same-z objects by identity. + if(that.ptr->zIndex() == ptr->zIndex()) + return that.ptr >= ptr; + return that.ptr->zIndex() >= ptr->zIndex(); + } + bool operator<(const CanvasItemPtr& that) const + { + // Order same-z objects by identity. + if(that.ptr->zIndex() == ptr->zIndex()) + return that.ptr > ptr; + return that.ptr->zIndex() > ptr->zIndex(); + } + bool operator>(const CanvasItemPtr& that) const + { + // Order same-z objects by identity. + if(that.ptr->zIndex() == ptr->zIndex()) + return that.ptr < ptr; + return that.ptr->zIndex() < ptr->zIndex(); + } + bool operator==(const CanvasItemPtr& that) const { return that.ptr == ptr; } + operator CanvasItem *() const { return ptr; } + +private: + CanvasItem *ptr; +}; + +class CanvasItemList : public QValueList<CanvasItem *> +{ +public: + void sort() { qHeapSort(*((QValueList<CanvasItemPtr> *) this)); } +}; + +class CanvasChunk +{ +public: + CanvasChunk(short x, short y) : m_x(x), m_y(y), m_dirty(false) { } + + void sort() { m_list.sort(); } + const CanvasItemList &list() const { return m_list; } + + void add(CanvasItem *item) { m_list.prepend(item); m_dirty = true; } + void remove(CanvasItem *item) { m_list.remove(item); m_dirty = true; } + + void setDirty() { m_dirty = true; } + bool unsetDirty() { bool y = m_dirty; m_dirty = false; return y; } + bool isDirty() const { return m_dirty; } + + short x() const { return m_x; } + short y() const { return m_y; } + + QRect bbox() const { return QRect(m_x * CHUNK_SIZE_HORIZONTAL, m_y * CHUNK_SIZE_VERTICAL, CHUNK_SIZE_HORIZONTAL, CHUNK_SIZE_VERTICAL); } + +private: + CanvasItemList m_list; + short m_x; + short m_y; + bool m_dirty : 1; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/CanvasItems.cpp b/ksvg/core/CanvasItems.cpp new file mode 100644 index 00000000..0ffd017c --- /dev/null +++ b/ksvg/core/CanvasItems.cpp @@ -0,0 +1,509 @@ +/* + 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 + aint 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 "KSVGCanvas.h" +#include "KSVGHelper.h" +#include "KSVGTextChunk.h" + +#include "CanvasItems.h" + +#include "SVGMatrixImpl.h" +#include "SVGDocumentImpl.h" +#include "SVGSVGElementImpl.h" +#include "SVGPathElementImpl.h" +#include "SVGMarkerElementImpl.h" +#include "SVGTSpanElementImpl.h" +#include "SVGAnimatedLengthImpl.h" +#include "SVGAnimatedStringImpl.h" +#include "SVGAnimatedLengthListImpl.h" +#include "SVGAnimatedEnumerationImpl.h" + +#include <Glyph.h> +#include <Converter.h> +#include <Font.h> + +#include <kdebug.h> + +using namespace KSVG; + +CanvasText::CanvasText(SVGTextElementImpl *text) : CanvasItem(), m_text(text) +{ +} + +CanvasText::~CanvasText() +{ +} + +void CanvasText::handleTSpan(KSVGCanvas *canvas, const SVGMatrixImpl *screenCTM, int &curx, int &cury, int &endx, int &endy, SVGElementImpl *element, KSVGTextChunk *textChunk, T2P::BezierPath *bpath) +{ + SVGTSpanElementImpl *tspan = dynamic_cast<SVGTSpanElementImpl *>(element); + if(!tspan) + return; + + if(!tspan->text().isEmpty() || element->nodeName() == "tref") + { + if((KSVG_TOKEN_NOT_PARSED_ELEMENT(SVGTextPositioningElementImpl::X, tspan) && KSVG_TOKEN_NOT_PARSED_ELEMENT(SVGTextPositioningElementImpl::Y, tspan)))// && !bpath) + textChunk->addText(tspan->text(), tspan); + else + { + // new absolute value for next textChunk, render old one + if(textChunk->count() > 0) + { + createGlyphs(textChunk, canvas, screenCTM, curx, cury, curx, cury, bpath); + textChunk->clear(); + } + + int usex, usey; + bool bMultipleX = false; + bool bMultipleY = false; + + if(tspan->x()->baseVal()->numberOfItems() == 0) + { + usex = curx; + + if(tspan->dx()->baseVal()->numberOfItems() > 0) + usex += int(tspan->dx()->baseVal()->getItem(0)->value()); + } + else + { + if(tspan->x()->baseVal()->numberOfItems() > 1) + bMultipleX = true; + + usex = int(tspan->x()->baseVal()->getItem(0)->value()); + } + + if(tspan->y()->baseVal()->numberOfItems() == 0) + { + usey = cury; + + if(tspan->dy()->baseVal()->numberOfItems() > 0) + usey += int(tspan->dy()->baseVal()->getItem(0)->value()); + } + else + { + if(tspan->y()->baseVal()->numberOfItems() > 1) + bMultipleY = true; + + usey = int(tspan->y()->baseVal()->getItem(0)->value()); + } + + QString text = tspan->text(); + if(!text.isEmpty()) + { + T2P::GlyphLayoutParams *params = tspan->layoutParams(); + + if(bMultipleX || bMultipleY) + { + for(unsigned int i = 0; i < text.length(); i++) + { + if(bMultipleX && i < tspan->x()->baseVal()->numberOfItems()) + usex = int(tspan->x()->baseVal()->getItem(i)->value()); + if(bMultipleY && i < tspan->y()->baseVal()->numberOfItems()) + usey = int(tspan->y()->baseVal()->getItem(i)->value()); + + textChunk->addText(QString(text.at(i)), tspan); + createGlyphs(textChunk, canvas, screenCTM, usex, usey, endx, endy, bpath); + textChunk->clear(); + + if(!params->tb()) + usex += endx; + else + usey += endy; + } + } + else + { + textChunk->addText(text, tspan); + //createGlyphs(textChunk, canvas, screenCTM, usex, usey, endx, endy, bpath); + //textChunk->clear(); + } + + curx = usex; + cury = usey; + + if(!params->tb()) + curx += endx; + else + cury += endy; + + delete params; + } + } + } + + DOM::Node node = (tspan->getTextDirection() == LTR) ? tspan->firstChild() : tspan->lastChild(); + + bool tspanFound = false; + for(; !node.isNull(); node = ((tspan->getTextDirection() == LTR) ? node.nextSibling() : node.previousSibling())) + { + SVGElementImpl *element = m_text->ownerDoc()->getElementFromHandle(node.handle()); + if(node.nodeType() == DOM::Node::TEXT_NODE) + { + if(tspanFound) + { + DOM::Text text = node; + QString temp = text.data().string(); + textChunk->addText(temp, tspan); + } + } + else if(element->nodeName() == "tspan" || element->nodeName() == "tref") + { + tspanFound = true; + handleTSpan(canvas, screenCTM, curx, cury, endx, endy, element, textChunk, 0); + } + } + +} + +KSVGTextChunk *CanvasText::createTextChunk(KSVGCanvas *canvas, const SVGMatrixImpl *screenCTM, int &curx, int &cury, int &endx, int &endy) +{ + KSVGTextChunk *textChunk = new KSVGTextChunk(); + + SVGLengthImpl *length = m_text->x()->baseVal()->getItem(0); + if(length) + curx = int(length->value()); + + length = m_text->y()->baseVal()->getItem(0); + if(length) + cury = int(length->value()); + + // Otherwhise some js scripts which require a child, don't work (Niko) + if(!m_text->hasChildNodes()) + { + DOM::Text impl = static_cast<DOM::Document *>(m_text->ownerDoc())->createTextNode(DOM::DOMString("")); + m_text->appendChild(impl); + } + else + { + DOM::Node node = (m_text->getTextDirection() == LTR) ? m_text->firstChild() : m_text->lastChild(); + + for(; !node.isNull(); node = ((m_text->getTextDirection() == LTR) ? node.nextSibling() : node.previousSibling())) + { + if(node.nodeType() == DOM::Node::TEXT_NODE) + { + DOM::Text text = node; + QString temp = text.data().string(); + + if(!temp.isEmpty()) + { + if(m_text->getTextDirection() != LTR) + { + QString convert = temp; + + for(int i = temp.length(); i > 0; i--) + convert[temp.length() - i] = temp[i - 1]; + + temp = convert; + } + + textChunk->addText(temp, m_text); + } + } + else + { + SVGElementImpl *element = m_text->ownerDoc()->getElementFromHandle(node.handle()); + if(element->nodeName() == "textPath") + { + // new absolute value for next textChunk, render old one + if(textChunk->count() > 0) + { + createGlyphs(textChunk, canvas, screenCTM, curx, cury, curx, cury); + textChunk->clear(); + } + + SVGTextPathElementImpl *tpath = dynamic_cast<SVGTextPathElementImpl *>(element); + QString target = SVGURIReferenceImpl::getTarget(tpath->href()->baseVal().string()); + SVGPathElementImpl *path = dynamic_cast<SVGPathElementImpl *>(tpath->ownerSVGElement()->getElementById(target)); + + T2P::BezierPath *bpath = 0; + if(path && path->item()) + bpath = tpath->ownerDoc()->canvas()->toBezierPath(path->item()); + + DOM::Node iterate = tpath->firstChild(); + for(; !iterate.isNull(); iterate = iterate.nextSibling()) + { + if(iterate.nodeType() == DOM::Node::TEXT_NODE) + { + DOM::Text text = iterate; + QString temp = text.data().string(); + + if(!temp.isEmpty()) + textChunk->addText(temp, tpath); + } + else + { + kdDebug() << "FOUND TSPAN IN TEXTPATH! BPATH:" << bpath <<endl; + + SVGElementImpl *itelement = m_text->ownerDoc()->getElementFromHandle(iterate.handle()); + handleTSpan(canvas, screenCTM, curx, cury, endx, endy, itelement, textChunk, bpath); + } + } + + if(textChunk->count() > 0) + { + int usex = 0, usey = 0; + createGlyphs(textChunk, canvas, screenCTM, usex, usey, endx, endy, bpath); + textChunk->clear(); + + curx = usex; + cury = usey; + + T2P::GlyphLayoutParams *params = tpath->layoutParams(); + + if(!params->tb()) + curx += endx; + else + cury += endy; + + delete params; + } + } + else if(element->nodeName() == "tspan" || element->nodeName() == "tref") + handleTSpan(canvas, screenCTM, curx, cury, endx, endy, element, textChunk, 0); + } + } + } + + return textChunk; +} + +void CanvasText::createGlyphs(KSVGTextChunk *textChunk, KSVGCanvas *canvas, const SVGMatrixImpl *screenCTM, int curx, int cury, int &endx, int &endy, T2P::BezierPath *bpath) const +{ + double _curx = double(curx); + QMemArray<double> _cury(1); + _cury[0] = double(cury); + + T2P::GlyphLayoutParams *params = m_text->layoutParams(); + SVGTextPositioningElementImpl *tp = textChunk->getTextElement(0); + SVGTextContentElementImpl *tc = textChunk->getTextContentElement(0); + SVGTextContentElementImpl *tc0 = tc; + + T2P::SharedFont font; + QString text; + QPtrList<T2P::GlyphSet> glyphs; + glyphs.setAutoDelete(true); + + double pathAdvance = 0; + SVGTextPathElementImpl *tpath = dynamic_cast<SVGTextPathElementImpl *>(tc0); + if(tpath) + pathAdvance = tpath->startOffset()->baseVal()->value(); + double pathLength = bpath ? bpath->length() : 0; + double pathDy = 0; + for(unsigned int i = 0; i < textChunk->count(); i++) + { + tp = textChunk->getTextElement(i); + tc = textChunk->getTextContentElement(i); + + if(tp && tp->dx()->baseVal()->numberOfItems() > 0) + if(bpath) + pathAdvance += tp->dx()->baseVal()->getItem(0)->value() / pathLength; + else + _curx += tp->dx()->baseVal()->getItem(0)->value(); + _cury[i] += (tp && tp->dy()->baseVal()->numberOfItems() > 0) ? tp->dy()->baseVal()->getItem(0)->value() : 0; + + SVGMatrixImpl *tempMatrix = SVGSVGElementImpl::createSVGMatrix(); + tempMatrix->translate(_curx, _cury[i]); + + text = textChunk->getText(i); + if(i != textChunk->count() - 1) + text += QChar(' '); + + if(!canvas->fontContext()->ready()) + canvas->fontContext()->init(); + + font = canvas->fontContext()->requestFont(canvas->fontVisualParams(tc)); + + if(!font) + break; + + double addLetterSpacing = 0; + + // Apply affine corrections, through lengthAdjust + textLength + if(tp && tp->textLength()->baseVal()->value() != -1) + { + // #1 Measure text + SVGTextElementImpl *textElement = dynamic_cast<SVGTextElementImpl *>(tp); + const SVGMatrixImpl *ctm = textElement->screenCTM(); + + T2P::Affine affine; + { + SVGMatrixImpl *temp = SVGSVGElementImpl::createSVGMatrix(); + + temp->multiply(ctm); + temp->translate(_curx, _cury[0]); + + KSVGHelper::matrixToAffine(temp, affine); + + temp->deref(); + } + + T2P::GlyphSet *measure = canvas->fontContext()->calcString(font.get(), text.ucs2(), text.length(), affine, params, bpath); + + // Free bpath's + measure->set().clear(); + + // #2 Calculate textLength + double textLength = tp->textLength()->baseVal()->value(); + + // #3 Apply the spacing + if(tp->lengthAdjust()->baseVal() == LENGTHADJUST_SPACINGANDGLYPHS) + tempMatrix->scaleNonUniform((textLength * ctm->a()) / measure->width(), 1); + else if(tp->lengthAdjust()->baseVal() == LENGTHADJUST_SPACING) + addLetterSpacing = ((textLength - (measure->width() / ctm->a())) / text.length()); + + // #4 cleanup + delete measure; + } + + { + T2P::GlyphLayoutParams *params = tc->layoutParams(); + params->setLetterSpacing(params->letterSpacing() + addLetterSpacing); + if(bpath) + { + params->setTextPathStartOffset(pathAdvance); + if(tp && tp->dy()->baseVal()->numberOfItems() > 0) + pathDy += tp->dy()->baseVal()->getItem(0)->value(); + QString shift = QString("%1%%").arg((pathDy / font->fontParams()->size()) * -100.0); + params->setBaselineShift(shift.latin1()); + } + + T2P::Affine affine; + KSVGHelper::matrixToAffine(tempMatrix, affine); + tempMatrix->deref(); + + T2P::GlyphSet *glyph = canvas->fontContext()->calcString(font.get(), text.ucs2(), text.length(), affine, params, bpath); + if(bpath) + pathAdvance += double(glyph->width()) / pathLength; + _curx += (params->tb() ? 0 : glyph->xpen()); + _cury.resize(i + 2); + _cury[i + 1] = _cury[i] + (params->tb() ? glyph->ypen() : 0); + if(!glyph) + break; + else + glyphs.append(glyph); + + delete params; + } + } + + // Calculate text-anchor + double anchor = 0; + + // anchor == "start" is the default here (Rob) + if(tc->getTextAnchor() == TAMIDDLE) + { + if(!params->tb()) + anchor = ((_curx - curx) + 1) / 2; + else + anchor = ((_cury[textChunk->count()] - cury) + 1) / 2; + } + else if(tc->getTextAnchor() == TAEND) + { + if(!params->tb()) + anchor = (_curx - curx); + else + anchor = (_cury[textChunk->count()] - cury); + } + + // Render all glyphs of the text chunk + // Take first glyphset + T2P::GlyphSet *glyph = glyphs.at(0); + if(!glyph) + return; + + // Draw 'text-decoration' + // TODO: Currently just ignore text-decoration on vertical layouts, is that correct? + // Underline and overline have to be drawn before the glyphs are rendered + if(tc0->getTextDecoration() & UNDERLINE && !params->tb()) + addTextDecoration(tc0, (curx - anchor), (cury + (glyph->underlinePosition() - glyph->pixelBaseline())), + _curx - curx, glyph->underlineThickness()); + if(tc0->getTextDecoration() & OVERLINE && !params->tb()) + addTextDecoration(tc0, (curx - anchor), (cury + (glyph->overlinePosition() - glyph->pixelBaseline())), + _curx - curx, glyph->underlineThickness()); + + for(unsigned int j = 0; j < glyphs.count(); j++) + { + glyph = glyphs.at(j); + SVGTextContentElementImpl *style = textChunk->getTextContentElement(j); + + // Draw 'text-decoration' + // TODO: Currently just ignore text-decoration on vertical layouts, is that correct? + // Underline and overline have to be drawn before the glyphs are rendered + if(style->getAttribute("text-decoration") == "underline" && !params->tb()) + addTextDecoration(style, glyph->bboxX() - anchor, (cury + (glyph->underlinePosition() - glyph->pixelBaseline())), + glyph->width(), glyph->underlineThickness()); + else if(style->getAttribute("text-decoration") == "overline" && !params->tb()) + addTextDecoration(style, glyph->bboxX() - anchor, (cury + (glyph->overlinePosition() - glyph->pixelBaseline())), + glyph->width(), glyph->underlineThickness()); + + renderCallback(style, screenCTM, glyph, params, anchor); + + // Clear GlyphAffinePair's + for(std::vector<T2P::GlyphAffinePair *>::iterator it = glyph->set().begin(); it != glyph->set().end(); ++it) + { + T2P::GlyphAffinePair *glyphAffine = *it; + delete glyphAffine; + } + + glyph->set().clear(); + + // Draw 'line-through' text decoration + // Line-through has to be drawn after the glyphs are rendered + if(style->getAttribute("text-decoration") == "line-through" && !params->tb()) + addTextDecoration(style, glyph->bboxX() - anchor, (cury + (glyph->strikeThroughPosition() - glyph->pixelBaseline())), glyph->width(), glyph->underlineThickness()); + + } + + endx = glyph->bboxX() + glyph->width(); + endy = int(_cury[glyphs.count() - 1]); + + // Draw 'line-through' text decoration + // Line-through has to be drawn after the glyphs are rendered + if(tc0->getTextDecoration() & LINE_THROUGH && !params->tb()) + addTextDecoration(tc0, (curx - anchor), (cury + (glyph->strikeThroughPosition() - glyph->pixelBaseline())), _curx - curx, glyph->underlineThickness()); + + delete params; +} + +// ##### + +void MarkerHelper::doMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle, const QString &markerId) +{ + SVGMarkerElementImpl *marker = dynamic_cast<SVGMarkerElementImpl *>(shape->ownerSVGElement()->getElementById(markerId)); + if(marker) + marker->draw(shape, x, y, style->getStrokeWidth()->baseVal()->value(), angle); +} + +void MarkerHelper::doStartMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle) +{ + doMarker(shape, style, x, y, angle, style->getStartMarker()); +} + +void MarkerHelper::doMidMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle) +{ + doMarker(shape, style, x, y, angle, style->getMidMarker()); +} + +void MarkerHelper::doEndMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle) +{ + doMarker(shape, style, x, y, angle, style->getEndMarker()); +} + +// vim:ts=4:noet diff --git a/ksvg/core/CanvasItems.h b/ksvg/core/CanvasItems.h new file mode 100644 index 00000000..8959d6ba --- /dev/null +++ b/ksvg/core/CanvasItems.h @@ -0,0 +1,133 @@ +/* + Copyright (C) 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 + aint 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. +*/ + +#ifndef CANVASITEMS_H +#define CANVASITEMS_H + +#include <qptrlist.h> +#include "CanvasItem.h" +#include "SVGTextElementImpl.h" +#include "SVGTextPathElementImpl.h" + +#include "svgpathparser.h" +#include "SVGBBoxTarget.h" + +namespace T2P +{ + class GlyphSet; + class BezierPath; + class GlyphLayoutParams; +} + +namespace KSVG +{ + +class KSVGCanvas; +class KSVGTextChunk; +class SVGPathParser; +class SVGMatrixImpl; +class SVGMarkerElementImpl; +class SVGClipPathElementImpl; +class SVGTextContentElementImpl; +class SVGTextPathElementImpl; + +#define CANVAS_CLASS(Prefix, Class, Postfix, Member) \ +class Canvas##Class : public CanvasItem \ +{ \ +public: \ + Canvas##Class(Prefix##Class##Postfix *Member) : CanvasItem(), m_##Member(Member) { } \ + virtual ~Canvas##Class() { } \ + virtual SVGElementImpl *element() { return reinterpret_cast<SVGElementImpl *>(m_##Member); } \ +protected: \ + Prefix##Class##Postfix *m_##Member; \ +}; + +CANVAS_CLASS(SVG, ClipPath, ElementImpl, clipPath) + +class CanvasMarker : public CanvasItem +{ +public: + CanvasMarker(SVGMarkerElementImpl *marker) : CanvasItem(), m_marker(marker) {} + virtual ~CanvasMarker() {} + virtual SVGElementImpl *element() { return reinterpret_cast<SVGElementImpl *>(m_marker); } + + virtual void draw(SVGShapeImpl *obj, double x, double y, double lwidth = 1.0, double angle = 0.0) + { + Q_UNUSED(obj); Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(lwidth); Q_UNUSED(angle); + } + +protected: + SVGMarkerElementImpl *m_marker; +}; + +class MarkerHelper +{ +protected: + void doStartMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle = 0.0); + void doMidMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle = 0.0); + void doEndMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle = 0.0); + +private: + void doMarker(SVGShapeImpl *shape, SVGStylableImpl *style, double x, double y, double angle, const QString &marker); +}; + +class CanvasText : public CanvasItem +{ +public: + CanvasText(SVGTextElementImpl *text); + virtual ~CanvasText(); + + KSVGTextChunk *createTextChunk(KSVGCanvas *canvas, const SVGMatrixImpl *screenCTM, int &curx, int &cury, int &endx, int &endy); + virtual SVGElementImpl *element() const { return m_text; } + + virtual void renderCallback(SVGTextContentElementImpl *element, const SVGMatrixImpl *screenCTM, T2P::GlyphSet *glyph, T2P::GlyphLayoutParams *params, double anchor) const = 0; + void createGlyphs(KSVGTextChunk *textChunk, KSVGCanvas *canvas, const SVGMatrixImpl *screenCTM, int curx, int cury, int &endx, int &endy, T2P::BezierPath *bpath = 0) const; + + virtual void addTextDecoration(SVGTextContentElementImpl *element, double x, double y, double w, double h) const = 0; + +private: + void handleTSpan(KSVGCanvas *canvas, const SVGMatrixImpl *screenCTM, int &curx, int &cury, int &endx, int &endy, SVGElementImpl *element, KSVGTextChunk *textChunk, T2P::BezierPath *bpath); + +protected: + SVGTextElementImpl *m_text; +}; + +class CanvasPaintServer : public SVGBBoxTarget +{ +public: + CanvasPaintServer() : SVGBBoxTarget() { m_finalized = false; } + virtual ~CanvasPaintServer() {} + + void setFinalized() { m_finalized = true; } + void resetFinalized() { m_finalized = false; } + bool finalized() { return m_finalized; } + + virtual void finalizePaintServer() = 0; + virtual void reference(const QString &href) = 0; + +private: + bool m_finalized; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/DocumentFactory.cpp b/ksvg/core/DocumentFactory.cpp new file mode 100644 index 00000000..bcc0ddcb --- /dev/null +++ b/ksvg/core/DocumentFactory.cpp @@ -0,0 +1,110 @@ +/* + Copyright (C) 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 <qobject.h> + +#include <kdebug.h> +#include <kstaticdeleter.h> + +#include "SVGDocument.h" +#include "SVGElementImpl.h" +#include "SVGDocumentImpl.h" +#include "DocumentFactory.h" + +using namespace KSVG; + +namespace KSVG +{ + class DocumentFactory::Private + { + public: + Private() { m_docs.setAutoDelete(true); } + ~Private() { m_docs.clear(); } + + void setup(bool bFit) { m_docs.append(new SVGDocumentImpl(!bFit /* animations */, bFit)); } + SVGDocumentImpl *doc() const { return m_docs.current(); } + + private: + QPtrList<SVGDocumentImpl> m_docs; + }; +} + +static KStaticDeleter<DocumentFactory> s_deleter; +static DocumentFactory *s_factory = 0; + +DocumentFactory::DocumentFactory() : m_d(new Private()) +{ +} + +DocumentFactory::~DocumentFactory() +{ + delete m_d; +} + +DocumentFactory *DocumentFactory::self() +{ + if(!s_factory) + s_deleter.setObject(s_factory, new DocumentFactory()); + return s_factory; +} + +SVGDocument *DocumentFactory::requestDocument(QObject *notifyObject, const char *notifySlot) const +{ + SVGDocumentImpl *impl = requestDocumentImpl(false); + QObject::connect(impl, SIGNAL(finishedParsing(bool, const QString &)), notifyObject, notifySlot); + + return new SVGDocument(impl); +} + +bool DocumentFactory::startParsing(SVGDocument *document, const KURL &url) +{ + if(!document || !document->handle()) + return false; + + return reinterpret_cast<SVGDocumentImpl *>(document->handle())->open(url); +} + +bool DocumentFactory::attachCanvas(KSVGCanvas *canvas, SVGDocument *document) +{ + if(!canvas || !document || !document->handle()) + return false; + + SVGDocumentImpl *docImpl = reinterpret_cast<SVGDocumentImpl *>(document->handle()); + + if(docImpl) + { + docImpl->attach(canvas); + return true; + } + + return false; +} + +SVGDocumentImpl *DocumentFactory::requestDocumentImpl(bool bFit) const +{ + m_d->setup(bFit); + + SVGDocumentImpl *impl = m_d->doc(); + impl->ref(); + + return impl; +} + +// vim:ts=4:noet diff --git a/ksvg/core/DocumentFactory.h b/ksvg/core/DocumentFactory.h new file mode 100644 index 00000000..290d54ac --- /dev/null +++ b/ksvg/core/DocumentFactory.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 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. +*/ + +#ifndef DocumentFactory_H +#define DocumentFactory_H + +#include <kurl.h> +#include <qobject.h> + +namespace KSVG +{ + +class KSVGCanvas; +class SVGDocument; +class SVGDocumentImpl; +class DocumentFactory +{ +public: + DocumentFactory(); + ~DocumentFactory(); + + static DocumentFactory *self(); + + // Creates a document and connects the parsingFinished() signal to the notifySlot... + SVGDocument *requestDocument(QObject *notifyObject, const char *notifySlot) const; + + // Loads 'url' and emits parsingFinisihed() signal, when done + bool startParsing(SVGDocument *document, const KURL &url); + + // Attaches the a canvas to the document, that is ksvg specific code + bool attachCanvas(KSVGCanvas *canvas, SVGDocument *document); + + // Internal use only - external coders don't have the + // possibility to use SVGDocumentImpl anyway + SVGDocumentImpl *requestDocumentImpl(bool bFit) const; + +private: + class Private; + Private *m_d; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGCanvas.cpp b/ksvg/core/KSVGCanvas.cpp new file mode 100644 index 00000000..d4e75d79 --- /dev/null +++ b/ksvg/core/KSVGCanvas.cpp @@ -0,0 +1,786 @@ +/* + 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 + aint 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 "CanvasItem.h" +#include "CanvasItems.h" +#include "KSVGCanvas.moc" + +#include "SVGRectImpl.h" +#include "SVGSVGElementImpl.h" +#include "SVGStringListImpl.h" +#include "SVGClipPathElementImpl.h" +#include "SVGImageElementImpl.h" +#include "SVGDocumentImpl.h" + +#include <kdebug.h> +#include <kglobal.h> + +#include <qstring.h> +#include <qdatetime.h> +#include <qpaintdevicemetrics.h> +#include <qwmatrix.h> + +#include <X11/Xlib.h> + +#include <math.h> + +#include <libs/xrgbrender/gdk-pixbuf-xlibrgb.h> + +#include <Font.h> +#include <fontconfig/fontconfig.h> + +#define USE_TIMER + +using namespace KSVG; + +KSVGCanvas::KSVGCanvas(unsigned int width, unsigned int height) : m_viewportWidth(width), m_viewportHeight(height), m_width(width), m_height(height) +{ + m_fontContext = 0; + + m_items.setAutoDelete(true); + + m_chunkSizeVer = CHUNK_SIZE_VERTICAL; + m_chunkSizeHor = CHUNK_SIZE_HORIZONTAL; + + m_zoom = 1; + + m_buffer = 0; + + m_backgroundColor = QColor(250, 250, 250); + + m_immediateUpdate = false; +} + +void KSVGCanvas::setup(QPaintDevice *drawWindow, QPaintDevice *directWindow) +{ + m_drawWindow = drawWindow; + m_directWindow = directWindow; + + m_buffer = 0; + m_nrChannels = 3; + + setRenderBufferSize(m_width, m_height); + + xlib_rgb_init_with_depth(m_drawWindow->x11Display(), XScreenOfDisplay(m_drawWindow->x11Display(), m_drawWindow->x11Screen()), m_drawWindow->x11Depth()); + m_gc = XCreateGC(m_drawWindow->x11Display(), m_drawWindow->handle(), 0, 0); +} + +void KSVGCanvas::setViewportDimension(unsigned int w, unsigned int h) +{ + m_viewportWidth = w; + m_viewportHeight = h; + setRenderBufferSize(w, h); +} + +void KSVGCanvas::setup(unsigned char *buffer, unsigned int width, unsigned int height) +{ + setBuffer(buffer); + m_drawWindow = 0; + m_directWindow = 0; + + m_nrChannels = 4; + + if(height > 0) + { + m_width = width; + m_height = height; + } + + setRenderBufferSize(m_width, m_height); + + m_gc = 0; +} + +void KSVGCanvas::setBuffer(unsigned char *buffer) +{ + m_buffer = buffer; +} + +KSVGCanvas::~KSVGCanvas() +{ + if(m_fontContext) + delete m_fontContext; + + if(m_buffer && m_gc) + delete []m_buffer; + + if(m_gc) + XFreeGC(m_drawWindow->x11Display(), m_gc); + + reset(); +} + +void KSVGCanvas::retune(unsigned int csh, unsigned int csv) +{ + m_chunkSizeHor = csh; + m_chunkSizeVer = csv; +} + +void KSVGCanvas::resize(unsigned int w, unsigned int h) +{ + if(m_buffer && (m_width != int(w) || m_height != int(h))) + { + unsigned char *oldbuffer = m_buffer; + + m_buffer = new unsigned char[w * h * m_nrChannels]; + + int minw = kMin(int(w), m_width); + int minh = kMin(int(h), m_height); + + int origstride = m_width * m_nrChannels; + int newstride = w * m_nrChannels; + + // Redraw new areas, if any + int diffw = w - m_width; + int diffh = h - m_height; + + QRect r(m_width, 0, diffw, m_height + diffh); + QRect r3(0, m_height, m_width + diffw, diffh); + + QWMatrix mtx; + mtx.translate(m_pan.x(), m_pan.y()); + mtx.scale(m_zoom, m_zoom); + + m_width = w; + m_height = h; + + setBuffer(m_buffer); + fill(); + + if(diffw > 0 || diffh > 0) + { + CanvasItemList drawables; + if(diffw > 0) + { + QRect r2 = mtx.invert().map(r); + + // Recalc items + for(int j = r2.top() / int(m_chunkSizeVer); j <= r2.bottom() / int(m_chunkSizeVer); j++) + { + for(int i = r2.left() / int(m_chunkSizeHor); i <= r2.right() / int(m_chunkSizeHor); i++) + { + CanvasChunk *chunk = m_chunkManager.getChunk(i, j); + if(chunk) + { + for(CanvasItemList::ConstIterator it = chunk->list().begin(); it != chunk->list().end(); ++it) + { + if(!drawables.contains(*it)) + drawables.append(*it); + } + } + } + } + } + + if(diffh > 0) + { + QRect r4 = mtx.invert().map(r3); + + // Recalc items + for(int j = r4.top() / int(m_chunkSizeVer); j <= r4.bottom() / int(m_chunkSizeVer); j++) + { + for(int i = r4.left() / int(m_chunkSizeHor); i <= r4.right() / int(m_chunkSizeHor); i++) + { + CanvasChunk *chunk = m_chunkManager.getChunk(i, j); + if(chunk) + { + for(CanvasItemList::ConstIterator it = chunk->list().begin(); it != chunk->list().end(); ++it) + { + if(!drawables.contains(*it)) + drawables.append(*it); + } + } + } + } + } + + drawables.sort(); + + for(CanvasItemList::Iterator it = drawables.begin(); it != drawables.end(); ++it) + (*it)->draw(); + } + + for(int y = 0; y < minh; y++) + memcpy(m_buffer + y * newstride, oldbuffer + y * origstride, minw * m_nrChannels); + + delete []oldbuffer; + } +} + +void KSVGCanvas::setRenderBufferSize(int w, int h) +{ + kdDebug(26005) << k_funcinfo << endl; + + if(m_drawWindow) + { + bool needsRedraw = (!m_buffer) || (m_width != w || m_height != h); + + if(needsRedraw) + { + QPaintDeviceMetrics metrics(m_drawWindow); + m_width = kMin(int(w), metrics.width()); + m_height = kMin(int(h), metrics.height()); + + if(m_buffer) + delete []m_buffer; + + m_buffer = new unsigned char[m_width * m_height * m_nrChannels]; + } + } + + fill(); +} + +void KSVGCanvas::clear(const QRect &r) +{ + QRect r2 = r & QRect(0, 0, m_width, m_height); + if(!r2.isEmpty() && m_buffer) + { + for(int i = 0; i < r2.height(); i++) + memset(m_buffer + int(r2.x() * m_nrChannels) + int((r2.y() + i) * (m_width * m_nrChannels)), qRgba(250, 250, 250, 250), r2.width() * m_nrChannels); + } +} + +void KSVGCanvas::fill() +{ + if(m_buffer) + { + unsigned char r = m_backgroundColor.red(); + unsigned char g = m_backgroundColor.green(); + unsigned char b = m_backgroundColor.blue(); + + if(m_nrChannels == 3) + { + if(r == g && r == b) + memset(m_buffer, r, m_width * m_height * m_nrChannels); + else + { + unsigned char *p = m_buffer; + + for(int i = 0; i < m_width * m_height; i++) + { + *p++ = r; + *p++ = g; + *p++ = b; + } + } + } + else + { + Q_UINT32 *p = reinterpret_cast<Q_UINT32 *>(m_buffer); + unsigned char a = qAlpha(m_backgroundColor.rgb()); + +#if X_BYTE_ORDER == X_LITTLE_ENDIAN + Q_UINT32 rgba = (a << 24) | (b << 16) | (g << 8) | r; +#else + Q_UINT32 rgba = (r << 24) | (g << 16) | (b << 8) | a; +#endif + for(int i = 0; i < m_width * m_height; i++) + *p++ = rgba; + } + } +} + +// Clipping +void KSVGCanvas::clipToBuffer(int &x0, int &y0, int &x1, int &y1) const +{ + // clamp to viewport + x0 = QMAX(x0, 0); + x0 = QMIN(x0, int(m_width - 1)); + + y0 = QMAX(y0, 0); + y0 = QMIN(y0, int(m_height - 1)); + + x1 = QMAX(x1, 0); + x1 = QMIN(x1, int(m_width - 1)); + + y1 = QMAX(y1, 0); + y1 = QMIN(y1, int(m_height - 1)); +} + +T2P::FontVisualParams *KSVGCanvas::fontVisualParams(SVGStylableImpl *style) const +{ + T2P::FontVisualParams *fontVisualParams = new T2P::FontVisualParams(); + + // Calc weight & slant + int weight = 0, slant = 0; + EFontStyle fontStyle = style->getFontStyle(); + QString fontWeight = style->getFontWeight(); + + if(fontWeight.contains("bold")) + weight |= FC_WEIGHT_DEMIBOLD; + if(fontWeight.contains("bolder")) + weight |= FC_WEIGHT_BOLD; + if(fontWeight.contains("lighter")) + weight |= FC_WEIGHT_LIGHT; + + bool ok = true; + int weightNumber = fontWeight.toInt(&ok); + + if(ok) + weight = weightNumber; + + if(fontStyle == FSNORMAL) + slant |= FC_SLANT_ROMAN; + else if(fontStyle == ITALIC) + slant |= FC_SLANT_ITALIC; + else if(fontStyle == OBLIQUE) + slant |= FC_SLANT_OBLIQUE; + + // Calc font names + SVGStringListImpl *fontList = style->getFontFamily(); + + for(unsigned int i = 0; i <= fontList->numberOfItems(); i++) + { + DOM::DOMString *string = fontList->getItem(i); + + if(string) + fontVisualParams->fontList().push_back(string->string().latin1()); + } + + fontVisualParams->setWeight(weight); + fontVisualParams->setSlant(slant); + fontVisualParams->setSize(style->getFontSize()); + + return fontVisualParams; +} + +void KSVGCanvas::invalidate(CanvasItem *item, bool recalc) +{ + if(m_chunksByItem.find(item) != m_chunksByItem.end()) + { + if(recalc) + { + removeFromChunks(item); + addToChunks(item); + } + + QPtrListIterator<CanvasChunk> it = m_chunksByItem[item]; + for(it.toFirst(); it.current(); ++it) + { + (*it)->setDirty(); + if(!m_dirtyChunks.contains(*it)) + m_dirtyChunks.append(*it); + } + } + else + addToChunks(item); +} + +void KSVGCanvas::insert(CanvasItem *item, int z) +{ + if(z == -1) + { + item->setZIndex(m_chunksByItem.size()); + m_chunksByItem[item] = QPtrList<CanvasChunk>(); + addToChunks(item); + m_items.append(item); + + bool visible = item->isVisible(); + if(visible) + invalidate(item, false); + + if(m_immediateUpdate) + { + if(visible) + { + item->draw(); + QRect bbox = item->bbox(); + blit(bbox, true); + } + } + } + else + { + // make some space + for(unsigned int i = z; i < m_items.count(); i++) + m_items.at(i)->setZIndex(m_items.at(i)->zIndex() + 1); + + item->setZIndex(z); + } +} + +void KSVGCanvas::removeItem(CanvasItem *item) +{ + removeFromChunks(item); + m_items.remove(item); +} + +void KSVGCanvas::removeFromChunks(CanvasItem *item) +{ + QPtrListIterator<CanvasChunk> it = m_chunksByItem[item]; + for(it.toFirst(); it.current(); ++it) + { + (*it)->remove(item); + if(!m_dirtyChunks.contains(*it)) + m_dirtyChunks.append(*it); + } + m_chunksByItem.remove(item); +} + +void KSVGCanvas::addToChunks(CanvasItem *item) +{ + QRect bbox = item->bbox(); + QWMatrix mtx; + mtx.translate(m_pan.x(), m_pan.y()); + mtx.scale(m_zoom, m_zoom); + + bbox = mtx.invert().map(bbox); + for(int j = bbox.top() / m_chunkSizeVer; j <= (bbox.bottom() / m_chunkSizeVer); j++) + { + for(int i = bbox.left() / int(m_chunkSizeHor); i <= (bbox.right() / m_chunkSizeHor); i++) + { + CanvasChunk *chunk = m_chunkManager.getChunk(i, j); + if(!chunk) + { + chunk = new CanvasChunk(i, j); + m_chunkManager.addChunk(chunk); + } + + chunk->add(item); + m_chunksByItem[item].append(chunk); + } + } +} + +unsigned int KSVGCanvas::setElementItemZIndexRecursive(SVGElementImpl *element, unsigned int z) +{ + SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(element); + + if(shape) + { + CanvasItem *item = shape->item(); + + if(item) + { + SVGImageElementImpl *image = dynamic_cast<SVGImageElementImpl *>(shape); + + if(image && image->svgImageRootElement()) + { + // Set the z for all items in the svg image, since they live in the + // same canvas. + z = setElementItemZIndexRecursive(image->svgImageRootElement(), z); + } + else + { + item->setZIndex(z); + invalidate(item, false); + z++; + } + } + } + + for(DOM::Node node = element->firstChild(); !node.isNull(); node = node.nextSibling()) + { + SVGElementImpl *e = element->ownerDoc()->getElementFromHandle(node.handle()); + + if(e) + z = setElementItemZIndexRecursive(e, z); + } + + return z; +} + +void KSVGCanvas::update(const QPoint &panPoint, bool erase) +{ +#ifdef USE_TIMER + QTime t; + t.start(); +#endif + + int dx = panPoint.x() - m_pan.x(); + int dy = panPoint.y() - m_pan.y(); + m_pan = panPoint; + + if(erase) + fill(); + + // reset clip paths + QDictIterator<CanvasClipPath> itr(m_clipPaths); + for(; itr.current(); ++itr) + (*itr)->update(UPDATE_TRANSFORM); + + QWMatrix mtx; + mtx.translate(m_pan.x(), m_pan.y()); + mtx.scale(m_zoom, m_zoom); + + QRect r(0, 0, m_width, m_height); + QRect r2 = mtx.invert().map(r); + + // pan all items + for(unsigned int i = 0; i < m_items.count(); i++) + m_items.at(i)->update(UPDATE_PAN, dx, dy); + + // recalc items + CanvasItemList drawables; + QPtrListIterator<CanvasItem> it = m_items; + for(int j = r2.top() / m_chunkSizeVer; j <= (r2.bottom() / m_chunkSizeVer); j++) + { + for(int i = r2.left() / m_chunkSizeHor; i <= (r2.right() / m_chunkSizeHor); i++) + { + CanvasChunk *chunk = m_chunkManager.getChunk(i, j); + if(chunk) + { + for(CanvasItemList::ConstIterator it = chunk->list().begin(); it != chunk->list().end(); ++it) + { + if(!drawables.contains(*it)) + drawables.append(*it); + } + } + } + } + + drawables.sort(); + for(CanvasItemList::Iterator it = drawables.begin(); it != drawables.end(); ++it) + (*it)->draw(); + + if(m_drawWindow) + blit(QRect(0, 0, m_width, m_height), false); + + m_dirtyChunks.clear(); + +#ifdef USE_TIMER + kdDebug(26000) << k_funcinfo << " Total time: " << t.elapsed() << endl; +#endif +} + +void KSVGCanvas::update(float zoomFactor) +{ +#ifdef USE_TIMER + QTime t; + t.start(); +#endif + + if(zoomFactor >= 1) + { + int newWidth = static_cast<int>(m_viewportWidth * zoomFactor); + int newHeight = static_cast<int>(m_viewportHeight * zoomFactor); + setRenderBufferSize(newWidth, newHeight); + } + else + fill(); + + // reset clip paths + QDictIterator<CanvasClipPath> itr(m_clipPaths); + for(; itr.current(); ++itr) + (*itr)->update(UPDATE_TRANSFORM); + + m_zoom = zoomFactor; + + QWMatrix mtx; + mtx.translate(m_pan.x(), m_pan.y()); + mtx.scale(m_zoom, m_zoom); + + QRect r(0, 0, m_width, m_height); + QRect r2 = mtx.invert().map(r); + + // zoom all items + for(unsigned int i = 0; i < m_items.count(); i++) + m_items.at(i)->update(UPDATE_ZOOM); + + // recalc items + CanvasItemList drawables; + QPtrListIterator<CanvasItem> it = m_items; + for(int j = r2.top() / m_chunkSizeVer; j <= (r2.bottom() / m_chunkSizeVer); j++) + { + for(int i = r2.left() / m_chunkSizeHor; i <= (r2.right() / m_chunkSizeHor); i++) + { + CanvasChunk *chunk = m_chunkManager.getChunk(i, j); + if(chunk) + { + for(CanvasItemList::ConstIterator it = chunk->list().begin(); it != chunk->list().end(); ++it) + { + if(!drawables.contains(*it)) + drawables.append(*it); + } + } + } + } + + drawables.sort(); + for(CanvasItemList::Iterator it = drawables.begin(); it != drawables.end(); ++it) + (*it)->draw(); + + if(m_drawWindow) + blit(QRect(0, 0, m_width, m_height), false); + + m_dirtyChunks.clear(); + +#ifdef USE_TIMER + kdDebug(26000) << k_funcinfo << " Total time: " << t.elapsed() << endl; +#endif +} + +void KSVGCanvas::reset() +{ + m_items.clear(); + m_chunkManager.clear(); + m_chunksByItem.clear(); + m_dirtyChunks.clear(); + m_pan.setX(0); + m_pan.setY(0); + m_zoom = 1; +} + +void KSVGCanvas::update() +{ +#ifdef USE_TIMER + QTime t; + t.start(); +#endif + + QWMatrix mtx; + mtx.translate(m_pan.x(), m_pan.y()); + mtx.scale(m_zoom, m_zoom); + + // Process dirty chunks + QPtrList<CanvasChunk> chunkList; + CanvasItemList drawables; + for(unsigned int i = 0; i < m_dirtyChunks.count(); i++) + { + CanvasChunk *chunk = m_dirtyChunks[i]; + Q_ASSERT(chunk->isDirty()); + + QRect r = chunk->bbox(); + QRect chunkbox(mtx.map(r.topLeft()), mtx.map(r.bottomRight())); + clear(chunkbox); + chunkList.append(chunk); + + for(CanvasItemList::ConstIterator it = chunk->list().begin(); it != chunk->list().end(); ++it) + { +// kdDebug(26005) << k_funcinfo << " Checking: " << *it << endl; + if(!drawables.contains(*it)) + { +// kdDebug(26005) << k_funcinfo << " Yes, appending to update list!" << endl; + drawables.append(*it); + } + } + + chunk->unsetDirty(); + } + + drawables.sort(); + + // Draw dirty chunks + for(CanvasItemList::Iterator it = drawables.begin(); it != drawables.end(); ++it) + { +// kdDebug(26005) << " Need to redraw dirty : " << (*it) << " with z : " << (*it)->zIndex() << endl; + (*it)->draw(); + } + + // Blit dirty chunks + QPtrListIterator<CanvasChunk> it = chunkList; + for(it.toFirst(); it.current(); ++it) + { + QRect r = (*it)->bbox(); + QRect chunkbox(mtx.map(r.topLeft()), mtx.map(r.bottomRight())); + blit(chunkbox, false); + } + + m_dirtyChunks.clear(); + +#ifdef USE_TIMER + kdDebug(26005) << k_funcinfo << " Total time: " << t.elapsed() << endl; +#endif +} + +CanvasItemList KSVGCanvas::collisions(const QPoint &p, bool exact) const +{ + QWMatrix mtx; + mtx.translate(m_pan.x(), m_pan.y()); + mtx.scale(m_zoom, m_zoom); + + QPoint p2 = mtx.invert().map(p); + if(p2.x() < 0 || p2.y() < 0) + return CanvasItemList(); + + unsigned int x = p2.x() / int(m_chunkSizeHor); + unsigned int y = p2.y() / int(m_chunkSizeVer); + + CanvasItemList result; + CanvasChunk *chunk = m_chunkManager.getChunk(x, y); + if(!chunk) + return result; + + CanvasItemList list = chunk->list(); + if(exact) + { + for(CanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if((*it)->fillContains(p) || (*it)->strokeContains(p) || (*it)->bbox().contains(p)) + result.append(*it); + } + + return result; + } + else + return list; +} + +void KSVGCanvas::blit(const QRect &rect, bool direct) +{ + if(m_drawWindow && m_width && m_height) + { + // clamp to viewport + int x0 = rect.x(); + x0 = QMAX(x0, 0); + x0 = QMIN(x0, int(m_width - 1)); + + int y0 = rect.y(); + y0 = QMAX(y0, 0); + y0 = QMIN(y0, int(m_height - 1)); + + int x1 = rect.x() + rect.width() + 1; + x1 = QMAX(x1, 0); + x1 = QMIN(x1, int(m_width)); + + int y1 = rect.y() + rect.height() + 1; + y1 = QMAX(y1, 0); + y1 = QMIN(y1, int(m_height)); + + xlib_draw_rgb_image(direct ? m_directWindow->handle() : m_drawWindow->handle(), m_gc, x0, y0, x1 - x0, y1 - y0, XLIB_RGB_DITHER_NONE, m_buffer + (m_width * y0 + x0) * m_nrChannels, m_width * m_nrChannels); + } +} + +void KSVGCanvas::blit() +{ + return blit(QRect(0, 0, m_width, m_height), false); +} + +void KSVGCanvas::ChunkManager::addChunk(CanvasChunk *chunk) +{ + QString key = QString("%1 %2").arg(chunk->x()).arg(chunk->y()); +// kdDebug(26005) << k_funcinfo << "Adding chunk : " << chunk << endl; + m_chunks.insert(key, chunk); +} + +CanvasChunk *KSVGCanvas::ChunkManager::getChunk(short x, short y) const +{ +// kdDebug(26005) << k_funcinfo << "getting chunk from : " << x << ", " << y << endl; + QString key = QString("%1 %2").arg(x).arg(y); + return m_chunks[key]; +} + +void KSVGCanvas::ChunkManager::clear() +{ + m_chunks.clear(); +} + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGCanvas.h b/ksvg/core/KSVGCanvas.h new file mode 100644 index 00000000..7b26997c --- /dev/null +++ b/ksvg/core/KSVGCanvas.h @@ -0,0 +1,190 @@ +/* + 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 + aint 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. +*/ + +#ifndef KSVGCANVAS_H +#define KSVGCANVAS_H + +#include <qmap.h> +#include <qdict.h> +#include <qcolor.h> +#include <qobject.h> +#include <qptrlist.h> +#include <qdict.h> + +#include <Converter.h> + +namespace KSVG +{ + +class SVGShapeImpl; +class SVGMatrixImpl; +class SVGStylableImpl; +class SVGElementImpl; +class SVGTextElementImpl; +class SVGRectElementImpl; +class SVGLineElementImpl; +class SVGPathElementImpl; +class SVGImageElementImpl; +class SVGCircleElementImpl; +class SVGMarkerElementImpl; +class SVGEllipseElementImpl; +class SVGPolygonElementImpl; +class SVGPolylineElementImpl; +class SVGClipPathElementImpl; + +class CanvasItem; +class CanvasChunk; +class CanvasItemList; +class CanvasClipPath; +class CanvasPaintServer; + +// Must be a QObject to be able to be loaded by KLibLoader... +class KSVGCanvas : public QObject +{ +Q_OBJECT +public: + KSVGCanvas(unsigned int width, unsigned int height); + virtual ~KSVGCanvas(); + + void setViewportDimension(unsigned int w, unsigned int h); + + void setup(QPaintDevice *drawWidget, QPaintDevice *directWindow); + void setup(unsigned char *buffer, unsigned int width = 0, unsigned int height = 0); + + void reset(); + void update(); + void update(float zoomFactor); + void update(const QPoint &panPoint, bool erase = true); + void resize(unsigned int w, unsigned int h); + void retune(unsigned int csh, unsigned int csv); + void invalidate(CanvasItem *item, bool recalc = true); + CanvasItemList collisions(const QPoint &p, bool exact = false) const; + + void setBackgroundColor(const QColor &c) { m_backgroundColor = c; } + void blit(); + void blit(const QRect &rect, bool direct); + + float zoom() const { return m_zoom; } + QPoint pan() const { return m_pan; } + void setPan(const QPoint &pan) { m_pan = pan; } + + int width() const { return m_width; } + int height() const { return m_height; } + + virtual void setRenderBufferSize(int w, int h); + void clipToBuffer(int &x0, int &y0, int &x1, int &y1) const; + + // creating canvas items + virtual CanvasItem *createRectangle(SVGRectElementImpl *rect) = 0; + virtual CanvasItem *createEllipse(SVGEllipseElementImpl *ellipse) = 0; + virtual CanvasItem *createCircle(SVGCircleElementImpl *circle) = 0; + virtual CanvasItem *createLine(SVGLineElementImpl *line) = 0; + virtual CanvasItem *createPolyline(SVGPolylineElementImpl *poly) = 0; + virtual CanvasItem *createPolygon(SVGPolygonElementImpl *poly) = 0; + virtual CanvasItem *createPath(SVGPathElementImpl *path) = 0; + virtual CanvasItem *createClipPath(SVGClipPathElementImpl *clippath) = 0; + virtual CanvasItem *createImage(SVGImageElementImpl *image) = 0; + virtual CanvasItem *createCanvasMarker(SVGMarkerElementImpl *marker) = 0; + virtual CanvasItem *createText(SVGTextElementImpl *text) = 0; + virtual CanvasPaintServer *createPaintServer(SVGElementImpl *pserver) = 0; + + void insert(CanvasItem *item, int z = -1); + void removeItem(CanvasItem *); + + // Enable to have the canvas updated and blitted on item insertion. + void setImmediateUpdate(bool immediateUpdate) { m_immediateUpdate = immediateUpdate; } + bool immediateUpdate() const { return m_immediateUpdate; } + + QPtrList<CanvasItem> allItems() const { return m_items; } + + unsigned char *renderingBuffer() const { return m_buffer; } + unsigned int nrChannels() const { return m_nrChannels; } + unsigned int rowStride() const { return m_nrChannels * m_width; } + + T2P::Converter *fontContext() { return m_fontContext; } + QPaintDevice *drawWindow() { return m_drawWindow; } + QPaintDevice *directWindow() { return m_directWindow; } + + T2P::FontVisualParams *fontVisualParams(SVGStylableImpl *style) const; + virtual T2P::BezierPath *toBezierPath(CanvasItem *item) const { Q_UNUSED(item); return 0; } + + // Assign z indices to the element and its children, starting with z, and + // return the next z value to be used. + unsigned int setElementItemZIndexRecursive(SVGElementImpl *element, unsigned int z); + +protected: + void addToChunks(CanvasItem *item); + void removeFromChunks(CanvasItem *item); + + void initVars(); + + void fill(); + void clear(const QRect &r); + + virtual void setBuffer(unsigned char *buffer); + +protected: + class ChunkManager + { + public: + ChunkManager() { m_chunks.setAutoDelete(true); } + void addChunk(CanvasChunk *chunk); + CanvasChunk *getChunk(short x, short y) const; + + void clear(); + + private: + QDict<CanvasChunk> m_chunks; + } m_chunkManager; + + QValueList<CanvasChunk *> m_dirtyChunks; + + QMap<CanvasItem *, QPtrList<CanvasChunk> > m_chunksByItem; + QPtrList<CanvasItem> m_items; + + QDict<CanvasClipPath> m_clipPaths; + + unsigned int m_viewportWidth, m_viewportHeight; + int m_width, m_height; + int m_chunkSizeHor, m_chunkSizeVer; + + QPaintDevice *m_drawWindow; + QPaintDevice *m_directWindow; + + float m_zoom; + QPoint m_pan; + + GC m_gc; + + T2P::Converter *m_fontContext; + + unsigned char *m_buffer; + unsigned int m_nrChannels; + + QColor m_backgroundColor; + + bool m_immediateUpdate; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGHelper.cpp b/ksvg/core/KSVGHelper.cpp new file mode 100644 index 00000000..86d111a6 --- /dev/null +++ b/ksvg/core/KSVGHelper.cpp @@ -0,0 +1,92 @@ +/* + Copyright (C) 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 <math.h> + +#include "KSVGHelper.h" + +using namespace KSVG; + +bool KSVGHelper::m_initialised = false; + +int KSVGHelper::m_linearRGBFromsRGB[256]; +int KSVGHelper::m_sRGBFromLinearRGB[256]; + +// Force initialisation of the lookup tables +static KSVGHelper ksvgHelper; + +KSVGHelper::KSVGHelper() +{ + if(!m_initialised) + { + initialise(); + m_initialised = true; + } +} + +void KSVGHelper::initialise() +{ + // Set up linearRGB - sRGB conversion tables + for(int i = 0; i < 256; i++) + { + m_linearRGBFromsRGB[i] = calcLinearRGBFromsRGB(i); + m_sRGBFromLinearRGB[i] = calcSRGBFromLinearRGB(i); + } +} + +int KSVGHelper::calcLinearRGBFromsRGB(int sRGB8bit) +{ + double sRGB = sRGB8bit / 255.0; + double linearRGB; + + if(sRGB <= 0.04045) + linearRGB = sRGB / 12.92; + else + linearRGB = pow((sRGB + 0.055) / 1.055, 2.4); + + return static_cast<int>(linearRGB * 255 + 0.5); +} + +int KSVGHelper::calcSRGBFromLinearRGB(int linearRGB8bit) +{ + double linearRGB = linearRGB8bit / 255.0; + double sRGB; + + if(linearRGB <= 0.0031308) + sRGB = linearRGB * 12.92; + else + sRGB = 1.055 * pow(linearRGB, 1 / 2.4) - 0.055; + + return static_cast<int>(sRGB * 255 + 0.5); +} + +extern "C" +int linearRGBFromsRGB(int sRGB8bit) +{ + return KSVGHelper::linearRGBFromsRGB(sRGB8bit); +} + +extern "C" +int sRGBFromLinearRGB(int linearRGB8bit) +{ + return KSVGHelper::sRGBFromLinearRGB(linearRGB8bit); +} + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGHelper.h b/ksvg/core/KSVGHelper.h new file mode 100644 index 00000000..e310889f --- /dev/null +++ b/ksvg/core/KSVGHelper.h @@ -0,0 +1,144 @@ +/* + 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 + aint 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. +*/ + +#ifndef KSVGHelper_H +#define KSVGHelper_H + +#ifdef __cplusplus + +#include <qcolor.h> +#include <qvaluevector.h> +#include "Affine.h" +#include "Point.h" +#include "SVGMatrixImpl.h" + +namespace KSVG +{ + +class KSVGHelper +{ +public: + KSVGHelper(); + + static void matrixToAffine(SVGMatrixImpl *matrix, double affine[6]) + { + affine[0] = matrix->a(); + affine[1] = matrix->b(); + affine[2] = matrix->c(); + affine[3] = matrix->d(); + affine[4] = matrix->e(); + affine[5] = matrix->f(); + } + + static void matrixToAffine(const SVGMatrixImpl *matrix, double affine[6]) + { + KSVGHelper::matrixToAffine(const_cast<SVGMatrixImpl *>(matrix), affine); + } + + static void matrixToAffine(const SVGMatrixImpl *matrix, T2P::Affine &affine) + { + KSVGHelper::matrixToAffine(const_cast<SVGMatrixImpl *>(matrix), affine.data()); + } + + static void matrixToAffine(SVGMatrixImpl *matrix, T2P::Affine &affine) + { + KSVGHelper::matrixToAffine(matrix, affine.data()); + } + + static QString toColorString(QColor color) + { + int r = color.red(); + int g = color.green(); + int b = color.blue(); + + return "rgb(" + QString::number(r) + "," + QString::number(g) + "," + QString::number(b) + ")"; + } + + static unsigned int toArtColor(const QColor &color) + { + return (qRed(color.rgb()) << 24) | (qGreen(color.rgb()) << 16) | ( qBlue(color.rgb()) << 8) | (qAlpha(color.rgb())); + } + + static unsigned int toArtColor(const QColor &color, short opacity) + { + return (qRed(color.rgb()) << 24) | (qGreen(color.rgb()) << 16) | ( qBlue(color.rgb()) << 8) | (opacity); + } + + static int linearRGBFromsRGB(int sRGB8bit) { return m_linearRGBFromsRGB[sRGB8bit]; } + static int sRGBFromLinearRGB(int linearRGB8bit) { return m_sRGBFromLinearRGB[linearRGB8bit]; } + +private: + static void initialise(); + static int calcLinearRGBFromsRGB(int sRGB8bit); + static int calcSRGBFromLinearRGB(int linearRGB8bit); + + static bool m_initialised; + + static int m_linearRGBFromsRGB[256]; + static int m_sRGBFromLinearRGB[256]; +}; + +typedef T2P::Point KSVGPoint; + +class KSVGPolygon +{ +public: + KSVGPolygon() {} + + void addPoint(const KSVGPoint& point) { m_points.append(point); } + void addPoint(double x, double y) { m_points.append(KSVGPoint(x, y)); } + + KSVGPoint point(unsigned int index) const { return index < m_points.count() ? m_points[index] : KSVGPoint(); } + + unsigned int numPoints() const { return m_points.count(); } + bool isEmpty() const { return m_points.isEmpty(); } + + void clear() { m_points.clear(); } + +private: + QValueVector<KSVGPoint> m_points; +}; + +class KSVGRectangle : public KSVGPolygon +{ +public: + KSVGRectangle() { addPoint(0, 0); addPoint(0, 0); addPoint(0, 0); addPoint(0, 0); } + + // Convenience constructor for an axis-aligned rectangle + KSVGRectangle(double x, double y, double width, double height) + { addPoint(KSVGPoint(x, y)); addPoint(KSVGPoint(x, y + height)); addPoint(KSVGPoint(x + width, y + height)); addPoint(KSVGPoint(x + width, y)); } + +}; +} + +extern "C" +{ +#endif // __cplusplus + +int linearRGBFromsRGB(int sRGB8bit); +int sRGBFromLinearRGB(int linearRGB8bit); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGLoader.cpp b/ksvg/core/KSVGLoader.cpp new file mode 100644 index 00000000..7f7591d7 --- /dev/null +++ b/ksvg/core/KSVGLoader.cpp @@ -0,0 +1,449 @@ +/* + 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 <qxml.h> +#include <qfile.h> +#include <qimage.h> +#include <qbuffer.h> + +#include <kurl.h> +#include <kdebug.h> +#include <kio/job.h> +#include <kfilterdev.h> +#include <kio/netaccess.h> + +#include "SVGDocumentImpl.h" +#include "SVGSVGElementImpl.h" +#include "SVGImageElementImpl.h" + +#include "KSVGLoader.moc" + +using namespace KSVG; + +KSVGLoader::KSVGLoader() : m_data(0) +{ + m_job = 0; +} + +KSVGLoader::~KSVGLoader() +{ +} + +QString KSVGLoader::loadXML(::KURL url) +{ + QString tmpFile; + if(KIO::NetAccess::download(url, tmpFile, 0)) + { + QIODevice *dev = KFilterDev::deviceForFile(tmpFile, "application/x-gzip", true); + QByteArray contents; + if(dev->open(IO_ReadOnly)) + contents = dev->readAll(); + delete dev; + KIO::NetAccess::removeTempFile(tmpFile); + return QString(contents); + } + + return QString::null; +} + +void KSVGLoader::getSVGContent(::KURL url) +{ + if(!url.prettyURL().isEmpty()) + { + if(m_job == 0) + m_job = KIO::get(url, false, false); + + m_job->setAutoErrorHandlingEnabled(true); + + connect(m_job, SIGNAL(data(KIO::Job *, const QByteArray &)), this, SLOT(slotData(KIO::Job *, const QByteArray &))); + connect(m_job, SIGNAL(result(KIO::Job *)), this, SLOT(slotResult(KIO::Job *))); + } +} + +void KSVGLoader::newImageJob(SVGImageElementImpl *image, ::KURL baseURL) +{ + if(image && image->fileName().isEmpty()) + { + kdDebug(26001) << "Image Element has no URL!" << endl; + return; + } + + ImageStreamMap *map = new ImageStreamMap(); + map->data = new QByteArray(); + map->imageElement = image; + + KIO::TransferJob *imageJob = KIO::get(::KURL(baseURL, map->imageElement->fileName()), false, false); + connect(imageJob, SIGNAL(data(KIO::Job *, const QByteArray &)), this, SLOT(slotData(KIO::Job *, const QByteArray &))); + connect(imageJob, SIGNAL(result(KIO::Job *)), this, SLOT(slotResult(KIO::Job *))); + + m_imageJobs.insert(imageJob, map); +} + +void KSVGLoader::slotData(KIO::Job *job, const QByteArray &data) +{ + if(job == m_job) + { + QDataStream dataStream(m_data, IO_WriteOnly | IO_Append); + dataStream.writeRawBytes(data.data(), data.size()); + } + else + { + QMap<KIO::TransferJob *, ImageStreamMap *>::Iterator it; + for(it = m_imageJobs.begin(); it != m_imageJobs.end(); ++it) + { + if(it.key() == job) + { + QDataStream dataStream(*(it.data())->data, IO_WriteOnly | IO_Append); + dataStream.writeRawBytes(data.data(), data.size()); + break; + } + } + } +} + +void KSVGLoader::slotResult(KIO::Job *job) +{ + if(job == m_job) + { + if(m_job->error() == 0) + { + QString check = static_cast<KIO::TransferJob *>(job)->url().prettyURL(); + if(check.contains(".svgz") || check.contains(".svg.gz")) + { + // decode the gzipped svg and emit it + QIODevice *dev = KFilterDev::device(new QBuffer(m_data), "application/x-gzip"); + dev->open(IO_ReadOnly); + emit gotResult(dev); + } + else + { + m_job = 0; + emit gotResult(new QBuffer(m_data)); + m_data.resize(0); + } + } + } + else if(m_postUrlData.job == job) + { + // Notify that we're done + KJS::List callBackArgs; + callBackArgs.append(*m_postUrlData.status); + + m_postUrlData.status->put(m_postUrlData.exec, KJS::Identifier("success"), KJS::Boolean(true)); + m_postUrlData.callBackFunction->call(m_postUrlData.exec, *m_postUrlData.callBackFunction, callBackArgs); + } + else + { + QMap<KIO::TransferJob *, ImageStreamMap *>::Iterator it; + for(it = m_imageJobs.begin(); it != m_imageJobs.end(); ++it) + { + if(it.key() == job) + { + ImageStreamMap *streamMap = it.data(); + + QBuffer buffer(*(streamMap->data)); + + if(buffer.open(IO_ReadOnly)) + { + const char *imageFormat = QImageIO::imageFormat(&buffer); + + if(imageFormat != 0) + { + QImageIO imageIO(&buffer, imageFormat); + + // Gamma correction + imageIO.setGamma(1/0.45454); + + if(imageIO.read()) + { + QImage *image = new QImage(imageIO.image()); + image->detach(); + (streamMap->imageElement)->setImage(image); + } + } + + buffer.close(); + } + + (streamMap->data)->resize(0); + + m_imageJobs.remove(static_cast<KIO::TransferJob *>(job)); + + emit imageReady(streamMap->imageElement); + break; + } + } + } +} + +QString KSVGLoader::getUrl(::KURL url, bool local) +{ + // Security issue: Only retrieve http and https + if(local || (!url.prettyURL().isEmpty()) && ((url.protocol() == "http") || (url.protocol() == "https"))) + return loadXML(url); + + return QString::null; +} + +void KSVGLoader::postUrl(::KURL url, const QByteArray &data, const QString &mimeType, KJS::ExecState *exec, KJS::Object &callBackFunction, KJS::Object &status) +{ + KIO::TransferJob *job = KIO::http_post(url, data, false); + job->addMetaData("content-type", mimeType); + + m_postUrlData.job = job; + m_postUrlData.exec = exec; + m_postUrlData.status = &status; + m_postUrlData.callBackFunction = &callBackFunction; + + connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *))); +} + +class CharacterDataSearcher : public QXmlDefaultHandler +{ +public: + CharacterDataSearcher(const QString &id) : m_id(id) { } + + virtual bool startDocument() + { + m_foundCount = 0; + return true; + } + + virtual bool startElement(const QString &, const QString &, const QString &qName, const QXmlAttributes &atts) + { + kdDebug(26001) << "CharacterDataSearcher::startElement, qName " << qName << endl; + + int pos = atts.index("id"); + if(pos > -1 && atts.value(pos) == m_id) + { + m_foundCount++; + m_tagFound = qName; + } + + return true; + } + + virtual bool endElement(const QString &, const QString &, const QString &qName) + { + if(m_tagFound == qName && m_foundCount > 0) + { + m_foundCount--; + if(m_foundCount == 0) + return false; // done! + } + + return true; + } + + virtual bool characters(const QString &ch) + { + kdDebug(26001) << "CharacterDataSearcher::characters, read " << ch.latin1() << endl; + + if(m_tagFound != 0) + m_result += ch; + + return true; + } + + QString result() { return m_result; } + +private: + QString m_id, m_result, m_tagFound; + int m_foundCount; +}; + +QString KSVGLoader::getCharacterData(::KURL url, const QString &id) +{ + QXmlSimpleReader reader; + + CharacterDataSearcher searcher(id); + reader.setContentHandler(&searcher); + reader.setErrorHandler(&searcher); + + QString s = loadXML(url); + + QXmlInputSource source; + source.setData(s); + + reader.parse(&source); + + return searcher.result(); +} + + + +class SVGFragmentSearcher : public QXmlDefaultHandler +{ +public: + SVGFragmentSearcher(SVGDocumentImpl *doc, const QString &id, ::KURL url) : m_id(id), m_url(url), m_doc(doc) { } + + virtual bool startDocument() + { + m_result = 0; + m_currentNode = 0; + + return true; + } + + virtual bool startElement(const QString &namespaceURI, const QString &, const QString &qName, const QXmlAttributes &attrs) + { + kdDebug(26001) << "SVGFragmentSearcher::startElement, namespaceURI " << namespaceURI << ", qName " << qName << endl; + bool parse = m_result; + if(!parse) + { + int pos = attrs.index("id"); + if(pos > -1 && attrs.value(pos) == m_id) + parse = true; + } + + if(parse) + { + DOM::Element impl = static_cast<DOM::Document *>(m_doc)->createElementNS(namespaceURI, qName); + SVGElementImpl *newElement = SVGDocumentImpl::createElement(qName, impl, m_doc); + newElement->setViewportElement(m_doc->rootElement()); + + if(m_currentNode) + m_currentNode->appendChild(*newElement); + else + m_result = newElement; + + QXmlAttributes newAttrs; + + for(int i = 0; i < attrs.count(); i++) + { + QString name = attrs.localName(i); + QString value = attrs.value(i); + + if(name == "id") + { + value = "@fragment@" + m_url.prettyURL() + "@" + value; + m_idMap[value] = newElement; + } + else + if(name == "href") + { + value.stripWhiteSpace(); + + if(value.startsWith("#")) + { + value.remove(0, 1); + + // Convert the id to its mangled version. + QString id = "@fragment@" + m_url.prettyURL() + "@" + value; + + if(m_idMap.contains(id)) + { + // This is a local reference to an element within the fragment. + // Just convert the href. + value = id; + } + else + { + // This is a local reference to an id outside of the fragment. + // Change it into an absolute href. + value = m_url.prettyURL() + "#" + value; + } + } + } + + newAttrs.append(attrs.qName(i), attrs.uri(i), attrs.localName(i), value); + } + + newElement->setAttributes(newAttrs); + m_currentNode = newElement; + } + + return true; + } + + virtual bool endElement(const QString &, const QString &, const QString &) + { + if(m_result) + { + m_parentNode = m_currentNode->parentNode(); + + if(m_parentNode.isNull()) + return false; // done! + + m_currentNode = &m_parentNode; + } + + return true; + } + + virtual bool characters(const QString &ch) + { + kdDebug(26001) << "SVGFragmentSearcher::characters, read " << ch.latin1() << endl; + + if(m_result) + { + SVGElementImpl *element = m_result->ownerDoc()->getElementFromHandle(m_currentNode->handle()); + if(element) + { + QString t = ch; + + SVGLangSpaceImpl *langSpace = dynamic_cast<SVGLangSpaceImpl *>(element); + if(langSpace) + t = langSpace->handleText(ch); + + if(!t.isEmpty()) + { + DOM::Text impl = static_cast<DOM::Document *>(m_result->ownerDoc())->createTextNode(t); + element->appendChild(impl); + } + } + } + + return true; + } + + SVGElementImpl *result() { return m_result; } + +private: + QString m_id; + ::KURL m_url; + + SVGDocumentImpl *m_doc; + SVGElementImpl *m_result; + + DOM::Node *m_currentNode, m_parentNode; + QMap<QString, SVGElementImpl *> m_idMap; +}; + +SVGElementImpl *KSVGLoader::getSVGFragment(::KURL url, SVGDocumentImpl *doc, const QString &id) +{ + QXmlSimpleReader reader; + + kdDebug(26001) << "getSVGFragment: " << url.prettyURL() << "#" << id << endl; + SVGFragmentSearcher searcher(doc, id, url); + reader.setContentHandler(&searcher); + reader.setErrorHandler(&searcher); + + QString s = loadXML(url); + + QXmlInputSource source; + source.setData(s); + + reader.parse(&source); + + return searcher.result(); +} + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGLoader.h b/ksvg/core/KSVGLoader.h new file mode 100644 index 00000000..d0418411 --- /dev/null +++ b/ksvg/core/KSVGLoader.h @@ -0,0 +1,92 @@ +/* + 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. +*/ + +#ifndef KSVGLoader_H +#define KSVGLoader_H + +#include <qobject.h> + +class KURL; + +namespace KIO +{ + class Job; + class TransferJob; +} + +namespace KJS +{ + class Object; + class ExecState; +} + +namespace KSVG +{ + +struct ImageStreamMap; + +typedef struct +{ + KIO::Job *job; + KJS::ExecState *exec; + KJS::Object *callBackFunction, *status; +} PostUrlData; + +class SVGImageElementImpl; +class SVGElementImpl; +class SVGDocumentImpl; +class KSVGLoader : public QObject +{ +Q_OBJECT +public: + KSVGLoader(); + ~KSVGLoader(); + + void getSVGContent(::KURL url); + void newImageJob(SVGImageElementImpl *impl, ::KURL url); + + static QString getUrl(::KURL url, bool local = false); + void postUrl(::KURL url, const QByteArray &data, const QString &mimeType, KJS::ExecState *exec, KJS::Object &callBackFunction, KJS::Object &status); + static QString getCharacterData(::KURL url, const QString &id); + static SVGElementImpl *getSVGFragment(::KURL, SVGDocumentImpl *doc, const QString &id); + +signals: + void gotResult(QIODevice *); + void imageReady(SVGImageElementImpl *); + +private slots: + void slotData(KIO::Job *, const QByteArray &); + void slotResult(KIO::Job *); + +private: + static QString loadXML(::KURL); + + PostUrlData m_postUrlData; + + QByteArray m_data; + KIO::TransferJob *m_job; + QMap<KIO::TransferJob *, ImageStreamMap *> m_imageJobs; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGReader.cc b/ksvg/core/KSVGReader.cc new file mode 100644 index 00000000..dd73e420 --- /dev/null +++ b/ksvg/core/KSVGReader.cc @@ -0,0 +1,500 @@ +/* + 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 <klocale.h> +#include <qmap.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 QString &errorDesc = "") { m_reader->setFinished(error, errorDesc); } + + // Error handling + void setErrorDescription(const QString &err) { m_errorDesc = err; } + QString errorDescription() { return m_errorDesc; } + bool hasError() const { return !m_errorDesc.isEmpty(); } + + bool getURLMode() const { return m_getURLMode; } + void setGetURLMode(bool mode) { m_getURLMode = mode; } + + QString SVGFragmentId() const { return m_SVGFragmentId; } + void setSVGFragmentId(const QString &SVGFragmentId) { m_SVGFragmentId = SVGFragmentId; } + +protected: + Helper(KSVGReader *reader); + +private: + Helper(); + Helper(const Helper &rhs); + Helper &operator=(const Helper &rhs); + + static Helper *m_instance; + QMap<DOM::NodeImpl *, SVGSVGElementImpl *> m_svgMap; + KSVGReader *m_reader; + bool m_bFit; + bool m_getURLMode; + QString m_errorDesc; + QString m_SVGFragmentId; +}; + +class InputHandler : public QXmlDefaultHandler +{ +public: + virtual bool startDocument(); + virtual bool endDocument(); + virtual bool startElement(const QString &namespaceURI, + const QString &localName, + const QString &qName, + const QXmlAttributes &atts); + virtual bool endElement(const QString &namespaceURI, + const QString &localName, + const QString &qName); + virtual bool characters(const QString &ch); + virtual bool warning(const QXmlParseException &e); + virtual bool error(const QXmlParseException &e); + virtual bool fatalError(const QXmlParseException &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 QString &ch) +{ + kdDebug(26001) << "InputHandler::characters, read " << ch << endl; + + if(ch.simplifyWhiteSpace().isEmpty()) + return true; + + QString 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 QString &namespaceURI, const QString &, const QString &qName, const QXmlAttributes &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 + // TODO: Those set/get attribute callls have NO effect anymore + // Convert to the new system, Rob? (Niko) + { + 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(svg->getAttribute("viewBox").string().isEmpty()) + { + SVGLengthImpl *width = SVGSVGElementImpl::createSVGLength(); + SVGLengthImpl *height = SVGSVGElementImpl::createSVGLength(); + width->setValueAsString(svg->getAttribute("width").string()); + height->setValueAsString(svg->getAttribute("height").string()); + QString viewbox = QString("0 0 %1 %2").arg(width->value()).arg(height->value()); + //kdDebug(26001) << "VIEWBOX : " << viewbox.latin1() << endl; + svg->setAttribute("viewBox", viewbox); + width->deref(); + height->deref(); + } + svg->setAttribute("width", QString::number(Helper::self()->canvas()->width())); + svg->setAttribute("height", QString::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 QString &, const QString &, const QString &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(); + + QValueList<SVGUseElementImpl *> forwardReferencingUseElements = Helper::self()->doc()->forwardReferencingUseElements(); + + if(!forwardReferencingUseElements.isEmpty()) + { + // Create the elements again now that we have parsed the whole document. + QValueList<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 QXmlParseException &e) +{ + kdDebug(26001) << "[" << e.lineNumber() << ":" << e.columnNumber() << "]: WARNING: " << e.message() << endl; + return true; +} + +bool InputHandler::error(const QXmlParseException &e) +{ + kdDebug(26001) << "[" << e.lineNumber() << ":" << e.columnNumber() << "]: ERROR: " << e.message() << endl; + return true; +} + +bool InputHandler::fatalError(const QXmlParseException &e) +{ + QString error; + + if(Helper::self()->hasError()) + { + error = Helper::self()->errorDescription(); + Helper::self()->setErrorDescription(QString::null); + } + else + error = QString("[%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 +{ + QXmlSimpleReader *reader; + InputHandler *inputHandler; + SVGDocumentImpl *doc; + KSVGCanvas *canvas; +}; + +KSVGReader::KSVGReader(SVGDocumentImpl *doc, KSVGCanvas *canvas, ParsingArgs args) : QObject(), d(new Private) +{ + d->doc = doc; + d->canvas = canvas; + + d->reader = new QXmlSimpleReader(); + 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(QXmlInputSource *source) +{ + d->reader->parse(source); +} + +void KSVGReader::finishParsing(bool, const QString &errorDesc) +{ + Helper::self()->setErrorDescription(errorDesc); +} + +void KSVGReader::setFinished(bool error, const QString &errorDesc) +{ + kdDebug(26001) << "KSVGReader::setFinished" << endl; + emit finished(error, errorDesc); +} + +SVGDocumentImpl *KSVGReader::doc() +{ + return d->doc; +} + +KSVG::KSVGCanvas *KSVGReader::canvas() +{ + return d->canvas; +} + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGReader.h b/ksvg/core/KSVGReader.h new file mode 100644 index 00000000..722720e1 --- /dev/null +++ b/ksvg/core/KSVGReader.h @@ -0,0 +1,67 @@ +/* + 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. +*/ + +#ifndef KSVGReader_H +#define KSVGReader_H + +#include <qxml.h> +#include <qobject.h> + +namespace KSVG +{ + +class SVGDocumentImpl; +class KSVGReader : public QObject +{ +Q_OBJECT +public: + struct ParsingArgs + { + bool fit; + bool getURLMode; + + QString SVGFragmentId; + }; + + KSVGReader(SVGDocumentImpl *doc, KSVGCanvas *canvas, ParsingArgs args); + virtual ~KSVGReader(); + + void parse(QXmlInputSource *source); + void finishParsing(bool, const QString &); + +signals: + void finished(bool, const QString &); + +protected: + friend class Helper; + + SVGDocumentImpl *doc(); + KSVGCanvas *canvas(); + + void setFinished(bool error, const QString &errorDesc = 0); + +private: + struct Private; + Private *d; +}; + +} + +#endif diff --git a/ksvg/core/KSVGTextChunk.cpp b/ksvg/core/KSVGTextChunk.cpp new file mode 100644 index 00000000..b8eddcad --- /dev/null +++ b/ksvg/core/KSVGTextChunk.cpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 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 + aint 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 "SVGTextContentElementImpl.h" +#include "SVGTextPositioningElementImpl.h" +#include "KSVGTextChunk.h" + +using namespace KSVG; + +// Text chunks (class to store text data) +KSVGTextChunk::KSVGTextChunk() +{ +} + +KSVGTextChunk::~KSVGTextChunk() +{ +} + +unsigned int KSVGTextChunk::count() const +{ + return m_text.count(); +} + +QString KSVGTextChunk::getText(unsigned int index) const +{ + return m_text[index]; +} + +SVGTextPositioningElementImpl *KSVGTextChunk::getTextElement(unsigned int index) +{ + SVGTextContentElementImpl *content = getTextContentElement(index); + return dynamic_cast<SVGTextPositioningElementImpl *>(content); +} + +SVGTextContentElementImpl *KSVGTextChunk::getTextContentElement(unsigned int index) +{ + return m_textElements.at(index); +} + +void KSVGTextChunk::clear() +{ + m_text.clear(); + m_textElements.clear(); +} + +void KSVGTextChunk::addText(const QString &text, SVGTextContentElementImpl *textElement) +{ + m_text.append(text); + m_textElements.append(textElement); +} + +// vim:ts=4:noet diff --git a/ksvg/core/KSVGTextChunk.h b/ksvg/core/KSVGTextChunk.h new file mode 100644 index 00000000..d684087a --- /dev/null +++ b/ksvg/core/KSVGTextChunk.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 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 + aint 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. +*/ + +#ifndef KSVGTEXTCHUNK_H +#define KSVGTEXTCHUNK_H + +#include <qstringlist.h> + +namespace KSVG +{ + +class SVGTextContentElementImpl; +class SVGTextPositioningElementImpl; +class KSVGTextChunk +{ +public: + KSVGTextChunk(); + ~KSVGTextChunk(); + + unsigned int count() const; + QString getText(unsigned int index) const; + SVGTextPositioningElementImpl *getTextElement(unsigned int index); + SVGTextContentElementImpl *getTextContentElement(unsigned int index); + + void clear(); + void addText(const QString &text, SVGTextContentElementImpl *textElement); + +private: + QStringList m_text; + QPtrList<SVGTextContentElementImpl> m_textElements; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/ksvg/core/Makefile.am b/ksvg/core/Makefile.am new file mode 100644 index 00000000..e1867428 --- /dev/null +++ b/ksvg/core/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libksvgcore.la + +libksvgcore_la_SOURCES = KSVGLoader.cpp KSVGCanvas.cpp KSVGReader.cc KSVGTextChunk.cpp CanvasFactory.cpp CanvasItems.cpp KSVGHelper.cpp DocumentFactory.cpp +libksvgcore_la_METASOURCES = AUTO + +servicetypedir = $(kde_servicetypesdir) +servicetype_DATA = ksvgrenderer.desktop + +ksvginclude_HEADERS = KSVGCanvas.h CanvasItems.h CanvasItem.h CanvasFactory.h DocumentFactory.h +ksvgincludedir = $(includedir)/ksvg + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) +INCLUDES = $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) -I$(top_srcdir)/ksvg/dom -I$(top_srcdir)/ksvg/impl -I$(top_srcdir)/ksvg/ecma -I$(top_srcdir)/ksvg/impl/libs/art_support -I$(top_srcdir)/ksvg/impl/libs/libtext2path/src $(all_includes) + +KDE_OPTIONS = nofinal + diff --git a/ksvg/core/ksvgrenderer.desktop b/ksvg/core/ksvgrenderer.desktop new file mode 100644 index 00000000..f9307621 --- /dev/null +++ b/ksvg/core/ksvgrenderer.desktop @@ -0,0 +1,56 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KSVG/Renderer +X-KDE-Derived= +Comment=KSVG Rendering Backend +Comment[ar]=خلفية رسم KSVG +Comment[bs]=KSVG renderiranje +Comment[ca]=Representació en segon pla de KSVG +Comment[cs]=Vykreslovací nástroj KSVG +Comment[cy]=Ôl-wyneb Llunio KSVG +Comment[da]=Underliggende program for KSVG-visning +Comment[de]=KSVG-Darstellungsmodul +Comment[el]=Σύστημα υποστήριξης αποτύπωσης του KSVG +Comment[es]=Motor de procesado de KSVG +Comment[et]=KSVG renderdamise taustarakendus +Comment[eu]=KSVG errendatze programa +Comment[fa]=پایانۀ پشتیبانی پرداخت KSVG +Comment[fi]=KSVG-piirtäjän taustaohjelma +Comment[fr]=Moteur de rendu KSVG +Comment[ga]=Inneall Rindreála KSVG +Comment[gl]=Backend de Renderizado KSVG +Comment[he]=מנוע רינדור KSVG +Comment[hi]=के-एसवीजी रेंडरिंग बैकएण्ड +Comment[hu]=KSVG megjelenítőmotor +Comment[is]=KSVG teiknari +Comment[it]=Backend di KSVG per il rendering +Comment[ja]=KSVG レンダリングバックエンド +Comment[kk]=KSVG кескіндеу бағдарламасы +Comment[km]=កម្មវិធីសម្រាប់បង្ហាញ KSVG +Comment[lt]=KSVG atkūrimo programinė sąsaja +Comment[ms]=Tepi Belakang Menrealisasi KSVG +Comment[nb]=Modul for KSVG-tegning +Comment[nds]=KSVG-Dorstellhölper +Comment[ne]=KSVG रेन्डरिङ ब्याकइन्ड +Comment[nl]=KSVG weergavecomponent +Comment[nn]=Modul for KSVG-teikning +Comment[pl]=Narzędzie do renderowania KSVG +Comment[pt]=Infra-Estrutura de Desenho KSVG +Comment[pt_BR]=Estrutura de Renderização do KSVG +Comment[ro]=Motorul de randare KSVG +Comment[ru]=Движок прорисовки KSVG +Comment[sk]=Nástroj pre zobrazovanie KSVG +Comment[sl]=Izrisovalnik KSVG +Comment[sr]=KSVG-ов позадински систем за рендеровање +Comment[sr@Latn]=KSVG-ov pozadinski sistem za renderovanje +Comment[sv]=KSVG-uppritningsmodul +Comment[ta]=KSVG வழங்கும் பின் அமைப்பு +Comment[tg]=Лағжандаи тасовироти KSVG +Comment[tr]=KSVG Tarama Arkayüzü +Comment[uk]=Інтерфейс відтворення KSVG +Comment[zh_CN]=KSVG 渲染后端 +Comment[zh_HK]=KSVG 合成後端 +Comment[zh_TW]=KSVG 上色後端介面 + +[PropertyDef::X-KSVG-InternalName] +Type=QString |