SVG 作为为可缩放矢量图形(Scalable Vector Graphics),易于编辑和维护,基于XML的文本文件存储,在网页设计、图标制作、数据可视化和其他图形相关的领域应用广泛。在应用工程中总会有动态修改路径、绘制颜色等需求,这就需要能够动态的解析 svg 文件,获取对应的路径、颜色等参数。有许多解析 svg 文件的类库,例如:svgHelper。svgHelper 通过 QDomDocument 对 svg 文件进行了解析并提取出了路径和颜色信息,虽然该方法可行,但是具体的解析过程还是相当繁琐,稍有不慎就会出现错误。另外,渐变色、画刷类型、字体、是否显示等属性该库就无法表示。

QT 提供了svg 库用于svg 的解析,但是大部分代码被封装起来,只提供了 QSvgRenderer 类和 QSvgGenerator 类给用户使用。而 QSvgRenderer 类能使用的只有 render() 方法。从 svg 库的文件目录可以看出来,svg 图像对应的节点、样式、字体等信息都有对应的描述类。

QSvgRenderer 加载 svg 文件后,由 QSvgTinyDocument 类负责解析文件。QSvgTinyDocument 通过 QSvgHandler 执行具体的解析工作,QSvgTinyDocument 存储了解析后的 SVG 结构、样式等信息。

QSvgTinyDocument 提供了 size()、 width() 、height() 、viewBox()、QSvgNode *namedNode(const QString &id) const; QSvgPaintStyleProperty *namedStyle(const QString &id) const;等方法,可以获取 SVG 解析后的相关信息。但是这些方法都在私有头文件中隐藏了起来。

如果要使用 QT 的 svg 库只有通过 render() 方法,该方法的具体执行由 QSvgTinyDocument 类的 draw() 方法负责。

QSvgTinyDocument 类的 draw() 方法调用 QSvgNode 类的 draw() 方法绘制具体的 node。qsvggraphics_p.h 定义了 SVG 支持的各种图形、路径、动画等的描述类,这些类都继承自 QSvgNode 并实现了虚函数 drawCommand(QPainter *p, QSvgExtraStates &states) 定义了对应图像如何在 QPainter 上绘制。

void QSvgTinyDocument::draw(QPainter *p, const QString &id,
const QRectF &bounds)
{
QSvgNode *node = scopeNode(id); if (!node) {
qCDebug(lcSvgHandler, "Couldn't find node %s. Skipping rendering.", qPrintable(id));
return;
}
if (m_time == 0)
m_time = QDateTime::currentMSecsSinceEpoch(); if (node->displayMode() == QSvgNode::NoneMode)
return; p->save(); const QRectF elementBounds = node->transformedBounds(); mapSourceToTarget(p, bounds, elementBounds);
QTransform originalTransform = p->worldTransform(); //XXX set default style on the painter
QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
pen.setMiterLimit(4);
p->setPen(pen);
p->setBrush(Qt::black);
p->setRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::SmoothPixmapTransform); QStack<QSvgNode*> parentApplyStack;
QSvgNode *parent = node->parent();
while (parent) {
parentApplyStack.push(parent);
parent = parent->parent();
} for (int i = parentApplyStack.size() - 1; i >= 0; --i)
parentApplyStack[i]->applyStyle(p, m_states); // Reset the world transform so that our parents don't affect
// the position
QTransform currentTransform = p->worldTransform();
p->setWorldTransform(originalTransform); node->draw(p, m_states); p->setWorldTransform(currentTransform); for (int i = 0; i < parentApplyStack.size(); ++i)
parentApplyStack[i]->revertStyle(p, m_states); //p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100)); p->restore();
}

drawCommand 方法通过 QPainter 提供的绘制函数进行图形绘制。QPainter 的绘制动作由 QPaintDevice 通过 QPaintEngine 实现。

如果实现一个 SVG 的绘制引擎,那么所有的绘制动作都可以被该引擎截获并重新解释。GitHub项目 Compelling Data Designer 通过继承 QPaintEngine 实现了 SVG 的绘制引擎,通过 QSvgRenderer 的 render()方法重新获取了 QPainterPath 及其填充、线条等属性。

