Files
2025-08-28 03:07:33 +07:00

133 lines
5.0 KiB
Python

#!/usr/bin/env python3
import argparse
import logging
import os
import sys
from pathlib import Path
try:
from . import plot
except ImportError:
import plot
_logger = logging.getLogger(__name__)
_log_levels = {'NOTSET': logging.NOTSET, 'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARN': logging.WARN,
'ERROR': logging.ERROR, 'FATAL': logging.FATAL}
_pdf_libs_merge = ['pypdf', 'pymupdf']
_pdf_libs_color = ['pypdf', 'pymupdf']
def shell_path(abspath: bool = True, exists: bool = True):
def path_expand(arg: str) -> str:
path = os.path.expanduser(os.path.expandvars(arg))
if abspath:
path = os.path.abspath(path)
if exists and not os.path.exists(path):
raise argparse.ArgumentTypeError(f'{path} must be an existing file')
return path
return path_expand
def num_range(arg_type, min_val, max_val):
def range_check(arg: str):
try:
f = arg_type(arg)
except ValueError:
raise argparse.ArgumentTypeError(f'must be a valid {arg_type}')
if not min_val <= f <= max_val:
raise argparse.ArgumentTypeError(f'must be within [{min_val}, {max_val}]')
return f
return range_check
def cli(board_filepath: str, configfile: str, **kwargs) -> bool:
import pcbnew
try:
from . import persistence
except ImportError:
import persistence
board = pcbnew.LoadBoard(board_filepath)
config = persistence.Persistence(configfile)
config_vars = config.load()
# note: cli parameters override config.ini values
config_vars.update(kwargs)
return plot.plot_pdfs(board, **config_vars)
def parse_args():
parser = argparse.ArgumentParser(description='Board2Pdf CLI.')
parser.add_argument('kicad_pcb', type=shell_path(), help='.kicad_pcb file')
parser.add_argument('--ini', default=None, type=shell_path(False, False), required=False,
help=f'Path to `board2pdf.config.ini` to use')
parser.add_argument('--log', default='NOTSET', choices=_log_levels.keys(), required=False,
help='Enables logging with given log-level')
parser.add_argument('--merge', default=None, choices=_pdf_libs_merge, required=False,
help='PDF merge processor library')
parser.add_argument('--colorize', default=None, choices=_pdf_libs_color, required=False,
help='PDF colorize processor library')
parser.add_argument('--ext', default=None, required=False,
help='File extension to use for the merged PDF. Default is `__Assembly`.')
parser.add_argument('--output', default=None, required=False,
help='Output file name. Takes precedent over --ext argument if set.')
return parser.parse_args()
def main():
args = parse_args()
pcb_path = args.kicad_pcb
log_level = _log_levels[args.log]
if log_level:
# use the path of the board for the log file
log_file = os.path.join(os.path.dirname(pcb_path), 'board2pdf.log')
logging.basicConfig(filename=log_file, level=log_level)
_logger.info(f'starting logging with level: {_log_levels[args.log]}')
# Check for the ini file specified with the --ini argument
# Terminate if the file is not found
# If no ini file is specified, look for an ini file in the pcb path, then the globally saved ini
# file, and last for the default ini file.
if args.ini:
ini_path = Path(args.ini).absolute()
if not ini_path.exists():
_logger.error(f'{ini_path=} specified with --ini argument not found, terminate')
print(f"Error: ini file `{args.ini}` specified with --ini argument not found.", sys.stderr)
sys.exit(1)
else:
ini_path = Path(os.path.join(os.path.dirname(pcb_path), 'board2pdf.config.ini'))
if not ini_path.exists():
_logger.info(f'{ini_path=} not found, use global path')
ini_path = Path(__file__).parent / 'board2pdf.config.ini'
if not ini_path.exists():
_logger.info(f'{ini_path=} not found, use default path')
ini_path = Path(__file__).parent / 'default_config.ini'
if not ini_path.exists():
_logger.error(f'{ini_path=} not found, terminate')
print(f"Error: ini file `{args.ini}` not found.", sys.stderr)
sys.exit(1)
_logger.info(f'{pcb_path=}')
_logger.info(f'{ini_path=}')
optional = {}
if args.colorize:
optional['colorize_lib'] = args.colorize
if args.merge:
optional['merge_lib'] = args.merge
if args.ext:
optional['assembly_file_extension'] = args.ext
if args.output:
optional['assembly_file_output'] = args.output
_logger.info(f'{optional=}')
sys.exit(0 if cli(pcb_path, ini_path, **optional) else 1)
if __name__ == "__main__":
main()