"""Notas de rodapé nativas, inclusive quando o documento já contém notas.""" from lxml import etree from docx.oxml import OxmlElement, parse_xml from docx.oxml.ns import qn from docx.opc.constants import CONTENT_TYPE as CT, RELATIONSHIP_TYPE as RT from docx.opc.packuri import PackURI from docx.opc.part import Part, XmlPart from docx.shared import Pt from docx.text.paragraph import Paragraph from copy import deepcopy import re from .public_text import public_text def add_first_footnote(doc, pattern, text): """Attach a note immediately after the first body/table occurrence of a term.""" for element in doc.element.body.iter(qn('w:p')): paragraph = Paragraph(element, doc._body) if element.find('.//'+qn('m:oMath')) is not None: display_text = ''.join(node.text or '' for node in element.iter() if node.tag in (qn('w:t'),qn('m:t'))) if re.search(pattern,display_text): # Word footnote references belong outside the math object. # Keep the expression editable and place its marker after it. return add_footnote(doc,paragraph,text) match = re.search(pattern, paragraph.text) if not match: continue offset = 0 for run in paragraph.runs: end = offset + len(run.text) if offset < match.end() <= end: cut = match.end() - offset tail = run.text[cut:] run.text = run.text[:cut] identifier = add_footnote(doc, paragraph, text) marker = paragraph.runs[-1]._r run._r.addnext(marker) if tail: remainder = OxmlElement('w:r') if run._r.rPr is not None: remainder.append(deepcopy(run._r.rPr)) content = OxmlElement('w:t') content.text = tail content.set(qn('xml:space'), 'preserve') remainder.append(content) marker.addnext(remainder) return identifier offset = end return add_footnote(doc, paragraph, text) return None def order_footnotes(doc): """Keep IDs and stored note order aligned with their first body occurrence. Notes can be attached retroactively to earlier terms. Align both orders so Word and LibreOffice associate every visible number with the right text. """ try: part = doc.part.part_related_by(RT.FOOTNOTES) except KeyError: return root = parse_xml(part.blob) notes = {n.get(qn('w:id')): n for n in root if int(n.get(qn('w:id'))) > 0} refs = list(doc.element.body.iter(qn('w:footnoteReference'))) ordered_ids = list(dict.fromkeys(r.get(qn('w:id')) for r in refs)) ordered_ids += [key for key in notes if key not in ordered_ids] mapping = {key: str(i+1) for i,key in enumerate(ordered_ids)} for reference in refs: reference.set(qn('w:id'),mapping[reference.get(qn('w:id'))]) for old_id in ordered_ids: note = notes[old_id] note.set(qn('w:id'),mapping[old_id]) root.append(note) if isinstance(part,XmlPart): part._element = root else: part._blob = etree.tostring(root,xml_declaration=True,encoding='UTF-8',standalone=True) def add_footnote(doc, paragraph, text): text = public_text(text) try: part = doc.part.part_related_by(RT.FOOTNOTES) root = parse_xml(part.blob) except KeyError: root = OxmlElement('w:footnotes') for identifier, kind in [(-1, 'separator'), (0, 'continuationSeparator')]: note = OxmlElement('w:footnote') note.set(qn('w:id'), str(identifier)); note.set(qn('w:type'), kind) p = OxmlElement('w:p'); r = OxmlElement('w:r') r.append(OxmlElement('w:'+kind)); p.append(r); note.append(p); root.append(note) part = Part(PackURI('/word/footnotes.xml'), CT.WML_FOOTNOTES, b'', doc.part.package) doc.part.relate_to(part, RT.FOOTNOTES) identifier = max([0] + [int(n.get(qn('w:id'))) for n in root]) + 1 note = OxmlElement('w:footnote'); note.set(qn('w:id'), str(identifier)) p = OxmlElement('w:p'); props = OxmlElement('w:pPr') spacing = OxmlElement('w:spacing'); spacing.set(qn('w:after'), '60') props.append(spacing); p.append(props) for marker, value in [('footnoteRef', None), ('t', ' '+text)]: r = OxmlElement('w:r'); rp = OxmlElement('w:rPr') font = OxmlElement('w:rFonts') font.set(qn('w:ascii'), 'Arial'); font.set(qn('w:hAnsi'), 'Arial'); rp.append(font) size = OxmlElement('w:sz'); size.set(qn('w:val'), '14'); rp.append(size) if value is None: vertical = OxmlElement('w:vertAlign'); vertical.set(qn('w:val'), 'superscript'); rp.append(vertical) r.append(rp); element = OxmlElement('w:'+marker) if value is not None: element.text = value; element.set(qn('xml:space'), 'preserve') r.append(element); p.append(r) note.append(p); root.append(note) if isinstance(part, XmlPart): part._element = root else: part._blob = etree.tostring(root, xml_declaration=True, encoding='UTF-8', standalone=True) run = paragraph.add_run(); run.font.superscript = True run.font.size = Pt(7) reference = OxmlElement('w:footnoteReference'); reference.set(qn('w:id'), str(identifier)) run._r.append(reference) return identifier