我正在寻找一个图表编辑器,它支持导入复杂的形状,同时保持页面的样式。 Draw.io 看起来很有前途,但如果我没记错的话,它主要促进 SVG 导入或原始 XML 输入,而不提供所见即所得的界面。
StackOverflow 上有各种与此相关的问题,但似乎都没有解决我的具体问题。例如,这个问题详细介绍了导入 SVG 然后更改其颜色的过程。
为了说明我的观点,我在下面提供了两张屏幕截图。左边的屏幕截图描绘了我导入的 SVG,而右边的屏幕截图则展示了标准的 Draw.io 形状。据观察,我只能修改 SVG 的 Fill .cls-1 或 Line .cls-1 等属性。相反,对于标准形状,我可以指定独特的风格。
我梦想有一个图表编辑器,可以导入复杂的形状并保留页面的样式。 Draw.io 有潜力,但据我了解,它只支持 SVG 导入或原始 XML 代码,没有所见即所得编辑器。
存在许多关于SO的问题,但目前没有人回答我的问题。展示如何导入 SVG 然后编辑颜色。
例如,在此屏幕截图中,左侧是导入的 SVG,右侧是常规的 Draw.io 形状。你可以看到,对于SVG,我只能编辑
Fill .cls-1
或Line .cls-1
,但对于第二个形状,我可以关联一个样式:
有没有一种方法可以将 SVG 转换为原生 Draw.io 形状,从而启用样式应用?
我天真的做法是翻译 SVG:
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 91.5 91.5">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #231f20;
stroke-miterlimit: 10;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<rect class="cls-1" x=".5" y=".5" width="90.5" height="90.5"/>
<polygon class="cls-1" points="61.09 19.17 30.4 19.17
15.06 45.75 30.4 72.32 61.09 72.32 76.44 45.75 61.09 19.17"/>
<line class="cls-1" x1="30.4" y1="72.32" x2="61.09" y2="19.17"/>
<line class="cls-1" x1="76.44" y1="45.75" x2="15.86" y2="45.75"/>
<line class="cls-1" x1="30.4" y1="19.17" x2="61.09" y2="72.32"/>
</g>
</svg>
进入Draw.io形状语言:
<shape aspect="variable" h="90.5" w="90.5" strokewidth="inherit">
<background>
<rect h="90.5" w="90.5" x="0.5" y=".5"/>
</background>
<foreground>
<fillstroke />
<path>
<move x="61.09" y="19.17"/>
<line x="30.4" y="19.17"/>
<line x="15.06" y="45.75"/>
<line x="30.4" y="72.32"/>
<line x="30.4" y="72.32"/>
<line x="61.09" y="72.32"/>
<line x="76.44" y="45.75"/>
<line x="61.09" y="19.17"/>
</path>
<stroke />
<path>
<move x="30.4" y="72.32"/>
<line x="61.09" y="19.17"/>
</path>
<stroke />
<path>
<move x="76.44" y="45.75"/>
<line x="15.86" y="45.75"/>
</path>
<stroke />
<path>
<move x="30.4" y="19.17"/>
<line x="61.09" y="72.32"/>
</path>
<stroke />
</foreground>
</shape>
为什么不使用 minidom 编写 Python 脚本?
from xml.dom import minidom
file = minidom.parse('specimen.svg')
svg = file.getElementsByTagName('svg')[0]
viewBox = svg.getAttribute('viewBox').split(' ')
width, height = float(viewBox[2]), float(viewBox[3])
def create_constraint(document, parent, x, y, perimeter=1):
""" Create a constraint """
constraint = document.createElement('constraint')
constraint.setAttribute('x', str(x))
constraint.setAttribute('y', str(y))
constraint.setAttribute('perimeter', str(perimeter))
parent.appendChild(constraint)
def create_connections(document, parent, height, width, nx=3, ny=3):
connections = document.createElement('connections')
parent.appendChild(connections)
nx += 1
ny += 1
for x in [1/nx*n for n in range(1, nx)]:
create_constraint(document, connections, 0, x)
create_constraint(document, connections, 1, x)
create_constraint(document, connections, x, 0)
create_constraint(document, connections, x, 1)
def process_rect(document, parent, rect):
rectangle = document.createElement('rect')
rectangle.setAttribute('x', rect.getAttribute('x'))
rectangle.setAttribute('y', rect.getAttribute('y'))
rectangle.setAttribute('width', rect.getAttribute('width'))
rectangle.setAttribute('height', rect.getAttribute('height'))
parent.appendChild(rectangle)
parent.appendChild(document.createElement('stroke'))
def process_polygon(document, parent, polygon):
""" Process a polygon """
points = polygon.getAttribute('points').split(' ')
points = zip(*(iter(points),) * 2)
path = document.createElement('path')
parent.appendChild(path)
move = document.createElement('move')
p = next(points)
move.setAttribute('x', p[0])
move.setAttribute('y', p[1])
path.appendChild(move)
for (x, y) in points:
line = document.createElement('line')
line.setAttribute('x', x)
line.setAttribute('y', y)
path.appendChild(line)
parent.appendChild(document.createElement('stroke'))
def process_line(document, parent, line):
""" Process a line """
x1 = line.getAttribute('x1')
y1 = line.getAttribute('y1')
x2 = line.getAttribute('x2')
y2 = line.getAttribute('y2')
path = document.createElement('path')
parent.appendChild(path)
move = document.createElement('move')
move.setAttribute('x', x1)
move.setAttribute('y', y1)
path.appendChild(move)
line = document.createElement('line')
line.setAttribute('x', x2)
line.setAttribute('y', y2)
path.appendChild(line)
parent.appendChild(document.createElement('stroke'))
def process_group(document, parent, group):
""" Process a group """
for child in group.childNodes:
if child.nodeType != child.ELEMENT_NODE:
continue
if child.tagName == 'g':
process_group(document, parent, child)
elif child.tagName == 'polygon':
process_polygon(document, parent, child)
elif child.tagName == 'line':
process_line(document, parent, child)
elif child.tagName == 'rect':
process_rect(document, parent, child)
document = minidom.Document()
shape = document.createElement('shape')
shape.setAttribute('aspect', 'variable')
shape.setAttribute('h', viewBox[2])
shape.setAttribute('w', viewBox[3])
shape.setAttribute('strokewidth', "inherit")
parent = document.appendChild(shape)
create_connections(document, parent, height, width)
background = document.createElement('background')
rect = document.createElement('rect')
rect.setAttribute('x', viewBox[0])
rect.setAttribute('y', viewBox[1])
rect.setAttribute('w', viewBox[2])
rect.setAttribute('h', viewBox[3])
background.appendChild(rect)
foreground = document.createElement('foreground')
foreground.appendChild(document.createElement('fillstroke'))
shape.appendChild(background)
shape.appendChild(foreground)
process_group(document, foreground, svg)
print(document.toprettyxml())