Aller au contenu principal

Headers, footers et pagination

Extraction des en-têtes et pieds de page

Depuis la version OCR 2512 (décembre 2025), le modèle OCR 3 peut extraire séparément les en-têtes (headers) et pieds de page (footers) de chaque page. Cette fonctionnalité est particulièrement utile pour les documents formels : contrats, rapports annuels, documents réglementaires.

Pourquoi séparer headers et footers ?

Dans un workflow de traitement documentaire, les en-têtes et pieds de page posent un problème récurrent :

  • Ils se répètent sur chaque page (nom de l’entreprise, numéro de page, date)
  • Ils polluent le contenu principal lors de l’indexation
  • Ils créent du bruit dans les résultats de recherche RAG
  • Mais ils contiennent parfois des métadonnées utiles (version du document, classification)

En les extrayant séparément, vous pouvez décider de les inclure ou non selon votre cas d’usage.

Accéder aux headers et footers

Les champs header et footer sont disponibles directement dans la réponse de chaque page :

import os
from mistralai import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

ocr_response = client.ocr.process(
    model="mistral-ocr-latest",
    document={
        "type": "document_url",
        "document_url": "https://exemple.com/contrat.pdf"
    }
)

for page in ocr_response.pages:
    print(f"--- Page {page.index + 1} ---")

    if page.header:
        print(f"En-tête : {page.header}")

    print(f"Contenu : {page.markdown[:100]}...")

    if page.footer:
        print(f"Pied de page : {page.footer}")

    print()

Gestion de la pagination

Numérotation des pages

Chaque page est indexée à partir de 0. Pour des documents multi-pages, vous pouvez reconstruire la pagination :

def extraire_avec_pagination(ocr_response) -> dict:
    """Extrait le contenu en préservant la pagination."""
    document = {
        "nombre_pages": len(ocr_response.pages),
        "pages": []
    }

    for page in ocr_response.pages:
        page_data = {
            "numero": page.index + 1,
            "contenu": page.markdown,
            "header": page.header or "",
            "footer": page.footer or "",
            "dimensions": {
                "largeur": page.dimensions.width if page.dimensions else None,
                "hauteur": page.dimensions.height if page.dimensions else None,
            },
            "nombre_images": len(page.images) if page.images else 0,
        }
        document["pages"].append(page_data)

    return document

Dimensions des pages

Les dimensions sont retournées en points PDF (1 point = 1/72 pouce). Les formats courants :

  • A4 : 595 x 842 points
  • Letter : 612 x 792 points
  • A3 : 842 x 1191 points
for page in ocr_response.pages:
    if page.dimensions:
        largeur_cm = page.dimensions.width * 2.54 / 72
        hauteur_cm = page.dimensions.height * 2.54 / 72
        print(f"Page {page.index + 1}: {largeur_cm:.1f} x {hauteur_cm:.1f} cm")

Filtrer le contenu principal

Pour vos pipelines RAG, vous voudrez souvent exclure les headers et footers répétitifs :

def contenu_principal(ocr_response) -> str:
    """Extrait uniquement le contenu principal, sans headers/footers."""
    parties = []

    for page in ocr_response.pages:
        contenu = page.markdown

        # Optionnel : retirer les headers/footers du markdown
        # s'ils sont inclus dans le contenu principal
        if page.header and contenu.startswith(page.header):
            contenu = contenu[len(page.header):].strip()

        if page.footer and contenu.endswith(page.footer):
            contenu = contenu[:-len(page.footer)].strip()

        parties.append(contenu)

    return "\n\n".join(parties)

Extraction des métadonnées depuis les footers

Les pieds de page contiennent souvent des informations utiles :

import re

def extraire_metadonnees_footer(ocr_response) -> dict:
    """Extrait les métadonnées depuis les pieds de page."""
    metadonnees = {
        "numeros_page": [],
        "dates": [],
        "versions": [],
        "classifications": [],
    }

    for page in ocr_response.pages:
        footer = page.footer or ""

        # Recherche de numéro de page
        match_page = re.search(r"(\d+)\s*/\s*(\d+)", footer)
        if match_page:
            metadonnees["numeros_page"].append(
                f"{match_page.group(1)}/{match_page.group(2)}"
            )

        # Recherche de date
        match_date = re.search(r"\d{2}/\d{2}/\d{4}", footer)
        if match_date:
            metadonnees["dates"].append(match_date.group())

        # Recherche de version
        match_version = re.search(r"v(?:ersion)?\s*(\d+\.?\d*)", footer, re.I)
        if match_version:
            metadonnees["versions"].append(match_version.group(1))

        # Classification (Confidentiel, Public, etc.)
        for classification in ["confidentiel", "public", "interne", "secret"]:
            if classification in footer.lower():
                metadonnees["classifications"].append(classification)

    return metadonnees

Traitement par plages de pages

Pour les documents volumineux, vous pouvez vouloir traiter uniquement certaines pages :

def extraire_plage(ocr_response, debut: int, fin: int) -> str:
    """Extrait le contenu d'une plage de pages (1-indexé)."""
    parties = []

    for page in ocr_response.pages:
        numero = page.index + 1
        if debut <= numero <= fin:
            parties.append(page.markdown)

    return "\n\n---\n\n".join(parties)


# Extraire les pages 5 à 10
contenu_partiel = extraire_plage(ocr_response, 5, 10)

Points clés à retenir

  • Les headers et footers sont extraits séparément depuis OCR 2512
  • Filtrez-les pour éviter le bruit dans vos pipelines RAG
  • Les dimensions de page sont en points PDF (72 points = 1 pouce)
  • Les footers contiennent souvent des métadonnées utiles (version, date, classification)
  • Traitez par plages de pages pour les documents volumineux