feat: Bild-Routing, Logo/Symbol/Fragment-Filter, Produktprogramm-Bilder

Bild-Verarbeitung der Anleitungen (lib/pdf_extract.py) erweitert:

- Routing nach Kapitel-Typ: generelle Kapitel (Steuerung) -> assets/shared/
  images/<subdir>, projektbezogene (Betrieb/Wartung) -> projekte/fortna/images.
  Fotos (.jpg) IMMER projektspezifisch, unabhaengig vom Kapitel.
- Benennung aus Bildunterschrift: <Stichwort>_NN.png (z.B. Anlage_Uebersicht_01);
  Fotos img_NNN.jpg, SW-Skizzen >10KB sketch_NNN.png, Rest img_NNN.png.
- Logos: nur wiederkehrende (>=3x) breite Randgrafiken gelten als Logo -> alle
  Aufloesungen nach assets/shared/logos/logo_<n>_<breite>px, nicht inline.
- Wertlose Fragmente (flach <=55px, breit, SW = gerenderte Kapitelnummern wie
  "5.2.6") werden verworfen.
- Ziel-Bildordner werden vor jedem Lauf geleert.

extract_products.py: Produktprogramm-Bilder -> assets/shared/images/
produktprogramm/{omniflo,ils,cpc,geruest,trolley} (generelle Produktbilder).

.gitignore: extrahierte Bildmassen ausgeschlossen, nur kuratierte symbols/ +
logos/ versioniert.

Voller PDF-Build sauber (lualatex exit 0, 589 Seiten, 638 Bild-Refs, 0 fehlend).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-23 14:35:19 +02:00
co-authored by Claude Opus 4.8
parent bbfe17ec37
commit 3ebfe06139
11 changed files with 356 additions and 72 deletions
+21 -5
View File
@@ -417,10 +417,18 @@ def dedup_images(img_dir: Path) -> int:
return removed
def process_all(data_dir: Path) -> None:
"""Verarbeitet alle konfigurierten PDFs."""
def process_all(data_dir: Path, assets_dir: Path | None = None) -> None:
"""Verarbeitet alle konfigurierten Produktprogramm-PDFs.
Bilder sind generelle Produktbilder und werden nach
assets/shared/images/produktprogramm/<ziel> abgelegt (wiederverwendbar).
Die extrahierte RST bleibt zur Information neben dem PDF.
"""
pp_dir = data_dir / "Produktprogramm"
images_base = pp_dir / "images"
if assets_dir is None:
# data/ liegt unter PROJECT -> assets/shared/images/produktprogramm
assets_dir = data_dir.parent / "assets" / "shared" / "images" / "produktprogramm"
images_base = assets_dir
if not pp_dir.exists():
log.error("Verzeichnis nicht gefunden: %s", pp_dir)
@@ -432,6 +440,7 @@ def process_all(data_dir: Path) -> None:
sys.exit(1)
log.info("Gefunden: %d PDF-Dateien in %s", len(pdf_files), pp_dir)
log.info("Bilder -> %s", images_base)
for pdf_path in sorted(pdf_files):
target = find_target(pdf_path.name)
@@ -441,8 +450,11 @@ def process_all(data_dir: Path) -> None:
log.info("Verarbeite: %s -> %s", pdf_path.name, target)
# Bilder extrahieren und Duplikate entfernen
# Bilder extrahieren und Duplikate entfernen. Vorher Zielordner leeren.
img_dir = images_base / target
if img_dir.exists():
import shutil
shutil.rmtree(img_dir)
img_count = extract_images(pdf_path, img_dir)
dup_count = dedup_images(img_dir)
log.info(" %d Bilder extrahiert, %d Duplikate entfernt -> %d einzigartig in %s",
@@ -463,6 +475,9 @@ def main() -> None:
)
parser.add_argument("--data-dir", type=Path, default=None,
help="data/ Verzeichnis (default: aus PV_DATA oder ./data)")
parser.add_argument("--assets-dir", type=Path, default=None,
help="Zielordner fuer Bilder (default: "
"assets/shared/images/produktprogramm unter PROJECT)")
parser.add_argument("--verbose", "-v", action="store_true",
help="Ausfuehrliche Ausgabe")
args = parser.parse_args()
@@ -480,8 +495,9 @@ def main() -> None:
else:
data_dir = Path("data").resolve()
assets_dir = args.assets_dir.resolve() if args.assets_dir else None
log.info("Data-Verzeichnis: %s", data_dir)
process_all(data_dir)
process_all(data_dir, assets_dir=assets_dir)
if __name__ == "__main__":