У меня есть экспортированное изображение, состоящее из ~ 35000 объектов (единственный способ вытащить его из исходного приложения - это скопированный с копией "метафайл"). Вставив в libreoffice draw и конвертировав в полигоны, я могу что-то редактировать. Каждая видимая строка в файле представляет собой стек из 8 строк, поэтому в 8 раз больше объектов, чем должно быть. В настоящий момент происходит сбой inkscape в формате .pdf или .svg, но я могу редактировать в libreoffice (делать что-либо, например панорамирование или масштабирование, мучительно медленно, и дедупликация вручную, по моим грубым подсчетам, будет работать 10-20 часов).

Есть ли что-нибудь, что может прочитать файл SVG, найти объекты, которые являются дубликатами друг друга, и сохранить только одну из копий? Подавляющее большинство моих объектов - это линии, поэтому, если бы он мог обрабатывать только строки, а не полигоны, мой файл мог бы получить полезный размер.

Поскольку SVG файл имеет размер 20 МБ, очевидно, что .odg и .pdf сжимаются.

У меня есть Linux или WinXP (VM, не спрашивайте!) в моем распоряжении.

1 ответ1

1

Оказывается, работать с 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")

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .