## Summary Consolidate the script help/startup fixes so standalone helper commands can show usage without requiring runtime-only dependencies or generating files. ## Details Several helper scripts imported optional packages, evaluated newer annotations, or started generation before users could inspect CLI usage. On a fresh checkout, this made common help paths fail on missing packages such as `cairosvg`, `Pillow`, `freetype-py`, `fontTools`, `pyphen`, `pyserial`, or on Python 3.9 annotation evaluation. A few generators also treated `--help` as a normal output argument and wrote files instead of printing usage. This change keeps runtime dependencies on the code paths that need them, handles lightweight help/list commands first, and adds explicit `--help` handling for generator scripts that previously started work. ## Validation - `python3 <script> --help` across tracked Python scripts, excluding `scripts/patch_jpegdec.py` because it is a PlatformIO pre-build hook rather than a standalone CLI - `python3 scripts/convert_icon.py` still exits with usage for missing required arguments - `python3 lib/EpdFont/scripts/fontconvert_sdcard.py --list-presets` - `python3 -m py_compile` for the changed scripts
87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
import os
|
|
import io
|
|
import sys
|
|
|
|
threshold = 128
|
|
USAGE = 'Usage: python scripts/convert_icon.py input.png|input.svg output_name width height'
|
|
|
|
def svg_to_png_bytes(svg_path, width, height):
|
|
import cairosvg
|
|
|
|
with open(svg_path, 'rb') as f:
|
|
svg_data = f.read()
|
|
png_bytes = cairosvg.svg2png(bytestring=svg_data, output_width=width, output_height=height)
|
|
return png_bytes
|
|
|
|
def load_image(path, width, height):
|
|
from PIL import Image
|
|
|
|
ext = os.path.splitext(path)[1].lower()
|
|
if ext == '.svg':
|
|
png_bytes = svg_to_png_bytes(path, width, height)
|
|
img = Image.open(io.BytesIO(png_bytes))
|
|
else:
|
|
img = Image.open(path)
|
|
img = img.convert('RGBA')
|
|
img = img.resize((width, height), Image.LANCZOS)
|
|
# Flatten alpha: paste on white background
|
|
background = Image.new('RGBA', img.size, (255, 255, 255, 255))
|
|
background.paste(img, mask=img.split()[3])
|
|
img = background
|
|
# Rotate 90 degrees counterclockwise
|
|
img = img.rotate(90, expand=True)
|
|
return img
|
|
|
|
def image_to_c_array(img, array_name):
|
|
# Convert to grayscale, then threshold to get white=1, black=0
|
|
# Convert to grayscale
|
|
img = img.convert('L')
|
|
width, height = img.size
|
|
pixels = list(img.getdata())
|
|
packed = []
|
|
for y in range(height):
|
|
for x in range(0, width, 8):
|
|
byte = 0
|
|
for b in range(8):
|
|
if x + b < width:
|
|
v = pixels[y * width + x + b]
|
|
# 1 for white, 0 for black
|
|
bit = 1 if v >= threshold else 0
|
|
byte |= (bit << (7 - b))
|
|
packed.append(byte)
|
|
# Format as C array
|
|
c = f'#pragma once\n#include <cstdint>\n\n'
|
|
c += f'// size: {width}x{height}\n'
|
|
c += f'static const uint8_t {array_name}[] = {{\n '
|
|
for i, v in enumerate(packed):
|
|
c += f'0x{v:02X}, '
|
|
if (i + 1) % 16 == 0:
|
|
c += '\n '
|
|
c = c.rstrip(', \n') + '\n};\n'
|
|
return c
|
|
|
|
def main():
|
|
if any(arg in ('-h', '--help') for arg in sys.argv[1:]):
|
|
print(USAGE)
|
|
sys.exit(0)
|
|
if len(sys.argv) != 5:
|
|
print(USAGE)
|
|
sys.exit(1)
|
|
input_path, output_name, width, height = sys.argv[1:5]
|
|
array_name = output_name.capitalize() + 'Icon'
|
|
width, height = int(width), int(height)
|
|
img = load_image(input_path, width, height)
|
|
c_array = image_to_c_array(img, array_name)
|
|
|
|
# Always save to src/components/icons/[output_name].h relative to project root
|
|
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
output_dir = os.path.join(project_root, 'src', 'components', 'icons')
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
output_path = os.path.join(output_dir, f'{output_name}.h')
|
|
with open(output_path, 'w') as f:
|
|
f.write(c_array)
|
|
print(f'Wrote {output_path}')
|
|
|
|
if __name__ == '__main__':
|
|
main()
|