Finished adding all the plot options to `pcb_print`

- Now is you can get most of the good things from both: print and plot
  in the same output.
- Is usually much faster than `pdf_pcb_print`
This commit is contained in:
Salvador E. Tropea 2022-04-08 15:01:10 -03:00
parent 3d8e842323
commit 3752bcb53e
8 changed files with 744 additions and 93 deletions

View File

@ -1530,23 +1530,41 @@ Next time you need this list just use an alias, like this:
- `name`: [string=''] Used to identify this particular output definition.
- `options`: [dict] Options for the `pcb_print` output.
* Valid keys:
- `color_theme`: [string='_builtin_classic'] Selects the color theme. Only applies to KiCad 6.
To use the KiCad 6 default colors select `_builtin_default`.
Usually user colors are stored as `user`, but you can give it another name.
- `dnf_filter`: [string|list(string)='_none'] Name of the filter to mark components as not fitted.
A short-cut to use for simple cases where a variant is an overkill.
- `drill_marks`: [string='full'] What to use to indicate the drill places, can be none, small or full (for real scale).
- `hide_excluded`: [boolean=false] Hide components in the Fab layer that are marked as excluded by a variant.
- `output`: [string='%f-%i%I%v.%x'] Filename for the output PDF (%i=assembly, %x=pdf). Affected by global options.
- *output_name*: Alias for output.
- `pages`: [list(dict)] List of pages to include in the output document.
Each page contains one or more layers of the PCB.
* Valid keys:
- `exclude_pads_from_silkscreen`: [boolean=false] Do not plot the component pads in the silk screen (KiCad 5.x only).
- `layers`: [list(dict)] List of layers printed in this page. Order is important, the last goes on top.
* Valid keys:
- `color`: [string=''] Color used for this layer.
- `description`: [string=''] A description for the layer, for documentation purposes.
- `force_plot_invisible_refs_vals`: [boolean=false] Include references and values even when they are marked as invisible.
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
- `plot_footprint_refs`: [boolean=true] Include the footprint references.
- `plot_footprint_values`: [boolean=true] Include the footprint values.
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
- `line_width`: [number=0.1] [0.02,2] For objects without width [mm] (KiCad 5).
- `mirror`: [boolean=false] Print mirrored (X axis inverted).
- `monochrome`: [boolean=false] Print in black and white.
- `sheet_reference_layer`: [string=''] Layer to plot the page frame.
- `monochrome`: [boolean=false] Print in gray scale.
- `negative_plot`: [boolean=false] Invert black and white. Only useful for a single layer.
- `scaling`: [number=1.0] Scale factor (0 means autoscaling).
- `sheet`: [string='Assembly'] Text to use for the `sheet` in the title block.
- `sheet_reference_color`: [string=''] Color to use for the frame and title block.
- `tent_vias`: [boolean=true] Cover the vias.
- `title`: [string=''] Text used to replace the sheet title. %VALUE expansions are allowed.
If it starts with `+` the text is concatenated.
- `plot_sheet_reference`: [boolean=true] Include the title-block.
- `title`: [string=''] Text used to replace the sheet title. %VALUE expansions are allowed.
If it starts with `+` the text is concatenated.
- `variant`: [string=''] Board variant to apply.
- `output_id`: [string=''] Text to use for the %I expansion content. To differentiate variations of this output.
- `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested.

View File

@ -957,9 +957,15 @@ outputs:
type: 'pcb_print'
dir: 'Example/pcb_print_dir'
options:
# [string='_builtin_classic'] Selects the color theme. Only applies to KiCad 6.
# To use the KiCad 6 default colors select `_builtin_default`.
# Usually user colors are stored as `user`, but you can give it another name
color_theme: '_builtin_classic'
# [string|list(string)='_none'] Name of the filter to mark components as not fitted.
# A short-cut to use for simple cases where a variant is an overkill
dnf_filter: '_none'
# [string='full'] What to use to indicate the drill places, can be none, small or full (for real scale)
drill_marks: 'full'
# [boolean=false] Hide components in the Fab layer that are marked as excluded by a variant
hide_excluded: false
# [string='%f-%i%I%v.%x'] Filename for the output PDF (%i=assembly, %x=pdf). Affected by global options
@ -968,22 +974,48 @@ outputs:
# [list(dict)] List of pages to include in the output document.
# Each page contains one or more layers of the PCB
pages:
# [list(dict)] List of layers printed in this page. Order is important, the last goes on top
- layers:
# [boolean=false] Do not plot the component pads in the silk screen (KiCad 5.x only)
- exclude_pads_from_silkscreen: false
# [list(dict)] List of layers printed in this page. Order is important, the last goes on top
layers:
# [string=''] Color used for this layer
- color: ''
# [string=''] A description for the layer, for documentation purposes
description: ''
# [boolean=false] Include references and values even when they are marked as invisible
force_plot_invisible_refs_vals: false
# [string=''] Name of the layer. As you see it in KiCad
layer: ''
# [boolean=true] Include the footprint references
plot_footprint_refs: true
# [boolean=true] Include the footprint values
plot_footprint_values: true
# [string=''] Suffix used in file names related to this layer. Derived from the name if not specified
suffix: ''
# [number=0.1] [0.02,2] For objects without width [mm] (KiCad 5)
line_width: 0.1
# [boolean=false] Print mirrored (X axis inverted)
mirror: false
# [boolean=false] Print in black and white
# [boolean=false] Print in gray scale
monochrome: false
# [string=''] Layer to plot the page frame
sheet_reference_layer: ''
# [boolean=false] Invert black and white. Only useful for a single layer
negative_plot: false
# [number=1.0] Scale factor (0 means autoscaling)
scaling: 1.0
# [string='Assembly'] Text to use for the `sheet` in the title block
sheet: 'Assembly'
# [string=''] Color to use for the frame and title block
sheet_reference_color: ''
# [boolean=true] Cover the vias
tent_vias: true
# [string=''] Text used to replace the sheet title. %VALUE expansions are allowed.
# If it starts with `+` the text is concatenated
title: ''
# [boolean=true] Include the title-block
plot_sheet_reference: true
# [string=''] Text used to replace the sheet title. %VALUE expansions are allowed.
# If it starts with `+` the text is concatenated
title: ''
# [string=''] Board variant to apply
variant: ''
# PcbDraw - Beautiful 2D PCB render:

View File

@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
"""
KiCad 6 color theme loader
"""
import os
import json
from pcbnew import BOARD, PCBNEW_LAYER_ID_START, PCB_LAYER_ID_COUNT
from ..gs import GS
from ..misc import W_COLORTHEME, W_WRONGCOLOR
from .config import KiConf
from .. import log
logger = log.get_logger()
BUILT_IN = {'_builtin_classic', '_builtin_default'}
KI6_KI5 = {'b_adhesive': 'b_adhes',
'f_adhesive': 'f_adhes',
'b_silkscreen': 'b_silks',
'f_silkscreen': 'f_silks',
'user_drawings': 'dwgs_user',
'user_comments': 'cmts_user',
'user_eco1': 'eco1_user',
'user_eco2': 'eco2_user',
'b_courtyard': 'b_crtyd',
'f_courtyard': 'f_crtyd'}
class KiCadColors(object):
def __init__(self):
self.layer_id2color = {}
self.pcb_frame = "#480000"
def parse_color(val):
if val.startswith('rgb('):
vals = val[4:-1].split(',')
elif val.startswith('rgba('):
vals = val[5:-1].split(',')
else:
logger.warning(W_WRONGCOLOR+"Wrong KiCad color: {}".format(val))
return "#000000"
res = '#'
for c, v in enumerate(vals):
res += '%02X' % (int(v) if c < 3 else int(float(v)*255))
return res
def load_color_theme(name):
logger.debug('Looking for color theme `{}`'.format(name))
is_built_in = name in BUILT_IN
if not is_built_in and GS.ki5():
logger.warning(W_COLORTHEME, "KiCad 5 doesn't support color themes ({})".format(name))
return None
if is_built_in:
fn = os.path.join(os.path.dirname(__file__), '..', 'kicad_colors', name+'.json')
else:
KiConf.init(GS.pcb_file)
fn = os.path.join(KiConf.config_dir, 'colors', name+'.json')
if not os.path.isfile(fn):
logger.warning(W_COLORTHEME, "Missing color theme: {}".format(fn))
return None
with open(fn, 'rt') as f:
text = f.read()
data = json.loads(text)
c = KiCadColors()
cl = c.layer_id2color
board = data['board']
copper = board['copper']
extra_debug = GS.debug_level >= 3
for id in range(PCBNEW_LAYER_ID_START, PCBNEW_LAYER_ID_START+PCB_LAYER_ID_COUNT):
c_name = c_name_ori = BOARD.GetStandardLayerName(id)
c_name = c_name.lower()
if c_name == 'rescue':
continue
if c_name.endswith('.cu'):
c_name = c_name[:-3]
if c_name in copper:
cl[id] = parse_color(copper[c_name])
else:
logger.warning(W_WRONGCOLOR+"The `{}` theme doesn't define a color for the {} layer".format(name, c_name_ori))
else:
c_name = c_name.replace('.', '_')
c_name = KI6_KI5.get(c_name, c_name)
if c_name in board:
cl[id] = parse_color(board[c_name])
else:
logger.warning(W_WRONGCOLOR+"The `{}` theme doesn't define a color for the {} layer".format(name, c_name_ori))
if extra_debug:
logger.debug('- Color for layer {} ({}): {}'.format(c_name_ori, id, cl[id]))
# Title block and frame color
if 'worksheet' in board:
c.pcb_frame = parse_color(board['worksheet'])
return c

View File

@ -0,0 +1,225 @@
{
"3d_viewer": {
"background_bottom": "rgb(102, 102, 128)",
"background_top": "rgb(204, 204, 230)",
"board": "rgba(51, 43, 23, 0.902)",
"copper": "rgb(179, 156, 0)",
"silkscreen_bottom": "rgb(230, 230, 230)",
"silkscreen_top": "rgb(230, 230, 230)",
"soldermask_bottom": "rgba(20, 51, 36, 0.831)",
"soldermask_top": "rgba(20, 51, 36, 0.831)",
"solderpaste": "rgb(128, 128, 128)",
"use_board_stackup_colors": true
},
"board": {
"anchor": "rgb(0, 0, 132)",
"aux_items": "rgb(255, 255, 255)",
"b_adhes": "rgb(0, 0, 132)",
"b_crtyd": "rgb(132, 132, 132)",
"b_fab": "rgb(0, 0, 132)",
"b_mask": "rgb(132, 132, 0)",
"b_paste": "rgb(0, 194, 194)",
"b_silks": "rgb(132, 0, 132)",
"background": "rgb(0, 0, 0)",
"cmts_user": "rgb(0, 0, 132)",
"copper": {
"b": "rgb(0, 132, 0)",
"f": "rgb(132, 0, 0)",
"in1": "rgb(194, 194, 0)",
"in10": "rgb(132, 0, 132)",
"in11": "rgb(132, 0, 0)",
"in12": "rgb(132, 132, 0)",
"in13": "rgb(194, 194, 194)",
"in14": "rgb(0, 0, 132)",
"in15": "rgb(0, 132, 0)",
"in16": "rgb(132, 0, 0)",
"in17": "rgb(194, 194, 0)",
"in18": "rgb(194, 0, 194)",
"in19": "rgb(194, 0, 0)",
"in2": "rgb(194, 0, 194)",
"in20": "rgb(0, 132, 132)",
"in21": "rgb(0, 132, 0)",
"in22": "rgb(0, 0, 132)",
"in23": "rgb(132, 132, 132)",
"in24": "rgb(132, 0, 132)",
"in25": "rgb(194, 194, 194)",
"in26": "rgb(132, 0, 132)",
"in27": "rgb(132, 0, 0)",
"in28": "rgb(132, 132, 0)",
"in29": "rgb(194, 194, 194)",
"in3": "rgb(194, 0, 0)",
"in30": "rgb(0, 0, 132)",
"in4": "rgb(0, 132, 132)",
"in5": "rgb(0, 132, 0)",
"in6": "rgb(0, 0, 132)",
"in7": "rgb(132, 132, 132)",
"in8": "rgb(132, 0, 132)",
"in9": "rgb(194, 194, 194)"
},
"cursor": "rgb(255, 255, 255)",
"drc_error": "rgba(255, 0, 0, 0.800)",
"drc_exclusion": "rgba(255, 255, 255, 0.800)",
"drc_warning": "rgba(255, 208, 66, 0.800)",
"dwgs_user": "rgb(194, 194, 194)",
"eco1_user": "rgb(0, 132, 0)",
"eco2_user": "rgb(194, 194, 0)",
"edge_cuts": "rgb(194, 194, 0)",
"f_adhes": "rgb(132, 0, 132)",
"f_crtyd": "rgb(194, 194, 194)",
"f_fab": "rgb(132, 132, 132)",
"f_mask": "rgb(132, 0, 132)",
"f_paste": "rgb(132, 0, 0)",
"f_silks": "rgb(0, 132, 132)",
"footprint_text_invisible": "rgb(194, 194, 194)",
"grid": "rgb(132, 132, 132)",
"grid_axes": "rgb(0, 0, 132)",
"margin": "rgb(194, 0, 194)",
"no_connect": "rgb(0, 0, 132)",
"pad_plated_hole": "rgb(194, 194, 0)",
"pad_through_hole": "rgb(194, 194, 0)",
"plated_hole": "rgb(194, 194, 0)",
"ratsnest": "rgb(255, 255, 255)",
"user_1": "rgb(0, 0, 132)",
"user_2": "rgb(0, 0, 132)",
"user_3": "rgb(0, 0, 132)",
"user_4": "rgb(0, 0, 132)",
"user_5": "rgb(0, 0, 132)",
"user_6": "rgb(0, 0, 132)",
"user_7": "rgb(0, 0, 132)",
"user_8": "rgb(0, 0, 132)",
"user_9": "rgb(0, 0, 132)",
"via_blind_buried": "rgb(132, 132, 0)",
"via_hole": "rgba(128, 102, 0, 0.800)",
"via_micro": "rgb(0, 132, 132)",
"via_through": "rgb(194, 194, 194)",
"worksheet": "rgb(72, 0, 0)"
},
"gerbview": {
"axes": "rgb(0, 0, 132)",
"background": "rgb(0, 0, 0)",
"dcodes": "rgb(255, 255, 255)",
"grid": "rgb(132, 132, 132)",
"layers": [
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)"
],
"negative_objects": "rgb(132, 132, 132)",
"worksheet": "rgb(0, 0, 132)"
},
"meta": {
"name": "A",
"version": 3
},
"palette": [
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)"
],
"schematic": {
"anchor": "rgb(0, 0, 255)",
"aux_items": "rgb(0, 0, 0)",
"background": "rgb(245, 244, 239)",
"brightened": "rgb(255, 0, 255)",
"bus": "rgb(0, 0, 132)",
"bus_junction": "rgb(0, 0, 132)",
"component_body": "rgb(255, 255, 194)",
"component_outline": "rgb(132, 0, 0)",
"cursor": "rgb(15, 15, 15)",
"erc_error": "rgba(230, 9, 13, 0.800)",
"erc_warning": "rgba(209, 146, 0, 0.800)",
"fields": "rgb(132, 0, 132)",
"grid": "rgb(181, 181, 181)",
"grid_axes": "rgb(0, 0, 132)",
"hidden": "rgb(94, 194, 194)",
"junction": "rgb(0, 150, 0)",
"label_global": "rgb(132, 0, 0)",
"label_hier": "rgb(114, 86, 0)",
"label_local": "rgb(15, 15, 15)",
"no_connect": "rgb(0, 0, 132)",
"note": "rgb(0, 0, 194)",
"override_item_colors": false,
"pin": "rgb(132, 0, 0)",
"pin_name": "rgb(0, 100, 100)",
"pin_number": "rgb(169, 0, 0)",
"reference": "rgb(0, 100, 100)",
"shadow": "rgba(102, 179, 255, 0.800)",
"sheet": "rgb(132, 0, 0)",
"sheet_background": "rgba(255, 255, 255, 0.000)",
"sheet_fields": "rgb(132, 0, 132)",
"sheet_filename": "rgb(114, 86, 0)",
"sheet_label": "rgb(0, 100, 100)",
"sheet_name": "rgb(0, 100, 100)",
"value": "rgb(0, 100, 100)",
"wire": "rgb(0, 150, 0)",
"worksheet": "rgb(132, 0, 0)"
}
}

View File

@ -0,0 +1,225 @@
{
"3d_viewer": {
"background_bottom": "rgb(102, 102, 128)",
"background_top": "rgb(204, 204, 230)",
"board": "rgba(51, 43, 23, 0.902)",
"copper": "rgb(179, 156, 0)",
"silkscreen_bottom": "rgb(230, 230, 230)",
"silkscreen_top": "rgb(230, 230, 230)",
"soldermask_bottom": "rgba(20, 51, 36, 0.831)",
"soldermask_top": "rgba(20, 51, 36, 0.831)",
"solderpaste": "rgb(128, 128, 128)",
"use_board_stackup_colors": true
},
"board": {
"anchor": "rgb(255, 38, 226)",
"aux_items": "rgb(255, 255, 255)",
"b_adhes": "rgb(0, 0, 132)",
"b_crtyd": "rgb(38, 233, 255)",
"b_fab": "rgb(88, 93, 132)",
"b_mask": "rgba(2, 255, 238, 0.400)",
"b_paste": "rgba(0, 194, 194, 0.902)",
"b_silks": "rgb(232, 178, 167)",
"background": "rgb(0, 16, 35)",
"cmts_user": "rgb(89, 148, 220)",
"copper": {
"b": "rgb(77, 127, 196)",
"f": "rgb(200, 52, 52)",
"in1": "rgb(127, 200, 127)",
"in10": "rgb(237, 124, 51)",
"in11": "rgb(91, 195, 235)",
"in12": "rgb(247, 111, 142)",
"in13": "rgb(167, 165, 198)",
"in14": "rgb(40, 204, 217)",
"in15": "rgb(232, 178, 167)",
"in16": "rgb(242, 237, 161)",
"in17": "rgb(237, 124, 51)",
"in18": "rgb(91, 195, 235)",
"in19": "rgb(247, 111, 142)",
"in2": "rgb(206, 125, 44)",
"in20": "rgb(167, 165, 198)",
"in21": "rgb(40, 204, 217)",
"in22": "rgb(232, 178, 167)",
"in23": "rgb(242, 237, 161)",
"in24": "rgb(237, 124, 51)",
"in25": "rgb(91, 195, 235)",
"in26": "rgb(247, 111, 142)",
"in27": "rgb(167, 165, 198)",
"in28": "rgb(40, 204, 217)",
"in29": "rgb(232, 178, 167)",
"in3": "rgb(79, 203, 203)",
"in30": "rgb(242, 237, 161)",
"in4": "rgb(219, 98, 139)",
"in5": "rgb(167, 165, 198)",
"in6": "rgb(40, 204, 217)",
"in7": "rgb(232, 178, 167)",
"in8": "rgb(242, 237, 161)",
"in9": "rgb(141, 203, 129)"
},
"cursor": "rgb(255, 255, 255)",
"drc_error": "rgba(215, 91, 107, 0.800)",
"drc_exclusion": "rgba(255, 255, 255, 0.800)",
"drc_warning": "rgba(255, 208, 66, 0.800)",
"dwgs_user": "rgb(194, 194, 194)",
"eco1_user": "rgb(180, 219, 210)",
"eco2_user": "rgb(216, 200, 82)",
"edge_cuts": "rgb(208, 210, 205)",
"f_adhes": "rgb(132, 0, 132)",
"f_crtyd": "rgb(255, 38, 226)",
"f_fab": "rgb(175, 175, 175)",
"f_mask": "rgba(216, 100, 255, 0.400)",
"f_paste": "rgba(180, 160, 154, 0.902)",
"f_silks": "rgb(242, 237, 161)",
"footprint_text_invisible": "rgb(132, 132, 132)",
"grid": "rgb(132, 132, 132)",
"grid_axes": "rgb(194, 194, 194)",
"margin": "rgb(255, 38, 226)",
"no_connect": "rgb(0, 0, 132)",
"pad_plated_hole": "rgb(194, 194, 0)",
"pad_through_hole": "rgb(227, 183, 46)",
"plated_hole": "rgb(26, 196, 210)",
"ratsnest": "rgba(245, 255, 213, 0.702)",
"user_1": "rgb(194, 194, 194)",
"user_2": "rgb(89, 148, 220)",
"user_3": "rgb(180, 219, 210)",
"user_4": "rgb(216, 200, 82)",
"user_5": "rgb(194, 194, 194)",
"user_6": "rgb(89, 148, 220)",
"user_7": "rgb(180, 219, 210)",
"user_8": "rgb(216, 200, 82)",
"user_9": "rgb(232, 178, 167)",
"via_blind_buried": "rgb(187, 151, 38)",
"via_hole": "rgb(227, 183, 46)",
"via_micro": "rgb(0, 132, 132)",
"via_through": "rgb(236, 236, 236)",
"worksheet": "rgb(200, 114, 171)"
},
"gerbview": {
"axes": "rgb(0, 0, 132)",
"background": "rgb(0, 0, 0)",
"dcodes": "rgb(255, 255, 255)",
"grid": "rgb(132, 132, 132)",
"layers": [
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)",
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)"
],
"negative_objects": "rgb(132, 132, 132)",
"worksheet": "rgb(0, 0, 132)"
},
"meta": {
"name": "B",
"version": 3
},
"palette": [
"rgb(200, 52, 52)",
"rgb(127, 200, 127)",
"rgb(206, 125, 44)",
"rgb(79, 203, 203)",
"rgb(219, 98, 139)",
"rgb(167, 165, 198)",
"rgb(40, 204, 217)",
"rgb(232, 178, 167)",
"rgb(242, 237, 161)",
"rgb(141, 203, 129)",
"rgb(237, 124, 51)",
"rgb(91, 195, 235)",
"rgb(247, 111, 142)",
"rgb(77, 127, 196)"
],
"schematic": {
"anchor": "rgb(0, 0, 255)",
"aux_items": "rgb(0, 0, 0)",
"background": "rgb(245, 244, 239)",
"brightened": "rgb(255, 0, 255)",
"bus": "rgb(0, 0, 132)",
"bus_junction": "rgb(0, 0, 132)",
"component_body": "rgb(255, 255, 194)",
"component_outline": "rgb(132, 0, 0)",
"cursor": "rgb(15, 15, 15)",
"erc_error": "rgba(230, 9, 13, 0.800)",
"erc_warning": "rgba(209, 146, 0, 0.800)",
"fields": "rgb(132, 0, 132)",
"grid": "rgb(181, 181, 181)",
"grid_axes": "rgb(0, 0, 132)",
"hidden": "rgb(94, 194, 194)",
"junction": "rgb(0, 150, 0)",
"label_global": "rgb(132, 0, 0)",
"label_hier": "rgb(114, 86, 0)",
"label_local": "rgb(15, 15, 15)",
"no_connect": "rgb(0, 0, 132)",
"note": "rgb(0, 0, 194)",
"override_item_colors": false,
"pin": "rgb(132, 0, 0)",
"pin_name": "rgb(0, 100, 100)",
"pin_number": "rgb(169, 0, 0)",
"reference": "rgb(0, 100, 100)",
"shadow": "rgba(102, 179, 255, 0.800)",
"sheet": "rgb(132, 0, 0)",
"sheet_background": "rgba(255, 255, 255, 0.000)",
"sheet_fields": "rgb(132, 0, 132)",
"sheet_filename": "rgb(114, 86, 0)",
"sheet_label": "rgb(0, 100, 100)",
"sheet_name": "rgb(0, 100, 100)",
"value": "rgb(0, 100, 100)",
"wire": "rgb(0, 150, 0)",
"worksheet": "rgb(132, 0, 0)"
}
}

View File

@ -230,6 +230,8 @@ W_NOTPDF = '(W080) '
W_NOREF = '(W081) '
W_UNKVAR = '(W082) '
W_WRONGEXT = '(W083) '
W_COLORTHEME = '(W084) '
W_WRONGCOLOR = '(W085) '
# Somehow arbitrary, the colors are real, but can be different
PCB_MAT_COLORS = {'fr1': "937042", 'fr2': "949d70", 'fr3': "adacb4", 'fr4': "332B16", 'fr5': "6cc290"}
PCB_FINISH_COLORS = {'hal': "8b898c", 'hasl': "8b898c", 'imag': "8b898c", 'enig': "cfb96e", 'enepig': "cfb96e",

View File

@ -7,13 +7,14 @@
# Adapted from: https://gitlab.com/dennevi/Board2Pdf/
# Note: Original code released as Public Domain
import os
from pcbnew import PLOT_CONTROLLER, IsCopperLayer, PLOT_FORMAT_PDF
from pcbnew import PLOT_CONTROLLER, PLOT_FORMAT_PDF, FromMM
from shutil import rmtree
from tempfile import mkdtemp
from .error import KiPlotConfigurationError
from .gs import GS
from .optionable import Optionable
from .out_base import VariantOptions
from .kicad.color_theme import load_color_theme
from .macros import macros, document, output_class # noqa: F401
from .layer import Layer
from . import PyPDF2
@ -22,29 +23,21 @@ from . import log
logger = log.get_logger()
# TODO:
# - Se pueden sacar los colores del esquema de colores?
# - Opciones de out_pdf y out_any_layer
# - Estas cosas:
# self.scaling = 1.0
# """ Scale factor (0 means autoscaling)"""
# self._drill_marks = 'full'
# """ What to use to indicate the drill places, can be none, small or full (for real scale) """
# self.title = ''
# """ Text used to replace the sheet title. %VALUE expansions are allowed.
# If it starts with `+` the text is concatenated """
# self.color_theme = '_builtin_classic'
# """ Selects the color theme. Onlyu applies to KiCad 6.
# To use the KiCad 6 default colors select `_builtin_default`.
# Usually user colors are stored as `user`, but you can give it another name """
def hex_to_rgb(value):
""" Return (red, green, blue) in float between 0-1 for the color given as #rrggbb. """
value = value.lstrip('#')
lv = len(value)
rgb = tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
rgb = tuple(int(value[i:i+2], 16) for i in range(0, 6, 2))
rgb = (rgb[0]/255, rgb[1]/255, rgb[2]/255)
return rgb
alpha = int(value[6:], 16)/255 if len(value) == 8 else 1.0
return rgb, alpha
def to_gray(color):
avg = (color[0]+color[1]+color[2])/3
return (avg, avg, avg)
def colorize_pdf(folder, in_file, out_file, color):
@ -89,7 +82,10 @@ def merge_pdf(input_folder, input_files, output_folder, output_file):
i = 0
er = None
open_files = []
extra_debug = GS.debug_level >= 3
for filename in input_files:
if extra_debug:
logger.debug(" - {}".format(filename))
try:
file = open(os.path.join(input_folder, filename), 'rb')
open_files.append(file)
@ -156,6 +152,19 @@ def create_pdf_from_pages(input_folder, input_files, output_fn):
f.close()
def colorize_layer(suffix, color, monochrome, filelist, temp_dir):
in_file = GS.pcb_basename+"-"+suffix+".pdf"
if color != "#000000":
out_file = GS.pcb_basename+"-"+suffix+"-colored.pdf"
logger.debug('- Giving color to {} -> {} ({})'.format(in_file, out_file, color))
rgb, alpha = hex_to_rgb(color)
color = rgb if not monochrome else to_gray(rgb)
colorize_pdf(temp_dir, in_file, out_file, color)
filelist.append(out_file)
else:
filelist.append(in_file)
class LayerOptions(Layer):
""" Data for a layer """
def __init__(self):
@ -164,10 +173,17 @@ class LayerOptions(Layer):
with document:
self.color = ""
""" Color used for this layer """
self.plot_footprint_refs = True
""" Include the footprint references """
self.plot_footprint_values = True
""" Include the footprint values """
self.force_plot_invisible_refs_vals = False
""" Include references and values even when they are marked as invisible """
def config(self, parent):
super().config(parent)
self.validate_color('color')
if self.color:
self.validate_color('color')
class PagesOptions(Optionable):
@ -179,9 +195,24 @@ class PagesOptions(Optionable):
self.mirror = False
""" Print mirrored (X axis inverted) """
self.monochrome = False
""" Print in black and white """
self.sheet_reference_layer = ''
""" Layer to plot the page frame """
""" Print in gray scale """
self.scaling = 1.0
""" Scale factor (0 means autoscaling)"""
self.title = ''
""" Text used to replace the sheet title. %VALUE expansions are allowed.
If it starts with `+` the text is concatenated """
self.sheet = 'Assembly'
""" Text to use for the `sheet` in the title block """
self.sheet_reference_color = ''
""" Color to use for the frame and title block """
self.line_width = 0.1
""" [0.02,2] For objects without width [mm] (KiCad 5) """
self.negative_plot = False
""" Invert black and white. Only useful for a single layer """
self.exclude_pads_from_silkscreen = False
""" Do not plot the component pads in the silk screen (KiCad 5.x only) """
self.tent_vias = True
""" Cover the vias """
self.layers = LayerOptions
""" [list(dict)] List of layers printed in this page. Order is important, the last goes on top """
@ -189,18 +220,10 @@ class PagesOptions(Optionable):
super().config(parent)
if isinstance(self.layers, type):
raise KiPlotConfigurationError("Missing `layers` list")
# Fill the ID member for all the layers
self.layers = Layer.solve(self.layers)
if self.sheet_reference_layer:
# Layer name to layer ID
layer = Layer()
name = self.sheet_reference_layer
layer.layer = self.sheet_reference_layer
self.sheet_reference_layer = layer._get_layer_id_from_name()
# Check this is one of the specified layers
layer = next(filter(lambda x: x._id == self.sheet_reference_layer, self.layers), None)
if layer is None:
raise KiPlotConfigurationError("The layer selected for the sheet reference ({}) isn't in the list of layers".
format(name))
if self.sheet_reference_color:
self.validate_color('sheet_reference_color')
class PCB_PrintOptions(VariantOptions):
@ -215,29 +238,51 @@ class PCB_PrintOptions(VariantOptions):
""" Filename for the output PDF (%i=assembly, %x=pdf)"""
self.hide_excluded = False
""" Hide components in the Fab layer that are marked as excluded by a variant """
self._drill_marks = 'full'
""" What to use to indicate the drill places, can be none, small or full (for real scale) """
self.color_theme = '_builtin_classic'
""" Selects the color theme. Only applies to KiCad 6.
To use the KiCad 6 default colors select `_builtin_default`.
Usually user colors are stored as `user`, but you can give it another name """
self.plot_sheet_reference = True
""" Include the title-block """
self.pages = PagesOptions
""" [list(dict)] List of pages to include in the output document.
Each page contains one or more layers of the PCB """
self.title = ''
""" Text used to replace the sheet title. %VALUE expansions are allowed.
If it starts with `+` the text is concatenated """
super().__init__()
self._expand_ext = 'pdf'
self._expand_id = 'assembly'
# @property
# def drill_marks(self):
# return self._drill_marks
#
# @drill_marks.setter
# def drill_marks(self, val):
# if val not in self._drill_marks_map:
# raise KiPlotConfigurationError("Unknown drill mark type: {}".format(val))
# self._drill_marks = val
@property
def drill_marks(self):
return self._drill_marks
@drill_marks.setter
def drill_marks(self, val):
if val not in self._drill_marks_map:
raise KiPlotConfigurationError("Unknown drill mark type: {}".format(val))
self._drill_marks = val
def config(self, parent):
super().config(parent)
if isinstance(self.pages, type):
raise KiPlotConfigurationError("Missing `pages` list")
# self._drill_marks = PCB_PrintOptions._drill_marks_map[self._drill_marks]
self._color_theme = load_color_theme(self.color_theme)
if self._color_theme is None:
raise KiPlotConfigurationError("Unable to load `{}` color theme".format(self.color_theme))
# Assign a color if none was defined
layer_id2color = self._color_theme.layer_id2color
for p in self.pages:
for la in p.layers:
if not la.color:
if la._id in layer_id2color:
la.color = layer_id2color[la._id]
else:
la.color = "#000000"
self._drill_marks = PCB_PrintOptions._drill_marks_map[self._drill_marks]
def filter_components(self):
if not self._comps:
@ -264,53 +309,59 @@ class PCB_PrintOptions(VariantOptions):
temp_dir = mkdtemp(prefix='tmp-kibot-pcb_print-')
logger.debug('- Temporal dir: {}'.format(temp_dir))
# Plot options
plot_controller = PLOT_CONTROLLER(GS.board)
plot_options = plot_controller.GetPlotOptions()
plot_options.SetOutputDirectory(temp_dir)
pc = PLOT_CONTROLLER(GS.board)
po = pc.GetPlotOptions()
po.SetOutputDirectory(temp_dir)
# Set General Options:
plot_options.SetPlotValue(True)
plot_options.SetPlotReference(True)
plot_options.SetPlotInvisibleText(False)
plot_options.SetPlotViaOnMaskLayer(False)
plot_options.SetExcludeEdgeLayer(True)
# plot_options.SetPlotPadsOnSilkLayer(False);
plot_options.SetUseAuxOrigin(False)
plot_options.SetNegative(False)
plot_options.SetScale(1.0)
plot_options.SetAutoScale(False)
po.SetExcludeEdgeLayer(True) # We plot it separately
po.SetUseAuxOrigin(False)
po.SetAutoScale(False)
po.SetDrillMarksType(self._drill_marks)
# Generate the output
pages = []
for n, p in enumerate(self.pages):
self.set_title(p.title if p.title else self.title)
# 1) Plot all layers to individual PDF files (B&W)
po.SetPlotFrameRef(False) # We plot it separately
po.SetMirror(p.mirror)
po.SetScale(p.scaling)
po.SetNegative(p.negative_plot)
po.SetPlotViaOnMaskLayer(not p.tent_vias)
if GS.ki5():
po.SetLineWidth(FromMM(p.line_width))
po.SetPlotPadsOnSilkLayer(not p.exclude_pads_from_silkscreen)
for la in p.layers:
id = la._id
logger.debug('- Plotting layer {} ({})'.format(la.layer, id))
plot_options.SetPlotFrameRef(id == p.sheet_reference_layer)
plot_options.SetMirror(p.mirror)
if IsCopperLayer(id): # Should probably do this on mask layers as well
plot_options.SetDrillMarksType(2) # NO_DRILL_SHAPE = 0, SMALL_DRILL_SHAPE = 1, FULL_DRILL_SHAPE = 2
else:
plot_options.SetDrillMarksType(0) # NO_DRILL_SHAPE = 0, SMALL_DRILL_SHAPE = 1, FULL_DRILL_SHAPE = 2
plot_controller.SetLayer(id)
plot_controller.OpenPlotfile(la.suffix, PLOT_FORMAT_PDF, "Assembly")
plot_controller.PlotLayer()
plot_controller.ClosePlot()
# 2) Apply the colors to the PDFs
po.SetPlotReference(la.plot_footprint_refs)
po.SetPlotValue(la.plot_footprint_values)
po.SetPlotInvisibleText(la.force_plot_invisible_refs_vals)
pc.SetLayer(id)
pc.OpenPlotfile(la.suffix, PLOT_FORMAT_PDF, p.sheet)
pc.PlotLayer()
# 2) Plot the frame using an empry layer and 1.0 scale
if self.plot_sheet_reference:
logger.debug('- Plotting the frame')
po.SetPlotFrameRef(True)
po.SetScale(1.0)
pc.SetLayer(GS.board.GetLayerID(GS.work_layer))
pc.OpenPlotfile('frame', PLOT_FORMAT_PDF, p.sheet)
pc.PlotLayer()
pc.ClosePlot()
# 3) Apply the colors to the layer PDFs
filelist = []
for la in p.layers:
in_file = GS.pcb_basename+"-"+la.suffix+".pdf"
if la.color != "#000000":
out_file = GS.pcb_basename+"-"+la.suffix+"-colored.pdf"
logger.debug('- Giving color to {} -> {} ({})'.format(in_file, out_file, la.color))
colorize_pdf(temp_dir, in_file, out_file, hex_to_rgb(la.color))
filelist.append(out_file)
else:
filelist.append(in_file)
# 3) Stack all layers in one file
colorize_layer(la.suffix, la.color, p.monochrome, filelist, temp_dir)
# 4) Apply color to the frame
if self.plot_sheet_reference:
color = p.sheet_reference_color if p.sheet_reference_color else self._color_theme.pcb_frame
colorize_layer('frame', color, p.monochrome, filelist, temp_dir)
# 5) Stack all layers in one file
assembly_file = GS.pcb_basename+"-"+str(n)+".pdf"
logger.debug('- Merging layers to {}'.format(assembly_file))
merge_pdf(temp_dir, filelist, temp_dir, assembly_file)
pages.append(assembly_file)
self.restore_title()
# Join all pages in one file
logger.debug('- Creating output file {}'.format(output))
create_pdf_from_pages(temp_dir, pages, output)
@ -319,7 +370,6 @@ class PCB_PrintOptions(VariantOptions):
def run(self, output):
super().run(output)
# self.set_title(self.title)
self.filter_components()
self.generate_output(output)
self.unfilter_components()

View File

@ -8,24 +8,27 @@ outputs:
type: pcb_print
dir: Layers
options:
# title: 'Fake title for front copper and silk'
# title: 'Fake title for front copper and silk'
# color_theme: _builtin_default
# drill_marks: small
title: Chau
# plot_sheet_reference: false
pages:
- sheet_reference_layer: F.Fab
- # monochrome: true
scaling: 2.0
title: Hola
sheet: Front
sheet_reference_color: "#A02020"
layers:
- layer: Edge.Cuts
color: "#FF8000"
- layer: F.Cu
color: "#B3FFB3"
- layer: F.Paste
color: "#FF8A8A"
- layer: F.SilkS
color: "#626262"
- layer: F.Fab
color: "#000080"
plot_footprint_refs: false
plot_footprint_values: false
- layer: User.Eco1
color: "#000080"
- mirror: true
sheet_reference_layer: B.Fab
layers:
- layer: B.Fab
color: "#000080"