// svgpathdevice.h
class SvgEngine : public QPaintEngine
{
public:
SvgEngine();
QList<PainterPathEx> getSvgPath();
// QPaintEngine interface
bool begin(QPaintDevice *pdev) override;
bool end() override;
void updateState(const QPaintEngineState &state) override;
void drawPath(const QPainterPath &path) override;
void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override;
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
void drawTextItem(const QPointF &p, const QTextItem &textItem) override;
QPaintEngine::Type type() const override;
private:
QList<PainterPathEx> pathList; }; class SvgPathDevice : public QPaintDevice
{
public: SvgPathDevice(int w, int h);
SvgPathDevice(const QSize &size);
~SvgPathDevice();
QList<PainterPathEx> getSvgPath(); qreal devicePixelRatio() const;
void setDevicePixelRatio(qreal scaleFactor);
// QPaintDevice interface
QPaintEngine *paintEngine() const override;
int devType() const override; protected:
int metric(PaintDeviceMetric metric) const override;
private:
SvgEngine *engine;
qreal pixelRatio{1};
int width;
int height;
int qt_defaultDpiX() const;
int qt_defaultDpiY() const;
}; // endtypefactory.cpp
QList<PainterPathEx> EndTypeFactory::extractPath(QSvgRenderer *render, QString id)
{
if (!render->elementExists(id)) {
return QList<PainterPathEx>();
}
render->setAspectRatioMode(Qt::KeepAspectRatio);
auto size = render->defaultSize();
SvgPathDevice svgPath(size);
QPainter p(&svgPath);
render->render(&p, id, QRectF{0, 0, size.width()*1.0, size.height()*1.0});
p.end();
return svgPath.getSvgPath();
}

该项目中线条的终端形状全部使用 SVG 文件进行定义,使用 EndTypeFactory 类作为终端形状的工厂类管理所有 svg 图形。EndTypeFactory 类加载 svg 图像时,通过 SvgPathDevice 类获取了图像的 PainterPath,保证了后续绘制过程中使用矢量图进行绘制。具体代码可以查看 plugins/lineplugin 目录下的 svgpathdevice 和 endtypefactory 类文件。

