Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
karl committed Nov 25, 2022
0 parents commit e8cf714
Show file tree
Hide file tree
Showing 12 changed files with 1,562 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
.vscode/*
__pycache__/
structmeta.egg-info/*
*.zip
*.jpg
*.pdf
165 changes: 165 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
---
title: "Structmeta"
author: "Karl-Ulrich Krägelin"
date: "24.11.2022"
subject: "Markdown"
titlepage: true
footnotes-pretty: true
caption-justification: raggedleft
disable-header-and-footer: false
footer-left: "\\hspace{1cm}"
lang: "de"
toc-own-page: true
geometry:
- margin=25mm
---

Mit dem structmeta-Script lassen sich durch die Ablage von Scans in einer speziellen Ordnerstruktur DDB- bzw. Zeitungsportal konforme METS/MODS XML-Dateien herstellen.

Structmeta kann METS/MODS für Monographien, Zeitschriften und Zeitungen erstellen; am besten funktioniert es auf Grund der geringen Anforderungen für Zeitungsausgaben.

Das Programm benötigt zum Funktionieren neben einer speziellen Ordnerstruktur, in der die Digitalisate abgelegt sind, pro Vorgang eine Datei in der Metadaten, die für den gesamten Vorgang gelten, enthalten sind. Diese Metadaten müssen im `TOML` Format vorliegen (s.u.).

Automatisch läuft eine TIF zu JPG Umwandlung.

Nach Programmaufruf muss der Pfad zur `TOML` Datei ausgewählt werden und der Ordner, der bearbeitet werden soll.

Die Erweiterten Funktionen sind:

- **Thumbnails**: Erzeugt für jede Bilddatei kleingerechnete Thumbnail Bilder
- **OCR**: Erstellt pro Bild eine ALTO-XML Datei mit Tesseract (siehe unten, Work in Progress, erfordert eine externe Installation)
- **ZIP**: Erstellt zwei ZIP-Dateien für die Übergabe der Bilder und der Metadaten an die Fachstelle Bibliothek der DDB
- **Rename Images**: Kann die Bilder eindeutig umbenennen, nützlich falls bspw. in jedem Unterordner Bilder mit Namen wie `01.jpg` liegen

![GUI](assets/gui.png)

Das Script kann auch im Kommandozeilenmodus laufen:

```
python structmeta.py --ignore-gooey --help
```

# Vorbemerkungen

Das Programm geht davon aus, dass die Namen der Bilderdateien auf Projektebene eindeutig sind. Ist dies nicht der Fall, müssen die Bilddateien umbennant werden (Option "Rename Images").

# Metadaten

Die TOML Datei besteht aus vier Ebenen: Einmal Informationen zum Datenpartner, und einmal Informationen zu den Objekten. Die Ebenen werden durch die Strings `[institution]`, `[objects]`, `[images]` und `[OCR]` getrennt. Unterhalb dieser Ebenen wird pro Zeile einem Schlüssel durch ein `=` Zeichen ein Wert zugewiesen. Zeichenfolgen werden dabei in Anführungsstriche gesetzt. Die TOML-Datei ist mit einem Plaintext-Texteditor zu bearbeiten (nicht etwa mit Word o.ä.). Mit Tools wie bspw. [TOML Lint](https://www.toml-lint.com/) kann die erzeugte TOML Datei validiert und auf Syntaxfehler geprüft werden. Das sieht dann exemplarisch so aus - hier fehlt in Zeile 16 ein einfaches Anführungszeichen:

![](assets/tomllint.png)

## TOML Beispiel

```toml
[institution]
isil = "DE-1234"
name = "Kreisarchiv Teststadt"
logoURL = "https://www.mein-archiv.de/logo.svg"
siteURL = "https://www.mein-archiv.de"
contact = "[email protected]"
license = "https://creativecommons.org/publicdomain/mark/1.0/"

[objects]
type = "newspaper"
erscheinungsort = "Teststadt"
verlag = "Testverlag"
year_of_digitization = "2022"
place_of_digitization = "Teststadt"
sprache = "ger"
title = "Teststätter Zeitung"

[images]
#imagebaseurl = "http://www.mein-archiv.de/repo/"
max_dimensions = 500
jpg_compression_level = 20

[OCR]
tesseract_language = "deu"
#tesseract_executable = "C:/Program Files/Tesseract-OCR/tesseract.exe"
```

Erweiterte Informationen zu den Elementen finden Sie unter: .

# Ordnerstrukturen

## Zeitungen

Für Zeitungen muss die Ordner/Dateistruktur wie folgt aussehen:

Auf der obersten Ebene ein Ordner der nach der [ZDB ID](https://zdb-katalog.de) der **digitalen Ausgabe** der Zeitung benannt ist, darunter pro Ausgabe ein Ordner, der mit dem Tagesdatum der Veröffentlichung in ISO (vierz Ziffern für das Jahr, Bindestrich, zwei Ziffern für Monat und Tag, getrennt durch einen Bindestrich. Also `YYYY-MM-DD`) beginnt. Darunter liegen die Scans der Ausgabe durch ihren Dateinamen in der richtigen Reihenfolge. Die Seiten sollten also irgendwie numerisch aufsteigend gekennzeichnet sein.

```
- 984399-1
- 1802-01-01
- img0001.jpg
...
- 1802-01-02
```

## Zeitschriften (Journals)

Achtung: Structmeta kann _keine Gesamtaufnahme_ erstellen, da diese von der DDB auch nicht gehostet werden könnte. Daher werden Jorunals als `monograph` angegeben.

Ordner/Dateistruktur: Auf der obersten Ebene der Titel der Zeitschrift, darunter pro Jahrgang ein Ordner, der am Ende mit einem Unterstrich abgetrennt das Jahr enthält. Unter dem Ordner des Jahrgangs dann pro Strukturelement ein Ordner (bspw. pro Heft), der nach einem Unterstrich den Titel des Elements enthält (statt `Jahrgang_1808_Ausgabe Januar` wäre auch `_Ausgabe Januar` möglich oder `Zeitschrift_Jahrgang_1808_Ausgabe Januar`. In allen Fällen wird als Titel `Ausgabe Januar` erkannt):

```
Testzeitschrift
- Jahrgang_1807
- Jahrgang_1808
- Jahrgang_1808_Titel
- Zeitschrift_1908_001.jpg
- Jahrgang_1808_Inhaltsverzeichnis
- Zeitschrift_1908_002.jpg
- Jahrgang_1808_Ausgabe Januar
- Zeitschrift_1908_003.jpg
- Zeitschrift_1908_004.jpg
- Zeitschrift_1908_005.jpg
...
- Jahrgang_1809
```

## Monographien (Work in Progress)

Ordner/Dateistruktur: Auf der obersten Ebene ein Ordner unter dem die Monographien sortiert werden, darunter für jedes Werk ein Ordner. Unterhalb eines jeden Werkes dann für die Strukturelemente Ordner, in denen sich die entsprechenden Bilder befinden.

Achtung: Diese Funktionalität ist noch nicht ausgereift. Außerdem fehlen für Monographien fast alle Angaben!

```
Monographien
- Der Testfall
- Abhandlungen die Testreichen Tests betreffend
- Titel
- xy1000_001.jpg
- Inhaltsverzeichnis
- xy1000_002.jpg
- Kapitel 1 / Ausgabe Januar
- xy1000_003.jpg
- xy1000_004.jpg
- xy1000_005.jpg
...
- Der Zauberlehrling
```

# OCR

Unter Verwendung einer externen Installation von Tesseract kann für jede Bilddatei eine zugehörige ALTO XML Datei erstellt und in einer `mets:fileGrp@USE=FULLTEXT` referenziert werden.

## Installation von Tesseract

### Ubuntu

```
sudo add-apt-repository ppa:alex-p/tesseract-ocr-devel
sudo apt update
sudo apt install tesseract-ocr
sudo apt-get install tesseract-ocr-deu
```

### Windows

- siehe https://github.com/UB-Mannheim/tesseract/wiki#tesseract-installer-for-windows
- Im Schritt Komponenten auswählen entsprechende benötigte Sprachpakete laden:
![](assets/tessinstall1.png)
![](assets/tessinstall2.png)

Binary file added assets/gui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/tessinstall1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/tessinstall2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/tomllint.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
lxml
loguru
click
toml
pillow
PyMuPDF
pandas
gooey
pytesseract
natsort
27 changes: 27 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Installs:
- structmeta
"""

from setuptools import setup
from setuptools import find_packages

setup(
name='structmeta',
version='0.5',
description='',
long_description="",
long_description_content_type='text/markdown',
author='Karl-Ulrich Krägelin',
author_email='[email protected]',
url='https://gitlab.gwdg.de/maps/mmservice',
license='MIT',
packages=find_packages(),
include_package_data=True,
install_requires=open('requirements.txt').read().split('\n'),
entry_points={
'console_scripts': [
'structmeta=structmeta:main',
]
}
)
66 changes: 66 additions & 0 deletions structmeta/PDF2JPG.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import fitz # PyMuPDF
import io
from PIL import Image
import re
from pathlib import Path
from gooey import Gooey, GooeyParser
import os


def extractImagesFromPDF(pdffile, outputfolder, compressionlevel: int):
pdffile = Path(pdffile)
if date := re.search(r"\d{1,}\.\d{1,}\.\d{2,}", pdffile.stem):
d = re.sub(r"(\d+)\.(\d+)\.(\d+)", r"\3-\2-\1", date[0])
isodate = re.sub(r"\D(\d)$", r"-0\1", re.sub(r"\D(\d)\D", r"-0\1-", d))
elif isodate := re.search(r"\d{4}-\d{2}-\d{2}", pdffile.stem):
isodate = isodate[0]
else:
isodate = None
# open the file
pdf_file = fitz.open(pdffile)
# iterate over PDF pages
n = 0
allpages = len(pdf_file)
for page_index in range(allpages):
n += 1
print(f"Speichere Datei {n} von {allpages}", flush=True)
# get the page itself
page = pdf_file[page_index]
for image_index, img in enumerate(page.get_images(), start=1):
# get the XREF of the image
xref = img[0]
# extract the image bytes
base_image = pdf_file.extract_image(xref)
image_bytes = base_image["image"]
# load it to PIL
image = Image.open(io.BytesIO(image_bytes))
# save it to local disk
if isodate:
filename = f"{isodate}--{pdffile.stem}_{page_index+1:02}.jpg"
else:
filename = f"{pdffile.stem}__{page_index+1:02}.jpg"
image.save(Path(outputfolder, filename), "JPEG", quality=compressionlevel)


@Gooey(program_name="PDF Image Extraktor", required_cols=1, default_size=(550, 450))
def cli():
parser = GooeyParser(description="0.3")
parser.add_argument(
"Ordner", help="Bitte den Ordner mit PDFs auswählen", widget="DirChooser"
)
parser.add_argument("-c", "--compression", type=int)
args = parser.parse_args()
inputfolder = Path(args.Ordner)
compressionlevel = args.compression
if compressionlevel is None:
compressionlevel = 90
for f in inputfolder.glob("*.pdf"):
print(f"Bearbeite {f}", flush=True)
outputfolder = Path(os.getcwd(), f.stem)
outputfolder.mkdir(exist_ok=True)
extractImagesFromPDF(f, outputfolder, compressionlevel)
print(f"Alle PDFs konvertiert.", flush=True)


if __name__ == "__main__":
cli()
Loading

0 comments on commit e8cf714

Please sign in to comment.