Skip to content

fix: parametriza número de colunas do corpo (era 2 fixo em vários lugares) - #1298

Open
Rossi-Luciano wants to merge 1 commit into
scieloorg:masterfrom
Rossi-Luciano:fix/pdf-configurable-column-count
Open

fix: parametriza número de colunas do corpo (era 2 fixo em vários lugares)#1298
Rossi-Luciano wants to merge 1 commit into
scieloorg:masterfrom
Rossi-Luciano:fix/pdf-configurable-column-count

Conversation

@Rossi-Luciano

@Rossi-Luciano Rossi-Luciano commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

O que esse PR faz?

Problema existente: o número de colunas do corpo está fixo em 2 em vários pontos do código (_setup_two_column_body_section, _add_two_column_section), e as contas de largura de figura/tabela (_compute_single_column_width, _compute_table_width) sempre dividem o espaço disponível por 2, sem ler nenhuma configuração. Não existe hoje forma de gerar um PDF com corpo em 1 coluna — e mesmo que existisse, essas contas dariam resultado errado (metade da largura real). Gap já registrado na issue #1278.

Solução proposta: generalizar as quatro funções pra usar page_attributes['default_column_count'] (novo campo, default 2 — preserva o comportamento atual). Com N colunas, a largura de uma coluna passa a ser (largura_disponível - (N-1)*espaçamento) / N, que se reduz exatamente à fórmula antiga quando N=2, e corresponde à largura total quando N=1 (sem precisar de caso especial). De caminho, corrigido decide_figure_layout pra chamar _compute_single_column_width em vez de duplicar a mesma conta (as duas podiam divergir se só uma fosse atualizada).

Resultado: com a configuração padrão (default_column_count: 2), a saída é idêntica à atual — validado nos 4 fixtures reais, mesma contagem de página. Com default_column_count: 1, corpo e tabelas passam a ocupar a largura inteira da página corretamente, em vez de ficarem espremidos em metade dela (imagem anexada).

Onde a revisão poderia começar?

packtools/sps/formats/pdf/renderer/docx/figure.py::_compute_single_column_width e packtools/sps/formats/pdf/pipeline/docx.py::_body_column_count.

Como este poderia ser testado manualmente?

Com a configuração padrão, nenhuma mudança visível:

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

Pra ver o efeito da nova opção (ainda não exposta via CLI — fica pra uma etapa seguinte de configuração de layout), definir default_column_count=1 diretamente via API Python antes de chamar pipeline_docx.

Testes automatizados: python -m unittest discover -s tests/sps/formats/pdf -v.

Algum cenário de contexto que queira dar?

Encontrado durante um levantamento mais amplo de qualidade do gerador de PDF, ao investigar se seria possível oferecer layout de 1 coluna como opção de configuração (issue #1278). O gap já estava documentado na própria issue, mas nunca tinha sido corrigido.

Screenshots

prA_before_after

Quais são os tickets relevantes?

Refs #1278. Closes #1297.

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)?

  • Sim — descreva os controles de proteção aplicados (criptografia, mascaramento, anonimização, etc.):
  • Não

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

  • Sim — descreva o que mudou e por quê:
  • Não

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

  • Sim — as novas dependências foram verificadas no SBOM/Trivy sem vulnerabilidades críticas/altas em aberto?
    • Verificado e aprovado
    • Pendente / vulnerabilidade aceita com justificativa:
  • Não

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

  • Sim — link do job:
  • 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?

  • Sim — confirme que há sanitização/parametrização (prepared statements, escaping, etc.):
  • Não

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

  • Sim — HTTPS obrigatório está garantido e o acesso segue o princípio de menor privilégio?
  • Não

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

  • Não, nenhum segredo foi commitado
  • Sim (bloquear merge e corrigir antes de prosseguir)

…ares)

Várias funções de layout dividiam a largura disponível por 2 direto no
código, assumindo que o corpo do artigo sempre tem duas colunas:
_compute_single_column_width e a conta duplicada dentro de
decide_figure_layout (figure.py), _compute_table_width (table.py), e
_setup_two_column_body_section/_add_two_column_section (pipeline/docx.py,
essa última usada pra restaurar o layout depois de uma tabela/figura de
largura total).