继承 QPaintEngine 利用 QSvgRenderer 从SVG 图片中提取路径(QPainterPath)的方法的更多相关文章

  1. 利用sfntly的sfnttool.jar提取中文字体

    雨忆博客中提到了sfntly(具体介绍可以看:https://code.google.com/p/sfntly/),利用其中sfnttool.jar就可以提取只包含指定字符的字体,如果想在页面中通过@ ...

  2. 利用Readability解决网页正文提取问题

    分享: 利用Readability解决网页正文提取问题   做数据抓取和分析的各位亲们, 有没有遇到下面的难题呢? - 如何从各式各样的网页中提取正文!? 虽然可以用SS为各种网站写脚本做解析, 但是 ...

  3. 利用ArcGIS水文分析工具提取河网

    转自原文 利用ArcGIS水文分析工具提取河网(转) DEM包含有多种信息,ArcToolBox提供了利用DEM提取河网的方法,但是操作比较烦琐(帮助可参看Hydrologic analysis sa ...

  4. 利用matlab自带函数快速提取二值图像的图像边缘 bwperim函数

      clear all;close all;clc; I = imread('rice.png'); I = im2bw(I); J = bwperim(I); % 提取二值图像图像边缘 figure ...

  5. HelloServlet类继承HttpServlet利用HttpServletResponse对象

    HelloServlet类继承HttpServlet利用HttpServletResponse对象 HelloServlet类的doGet()方法先得到username请求参数,对其进行中文字符编码转 ...

  6. 等效介质理论模型---利用S参数反演法提取超材料结构的等效参数

    等效介质理论模型---利用S参数反演法提取超材料结构的等效参数 S参数反演法,即利用等效模型的传输矩阵和S参数求解超材料结构的等效折射率n和等效阻抗Z的过程.本文对等效介质理论模型进行了详细介绍,并提 ...

  7. Java -- JDBC_利用反射及 JDBC 元数据编写通用的查询方法

    先利用 SQL 进行查询,得到结果集: 利用反射创建实体类的对象:创建对象: 获取结果集的列的别名: 再获取结果集的每一列的值, 结合 3 得到一个 Map,键:列的别名,值:列的值: 再利用反射为 ...

  8. Jmeter正则表达式提取器的使用方法(转)

    下面简单介绍一下Jmeter正则表达式提取器的使用方法. 1.添加Jmeter正则表达式提取器:在具体的Request下添加Jmeter正则表达式提取器(Jmeter正则表达式在“后置处理器”下面)  ...

  9. JDBC学习笔记(5)——利用反射及JDBC元数据编写通用的查询方法

    JDBC元数据 1)DatabaseMetaData /** * 了解即可:DatabaseMetaData是描述数据库的元数据对象 * 可以由Connection得到 */ 具体的应用代码: @Te ...

  10. javascript 正则匹配 提取所有 preg_match_all matchAll方法

    javascript 提取全部的的方法.javascript中没有matchAll这种方法. 用while来实现类似 PHP 中的preg_match_all() :(by default7#zbph ...

随机推荐

  1. Linux系统用户登录命令行或执行命令显示日志文件异常-bash: /var/log/ 解决办法

    经常会遇到Linux系统用户登录命令行或执行命令显示日志文件异常,比如:-bash: /var/log/xxx_audit/xxx_audit.log: Permission denied 其实是说开 ...

  2. A note on the calculation of some functions in finite fields: Tricks of the Trade解读

    本节对该paper进行解读,记录笔记. 经常见到的是在素域\(F_p\)上计算的,尤其是双线性对出现后,在扩域\(F_{p^m}\)上计效率就需要优化了.该论文主要总结了一些在有限域上进行某些计算(求 ...

  3. HPC的基本概念

    本文分享自天翼云开发者社区<HPC的基本概念>,作者:它零食抱佛jio HPC(High Performance Computing)是一种高性能计算技术,它利用大规模并行处理器集群或超级 ...

  4. .NET周刊【2月第1期 2025-02-02】

    国内文章 dotnet 9 已知问题 默认开启 CET 导致进程崩溃 https://www.cnblogs.com/lindexi/p/18700406 本文记录 dotnet 9 的一个已知且当前 ...

  5. 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-13- iframe操作-中篇(详细教程)

    1.简介 按照计划今天就要用实际的例子进行iframe自动化测试.宏哥还是用之前找到的一个含有iframe的网页(QQ邮箱和163邮箱),别的邮箱宏哥就没有细看了,可能后期这两个邮箱页面优化升级,也就 ...

  6. SYSTEM表空间使用率高

    数据库监控告警,SYSTEM表空间使用率很高.检查发现LOB字段使用了很大的空间,定位到LOB字段属于SYS_EXPORT_FULL_%表,参考(Doc ID 1626201.1)步骤处理后,表空间使 ...

  7. V-Control:一个基于 .NET MAUI 的开箱即用的UI组件库

    前言 今天大姚给大家分享一个基于 .NET MAUI 的开源免费.开箱即用的UI组件库:V-Control. V-Control项目介绍 V-Control 是一个适用于 .NET MAUI 的开源( ...

  8. [luogu1248] 加工生产调度 题解

    考虑 \(i\) 排在 \(j\) 前的条件是 \(a_i+\max(a_j,b_i)+b_j\le a_j+\max(a_i,b_j)+b_i\),然后发现这一坨东西是皇后游戏中的倒数第三个式子,直 ...

  9. [JOI 2020 Final] 火事 题解

    给一篇题解.(下面这张图是从 luogu 上粘贴的,因为不太会画图) 其中纵坐标为 \(t\),横坐标为 \(a_i\). 发现同颜色块只有平行四边形和直角梯形(等腰直角三角形)两种情况. 可以将直角 ...

  10. nacos(六): 创建第一个多模块微服务项目(生产者与消费者)

    要创建出生产者与消费者一体的多模块项目,总体来说分成三个步骤.分别是先创建出项目主体,再分别创建出生产者与消费者. 步骤一:创建项目主体 1. 先创建一个单体项目作为项目的主题.在这里,我们不用脚手架 ...