Skip to content

fix: autodetecta binário do LibreOffice na conversão de DOCX para PDF - #1292

Merged
Rossi-Luciano merged 2 commits into
scieloorg:masterfrom
Rossi-Luciano:fix/1291-libreoffice-binary-autodetect
Aug 26, 2026
Merged

fix: autodetecta binário do LibreOffice na conversão de DOCX para PDF#1292
Rossi-Luciano merged 2 commits into
scieloorg:masterfrom
Rossi-Luciano:fix/1291-libreoffice-binary-autodetect

Conversation

@Rossi-Luciano

Copy link
Copy Markdown
Contributor

O que esse PR faz?

Corrige o comando pdf_generator (CLI de geração de PDF), que quebrava ao converter o DOCX intermediário para PDF quando a flag --libreoffice-binary não era informada, mesmo com o LibreOffice instalado no PATH. convert_docx_to_pdf agora autodetecta o binário (tenta libreoffice, depois soffice) via shutil.which, e levanta um RuntimeError claro quando nenhum é encontrado, em vez do TypeError interno de subprocess que ocorria antes.

Onde a revisão poderia começar?

packtools/sps/formats/pdf/utils/file_utils.py, função convert_docx_to_pdf.

Como este poderia ser testado manualmente?

Com o LibreOffice instalado (soffice ou libreoffice no PATH):

python -m packtools.sps.formats.pdf_generator -i tests/fixtures/pdf/a1.xml -l tests/fixtures/pdf/layout.docx -o saida.pdf

Antes deste PR, esse comando quebrava com TypeError: expected str, bytes or os.PathLike object, not NoneType. Depois, gera saida.pdf normalmente.

Testes automatizados: python -m unittest tests.sps.formats.pdf.utils.test_file_utils -v (4 casos: binário explícito respeitado, autodetecção de libreoffice, fallback para soffice, erro claro sem binário).

Algum cenário de contexto que queira dar?

--libreoffice-binary não tinha default no argparse do CLI, então ficava None quando omitida — e esse None era repassado explicitamente para convert_docx_to_pdf, sobrescrevendo o default "libreoffice" que a própria função já declarava (um default de parâmetro só é usado quando o argumento não é passado na chamada; o CLI sempre passava). Isso bloqueava a geração de PDF para qualquer usuário seguindo o --help do comando.

Screenshots

N/A (mudança de backend/CLI, sem interface visual).

Quais são os tickets relevantes?

Closes #1291.

Referências

N/A


Segurança da informação (NSI.04)

Seção obrigatória. Marque as opções aplicáveis e justifique quando necessário. Referência: NSI.04 - Norma de Desenvolvimento Seguro.

Este PR manipula dados sensíveis ou pessoais (LGPD)?

  • Não

Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?

  • Não

Este PR introduz, atualiza ou remove dependências de terceiros?

  • Não

Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?

  • Não aplicável a este PR (justifique): Trivy/SonarQube não estão configurados neste repositório (packtools é biblioteca Python, não serviço containerizado) — ver SECURITY_ADHERENCE.md. Os gates automáticos reais deste repositório (Snyk e GitGuardian) rodam via CI neste PR.

Este PR concatena, monta ou executa comandos SQL, HTML ou JavaScript a partir de entrada externa?

  • Não — o único comando externo é a chamada ao binário do LibreOffice via subprocess.run com lista de argumentos fixa (sem shell=True, sem interpolação de entrada externa).

Este PR expõe novos endpoints, telas ou serviços?

  • Não

Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?

  • Não, nenhum segredo foi commitado

convert_docx_to_pdf tinha default="libreoffice", mas o CLI (pdf_generator.py)
sempre repassava arguments.libreoffice_binary explicitamente, que fica None
quando --libreoffice-binary não é informado - isso sobrescrevia o default da
função (default de parâmetro só vale quando o argumento não é passado na
chamada) e quebrava o subprocess com TypeError, mesmo com o LibreOffice
instalado e no PATH.

Move a resolução do binário para dentro de convert_docx_to_pdf: autodetecta
via shutil.which (tenta "libreoffice", depois "soffice", já que várias
instalações só têm um dos dois) e levanta RuntimeError claro quando nada é
encontrado, em vez do TypeError interno do subprocess.

Fixes scieloorg#1291.
Comment on lines +27 to +34
if not libreoffice_binary:
libreoffice_binary = shutil.which("libreoffice") or shutil.which("soffice")
if not libreoffice_binary:
raise RuntimeError(
"LibreOffice binary not found on PATH. Install LibreOffice, or pass "
"libreoffice_binary (CLI: --libreoffice-binary) with the path to the "
"'libreoffice' or 'soffice' executable."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sugiro chamar de binary pois pode ser MSOffice, LibreOffice, etc. Sugiro também que trate como FileNotFoundError.

binary = libreoffice_binary or shutil.which("libreoffice") or shutil.which("soffice")
if not binary:
    raise FileNotFoundError("LibreOffice binary ('libreoffice' or 'soffice') was not found")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concordo, ajustado. Renomeei a variável resolvida para binary e troquei RuntimeError por FileNotFoundError quando o binário não é encontrado, seguindo o snippet sugerido. Commit a1d31d51.

Comment on lines 36 to 37
output_dir = os.path.dirname(docx_path)
os.makedirs(output_dir, exist_ok=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Se informar na CLI "-o saida.pdf", aqui vai gerar um os.makedirs(""), causando erro. Sugiro corrigir para algo como:

output_dir = os.path.dirname(docx_path) or "."
if output_dir != ".":
    os.makedirs(output_dir, exist_ok=True)

Veja o que ocorre com "saida.pdf":

>>> os.path.dirname("saida.pdf")
''
>>> os.makedirs('')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<frozen os>", line 225, in makedirs
FileNotFoundError: [Errno 2] No such file or directory: ''
>>> 

E com ".":

>>> os.makedirs('.', exist_ok=True)
>>> 

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bom achado, é o mesmo bug da issue #773. Corrigido com output_dir = os.path.dirname(docx_path) or ".", pulando o makedirs nesse caso. Validei manualmente com -o saida.pdf (sem diretório): gera o PDF normalmente agora. Coberto também em test_docx_path_without_directory_component_does_not_raise. Commit a1d31d51.

@pitangainnovare pitangainnovare left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precisa fazer pequenos ajustes que envolvem o makedirs

Ajustes pedidos na revisão do PR scieloorg#1292:

- Variável do binário resolvido renomeada para "binary" (pode ser
  libreoffice ou soffice, não só "libreoffice"); ausência agora levanta
  FileNotFoundError em vez de RuntimeError.
- output_dir = os.path.dirname(docx_path) é "" quando docx_path não tem
  componente de diretório (ex.: CLI com -o saida.pdf), e os.makedirs("")
  levanta FileNotFoundError - mesmo bug já registrado na issue scieloorg#773.
  Corrigido com output_dir = os.path.dirname(docx_path) or "." e pulando
  o makedirs nesse caso.

Validado manualmente com "-o saida.pdf" (sem diretório) - antes quebrava,
agora gera o PDF normalmente.

@pitangainnovare pitangainnovare left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correto.

@Rossi-Luciano
Rossi-Luciano merged commit 1dff9d2 into scieloorg:master Aug 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pdf_generator: CLI quebra ao gerar PDF sem passar --libreoffice-binary explicitamente

2 participants