Generaliza todas pra usar page_attributes['default_column_count'] (novo
campo em PAGE_ATTRIBUTES, default 2 - preserva o comportamento atual).
Com N colunas, a largura de uma coluna passa a ser
(content_width - (N-1)*spacing) / N, que se reduz exatamente à fórmula
antiga quando N=2, e correspode à largura total quando N=1 (não precisa de
caso especial: com 1 coluna já não existe "largura de coluna vs largura
total" pra decidir).

De caminho, corrige decide_figure_layout pra chamar
_compute_single_column_width em vez de duplicar a mesma conta - as duas
podiam divergir se só uma fosse atualizada.

Validado ponta a ponta: com default_column_count=2 (padrão), a saída pros
4 fixtures reais (a1-a4) é idêntica à do master sem essa mudança (mesma
contagem de página). Com default_column_count=1 forçado (ainda sem
exposição via CLI/config - isso fica pra próxima etapa), gerando a3.xml,
o corpo e as tabelas passam a ocupar a largura inteira da página
corretamente, em vez de ficar espremidos em metade dela.

Refs scieloorg#1278.
Comment on lines +487 to +489
def _body_column_count():
"""Number of columns configured for the body, from PAGE_ATTRIBUTES."""
return max(1, pdf_enum.PAGE_ATTRIBUTES.get('default_column_count', 2))

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.

Boa.

section = docx_renderer.section.get_or_create_second_section(docx)
docx_renderer.section.setup_section_columns(section, 2, pdf_enum.TWO_COLUMNS_SPACING)
column_count = _body_column_count()
spacing = pdf_enum.TWO_COLUMNS_SPACING if column_count > 1 else 0

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.

E se o número de colunas for 3, usaria o espaço entre colunas adequado para 2?

Isso é apenas uma nota. Nada que bloqueie o PR, até porque não lembro de ter visto paper com 3 colunas. Caso haja um pdf_enum.N_COLUMNS_SPACING ou algo mais dinâmico, melhor.

@@ -521,13 +528,15 @@


def _add_two_column_section(docx):

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.

Agora esta função não adiciona necessariamente uma seção de duas colunas; ela restaura a quantidade configurada para o corpo. Podemos renomeá-la para algo como _restore_body_column_section?

Pelo mesmo motivo, _setup_two_column_body_section poderia se chamar _setup_body_section.

self.assertEqual(table_width, expected)
self.assertEqual(layout, pdf_enum.DOUBLE_COLUMN_PAGE_LABEL)

def test_one_column_equals_full_content_width(self):

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.

Estes testes comprovam os casos de uma coluna e a compatibilidade com as duas colunas anteriores, mas a implementação e a mensagem do commit afirmam suporte genérico a N colunas. Podemos acrescentar um caso com três colunas, validando explicitamente:

expected = (
    content_width - 2 * spacing_cm
) / 3

def _render_tables(docx, tables):
"""Render tables, switching to single column when required by layout."""
for table in tables:
if table.get('layout') == pdf_enum.SINGLE_COLUMN_PAGE_LABEL:

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.

Quando default_column_count == 1, um elemento de largura total já está no mesmo espaço disponível do corpo. Mesmo assim, este fluxo adiciona uma seção contínua antes do elemento e outra depois dele. O mesmo ocorre em _render_figures.

No fixture A3 com uma coluna, o resultado visual ficou correto, mas o documento continuou acumulando essas seções redundantes. Como as seções também afetam cabeçalhos, rodapés e numeração - algo que apareceu no PR #1296 -, poderíamos evitar a troca quando o corpo já tiver uma coluna?

Algo como considerar a troca necessária somente quando layout == SINGLE_COLUMN_PAGE_LABEL e _body_column_count() > 1, por exemplo.

@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.

A parametrização está funcionando corretamente para 1, 2 e 3 colunas, e os testes atuais estão passando. Antes da aprovação, peço dois ajustes:

  1. Quando default_column_count == 1, tabelas e figuras de largura total ainda criam duas seções contínuas desnecessárias. Como essas seções afetam também cabeçalhos, rodapés e numeração, sugiro evitar a troca quando o corpo já tiver uma coluna.
  2. Como a implementação declara suporte genérico a N colunas, incluir um teste com três colunas para a largura de figuras, tabelas e restauração da seção. Os casos atuais com 1 e 2 colunas não protegem completamente o termo (column_count - 1).
  3. Como melhoria de clareza, também sugiro renomear _setup_two_column_body_section e _add_two_column_section, pois agora essas funções configuram/restauram a quantidade de colunas definida no perfil, não necessariamente duas.

Nota de integração: ao resolver o conflito com o PR #1296 em PAGE_ATTRIBUTES, devem ser preservados tanto start_type=CONTINUOUS quanto default_column_count=2.

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: número de colunas do corpo está fixo em 2, com contas de largura hardcoded

2 participants