I want to convert png to base64 and insert the encoded string in an svg.
fIm = open('name.png', 'rb')
dataIm = fIm.read().encode("base64").replace('\n','')
baseIm += '<g id="%s"><image xlink:href="data:image/png;base64,%s" width="%s" height="%s"/></g>' % (newVal, dataIm, curX, curY)
The result image does not display.
What's the problem?
Here's the output svg file:
<?xml version='1.0' ?>
<svg viewBox='0 0 200 200' width='200' height='200' xmlns='http://www.w3.org/2000/svg'>
<defs><g id="name">
<image xlink:href="data:image/png;base64,..." width='20' height='20'/>
</g></defs>
<use xlink:href="#name" x='30' y='30' />
</svg>
solution
fIm = open('switchToMinus.png', 'rb')
dataIm = fIm.read().encode("base64").replace('\n','')
addText = '<image xlink:href="data:image/png;base64,{0}" width="20" height="20" x="40" y="40" />'.format(dataIm)
startSvg = """<?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 version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="240px" height="240px" viewBox="0 0 240 240">
"""
endSvg = """
</svg>
"""
if __name__ == '__main__':
f = open('image2.svg','w')
f.write( startSvg + addText + endSvg )
f.close()
print 'Okay!'
Related
I have an SVG image with Model and Group as ID attribute of tag g, like
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="1141.5200729370117" version="1.1" width="1726.9000701904297" id="temp" viewBox="0 0 1726.9000701904297 1141.5200729370117">
<defs/>
<g id="Model" class="Model A">
<g class="Group">
<g id="Group-1" class="Group A">
<g id="Line" fill="#000000" stroke="#000000" style="fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2;" class="Line External"><polygon points="118.00,21.56 1293.38,21.56 1277.38,37.56 133.97,37.56 "/> </g>
<g id="Box" fill="#FFFFFF" stroke="#FFFFFF" style="fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2;" class="Box External"><polygon points="118.00,21.56 1293.38,21.56 1277.38,37.56 133.97,37.56 "/> </g>
</g>
</g>
</g>
</svg>
I want to remove all g tags which have id other than Line. I read the SVG file using:
import xml.etree.ElementTree as ET
root = ET.parse('my file.svg')
SVG_NS = "http://www.w3.org/2000/svg"
Then I create a parent map
parent_map = {c:p for p in root.iter() for c in p}
And remove those other than Line
for node in root.findall('.//{%s}g' % SVG_NS):
name = node.get('id')
if "Line" in str(name):
print('n=', node)
else:
parent_map[node].remove(node)
How do I convert back the parent_map to an SVG and save it as a png file?
My main objective is to change stroke width of a given svg file, but I see no inbuilt method to do so.
The width's are unfortunately hardcoded in the svg file, and my initial thought was to buffer the data, and edit it in memory before changing the rendered svg file. But that is just additional processing overhead.
Next, I looked into converting the svg to a dom - tree like structure, but then I might have to save it back again before loading it in the QGraphicsSvgItem, which would be a bizarre intermediate step for changing line thickness on the fly.
Here is an example 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="3.2421772mm"
height="6.1269817mm"
viewBox="0 0 3.2421772 6.1269817"
version="1.1"
id="svg12403"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="Bag.svg">
<defs
id="defs12397" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="-229.58734"
inkscape:cy="-219.85002"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="705"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata12400">
<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"
transform="translate(-43.736054,-84.5377)">
<g
id="use26359"
transform="matrix(0.26458333,0,0,0.26458333,-3.9887501,73.928193)">
<desc
id="desc12960">Bag</desc>
<title
id="title12962">Bag</title>
<path
d="m 180.61365,49.4094 v 13.609494 h 11.78107 V 49.4094 C 192.27,46.875236 189.67,44.872517 186.50494,44.872517 c -3.16454,0 -5.76448,2.002719 -5.89129,4.536883 z m 2.94488,-9.07316 2.94641,4.536277 2.9449,-4.536277 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.66862381"
id="path12964"
inkscape:connector-curvature="0" />
<g
style="stroke-width:1.31504369"
id="g12968"
transform="matrix(0.507302,0,0,0.50958516,89.699529,-958.6518)">
<path
d="m 179.211,1978.199 v 26.707 h 23.223 v -26.707 c -0.246,-4.972 -5.371,-8.902 -11.61,-8.902 -6.238,0 -11.363,3.93 -11.613,8.902 z m 5.805,-17.804 5.808,8.902 5.805,-8.902 z m 5.808,0 v 8.902"
style="fill:none;stroke:#000000;stroke-width:0.93203717;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path12966"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</svg>
As you can see, there is a stroke width value in the style attribute for the path.
Is there a simple less obnoxious "hack" way of achieving this?
I have written some code to try and do the following
Open SVG file I have already retrieved earlier in the Python code
Look through the file for a particular regex (r"(?:xlink:href\=\")(.*)(?:\?q=80\"/>)")
If the match is found, replace the text with a particular string e.g.
https://regex101.com/r/IECsHu/1
Then retrieve the JPG from the url which is matched (see above link to regex101.com)
However, this does not work and completely blanks the file out (so it is 0 bytes)
I think I must be very close to getting this working, but haven't managed it yet. Any guidance would be appreciated
pagenumber=1
directory_in_str='/home/somewhere/somedir/'
pathlist = Path(directory_in_str).glob('**/*.svg')
for path in pathlist:
#because path is object not string
path_in_str = str(path)
print(path_in_str)
with open(path_in_str, 'r+') as f:
for line in f:
myregex = r"(?:xlink:href\=\")(.*)(?:\?q=80\"\/\>)"
result = myregex.search(line)
if result:
#If a match is found, replace the text in the line
origfullimgfilename = result
formattedpagenumber = '{:0>3}'.format(pagenumber)
replfullimgfilename='page-'+str(formattedpagenumber)+'-img1.jpg'
line = re.sub(origfullimgfilename, replfullimgfilename, line.rstrip())
#Then retrieve the file! (origfullimgfilename)
try:
urllib.request.urlretrieve(origfullimgfilename+"?q=100", replfullimgfilename)
except urllib.error.HTTPError as e:
print("HTTP Error: "+str(e.code)+" - SVG URL: "+str(origfullimgfilename)+" - aborting\n")
break
pagenumber += 1
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">
<!-- Page 1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="1198" height="1576" viewBox="0 0 1198 1576" version="1.1" style="display: block;margin-left: auto;margin-right: auto;">
<path d="M0,0 L0,1576 L1198,1576 L1198,0 Z " fill="#FFFFFF" stroke="none"/>
<image preserveAspectRatio="none" x="0" y="0" width="1199" height="1577" xlink:href="https://cdn-assets.somewhere.com/f929e7b4404d3e48918cdc0ecd4efbc9fa91dab5_2734/9c237c7e35efe88878f9e5f7a3555f7a379ed9ee9d95b491b6d0003fd80afc6b/9c68a28d6b5161f7e975a163ff093a81172916e23c778068c6bfefcfab195154.jpg?q=80"/>
<g fill="#112449">
<use xlink:href="#f0_2" transform="matrix(19.1,0,0,19.1,885.3,204.3)"/>
<use xlink:href="#f0_o" transform="matrix(19.1,0,0,19.1,910,204.3)"/>
<use xlink:href="#f0_t" transform="matrix(19.1,0,0,19.1,930.3,204.3)"/>
<use xlink:href="#f0_f" transform="matrix(19.1,0,0,19.1,949.6,204.3)"/>"""
import re
newlines = []
for line in lines.split('\n'):
myregex = re.compile(r"(?:xlink:href\=\")(.*)(?:\?q=80\"\/\>)")
result = myregex.search(line)
if result:
print(result)
#If a match is found, replace the text in the line
origfullimgfilename = result.group(1)
replfullimgfilename='page-##-img1.jpg'
line = re.sub(origfullimgfilename, replfullimgfilename, line)
newlines.append(line)
print( '\n'.join(newlines) )
I am having trouble selecting a particular set of paths using lxml. The SVG structure looks like this
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Created with matplotlib (http://matplotlib.org/) -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="288pt" version="1.1" viewBox="0 0 432 288" width="432pt">
<defs>
<style type="text/css">
*{stroke-linecap:butt;stroke-linejoin:round;}
</style>
</defs>
<g id="figure_1">
<g id="patch_1">
<path d=" M0 288 L432 288 L432 0 L0 0 z " style="fill:#ffffff;"/>
</g>
<g id="patch_2">
<path d=" M0 288 L432 288 L432 0 L0 0 z " style="fill:#ffffff;"/>
</g>
<g id="axes_1">
<g id="Poly3DCollection_1">
<path clip-path="url(#pe61355d493)" d=" M195.211 34.2225 L194.801 34.0894 L196.527 212.986 L196.909 212.999 z " style="fill:#0000ff;"/>
<path clip-path="url(#pe61355d493)" d=" M195.504 34.3231 L195.211 34.2225 L196.909 212.999 L197.184 213.022 z " style="fill:#0000ff;"/>
...
Its the paths listed at the bottom that I want to select and change their styles but I can't seem to get the syntax right and I fail to select the paths
ifilename = "myfig.svg"
with open( ifilename, 'r') as infile:
tree = etree.parse( infile )
elements = tree.findall(".//g[#id='Poly3DCollection_1'")
new_style = 'stroke-width:4px; stroke: linear-gradient(orange, darkblue)'
for child in elements:
child.attrib['style'] = new_style
mod_svg = 'myfigmod.svg'
tree.write(mod_svg)
EDIT
so this gets me the element I want in this instance but I would still like a specific way of getting this element
root = tree.getroot()
for child in root[1][2][0]:
child.attrib['style'] = new_style
There is no get_element_by_id in etree, so you have to use xpath, like you are doing to grab the element. I created your file and ran the code below and was able to change the style of the group.
element = tree.findall(".//{%s}g[#id='Poly3DCollection_1']" % SVG_NS)[0]
element.attrib["style"] = new_style
I have a SVG with 2 circles. In fact, they use just a single "def" called s1. How can I change attributes from just one circle (use). For example I want to set a different class to the element s1 when using by an specific "use" element.
<svg viewBox = "0 0 1000 1000" version = "1.1">
<defs>
<!-- A circle of radius 200 -->
<circle id = "s1" cx = "200" cy = "200" r = "200" fill = "yellow" stroke = "black" stroke-width = "3"/>
</defs>
<use x = "100" y = "100" xlink:href = " #s1 "/>
<use x = "100" y = "650" xlink:href = " #s1 "/>
Thanks in advance.
You can't change element's specific atributes (cx,cy,r), but you can change all atributes on this list using <set ... >.
For example "opacity". Here is how to change it for your circles (hint: if you open this svg with opera or chrome, put cursor over third circle):
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox="0 0 1000 1000" version="1.1" height="1000px" width="1000px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<g>
<rect y="0" width="1000" fill="blue" x="0" height="1000" />
<defs>
<circle id = "s1" cx = "200" cy = "200" r = "200" fill = "yellow" stroke = "black" stroke-width = "3"/>
</defs>
<use id = "one" x = "100" y = "100" xlink:href = "#s1"/>
<use id = "two" x = "100" y = "500" xlink:href = "#s1"/>
<use id = "three" x = "500" y = "100" xlink:href = "#s1">
<set attributeName="opacity" from="1" to="0.7" begin="mouseover" end="mouseout"/>
</use>
<set xlink:href="#two" attributeName="opacity" from="1" to="0.2" begin="three.mouseover" end="three.mouseout"/>
<set xlink:href="#one" attributeName="opacity" from="1" to="0.4"/>
</g>
</svg>
I hope this helps.