dsvg imageformats plugin sans dtk dependency
This commit is contained in:
parent
383edf5627
commit
fbe90c3485
|
@ -1,8 +1,10 @@
|
||||||
project (pimageformats)
|
project(pimageformats)
|
||||||
|
|
||||||
cmake_minimum_required (VERSION 3.9.5)
|
cmake_minimum_required(VERSION 3.9.5)
|
||||||
|
|
||||||
include (GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
option(BUILD_EXPERIMENTAL_PLUGINS "Enable the build of imageformats plugin(s) that might not working" ON)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
@ -10,5 +12,8 @@ set(QT_MINIMUM_VERSION "5.10")
|
||||||
|
|
||||||
find_package(Qt5 ${QT_MINIMUM_VERSION} CONFIG REQUIRED Gui)
|
find_package(Qt5 ${QT_MINIMUM_VERSION} CONFIG REQUIRED Gui)
|
||||||
|
|
||||||
add_subdirectory(vendor/libsai)
|
if(BUILD_EXPERIMENTAL_PLUGINS)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/vendor/libsai)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(imageformats)
|
add_subdirectory(imageformats)
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
set (plugin pimg_sai)
|
if(BUILD_EXPERIMENTAL_PLUGINS)
|
||||||
|
add_subdirectory(sai)
|
||||||
set (PLUGIN_SOURCES
|
endif()
|
||||||
sai_p.h
|
add_subdirectory(svg)
|
||||||
sai.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# {{{ KCM style
|
|
||||||
set(CMAKE_SHARED_MODULE_PREFIX "")
|
|
||||||
unset(CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
add_library(${plugin} MODULE ${PLUGIN_SOURCES})
|
|
||||||
set_property(TARGET ${plugin} APPEND PROPERTY AUTOGEN_TARGET_DEPENDS "sai.json")
|
|
||||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/imageformats")
|
|
||||||
target_link_libraries(${plugin} Qt5::Gui sai)
|
|
||||||
|
|
17
imageformats/sai/CMakeLists.txt
Normal file
17
imageformats/sai/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
set (plugin pimg_sai)
|
||||||
|
|
||||||
|
set (PLUGIN_SOURCES
|
||||||
|
sai_p.h
|
||||||
|
sai.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# {{{ KCM style
|
||||||
|
set(CMAKE_SHARED_MODULE_PREFIX "")
|
||||||
|
unset(CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
add_library(${plugin} MODULE ${PLUGIN_SOURCES})
|
||||||
|
set_property(TARGET ${plugin} APPEND PROPERTY AUTOGEN_TARGET_DEPENDS "sai.json")
|
||||||
|
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/imageformats")
|
||||||
|
target_link_libraries(${plugin} Qt5::Gui sai)
|
26
imageformats/svg/CMakeLists.txt
Normal file
26
imageformats/svg/CMakeLists.txt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
set (plugin pimg_svg)
|
||||||
|
|
||||||
|
set (PLUGIN_SOURCES
|
||||||
|
main.cpp
|
||||||
|
svg_p.h
|
||||||
|
svg.cpp
|
||||||
|
# since dtkgui still cannot compile under platforms other than Linux...
|
||||||
|
dsvgrenderer.h
|
||||||
|
dsvgrenderer.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package(Qt5 ${QT_MINIMUM_VERSION} CONFIG REQUIRED Svg)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(rsvg REQUIRED librsvg-2.0 IMPORTED_TARGET)
|
||||||
|
|
||||||
|
# {{{ KCM style
|
||||||
|
set(CMAKE_SHARED_MODULE_PREFIX "")
|
||||||
|
unset(CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
add_library(${plugin} MODULE ${PLUGIN_SOURCES})
|
||||||
|
set_property(TARGET ${plugin} APPEND PROPERTY AUTOGEN_TARGET_DEPENDS "svg.json")
|
||||||
|
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/imageformats")
|
||||||
|
target_link_libraries(${plugin} Qt5::Gui Qt5::Svg PkgConfig::rsvg)
|
280
imageformats/svg/dsvgrenderer.cpp
Normal file
280
imageformats/svg/dsvgrenderer.cpp
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 - 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef DTK_DISABLE_LIBRSVG
|
||||||
|
#include <librsvg/rsvg.h>
|
||||||
|
#else
|
||||||
|
#include <QSvgRenderer>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "dsvgrenderer.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
|
class DSvgRendererPrivate : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DSvgRendererPrivate(DSvgRenderer *qq);
|
||||||
|
|
||||||
|
QImage getImage(const QSize &size, const QString &elementId) const;
|
||||||
|
|
||||||
|
RsvgHandle *handle = nullptr;
|
||||||
|
QSize defaultSize;
|
||||||
|
mutable QRectF viewBox;
|
||||||
|
};
|
||||||
|
|
||||||
|
DSvgRendererPrivate::DSvgRendererPrivate(DSvgRenderer *qq)
|
||||||
|
: QObject(qq) // qq ==> DObject
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage DSvgRendererPrivate::getImage(const QSize &size, const QString &elementId) const
|
||||||
|
{
|
||||||
|
QImage image(size, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
|
||||||
|
image.fill(Qt::transparent);
|
||||||
|
|
||||||
|
cairo_surface_t *surface = cairo_image_surface_create_for_data(image.bits(), CAIRO_FORMAT_ARGB32, image.width(), image.height(), image.bytesPerLine());
|
||||||
|
cairo_t *cairo = cairo_create(surface);
|
||||||
|
cairo_scale(cairo, image.width() / viewBox.width(), image.height() / viewBox.height());
|
||||||
|
cairo_translate(cairo, -viewBox.x(), -viewBox.y());
|
||||||
|
|
||||||
|
if (elementId.isEmpty())
|
||||||
|
rsvg_handle_render_cairo(handle, cairo);
|
||||||
|
else
|
||||||
|
rsvg_handle_render_cairo_sub(handle, cairo, elementId.toUtf8().constData());
|
||||||
|
|
||||||
|
cairo_destroy(cairo);
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class Dtk::Gui::DSvgRenderer
|
||||||
|
\inmodule dtkgui
|
||||||
|
\brief 提供了将SVG文件的内容绘制到绘制设备上的方法.
|
||||||
|
|
||||||
|
SVG图形可以在构造 DSvgRenderer 时加载,也可以稍后使用load()函数加载。
|
||||||
|
因为渲染是使用 QPainter 执行的,所以可以在 QPaintDevice 的任何子类上渲染SVG图形。
|
||||||
|
如果加载了有效文件,则无论是在构造时还是以后某个时间,isValid()都将返回true;否则将返回false。
|
||||||
|
DSvgRenderer提供render()插槽,用于使用给定的 QPainter 渲染当前文档或动画文档的当前帧
|
||||||
|
\note 使用 DSvgRenderer 需要 librsvg库
|
||||||
|
*/
|
||||||
|
|
||||||
|
DSvgRenderer::DSvgRenderer(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DSvgRenderer::DSvgRenderer(const QString &filename, QObject *parent)
|
||||||
|
: DSvgRenderer(parent)
|
||||||
|
{
|
||||||
|
load(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
DSvgRenderer::DSvgRenderer(const QByteArray &contents, QObject *parent)
|
||||||
|
: DSvgRenderer(parent)
|
||||||
|
{
|
||||||
|
load(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
DSvgRenderer::~DSvgRenderer()
|
||||||
|
{
|
||||||
|
Q_D(DSvgRenderer);
|
||||||
|
|
||||||
|
if (d->handle) {
|
||||||
|
Q_ASSERT(isValid());
|
||||||
|
g_object_unref(d->handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DSvgRenderer::isValid() const
|
||||||
|
{
|
||||||
|
Q_D(const DSvgRenderer);
|
||||||
|
return d->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize DSvgRenderer::defaultSize() const
|
||||||
|
{
|
||||||
|
Q_D(const DSvgRenderer);
|
||||||
|
return d->defaultSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect DSvgRenderer::viewBox() const
|
||||||
|
{
|
||||||
|
Q_D(const DSvgRenderer);
|
||||||
|
return d->handle ? d->viewBox.toRect() : QRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF DSvgRenderer::viewBoxF() const
|
||||||
|
{
|
||||||
|
Q_D(const DSvgRenderer);
|
||||||
|
return d->handle ? d->viewBox : QRectF();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSvgRenderer::setViewBox(const QRect &viewbox)
|
||||||
|
{
|
||||||
|
setViewBox(QRectF(viewbox));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSvgRenderer::setViewBox(const QRectF &viewbox)
|
||||||
|
{
|
||||||
|
Q_D(DSvgRenderer);
|
||||||
|
if (d->handle)
|
||||||
|
d->viewBox = viewbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF DSvgRenderer::boundsOnElement(const QString &id) const
|
||||||
|
{
|
||||||
|
Q_D(const DSvgRenderer);
|
||||||
|
if (!d->handle)
|
||||||
|
return QRectF();
|
||||||
|
|
||||||
|
const QByteArray &id_data = id.toUtf8();
|
||||||
|
|
||||||
|
RsvgDimensionData dimension_data;
|
||||||
|
|
||||||
|
if (!rsvg_handle_get_dimensions_sub(d->handle, &dimension_data, id_data.constData()))
|
||||||
|
return QRectF();
|
||||||
|
|
||||||
|
RsvgPositionData pos_data;
|
||||||
|
|
||||||
|
if (!rsvg_handle_get_position_sub(d->handle, &pos_data, id_data.constData()))
|
||||||
|
return QRectF();
|
||||||
|
|
||||||
|
return QRectF(pos_data.x, pos_data.y, dimension_data.width, dimension_data.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DSvgRenderer::elementExists(const QString &id) const
|
||||||
|
{
|
||||||
|
Q_D(const DSvgRenderer);
|
||||||
|
if (!d->handle)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return rsvg_handle_has_sub(d->handle, id.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage DSvgRenderer::toImage(const QSize sz, const QString &elementId) const
|
||||||
|
{
|
||||||
|
Q_D(const DSvgRenderer);
|
||||||
|
|
||||||
|
return d->getImage(sz, elementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray updateXmlAttribute(const QString &contents)
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
QXmlStreamWriter writer(&data);
|
||||||
|
QXmlStreamReader reader(contents);
|
||||||
|
while(reader.readNext() != QXmlStreamReader::Invalid && !reader.atEnd()) {
|
||||||
|
if (reader.tokenType() != QXmlStreamReader::StartElement ||
|
||||||
|
!reader.attributes().hasAttribute("href")) {
|
||||||
|
writer.writeCurrentToken(reader);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &nd : reader.namespaceDeclarations())
|
||||||
|
writer.writeNamespace(nd.namespaceUri().toString(), nd.prefix().toString());
|
||||||
|
|
||||||
|
writer.writeStartElement(reader.namespaceUri().toString(), reader.name().toString());
|
||||||
|
|
||||||
|
for (const auto &attr : reader.attributes()) {
|
||||||
|
if (attr.name() == "href") {
|
||||||
|
writer.writeAttribute("xlink:href", attr.value().toString());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
writer.writeAttribute(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray format(const QByteArray &contents)
|
||||||
|
{
|
||||||
|
QXmlStreamReader reader(contents);
|
||||||
|
while (reader.readNextStartElement()) {
|
||||||
|
if (reader.attributes().hasAttribute("href"))
|
||||||
|
return updateXmlAttribute(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DSvgRenderer::load(const QString &filename)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
|
||||||
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
|
// TODO: if `href` attribute is adapted after librsvg upgrade revert me
|
||||||
|
return load(format(file.readAll()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DSvgRenderer::load(const QByteArray &contents)
|
||||||
|
{
|
||||||
|
Q_D(DSvgRenderer);qDebug() << "drsvg load()";
|
||||||
|
|
||||||
|
if (d->handle) {
|
||||||
|
g_object_unref(d->handle);
|
||||||
|
d->handle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GError *error = nullptr;
|
||||||
|
d->handle = rsvg_handle_new_from_data((const guint8*)contents.constData(), contents.length(), &error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
qWarning("DSvgRenderer::load: %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RsvgDimensionData rsvg_data;
|
||||||
|
|
||||||
|
rsvg_handle_get_dimensions(d->handle, &rsvg_data);
|
||||||
|
|
||||||
|
d->defaultSize.setWidth(rsvg_data.width);
|
||||||
|
d->defaultSize.setHeight(rsvg_data.height);
|
||||||
|
d->viewBox = QRectF(QPointF(0, 0), d->defaultSize);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSvgRenderer::render(QPainter *p)
|
||||||
|
{
|
||||||
|
render(p, QString(), QRectF());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSvgRenderer::render(QPainter *p, const QRectF &bounds)
|
||||||
|
{
|
||||||
|
render(p, QString(), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSvgRenderer::render(QPainter *p, const QString &elementId, const QRectF &bounds)
|
||||||
|
{
|
||||||
|
Q_D(DSvgRenderer);
|
||||||
|
if (!d->handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p->save();
|
||||||
|
|
||||||
|
const QImage image = d->getImage(QSize(p->device()->width(), p->device()->height()), elementId);
|
||||||
|
|
||||||
|
if (bounds.isEmpty())
|
||||||
|
p->drawImage(0, 0, image);
|
||||||
|
else
|
||||||
|
p->drawImage(bounds, image);
|
||||||
|
|
||||||
|
p->restore();
|
||||||
|
}
|
||||||
|
|
52
imageformats/svg/dsvgrenderer.h
Normal file
52
imageformats/svg/dsvgrenderer.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef DSVGRENDERER_H
|
||||||
|
#define DSVGRENDERER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QRectF>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QPainter;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class DSvgRendererPrivate;
|
||||||
|
class DSvgRenderer : public QObject
|
||||||
|
{
|
||||||
|
Q_PROPERTY(QRectF viewBox READ viewBoxF WRITE setViewBox)
|
||||||
|
public:
|
||||||
|
explicit DSvgRenderer(QObject *parent = Q_NULLPTR);
|
||||||
|
DSvgRenderer(const QString &filename, QObject *parent = Q_NULLPTR);
|
||||||
|
DSvgRenderer(const QByteArray &contents, QObject *parent = Q_NULLPTR);
|
||||||
|
~DSvgRenderer();
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
QSize defaultSize() const;
|
||||||
|
|
||||||
|
QRect viewBox() const;
|
||||||
|
QRectF viewBoxF() const;
|
||||||
|
void setViewBox(const QRect &viewbox);
|
||||||
|
void setViewBox(const QRectF &viewbox);
|
||||||
|
|
||||||
|
QRectF boundsOnElement(const QString &id) const;
|
||||||
|
bool elementExists(const QString &id) const;
|
||||||
|
|
||||||
|
QImage toImage(const QSize sz, const QString &elementId = QString()) const;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
bool load(const QString &filename);
|
||||||
|
bool load(const QByteArray &contents);
|
||||||
|
void render(QPainter *p);
|
||||||
|
void render(QPainter *p, const QRectF &bounds);
|
||||||
|
|
||||||
|
void render(QPainter *p, const QString &elementId,
|
||||||
|
const QRectF &bounds = QRectF());
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DECLARE_PRIVATE(DSvgRenderer)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DSVGRENDERER_H
|
53
imageformats/svg/main.cpp
Normal file
53
imageformats/svg/main.cpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
#include <qimageiohandler.h>
|
||||||
|
#include <qstringlist.h>
|
||||||
|
|
||||||
|
#include "svg_p.h"
|
||||||
|
|
||||||
|
#include <qiodevice.h>
|
||||||
|
#include <qbytearray.h>
|
||||||
|
#include <qdebug.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QSvgPlugin : public QImageIOPlugin
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "svg.json")
|
||||||
|
|
||||||
|
public:
|
||||||
|
QStringList keys() const;
|
||||||
|
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
|
||||||
|
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
QStringList QSvgPlugin::keys() const
|
||||||
|
{
|
||||||
|
return QStringList() << QLatin1String("svg") << QLatin1String("svgz");
|
||||||
|
}
|
||||||
|
|
||||||
|
QImageIOPlugin::Capabilities QSvgPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||||
|
{
|
||||||
|
if (format == "svg" || format == "svgz")
|
||||||
|
return Capabilities(CanRead);
|
||||||
|
if (!format.isEmpty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Capabilities cap;
|
||||||
|
if (device->isReadable() && QSvgIOHandler::canRead(device))
|
||||||
|
cap |= CanRead;
|
||||||
|
return cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImageIOHandler *QSvgPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||||
|
{
|
||||||
|
QSvgIOHandler *hand = new QSvgIOHandler();
|
||||||
|
hand->setDevice(device);
|
||||||
|
hand->setFormat(format);
|
||||||
|
return hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#include "main.moc"
|
234
imageformats/svg/svg.cpp
Normal file
234
imageformats/svg/svg.cpp
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
#include "svg_p.h"
|
||||||
|
|
||||||
|
#ifndef QT_NO_SVGRENDERER
|
||||||
|
|
||||||
|
#include "qimage.h"
|
||||||
|
#include "qpixmap.h"
|
||||||
|
#include "qpainter.h"
|
||||||
|
#include "qvariant.h"
|
||||||
|
#include "qbuffer.h"
|
||||||
|
#include "qdebug.h"
|
||||||
|
|
||||||
|
#include "dsvgrenderer.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QSvgIOHandlerPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QSvgIOHandlerPrivate(QSvgIOHandler *qq)
|
||||||
|
: q(qq), loaded(false), readDone(false), backColor(Qt::transparent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool load(QIODevice *device);
|
||||||
|
|
||||||
|
QSvgIOHandler *q;
|
||||||
|
DSvgRenderer r;
|
||||||
|
QSize defaultSize;
|
||||||
|
QRect clipRect;
|
||||||
|
QSize scaledSize;
|
||||||
|
QRect scaledClipRect;
|
||||||
|
bool loaded;
|
||||||
|
bool readDone;
|
||||||
|
QColor backColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool QSvgIOHandlerPrivate::load(QIODevice *device)
|
||||||
|
{qDebug() << "load() started";
|
||||||
|
if (!device)
|
||||||
|
return false;
|
||||||
|
qDebug() << "device not null";
|
||||||
|
if (loaded)
|
||||||
|
return true;
|
||||||
|
if (q->format().isEmpty())
|
||||||
|
q->canRead();
|
||||||
|
qDebug() << "can read to set format";
|
||||||
|
// # The SVG renderer doesn't handle trailing, unrelated data, so we must
|
||||||
|
// assume that all available data in the device is to be read.
|
||||||
|
bool res = false;
|
||||||
|
QBuffer *buf = qobject_cast<QBuffer *>(device);
|
||||||
|
if (buf) {
|
||||||
|
const QByteArray &ba = buf->data();
|
||||||
|
res = r.load(QByteArray::fromRawData(ba.constData() + buf->pos(), ba.size() - buf->pos()));
|
||||||
|
buf->seek(ba.size());
|
||||||
|
} else if (q->format() == "svgz") {
|
||||||
|
res = r.load(device->readAll());
|
||||||
|
} else {
|
||||||
|
res = r.load(device->readAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
defaultSize = QSize(r.viewBox().width(), r.viewBox().height());
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QSvgIOHandler::QSvgIOHandler()
|
||||||
|
: d(new QSvgIOHandlerPrivate(this))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QSvgIOHandler::~QSvgIOHandler()
|
||||||
|
{
|
||||||
|
// delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool QSvgIOHandler::canRead() const
|
||||||
|
{
|
||||||
|
if (!device())
|
||||||
|
return false;
|
||||||
|
if (d->loaded && !d->readDone)
|
||||||
|
return true; // Will happen if we have been asked for the size
|
||||||
|
|
||||||
|
QByteArray buf = device()->peek(8);
|
||||||
|
if (buf.startsWith("\x1f\x8b")) {
|
||||||
|
setFormat("svgz");
|
||||||
|
return true;
|
||||||
|
} else if (buf.contains("<?xml") || buf.contains("<svg") || buf.contains("<!--")) {
|
||||||
|
setFormat("svg");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray QSvgIOHandler::name() const
|
||||||
|
{
|
||||||
|
return "svg";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool QSvgIOHandler::read(QImage *image)
|
||||||
|
{qDebug() << "started";
|
||||||
|
if (d->readDone || d->load(device())) {qDebug() << "loaded";
|
||||||
|
bool xform = (d->clipRect.isValid() || d->scaledSize.isValid() || d->scaledClipRect.isValid());
|
||||||
|
QSize finalSize = d->defaultSize;
|
||||||
|
QRectF bounds;
|
||||||
|
if (xform && !d->defaultSize.isEmpty()) {qDebug() << "defaultSize not empty";
|
||||||
|
bounds = QRectF(QPointF(0,0), QSizeF(d->defaultSize));
|
||||||
|
QPoint tr1, tr2;
|
||||||
|
QSizeF sc(1, 1);
|
||||||
|
if (d->clipRect.isValid()) {
|
||||||
|
tr1 = -d->clipRect.topLeft();
|
||||||
|
finalSize = d->clipRect.size();
|
||||||
|
}
|
||||||
|
if (d->scaledSize.isValid()) {
|
||||||
|
sc = QSizeF(qreal(d->scaledSize.width()) / finalSize.width(),
|
||||||
|
qreal(d->scaledSize.height()) / finalSize.height());
|
||||||
|
finalSize = d->scaledSize;
|
||||||
|
}
|
||||||
|
if (d->scaledClipRect.isValid()) {
|
||||||
|
tr2 = -d->scaledClipRect.topLeft();
|
||||||
|
finalSize = d->scaledClipRect.size();
|
||||||
|
}
|
||||||
|
QTransform t;
|
||||||
|
t.translate(tr2.x(), tr2.y());
|
||||||
|
t.scale(sc.width(), sc.height());
|
||||||
|
t.translate(tr1.x(), tr1.y());
|
||||||
|
bounds = t.mapRect(bounds);
|
||||||
|
}
|
||||||
|
if (!finalSize.isEmpty()) {qDebug() << "finalSize not empty";
|
||||||
|
if (bounds.isEmpty() && d->backColor.alpha() == 0) {
|
||||||
|
*image = d->r.toImage(finalSize);
|
||||||
|
} else {
|
||||||
|
*image = QImage(finalSize, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
image->fill(d->backColor.rgba());
|
||||||
|
QPainter p(image);
|
||||||
|
p.setRenderHints(QPainter::SmoothPixmapTransform);
|
||||||
|
d->r.render(&p, bounds);
|
||||||
|
p.end();
|
||||||
|
}
|
||||||
|
}qDebug() << "readDone";
|
||||||
|
d->readDone = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariant QSvgIOHandler::option(ImageOption option) const
|
||||||
|
{
|
||||||
|
switch(option) {
|
||||||
|
case ImageFormat:
|
||||||
|
return QImage::Format_ARGB32_Premultiplied;
|
||||||
|
break;
|
||||||
|
case Size:
|
||||||
|
d->load(device());
|
||||||
|
return d->defaultSize;
|
||||||
|
break;
|
||||||
|
case ClipRect:
|
||||||
|
return d->clipRect;
|
||||||
|
break;
|
||||||
|
case ScaledSize:
|
||||||
|
return d->scaledSize;
|
||||||
|
break;
|
||||||
|
case ScaledClipRect:
|
||||||
|
return d->scaledClipRect;
|
||||||
|
break;
|
||||||
|
case BackgroundColor:
|
||||||
|
return d->backColor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QSvgIOHandler::setOption(ImageOption option, const QVariant & value)
|
||||||
|
{
|
||||||
|
switch(option) {
|
||||||
|
case ClipRect:
|
||||||
|
d->clipRect = value.toRect();
|
||||||
|
break;
|
||||||
|
case ScaledSize:
|
||||||
|
d->scaledSize = value.toSize();
|
||||||
|
break;
|
||||||
|
case ScaledClipRect:
|
||||||
|
d->scaledClipRect = value.toRect();
|
||||||
|
break;
|
||||||
|
case BackgroundColor:
|
||||||
|
d->backColor = value.value<QColor>();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool QSvgIOHandler::supportsOption(ImageOption option) const
|
||||||
|
{
|
||||||
|
switch(option)
|
||||||
|
{
|
||||||
|
case ImageFormat:
|
||||||
|
case Size:
|
||||||
|
case ClipRect:
|
||||||
|
case ScaledSize:
|
||||||
|
case ScaledClipRect:
|
||||||
|
case BackgroundColor:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool QSvgIOHandler::canRead(QIODevice *device)
|
||||||
|
{
|
||||||
|
QByteArray buf = device->peek(8);
|
||||||
|
return buf.startsWith("\x1f\x8b") || buf.contains("<?xml") || buf.contains("<svg") || buf.contains("<!--");
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QT_NO_SVGRENDERER
|
4
imageformats/svg/svg.json
Normal file
4
imageformats/svg/svg.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Keys": [ "svg", "svgz" ],
|
||||||
|
"MimeTypes": [ "image/svg+xml" ]
|
||||||
|
}
|
35
imageformats/svg/svg_p.h
Normal file
35
imageformats/svg/svg_p.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
#ifndef QSVGIOHANDLER_H
|
||||||
|
#define QSVGIOHANDLER_H
|
||||||
|
|
||||||
|
#include <QImageIOHandler>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QImage;
|
||||||
|
class QByteArray;
|
||||||
|
class QIODevice;
|
||||||
|
class QVariant;
|
||||||
|
|
||||||
|
class QSvgIOHandlerPrivate;
|
||||||
|
class QSvgIOHandler : public QImageIOHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QSvgIOHandler();
|
||||||
|
~QSvgIOHandler();
|
||||||
|
virtual bool canRead() const;
|
||||||
|
virtual QByteArray name() const;
|
||||||
|
virtual bool read(QImage *image);
|
||||||
|
static bool canRead(QIODevice *device);
|
||||||
|
virtual QVariant option(ImageOption option) const;
|
||||||
|
virtual void setOption(ImageOption option, const QVariant & value);
|
||||||
|
virtual bool supportsOption(ImageOption option) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSvgIOHandlerPrivate *d;
|
||||||
|
};
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QSVGIOHANDLER_H
|
Loading…
Reference in New Issue
Block a user