Docling 2.135.0: DOCX e XLSX viram tabela Markdown, PDF travou

Docling 2.135.0 converteu DOCX e XLSX em tabela Markdown sem modelo; o PDF travou no download de modelos. A novidade é ler tabela .md com negrito.

Rogério Rodrigues

Rogério Rodrigues

Laudo sintético em DOCX convertido pelo Docling 2.135.0 em tabela Markdown com Hemoglobina 11,2, Leucócitos 7.850, Glicose 126 e Potássio 5,8, negrito preservado, em 0,06 s.

Rodei o Docling 2.135.0 num laudo de exames fictício. Em DOCX e XLSX, docling laudo_sintetico.docx exames_sinteticos.xlsx --to md devolveu tabela Markdown limpa em 0,06 s e 0,02 s, com o negrito dos resultados fora da referência preservado e sem baixar modelo nenhum. No PDF, travou: o pipeline de PDF precisa baixar modelos de layout e de tabela, e a rede do ambiente onde rodei bloqueia esses downloads. E um detalhe que muda a leitura do changelog: a melhoria de tabela Markdown da v2.135.0 é na leitura de arquivos .md. Na exportação, o mesmo DOCX saiu idêntico na 2.134.0.

Abaixo, o comando, a saída real e o que eu faria pra rodar o PDF de verdade.

O que o Docling faz e por que isso importa pro LLM

Docling é um conversor de documentos open source, licença MIT, iniciado pela IBM Research Zurich e hoje hospedado na LF AI & Data Foundation. Ele lê PDF, DOCX, PPTX, XLSX, HTML, EPUB, Markdown e outros formatos, monta uma representação única do documento e exporta pra Markdown, JSON, HTML ou texto. Roda local.

O motivo de usar antes do LLM é simples. Laudo de laboratório é tabela. Se o PDF sai como texto corrido, a linha “Potássio 5,8 mEq/L 3,5 a 5,1” pode virar uma sopa de números e o modelo precisa adivinhar qual é resultado e qual é referência. Em Markdown com pipes, cada valor fica preso à coluna. No RAG, o chunk carrega o cabeçalho junto. Menos chute.

O que mudou na v2.135.0, lido no changelog

A versão saiu em 07/10/2026 no GitHub e no PyPI. Duas features e 27 correções:

Mudança PR O que faz Testei?
iwork #4630 Renderiza imagem de gráfico do Numbers e lê os tipos de gráfico do iWork ’09 Não. Não tenho arquivo .numbers sintético
md #4488 Preserva formatação inline (negrito, itálico, código, link) dentro de célula de tabela em arquivo Markdown de entrada Sim, 2.134.0 contra 2.135.0

A pauta que chegou pra mim dizia “exporta tabela Markdown melhor”. Fui no PR. A descrição é clara: o MarkdownDocumentBackend lia células de tabela como texto puro e jogava fora a marcação. Agora usa o parser GFM do Marko e guarda a formatação. É entrada, não saída.

O teste: um laudo fictício em três formatos

Instalação

python3 -m venv .venv
.venv/bin/pip install "docling==2.135.0" reportlab
.venv/bin/docling --version
# Docling version: 2.135.0
# Docling Core version: 2.100.0
# Docling Parse version: 7.22.2

Gerei o mesmo laudo em PDF (reportlab), DOCX (python-docx) e XLSX (openpyxl). Paciente inventada, seis exames. No DOCX, os resultados fora da referência vão em negrito, como muito laboratório faz:

fora = {"Hemoglobina", "Glicose em jejum", "Potássio"}
for i, row in enumerate(linhas):
    cells = t.add_row().cells
    for j, v in enumerate(row):
        r = cells[j].paragraphs[0].add_run(v)
        if j == 1 and row[0] in fora:
            r.bold = True   # resultado fora da referência em negrito

PDF: parou no download de modelo

docling laudo_sintetico.pdf --to md --output out
# rapidocr...DownloadFileException: Failed to download
#   https://www.modelscope.cn/.../PP-OCRv6_det_small.pth

docling laudo_sintetico.pdf --no-ocr --to md --output out
# httpx.ProxyError: 403 Forbidden

Primeira tentativa: o OCR padrão (RapidOCR) tenta baixar pesos do ModelScope. Desliguei o OCR, porque o PDF é nascido digital e tem camada de texto. Segunda tentativa: caiu em httpx.ProxyError: 403 Forbidden. O log não diz o host. A política de rede da máquina nega huggingface.co, download.pytorch.org e modelscope.cn. Nenhum Markdown saiu do PDF. Então não tenho resultado próprio de tabela extraída de PDF pra mostrar aqui.

DOCX e XLSX: Markdown limpo, sem modelo

docling laudo_sintetico.docx exames_sinteticos.xlsx --to md --output out

Saída real do DOCX:

## Laboratório Fictício Ltda - Laudo de exames

Paciente: Maria Teste da Silva (dado sintético) - Coleta: 01/10/2026

| Exame            | Resultado   | Unidade   | Referência     |
|------------------|-------------|-----------|----------------|
| Hemoglobina      | **11,2**    | g/dL      | 12,0 a 15,5    |
| Leucócitos       | 7.850       | /mm³      | 4.000 a 11.000 |
| Glicose em jejum | **126**     | mg/dL     | 70 a 99        |
| Creatinina       | 0,9         | mg/dL     | 0,5 a 1,1      |
| Potássio         | **5,8**     | mEq/L     | 3,5 a 5,1      |
| TSH              | 2,1         | µUI/mL    | 0,4 a 4,0      |

Valores em negrito estão fora da referência.

