Friday, January 31, 2014

Using inkscape to generate svg objects from a PDF

I have a client who has a collection of storyboard / wireframe PDF documents describing end-user interaction with a company website, and my client wanted me to design and build a website that could closely match the PDFs. There is a particular page which shows a kind of user/vehicle history in a vertical format, where each individual historical event is rendered as a "bubble":



I could see no other way to create such a result without using Canvas or SVG. In the end I went with SVG for this particular application and leveraged a great application called Inkscape to pull the individual "bubble" out of the PDF and export it into SVG format:



This produced a fairly verbose SVG file:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg19746"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="New document 6">
<defs
id="defs19748">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath8551">
<path
inkscape:connector-curvature="0"
d="m 0,5177.253 3126.964,0 L 3126.964,0 0,0 0,5177.253 z"
id="path8553" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="375"
inkscape:cy="520"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="463"
inkscape:window-height="453"
inkscape:window-x="225"
inkscape:window-y="225"
inkscape:window-maximized="0" />
<metadata
id="metadata19751">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
transform="matrix(1.25,0,0,-1.25,-1385.6255,4051.4283)"
id="g8547">
<g
id="g8549"
clip-path="url(#clipPath8551)">
<g
id="g8555"
transform="translate(1275.4824,2776.2529)">
<path
inkscape:connector-curvature="0"
d="m 0,0 c -5.79,0 -10.482,5.003 -10.482,11.344 l 0,56 C -10.482,73.428 -6.084,78 0,78 l 188,0 c 5.99,0 13.449,-3.583 16.981,-8.057 L 221.802,48.552 C 225.049,44.438 232.106,41 237.211,41 l 39.307,0 0,-4 -39.307,0 c -5.356,0 -12.27,-3.316 -15.411,-7.296 L 204.983,8.317 C 201.345,3.709 193.728,0 188,0 L 0,0 z"
style="fill:#c1212c;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path8557" />
</g>
<g
id="g8559"
transform="translate(1463.4824,2855.2529)">
<path
inkscape:connector-curvature="0"
d="m 0,0 -188,0 c -6.556,0 -11.482,-5.1 -11.482,-11.656 l 0,-56 c 0,-6.893 5.141,-12.344 11.482,-12.344 l 188,0 c 5.994,0 13.964,3.84 17.766,8.656 l 16.822,21.471 C 37.511,-46.17 44.209,-43 49.211,-43 l 20.789,0 17.5,0 2.018,0 0,1.844 0,2 0,2.156 -2.018,0 -17.5,0 -20.789,0 c -4.842,0 -11.54,3.225 -14.627,7.135 L 17.77,-8.402 C 14.065,-3.711 6.262,0 0,0 m 0,-2 c 5.5,0 12.788,-3.359 16.196,-7.677 L 33.015,-31.065 C 36.423,-35.383 43.711,-39 49.211,-39 l 20.789,0 17.518,0 0,-2 -17.518,0 -20.789,0 c -5.5,0 -12.788,-3.359 -16.196,-7.677 L 16.196,-70.065 C 12.788,-74.383 5.5,-78 0,-78 l -188,0 c -5.5,0 -9.482,4.844 -9.482,10.344 l 0,56 c 0,5.5 3.982,9.656 9.482,9.656 l 187.518,0"
style="fill:#d7d6d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path8561" />
</g>
</g>
</g>
</g>
</svg>

