Оказывается, работать с XML на python довольно просто. Следующий код рекурсивно (для работы с вложенными группами) работает над файлом. Хэш каждого пути (некоторые пути довольно длинные) сохраняется, если только хеш не является дубликатом, и в этом случае узел помечается для удаления. Фактическое удаление происходит перед возвратом к родительскому элементу, поскольку кажется, что оно прерывает итерацию по дочерним элементам, если выполняется на лету.
Удаление объектов, заполненных белым цветом, также происходит там.
import xml.etree.ElementTree as ET
import hashlib as hash
def iter_groups(group):
global hashlist
global count
rem=[]
for child in group:
if child.tag==rtag+'g' :#we have a group
iter_groups(child)
else:
if child.tag==rtag+'path':
h=hash.md5(str(child.attrib)).hexdigest()
print h
if h in hashlist:
rem.append(child)
print "removing ",child.tag, "in",group.tag,group.attrib," -- duplicate"
count+=1
else:
try:
print child.attrib['fill']
except KeyError:
print 'no fill'
#no fill attribute
else:
if ("rgb(255,255,255)") in child.attrib['fill']:
rem.append(child)
print "removing ",child.tag, "in",group.tag,group.attrib," -- white"
else:
hashlist.append(h)
for r in rem: group.remove(r)
#main#
hashlist=[]
count=0
tree = ET.parse('imgtest.svg')
root = tree.getroot()
rtag= root.tag.split('}')[0]+'}'
iter_groups(root)
tree.write('imgtest_out.svg',encoding="us-ascii", xml_declaration=True, default_namespace="", method="xml")
Вопросы:
- По какой-то причине все теги в выводе начинаются с "ns0:" - найдите и замените исправления, которые
- Возможно, у вас останется множество пустых групп и идентификаторов, на которые нет ссылок. Хорошей идеей будет последующий анализ файла (с помощью
--enable-id-stripping
).
Результаты: исходный файл: 20 030 КБ после этого кода: 8 555 КБ после очистки: 4 455 КБ, это почти выполнимо в Inkscape.
Есть еще несколько визуальных дубликатов, произведенных незначительно отличающимся кодом, и все еще некоторые функционально пустые группы.
Редактирование приведенного выше кода было несколько ошибок, не в последнюю очередь, что он на самом деле не удаляет белые объекты. Я также взломал что-то, чтобы иметь дело с пустыми группами и группами, содержащими только 1 элемент. Это некрасиво, но здесь все равно.
import xml.etree.cElementTree as ET
import hashlib as hash
import copy
def get_attr(obj,attr):
try:
return obj.attrib[attr]
except KeyError:
return None
else:
return None
def iter_groups(group):
global hashlist
global count
rem=[]
for child in group:
if child.tag==rtag+'g' :#we have a group
iter_groups(child)
else:
if child.tag==rtag+'path':
h=hash.md5(str(child.attrib)).hexdigest()
print h
if h in hashlist:
rem.append(child)
print "removing ",child.tag, "in",group.tag,group.attrib," -- duplicate"
count+=1
else:
if get_attr(child,'fill')!=None:
if ("rgb(255,255,255)") in child.attrib['fill']:
print "removing ",get_attr(child,'id'), "in",group.tag,group.attrib," -- white"
rem.append(child)
else:
hashlist.append(h)
for r in rem:
print "about to remove",r.attrib
group.remove(r)
rem=[]
for child in group:
if child.tag==rtag+'g' :#we have a group
if len(child.findall('*'))==0:
print "removing ",child.tag, "in",group.tag,group.attrib," -- empty"
rem.append(child)
for r in rem: group.remove(r)
def ungroup_singles(group):
global count
for child in group:
#print child.tag,rtag
if child.tag==rtag+'g' :#we have a group
print "len(group",get_attr(child,'id'),")",len(child)
if len(child)>1:
ungroup_singles(child)
else :
if len(child)==1:
if (len(child[0])>=1)or(child[0].tag<>rtag+'g'):
print "about to promote",child[0].tag,get_attr(child[0],'id'),get_attr(child[0],'class')
print len(child[0])
moveelem=copy.deepcopy(child[0])
group.append(moveelem)
group.remove(child)
count+=1
else:
print "about to remove",child[0].tag,get_attr(child[0],'id'),get_attr(child[0],'class')
child.remove(child[0])
count+=1
else:#i.e. len(child)==0
print "about to remove",child.tag,get_attr(child,'id'),get_attr(child,'class')
group.remove(child)
count+=1
#else:
# if gl==1:#and not clipped?
#moveelem= ET.copy.deepcopy(child)
#main#
hashlist=[]
count=0
ET.register_namespace("","http://www.w3.org/2000/svg")
tree = ET.parse('imgtest_l.svg')
root = tree.getroot()
rtag= root.tag.split('}')[0]+'}'
iter_groups(root)
print "A", count," elements removed"
lcount=1
while True:
count=0
ungroup_singles(root)
print lcount,":",count," empty groups removed / single elements promoted from groups"
lcount+=1
if count==0 or lcount>10:
break
tree.write('imgtest_out.svg',encoding="us-ascii", xml_declaration=True, default_namespace="", method="xml")