fix: remove espaço extra ao redor de parênteses e ; nas citações do corpo - #1327
Open
Rossi-Luciano wants to merge 2 commits into
Open
fix: remove espaço extra ao redor de parênteses e ; nas citações do corpo#1327Rossi-Luciano wants to merge 2 commits into
Rossi-Luciano wants to merge 2 commits into
Conversation
…orpo
extract_body_data extraia o texto de cada <p> via
`para.xpath('.//text()...')` seguido de `' '.join(texts).split()`, que
insere um espaco entre TODO par de fragmentos de texto, independente de
ter espaco na fonte. Um paragrafo com citacao entre parenteses, ex.:
"...pressure (<xref>Lang and Barling, 2012</xref>; <xref>Ripple et al.,
2019</xref>) crop...", virava "...pressure ( Lang and Barling, 2012 ;
Ripple et al., 2019 ) crop...": espaco extra logo apos "(", antes de
")" e antes de cada ";".
get_text_from_node (xml_utils.py) ja fazia extracao correta preservando
adjacencia real via .tail, inclusive com tratamento proprio pra xref,
mas so era usado como fallback de excecao nessa funcao. Passa a ser o
caminho principal, com um novo parametro skip_tags pra manter a exclusao
de <fig>/<table-wrap> do texto do paragrafo (preservando so o .tail que
vem depois deles).
_remove_double_spaces (chamada por get_text_from_node e por
get_text_from_mixed_citation_node) passa a colapsar qualquer sequencia
de espaco em branco (nao so espaco duplo literal) num unico espaco: o
.tail de uma <fig>/<table-wrap> pulada pode carregar a indentacao de
quebra de linha do XML fonte (`\n `), que antes vazava crua pro
paragrafo renderizado.
Validado contra o corpus real de 26 artigos: 1765 paragrafos
extraidos, so 15 ainda tem parenteses/espaco "suspeitos" e todos sao
explicaveis (espaco literal na propria fonte XML, ou formula MathML
achatada em texto, sem tratamento de formula ainda).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8r2LRJ3PGTT9vLaPtb373
Contributor
| texts_els.append(node.text) | ||
|
|
||
| for child in node: | ||
| if child.tag == 'xref': |
Contributor
There was a problem hiding this comment.
Sugiro simplificar get_text_from_node e aplicar uma normalização de espaçamento de pontuação:
- A remoção dos branches
elif child.tag == 'xref':eelif child.tag in ('italic', 'bold'):evita a perda de tags como<sup>(estilo Vancouver),<sub>,<sc>, além de evitar a perda desubchild.tailem marcações aninhadas. - A inclusão de
_normalize_punctuation_spacinggarante que artefatos de espaço presentes no XML de origem (como( <xref>ema2.xmlea16.xml) sejam corrigidos para o padrão tipográfico esperado no PDF ((Citation)em vez de( Citation)).
def get_text_from_node(node, skip_tags=None):
skip_tags = skip_tags or set()
texts_els = []
if node.text:
texts_els.append(node.text)
for child in node:
if child.tag in skip_tags:
pass
else:
texts_els.append(get_text_from_node(child, skip_tags=skip_tags))
if child.tail:
texts_els.append(child.tail)
text = ''.join(texts_els)
text = _remove_double_spaces(text)
text = _normalize_punctuation_spacing(text)
return text
def _normalize_punctuation_spacing(text):
text = re.sub(r'\(\s+', '(', text)
text = re.sub(r'\s+\)', ')', text)
text = re.sub(r'\[\s+', '[', text)
text = re.sub(r'\s+\]', ']', text)
text = re.sub(r'\s+;', ';', text)
text = re.sub(r'\s+,', ',', text)
return text| ) | ||
| result = xml_utils.get_text_from_node(xmltree) | ||
| self.assertEqual('Before Figure 1 after', result) | ||
|
|
Contributor
There was a problem hiding this comment.
Sugiro adicionar testes cobrindo:
<sup>dentro de<xref>- Marcações aninhadas com tail
- Normalização de espaços ao redor de parênteses, colchetes, ponto-e-vírgula e vírgula
def test_get_text_from_node_with_sup_inside_xref(self):
xmltree = etree.fromstring(
'<p>Author <xref ref-type="bibr"><sup>1,2</sup></xref> stated</p>'
)
self.assertEqual('Author 1,2 stated', xml_utils.get_text_from_node(xmltree))
def test_get_text_from_node_nested_formatting_with_tail(self):
xmltree = etree.fromstring(
'<p>Start <bold>bold <italic>and italic</italic> still bold</bold> end</p>'
)
self.assertEqual(
'Start bold and italic still bold end',
xml_utils.get_text_from_node(xmltree),
)
def test_get_text_from_node_normalizes_spaces_around_parentheses_and_punctuation(self):
xmltree = etree.fromstring(
'<p>Studies ( <xref ref-type="bibr">Author, 2020</xref> ; '
'<xref ref-type="bibr">Other, 2021</xref> ) and [ <xref ref-type="bibr">1</xref> ] '
'with comma ( <xref ref-type="bibr">Foo, 2019</xref> , more).</p>'
)
self.assertEqual(
'Studies (Author, 2020; Other, 2021) and [1] with comma (Foo, 2019, more).',
xml_utils.get_text_from_node(xmltree),
)
pitangainnovare
requested changes
Sep 5, 2026
pitangainnovare
left a comment
Contributor
There was a problem hiding this comment.
Testando o PR em artigos reais (ex.: a2.xml, a10.xml, a11.xml, a19.xml), identifiquei dois pontos:
- Problema em citações numéricas (
<sup>): A funçãoget_text_from_node(emxml_utils.py) tinha um tratamento específicoelif child.tag == 'xref':que só considerava subelementos<italic>e<bold>. Qualquer tag diferente - em especial<sup>(estilo Vancouver, como<xref ref-type="bibr"><sup>1</sup></xref>) - é descartada. Isso apagou 531 citações em 118 parágrafos de 3 artigos do corpus (ex.:tratamento fonoterápico().ema11.xml). - O problema de espaçamento ainda persiste no PDF em artigos como
a2.xml: Você mencionou na descrição que sobraram casos comoa16.xml: "( <xref>Figure</xref> )"porque o espaço já existia no XML fonte. Ema2.xml, há 47 ocorrências de( <xref...no próprio XML. No entanto, entendo que esses são defeitos do XML que podem ser tratados pelo sistema.
Recomendo
- Remover os branches especiais
elif child.tag == 'xref':eelif child.tag in ('italic', 'bold'):deget_text_from_node, deixando a recursão geral tratar todos os nós uniformemente - Adicionar uma função simples de normalização tipográfica
_normalize_punctuation_spacingpara remover espaços internos a parênteses/colchetes ((->(,)->)) e antes de pontuação (;->;,,->,).
Rossi-Luciano
added a commit
to Rossi-Luciano/packtools
that referenced
this pull request
Sep 8, 2026
…m_node Atende à revisão de @pitangainnovare no PR scieloorg#1327: os branches especiais para <xref> e <italic>/<bold> em get_text_from_node só reconheciam subelementos italic/bold, descartando qualquer outra tag (ex.: <sup>, estilo Vancouver) e perdendo o tail de marcação aninhada (ex.: texto depois de um <italic> dentro de um <bold>). Confirmado contra o corpus de 26 artigos: 659 ocorrências de <sup> dentro de <xref> em 17 artigos tinham a citação apagada (ex. a11.xml: "fonoterápico()." em vez de "fonoterápico(1)."). Substituídos os branches especiais por recursão genérica uniforme, que já trata texto+filhos+tail corretamente para qualquer tag. Adicionada _normalize_punctuation_spacing para remover espaços presos a parênteses/colchetes e antes de ";"/"," quando existem literalmente no XML fonte ao redor de um <xref> (segundo ponto da revisão). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QvPK3d53nxxxVnMhQJJdzC
…m_node Atende à revisão de @pitangainnovare no PR scieloorg#1327: os branches especiais para <xref> e <italic>/<bold> em get_text_from_node só reconheciam subelementos italic/bold, descartando qualquer outra tag (ex.: <sup>, estilo Vancouver) e perdendo o tail de marcação aninhada (ex.: texto depois de um <italic> dentro de um <bold>). Confirmado contra o corpus de 26 artigos: 659 ocorrências de <sup> dentro de <xref> em 17 artigos tinham a citação apagada (ex. a11.xml: "fonoterápico()." em vez de "fonoterápico(1)."). Substituídos os branches especiais por recursão genérica uniforme, que já trata texto+filhos+tail corretamente para qualquer tag. Adicionada _normalize_punctuation_spacing para remover espaços presos a parênteses/colchetes e antes de ";"/"," quando existem literalmente no XML fonte ao redor de um <xref> (segundo ponto da revisão).
Rossi-Luciano
force-pushed
the
fix/pdf-citation-spacing
branch
from
September 8, 2026 12:38
d58fb1b to
be8ec53
Compare
Contributor
Author
|
Obrigado pela revisão, @pitangainnovare. Os dois pontos procedem, corrigidos em be8ec53:
Também adicionei os 3 testes que você propôs. Suíte completa do pipeline PDF: 191 testes passando. |
pitangainnovare
approved these changes
Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


O que esse PR faz?
Corrige o espaçamento das citações no corpo do texto do
pdf_generator: citações entre parênteses ganhavam espaço extra logo após(, antes de)e antes de cada;que separa referências agrupadas — ex.:( Lang and Barling, 2012 ; Ripple et al., 2019 )em vez de(Lang and Barling, 2012; Ripple et al., 2019).Causa raiz:
extract_body_dataextraía o texto de cada<p>viapara.xpath('.//text()...')seguido de' '.join(texts).split(), que insere um espaço entre todo par de fragmentos de texto, independente de ter espaço na fonte XML. Um<xref>cercado de parênteses vira dois fragmentos de texto adjacentes sem espaço na fonte, mas ganhava um artificialmente.A função
get_text_from_node(emxml_utils.py) já fazia extração correta preservando adjacência real via.tail, com tratamento próprio para<xref>, mas só era usada como fallback de exceção. Passei a usá-la como caminho principal, adicionando um parâmetroskip_tagspara manter a exclusão de<fig>/<table-wrap>do texto do parágrafo (preservando só o.tailque vem depois deles). Também generalizei_remove_double_spacespara colapsar qualquer sequência de espaço em branco (não só espaço duplo literal), porque o.tailde uma<fig>/<table-wrap>pulada pode carregar a indentação de quebra de linha do XML fonte.Onde a revisão poderia começar?
packtools/sps/formats/pdf/utils/xml_utils.py—get_text_from_node(novo parâmetroskip_tags) e_remove_double_spaces(generalizada). Depoispacktools/sps/formats/pdf/pipeline/xml.py—extract_body_data, que passou a chamarget_text_from_node(para, skip_tags={'fig', 'table-wrap'})em vez doxpathantigo.Como este poderia ser testado manualmente?
Conferir qualquer citação entre parênteses no corpo do texto.
Algum cenário de contexto que queira dar?
Achado revisando visualmente o corpus de teste de 26 artigos reais (
layout_examples_rafael/corpus/). Validado contra o corpus inteiro (1765 parágrafos extraídos): sobraram 15 casos com parênteses/espaço "suspeitos", todos explicáveis — 6 são espaço que existe de verdade na fonte XML (a16.xml:"( <xref>Figure</xref> )", espaço literal no XML original) e 9 são fórmulas matemáticas achatadas em texto sem tratamento de fórmula (fora de escopo, item futuro do backlog de PDF).Screenshots
Quais são os tickets relevantes?
Nenhuma issue aberta associada; achado durante revisão visual do corpus de teste do gerador de PDF.
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)?
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?
🤖 Generated with Claude Code
https://claude.ai/code/session_01X8r2LRJ3PGTT9vLaPtb373