dof2md: OCR for the Official Gazette

Turning a DOF edition’s PDF straight into Markdown

Published

August 26, 2026

Abstract

dof2md is the LegalIA package that never looks at a “note” at all: given a PDF or a set of scanned page images from Mexico’s Official Gazette — DOF, for Diario Oficial de la Federación, its Spanish name — it produces Markdown, optionally cropped down to a single note by title. It is, underneath, a wrapper around mineru for the OCR and layout analysis; this page shows it converting a real, current edition end to end, and explains what dof2md adds on top of mineru itself.

Open in Colab

The packages are available on PyPI. If you are running this notebook outside the repository — on Google Colab, for instance — uncomment the following cell:

Code
# %pip install dof2md

1 What dof2md does

dof2md operates at the coarsest grain of the three LegalIA packages that work with the gazette: it has no notion of which note a page belongs to, only of pages. Given a PDF — a whole DOF edition, or any other document — or an ordered list of scanned page images, it produces Markdown. It is, underneath, a wrapper around mineru for the OCR and layout analysis itself; that should be explicit rather than implied. On top of mineru, dof2md adds:

  • Keeping mineru’s own mineru-api server warm across a batch of documents, instead of paying its startup (and model-loading) cost once per document.
  • Stitching the OCR of a list of page images — several scanned pages of the same note — into one continuous Markdown document.
  • Rewriting the raw HTML tables mineru falls back to (rowspan/colspan) into Markdown tables.
  • Cropping the result down to a single note, by locating its title and the next note’s title in the OCR’d text — useful because a scanned page usually holds the tail of one note and the head of the next.

It is only needed directly for notes that predate the HTML era of the gazette (pre-1999-ish) and survive only as scanned images — nota2md borrows it lazily as its own OCR fallback for that path. Used on its own, as here, it never talks to dofjson or nota2md at all.

This page tells one story end to end, with one real edition; it is not the API reference. For every option BatchConverter/dof2md accept, each with its own worked example, see dof2md’s page on Read the Docs.

2 A real edition, converted

On June 16, 2026, the DOF’s evening edition (VES) carried a single decree: administrative measures for the venues of the 2026 FIFA World Cup in Mexico City and the Guadalajara metropolitan area. At four pages it is one of the shortest editions in the gazette’s recent archive, and, being a current edition, its PDF already carries the pages’ own text rather than only a scan of them, which keeps mineru’s OCR pass short. That makes it a fast, realistic example of dof2md converting a complete edition, with dofjson/nota2md and their note lookups nowhere in the picture:

Code
import datetime as dt
from pathlib import Path

import dofjson

date = dt.date(2026, 6, 16)

Path("output").mkdir(exist_ok=True)
pdf_path = dofjson.download_edicion_pdf(date, "VES", Path("output"))
pdf_path
PosixPath('output/edicion-327945.pdf')

BatchConverter is dof2md’s entry point — the Python name behind the dof2md command — and it keeps mineru’s server warm, which only pays off across a batch; for a single document it still runs the same OCR/layout pipeline underneath:

Code
from dof2md import BatchConverter

with BatchConverter() as convert:
    md_path = convert(pdf_path, "output", "16062026-VES.md")

md_path
2026-09-02 19:41:12.110 | INFO     | mineru.cli.client:run_planned_task:832 - Submitting batch 1/1 | 1 document, 4 pages in this batch | 4 pages total | task#1 [edicion-327945]
2026-09-02 19:41:35.416 | INFO     | mineru.cli.client:run_planned_task:883 - Completed batch 1/1 | Processed 4/4 pages | 1 of 1 batch finished | task#1 [edicion-327945]
PosixPath('output/16062026-VES.md')

The result is one continuous Markdown document — the cover page’s own index of contents, then the decree’s text:

Code
print(md_path.read_text(encoding="utf-8")[:1200])
![](edicion-327945_images/3ca9bcd469bd7ec19708ed49de120329e3e58f07effde3441ab4b752411fada7.jpg)

# DIARIO OFICIAL DE LA FEDERACION

ORGANO DEL GOBIERNO CONSTITUCIONAL DE LOS ESTADOS UNIDOS MEXICANOS

No. de publicación: 159/2026

Ciudad de México, martes 16 de junio de 2026

## EDICION VESPERTINA CONTENIDO

PODER EJECUTIVO

PRESIDENCIA DE LA REPUBLICA

Decreto por el que se establecen diversas medidas administrativas en el marco de los eventos relativos a la Copa Mundial de la FIFA 2026, a celebrarse en la Ciudad de México y en la zona metropolitana de Guadalajara, Jalisco. ....

# PODER EJECUTIVO

# PRESIDENCIA DE LA REPUBLICA

DECRETO por el que se establecen diversas medidas administrativas en el marco de los eventos relativos a la Copa Mundial de la FIFA 2026, a celebrarse en la Ciudad de México y en la zona metropolitana de Guadalajara, Jalisco.

Al margen un sello con el Escudo Nacional, que dice: Estados Unidos Mexicanos.- Presidencia de la República.

CLAUDIA SHEINBAUM PARDO, Presidenta de los Estados Unidos Mexicanos, en ejercicio de la facultad que me confiere el artículo 89, fracción I, de la Constitución Política de los Estados Unidos Mexicanos; con fundamento en los ar

This edition carries a single note, so nothing needed cropping. A typical edition carries dozens, and the same call accepts a titulo and titulo_siguiente to keep only the text between them — the note’s own title and the next note’s, as they appear in the gazette’s index. See dof2md’s page on Read the Docs for that full example, worked with a scanned page holding two notes back to back, and for min_confidence, keep_pages and the other options BatchConverter.__call__ accepts.

3 From the command line

Downloading a whole edition’s PDF by date and edition is a dofjson function, not a CLI flag (dofjson.download_edicion_pdf(date, edicion, outdir), as above) — dof2md itself never downloads anything. Once the PDF is sitting in output/, --pdf converts it from the command line:

dof2md --pdf output/edicion-327945.pdf --filename local-copy.md

--titulo/--titulo-siguiente crop the result to one note the same way BatchConverter does above; see dof2md’s page on Read the Docs for the full flag reference, including --images for a set of scanned pages already on disk.

Methodological note

This page is a Jupyter notebook executed at render time; the PDF converted above is the DOF’s own evening edition of June 16, 2026, downloaded fresh via dofjson.download_edicion_pdf from SIDOF (https://sidof.segob.gob.mx). It tells one story, with one real document; the package’s full API — every public and private symbol, each public one with its own worked example — is on Read the Docs, not duplicated here.

This page was written by the LegalIA team together with Claude, Anthropic’s coding assistant, through Claude Code: the assistant helped design and implement dof2md itself, along with its test suite. As with the rest of the project, the authors reviewed and validated every change before it was committed.