You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/ksvg/core/KSVGLoader.cpp

450 lines
11 KiB

/*
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 <tqxml.h>
#include <tqfile.h>
#include <tqimage.h>
#include <tqbuffer.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()
{
}
TQString KSVGLoader::loadXML(::KURL url)
{
TQString tmpFile;
if(KIO::NetAccess::download(url, tmpFile, 0))
{
TQIODevice *dev = KFilterDev::deviceForFile(tmpFile, "application/x-gzip", true);
TQByteArray contents;
if(dev->open(IO_ReadOnly))
contents = dev->readAll();
delete dev;
KIO::NetAccess::removeTempFile(tmpFile);
return TQString(contents);
}
return TQString::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, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)), this, TQT_SLOT(slotData(KIO::Job *, const TQByteArray &)));
connect(m_job, TQT_SIGNAL(result(KIO::Job *)), this, TQT_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 TQByteArray();
map->imageElement = image;
KIO::TransferJob *imageJob = KIO::get(::KURL(baseURL, map->imageElement->fileName()), false, false);
connect(imageJob, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)), this, TQT_SLOT(slotData(KIO::Job *, const TQByteArray &)));
connect(imageJob, TQT_SIGNAL(result(KIO::Job *)), this, TQT_SLOT(slotResult(KIO::Job *)));
m_imageJobs.insert(imageJob, map);
}
void KSVGLoader::slotData(KIO::Job *job, const TQByteArray &data)
{
if(job == m_job)
{
TQDataStream dataStream(m_data, IO_WriteOnly | IO_Append);
dataStream.writeRawBytes(data.data(), data.size());
}
else
{
TQMap<KIO::TransferJob *, ImageStreamMap *>::Iterator it;
for(it = m_imageJobs.begin(); it != m_imageJobs.end(); ++it)
{
if(it.key() == job)
{
TQDataStream 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)
{
TQString check = static_cast<KIO::TransferJob *>(job)->url().prettyURL();
if(check.contains(".svgz") || check.contains(".svg.gz"))
{
// decode the gzipped svg and emit it
TQIODevice *dev = KFilterDev::device(new TQBuffer(m_data), "application/x-gzip");
dev->open(IO_ReadOnly);
emit gotResult(dev);
}
else
{
m_job = 0;
emit gotResult(new TQBuffer(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
{
TQMap<KIO::TransferJob *, ImageStreamMap *>::Iterator it;
for(it = m_imageJobs.begin(); it != m_imageJobs.end(); ++it)
{
if(it.key() == job)
{
ImageStreamMap *streamMap = it.data();
TQBuffer buffer(*(streamMap->data));
if(buffer.open(IO_ReadOnly))
{
const char *imageFormat = TQImageIO::imageFormat(&buffer);
if(imageFormat != 0)
{
TQImageIO imageIO(&buffer, imageFormat);
// Gamma correction
imageIO.setGamma(1/0.45454);
if(imageIO.read())
{
TQImage *image = new TQImage(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;
}
}
}
}
TQString 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 TQString::null;
}
void KSVGLoader::postUrl(::KURL url, const TQByteArray &data, const TQString &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, TQT_SIGNAL(result(KIO::Job *)), TQT_SLOT(slotResult(KIO::Job *)));
}
class CharacterDataSearcher : public QXmlDefaultHandler
{
public:
CharacterDataSearcher(const TQString &id) : m_id(id) { }
virtual bool startDocument()
{
m_foundCount = 0;
return true;
}
virtual bool startElement(const TQString &, const TQString &, const TQString &qName, const TQXmlAttributes &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 TQString &, const TQString &, const TQString &qName)
{
if(m_tagFound == qName && m_foundCount > 0)
{
m_foundCount--;
if(m_foundCount == 0)
return false; // done!
}
return true;
}
virtual bool characters(const TQString &ch)
{
kdDebug(26001) << "CharacterDataSearcher::characters, read " << ch.latin1() << endl;
if(m_tagFound != 0)
m_result += ch;
return true;
}
TQString result() { return m_result; }
private:
TQString m_id, m_result, m_tagFound;
int m_foundCount;
};
TQString KSVGLoader::getCharacterData(::KURL url, const TQString &id)
{
TQXmlSimpleReader reader;
CharacterDataSearcher searcher(id);
reader.setContentHandler(&searcher);
reader.setErrorHandler(&searcher);
TQString s = loadXML(url);
TQXmlInputSource source;
source.setData(s);
reader.parse(&source);
return searcher.result();
}
class SVGFragmentSearcher : public QXmlDefaultHandler
{
public:
SVGFragmentSearcher(SVGDocumentImpl *doc, const TQString &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 TQString &namespaceURI, const TQString &, const TQString &qName, const TQXmlAttributes &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;
TQXmlAttributes newAttrs;
for(int i = 0; i < attrs.count(); i++)
{
TQString name = attrs.localName(i);
TQString 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.
TQString 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 TQString &, const TQString &, const TQString &)
{
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 TQString &ch)
{
kdDebug(26001) << "SVGFragmentSearcher::characters, read " << ch.latin1() << endl;
if(m_result)
{
SVGElementImpl *element = m_result->ownerDoc()->getElementFromHandle(m_currentNode->handle());
if(element)
{
TQString 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:
TQString m_id;
::KURL m_url;
SVGDocumentImpl *m_doc;
SVGElementImpl *m_result;
DOM::Node *m_currentNode, m_parentNode;
TQMap<TQString, SVGElementImpl *> m_idMap;
};
SVGElementImpl *KSVGLoader::getSVGFragment(::KURL url, SVGDocumentImpl *doc, const TQString &id)
{
TQXmlSimpleReader reader;
kdDebug(26001) << "getSVGFragment: " << url.prettyURL() << "#" << id << endl;
SVGFragmentSearcher searcher(doc, id, url);
reader.setContentHandler(&searcher);
reader.setErrorHandler(&searcher);
TQString s = loadXML(url);
TQXmlInputSource source;
source.setData(s);
reader.parse(&source);
return searcher.result();
}
// vim:ts=4:noet