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.
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
in 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">
in 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">
in 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>
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]
The initial view size is reduced.
The graphic output is clipped against 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.
Morning folks!
So I am currently working on an FOP solution for a project, the end goal of which is basically to allow me to print onto a medium with the second iteration of the same information upside down, a feature which is not possible in Access 2007.
Everything is working swimmingly, and I can get the FOP to parse when I duplicate the code for the front side and use it for the back side.
However, when I try to use the parameter, I am getting an error I simply don't understand from the FOP terminal, having spent the better part of 4-5 hours trying to get my head around it. Code and error to follow.
<?xml version="1.0" encoding="utf-8"?>
<!-- WARNING - THIS TEMPLATE IS FOR Z FOLD CANON STOCK - DOUBLE SIDED -->
<!-- In order to covert CM to pixels, multiply the CM by 37.7952755905511, and round to two decimal places. It may be easier to use a spreadsheet to do this if you are editing multiple values -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<!-- EDIT THIS ROW TO CHANGE THE SIZE OF THE PAGE - AKA SET THIS TO THE SIZE OF THE MEDIUM YOU ARE PRINTING TO - CONVERTED TO PIXELS-->
<fo:simple-page-master master-name="simple" page-height="740.787px" page-width="317.480px">
<!-- IGNORE EVERYTHING BETWEEN HERE-->
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="simple">
<fo:flow flow-name="xsl-region-body">
<xsl:for-each select="//Person">
<fo:block>
<fo:instream-foreign-object>
<!--<svg xmlns="http://www.w3.org/2000/svg">-->
<!-- AND HERE-->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="370.4" height="634.96">
<g class="mirrorpage">
<rect style= "fill:none; stroke: black" width="370.394" height="317.480"/>
<text x="37.8" y="122.83" style="text-anchor:start;" font-family="'ChevinLight'">
<tspan font-size="20">
Firstname
</tspan>
<tspan font-size="20">
Lastname
</tspan>
</text>
<text style="text-anchor:start;" x="37.8" y="150" font-family="'ChevinLight'">
<tspan font-size="20">
Company Name
</tspan>
</text>
<xsl:variable name="code" select="Code"/>
<image xlink:href="LINK REDACTED" x="37.8" y="190" height="50" width="50"/>
<image xlink:href="LINK REDACTED" x="250" y="200" height="100"width="100"/>
</g>
<use href= "#mirrorpage" transform="translate (370.394 634.96) scale (-1 -1)"/>
</svg>
</fo:instream-foreign-object>
</fo:block>
</xsl:for-each>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
The error I am then receiving when I try to parse this is as follows:
[ERROR] svg graphic could not be built: file:/C:/Users/Events/Desktop/Angledtext/BadgePrinting/PDF/:-1
The URI '' specified on the element is invalid.
From my (limited) understanding, there is an issue with the parsing of the "#" in the use line. I have tried to research URI properties but I have to be honest, I am completely lost.
Any help, or even a pointer to some relevant material I can read would be greatly appreciated.
Thanks
My team's designer has us using SVG "sprites" in our application. I'm want to be able to see all the available images. I was going to parse the XML and build something on the back end but then I thought about XSLT. I'd like to have an XSLT file that parses the SVG and creates a list of images. I'm close to getting it... here's what I have.
Sample SVG (although I also tried the sample file in the post):
<?xml-stylesheet type="text/xsl" href="/pages/sprites.xslt" ?>
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<symbol id="fitness" viewBox="0 0 64 64">
<g fill="none" fill-rule="evenodd" stroke-width="2" transform="translate(5 3)" stroke-linecap="square">
<path d="M5.8,27.0664159 L5.8,10.6333223 C5.8,4.7607008 10.5607008,0 16.4333223,0 L16.4333445,0 C22.3059668,0 27.0666667,4.7607008 27.0666667,10.6333223 L27.0666667,47.3666778 C27.0666667,53.2393002 31.8273665,58 37.699989,58 L37.7000111,58 C43.5726335,58 48.3333333,53.2393002 48.3333333,47.3666777 L48.3333333,30.9333333"/>
<polygon points="11.6 50.267 11.6 47.367 9.667 44.467 9.667 32.867 11.6 29.967 11.6 27.067 0 27.067 0 29.967 1.933 32.867 1.933 44.467 0 47.367 0 50.267"/>
<polygon points="54.133 30.933 54.133 28.033 52.2 25.134 52.2 13.533 54.133 10.633 54.133 7.733 42.533 7.733 42.533 10.633 44.467 13.533 44.467 25.134 42.533 28.033 42.533 30.933"/>
</g>
</symbol>
</defs>
</svg>
and the XSLT file found here:
How can I show all symbols in an SVG file?
It mostly works... it creates all the dom objects I'm expecting out of the box, which is pretty amazing. But inside use I get:
#shadow-root (closed)
which is what we see in our actual app... but in our app, the image is nested inside the shadow-root. But in this version, it's empty. Seems basically the same as how we're doing it on the app side. What's the issue?
Instead of loading the symbols from a file, write them into the output and reference locally.
Aditionally, you need to define a size for the used elements (the default would be 100%). As position() is one-based, subtract one to start top-left with the display.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns="http://www.w3.org/2000/svg"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<xsl:template match="/">
<svg>
<xsl:copy>
<xsl:copy-of select="//svg:defs"/>
</xsl:copy>
<g stroke="black" fill="black">
<xsl:for-each select="//svg:symbol">
<use width="32" height="32">
<xsl:attribute name="x"><xsl:value-of select="(position()-1) mod 10 * 32"/></xsl:attribute>
<xsl:attribute name="y"><xsl:value-of select="floor((position()-1) div 10) * 32"/></xsl:attribute>
<xsl:attribute name="xlink:href">#<xsl:value-of select="#id"/></xsl:attribute>
</use>
</xsl:for-each>
</g>
</svg>
</xsl:template>
</xsl:stylesheet>
Hello so i've been making a bar chart. Which takes xml code and transforms it into a svg bar chart. I've made one with data going in the normal way, with the 'questions' running along the x-axis and data going up the y-axis. I want the bar chart to display questions going up the y-axis and then the data going along the x-axis. I'm not sure how to go about this.
<?xml version="1.0"?>
<?xml-stylesheet type='text/xsl' href='mytransformation.xslt'
?>
<Survey>
<Questions>
<Number>1</Number>
<Question>Is there free circulation of air through and about the building in which you work?</Question>
<Comment>"One says where she is employed there are 100 women and small girls who work in a cellar without ventilation, and gas and electric lights burning all day."</Comment>
<Yes>468</Yes>
<No>249</No>
<Blank>93</Blank>
</Questions>
<Questions>
<Number>2</Number>
<Question>Are there offensive odors in the rooms occupied by employees; if so, from what causes?</Question>
<Comment>"Eighty-one from water-closets and seventy-three from various other causes."</Comment>
<Yes>342</Yes>
<No>233</No>
<Blank>235</Blank>
</Questions>
<Questions>
<Number>3</Number>
<Question>Are there facilities for washing?</Question>
<Comment>Fourteen answered yes, but if caught washing are fined.</Comment>
<Yes>460</Yes>
<No>124</No>
<Blank>226</Blank>
</Questions>
<Questions>
<Number>4</Number>
<Question>Are seats provided, as prescribed by law?</Question>
<Yes>320</Yes>
<No>192</No>
<Blank>298</Blank>
</Questions>
</Survey>
This is the xslt code.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="/">
<svg width="1000" height="1500">
<text id="title" x="500" y="50px" text-anchor="middle" style="fill:rgb(0,0,0);font-size:20;">SANITARY CONDITIONS OF WORKSHOPS AND FACTORIES</text>
<text id="50percentlabel" x="45" y="260" text-anchor="end">50%</text>
<text id="1000percentlabel" x="45" y="70" text-anchor="end">100%</text>
<text id="legendlabel" x="750" y="200">Legend: Survey Responses</text>
<rect height="20" width="50" x="750" y="220" fill="blue"/>
<text id="legend" x="810" y="235">Blank</text>
<rect height="20" width="50" x="750" y="250" fill="red"/>
<text id="legend" x="810" y="265">No</text>
<rect height="20" width="50" x="750" y="280" fill="black"/>
<text id="legend" x="810" y="295">Yes</text>
<line id="xaxis" x1="50" y1="450" x2="700" y2="450" style="stroke:rgb(0,0,0);"/>
<line id="yaxis" x1="50" y1="70" x2="50" y2="450" style="stroke:rgb(0,0,0);"/>
<xsl:for-each select="Survey/Questions">
<xsl:sort select="No" order="descending"/>
<xsl:if test="Yes">
<g>
<rect height="{(Blank div (Yes + No + Blank))*380}" width="25" x="{10+ (position()*50)}" y="70" fill="blue"/>
</g>
<g>
<rect height="{(No div (Yes + No + Blank))*380}" width="25" x="{10+ (position()*50)}" y="{70+(Blank div (Yes + No + Blank))*380}" fill="red"/>
</g>
<g>
<rect height="{(Yes div (Yes + No + Blank))*380}" width="25" x="{10+ (position()*50)}" y="{70+((Blank + No) div (Yes + No + Blank))*380}" fill="black"/>
</g>
<text id="Question" x="{15+ (position()*50)}" y="455" style="writing-mode: tb; glyph-orientation-vertical: 0;" >Q<xsl:number value="position()"/>: <xsl:value-of select="Question"/></text>
</xsl:if>
</xsl:for-each>
</svg>
</xsl:template>
</xsl:stylesheet>
I use graphviz to generate SVG graphs, and use some perl expressions (some -lane, some -0pe) to post-process/slim the dot output.
The following shell/perl command pipeline will:
convert some <polygons into <rect, because dot outputs even perfect rectangles as polygons.
convert some more polygons, because "clusters" -though rectangular- are layed out differently to "normal" nodes
removes/rounds some ".9999999" fractions somehow resulting from the first 2 commands...
rewrites straight lines (h+v) from <path to <line
remove empty groups resulting from invisible nodes and paths still being "rendered" to the SVG.
Ultimately make the file smaller and also easier to read.
How would I optimize this script and/or compact it into less statements under one perl invocation?
$ cat Graph.dot.svg |
perl -lane '# Rectangular Nodes: Polygons like DR-DL-UL-UR-DR
/^<polygon( .* )points="([-\d.]*),([-\d.]*) ([-\d.]*),\3 \4,([-\d.]*) \2,\5 \2,\3"(.*)$/
and do {
$x=($2<$4?$2:$4); $y=($3<$5?$3:$5);
$w=abs($2-$4); $h=abs($3-$5);
print "<rect$1x=\"$x\" y=\"$y\" width=\"$w\" height=\"$h\"$6";
} or print $_;' |
perl -lane '# Cluster Nodes -also rects-, but layed out differntly:
/^<polygon( .* )points="([-\d.]*),([-\d.]*) \2,([-\d.]*) ([-\d.]*),\4 \5,\3 \2,\3"(.*)$/
and do {
$x=($2<$5?$2:$5); $y=($3<$4?$3:$4);
$w=abs($2-$5); $h=abs($3-$4);
print "<rect$1x=\"$x\" y=\"$y\" width=\"$w\" height=\"$h\"$6";
} or print $_;
' | perl -pe '# Rounds ".999999" and "\d*[36]99+7" "errors"
/^<rect/ and s/([0-9]+\.)99+/$1+1/e;s/([36])99+7/$1+1/e
' | perl -lane '# Straight vertical lines as paths -> "<line"
print /^<path( .* )d="M([-\d.]*),([-\d.]*)C(([-\d.]*),\3 ?){3}"(.*)$/
?"<line$1x1=\"$2\" y1=\"$3\" x2=\"$5\" y2=\"$3\" stroke-width=\"2\"$6" : $_;
' | perl -lane '# Straight horizontal lines as paths -> "<line"
print /^<path( .* )d="M([-\d.]*),([-\d.]*)C(\2,([-\d.]*) ?){3}"(.*)$/
?"<line$1x1=\"$2\" y1=\"$3\" x2=\"$2\" y2=\"$5\" stroke-width=\"2\"$6" : $_;
' | perl -0777 -pe '
s/^<!-- [^ ]\* -->\s<g [^<]*<title>[^>]*>\s<\/g>$//mg' > Output.dot.svg
Example Input data below generated from graph:
graph G {
node[shape=box,height=0,width=0,margin=0]
subgraph cluster_1 {
start -- b1;
}
rank=same{b1 -- end};
end [shape=circle];
}
with either dot -Tsvg or online here(viz.je)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="89pt" height="105pt"
viewBox="0.00 0.00 89.29 105.39" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 101.388)">
<polygon fill="white" stroke="none" points="-4,4 -4,-101.388 85.2939,-101.388 85.2939,4 -4,4"/>
<g id="clust1" class="cluster"><title>cluster_1</title>
<polygon fill="none" stroke="black" points="8,-56.5878 8,-89.3878 48,-89.3878 48,-56.5878 8,-56.5878"/>
</g>
<g id="node1" class="node"><title>start</title>
<polygon fill="none" stroke="black" points="40.1012,-81.2889 15.8988,-81.2889 15.8988,-64.6866 40.1012,-64.6866 40.1012,-81.2889"/>
<text text-anchor="middle" x="28" y="-68.7878" font-family="Times,serif" font-size="14.00">start</text>
</g>
<g id="node2" class="node"><title>b1</title>
<polygon fill="none" stroke="black" points="35,-22.5951 21,-22.5951 21,-5.9927 35,-5.9927 35,-22.5951"/>
<text text-anchor="middle" x="28" y="-10.0939" font-family="Times,serif" font-size="14.00">b1</text>
</g>
<!-- start--b1 -->
<g id="edge1" class="edge"><title>start--b1</title>
<path fill="none" stroke="black" d="M28,-64.576C28,-53.7707 28,-33.796 28,-22.8884"/>
</g>
<g id="node3" class="node"><title>end</title>
<ellipse fill="none" stroke="black" cx="67" cy="-14.2939" rx="14.0907" ry="14.0907"/>
<text text-anchor="middle" x="67" y="-10.0939" font-family="Times,serif" font-size="14.00">end</text>
</g>
<!-- b1--end -->
<g id="edge2" class="edge"><title>b1--end</title>
<path fill="none" stroke="black" d="M35.0078,-14.2939C40.8397,-14.2939 46.6716,-14.2939 52.5035,-14.2939"/>
</g>
</g>
</svg>
The result after feeding the above input into the perl pipeline (Notice all the polygons and paths have been converted to rectangles and lines.)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="89pt" height="105pt"
viewBox="0.00 0.00 89.29 105.39" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 101.388)">
<rect fill="white" stroke="none" x="-4" y="-101.388" width="89.2939" height="105.388"/>
<g id="clust1" class="cluster"><title>cluster_1</title>
<rect fill="none" stroke="black" x="8" y="-89.3878" width="40" height="32.8"/>
</g>
<g id="node1" class="node"><title>start</title>
<rect fill="none" stroke="black" x="15.8988" y="-81.2889" width="24.2024" height="16.6023"/>
<text text-anchor="middle" x="28" y="-68.7878" font-family="Times,serif" font-size="14.00">start</text>
</g>
<g id="node2" class="node"><title>b1</title>
<rect fill="none" stroke="black" x="21" y="-22.5951" width="14" height="16.6024"/>
<text text-anchor="middle" x="28" y="-10.0939" font-family="Times,serif" font-size="14.00">b1</text>
</g>
<!-- start--b1 -->
<g id="edge1" class="edge"><title>start--b1</title>
<line fill="none" stroke="black" x1="28" y1="-64.576" x2="28" y2="-22.8884" stroke-width="2"/>
</g>
<g id="node3" class="node"><title>end</title>
<ellipse fill="none" stroke="black" cx="67" cy="-14.2939" rx="14.0907" ry="14.0907"/>
<text text-anchor="middle" x="67" y="-10.0939" font-family="Times,serif" font-size="14.00">end</text>
</g>
<!-- b1--end -->
<g id="edge2" class="edge"><title>b1--end</title>
<line fill="none" stroke="black" x1="35.0078" y1="-14.2939" x2="52.5035" y2="-14.2939" stroke-width="2"/>
</g>
</g>
</svg>