Reputation: 37
For a project, I am using QPainter with QSvgGenerator to create SVG as an output. The project basically produces basic class diagrams from C++ code. However, when I open the SVG using Google Chrome or any other web-browser, it is clipped according to the size of browser window without any scrollbars. On resizing window, further clipping occurs.
Related Code
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();
I have tried using setViewBox() but no effect. I am using Qt for the first time so please try to be as elaborate as possible. I preferred SVG over Bitmaps because of Quality issues.
EDIT: Zooming out reveals the hidden parts.
Upvotes: 1
Views: 1958
Reputation: 20141
The specification of either size or view box is essential for a stand-alone SVG.
This is described in the SVG Specification by the W3C:
7.2 The initial viewport
The SVG user agent negotiates with its parent user agent to determine the viewport into which the SVG user agent can render the document. In some circumstances, SVG content will be embedded (by reference or inline) within a containing document. This containing document might include attributes, properties and/or other parameters (explicit or implicit) which specify or provide hints about the dimensions of the viewport for the SVG content. SVG content itself optionally can provide information about the appropriate viewport region for the content via the 'width' and 'height' XML attributes on the 'svg' element. The negotiation process uses any information provided by the containing document and the SVG content itself to choose the viewport location and size.
If the parent document format defines rules for referenced or embedded graphics content, then the negotiation process is determined by the parent document format specification. If the parent document is styled with CSS, then the negotiation process must follow the CSS rules for replaced elements. If there are CSS width and height properties (or corresponding XSL properties) on the referencing element (or rootmost 'svg' element for inline SVG content) that are sufficient to establish the width and height of the viewport, then these positioning properties establish the viewport's width, height, and aspect ratio.
If there is no parent document, the SVG user agent must use the 'width' and 'height' attributes on the rootmost 'svg' element element as the width and height for the viewport.
Note that the time at which the viewport size negotiation is finalized is implementation-specific. Authors who need to be sure of the dimensions of the viewport should do so with load-event or resize-event handlers.
I made some examples to illustrate this.
A test file without width
, height
, and viewBox
– test.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>
Output in Google Chrome:
[![Snapshot of test.svg in Google Chrome][1]][1]
[![Snapshot of test.svg in Google Chrome (after resize)][2]][2]
Output in Firefox:
[![Snapshot of test.svg in Firefox][3]][3]
Although, the browser seems to render correctly, the graph size isn't considered correctly. (No vertical scrollbar appears although the SVG contents doesn't fit into view.)
Providing a view port (width
and height
) – test.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>
Output in Google Chrome:
[![Snapshot of test.100x100.svg in Google Chrome][4]][4]
[![Snapshot of test.100x100.svg in Google Chrome (after resize)][5]][5]
The same file with reduced width
and height
– test.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>
Output in Google Chrome:
[![Snapshot of test.50x50.svg in Google Chrome][6]][6]
width
and height
.Next, I added a viewBox
attribute – 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>
Output in Google Chrome:
[![Snapshot of test.50x50.view100x100.svg in Google Chrome][7]][7]
The view is still reduced but the graphics contents is scaled to fit into view.
What happens when width
and height
are missing? – test.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>
Output in Google Chrome:
[![Snapshot of test.view100x100.svg in Google Chrome][8]][8]
[![Snapshot of test.view100x100.svg in Google Chrome (after resize)][9]][9]
The output is scaled to fit in the browser view.
This can be compiled to the following rule of thumbs:
The viewBox
defines something like a clip space for the graphical contents.
The width
and height
provide a hint for the output size the renderer should use.
If viewBox
is missing then it is set to viewBox="0 0 width height"
.
If width
and height
are missing the renderer is free to chose it on its own.
At least, either width
and height
, or viewBox
should be provided.
The QSvgGenerator
provides the following properties to control width
, height
, and viewBox
:
size : QSize
This property holds the size of the generated SVG drawing
By default this property is set to
QSize(-1, -1)
, which indicates that the generator should not output the width and height attributes of the element.Note: It is not possible to change this property while a QPainter is active on the generator.
viewBox : QRectF
This property holds the viewBox of the generated SVG drawing
By default this property is set to
QRect(0, 0, -1, -1)
, which indicates that the generator should not output the viewBox attribute of the element.Note: It is not possible to change this property while a QPainter is active on the generator.
Upvotes: 0
Reputation: 20141
As the OP didn't provide a MCVE, I prepared one on my own:
#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;
}
It generates three files:
testQSvgGen.10%.svg
testQSvgGen.100%.svg
testQSvgGen.1000%.svg
Although the images are written with different sizes, there is no noticable difference in the preview. The reason is that the preview scales the result to its own required resolution to fit the output into current Explorer icon size. (The same applies to the preview size on right size.)
In opposition to this, a Web Browser (Google Chrome in the above snapshot) considers the size setting of SVG.
These settings are
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">
A closer look into the generated SVG-Code uncovered that the 3 files look very similar in general. The scaling which I forced by
qPainter.setTransform(QTransform().scale(s, s));
to adjust the graphics output to the intended image size is simply translated into a transform="matrix()"
attribute with the scaling for each group (<g>
).
So, I cannot confirm what OP complained:
The size set in QSvgGenerator::setSize()
is considered in generated SVG files, and the browsers respect this setting (as expected).
Source code of generated 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>
Upvotes: 1