使用Qt QPainter和QSvgGenerator创建的SVG裁剪为视口大小

时间:2019-07-16 10:21:05

标签: c++ qt svg qt5 qpainter

对于一个项目,我使用QPainter和QSvgGenerator来创建SVG作为输出。该项目基本上从C ++代码生成基本的类图。但是,当我使用Google Chrome或任何其他网络浏览器打开SVG时,它会根据浏览器窗口的大小进行裁剪,而没有任何滚动条。在调整窗口大小时,会发生进一步的裁剪。

相关代码

QSvgGenerator temp_img; 
//Save file as image
QString path = QFileDialog::getSaveFileName(w, ("Save as image"), "", 
("SVG file (*.svg)"));

if (path.isEmpty())
    return;

temp_img.setFileName(path);

QPainter painter;
painter.begin(&temp_img);
painter.setFont(QFont("Arial",12));
.
.
.
painter.end();

我尝试使用setViewBox()但没有效果。 我是第一次使用Qt,因此请尽量详细。由于质量问题,我更喜欢SVG而不是位图。

编辑:缩小将显示隐藏的部分。

2 个答案:

答案 0 :(得分:1)

由于OP没有提供MCVE,所以我自己准备了一个:

#include <QtWidgets>
#include <QtSvg/QSvgGenerator>

const int w = 100, h = 100;

void renderTest(QPainter &qPainter, double s)
{
  qPainter.setTransform(QTransform().scale(s, s));
  qPainter.setFont(QFont("Arial", 12));
  qPainter.setPen(Qt::gray);
  qPainter.drawRect(0, 0, w, h);
  qPainter.setPen(Qt::black);
  qPainter.drawLine(0.1 * w, 0.5 * h, 0.9 * w, 0.5 * h);
  qPainter.drawLine(0.5 * w, 0.1 * h, 0.5 * w, 0.9 * h);
  qPainter.drawLine(0.45 * w, 0.2 * h, 0.55 * w, 0.2 * h);
  qPainter.drawLine(0.45 * w, 0.8 * h, 0.55 * w, 0.8 * h);
  qPainter.drawLine(0.2 * w, 0.45 * h, 0.2 * w, 0.55 * h);
  qPainter.drawLine(0.8 * w, 0.45 * h, 0.8 * w, 0.55 * h);
  qPainter.drawText(QPointF(0.51 * w, 0.49 * h), "0");
  qPainter.drawText(QPointF(0.51 * w, 0.79 * h), "-1");
  qPainter.drawText(QPointF(0.51 * w, 0.19 * h), "+1");
  qPainter.drawText(QPointF(0.21 * w, 0.49 * h), "-1");
  qPainter.drawText(QPointF(0.81 * w, 0.49 * h), "+1");
  qPainter.setPen(Qt::blue);
  qPainter.drawEllipse(QPointF(0.5 * w, 0.5 * h), 0.3 * w, 0.3 * h);
}

void renderSvgFile(const QString &qFilePath, double s)
{
  QSvgGenerator qSvgGen;
  qSvgGen.setFileName(qFilePath);
  qSvgGen.setSize(QSize(s * w, s * h));
  renderTest(QPainter(&qSvgGen), s);
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // render tests
  for (int i = 1; i <= 100; i *= 10) {
    const QString qFilePath = QString("testQSvgGen.%1%2.svg").arg(i).arg("0%");
    qDebug() << "Render" << qFilePath;
    renderSvgFile(qFilePath, i * 0.1);
  }
  // done
  return 0;
}

它生成三个文件:

  • testQSvgGen.10%.svg
  • testQSvgGen.100%.svg
  • testQSvgGen.1000%.svg

Preview in Windows Explorer (with SVG-Preview plug-in)

尽管以不同的尺寸写入图像,但预览中没有显着差异。原因是预览会将结果缩放到其自己所需的分辨率,以使输出适合当前的Explorer图标大小。 (同样适用于正确尺寸的预览尺寸。)

View in Google Chrome Browser

与此相反,Web浏览器(上图中的Google Chrome)考虑了SVG的大小设置。

