“unoconv” para facilitar a conversão de documentos
Se sua aplicação ainda depende do unoconv, comece verificando o ambiente Python/UNO e convertendo um documento cujo conteúdo você conhece. Este guia aborda um processador existente com Debian 12, um script de conversão de DOCX em lote e as verificações de PDF necessárias antes de aceitar os resultados.
O unoconv está obsoleto, e seu repositório foi arquivado. Para novas implementações, use unoserver. Use esta receita para manter um fluxo de trabalho unoconv existente enquanto planeja essa migração.
Introdução ao “unoconv” e aos seus recursos de código aberto
O unoconv é uma ferramenta de linha de comando de código aberto que chama o LibreOffice por meio das integrações com UNO. O LibreOffice fornece os filtros de importação e exportação; instalar apenas um script Python não oferece conversão de documentos. As fontes também afetam o layout resultante.
Configurar e instalar “unoconv” no seu sistema
Pré-requisitos
Use um processador legado dedicado com um ambiente já provisionado e com versões fixadas. O
processador testado usava Debian 12 Bookworm, Python 3.11.2 da distribuição,
python3-uno compatível, unoconv 0.7 do pacote Debian 0.7-2,
LibreOffice Writer 7.4.7.2 e Liberation Sans de fonts-liberation2. Essas são versões
testadas para manutenção de sistemas legados; esta página não recomenda instalar um stack antigo
para uma nova aplicação.
O processador precisa de um perfil de usuário do LibreOffice e de um diretório temporário, ambos com
permissão de escrita. Execute um lote por vez, sem uma sessão do LibreOffice no desktop ou um listener
compartilhado usando esse perfil. Use documentos DOCX e ODT conhecidos e confiáveis na primeira
conversão. Instale as fontes usadas pelos seus documentos reais antes de avaliar a paginação. Os
comandos de inspeção abaixo exigem o poppler-utils do Poppler na máquina em que você
inspeciona os PDFs. Pode ser o processador ou outra máquina com acesso aos mesmos arquivos de saída.
Instalar no Linux (Ubuntu/Debian)
O pacote Bookworm do Debian fornece essa versão legada
do unoconv. Mantenha juntos o Python da distribuição e os pacotes UNO. A verificação a seguir presume
que python3 corresponde a /usr/bin/python3, fora de um ambiente
virtual. Ela verifica cada comando separadamente e confirma que esse interpretador consegue importar
UNO antes da conversão:
(
for tool in python3 unoconv libreoffice fc-match; do
command -v "$tool" >/dev/null || { printf 'Missing %s\n' "$tool" >&2; exit 1; }
done
python3 -c 'import uno, unohelper; print(uno.__file__)' &&
python3 --version &&
unoconv --version &&
libreoffice --version &&
fc-match -f '%{family}\n' 'Liberation Sans'
)
No processador testado, UNO vinha de /usr/lib/python3/dist-packages/uno.py. Pare se a importação falhar
ou se as versões informadas diferirem das da imagem verificada da sua implantação. Esta é uma
verificação de um processador existente, não uma receita de instalação para versões atuais do
Ubuntu ou Debian.
Na máquina em que você inspecionará os resultados, verifique também as ferramentas de PDF. As verificações aqui usaram Poppler 26.08.0:
(
for tool in pdfinfo pdftotext pdftoppm; do
command -v "$tool" >/dev/null || { printf 'Missing %s\n' "$tool" >&2; exit 1; }
done
pdfinfo -v
)
Instalar no macOS
O Homebrew lista unoconv como desabilitado. Instalar apenas o LibreOffice não estabelece uma combinação funcional de unoconv e Python. Este tutorial não verifica uma instalação no macOS; siga as orientações de instalação do sucessor mantido ao migrar de um processador legado com macOS.
Instalar no Windows
Instalações nativas no Windows e no WSL estão fora do escopo testado deste guia. O WSL, por si só, não fornece o Python compatível, as integrações com UNO, as fontes ou os filtros do LibreOffice. Não considere a mudança para WSL uma solução para um ambiente não verificado.
Guia passo a passo: converter documentos de docx para PDF
Coloque um documento DOCX conhecido em document.docx e um documento ODT conhecido
em notes.odt, em um diretório de trabalho. Registre as quantidades de páginas,
os títulos, os parágrafos finais e quaisquer tabelas ou elementos gráficos esperados. Estes comandos
diretos gravam document.pdf e notes.pdf ao lado dos arquivos de
entrada e podem substituir PDFs existentes, portanto use um diretório sem esses destinos:
unoconv -f pdf ./document.docx &&
unoconv -f pdf ./notes.odt
Inspecione o resultado do DOCX com estes comandos. Eles gravam document.txt e arquivos
PNG numerados, como document-page-1.png; repetir a verificação substitui esses arquivos de
inspeção:
pdfinfo -box ./document.pdf &&
pdftotext -layout ./document.pdf ./document.txt &&
pdftoppm -cropbox -r 96 -png ./document.pdf ./document-page
Compare a quantidade de páginas e o texto extraído com o original, incluindo o último parágrafo de
cada página. Abra todos os PNGs renderizados e verifique tabelas, elementos gráficos, margens e
quebras de página. A renderização da CropBox mostra a página visível, para que o conteúdo fora dela
não possa ocultar um problema de corte. Repita a inspeção de notes.pdf usando
notes.txt e o prefixo notes-page. Um arquivo não vazio, uma
assinatura PDF ou um comando bem-sucedido não comprova que o documento correto foi convertido
integralmente.
Automatizar tarefas de conversão de documentos com scripts
Para um lote sequencial de DOCX nessa instalação funcional, salve o código a seguir como
convert_documents.py. Ele seleciona arquivos regulares com o sufixo
.docx, sem distinguir maiúsculas de minúsculas, ignora diretórios e links
simbólicos e exige um novo diretório de saída. Sua verificação básica do arquivo contêiner reconhece
os namespaces do documento principal de OOXML Transitional e Strict; ela não é um validador completo
do esquema OOXML.
import os
from pathlib import Path
import posixpath
import subprocess
import sys
from tempfile import TemporaryDirectory
from urllib.parse import unquote
from xml.etree import ElementTree
from zipfile import BadZipFile, ZipFile
import zlib
def check_docx(path):
relationship_types = {
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
'http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument',
}
document_tags = {
'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}document',
'{http://purl.oclc.org/ooxml/wordprocessingml/main}document',
}
with ZipFile(path) as archive:
if archive.testzip() is not None:
raise ValueError('Damaged DOCX member')
relationships = ElementTree.fromstring(archive.read('_rels/.rels'))
main_parts = [item for item in relationships
if item.get('Type') in relationship_types
and item.get('TargetMode', 'Internal') == 'Internal']
if len(main_parts) != 1:
raise ValueError('Missing main document relationship')
part = posixpath.normpath(unquote(main_parts[0].get('Target', '')).lstrip('/'))
types = ElementTree.fromstring(archive.read('[Content_Types].xml'))
content_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml'
if not any(item.get('PartName') == '/' + part
and item.get('ContentType') == content_type for item in types):
raise ValueError('Not a DOCX main part')
if ElementTree.fromstring(archive.read(part)).tag not in document_tags:
raise ValueError('Not a Word document')
def convert_documents(input_dir, output_dir):
source = Path(input_dir).resolve(strict=True)
files = sorted(path for path in source.iterdir()
if not path.is_symlink() and path.is_file()
and path.suffix.lower() == '.docx')
if not files:
print('No DOCX files found', file=sys.stderr)
return 1
output = Path(output_dir).absolute()
output.mkdir(mode=0o700)
failures = 0
for path in files:
try:
check_docx(path)
with TemporaryDirectory(prefix='.conversion-', dir=output) as temporary:
candidate = Path(temporary) / 'result.pdf'
subprocess.run(
['unoconv', '-f', 'pdf', '-o', str(candidate), str(path)],
check=True, timeout=120,
)
if not candidate.is_file() or candidate.stat().st_size == 0:
raise ValueError('No nonempty PDF was produced')
# Hard-link publication fails instead of replacing a destination that appeared.
os.link(candidate, output / (path.stem + '.pdf'))
print(f'Converted {path.name}')
except (OSError, ValueError, KeyError, BadZipFile, ElementTree.ParseError,
zlib.error, NotImplementedError, RuntimeError, subprocess.SubprocessError):
failures += 1
print(f'Conversion failed: {path.name}', file=sys.stderr)
return 1 if failures else 0
if __name__ == '__main__':
if len(sys.argv) != 3:
sys.exit('Usage: python3 convert_documents.py <input-directory> <new-output-directory>')
try:
sys.exit(convert_documents(sys.argv[1], sys.argv[2]))
except (OSError, ValueError):
sys.exit('Cannot prepare conversion directories; use an existing input and a new output directory')
Execute o script com seus arquivos DOCX em docs e sem que o diretório
new-pdfs exista:
python3 convert_documents.py docs new-pdfs
O script tenta processar os arquivos seguintes após uma falha e termina com status 1 se qualquer arquivo falhar. Ele informa o nome do arquivo que falhou, remove os candidatos temporários e preserva os destinos existentes, inclusive em colisões entre nomes com a mesma base. Os PDFs bem-sucedidos continuam disponíveis quando outro arquivo falha. Inspecione-os com as verificações de texto e renderização acima antes de usá-los. A publicação significa apenas que o conversor terminou com sucesso e produziu um candidato não vazio.
Para uma nova tentativa, mantenha os PDFs publicados anteriormente e use outro diretório de saída novo. Uma seleção vazia faz o script terminar com status 1 sem criar o diretório de saída. O tempo limite de 120 segundos limita a duração de cada processo cliente; ele não garante que o LibreOffice parou de trabalhar. Um processador interrompido pode deixar diretórios intermediários, portanto inspecione o armazenamento pertencente a ele antes de tentar novamente. Este script serve para um lote local confiável, não para um endpoint de uploads não confiáveis. Um processador desse tipo precisa de supervisão própria de processos, limites de recursos e isolamento do sistema de arquivos.
Solucionar problemas comuns
Conflitos de versão do Python
Se a verificação de importação de UNO falhar, confira qual Python está em execução e se ele é compatível com a ponte UNO instalada. O projeto original documenta esse requisito do interpretador. Um ambiente virtual arbitrário não é suficiente: um venv padrão oculta o módulo UNO da distribuição. Alterar apenas a versão do Python não instala uma ponte compatível.
Compatibilidade de versões do LibreOffice
O unoconv do Debian testado emite um aviso de descontinuação de distutils no
Python 3.11; mesmo uma conversão bem-sucedida exige verificar os PDFs efetivamente produzidos.
Mantenha a combinação verificada de Python/UNO/LibreOffice na sua imagem legada. Se um documento
falhar, tente abrir e exportar esse mesmo documento no LibreOffice para distinguir um problema de
importação/exportação de um problema no caminho de execução do cliente unoconv.
Considerações sobre implantação em contêineres
Um contêiner deve fornecer diretórios de perfil, cache, dados, execução e arquivos temporários com
permissão de escrita para o usuário que executa a conversão. Crie os diretórios antes de iniciar o
LibreOffice ao definir XDG_CONFIG_HOME, XDG_CACHE_HOME,
XDG_DATA_HOME ou XDG_RUNTIME_DIR. A ausência de um perfil com permissão
de escrita pode impedir a inicialização antes da conversão do documento. Mantenha as tarefas
sequenciais e pare todo o processador sob sua responsabilidade quando um cliente atingir o tempo
limite; encerrar apenas o cliente não comprova a limpeza.
Alternativas modernas e comparação
| Implantação | Escolha |
|---|---|
| Processador legado existente com versões fixadas | Manter unoconv enquanto testa documentos representativos. |
| Nova implantação | Usar unoserver. |
Unoserver (recomendado)
O unoserver separa um processador persistente do LibreOffice de seu cliente de conversão. Seu servidor ainda precisa de um interpretador Python capaz de importar UNO; ele não elimina essa dependência. Siga as instruções de instalação do unoserver e depois consulte conversão de documentos a partir de Rust para conhecer a integração mantida. Os testes desta página abrangem unoconv, não esse fluxo de trabalho separado com servidor.
CLI do LibreOffice
Para uma conversão pontual, o LibreOffice também oferece uma
opção de exportação por linha de comando.
No processador Debian testado, este comando grava document.pdf no diretório atual:
libreoffice --headless --convert-to pdf ./document.docx
Use um destino que ainda não exista e faça a mesma inspeção de PDF. O LibreOffice pode substituir um PDF existente, e o status de término, por si só, não comprova o conteúdo resultante. Esse caminho contorna o cliente unoconv, mas ainda usa os filtros e as fontes do LibreOffice.
Soluções na nuvem
Se você prefere uma interface de API a operar um processador de documentos de escritório, avalie o serviço de conversão de documentos da Transloadit com seus documentos representativos. Essa é uma integração separada; os comandos locais acima não testam um serviço hospedado.
