summaryrefslogtreecommitdiff
path: root/capi/qt-wrapper/ResvgQt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'capi/qt-wrapper/ResvgQt.cpp')
-rw-r--r--capi/qt-wrapper/ResvgQt.cpp287
1 files changed, 287 insertions, 0 deletions
diff --git a/capi/qt-wrapper/ResvgQt.cpp b/capi/qt-wrapper/ResvgQt.cpp
new file mode 100644
index 0000000..e9a1a62
--- /dev/null
+++ b/capi/qt-wrapper/ResvgQt.cpp
@@ -0,0 +1,287 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <ResvgQt.h>
+
+extern "C" {
+#define RESVG_QT_BACKEND
+#include <resvg.h>
+}
+
+#include <QGuiApplication>
+#include <QScreen>
+#include <QPainter>
+#include <QFile>
+#include <QDebug>
+
+
+class ResvgRendererPrivate
+{
+public:
+ ~ResvgRendererPrivate()
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ if (tree) {
+ resvg_tree_destroy(tree);
+ tree = nullptr;
+ }
+
+ if (opt.path) {
+ delete[] opt.path; // do not use free() because was allocated via qstrdup()
+ opt.path = NULL;
+ }
+
+ viewBox = QRectF();
+ errMsg = QString();
+ }
+
+ resvg_render_tree *tree = nullptr;
+ resvg_options opt;
+ QRectF viewBox;
+ QString errMsg;
+};
+
+
+static void initOptions(resvg_options &opt)
+{
+ resvg_init_options(&opt);
+
+ const auto screens = qApp->screens();
+ if (!screens.isEmpty()) {
+ const auto screen = screens.at(0);
+ opt.dpi = screen->logicalDotsPerInch() * screen->devicePixelRatio();
+ }
+}
+
+static QString errorToString(const int err)
+{
+ switch (err) {
+ case RESVG_OK :
+ return QString(); break;
+ case RESVG_ERROR_NOT_AN_UTF8_STR :
+ return QLatin1Literal("The SVG content has not an UTF-8 encoding."); break;
+ case RESVG_ERROR_FILE_OPEN_FAILED :
+ return QLatin1Literal("Failed to open the file."); break;
+ case RESVG_ERROR_FILE_WRITE_FAILED :
+ return QLatin1Literal("Failed to write to the file."); break;
+ case RESVG_ERROR_INVALID_FILE_SUFFIX :
+ return QLatin1Literal("Invalid file suffix."); break;
+ case RESVG_ERROR_MALFORMED_GZIP :
+ return QLatin1Literal("Not a GZip compressed data."); break;
+ case RESVG_ERROR_NO_CANVAS :
+ return QLatin1Literal("Failed to allocate the canvas."); break;
+ }
+
+ Q_UNREACHABLE();
+}
+
+ResvgRenderer::ResvgRenderer()
+ : d(new ResvgRendererPrivate())
+{
+}
+
+ResvgRenderer::ResvgRenderer(const QString &filePath)
+ : d(new ResvgRendererPrivate())
+{
+ load(filePath);
+}
+
+ResvgRenderer::ResvgRenderer(const QByteArray &data)
+ : d(new ResvgRendererPrivate())
+{
+ load(data);
+}
+
+ResvgRenderer::~ResvgRenderer() {}
+
+bool ResvgRenderer::load(const QString &filePath)
+{
+ // Check for Qt resource path.
+ if (filePath.startsWith(":/")) {
+ QFile file(filePath);
+ if (file.open(QFile::ReadOnly)) {
+ return load(file.readAll());
+ } else {
+ return false;
+ }
+ }
+
+ d->reset();
+
+ resvg_options opt;
+ initOptions(opt);
+
+ const auto utf8Str = filePath.toUtf8();
+ const auto rawFilePath = utf8Str.constData();
+ opt.path = qstrdup(rawFilePath);
+
+ resvg_render_tree *tree = NULL;
+ const auto err = resvg_parse_tree_from_file(opt.path, &opt, &tree);
+ if (err != RESVG_OK) {
+ d->errMsg = errorToString(err);
+ return false;
+ }
+
+ d->tree = tree;
+ d->opt = opt;
+
+ const auto r = resvg_get_image_viewbox(d->tree);
+ d->viewBox = QRectF(r.x, r.y, r.width, r.height);
+
+ return true;
+}
+
+bool ResvgRenderer::load(const QByteArray &data)
+{
+ d->reset();
+
+ resvg_options opt;
+ initOptions(opt);
+
+ resvg_render_tree *tree = NULL;
+ resvg_parse_tree_from_data(data.constData(), data.size(), &opt, &tree);
+
+ d->tree = tree;
+ d->opt = opt;
+
+ const auto r = resvg_get_image_viewbox(d->tree);
+ d->viewBox = QRectF(r.x, r.y, r.width, r.height);
+
+ return true;
+}
+
+bool ResvgRenderer::isValid() const
+{
+ return d->tree;
+}
+
+QString ResvgRenderer::errorString() const
+{
+ return d->errMsg;
+}
+
+bool ResvgRenderer::isEmpty() const
+{
+ if (d->tree)
+ return !resvg_is_image_empty(d->tree);
+ else
+ return true;
+}
+
+QSize ResvgRenderer::defaultSize() const
+{
+ return defaultSizeF().toSize();
+}
+
+QSizeF ResvgRenderer::defaultSizeF() const
+{
+ if (d->tree)
+ return d->viewBox.size();
+ else
+ return QSizeF();
+}
+
+QRect ResvgRenderer::viewBox() const
+{
+ return viewBoxF().toRect();
+}
+
+QRectF ResvgRenderer::viewBoxF() const
+{
+ if (d->tree)
+ return d->viewBox;
+ else
+ return QRectF();
+}
+
+bool ResvgRenderer::elementExists(const QString &id) const
+{
+ if (d->tree) {
+ const auto utf8Str = id.toUtf8();
+ const auto rawId = utf8Str.constData();
+ return resvg_node_exists(d->tree, rawId);
+ }
+
+ return false;
+}
+
+QTransform ResvgRenderer::transformForElement(const QString &id) const
+{
+ if (d->tree) {
+ const auto utf8Str = id.toUtf8();
+ const auto rawId = utf8Str.constData();
+ resvg_transform ts;
+ if (resvg_get_node_transform(d->tree, rawId, &ts)) {
+ return QTransform(ts.a, ts.b, ts.c, ts.d, ts.e, ts.f);
+ }
+ }
+
+ return QTransform();
+}
+
+void ResvgRenderer::render(QPainter *p)
+{
+ render(p, QRectF());
+}
+
+void ResvgRenderer::render(QPainter *p, const QRectF &bounds)
+{
+ if (!d->tree)
+ return;
+
+ const auto r = bounds.isValid() ? bounds : p->viewport();
+
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+
+ const double sx = (double)r.width() / d->viewBox.width();
+ const double sy = (double)r.height() / d->viewBox.height();
+
+ p->setTransform(QTransform(sx, 0, 0, sy, r.x(), r.y()), true);
+
+ resvg_size imgSize { (uint)d->viewBox.width(), (uint)d->viewBox.height() };
+ resvg_qt_render_to_canvas(d->tree, &d->opt, imgSize, p);
+
+ p->restore();
+}
+
+void ResvgRenderer::render(QPainter *p, const QString &elementId, const QRectF &bounds)
+{
+ if (!d->tree)
+ return;
+
+ const auto utf8Str = elementId.toUtf8();
+ const auto rawId = utf8Str.constData();
+
+ resvg_rect bbox;
+ if (!resvg_qt_get_node_bbox(d->tree, &d->opt, rawId, &bbox)) {
+ qWarning() << QString("Element '%1' has no bounding box.").arg(elementId);
+ return;
+ }
+
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+
+ const auto r = bounds.isValid() ? bounds : p->viewport();
+
+ const double sx = (double)r.width() / bbox.width;
+ const double sy = (double)r.height() / bbox.height;
+ p->setTransform(QTransform(sx, 0, 0, sy, bounds.x(), bounds.y()), true);
+
+ resvg_size imgSize { (uint)bbox.width, (uint)bbox.height };
+ resvg_qt_render_to_canvas_by_id(d->tree, &d->opt, imgSize, rawId, p);
+
+ p->restore();
+}
+
+void ResvgRenderer::initLog()
+{
+ resvg_init_log();
+}