这些设置是

  • testQSvgGen.10%.svg中:
<svg width="3.52778mm" height="3.52778mm"
 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
 version="1.2" baseProfile="tiny">
  • testQSvgGen.100%.svg
<svg width="35.2778mm" height="35.2778mm"
 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
 version="1.2" baseProfile="tiny">
  • testQSvgGen.100%.svg
<svg width="352.778mm" height="352.778mm"
 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
 version="1.2" baseProfile="tiny">

仔细查看生成的SVG代码,发现这3个文件通常看起来非常相似。我强加的缩放比例

  qPainter.setTransform(QTransform().scale(s, s));

将图形输出调整为预期的图像大小,只需将其转换为transform="matrix()"属性,并按比例缩放每个组(<g>)。


所以,我无法确定OP抱怨什么:

在生成的SVG文件中考虑在QSvgGenerator::setSize()中设置的大小,并且浏览器会遵守此设置(符合预期)。


生成的testQSvgGen.10%.svg的源代码:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="352.778mm" height="352.778mm"
 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  version="1.2" baseProfile="tiny">
<title>Qt SVG Document</title>
<desc>Generated with Qt</desc>
<defs>
</defs>
<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >

<g fill="none" stroke="#a0a0a4" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<rect x="0" y="0" width="100" height="100"/>
</g>

<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<polyline fill="none" vector-effect="none" points="10,50 90,50 " />
<polyline fill="none" vector-effect="none" points="50,10 50,90 " />
<polyline fill="none" vector-effect="none" points="45,20 55,20 " />
<polyline fill="none" vector-effect="none" points="45,80 55,80 " />
<polyline fill="none" vector-effect="none" points="20,45 20,55 " />
<polyline fill="none" vector-effect="none" points="80,45 80,55 " />
</g>

<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="51" y="49" font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
 >0</text>
</g>

<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="51" y="79" font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
 >-1</text>
</g>

<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="51" y="19" font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
 >+1</text>
</g>

<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="21" y="49" font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
 >-1</text>
</g>

<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="81" y="49" font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
 >+1</text>
</g>

<g fill="none" stroke="#0000ff" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(10,0,0,10,0,0)"
font-family="Arial" font-size="12" font-weight="400" font-style="normal" 
>
<circle cx="50" cy="50" r="30"/>
</g>
</g>
</svg>

答案 1 :(得分:0)

大小或视图框的规范对于独立的SVG至关重要。

这在W3C的SVG规范中进行了描述:

  

7.2 The initial viewport

     

SVG用户代理与其父用户代理进行协商,以确定SVG用户代理可以将文档呈现到的视口。在某些情况下,SVG内容将被嵌入(通过引用或内联)在包含文档中。该包含文档可能包括属性,属性和/或其他参数(显式或隐式),这些属性指定或提供有关SVG内容的视口尺寸的提示。 SVG内容本身可以选择通过“ svg”元素上的“ width”和“ height” XML属性提供有关内容的适当视口区域的信息。协商过程使用包含文档提供的任何信息以及SVG内容本身来选择视口位置和大小。

     

如果父文档格式为参考或嵌入式图形内容定义了规则,则协商过程由父文档格式规范确定。如果父文档使用CSS设置样式,则协商过程必须遵循CSS规则来替换元素。如果参考元素(或内联SVG内容的最根“ svg”元素)上的CSS宽度和高度属性(或相应的XSL属性)足以确定视口的宽度和高度,则这些定位属性将建立视口的宽度,高度和长宽比。

     

如果没有父文档,则SVG用户代理必须使用最根“ svg”元素元素上的“ width”和“ height”属性作为视口的宽度和高度。

     

请注意,完成视口大小协商的时间是特定于实现的。需要确定视口尺寸的作者应该使用load-event或resize-event处理程序来实现。

我举了一些例子来说明这一点。