Acento, µ e ³ intactos. Vírgula decimal brasileira intacta. O negrito que marca o valor alterado chegou até o Markdown, e é isso que o LLM vai ler. A planilha saiu com a mesma tabela, sem negrito porque eu não formatei a XLSX. DOCX e XLSX usam backends declarativos: leem a estrutura do arquivo, sem rede neural. Por isso rodaram mesmo com os downloads bloqueados.

Rodei o mesmo DOCX no Docling 2.134.0. Saída idêntica, diff vazio. Pra esse caso, atualizar não muda nada.

Tabela em Markdown como entrada: aqui a v2.135.0 aparece

Escrevi um .md com tabela que tem negrito, código e link nas células, e passei pelas duas versões com --from md --to md. Entrada:

# Exames de controle (dado sintético)

| Exame | Resultado | Referência |
|---|---|---|
| Glicose em jejum | **126** mg/dL | 70 a 99 |
| Potássio | **5,8** mEq/L | 3,5 a 5,1 |
| TSH | 2,1 µUI/mL | 0,4 a 4,0 |
| Creatinina | `0,9` mg/dL | [ver protocolo](https://example.org/protocolo) |

Comando e saída nas duas versões (trecho):

docling exames_sinteticos.md --from md --to md --output out
# 2.134.0
| Glicose em jejum | 126 mg/dL   | 70 a 99       |
| Potássio         | 5,8 mEq/L   | 3,5 a 5,1     |
| Creatinina       | 0,9 mg/dL   | ver protocolo |

# 2.135.0
| Glicose em jejum | **126** mg/dL | 70 a 99                                        |
| Potássio         | **5,8** mEq/L | 3,5 a 5,1                                      |
| Creatinina       | `0,9` mg/dL   | [ver protocolo](https://example.org/protocolo) |

Na 2.134.0, o negrito sumia e o link virava só o texto. A URL ia embora. Na 2.135.0, tudo fica. Se o seu RAG ingere documentação interna em Markdown, wiki exportada ou protocolo escrito em .md, essa é a mudança que interessa: o link pro protocolo agora chega no chunk.

Como rodar o PDF com os modelos (pela documentação)

Esta parte eu não consegui executar. É o caminho que a documentação oficial descreve pra ambiente sem internet: baixar os modelos numa máquina com acesso e apontar o conversor pra pasta.

docling-tools models download
# baixa layout, tableformer, picture classifier, code formula e RapidOCR
# em $HOME/.cache/docling/models
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions, TableFormerMode
from docling.document_converter import DocumentConverter, PdfFormatOption

opts = PdfPipelineOptions(artifacts_path="/opt/docling-models")
opts.do_ocr = False                 # PDF nascido digital
opts.do_table_structure = True
opts.table_structure_options.mode = TableFormerMode.ACCURATE

conv = DocumentConverter(
    format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)}
)
md = conv.convert("laudo_sintetico.pdf").document.export_to_markdown()

Confirmei que esse código importa e instancia o conversor na 2.135.0. A conversão do PDF em si ficou sem rodar. Na CLI, o equivalente é --artifacts-path e --table-mode accurate.

Limites e o que não funcionou

  • PDF sem rede não roda de primeira. O pip instala o pacote, mas os modelos vêm depois, na primeira conversão. Em servidor de hospital com saída bloqueada, você descobre isso em produção se não fizer o prefetch antes.
  • O OCR padrão baixa de outro lugar. No meu teste, o RapidOCR foi buscar pesos no ModelScope, fora do Hugging Face. Liberar só um host no firewall não basta.
  • Não testei a extração de tabela de PDF. Qualidade de TableFormer em laudo brasileiro com célula mesclada, cabeçalho repetido por página e rodapé de assinatura segue sem medição minha.
  • Não testei o Numbers. A feature de gráfico do iWork está só lida no changelog.
  • Markdown limpo não é Markdown anônimo. O nome da paciente fictícia passou inteiro pra saída. Anonimizar é outra etapa, e o que acontece quando ela falha eu contei em A IA achou o CPF. E o CPF vazou mesmo assim.

Quando não usar

  • O dado já nasce estruturado. Exame que vem de HL7, FHIR ou banco do LIS não precisa virar PDF e voltar. Leia a fonte.
  • Você só tem XLSX ou CSV simples. pandas resolve com menos dependência. O Docling compensa quando há mistura de formatos.
  • Seu ambiente não pode baixar modelo nem receber pasta de modelo por outro caminho. Aí o pipeline de PDF não sai do lugar.
  • O PDF é escaneado e de baixa qualidade. OCR erra dígito, e dígito errado em potássio é problema clínico. Precisa de revisão humana de qualquer jeito.

Fonte e como reproduzir

Pra reproduzir: Python 3.11, duas venvs (docling==2.134.0 e docling==2.135.0), gere o DOCX com python-docx e o .md com a tabela acima, rode os comandos deste post e compare com diff. Todos os dados são sintéticos.

In English

I ran Docling 2.135.0 on a synthetic lab report. DOCX and XLSX converted to clean Markdown tables in under 0.1 s with no model download, keeping bold on out-of-range values. The PDF pipeline failed because layout and table models could not be downloaded in my environment. The v2.135.0 table change (#4488) is about reading Markdown input: bold, code and links inside table cells are now kept, where 2.134.0 dropped them.

Última revisão em 07/10/2026.

Gostou? O próximo teste sai primeiro no LinkedIn e no Instagram.

Se você põe dado pessoal brasileiro em IA no trabalho e tem uma dúvida, me escreve. O que der pra responder em público vira artigo aqui.