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:
co-authored by
Claude Opus 4.8
parent
bbfe17ec37
commit
3ebfe06139
@@ -81,6 +81,32 @@ CHAPTER_DOC_TITLE = {
|
||||
"06_steuerung/loading_loop": "Funktionsbeschreibung Steuerung Loading Loop",
|
||||
}
|
||||
|
||||
# Kapitel-Typ: "general" -> Bilder sind wiederverwendbar (Maschinenelemente,
|
||||
# Steuerungs-Funktionsbeschreibungen) -> assets/shared
|
||||
# "project" -> projektspezifisch (Betrieb/Wartung der Anlage)
|
||||
# -> projekte/fortna/images
|
||||
# Fotos (.jpg) landen unabhaengig davon IMMER projektspezifisch (siehe
|
||||
# convert_manual: Routing ueber Klassifizierung im Aufrufer wird durch
|
||||
# pdf_extract je Bild nicht getrennt - daher legen wir generelle Kapitel nach
|
||||
# assets, projektbezogene nach projekte/fortna). Feinsteuerung .jpg->projekt
|
||||
# erfolgt zusaetzlich unten.
|
||||
CHAPTER_TYPE = {
|
||||
"04_betrieb/betriebsanleitung": "project",
|
||||
"05_wartung/wartungsanleitung": "project",
|
||||
"06_steuerung/ils": "general",
|
||||
"06_steuerung/omniflo": "general",
|
||||
"06_steuerung/loading_loop": "general",
|
||||
}
|
||||
|
||||
# kurzer Unterordner-Name je Kapitel fuer die Bildablage
|
||||
CHAPTER_IMG_SUBDIR = {
|
||||
"04_betrieb/betriebsanleitung": "betrieb",
|
||||
"05_wartung/wartungsanleitung": "wartung",
|
||||
"06_steuerung/ils": "steuerung_ils",
|
||||
"06_steuerung/omniflo": "steuerung_omniflo",
|
||||
"06_steuerung/loading_loop": "steuerung_loading_loop",
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# action="convert": Volltext + Bilder
|
||||
@@ -88,18 +114,38 @@ CHAPTER_DOC_TITLE = {
|
||||
|
||||
def convert_manual(item: dict, source_root: Path, chapters_dir: Path,
|
||||
dry_run: bool, symbols_dir: Path | None = None,
|
||||
symbol_registry: dict | None = None) -> None:
|
||||
"""Konvertiert eine Hauptanleitung nach chapters/<kapitel>/."""
|
||||
symbol_registry: dict | None = None,
|
||||
logos_dir: Path | None = None,
|
||||
logo_registry: dict | None = None,
|
||||
project_dir: Path | None = None,
|
||||
assets_dir: Path | None = None) -> None:
|
||||
"""Konvertiert eine Hauptanleitung nach chapters/<kapitel>/.
|
||||
|
||||
Bild-Routing je Kapitel:
|
||||
general -> assets/shared/images/<subdir>
|
||||
project -> projekte/fortna/images/<subdir>
|
||||
Fotos (.jpg) gehen unabhaengig davon IMMER nach projekte/fortna/images/<subdir>.
|
||||
"""
|
||||
chapter = item["chapter"]
|
||||
src_pdf = source_root / item["rel_path"]
|
||||
chapter_dir = chapters_dir / item["chapter"]
|
||||
chapter_dir = chapters_dir / chapter
|
||||
stem = slugify(item["filename"])
|
||||
rst_path = chapter_dir / f"{stem}.de.rst"
|
||||
img_dir = chapter_dir / "images"
|
||||
|
||||
log.info("CONVERT %s", item["chapter"])
|
||||
subdir = CHAPTER_IMG_SUBDIR.get(chapter, slugify(chapter))
|
||||
ctype = CHAPTER_TYPE.get(chapter, "project")
|
||||
assets_base = assets_dir or (chapters_dir.parent / "assets" / "shared" / "images")
|
||||
project_base = (project_dir or (chapters_dir.parent / "projekte" / "fortna")) / "images"
|
||||
# generelle Grafiken -> assets, projektbezogene -> projekt; Fotos immer projekt
|
||||
image_dest = (assets_base if ctype == "general" else project_base) / subdir
|
||||
photo_dest = project_base / subdir
|
||||
|
||||
log.info("CONVERT %s (%s)", chapter, ctype)
|
||||
log.info(" Quelle: %s (%d MB)", item["rel_path"],
|
||||
item["size_bytes"] // (1024 * 1024))
|
||||
log.info(" -> %s", rst_path.relative_to(chapters_dir))
|
||||
log.info(" Bilder -> %s (Fotos -> %s)",
|
||||
image_dest.relative_to(chapters_dir.parent),
|
||||
photo_dest.relative_to(chapters_dir.parent))
|
||||
|
||||
if dry_run:
|
||||
return
|
||||
@@ -108,21 +154,21 @@ def convert_manual(item: dict, source_root: Path, chapters_dir: Path,
|
||||
log.error(" Quelle fehlt: %s", src_pdf)
|
||||
return
|
||||
|
||||
# Zielordner vorbereiten: vorhandene RST/Bilder ersetzen (aus einem Guss)
|
||||
# Zielordner vorbereiten und ALLE vorher existierenden Bilder darin loeschen.
|
||||
chapter_dir.mkdir(parents=True, exist_ok=True)
|
||||
if img_dir.exists():
|
||||
shutil.rmtree(img_dir)
|
||||
# Alte .de.rst im Zielordner entfernen (Testbestand)
|
||||
for d in (image_dest, photo_dest, chapter_dir / "images"):
|
||||
if d.exists():
|
||||
shutil.rmtree(d)
|
||||
for old in chapter_dir.glob("*.de.rst"):
|
||||
old.unlink()
|
||||
|
||||
# Text + Bilder positionsbasiert extrahieren (verwoben in Lesereihenfolge,
|
||||
# PDF-Ueberschriftennummern entfernt, Bilder als .. figure:: referenziert).
|
||||
doc_title = CHAPTER_DOC_TITLE.get(item["chapter"], slugify(item["filename"]))
|
||||
doc_title = CHAPTER_DOC_TITLE.get(chapter, slugify(item["filename"]))
|
||||
blocks, img_count = pdf_extract.extract_manual(
|
||||
src_pdf, rst_path, img_dir, title=doc_title,
|
||||
symbols_dir=symbols_dir, symbol_registry=symbol_registry)
|
||||
log.info(" %d Textbloecke, %d Bilder (referenziert) -> %s",
|
||||
src_pdf, rst_path, chapter_dir / "images", title=doc_title,
|
||||
symbols_dir=symbols_dir, symbol_registry=symbol_registry,
|
||||
logos_dir=logos_dir, logo_registry=logo_registry,
|
||||
image_dest=image_dest, photo_dest=photo_dest)
|
||||
log.info(" %d Textbloecke, %d Bilder -> %s",
|
||||
blocks, img_count, rst_path.name)
|
||||
|
||||
|
||||
@@ -270,17 +316,28 @@ def main() -> None:
|
||||
if args.only in (None, "convert"):
|
||||
conv = [i for i in items if i["action"] == "convert"]
|
||||
log.info("--- %d Hauptanleitungen konvertieren ---", len(conv))
|
||||
# Symbole dokumentuebergreifend zentral in assets/shared/symbols/ ablegen
|
||||
# und nur einmal referenzieren (Warndreiecke etc. tauchen in mehreren
|
||||
# Anleitungen auf). Registry ueber alle Dokumente hinweg teilen.
|
||||
symbols_dir = chapters_dir.parent / "assets" / "shared" / "symbols"
|
||||
# Symbole + Logos dokumentuebergreifend zentral in assets/shared ablegen
|
||||
# und nur einmal referenzieren. Registries ueber alle Dokumente teilen.
|
||||
base = chapters_dir.parent
|
||||
symbols_dir = base / "assets" / "shared" / "symbols"
|
||||
logos_dir = base / "assets" / "shared" / "logos"
|
||||
assets_img_dir = base / "assets" / "shared" / "images"
|
||||
project_dir = base / "projekte" / "fortna"
|
||||
symbol_registry: dict = {}
|
||||
logo_registry: dict = {}
|
||||
# Vor dem Lauf zentrale Ziel-Bildordner leeren (frischer Stand).
|
||||
if not dry_run:
|
||||
for d in (symbols_dir, logos_dir, assets_img_dir, project_dir / "images"):
|
||||
if d.exists():
|
||||
shutil.rmtree(d)
|
||||
for it in conv:
|
||||
convert_manual(it, source_root, chapters_dir, dry_run,
|
||||
symbols_dir=symbols_dir, symbol_registry=symbol_registry)
|
||||
symbols_dir=symbols_dir, symbol_registry=symbol_registry,
|
||||
logos_dir=logos_dir, logo_registry=logo_registry,
|
||||
project_dir=project_dir, assets_dir=assets_img_dir)
|
||||
if not dry_run:
|
||||
log.info("Symbole zentral: %d eindeutige -> %s",
|
||||
len(symbol_registry), symbols_dir)
|
||||
log.info("Symbole zentral: %d | Logos: %d -> assets/shared/",
|
||||
len(symbol_registry), len(logo_registry))
|
||||
|
||||
# 2. Verzeichnislisten
|
||||
if args.only in (None, "list"):
|
||||
|
||||
Reference in New Issue
Block a user