dsvg imageformats plugin sans dtk dependency
This commit is contained in:
		@ -4,11 +4,16 @@ cmake_minimum_required (VERSION 3.9.5)
 | 
			
		||||
 | 
			
		||||
include(GNUInstallDirs)
 | 
			
		||||
 | 
			
		||||
option(BUILD_EXPERIMENTAL_PLUGINS "Enable the build of imageformats plugin(s) that might not working" ON)
 | 
			
		||||
 | 
			
		||||
set(CMAKE_AUTOMOC ON)
 | 
			
		||||
set(CMAKE_AUTORCC ON)
 | 
			
		||||
set(QT_MINIMUM_VERSION "5.10")
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,4 @@
 | 
			
		||||
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)
 | 
			
		||||
if(BUILD_EXPERIMENTAL_PLUGINS)
 | 
			
		||||
    add_subdirectory(sai)
 | 
			
		||||
endif()
 | 
			
		||||
add_subdirectory(svg)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
		Reference in New Issue
	
	Block a user