fix: passa largura explícita ao inserir figura, evitando mismatch de DPI - #1300
fix: passa largura explícita ao inserir figura, evitando mismatch de DPI#1300Rossi-Luciano wants to merge 1 commit into
Conversation
_try_insert_picture deixava o python-docx inferir a largura da imagem por conta própria (padrão 72 DPI quando ausente metadado), independente da _infer_image_dpi já usada para decidir a largura da figura (padrão 96 DPI) - os dois podiam divergir em ~33% para a mesma imagem. Passa a largura já decidida (capada ao teto disponível, nunca ampliada) explicitamente para add_picture(width=...). Refs scieloorg#1278.
| if not (img_path and os.path.exists(img_path)): | ||
| return ceiling_width | ||
| try: | ||
| from PIL import Image |
There was a problem hiding this comment.
Mover este import para o topo, removendo desta linha e de outra neste arquivo.
|
|
||
| return img_path | ||
|
|
||
| def _natural_width_capped(img_path, ceiling_width): |
There was a problem hiding this comment.
Note que:
decide_figure_layoutescolhe entre largura total e largura de coluna._natural_width_cappeddetermina a largura efetiva de inserção, limitada ao teto.
Nelas há um trecho duplicado que se refere ao cálculo da largura natural:
px_w = im.width
dpi = _infer_image_dpi(im)
natural_width = (px_w / max(1.0, dpi)) * 2.54Ainda,
decide_figure_layoutmantém o resultado comofloatem centímetros;_natural_width_cappedconverte o resultado comCm(...), produzindo um objetoLengthrepresentado internamente em EMU.
As funções têm responsabilidades diferentes, mas ambas abrem a imagem e calculam sua largura natural a partir de pixels e DPI. Além da duplicação, atualmente elas produzem unidades diferentes: decide_figure_layout mantém um float em centímetros, enquanto _natural_width_capped retorna Cm/EMU.
Podemos extrair uma operação única que devolva a largura natural em Cm e reutilizá-la tanto na decisão quanto no limite de inserção? Isso mantém interpretação de DPI e unidade consistentes nos dois fluxos.
pitangainnovare
left a comment
There was a problem hiding this comment.
Seria desejável atender aos dois comentários apresentados, embora isso não seja obrigatório.
O que esse PR faz?
Problema existente:
_try_insert_picturechamarun.add_picture(img_path)sem passarwidth. O python-docx então lê o DPI do arquivo por conta própria pra decidir o tamanho — independente de_infer_image_dpi, a função que o resto do código já usa (emdecide_figure_layout) pra decidir se a figura cabe na coluna. As duas leituras de DPI podem discordar: sem metadado de DPI, o python-docx assume 72 DPI enquanto_infer_image_dpiassume outro valor — resultando em ~33% de diferença entre "a largura que o código decidiu" e "a largura que realmente foi inserida".Solução proposta: calcular a largura natural da imagem via
_infer_image_dpi(a mesma função já usada na decisão), capada ao teto disponível — nunca ampliando além do espaço da coluna/página — e passar esse valor explicitamente comowidth=noadd_picture(), em vez de deixar o python-docx inferir sozinho.Resultado: a largura inserida na figura passa a bater exatamente com a largura que o código decidiu. Construí um exemplo controlado (imagem de teste de 200px sem metadado de DPI, inserida numa cópia do
a1.xml) pra medir a diferença de forma isolada: antes, a imagem era inserida a 7,06cm (72 DPI, inferência própria do python-docx); depois, a 5,29cm (96 DPI,_infer_image_dpi) — exatamente a razão 96/72 do bug descrito. Não consegui reproduzir esse caso específico com os 4 artigos reais detests/fixtures/pdf/porque as imagens deles já têm DPI explícito no arquivo (divergência de DPI entre figuras, não ausência de DPI — esse é o achado #2, já em outro PR).Onde a revisão poderia começar?
packtools/sps/formats/pdf/renderer/docx/figure.py, funçõesadd_figure,_natural_width_capped(nova) e_try_insert_picture.Como este poderia ser testado manualmente?
Testes automatizados:
python -m unittest discover -s tests/sps/formats/pdf -v(inclui teste de regressão que insere uma figura sem DPI e confirma que a largura bate com_infer_image_dpi, não com o default do python-docx).Validado também ponta a ponta nos 4 fixtures reais (
a1-a4): mesma contagem de página, sem regressão — o fix não afeta imagens que já têm DPI correto/consistente.Algum cenário de contexto que queira dar?
Encontrado e corrigido originalmente durante o trabalho do PR #1279 (fechado por ser grande demais — 14 arquivos, 54% de um módulo novo em docstring). O comentário de fechamento registra este bugfix como "já validado" e destinado a ser resubmetido separadamente, junto com outro (
/2fixo no número de colunas, já em outro PR).Screenshots
Quais são os tickets relevantes?
Closes #1299.
Referências
N/A
Segurança da informação (NSI.04)
Este PR manipula dados sensíveis ou pessoais (LGPD)?
Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?
Este PR introduz, atualiza ou remove dependências de terceiros?
Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?
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?
Este PR expõe novos endpoints, telas ou serviços?
Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?