不含widthheightviewBoxtest.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> <title>SVG Test</title> <desc>A test to illustrate the role of width and height</desc> <rect x="0" y="0" width="100" height="100" style="stroke-width:1;stroke:#888;fill:none"/> <circle cx="50" cy="50" r="30" style="stroke-width:1;stroke:#000;fill:none"/> </svg> 的测试文件:

width

在Google Chrome浏览器中输出:

[![Google Chrome中的test.svg快照] [1]] [1]

[![Google Chrome浏览器中test.svg的快照(调整大小后)] [2]] [2]

Firefox的输出:

[![Firefox中test.svg的快照] [3]] [3]

尽管浏览器似乎可以正确呈现,但图形尺寸却不正确。 (尽管SVG内容不适合显示,但没有垂直滚动条显示。)

提供查看端口(heighttest.100x100.svg)– <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> <title>SVG Test</title> <desc>A test to illustrate the role of width and height</desc> <rect x="0" y="0" width="100" height="100" style="stroke-width:1;stroke:#888;fill:none"/> <circle cx="50" cy="50" r="30" style="stroke-width:1;stroke:#000;fill:none"/> </svg>

width

在Google Chrome浏览器中输出:

[![Google Chrome浏览器中test.100x100.svg的快照] [4]] [4]

[![Google Chrome浏览器中test.100x100.svg的快照(调整大小后)] [5]] [5]

同一文件,其中heighttest.50x50.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="50" height="50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> <title>SVG Test</title> <desc>A test to illustrate the role of width and height</desc> <rect x="0" y="0" width="100" height="100" style="stroke-width:1;stroke:#888;fill:none"/> <circle cx="50" cy="50" r="30" style="stroke-width:1;stroke:#000;fill:none"/> </svg> 减少了:

width

在Google Chrome浏览器中输出:

[![Google Chrome中test.50x50.svg的快照] [6]] [6]

  1. 减小了初始视图的大小。
  2. 图形输出被裁剪为heightviewBox

接下来,我添加了一个test.50x50.view100x100.svg属性– <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="50" height="50" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> <title>SVG Test</title> <desc>A test to illustrate the role of width and height</desc> <rect x="0" y="0" width="100" height="100" style="stroke-width:1;stroke:#888;fill:none"/> <circle cx="50" cy="50" r="30" style="stroke-width:1;stroke:#000;fill:none"/> </svg>

width

在Google Chrome浏览器中输出:

[![Google Chrome浏览器中test.50x50.view100x100.svg的快照] [7]] [7]

视图仍会缩小,但图形内容会缩放以适合视图。

缺少heighttest.view100x100.svg会怎样? – <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> <title>SVG Test</title> <desc>A test to illustrate the role of width and height</desc> <rect x="0" y="0" width="100" height="100" style="stroke-width:1;stroke:#888;fill:none"/> <circle cx="50" cy="50" r="30" style="stroke-width:1;stroke:#000;fill:none"/> </svg>

viewBox

在Google Chrome浏览器中输出:

[![Google Chrome中的test.view100x100.svg快照] [8]] [8]

[![Google Chrome浏览器中test.view100x100.svg的快照(调整大小后)] [9]] [9]

按比例缩放输出以适合浏览器视图。

可以将其编译为以下经验法则:

  1. width为图形内容定义了类似于剪辑空间的内容。

  2. heightviewBox提示了渲染器应使用的输出大小。

  3. 如果缺少viewBox="0 0 width height",则将其设置为width

  4. 如果缺少heightwidth,则渲染器可以自由选择。

  5. 至少应提供heightviewBoxwidth


QSvgGenerator提供以下属性来控制heightviewBoxQSize(-1, -1)

  

size : QSize

     

此属性保存生成的SVG图形的大小

     

默认情况下,此属性设置为QRect(0, 0, -1, -1),指示生成器不应输出元素的width和height属性。

     

注意:在生成器上激活QPainter时,无法更改此属性。

     

viewBox : QRectF

     

此属性保存生成的SVG绘图的viewBox

     

默认情况下,此属性设置为{{1}},指示生成器不应输出元素的viewBox属性。

     

注意:在生成器上激活QPainter时,无法更改此属性。