Spaces:
Sleeping
Sleeping
Download document/footnotes.py from gui-sparim/teste_docs: direct link, hf CLI and curl.
- Browser
- Download file 5.55 kB
-
https://huggingface.co/spaces/gui-sparim/teste_docs/resolve/main/document/footnotes.py
- Command line
-
hf download hf://spaces/gui-sparim/teste_docs/document/footnotes.py
-
curl -L -o footnotes.py https://huggingface.co/spaces/gui-sparim/teste_docs/resolve/main/document/footnotes.py
5.55 kB
| """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 | |