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. react 趟坑

    最近一直在做react项目,发现一个bug,困扰了我两天. Can't perform a React state update on an unmounted component. This is ...

  2. 一个WPF下的虚拟键盘实现

    给上位机触摸屏做一个虚拟键盘,这玩意儿不就是一排的网格里面放满button嘛 .说归这样说 依然还是有一堆细节需要你去处理的.不论如何 先画个键盘吧. 简单的从网上找个键盘位图做参照使用 4行Grid ...

  3. Ceph的crush算法与一致性hash对比介绍

    本文分享自天翼云开发者社区<Ceph的crush算法与一致性hash对比介绍>,作者:l****n 首先,我们先回顾下一致性hash以及其在经典存储系统中的应用. 一致性hash的基本原理 ...

  4. sql中的inerval函数使用方法

    在SQL中,INTERVAL函数可以用于添加或减去特定的时间间隔.其基本语法如下: SELECT field1, field2, ... FROM table_name WHERE condition ...

  5. jenkins+gitee+tomcat

    1.Jenkins [系统配置]添加gitee服务 2.项目配置 General 配置之前配置的gitee服务连接 3.源码配置 4.构建配置 5.构建触发器配置 最重要的是: 6.在gitee中配置 ...

  6. C#/.NET/.NET Core技术前沿周刊 | 第 25 期(2025年2.1-2.9)

    前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...

  7. ABB机器人伺服电机维修有脉冲输出时不工作怎么处理

    1.ABB机器人伺服电机维修有脉冲输出时不工作怎么处理? 监督操控器的脉冲输出当时值以及脉冲输出灯是否闪烁,承认指令脉冲现已履行并现已正常输出脉冲:查看操控器到驱动器的操控电缆,动力电缆,编码器电缆是 ...

  8. VMware与宿主机文件夹共享的方法

    首先,在打开的虚拟机的主界面中点选我的电脑上的虚拟机系统,再点击右侧的编辑虚拟机设置. 然后,在弹出的虚拟机设置中点击"选项"标签栏目 点选在"选项"标签栏目中 ...

  9. startup_stm32f10x_xx.s 启动代码文件的选择

    网上查到的各个文件的解释是: startup_stm32f10x_cl.s 互联型的器件startup_stm32f10x_hd.s 大容量startup_stm32f10x_hd_vl.s 大容量s ...

  10. QT5笔记:6. QT 与 C++

    QT 对标准的C++进行了扩展,引入了一些新的概念和功能 QT 的元对象编译器(Meta-Object Compiler, MOC)是一个预处理器,它预处理QT项目,先将QT的一些特性代码转换为标准的 ...