Validating an SVG file from RDKit - svg

I am attempting to produce an SVG image from the package RDKit. A minimum reproducable example:
from rdkit import Chem
from rdkit.Chem import Draw
img = Draw.MolsToGridImage([Chem.MolFromSmiles(x) for x in ['C', 'CO', 'CN']], useSVG = True)
print(img)
The output is below, but doesn't render any image in any browser or image viewer I have tried. Is this a valid SVG file, or is the package producing an invalid format? Or is there some other error I cannot see?
<?xml version='1.0' encoding='iso-8859-1'?>
<svg version='1.1' baseProfile='full'
xmlns='http://www.w3.org/2000/svg'
xmlns:rdkit='http://www.rdkit.org/xml'
xmlns:xlink='http://www.w3.org/1999/xlink'
xml:space='preserve'
width='600px' height='200px' >
<!-- END OF HEADER -->
<rect style='opacity:1.0;fill:#FFFFFF;stroke:none' width='600' height='200' x='0' y='0'> </rect>
<text x='0' y='200' style='font-size:0px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#000000' ><tspan>CH</tspan><tspan style='baseline-shift:sub;font-size:0px;'>4</tspan><tspan></tspan></text>
<path class='bond-0' d='M 200,200 200,200' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
<path class='bond-0' d='M 200,200 200,200' style='fill:none;fill-rule:evenodd;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
<text x='200' y='200' style='font-size:0px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#FF0000' ><tspan>OH</tspan></text>
<path d='M 200,200 200,200 200,200 200,200 200,200' style='fill:none;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
<path class='bond-0' d='M 400,200 400,200' style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
<path class='bond-0' d='M 400,200 400,200' style='fill:none;fill-rule:evenodd;stroke:#0000FF;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
<text x='400' y='200' style='font-size:0px;font-style:normal;font-weight:normal;fill-opacity:1;stroke:none;font-family:sans-serif;text-anchor:start;fill:#0000FF' ><tspan>NH</tspan><tspan style='baseline-shift:sub;font-size:0px;'>2</tspan><tspan></tspan></text>
<path d='M 400,200 400,200 400,200 400,200 400,200' style='fill:none;stroke:#FF0000;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
</svg>

RDKit is for an unknown reason producing SVG code which doesn't show anything. However, changing the list of molecules does result in SVG code which produces a visible image.
This works:
from rdkit import Chem
from rdkit.Chem import Draw
img = Draw.MolsToGridImage([Chem.MolFromSmiles(x) for x in ['C', 'CO', 'CN', 'CCC']], useSVG = True)
print(img)
A bug has been opened here: https://github.com/rdkit/rdkit/issues/2641

Related

Radial gradient SwiftUI using SVG file input

How can I add radial gradient in SwiftUI using provided SVG file?
All I have got is below SVG file which has gradient inforamtion:
<defs>
<clipPath id>
<path d="M1890, 0 L1890, 888 L0, 888 L0, 0L1890, 0 Z" />
</clipPath>
<radialGradient id: "grad1" cx = "0px" cy = "0px" r = "1000px" gradientTransform = "scale(1.0.0.56)" gradientUnits="userSpaceOnUse">
<stop stop-color = "rgba(0,0,0,0.6)" offset="0%" />
<stop stop-color = "rgba(0,0,0,0)" offset="100%" />
</radialGradient>
<clipPath id>
<path d="0, 0 1890, 0 1890, 888 0, 888 0, 0" />
</clipPath>
</defs>
My SwiftUI code has image which needs above gradient:
Image(room.thumbnailImage)
.resizable()
.frame(maxWidth: 400, maxHeight: 140.0)

How to transform the right side of SVG blocks a bit to the back?

