diff --git a/CHANGELOG.md b/CHANGELOG.md index ab761c69..4a6c91ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Diff: - Option to compare only the first schematic page. (See #319) +- PcbDraw: + - Support for BMP output ### Changed - Diff: diff --git a/README.md b/README.md index 4595866c..e6687ee0 100644 --- a/README.md +++ b/README.md @@ -173,16 +173,18 @@ Notes: - Create outputs preview for `navigate_results` - Create PNG icons for `navigate_results` - Create PDF, PNG, PS and EPS formats for `pcb_print` - -[**Ghostscript**](https://www.ghostscript.com/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://www.ghostscript.com/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/ghostscript) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) -- Optional to: - - Create outputs preview for `navigate_results` - - Create PNG, PS and EPS formats for `pcb_print` + - Create PNG, JPG and BMP images for `pcbdraw` [**ImageMagick**](https://imagemagick.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://imagemagick.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/imagemagick) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) - Optional to: - Create outputs preview for `navigate_results` - Create monochrome prints and scaled PNG files for `pcb_print` + - Create JPG and BMP images for `pcbdraw` + +[**Ghostscript**](https://www.ghostscript.com/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://www.ghostscript.com/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/ghostscript) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) +- Optional to: + - Create outputs preview for `navigate_results` + - Create PNG, PS and EPS formats for `pcb_print` [**Pandoc**](https://pandoc.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://pandoc.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/pandoc) - Optional to create PDF/ODF/DOCX files for `report` @@ -2557,7 +2559,7 @@ Notes: - **`options`**: [dict] Options for the `pcbdraw` output. * Valid keys: - **`bottom`**: [boolean=false] Render the bottom side of the board (default is top side). - - **`format`**: [string='svg'] [svg,png,jpg] Output format. Only used if no `output` is specified. + - **`format`**: [string='svg'] [svg,png,jpg,bmp] Output format. Only used if no `output` is specified. - **`mirror`**: [boolean=false] Mirror the board. - **`output`**: [string='%f-%i%I%v.%x'] Name for the generated file. Affected by global options. - **`show_components`**: [list(string)|string=none] [none,all] List of components to draw, can be also a string for none or all. diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index d5ade69e..f105ab0f 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -1356,7 +1356,7 @@ outputs: dnf_filter: '_none' # [number=300] [10,1200] Dots per inch (resolution) of the generated image dpi: 300 - # [string='svg'] [svg,png,jpg] Output format. Only used if no `output` is specified + # [string='svg'] [svg,png,jpg,bmp] Output format. Only used if no `output` is specified format: 'svg' # [list(string)=[]] List of components to highlight highlight: [] diff --git a/kibot/PcbDraw/README.md b/kibot/PcbDraw/README.md index ab25cbcc..4ec1eec2 100644 --- a/kibot/PcbDraw/README.md +++ b/kibot/PcbDraw/README.md @@ -17,6 +17,61 @@ Currently only the `plot` module is included. ### convert.py - Made the `pcbdraw` import relative +- Removed PIL as dependency + - So now the save function only supports SVG as input and SVG/PNG as output + - All other cases are handled from outside + - This is because KiBot heavily uses ImageMagick and migrating to PIL is not something simple + - There is no point in using PIL just for file conversion, as we don't use `render` this is the only use + +```diff +diff --git a/kibot/PcbDraw/convert.py b/kibot/PcbDraw/convert.py +index ba856a69..7fe64738 100644 +--- a/kibot/PcbDraw/convert.py ++++ b/kibot/PcbDraw/convert.py +@@ -6,7 +6,7 @@ import textwrap + import os + from typing import Union + from tempfile import TemporaryDirectory +-from PIL import Image ++# from PIL import Image + from lxml.etree import _ElementTree # type: ignore + + # Converting SVG to bitmap is a hard problem. We used Wand (and thus +@@ -66,17 +66,17 @@ def svgToPng(inputFilename: str, outputFilename: str, dpi: int=300) -> None: + message += textwrap.indent(m, " ") + raise RuntimeError(message) + +-def save(image: Union[_ElementTree, Image.Image], filename: str, dpi: int=600) -> None: ++def save(image: _ElementTree, filename: str, dpi: int=600, format: str=None) -> None: + """ + Given an SVG tree or an image, save to a filename. The format is deduced + from the extension. + """ +- ftype = os.path.splitext(filename)[1][1:].lower() +- if isinstance(image, Image.Image): +- if ftype not in ["jpg", "jpeg", "png", "bmp"]: +- raise TypeError(f"Cannot save bitmap image into {ftype}") +- image.save(filename) +- return ++ ftype = os.path.splitext(filename)[1][1:].lower() if format is None else format ++# if isinstance(image, Image.Image): ++# if ftype not in ["jpg", "jpeg", "png", "bmp"]: ++# raise TypeError(f"Cannot save bitmap image into {ftype}") ++# image.save(filename) ++# return + if isinstance(image, _ElementTree): + if ftype == "svg": + image.write(filename) +@@ -91,6 +91,6 @@ def save(image: Union[_ElementTree, Image.Image], filename: str, dpi: int=600) - + svgToPng(svg_filename, png_filename, dpi=dpi) + if ftype == "png": + return +- Image.open(png_filename).convert("RGB").save(filename) +- return ++# Image.open(png_filename).convert("RGB").save(filename) ++# return + raise TypeError(f"Unknown image type: {type(image)}") +``` ### convert_common.py @@ -32,7 +87,9 @@ No current changes ### unit.py -No current changes +- Replaced `unit` code. + - So we have only one units conversion + - I think the only difference is that KiBot code currently supports the locales decimal point ### plot.py @@ -183,6 +240,3 @@ index f8990722..17f90185 100644 - Find the index of the smaller element (1 line code) - I added a replacemt for the `array` function, it just makes all matrix elements float -- Replaced `unit` code. - - So we have only one units conversion - - I think the only difference is that KiBot code currently supports the locales decimal point diff --git a/kibot/PcbDraw/convert.py b/kibot/PcbDraw/convert.py index ba856a69..7fe64738 100644 --- a/kibot/PcbDraw/convert.py +++ b/kibot/PcbDraw/convert.py @@ -6,7 +6,7 @@ import textwrap import os from typing import Union from tempfile import TemporaryDirectory -from PIL import Image +# from PIL import Image from lxml.etree import _ElementTree # type: ignore # Converting SVG to bitmap is a hard problem. We used Wand (and thus @@ -66,17 +66,17 @@ def svgToPng(inputFilename: str, outputFilename: str, dpi: int=300) -> None: message += textwrap.indent(m, " ") raise RuntimeError(message) -def save(image: Union[_ElementTree, Image.Image], filename: str, dpi: int=600) -> None: +def save(image: _ElementTree, filename: str, dpi: int=600, format: str=None) -> None: """ Given an SVG tree or an image, save to a filename. The format is deduced from the extension. """ - ftype = os.path.splitext(filename)[1][1:].lower() - if isinstance(image, Image.Image): - if ftype not in ["jpg", "jpeg", "png", "bmp"]: - raise TypeError(f"Cannot save bitmap image into {ftype}") - image.save(filename) - return + ftype = os.path.splitext(filename)[1][1:].lower() if format is None else format +# if isinstance(image, Image.Image): +# if ftype not in ["jpg", "jpeg", "png", "bmp"]: +# raise TypeError(f"Cannot save bitmap image into {ftype}") +# image.save(filename) +# return if isinstance(image, _ElementTree): if ftype == "svg": image.write(filename) @@ -91,6 +91,6 @@ def save(image: Union[_ElementTree, Image.Image], filename: str, dpi: int=600) - svgToPng(svg_filename, png_filename, dpi=dpi) if ftype == "png": return - Image.open(png_filename).convert("RGB").save(filename) - return +# Image.open(png_filename).convert("RGB").save(filename) +# return raise TypeError(f"Unknown image type: {type(image)}") diff --git a/kibot/out_pcbdraw.py b/kibot/out_pcbdraw.py index d77cc1e7..5afbfd0a 100644 --- a/kibot/out_pcbdraw.py +++ b/kibot/out_pcbdraw.py @@ -3,19 +3,18 @@ # Copyright (c) 2020-2022 Instituto Nacional de TecnologĂ­a Industrial # License: GPL-3.0 # Project: KiBot (formerly KiPlot) -# TODO: PIL dependency? # TODO: Package resources # TODO: wxApp messages -# """ -# Dependencies: -# - from: RSVG -# role: Create PNG and JPG images -# - from: ImageMagick -# role: Create JPG images -# - from: PcbDraw -# role: mandatory -# """ +""" +Dependencies: + - from: RSVG + role: Create PNG, JPG and BMP images + - from: ImageMagick + role: Create JPG and BMP images +""" import os +import shlex +import subprocess from tempfile import NamedTemporaryFile # Here we import the whole module to make monkeypatch work from .error import KiPlotConfigurationError @@ -37,6 +36,26 @@ def pcbdraw_warnings(tag, msg): logger.warning('{}({}) {}'.format(W_PCBDRAW, tag, msg)) +def _get_tmp_name(ext): + with NamedTemporaryFile(mode='w', suffix=ext, delete=False) as f: + f.close() + return f.name + + +def _run_command(cmd): + logger.debug('Executing: '+shlex.join(cmd)) + try: + cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + logger.error('Failed to run %s, error %d', cmd[0], e.returncode) + if e.output: + logger.debug('Output from command: '+e.output.decode()) + exit(PCBDRAW_ERR) + out = cmd_output.decode() + if out.strip(): + logger.debug('Output from command:\n'+out) + + class PcbDrawStyle(Optionable): def __init__(self): super().__init__() @@ -140,7 +159,7 @@ class PcbDrawOptions(VariantOptions): self.dpi = 300 """ [10,1200] Dots per inch (resolution) of the generated image """ self.format = 'svg' - """ *[svg,png,jpg] Output format. Only used if no `output` is specified """ + """ *[svg,png,jpg,bmp] Output format. Only used if no `output` is specified """ self.output = GS.def_global_output """ *Name for the generated file """ super().__init__() @@ -260,6 +279,19 @@ class PcbDrawOptions(VariantOptions): def run(self, name): super().run(name) + # Select a name and format that PcbDraw can handle + save_output_name = name + save_output_format = self.format + self.convert_command = None + # Check we have the tools needed for the output format + if self.format != 'svg': + # We need RSVG for anything other than SVG + self.ensure_tool('RSVG') + # We need ImageMagick for anything other than SVG and PNG + if self.format != 'png': + self.convert_command = self.ensure_tool('ImageMagick') + save_output_name = _get_tmp_name('.png') + save_output_format = 'png' try: # TODO: Avoid loading the PCB again @@ -311,7 +343,15 @@ class PcbDrawOptions(VariantOptions): if tmp_style: os.remove(tmp_style) - save(image, name, self.dpi) + save(image, save_output_name, self.dpi, format=save_output_format) + # Do we need to convert the saved file? + if self.convert_command is not None: + cmd = [self.convert_command, save_output_name] + if self.format == 'jpg': + cmd += ['-quality', '85%'] + cmd.append(name) + _run_command(cmd) + os.remove(save_output_name) return diff --git a/src/kibot-check b/src/kibot-check index 06f3cd74..84eaa1e1 100755 --- a/src/kibot-check +++ b/src/kibot-check @@ -159,7 +159,7 @@ deps = '{\ ],\ "extra_deb": null,\ "help_option": "--version",\ - "importance": 2,\ + "importance": 3,\ "in_debian": true,\ "is_kicad_plugin": false,\ "is_python": false,\ @@ -183,6 +183,13 @@ deps = '{\ "max_version": null,\ "output": "pcb_print",\ "version": null\ + },\ + {\ + "desc": "Create JPG and BMP images",\ + "mandatory": false,\ + "max_version": null,\ + "output": "pcbdraw",\ + "version": null\ }\ ],\ "tests": [],\ @@ -719,7 +726,7 @@ deps = '{\ "extra_arch": null,\ "extra_deb": null,\ "help_option": "--version",\ - "importance": 3,\ + "importance": 4,\ "in_debian": true,\ "is_kicad_plugin": false,\ "is_python": false,\ @@ -750,6 +757,13 @@ deps = '{\ "max_version": null,\ "output": "pcb_print",\ "version": null\ + },\ + {\ + "desc": "Create PNG, JPG and BMP images",\ + "mandatory": false,\ + "max_version": null,\ + "output": "pcbdraw",\ + "version": null\ }\ ],\ "tests": [\