After some common sense analysis, I trimmed it down to the following:
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
>
<g
id="layer1">
<g
transform="matrix(1.25,0,0,-1.25,-1369.1969,3999.9997)"
id="g8547">
<g
id="g8549"
clip-path="url(#clipPath8551)">
<g
id="g8555"
transform="translate(1275.4824,2776.2529)">
<path
d="m 0,0 c -5.79,0 -10.482,5.003 -10.482,11.344 l 0,56 C -10.482,73.428 -6.084,78 0,78 l 188,0 c 5.99,0 13.449,-3.583 16.981,-8.057 L 221.802,48.552 C 225.049,44.438 232.106,41 237.211,41 l 39.307,0 0,-4 -39.307,0 c -5.356,0 -12.27,-3.316 -15.411,-7.296 L 204.983,8.317 C 201.345,3.709 193.728,0 188,0 L 0,0 z"
style="fill:#c1212c;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path8557" />
</g>
<g
id="g8559"
transform="translate(1463.4824,2855.2529)">
<path
d="m 0,0 -188,0 c -6.556,0 -11.482,-5.1 -11.482,-11.656 l 0,-56 c 0,-6.893 5.141,-12.344 11.482,-12.344 l 188,0 c 5.994,0 13.964,3.84 17.766,8.656 l 16.822,21.471 C 37.511,-46.17 44.209,-43 49.211,-43 l 20.789,0 17.5,0 2.018,0 0,1.844 0,2 0,2.156 -2.018,0 -17.5,0 -20.789,0 c -4.842,0 -11.54,3.225 -14.627,7.135 L 17.77,-8.402 C 14.065,-3.711 6.262,0 0,0 m 0,-2 c 5.5,0 12.788,-3.359 16.196,-7.677 L 33.015,-31.065 C 36.423,-35.383 43.711,-39 49.211,-39 l 20.789,0 17.518,0 0,-2 -17.518,0 -20.789,0 c -5.5,0 -12.788,-3.359 -16.196,-7.677 L 16.196,-70.065 C 12.788,-74.383 5.5,-78 0,-78 l -188,0 c -5.5,0 -9.482,4.844 -9.482,10.344 l 0,56 c 0,5.5 3.982,9.656 9.482,9.656 l 187.518,0"
style="fill:#d7d6d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path8561" />
</g>
</g>
</g>
</g>
</svg>

Finally, I removed some un-needed "g" container elements, removed the clip path (as it's not needed since we have a single object) and corrected the "translate" (x/y offsetting) by normalising the lowest pair to 0,0 (in this case 1275.4824,2776.2529) and then subtracted those values from the remaining translate transforms (1463.4824,2855.2529). This now gives us a shape that is drawn from a ZERO offset. One final task was to tweak the transform matrix (1.25,0,0,-1.25,-1369.1969,3999.9997) so we take the scale down to 1.0 and again correct the x/y positioning so the whole SVG is now at a zero offset. Most of these tweaks are needed simply because of the way Inkscape exported the original object to SVG:
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
>
<g
transform="matrix(1.0,0,0,-1.0,12,79)"
>
<g
id="g8555">
<path
d="m 0,0 c -5.79,0 -10.482,5.003 -10.482,11.344 l 0,56 C -10.482,73.428 -6.084,78 0,78 l 188,0 c 5.99,0 13.449,-3.583 16.981,-8.057 L 221.802,48.552 C 225.049,44.438 232.106,41 237.211,41 l 39.307,0 0,-4 -39.307,0 c -5.356,0 -12.27,-3.316 -15.411,-7.296 L 204.983,8.317 C 201.345,3.709 193.728,0 188,0 L 0,0 z"
style="fill:#c1212c;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path8557" />
</g>
<g
id="g8559"
transform="translate(188,79)">
<path
d="m 0,0 -188,0 c -6.556,0 -11.482,-5.1 -11.482,-11.656 l 0,-56 c 0,-6.893 5.141,-12.344 11.482,-12.344 l 188,0 c 5.994,0 13.964,3.84 17.766,8.656 l 16.822,21.471 C 37.511,-46.17 44.209,-43 49.211,-43 l 20.789,0 17.5,0 2.018,0 0,1.844 0,2 0,2.156 -2.018,0 -17.5,0 -20.789,0 c -4.842,0 -11.54,3.225 -14.627,7.135 L 17.77,-8.402 C 14.065,-3.711 6.262,0 0,0 m 0,-2 c 5.5,0 12.788,-3.359 16.196,-7.677 L 33.015,-31.065 C 36.423,-35.383 43.711,-39 49.211,-39 l 20.789,0 17.518,0 0,-2 -17.518,0 -20.789,0 c -5.5,0 -12.788,-3.359 -16.196,-7.677 L 16.196,-70.065 C 12.788,-74.383 5.5,-78 0,-78 l -188,0 c -5.5,0 -9.482,4.844 -9.482,10.344 l 0,56 c 0,5.5 3.982,9.656 9.482,9.656 l 187.518,0"
style="fill:#d7d6d4;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path8561" />
</g>
</g>
</svg>

... and the resultant individual SVG object:

Quite a nice result... all that is left now is to get the rest of the site working and the positioning correct ;-)