From c100ed260deaeb99c3fdfd7b6eb3c7985b4c6856 Mon Sep 17 00:00:00 2001 From: Mike Shevchenko Date: Sat, 19 Sep 2026 04:17:00 +0300 Subject: [PATCH 1/2] [Fixed] Multi-page PNG cache tested the wrong file's timestamp The branch that returns a cached set of multi-page PNGs checks isfile(destm) but then reads getmtime(dest1). When only the multi-page files exist, dest1 is absent and getmtime raises FileNotFoundError instead of using the cache. --- CHANGELOG.md | 5 +++++ kicad-diff.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1203cda..dc1f074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### Fixed +* Crash instead of a cache hit when only the multi-page PNGs were cached + + ## [2.6.0] - 2026-06-01 ### Added * KiCad 10 variants support for PCB diff --git a/kicad-diff.py b/kicad-diff.py index 9b09203..b5f1650 100755 --- a/kicad-diff.py +++ b/kicad-diff.py @@ -430,7 +430,7 @@ def pdf2png(base_name, blank=False, ref=None): if isfile(dest1) and getmtime(dest1) > source_mtime: logger.debug(source+" already converted to PNG") return [dest1] - if isfile(destm) and getmtime(dest1) > source_mtime: + if isfile(destm) and getmtime(destm) > source_mtime: logger.debug(source+" already converted to PNG") return sorted(glob(base_name+'-*.png')) if isfile(source): From 69cce8b0cc73254cd290a554d520d8714629d9ef Mon Sep 17 00:00:00 2001 From: Mike Shevchenko Date: Sat, 19 Sep 2026 04:17:23 +0300 Subject: [PATCH 2/2] [Fixed] PNG cache ignored the resolution pdf2png decided a cache hit from the file times alone, so a cache populated at one resolution was reused at another and --resolution was silently discarded. Diffing at 400 DPI against a cache built at 150 gave 150 DPI images. The PNG names now carry the resolution. The layer PDFs they come from are vector, so they stay shared: changing the resolution re-rasterizes but never re-plots, and caches for several resolutions coexist in one directory. The schematic path has the same flaw and is not touched here. svg2png rasterizes at the same resolution, but its PNG names double as sheet keys in layer_names, so tagging them is not a local change. Old untagged PNGs left in a cache are simply never read again. --- CHANGELOG.md | 2 ++ kicad-diff.py | 16 ++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc1f074..fee8fdc 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 ## [Unreleased] ### Fixed * Crash instead of a cache hit when only the multi-page PNGs were cached +* The PNG cache ignored --resolution, so a cache populated at one resolution was + reused at another and the requested resolution was silently discarded ## [2.6.0] - 2026-06-01 diff --git a/kicad-diff.py b/kicad-diff.py index b5f1650..f23094d 100755 --- a/kicad-diff.py +++ b/kicad-diff.py @@ -425,14 +425,18 @@ def run_command(command): def pdf2png(base_name, blank=False, ref=None): source = base_name+'.pdf' if not blank else ref+'.pdf' source_mtime = getmtime(source) if isfile(source) else 0 - dest1 = base_name+'.png' - destm = base_name+'-0.png' + # The PNGs are rasterized at `resolution`, while the PDFs they come from are vector and + # resolution independent. Tagging only the PNG names lets one plot serve every resolution, + # and keeps caches made at different resolutions from being reused for each other. + tag = '.r{}'.format(resolution) + dest1 = base_name+tag+'.png' + destm = base_name+tag+'-0.png' if isfile(dest1) and getmtime(dest1) > source_mtime: logger.debug(source+" already converted to PNG") return [dest1] if isfile(destm) and getmtime(destm) > source_mtime: logger.debug(source+" already converted to PNG") - return sorted(glob(base_name+'-*.png')) + return sorted(glob(base_name+tag+'-*.png')) if isfile(source): if use_poppler: cmd = 'cat "{}" | pdftoppm -r {} -gray - | {} - "{}"'.format(source, resolution, CONVERT, dest1) @@ -441,13 +445,13 @@ def pdf2png(base_name, blank=False, ref=None): '-colorspace Gray -resample {} -depth 8 "{}"'.format(resolution*2, source, resolution, dest1)) run_command(['bash', '-c', cmd]) else: - png = ref+'.png' + png = ref+tag+'.png' assert isfile(png), png copy2(png, dest1) if blank: # Create a blank file logger.debug('Blanking '+dest1) - blanked = base_name+'_blanked.png' + blanked = base_name+tag+'_blanked.png' cmd = (CONVERT + ' "{}" -background white -threshold 100% -negate -colorspace Gray "{}"'.format(dest1, blanked)) run_command(['bash', '-c', cmd]) remove(dest1) @@ -455,7 +459,7 @@ def pdf2png(base_name, blank=False, ref=None): if isfile(dest1): return [dest1] if isfile(destm): - return sorted(glob(base_name+'-*.png')) + return sorted(glob(base_name+tag+'-*.png')) assert False, f"Failed to convert {source} to PNG"