Geautomatiseerde pdf validatie met python een open source aanpak

29 mei 2024 | by Ralph Van Der Horst

Geautomatiseerde PDF-validatie met Python Een Open Source Aanpak

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 gebruiken

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
  • Pillow: voor beeldverwerking
  • NumPy: voor numerieke bewerkingen tijdens beeldvergelijking

Uw omgeving instellen

Creëer een virtuele omgeving

python -m venv pdf_test_env

Activeer de virtuele omgeving

Op MacOS/Linux:

source pdf_test_env/bin/activate

Op Windows:

pdf_test_env\Scripts\activate

Installeer de vereiste pakketten

pip install pymupdf pillow numpy

Het validatiescript schrijven

Testcase

We gaan een factuur als PDF valideren.

PDF Validation Example

De test wordt gemaakt op het logo (ik heb een verwacht logo) en de tekstwaarden zoals:

  • “Factuurdatum: 5/6/2024”
  • “Factuurnummer: 20240020”
  • “Jan klaasen 25G”
  • “Nederland”
  • “€8000,00”
  • “BTW: NL 23.80.12.34.B01”
  • info@bsure-digital.nl

Invoice Template Example

Tip: U kunt ook de gegevens en afbeeldingen uit een factuursjabloon halen en dit als basis gebruiken voor de vergelijking. Dit is slechts een voorbeeld van de mogelijkheden.

De complete code

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

# Configuratie van paden en verwachte waarden
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"
]

# Valideer de afbeeldingen in de PDF
validate_pdf_images(pdf_path, expected_images_paths)

# Valideer tekstfragmenten in de eenpagina PDF
validate_partial_text_in_single_page(pdf_path, expected_texts)

Uitleg van het script

Dit Python-script gebruikt de PyMuPDF-, Pillow- en NumPy-bibliotheken om de inhoud van een PDF-document te valideren.

Gebruikte imports:

  • fitz (PyMuPDF): een bibliotheek voor het verwerken van PDF-documenten
  • PIL (Pillow): Python Imaging Library voor beeldverwerking
  • io: Voor het verwerken van binaire streams in het geheugen
  • numpy: Voor numerieke bewerkingen en beeldvergelijkingen

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
  • Gebruikt 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

Tip: De verzameling teksten in dit voorbeeld is een lijst, maar u kunt dit eenvoudiger maken voor bedrijfsanalisten door functionaliteit in Excel te creëren.

Integratie met CI/CD

Hieronder een voorbeeld van een GitHub Actions workflow voor PDF-validatie:

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

Resultaten uit de praktijk

De beeldcontrole kan succesvol zijn, maar let op de drempel die je instelt. In dit voorbeeld heb ik de drempel op 0,60 gezet:

def compare_images(img1, img2, threshold=0.60):

Zorg ervoor dat je een goede verwachte afbeelding maakt om te vergelijken.

Voorbeeld van een mislukking: In mijn GitHub Action logboek zag ik dat het tekstfragment faalde voor “info@bsure-digital.n44” in plaats van “info@bsure-digital.nl” - een kleine typfout die automatisch werd gedetecteerd!

Integratie met Test Automation Frameworks

Deze open source-functionaliteit kan samenwerken met webapplicaties voor test automation die PDF’s genereren. Enkele mogelijkheden:

  • Selenium/Playwright/Cypress integratie: Download de PDF via je web automation tool en gebruik vervolgens deze functionaliteit als aparte pipeline om de inhoud te valideren
  • API testing: Combineer met API tests die PDF-generatie triggers bevatten
  • End-to-end workflows: Integreer in je volledige test suite

Dit kan een sterke business case opleveren door de kwaliteitsborging te automatiseren.

Demo en Broncode

Een volledig werkend demo-voorbeeld is beschikbaar op: 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 integreert in uw ontwikkelingsworkflow
  • Combineert met uw UI/API test automation
  • Schaalt met uw applicatie-eisen

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 applicatie aanzienlijk wordt verbeterd.

by Ralph Van Der Horst

arrow right
back to blog

share this article

Relevant articles

Hoe meerdere tests tegelijk uit te voeren in web driver io een eenvoudige handleiding

WebDriverIO: Parallelle Tests Uitvoeren - Complete Handleiding Het parallel uitvoeren van tests in WebDriverIO is een krachtige manier om je …

Read More

Verkeerslichten op de digitale snelweg een duik in google lighthouse en wdio

De verkeerslichten van de digitale snelweg: een duik in Google Lighthouse en WDIO Stel je voor dat je met je gloednieuwe virtuele auto (lees: …

Read More

Allure rapportage in webdriverio jenkins

Inleiding Allure Reporting is een krachtige tool voor het genereren van visuele en gedetailleerde testrapporten in WebdriverIO. In plaats van de …

Read More