Source code for pygerber.parser.pillow.api

# -*- coding: utf-8 -*-
from __future__ import annotations

import sys
from concurrent.futures import Future, ProcessPoolExecutor
from dataclasses import dataclass
from typing import Dict, List

from PIL import Image

import pygerber
from pygerber.parser.pillow.parser import (
    DEFAULT_COLOR_SET_GREEN,
    DEFAULT_COLOR_SET_ORANGE,
    ColorSet,
    ParserWithPillow,
)
from pygerber.parser.project_spec import LayerSpecBase, ProjectSpecBase

NAMED_COLORS = {
    "silk": ColorSet((255, 255, 255, 255)),
    "paste_mask": ColorSet((117, 117, 117, 255)),
    "solder_mask": ColorSet((153, 153, 153, 255)),
    "copper": ColorSet((40, 143, 40, 255), (60, 181, 60, 255)),
    "orange": DEFAULT_COLOR_SET_ORANGE,
    "green": DEFAULT_COLOR_SET_GREEN,
    "debug": ColorSet(
        (120, 120, 255, 255),
        (255, 120, 120, 255),
        (0, 0, 0, 0),
    ),
}
_skip_next_render_cache = False


def _skip_next_render():
    global _skip_next_render_cache
    _skip_next_render_cache = True


def _get_skip_next_render():
    global _skip_next_render_cache
    if _skip_next_render_cache:
        _skip_next_render_cache = False
        return True
    else:
        return False


[docs]class PillowProjectSpec(ProjectSpecBase): dpi: int = 600 ignore_deprecated: bool = True image_padding: int = 0 layers: List[PillowLayerSpec] = [] @property def LayerSpecClass(self) -> PillowLayerSpec: return PillowLayerSpec
[docs] def render(self) -> Image.Image: if _get_skip_next_render() is False: return self._join_layers(self._render_layers())
def _join_layers(self, layer_images: List[Image.Image]) -> Image.Image: bottom_most_layer = layer_images[0].copy() base_size = bottom_most_layer.size for layer in layer_images[1:]: self.__paste_layer(base_size, layer, bottom_most_layer) return bottom_most_layer @staticmethod def __paste_layer(base_size, layer, bottom_most_layer): base_width, base_height = base_size width, height = layer.size delta_width = (width - base_width) // 2 delta_height = (height - base_height) // 2 bottom_most_layer.paste(layer, (delta_width, delta_height), layer) def _render_layers(self) -> List[Image.Image]: NEW_PATH = list(sys.path) sys.path = pygerber.SYS_PATH with ProcessPoolExecutor() as executor: processes = self.__submit_rendering_processes(executor) results = self.__get_rendering_processes_results(processes) sys.path = NEW_PATH return results def __submit_rendering_processes(self, executor: ProcessPoolExecutor): processes: List[Future] = [] for layer in self.layers: future = executor.submit( _render_file, layer.file_path, dpi=self.dpi, colors=layer.colors, ignore_deprecated=self.ignore_deprecated, image_padding=self.image_padding, ) processes.append(future) return processes @staticmethod def __get_rendering_processes_results(processes): results: List[Image.Image] = [] for future in processes: rendered_image: Image.Image = future.result() results.append(rendered_image) return results
[docs]@dataclass class PillowLayerSpec(LayerSpecBase): file_path: str colors: ColorSet
[docs] @classmethod def load(cls, contents: Dict): file_path = cls._get_checked_file_path(contents) colors = cls.__load_colors(contents) colors = cls._replace_none_color_with_named_color_based_on_file_name( colors, file_path, NAMED_COLORS ) return cls(file_path, colors)
@staticmethod def __load_colors(contents): colors = contents.get("colors", None) if isinstance(colors, str): colors = NAMED_COLORS.get(colors) elif isinstance(colors, dict): colors = ColorSet( tuple(colors.get("dark")), tuple(colors.get("clear", (0, 0, 0, 0))), tuple(colors.get("background", (0, 0, 0, 0))), ) elif isinstance(colors, list): colors = ColorSet( tuple(colors[0]), tuple(colors[1] if 1 < len(colors) else (0, 0, 0, 0)), tuple(colors[2] if 2 < len(colors) else (0, 0, 0, 0)), ) elif colors is None: pass else: raise TypeError(f"Invalid color specification for LayerSpec: {colors}") return colors
def _render_file( file_path: str, dpi: int = 600, colors: ColorSet = DEFAULT_COLOR_SET_GREEN, ignore_deprecated: bool = True, image_padding: int = 0, ): parser = ParserWithPillow( dpi=dpi, colors=colors, ignore_deprecated=ignore_deprecated, image_padding=image_padding, ) parser.parse_file(file_path) return parser.get_image()