Het waarborgen van de nauwkeurigheid en integriteit van PDF’s die door uw toepassing worden gegenereerd, is cruciaal, vooral voor documenten zoals facturen, rapporten en certificaten. Ik heb gezien dat dit belangrijk is voor een groot aantal bedrijven, die veel brieven naar klanten sturen. Het automatiseren van dit validatieproces kan uw CI/CD-pipeline aanzienlijk verbeteren, zodat elke gegenereerde PDF aan uw kwaliteitsnormen voldoet voordat deze uw gebruikers bereikt. Er zijn veel betaalde software die dit goed kan, maar ik wil je laten zien dat je dit ook in Python open source kunt doen. In deze blogpost zullen we onderzoeken hoe je een geautomatiseerd validatiesysteem voor PDF-inhoud kunt opzetten.
Waarom PDF-validatie automatiseren?
PDF-documenten bevatten vaak kritieke informatie en eventuele fouten kunnen leiden tot aanzienlijke problemen, zoals financiële discrepanties of miscommunicatie. Het automatiseren van de validatie van PDF’s zorgt ervoor dat:
- De inhoud is nauwkeurig en zoals verwacht.
- Belangrijke informatie is aanwezig.
- De visuele integriteit van het document blijft behouden.
Tools die we zullen of kunnen gebruiken Gebruik
Om geautomatiseerde PDF-validatie te bereiken, gebruiken we de volgende open-source tools:
- PyMuPDF (Fitz) **: voor het extraheren van tekst en afbeeldingen uit PDF’s.
- Kussen: voor beeldverwerking.
- NumPy: voor numerieke bewerkingen tijdens beeldvergelijking.
Uw omgeving instellen
Eerst zetten we een virtuele Python-omgeving op en installeren we de benodigde pakketten.
Creëer een virtuele omgeving
python -m venv pdf_test_env
Op MacOS/Linux:
source pdf_test_env/bin/activate
De vereiste pakketten installeren
pip install pymupdf pillow numpy
Het validatiescript schrijven
Testcase
We gaan een factuur als pdf valideren
We maken een Python-script dat tekst en afbeeldingen uit een PDF extraheert en deze vergelijkt met de verwachte waarden.
De test wordt gemaakt op het logo, ik heb een verwacht logo en de tekstwaarden gegenereerd.
„Factuurdatum: 5/6/2024",
„Factuurnummer: 20240020",
„Jan klaasen 25G”,
„Nederland”,
„€8000,00”,
„BTW: NL 23.80.12.34.B01",
„info@bsure-digital.nl”
Maar wat u ook kunt doen, is de gegevens en afbeeldingen uit een factuursjabloon halen en dit als basis gebruiken voor de vergelijking. Dit is slechts een voorbeeld van de mogelijkheden.
Laten we eens in de code duiken.
import fitz # PyMuPDF
from PIL import Image
import io
import numpy as np
def extract_images_from_pdf(pdf_path):
doc = fitz.open(pdf_path)
images = []
for page_num in range(len(doc)):
page = doc.load_page(page_num)
image_list = page.get_images(full=True)
for img_index, img in enumerate(image_list):
xref = img[0]
base_image = doc.extract_image(xref)
image_bytes = base_image["image"]
image = Image.open(io.BytesIO(image_bytes))
images.append(image)
return images
def compare_images(img1, img2, threshold=0.60):
img1 = img1.resize((100, 100)).convert("L")
img2 = img2.resize((100, 100)).convert("L")
img1_array = np.array(img1).astype(np.float32)
img2_array = np.array(img2).astype(np.float32)
img1_array /= 255.0
img2_array /= 255.0
mse = np.mean((img1_array - img2_array) ** 2)
similarity = 1 - mse
return similarity >= threshold
def validate_pdf_images(pdf_path, expected_images_paths):
extracted_images = extract_images_from_pdf(pdf_path)
expected_images = [Image.open(path) for path in expected_images_paths]
if len(extracted_images) != len(expected_images):
print("Number of extracted images does not match the number of expected images.")
return False
for i in range(len(extracted_images)):
if not compare_images(extracted_images[i], expected_images[i]):
print(f"Image {i+1} does not match the expected image.")
return False
print("All images match the expected images.")
return True
def extract_text_from_pdf(pdf_path):
doc = fitz.open(pdf_path)
text_content = []
for page_num in range(len(doc)):
page = doc.load_page(page_num)
text = page.get_text()
text_content.append(text)
return text_content
def normalize_text(text):
return ' '.join(text.lower().split())
def validate_partial_text_in_single_page(pdf_path, expected_texts):
extracted_texts = extract_text_from_pdf(pdf_path)
if len(extracted_texts) != 1:
print("PDF does not contain exactly one page.")
return False
extracted_text_normalized = normalize_text(extracted_texts[0])
for expected_text in expected_texts:
expected_text_normalized = normalize_text(expected_text)
if expected_text_normalized not in extracted_text_normalized:
print(f"Expected text snippet not found in the PDF:")
print("Extracted text:")
print(extracted_texts[0])
print("Expected text snippet:")
print(expected_text)
return False
print("All expected text snippets are found in the PDF.")
return True
# Paths to the PDF file, expected images, and expected text snippets
pdf_path = "jan_klaasen_20240020.pdf"
expected_images_paths = ["image.png"]
expected_texts = [
"Invoice date: 5/6/2024",
"Invoice num.: 20240020",
"Jan klaasen 25G",
"The Netherlands",
"€8000.00",
"VAT: NL 23.80.12.34.B01",
"info@bsure-digital.nl"
]
# Validate the images in the PDF
validate_pdf_images(pdf_path, expected_images_paths)
# Validate partial text snippets in the single-page PDF
validate_partial_text_in_single_page(pdf_path, expected_texts)
Dit Python-script gebruikt de PyMuPDF-, Pillow- en NumPy-bibliotheken om de inhoud van een PDF-document te valideren.
Uitleg over de invoer
- fitz (PyMuPDF): een bibliotheek voor het verwerken van PDF-documenten. Hiermee kunt u PDF-bestanden openen, lezen en manipuleren.
- PIL (Pillow): Python Imaging Library die wordt gebruikt voor het openen, manipuleren en opslaan van afbeeldingsbestanden.
- io: Een kernmodule van Python die de tools biedt voor verschillende soorten I/O-bewerkingen, die hier worden gebruikt om binaire streams in het geheugen te verwerken.
- numpy: Een bibliotheek voor numerieke bewerkingen in Python, die hier wordt gebruikt om beeldvergelijkingen uit te voeren.
Het script voert twee hoofdtaken uit:
Afbeeldingsvalidatie:
- Functie: validate_pdf_images (pdf_path, expected_images_paths)
- Proces:
- Extraheert afbeeldingen uit de PDF met PyMuPDF.
- Vergelijkt elke geëxtraheerde afbeelding met verwachte afbeeldingen met behulp van beeldformaat, grijswaardenconversie en gemiddelde kwadratische fout (MSE) voor het meten van overeenkomsten.
- Uitkomst: Drukt af of alle afbeeldingen overeenkomen met de verwachte afbeeldingen.
Tekstvalidatie:
- Functie: validate_partial_text_in_single_page (pdf_path, expected_texts)
- Proces:
- Extraheert tekst van de enkele pagina van de PDF met PyMuPDF.
- Normaliseert de tekst (kleine letters, verwijdert extra witruimte).
- Controleert of specifieke verwachte tekstfragmenten aanwezig zijn in de genormaliseerde geëxtraheerde tekst.
- Uitkomst: Drukt af of alle verwachte tekstfragmenten in de PDF zijn gevonden.
De verzameling teksten in dit voorbeeld is een lijst, maar u kunt dit eenvoudiger maken voor bedrijfsanalisten en functionaliteit creëren in Excel
Integratie met CI/CD
name: PDF Validation
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
validate_pdf:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m venv pdf_test_env
source pdf_test_env/bin/activate
pip install pymupdf pillow numpy
- name: Run PDF validation script
run: |
source pdf_test_env/bin/activate
python validate_pdf.py
De acties die worden uitgevoerd in github-acties
De beeldcontrole is geslaagd, hou er rekening mee dat ik de drempel op 0,60 heb gezet. Zorg ervoor dat je een goede verwachte afbeelding maakt om te vergelijken
def compare_images(img1, img2, threshold=0.60):
Zoals je kunt zien in mijn github-actielogboek is het tekstfragment mislukt info @bsure -digital.n44 in plaats van info@bsure-digital.nl
Integratie met selenium/playwright/cypress
Deze open source-functionaliteit kan samenwerken met webapplicaties voor testautomation die PDF’s genereren. Stel dat u de pdf downloadt en deze open source-functionaliteit vervolgens als een aparte pijplijn gebruikt om de inhoud te valideren. Dit kan een businesscase zijn
Het demo-voorbeeld
Ik heb hier een demo-voorbeeld gemaakt
https://github.com/learn-automated-testing/pdftesterpython
Conclusie
Door PDF-validatie in uw CI/CD-pipeline te automatiseren, zorgt u ervoor dat elke PDF die uw toepassing genereert voldoet aan de vereiste normen, wat tijd bespaart en fouten voorkomt. Door gebruik te maken van open-source tools zoals PyMuPDF, Pillow en NumPy, in combinatie met de flexibiliteit van Python, kunt u een robuust validatieproces creëren dat naadloos in uw ontwikkelingsworkflow wordt geïntegreerd en dit integreert in uw ui-api voor testautomatisering of wat dan ook. Met deze configuratie kunt u met een gerust hart updates implementeren in de wetenschap dat uw PDF’s nauwkeurig en foutloos zijn, waardoor de betrouwbaarheid van de toepassing wordt verbeterd.