KiBot/kibot/out_svg.py

111 lines
4.6 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# Copyright (c) 2018 John Beard
# License: AGPL-3.0
# Project: KiBot (formerly KiPlot)
# Adapted from: https://github.com/johnbeard/kiplot
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 W_ESCINV, FONT_HELP_TEXT
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):
def __init__(self):
super().__init__()
with document:
self.line_width = 0.25
""" [0.02,2] For objects without width [mm] (KiCad 5) """
self.mirror_plot = False
""" Plot mirrored """
self.negative_plot = False
""" Invert black and white """
self.svg_precision = 4
""" [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.
This option can't be enabled when using a scale """
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):
super()._configure_plot_ctrl(po, output_dir)
po.SetMirror(self.mirror_plot)
if GS.ki5:
po.SetLineWidth(FromMM(self.line_width))
po.SetNegative(self.negative_plot)
GS.SetSvgPrecision(po, self.svg_precision)
def read_vals_from_po(self, po):
super().read_vals_from_po(po)
if GS.ki5:
self.line_width = ToMM(po.GetLineWidth())
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
if self.scaling != 1:
logger.warning(W_ESCINV+"Scaling and view port limit can't be mixed")
return
# Limit the view box of the SVG
bbox = GS.get_rect_for(GS.board.ComputeBoundingBox(self.size_detection == 'kicad_edge'))
# 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
bbox = GS.iu_to_svg(bbox, self.svg_precision)
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):
""" SVG (Scalable Vector Graphics)
Exports the PCB to a format suitable for 2D graphics software.
Unlike bitmaps SVG drawings can be scaled without losing resolution.
This output is what you get from the File/Plot menu in pcbnew.
The `pcb_print` is usually a better alternative. """
__doc__ += FONT_HELP_TEXT
def __init__(self):
super().__init__()
with document:
self.options = SVGOptions
""" *[dict] Options for the `svg` output """
self._category = 'PCB/docs'