Created
October 13, 2021 10:45
-
-
Save MayeulC/485ce3fa16015c6227624deacc2ffb37 to your computer and use it in GitHub Desktop.
Klayout svg export
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SVGWriter | |
# Based off https://www.klayout.de/forum/discussion/873/working-with-dxfs | |
def initialize(file) | |
@file = File.open(file, "w") | |
@shift = 0 | |
end | |
def start(w, h) | |
@file.puts(<<"END") | |
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
<svg | |
xmlns:svg="http://www.w3.org/2000/svg" | |
xmlns="http://www.w3.org/2000/svg" | |
xmlns:xlink="http://www.w3.org/1999/xlink" | |
width="#{'%.12g'%w}mm" | |
height="#{'%.12g'%h}mm" | |
viewBox="0 0 #{'%.12g'%w} #{'%.12g'%h}" | |
version="1.1"> | |
END | |
end | |
def layerPattern(idx, color, stipple, name) | |
@shift= @shift+0.1 # TODO: make this better by only shifting the same | |
if @shift > 0.6 | |
@shift = 0 | |
end | |
color = '%06x' % (color & 0xffffff) | |
linePattern = '<line x1="0" y1="0" x2="0" y2="10" style="stroke-width:0.1" />' | |
angle = 0 | |
scale = 2 | |
pwidth=0.5 | |
pheight=0.5 | |
case stipple | |
when 0 # Full | |
pattern = '<rect x="0" y="0" width="0.5" height="0.5" style="fill:#'"#{color}"';stroke:none" />' | |
when 1 # empty | |
pattern = '' | |
when 2 # tiny squares close one to another | |
pwidth = 0.25 | |
pheight = 0.25 | |
pattern = '<rect x="0" y="0" width="0.125" height="0.125" style="fill:#'"#{color}"';stroke:none" />\n<rect x="0.125" y="0.125" width="0.125" height="0.125" style="fill:#'"#{color}"';stroke:none" />\n' | |
when 3 # tiny square, but spaced out | |
pattern = '<rect x="0.125" y="0" width="0.125" height="0.125" style="fill:#'"#{color}"';stroke:none" />' | |
when 5 # dash 1 | |
angle = 135 | |
scale = 2 | |
pattern = linePattern | |
when 9 # dash 2 | |
angle = 45 | |
scale = 2 | |
pattern = linePattern | |
when 13 | |
pattern = '<line x1="0" y1="0" x2="0" y2="10" style="stroke-width:0.1" />\n<line x1="10" y1="0" x2="0" y2="0" style="stroke-width:0.1" />' | |
angle = 45 | |
else | |
print("dither pattern #{stipple} not found\n") | |
pattern = '' | |
end | |
@file.puts(<<"END") | |
<!-- #{idx} / #{name} --> | |
<pattern | |
id="pattern-layer-#{idx}" | |
width="#{pwidth}" | |
height="#{pheight}" | |
stroke="##{color}" | |
stroke-linecap="square" | |
patternTransform="rotate(#{angle}) scale(#{scale}) translate(#{@shift})" | |
patternUnits="userSpaceOnUse"> | |
#{pattern} | |
</pattern> | |
END | |
end | |
def prepareLayers(layers) | |
@file.puts("<defs>\n") | |
layers.each do |lp| | |
idx = lp.layer_index | |
col = lp.fill_color | |
stip = lp.dither_pattern | |
layerPattern(idx, col, stip, lp.name) | |
end | |
@file.puts("</defs>\n") | |
end | |
def finish | |
@file.puts("</svg>") | |
@file.close | |
end | |
def begin_layer(lp) | |
@stroke_color = lp.frame_color | |
@fill_color = lp.fill_color | |
# see https://www.klayout.de/doc-qt5/code/class_LayerProperties.html#method24 | |
@stipple = lp.eff_dither_pattern | |
@index = lp.layer_index | |
@name = lp.name | |
@file.puts("<g>") | |
end | |
def end_layer(lp) | |
@file.puts("</g>") | |
end | |
def polygon(p, dbu) | |
pts = [] | |
p.each_point_hull { |pt| pts << ("%.12g %.12g" % [pt.x * dbu, pt.y * dbu]) } | |
ctrs = [ "M " + pts.join(" L ") + " z" ] | |
p.holes.times do |h| | |
pts = [] | |
p.each_point_hole(h) { |pt| pts << ("%.12g %.12g" % [pt.x * dbu, pt.y * dbu]) } | |
ctrs << [ "M " + pts.join(" L ") + " z" ] | |
end | |
d = ctrs.join(" ") | |
@file.puts(" <path style=\"fill:url(#pattern-layer-#{@index});stroke-width:0.2;stroke:##{'%06x' % (@stroke_color & 0xffffff)}\"") | |
@file.puts(" d=\"#{d}\"/>") | |
end | |
end | |
lv = RBA::LayoutView::current | |
lv || raise("No view opened") | |
cv = RBA::CellView::active | |
cv || raise("No layout loaded") | |
ly = cv.layout | |
cell = cv.cell | |
out = RBA::FileDialog::ask_save_file_name("Chose SVG file to write", ".", "SVG files (*.svg);;All files (*)") | |
if out | |
layers = [] | |
li = lv.begin_layers | |
while !li.at_end? | |
if li.current.visible? && !li.current.has_children? && li.current.layer_index >= 0 | |
layers << li.current | |
end | |
li.next | |
end | |
bbox = cell.bbox | |
dbu = ly.dbu*10 | |
writer = SVGWriter::new(out) | |
writer.start(bbox.width * dbu, bbox.height * dbu) | |
canvas_tr = RBA::CplxTrans::new(1.0, 0.0, true, RBA::DPoint::new(-bbox.left, bbox.top)) | |
writer.prepareLayers(layers) | |
layers.each do |lp| | |
writer.begin_layer(lp) | |
si = cell.begin_shapes_rec(lp.layer_index) | |
while !si.at_end? | |
s = si.shape | |
if s.is_path? || s.is_box? || s.is_polygon? | |
writer.polygon(s.polygon.transformed(canvas_tr * si.trans), dbu) | |
end | |
si.next | |
end | |
writer.end_layer(lp) | |
end | |
writer.finish | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'll create an actual repo or plugin a bit latter. I still have a lot of stipples to match, border patterns, etc. The code is quite hackish, lacks testing, and the layer shift is very hacky too. I need to make sure it works at different zoom levels.
Bear with me, it's my first time writing ruby :)