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) )
Related
I'm trying to extract a list of X,Y,Z from xml file. A part of xml is:
<Data>
<TargetPosition X="57.23471787820652" Y="-26.04271457691532"
Z="9.988092935592704" Valid="1"/> #PosLang
<StartPosition X="0" Y="0" Z="0" Valid="0"/>
</Data>
</Object>
<Object Type="{aa99a9ec-4b85-442e-b914-de3579656eb5}">
<ParentTObject Valid="1">
<Translation X="0" Y="0" Z="0"/>
<Rotation W="1" X="0" Y="0" Z="0"/>
</ParentTObject>
<Data>
<TargetPosition X="58.81901290773406" Y="-20.09883392050945"
Z="16.53197054898237" Valid="1"/> #NegLang
<StartPosition X="0" Y="0" Z="0" Valid="0"/>
</Data>
</Object>
I need to extract X,Y,Z from all TargetPosition in file that have #PosLang
comment
def targets(path='some.xml'):
try:
e = ET.parse(path).getroot()
except FileNotFoundError:
return list()
Position = namedtuple('float', ['x', 'y', 'z'])
for position in e.iter('TargetPosition'):
yield Position(
x=float(position.get('X')),
y=float(position.get('Y')),
z=float(position.get('Z'))
)
In y code i extract X,Y,Z of all TargetPosition, but i need only that have #PosLang comment
If your XML really contains the string #PosLang in exactly the way your sample shows, then that's not a comment, but a regular text node.
And since that text node follows the <TargetPosition> element, it will be in the .tail property:
def targets(path='some.xml'):
try:
e = ET.parse(path).getroot()
except FileNotFoundError:
return list()
Position = namedtuple('float', ['x', 'y', 'z'])
for position in e.iter('TargetPosition'):
if "#PosLang" in position.tail:
yield Position(
x=float(position.get('X')),
y=float(position.get('Y')),
z=float(position.get('Z'))
)
As per the docs, ElementTree ignores XML comments. Can you get the XML generated differently?
I've used XMLGenerator to write things but can't seem to figure out how to use namsepaces. I keep getting KeyErrors.
Just as a quick example I would like to write
<svg width="120" height="120"
viewBox="0 0 120 120"
xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10"
width="100" height="100"
rx="15" ry="15"/>
</svg>
How should I call startElementNS?
xmlgen.startElementNS(('http://www.w3.org/2000/svg','svg'),'svg',{})
gives me this error:
File "c:\app\python\anaconda\1.6.0\envs\emblaze\lib\xml\sax\saxutils.py", line 169, in startElementNS
self._write(u'<' + self._qname(name))
File "c:\app\python\anaconda\1.6.0\envs\emblaze\lib\xml\sax\saxutils.py", line 134, in _qname
prefix = self._current_context[name[0]]
KeyError: 'http://www.w3.org/2000/svg'
Looking at the docs seems to suggest you need something like:
from tempfile import TemporaryFile
f = TemporaryFile()
ns = "http://www.w3.org/2000/svg"
xmlgen = XMLGenerator(f)
xmlgen.startDocument()
xmlgen.startPrefixMapping("ns1", ns)
xmlgen.startElementNS((ns, "svg"), "ns1:svg", {})
f.seek(0)
print(f.read())
<?xml version="1.0" encoding="iso-8859-1"?>
<ns1:svg xmlns:ns1="http://www.w3.org/2000/svg">
The xmlgen.startPrefixMapping seems to be necessary.
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.
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!'