[SVG][Added] Options to limit the view box to the used area.
This commit is contained in:
parent
b2f5612b77
commit
42f5dcd8d6
|
|
@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Support for multi-boards as defined by KiKit
|
||||
- iBoM:
|
||||
- `hide_excluded` to hide excluded *.Fab drawings.
|
||||
- SVG:
|
||||
- Options to limit the view box to the used area.
|
||||
### Fixed
|
||||
- PCB_Print:
|
||||
- Images not showing in custom frames. (#352)
|
||||
|
|
|
|||
16
README.md
16
README.md
|
|
@ -3300,6 +3300,7 @@ Notes:
|
|||
see `show_components`.
|
||||
- `libs`: [list(string)=[]] List of libraries.
|
||||
- `margin`: [number|dict] Margin around the generated image [mm].
|
||||
Using a number the margin is the same in the four directions.
|
||||
* Valid keys:
|
||||
- `bottom`: [number=0] Bottom margin [mm].
|
||||
- `left`: [number=0] Left margin [mm].
|
||||
|
|
@ -4145,13 +4146,28 @@ Notes:
|
|||
- `inner_extension_pattern`: [string=''] Used to change the Protel style extensions for inner layers.
|
||||
The replacement pattern can contain %n for the inner layer number and %N for the layer number.
|
||||
Example '.g%n'.
|
||||
- `limit_viewbox`: [boolean=false] When enabled the view box is limited to a selected area.
|
||||
- `line_width`: [number=0.25] [0.02,2] For objects without width [mm] (KiCad 5).
|
||||
- `margin`: [number|dict] Margin around the view box [mm].
|
||||
Using a number the margin is the same in the four directions.
|
||||
See `limit_viewbox` option.
|
||||
* Valid keys:
|
||||
- `bottom`: [number=0] Bottom margin [mm].
|
||||
- `left`: [number=0] Left margin [mm].
|
||||
- `right`: [number=0] Right margin [mm].
|
||||
- `top`: [number=0] Top margin [mm].
|
||||
- `mirror_plot`: [boolean=false] Plot mirrored.
|
||||
- `negative_plot`: [boolean=false] Invert black and white.
|
||||
- `plot_footprint_refs`: [boolean=true] Include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] Include the footprint values.
|
||||
- `pre_transform`: [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `size_detection`: [string='kicad_edge'] [kicad_edge,kicad_all] Method used to detect the size of the view box.
|
||||
The `kicad_edge` method uses the size of the board as reported by KiCad,
|
||||
components that extend beyond the PCB limit will be cropped. You can manually
|
||||
adjust the margin to make them visible.
|
||||
The `kicad_all` method uses the whole size reported by KiCad. Usually includes extra space.
|
||||
See `limit_viewbox` option.
|
||||
- `sketch_pad_line_width`: [number=0.1] Line width for the sketched pads [mm], see `sketch_pads_on_fab_layers` (KiCad 6+)
|
||||
Note that this value is currently ignored by KiCad (6.0.9).
|
||||
- `sketch_pads_on_fab_layers`: [boolean=false] Draw only the outline of the pads on the *.Fab layers (KiCad 6+).
|
||||
|
|
|
|||
|
|
@ -2038,7 +2038,8 @@ outputs:
|
|||
highlight: []
|
||||
# [list(string)=[]] List of libraries
|
||||
libs: []
|
||||
# [number|dict] Margin around the generated image [mm]
|
||||
# [number|dict] Margin around the generated image [mm].
|
||||
# Using a number the margin is the same in the four directions
|
||||
margin:
|
||||
# [number=0] Bottom margin [mm]
|
||||
bottom: 0
|
||||
|
|
@ -2823,8 +2824,22 @@ outputs:
|
|||
# The replacement pattern can contain %n for the inner layer number and %N for the layer number.
|
||||
# Example '.g%n'
|
||||
inner_extension_pattern: ''
|
||||
# [boolean=false] When enabled the view box is limited to a selected area
|
||||
limit_viewbox: false
|
||||
# [number=0.25] [0.02,2] For objects without width [mm] (KiCad 5)
|
||||
line_width: 0.25
|
||||
# [number|dict] Margin around the view box [mm].
|
||||
# Using a number the margin is the same in the four directions.
|
||||
# See `limit_viewbox` option
|
||||
margin:
|
||||
# [number=0] Bottom margin [mm]
|
||||
bottom: 0
|
||||
# [number=0] Left margin [mm]
|
||||
left: 0
|
||||
# [number=0] Right margin [mm]
|
||||
right: 0
|
||||
# [number=0] Top margin [mm]
|
||||
top: 0
|
||||
# [boolean=false] Plot mirrored
|
||||
mirror_plot: false
|
||||
# [boolean=false] Invert black and white
|
||||
|
|
@ -2845,6 +2860,13 @@ outputs:
|
|||
# [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
|
||||
# A short-cut to use for simple cases where a variant is an overkill
|
||||
pre_transform: '_none'
|
||||
# [string='kicad_edge'] [kicad_edge,kicad_all] Method used to detect the size of the view box.
|
||||
# The `kicad_edge` method uses the size of the board as reported by KiCad,
|
||||
# components that extend beyond the PCB limit will be cropped. You can manually
|
||||
# adjust the margin to make them visible.
|
||||
# The `kicad_all` method uses the whole size reported by KiCad. Usually includes extra space.
|
||||
# See `limit_viewbox` option
|
||||
size_detection: 'kicad_edge'
|
||||
# [number=0.1] Line width for the sketched pads [mm], see `sketch_pads_on_fab_layers` (KiCad 6+)
|
||||
# Note that this value is currently ignored by KiCad (6.0.9)
|
||||
sketch_pad_line_width: 0.1
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2022 Salvador E. Tropea
|
||||
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2022-2023 Salvador E. Tropea
|
||||
# Copyright (c) 2022-2023 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
# Note about the size:
|
||||
|
|
@ -11,6 +11,10 @@ import re
|
|||
from .. import log
|
||||
|
||||
logger = log.get_logger()
|
||||
SVG_VIEW_BOX_REGEX = r'<svg (.*) width="(.*)" height="(.*)" viewBox="(\S+) (\S+) (\S+) (\S+)"'
|
||||
SVG_VIEW_BOX_SUB_FIX = r'<svg \1 width="\3" height="\2" viewBox="\4 \5 \7 \6"'
|
||||
SVG_VIEW_BOX_REGEX2 = r'width="(.*)" height="(.*)" viewBox="(\S+) (\S+) (\S+) (\S+)"'
|
||||
SVG_VIEW_BOX_SUB_PAT = r'width="{}cm" height="{}cm" viewBox="{} {} {} {}"'
|
||||
|
||||
|
||||
def patch_svg_file(file, remove_bkg=False, is_portrait=False):
|
||||
|
|
@ -22,8 +26,7 @@ def patch_svg_file(file, remove_bkg=False, is_portrait=False):
|
|||
with open(file, 'rt') as f:
|
||||
text = f.read()
|
||||
if not is_portrait:
|
||||
text = re.sub(r'<svg (.*) width="(.*)" height="(.*)" viewBox="(\S+) (\S+) (\S+) (\S+)"',
|
||||
r'<svg \1 width="\3" height="\2" viewBox="\4 \5 \7 \6"', text)
|
||||
text = re.sub(SVG_VIEW_BOX_REGEX, SVG_VIEW_BOX_SUB_FIX, text)
|
||||
if remove_bkg:
|
||||
text = re.sub(r'<rect.*>', '', text)
|
||||
elif not is_portrait:
|
||||
|
|
@ -31,3 +34,12 @@ def patch_svg_file(file, remove_bkg=False, is_portrait=False):
|
|||
r'<rect x="\1" y="\2" width="\4" height="\3"', text)
|
||||
with open(file, 'wt') as f:
|
||||
f.write(text)
|
||||
|
||||
|
||||
def change_svg_viewbox(file, view_box, w, h):
|
||||
with open(file, 'rt') as f:
|
||||
text = f.read()
|
||||
text = re.sub(SVG_VIEW_BOX_REGEX2, SVG_VIEW_BOX_SUB_PAT.format(w, h, view_box[0], view_box[1], view_box[2], view_box[3]),
|
||||
text)
|
||||
with open(file, 'wt') as f:
|
||||
f.write(text)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020-2022 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2020-2023 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
""" Base class for output options """
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ class AnyLayerOptions(VariantOptions):
|
|||
# Restore the eliminated layers
|
||||
if exclude:
|
||||
self.unfilter_pcb_components()
|
||||
self._generated_files = generated
|
||||
|
||||
def solve_extension(self, layer):
|
||||
if self._plot_format == PLOT_FORMAT_GERBER and self.use_protel_extensions:
|
||||
|
|
|
|||
|
|
@ -983,3 +983,27 @@ class VariantOptions(BaseOptions):
|
|||
comps = self.variant.filter(comps)
|
||||
self._sub_pcb = self.variant._sub_pcb
|
||||
self._comps = comps
|
||||
|
||||
|
||||
class PcbMargin(Optionable):
|
||||
""" To adjust each margin """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.left = 0
|
||||
""" Left margin [mm] """
|
||||
self.right = 0
|
||||
""" Right margin [mm] """
|
||||
self.top = 0
|
||||
""" Top margin [mm] """
|
||||
self.bottom = 0
|
||||
""" Bottom margin [mm] """
|
||||
|
||||
@staticmethod
|
||||
def solve(margin):
|
||||
if isinstance(margin, type):
|
||||
return (0, 0, 0, 0)
|
||||
if isinstance(margin, PcbMargin):
|
||||
return (GS.from_mm(margin.left), GS.from_mm(margin.right), GS.from_mm(margin.top), GS.from_mm(margin.bottom))
|
||||
margin = GS.from_mm(margin)
|
||||
return (margin, margin, margin, margin)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ from .misc import (PCBDRAW_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS
|
|||
from .gs import GS
|
||||
from .layer import Layer
|
||||
from .optionable import Optionable
|
||||
from .out_base import VariantOptions
|
||||
from .out_base import VariantOptions, PcbMargin
|
||||
from .macros import macros, document, output_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
|
|
@ -183,21 +183,6 @@ class PcbDrawRemapComponents(Optionable):
|
|||
raise KiPlotConfigurationError("The component remapping must specify a `ref`, a `lib` and a `comp`")
|
||||
|
||||
|
||||
class PcbDrawMargin(Optionable):
|
||||
""" To adjust each margin """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.left = 0
|
||||
""" Left margin [mm] """
|
||||
self.right = 0
|
||||
""" Right margin [mm] """
|
||||
self.top = 0
|
||||
""" Top margin [mm] """
|
||||
self.bottom = 0
|
||||
""" Bottom margin [mm] """
|
||||
|
||||
|
||||
class PcbDrawOptions(VariantOptions):
|
||||
def __init__(self):
|
||||
with document:
|
||||
|
|
@ -248,8 +233,9 @@ class PcbDrawOptions(VariantOptions):
|
|||
""" *[svg,png,jpg,bmp] Output format. Only used if no `output` is specified """
|
||||
self.output = GS.def_global_output
|
||||
""" *Name for the generated file """
|
||||
self.margin = PcbDrawMargin
|
||||
""" [number|dict] Margin around the generated image [mm] """
|
||||
self.margin = PcbMargin
|
||||
""" [number|dict] Margin around the generated image [mm].
|
||||
Using a number the margin is the same in the four directions """
|
||||
self.outline_width = 0.15
|
||||
""" [0,10] Width of the trace to draw the PCB border [mm].
|
||||
Note this also affects the drill holes """
|
||||
|
|
@ -294,14 +280,7 @@ class PcbDrawOptions(VariantOptions):
|
|||
else:
|
||||
self.highlight = self.solve_kf_filters(self.highlight)
|
||||
# Margin
|
||||
if isinstance(self.margin, type):
|
||||
self.margin = (0, 0, 0, 0)
|
||||
elif isinstance(self.margin, PcbDrawMargin):
|
||||
self.margin = (mm2ki(self.margin.left), mm2ki(self.margin.right),
|
||||
mm2ki(self.margin.top), mm2ki(self.margin.bottom))
|
||||
else:
|
||||
margin = mm2ki(self.margin)
|
||||
self.margin = (margin, margin, margin, margin)
|
||||
self.margin = PcbMargin.solve(self.margin)
|
||||
# Filter
|
||||
if isinstance(self.show_components, type):
|
||||
# Default option is 'none'
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020 Salvador E. Tropea
|
||||
# Copyright (c) 2020 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2020-2023 Salvador E. Tropea
|
||||
# Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
|
||||
# Copyright (c) 2018 John Beard
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
# Adapted from: https://github.com/johnbeard/kiplot
|
||||
from pcbnew import (PLOT_FORMAT_SVG, FromMM, ToMM)
|
||||
from .out_any_layer import AnyLayer
|
||||
import os
|
||||
from pcbnew import PLOT_FORMAT_SVG, FromMM, ToMM
|
||||
from .drill_marks import DrillMarks
|
||||
from .gs import GS
|
||||
from .kicad.patch_svg import change_svg_viewbox
|
||||
from .misc import KICAD5_SVG_SCALE
|
||||
from .out_base import PcbMargin
|
||||
from .out_any_layer import AnyLayer
|
||||
from .macros import macros, document, output_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger()
|
||||
|
||||
|
||||
class SVGOptions(DrillMarks):
|
||||
|
|
@ -26,6 +33,19 @@ class SVGOptions(DrillMarks):
|
|||
""" [0,6] Scale factor used to represent 1 mm in the SVG (KiCad 6).
|
||||
The value is how much zeros has the multiplier (1 mm = 10 power `svg_precision` units).
|
||||
Note that for an A4 paper Firefox 91 and Chrome 105 can't handle more than 5 """
|
||||
self.limit_viewbox = False
|
||||
""" When enabled the view box is limited to a selected area """
|
||||
self.size_detection = 'kicad_edge'
|
||||
""" [kicad_edge,kicad_all] Method used to detect the size of the view box.
|
||||
The `kicad_edge` method uses the size of the board as reported by KiCad,
|
||||
components that extend beyond the PCB limit will be cropped. You can manually
|
||||
adjust the margin to make them visible.
|
||||
The `kicad_all` method uses the whole size reported by KiCad. Usually includes extra space.
|
||||
See `limit_viewbox` option """
|
||||
self.margin = PcbMargin
|
||||
""" [number|dict] Margin around the view box [mm].
|
||||
Using a number the margin is the same in the four directions.
|
||||
See `limit_viewbox` option """
|
||||
self._plot_format = PLOT_FORMAT_SVG
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
|
|
@ -44,6 +64,33 @@ class SVGOptions(DrillMarks):
|
|||
self.negative_plot = po.GetNegative()
|
||||
self.mirror_plot = po.GetMirror()
|
||||
|
||||
def config(self, parent):
|
||||
super().config(parent)
|
||||
# Margin
|
||||
self.margin = PcbMargin.solve(self.margin)
|
||||
|
||||
def run(self, output_dir, layers):
|
||||
super().run(output_dir, layers)
|
||||
if not self.limit_viewbox:
|
||||
return
|
||||
# Limit the view box of the SVG
|
||||
bbox = GS.board.ComputeBoundingBox(self.size_detection == 'kicad_edge').getWxRect()
|
||||
# Apply the margin (left right top bottom)
|
||||
bbox = (bbox[0]-self.margin[0], bbox[1]-self.margin[2],
|
||||
bbox[2]+self.margin[0]+self.margin[1], bbox[3]+self.margin[2]+self.margin[3])
|
||||
# Width/height of the used area in cm
|
||||
width = ToMM(bbox[2])*0.1
|
||||
height = ToMM(bbox[3])*0.1
|
||||
# Scale factor to convert KiCad IU to the SVG units
|
||||
mult = KICAD5_SVG_SCALE if GS.ki5 else 10.0 ** (self.svg_precision - 6)
|
||||
# View port in SVG units
|
||||
bbox = tuple(map(lambda x: int(x*mult), bbox))
|
||||
logger.debug('Adjusting SVG viewBox to {} for width {} cm and height {} cm'.format(bbox, width, height))
|
||||
for f in self._generated_files.values():
|
||||
fname = os.path.join(output_dir, f)
|
||||
logger.debugl(2, '- '+f)
|
||||
change_svg_viewbox(fname, bbox, width, height)
|
||||
|
||||
|
||||
@output_class
|
||||
class SVG(AnyLayer):
|
||||
|
|
|
|||
Loading…
Reference in New Issue