How do i transform/rotate(?) the right side of these SVG blocks to appear more the back?
The right side of the tiles should not be higher but more to the back giving it more dept.
This is my SVG.
width="1250.345px" height="350.345px" viewBox="-30 -30 1250.345 350.345" style="enable-background:new 0 0 1250.574 350.574;"
xml:space="preserve">
<g>
<path d="M 14,19 L14,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,20 L117,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,20 L220,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
<g>
<path d="M 14,103 L14,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,103 L117,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,103 L220,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
<g>
<path d="M 14,187 L14,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,187 L 117,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,187 L 220,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
</svg> ```
I don't know what you mean by
The right side of the tiles should not be higher but more to the back giving it more dept.
when that appears to be exactly what you have drawn.
You can reproduce your drawing using a skewY() transform.
<svg version="1.1" id="scgblocks" xmlns="http://www.w3.org/2000/svg"
width="1250.345px" height="350.345px" viewBox="-30 -30 1250.345 350.345">
<g transform="skewY(-9)">
<g>
<path d="M 14,19 L14,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,20 L117,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,20 L220,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
<g>
<path d="M 14,103 L14,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,103 L117,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,103 L220,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
<g>
<path d="M 14,187 L14,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,187 L 117,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,187 L 220,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
</g>
</svg>
Is that what you wanted?
However, if instead you mean that you want it to have perspective, then you need to do it a slightly different way. You can't use perspective transforms (yet) inside an SVG, but perspective transforms are allowed on HTML elements. So, as long as the SVG is in an web page, then you can apply a perspective transform to the entire <svg> element because browsers treat top level <svg> elements like other HTML elements.
svg {
transform: perspective(1800px) rotateY(50deg);
}
<svg version="1.1" id="scgblocks" xmlns="http://www.w3.org/2000/svg"
width="1250.345px" height="350.345px" viewBox="-30 -30 1250.345 350.345">
<g>
<path d="M 14,19 L14,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,20 L117,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,20 L220,100 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
<g>
<path d="M 14,103 L14,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,103 L117,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,103 L220,183 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
<g>
<path d="M 14,187 L14,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 117,187 L 117,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
<path d="M 220,187 L 220,267 l 100,0 l 0,-80 Z" style="fill:#B1BCB6;" />
</g>
</svg>

Transform attribute affects other children in SVG

I'm trying to apply a transform on one of the elements of a SVG but the transform affects the other children as well.
The transform applied on the path element below affects the two lines as well. How can I prevent this from happening?
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" height="20" width="20">
<line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="15" x2="15" y1="0" x1="0" stroke-width="1.5" stroke="#fff" fill="none"/>
<line transform="rotate(90) " stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_2" y2="15" x2="15" y1="0" x1="0" stroke-width="1.5" stroke="#fff" fill="none"/>
<path stroke="#000000" stroke-width="27" fill="#ffffff" transform="translate(10 10) scale(0.6)" d="M512 320s-64 92.65-64 128c0 35.35 28.66 64 64 64s64-28.65 64-64-64-128-64-128zm-9.37-102.94L294.94 9.37C288.69 3.12 280.5 0 272.31 0s-16.38 3.12-22.62 9.37l-81.58 81.58L81.93 4.76c-6.25-6.25-16.38-6.25-22.62 0L36.69 27.38c-6.24 6.25-6.24 16.38 0 22.62l86.19 86.18-94.76 94.76c-37.49 37.48-37.49 98.26 0 135.75l117.19 117.19c18.74 18.74 43.31 28.12 67.87 28.12 24.57 0 49.13-9.37 67.87-28.12l221.57-221.57c12.5-12.5 12.5-32.75.01-45.25zm-116.22 70.97H65.93c1.36-3.84 3.57-7.98 7.43-11.83l13.15-13.15 81.61-81.61 58.6 58.6c12.49 12.49 32.75 12.49 45.24 0s12.49-32.75 0-45.24l-58.6-58.6 58.95-58.95 162.44 162.44-48.34 48.34z"/>
</svg>
As commented #Robert Longson, there should be no line transformation during the transformation of the patch.
Image is very small.
To see the immutability of the lines during the transformation of the path, I increased the image size width = "400" and height = "400" and painted the lines in red.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" height="400" width="400">
<line id="svg_1" y2="150" x2="150" y1="0" x1="0" stroke-width="1.5" stroke="red" fill="none"/>
<line transform="rotate(90)" id="svg_2" y2="15" x2="15" y1="0" x1="0" stroke-width="1.5" stroke="red" fill="none"/>
<path stroke="#000000" transform="translate(10 10) scale(0.6)" stroke-width="27" fill="#ffffff" d="M512 320s-64 92.65-64 128c0 35.35 28.66 64 64 64s64-28.65 64-64-64-128-64-128zm-9.37-102.94L294.94 9.37C288.69 3.12 280.5 0 272.31 0s-16.38 3.12-22.62 9.37l-81.58 81.58L81.93 4.76c-6.25-6.25-16.38-6.25-22.62 0L36.69 27.38c-6.24 6.25-6.24 16.38 0 22.62l86.19 86.18-94.76 94.76c-37.49 37.48-37.49 98.26 0 135.75l117.19 117.19c18.74 18.74 43.31 28.12 67.87 28.12 24.57 0 49.13-9.37 67.87-28.12l221.57-221.57c12.5-12.5 12.5-32.75.01-45.25zm-116.22 70.97H65.93c1.36-3.84 3.57-7.98 7.43-11.83l13.15-13.15 81.61-81.61 58.6 58.6c12.49 12.49 32.75 12.49 45.24 0s12.49-32.75 0-45.24l-58.6-58.6 58.95-58.95 162.44 162.44-48.34 48.34z"/>
</svg>
Move the path to the right transform="translate(40 10)"
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" height="400" width="400">
<line id="svg_1" y2="150" x2="150" y1="0" x1="0" stroke-width="1.5" stroke="red" fill="none"/>
<line transform="rotate(90)" id="svg_2" y2="15" x2="15" y1="0" x1="0" stroke-width="1.5" stroke="red" fill="none"/>
<path stroke="#000000" transform="translate(40 10) scale(0.6)" stroke-width="27" fill="#ffffff" d="M512 320s-64 92.65-64 128c0 35.35 28.66 64 64 64s64-28.65 64-64-64-128-64-128zm-9.37-102.94L294.94 9.37C288.69 3.12 280.5 0 272.31 0s-16.38 3.12-22.62 9.37l-81.58 81.58L81.93 4.76c-6.25-6.25-16.38-6.25-22.62 0L36.69 27.38c-6.24 6.25-6.24 16.38 0 22.62l86.19 86.18-94.76 94.76c-37.49 37.48-37.49 98.26 0 135.75l117.19 117.19c18.74 18.74 43.31 28.12 67.87 28.12 24.57 0 49.13-9.37 67.87-28.12l221.57-221.57c12.5-12.5 12.5-32.75.01-45.25zm-116.22 70.97H65.93c1.36-3.84 3.57-7.98 7.43-11.83l13.15-13.15 81.61-81.61 58.6 58.6c12.49 12.49 32.75 12.49 45.24 0s12.49-32.75 0-45.24l-58.6-58.6 58.95-58.95 162.44 162.44-48.34 48.34z"/>
</svg>
Move the path to the down transform ="translate(10 80)"
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" height="400" width="400">
<line id="svg_1" y2="150" x2="150" y1="0" x1="0" stroke-width="1.5" stroke="red" fill="none"/>
<line transform="rotate(90)" id="svg_2" y2="15" x2="15" y1="0" x1="0" stroke-width="1.5" stroke="red" fill="none"/>
<path stroke="#000000" transform="translate(10 80) scale(0.6)" stroke-width="27" fill="#ffffff" d="M512 320s-64 92.65-64 128c0 35.35 28.66 64 64 64s64-28.65 64-64-64-128-64-128zm-9.37-102.94L294.94 9.37C288.69 3.12 280.5 0 272.31 0s-16.38 3.12-22.62 9.37l-81.58 81.58L81.93 4.76c-6.25-6.25-16.38-6.25-22.62 0L36.69 27.38c-6.24 6.25-6.24 16.38 0 22.62l86.19 86.18-94.76 94.76c-37.49 37.48-37.49 98.26 0 135.75l117.19 117.19c18.74 18.74 43.31 28.12 67.87 28.12 24.57 0 49.13-9.37 67.87-28.12l221.57-221.57c12.5-12.5 12.5-32.75.01-45.25zm-116.22 70.97H65.93c1.36-3.84 3.57-7.98 7.43-11.83l13.15-13.15 81.61-81.61 58.6 58.6c12.49 12.49 32.75 12.49 45.24 0s12.49-32.75 0-45.24l-58.6-58.6 58.95-58.95 162.44 162.44-48.34 48.34z"/>
</svg>
In both examples, the lines remain in the same place, only the patch is shifted.

How can I convert a shapely Polygon to an SVG?

I can create a Polygon by:
#!/usr/bin/env python
from shapely.geometry import Polygon
area = Polygon(((52, 13), (57, 14), (58, 12)))
with open('test.svg', 'w') as f:
f.write(area.svg())
which returns
<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" stroke-width="2.0" opacity="0.6" d="M 52.0,13.0 L 57.0,14.0 L 58.0,12.0 L 52.0,13.0 z" />
This is not a valid SVG file. How can I get a valid SVG?
I tried
#!/usr/bin/env python
from shapely.geometry import Polygon
area = Polygon(((52, 13), (57, 14), (58, 12)))
with open('test.svg', 'w') as f:
f.write('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">')
f.write(area.svg())
f.write('</svg>')
When I view this, the viewport is way to big for the polygon. Manually editing it with Inkscape and resizing it gives:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
version="1.1"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="test.svg"
width="7.9687681"
height="4.4396091">
<metadata
id="metadata10">
<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>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1364"
id="namedview6"
showgrid="false"
inkscape:zoom="2.36"
inkscape:cx="-1.8038839"
inkscape:cy="-34.869627"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<path
d="m 0.19611614,2.3092357 4.99999996,1 1,-2 -5.99999996,1 z"
id="path4"
inkscape:connector-curvature="0"
style="opacity:0.6;fill:#66cc99;fill-rule:evenodd;stroke:#555555;stroke-width:2" />
</svg>
Is there a way to get this automatically?
try:
with open('test.svg', 'w') as f:
f.write(area._repr_svg_())
the BaseGeometry._repr_svg_ function is used for IPython/Jupyter integration, in order to render Shapely objects inline in Jupyter notebooks. Hence the "internal" naming, which is defined here:
http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html
Effectively, it results in a valid SVG output.
You would need to specify the size and viewBox of the resulting image. For example:
#!/usr/bin/env python
from shapely.geometry import Polygon
import textwrap
area = Polygon(((52, 13), (57, 14), (58, 12)))
with open('test.svg', 'w') as f:
#specify margin in coordinate units
margin = 5
bbox = list(area.bounds)
bbox[0] -= margin
bbox[1] -= margin
bbox[2] += margin
bbox[3] += margin
width = bbox[2] - bbox[0]
height = bbox[3] - bbox[1]
#transform each coordinate unit into "scale" pixels
scale = 10
props = {
'version': '1.1',
'baseProfile': 'full',
'width': '{width:.0f}px'.format(width = width*scale),
'height': '{height:.0f}px'.format(height = height*scale),
'viewBox': '%.1f,%.1f,%.1f,%.1f' % (bbox[0], bbox[1], width, height),
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:ev': 'http://www.w3.org/2001/xml-events',
'xmlns:xlink': 'http://www.w3.org/1999/xlink'
}
f.write(textwrap.dedent(r'''
<?xml version="1.0" encoding="utf-8" ?>
<svg {attrs:s}>
{data:s}
</svg>
''').format(
attrs = ' '.join(['{key:s}="{val:s}"'.format(key = key, val = props[key]) for key in props]),
data = area.svg()
).strip())

Drawing multiple circular arcs dynamically using SVG

I've got SVG image (code below):
<svg height="500" width="500">
<!-- Inner line -->
<path d="M100,50 L 100,100 A 45,45 0 1,0 150,150 L 200 150" style="stroke:#000; stroke-width:6; fill:none;" />
<!-- Outer line -->
<path d="M 90,50 L 90,92.5 A 54.5,54.5 0 0,0 90,197.5 L 90, 240.5" style="stroke:#000; stroke-width:1; fill:none;" />
<circle cx="105" cy="145" r="35" stroke="black" stroke-width="1" fill="green" />
I cannot see any pattern while drawing curved lines that are separated by the same margin (I've made the image above just by "guessing"). The number of lines is dynamic so I need a formula to count every M L A parameter. E.g. I can take inner line as static (showing just the d attribute),
d="M100,50 L 100,100 A 45,45 0 1,0 150,150 L 200 150"
but then first outer should be calculated somehow, to get d:
d="M 90,50 L 90,92.5 A 54.5,54.5 0 0,0 90,197.5 L 90, 240.5"
SVG arcs are drawn with A rx ry rotation large-arc-flag sweep-flag x y
The key parameters here are rx and ry: the radii of the arc and x and y which are the final point in the arc.
For example, if you have a circle centred on (cx, cy) with radius r, you can create and arc around it, d units from the edge of the circle with:
<path d="M cx cy+r+d A r+d r+d 0 0 0 cx+r+d cy" />
This arc will be in the lower, right quadrant because of where it starts (cx, cy+r+d) and ends (cx+r+d, cy).
So call the radius of the inner arc r and the radius of the outer arc r + d. Now we need to know where to start and stop the arc. The x-coordinate of the start and stop points is d units to the left of the cx. We can find the y-coordinate by using Pythagoras's theorem to calculate the height of the triangle with a hypotenuse of r+d and a base of d:
h = sqrt((r + d)^2 - d^2).
The code for the arc will therefore be:
d="M cx-d, 50 L cx-d, cy-h A r+d r+d 0 0 0 cx-d, cy+h L cx-d, 240"
For example, with (cx, cy) = (100, 150), r = 50, and d = 10
<path d="M100,50 L 100,100 A 50,50 0 1,0 150,150 L 200 150" style="stroke:#000; stroke-width:6; fill:none;" />
<path d="M 90,50 L 90,90.8 A 60,60 0 0,0 90,209.1 L 90, 240.5" style="stroke:#000; stroke-width:1; fill:none;" />
<circle cx="100" cy="150" r="40" stroke="black" stroke-width="1" fill="green" />
Does that make sense?

Resources