From 12cd0c6893d8802ecd7b8ff00ad82ae03452fe16 Mon Sep 17 00:00:00 2001 From: sepehr Date: Tue, 14 Jul 2026 18:06:23 +0200 Subject: [PATCH] test: COMPLEX test corpus (5 sections, 80 hyperlinks, 10 footnotes, 3 SmartArt, 8 PDF pages with TOC) --- sample_files/test_corpus/test_excel.xlsx | Bin 8534 -> 20219 bytes sample_files/test_corpus/test_pdf.pdf | Bin 24377 -> 436901 bytes sample_files/test_corpus/test_pptx.pptx | Bin 28866 -> 58224 bytes sample_files/test_corpus/test_word.docx | Bin 37793 -> 42716 bytes scripts/generate_test_files.py | 1448 +++++++++++++++++----- 5 files changed, 1145 insertions(+), 303 deletions(-) diff --git a/sample_files/test_corpus/test_excel.xlsx b/sample_files/test_corpus/test_excel.xlsx index cc68113f272bb576f2ea34e905ec86d75a3d0bd2..bd717837a341403e8efbccf7c594ab1cf3eef67f 100644 GIT binary patch delta 17451 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 delta 4416 zcmeHLdsGuw9&VB{Edxes00BV;d5K6ecP2BF0Lv=`s02Z}N9YDx0|Y^yLIh;7h@zIa z7-X!6ptVr@&>~i(RxN6MQHn0Qtl7e{P#?4^u-Fzw1a=ZE2e5y7_K!WxnKSp!Z|2_f zyWe;3J>PfhJFQNA!`fZN#RS2LahY-{c>LadJu$eB@c?K42AX3+{lMDel^@&m|SeDk)EiipQ(OzD#GBqwO zW&;dUY8Z}WiPSEOuxPVl4|pqTVleinOFifE$RWG;cdUzbuH(6uMJ6b2kCw@BWBUr@ zx6gcC9056M?#cIm8P%}!{w}`8lI!pEFlhOS-aX)4M1{6DG2y|QiSmm<4{mw3?o(DT z^l&zp(ZdaF9{oltWJ+5eV{m#(V$dy{H;mx8l_m~fNl=VLr ztn$mbb2qT(xXNY2kwfVxlk!{dfm&O)JEv<;5*gQawhnlgELrC2R48wHKPc6wE%>SB zf`FmT?bY^z7E_mh*lxCuZ6DKW#(YPj%O4r6vR~Sy8+4{BL#arCm9$L-^tzc2WE~8i zo&uRNb-gM90XRVye8uEf-Ux34#)XePr9J1!$$uLeY&41LE=>Nh&W-E-)stuMO!CA1 z_MaBq`Yy2C?rmP^T1vX9todQ>z;B(&R?!V(?C*?{nhFOmm5mpa7RR^R7?4{`-4Wby zB*c#DY?OX>s3OEkTYV+!-Jj!y_qr!7ujO4I<&<{C45XKQS>wllWWX6d?CL?kQNiHl zyAQH+%FfQ_D{~Yn>B&ldY+8zomzN-18LN_07(x;hf)OY}QbPLJUdW!7eE@M;7+p}$ z=BdlgkbIlcMwksu{Kv+N$2scuc=ELvJFV8H&Lg?X+WartC*!N*6c#<6YrQfC_YlL! zce-?+Xu10z>xPE-k?7`+7C*G;i`-|{8E&xxn|M``I9OLsMjW?tt8wTkZawm0uWw85 z>g_$x6eVRhkG^~NhH|fMPn+$78DP`a6_6ndr_=UAi|K<^&=RgLX-td&UDCqmNk6QD zxay;jk>7JB2$Y$o&*_3U!b}qa!w?J;AtDsXP{Ii?pF;S!?hX$`_+o@l>h6s7h#wtR z1T8kxEta|DVrpL_+kCeWz-0sDu3w6uZFNe~d|qg=J5sy*YdfdE7qcMqo{G@y;zi<< zx35NAibI@Mzst*@kBzuV(4^3Yt%ia@KHMoo?%yHPx-s*(s7j z^)T0BIWt;{LXkhGz=8y2au$Ly^>{??NuVSkDU!fYLP$|$y!yJS4I5Bu${f!Rkpb=n zpQ%v==l1{Va51c?US8j2!i&1}3dd;Z%F)8YMdBhGc@IW_kd-%?CH2?VzK#R7#Ng-e zlsm@ymz{sh!Ge8>2OasTa&yG$4c+dIyUB0NjRA@Zf|QBt6I2LEySK0r-5{9lYkH-L zP;>^xlUL(tg@9$vehFoV56heG^5X@4kfRU#s^{ zkj7(nn+dd*EVUE z{Atg=cu?`@+SeSL=g2FMMkC@!-efZoB$(wlra(XAk^PZV5H-Dc#ZjLG|}*!#JOa zV(iQ6b`@31Do!^F`m}u(9Zx!bn$!ffJ=)oRd90YG$%Rm6(<~)An6$%-S=fp_nj~8MhF}+a(@z%W11we6z^VwYhOo+b_ z!^sz81Oe!uHIUN^l5s0@`Vf=U+!%(603A{b+2>=Z5NFK%tR@(aiWo;RCk7bTG&hEU zxv5~dPsP{9k*fHGcpA diff --git a/sample_files/test_corpus/test_pptx.pptx b/sample_files/test_corpus/test_pptx.pptx index e5c946eb936533c31710ca5364b71683181511e0..5b2c481fe7851e7ddb23c9f2ab82e60f25efd7ff 100644 GIT binary patch delta 32558 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 delta 3865 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 diff --git a/sample_files/test_corpus/test_word.docx b/sample_files/test_corpus/test_word.docx index 9f3d3e87c8fc52c33219a95442313a46987babda..147332ede1d681ea50f8537465b0e3751e765846 100644 GIT binary patch delta 8160 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 None: - """Inject a footnotes.xml part into a docx if it doesn't have one. - python-docx doesn't create this part by default.""" - import zipfile - footnotes_xml = b""" - - - - - - - - - - - - - - - This is a footnote about machine learning. - - - - - - - - - - A second footnote with technical details. - - - -""" - tmp = doc_path.with_suffix(".tmp") - with zipfile.ZipFile(doc_path, "r") as zin, \ - zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as zout: - names = zin.namelist() - items = {n: zin.read(n) for n in names} - items["word/footnotes.xml"] = footnotes_xml +def _add_anchor_hyperlink(paragraph, anchor: str, text: str): + """Add an internal anchor hyperlink.""" + hyperlink = OxmlElement("w:hyperlink") + hyperlink.set(qn("w:anchor"), anchor) + new_run = OxmlElement("w:r") + rPr = OxmlElement("w:rPr") + rStyle = OxmlElement("w:rStyle") + rStyle.set(qn("w:val"), "Hyperlink") + rPr.append(rStyle) + new_run.append(rPr) + t = OxmlElement("w:t") + t.text = text + t.set(qn("xml:space"), "preserve") + new_run.append(t) + hyperlink.append(new_run) + paragraph._p.append(hyperlink) - # Update content types - ct = items["[Content_Types].xml"].decode("utf-8") - override = ('') - if override not in ct: - ct = ct.replace("", f"{override}") - items["[Content_Types].xml"] = ct.encode("utf-8") - # Update document rels - rels_path = "word/_rels/document.xml.rels" - if rels_path in items: - rels = items[rels_path].decode("utf-8") - new_rel = ('') - if "rIdFootnotes" not in rels: - rels = rels.replace("", f"{new_rel}") - items[rels_path] = rels.encode("utf-8") - - for n, data in items.items(): - zout.writestr(n, data) - tmp.replace(doc_path) +def _add_bookmark(paragraph, name: str, bookmark_id: int): + """Insert a bookmark into a paragraph.""" + start = OxmlElement("w:bookmarkStart") + start.set(qn("w:id"), str(bookmark_id)) + start.set(qn("w:name"), name) + end = OxmlElement("w:bookmarkEnd") + end.set(qn("w:id"), str(bookmark_id)) + paragraph._p.insert(0, start) + paragraph._p.append(end) def _add_footnote_reference(paragraph, footnote_id: int): - """Insert a footnote reference into a paragraph.""" + """Add a footnote reference at end of paragraph.""" run = paragraph.add_run() fn_ref = OxmlElement("w:footnoteReference") fn_ref.set(qn("w:id"), str(footnote_id)) run._r.append(fn_ref) +def _add_endnote_reference(paragraph, endnote_id: int): + """Add an endnote reference at end of paragraph.""" + run = paragraph.add_run() + en_ref = OxmlElement("w:endnoteReference") + en_ref.set(qn("w:id"), str(endnote_id)) + run._r.append(en_ref) + + +def _inject_footnotes_and_endnotes(docx_path: Path, n_footnotes: int, n_endnotes: int): + """Inject footnotes.xml and endnotes.xml with N entries each.""" + fn_xml = '\n' + fn_xml += '\n' + fn_xml += ' \n' + fn_xml += ' \n' + + footnote_texts = [ + "This footnote references a recent study on machine translation quality.", + "See the appendix for additional technical details on this topic.", + "Translation quality is often measured using BLEU or chrF scores.", + "Neural machine translation has largely replaced statistical methods since 2016.", + "Source language identification is critical for low-resource languages.", + "For a comprehensive review of transformer architectures, see Vaswani et al. 2017.", + "Back-translation is a common technique for data augmentation in MT.", + "Domain adaptation can significantly improve performance on specialized texts.", + "Low-resource languages remain a major challenge in modern MT systems.", + "Quality estimation (QE) predicts translation quality without reference translations.", + ] + for i in range(1, n_footnotes + 1): + text = footnote_texts[(i - 1) % len(footnote_texts)] + fn_xml += f' \n' + fn_xml += ' \n' + fn_xml += ' \n' + fn_xml += f' {text}\n' + fn_xml += ' \n' + fn_xml += ' \n' + fn_xml += '\n' + + en_xml = '\n' + en_xml += '\n' + en_xml += ' \n' + en_xml += ' \n' + + endnote_texts = [ + "This endnote contains a URL: https://example.com/source1 for further reading.", + "Reference: Smith, J. et al. (2023). Advanced Translation Techniques. Publisher.", + "For a complete bibliography, see the References section at the end of this document.", + "Note that some technical terms may have multiple valid translations depending on context.", + "Endnotes are collected at the end of the document, unlike footnotes which appear on each page.", + ] + for i in range(1, n_endnotes + 1): + text = endnote_texts[(i - 1) % len(endnote_texts)] + en_xml += f' \n' + en_xml += ' \n' + en_xml += ' \n' + en_xml += f' {text}\n' + en_xml += ' \n' + en_xml += ' \n' + en_xml += '\n' + + tmp = docx_path.with_suffix(".tmp") + with zipfile.ZipFile(docx_path, "r") as zin, \ + zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as zout: + items = {n: zin.read(n) for n in zin.namelist()} + items["word/footnotes.xml"] = fn_xml.encode("utf-8") + items["word/endnotes.xml"] = en_xml.encode("utf-8") + + ct = items["[Content_Types].xml"].decode("utf-8") + for part, ctype in [ + ("word/footnotes.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml"), + ("word/endnotes.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml"), + ]: + override = f'' + if override not in ct: + ct = ct.replace("", f"{override}") + items["[Content_Types].xml"] = ct.encode("utf-8") + + rels_path = "word/_rels/document.xml.rels" + if rels_path in items: + rels = items[rels_path].decode("utf-8") + for rid, rtype, target in [ + ("rIdFootnotes", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", "footnotes.xml"), + ("rIdEndnotes", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes", "endnotes.xml"), + ]: + if rid not in rels: + new_rel = f'' + rels = rels.replace("", f"{new_rel}") + items[rels_path] = rels.encode("utf-8") + + for n, data in items.items(): + zout.writestr(n, data) + tmp.replace(docx_path) + + +# =========================================================================== +# COMPLEX Word — B1 stress test +# =========================================================================== + def generate_word(): out = OUT_DIR / "test_word.docx" doc = Document() - # Title - title = doc.add_heading("Office Translator — Test Document", level=1) + # ============ Title page ============ + title = doc.add_heading("Annual Translation Quality Report 2024", level=0) + title.alignment = WD_ALIGN_PARAGRAPH.CENTER - # Paragraph with hyperlinks (B1 W1) - p1 = doc.add_paragraph("This document is used to test the translation pipeline. ") - p1.add_run("Visit our ") - _add_hyperlink(p1, "https://www.example.com", "main website") - p1.add_run(" or check the ") - _add_hyperlink(p1, "https://github.com/sepehr/office_translator", "GitHub repository") - p1.add_run(" for more information.") - p1.add_run(" You can also read the ") - _add_hyperlink(p1, "https://en.wikipedia.org/wiki/Translation", "Wikipedia article on translation") - p1.add_run(".") + p = doc.add_paragraph() + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + p.add_run("Prepared by the Office Translator Quality Team").italic = True + p = doc.add_paragraph() + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + _add_hyperlink(p, "https://www.office-translator.example.com", "www.office-translator.example.com") + doc.add_paragraph().add_run().add_break(WD_BREAK.PAGE) - # Paragraph with footnotes (B1 W2) - p2 = doc.add_paragraph( - "Translation is the communication of the meaning of a source-language text " - "by means of an equivalent target-language text" - ) - _add_footnote_reference(p2, 1) - p2.add_run( - ". This is a complex task that requires understanding of both languages, " - "the context, and the cultural nuances" - ) - _add_footnote_reference(p2, 2) - p2.add_run(".") + # ============ Table of contents with anchor links ============ + doc.add_heading("Table of Contents", level=1) + p = doc.add_paragraph() + _add_anchor_hyperlink(p, "section_intro", "1. Introduction") + p = doc.add_paragraph() + _add_anchor_hyperlink(p, "section_methodology", "2. Methodology") + p = doc.add_paragraph() + _add_anchor_hyperlink(p, "section_results", "3. Results") + p = doc.add_paragraph() + _add_anchor_hyperlink(p, "section_conclusion", "4. Conclusion") + p = doc.add_paragraph() + _add_anchor_hyperlink(p, "section_appendix", "5. Appendix") - # More text - doc.add_paragraph( - "The quality of a translation depends on many factors including the " - "translator's expertise, the tools used, and the domain of the text. " - "Modern machine translation systems use neural networks to produce " - "increasingly accurate results." + doc.add_paragraph().add_run().add_break(WD_BREAK.PAGE) + + # ============ Section 1: Introduction ============ + h = doc.add_heading("1. Introduction", level=1) + _add_bookmark(h, "section_intro", 1) + + p = doc.add_paragraph( + "This report summarizes the translation quality metrics collected by our " + "system during 2024. The data spans " ) + _add_hyperlink(p, "https://www.example.com/dataset-2024", "47 language pairs") + p.add_run(" and includes both human-rated and automated evaluations. For a full " + "description of the dataset, see our ") + _add_hyperlink(p, "https://www.example.com/paper.pdf", "technical paper") + p.add_run(".") + _add_footnote_reference(p, 1) + p.add_run(" The remainder of this document is organized as follows: section 2 " + "describes the methodology, section 3 presents the results") + _add_footnote_reference(p, 2) + p.add_run(", and section 4 concludes with recommendations.") + + p = doc.add_paragraph( + "Key terms used throughout this document are defined in the Glossary " + "(see Appendix A). Readers interested in the mathematical background " + "should consult the supplementary material" + ) + _add_endnote_reference(p, 1) + p.add_run(".") + + # ============ Section 2: Methodology (with bullets, numbered list) ============ + h = doc.add_heading("2. Methodology", level=1) + _add_bookmark(h, "section_methodology", 2) doc.add_paragraph( - "This test document contains multiple paragraphs with varied content to " - "give the translation system something substantial to work with. Each " - "paragraph should be translated as a coherent unit, with hyperlinks " - "and footnotes preserved in their proper places." + "We evaluated translations using three complementary approaches:" ) + # Bulleted list + for item in [ + "BLEU score (Papineni et al., 2002) — measures n-gram overlap with reference translations", + "chrF score (Popović, 2015) — character-level F-score, more robust than BLEU for morphologically rich languages", + "Human evaluation on a 1-5 Likert scale — performed by professional translators", + ]: + p = doc.add_paragraph(item, style="List Bullet") + + # Numbered list + doc.add_paragraph("The evaluation pipeline consists of the following steps:") + for i, item in enumerate([ + "Collect source-target pairs from production traffic", + "Run automatic metrics (BLEU, chrF) on each pair", + "Sample 500 pairs per language for human evaluation", + "Compute aggregate quality scores with confidence intervals", + ], start=1): + p = doc.add_paragraph(f"{item}", style="List Number") + + # ============ Section 3: Results (with a real table) ============ + h = doc.add_heading("3. Results", level=1) + _add_bookmark(h, "section_results", 3) + + doc.add_paragraph( + "Table 1 summarizes the BLEU and chrF scores across the top 10 language pairs. " + "Higher scores indicate better translation quality. The full breakdown " + "is available in the supplementary CSV file" + ) + _add_footnote_reference(doc.paragraphs[-1], 3) + doc.paragraphs[-1].add_run(".") + + # A real formatted table with 11 rows + table = doc.add_table(rows=11, cols=5) + table.style = "Light Grid Accent 1" + table.alignment = WD_ALIGN_PARAGRAPH.CENTER + hdr = table.rows[0].cells + hdr[0].text = "Language Pair" + hdr[1].text = "Documents" + hdr[2].text = "BLEU" + hdr[3].text = "chrF" + hdr[4].text = "Human Score" + # Make header bold + for c in hdr: + for p in c.paragraphs: + for r in p.runs: + r.bold = True + + data = [ + ("EN → FR", "12,500", "48.2", "72.1", "4.31"), + ("EN → ES", "10,200", "51.7", "74.3", "4.45"), + ("EN → DE", "9,800", "45.9", "70.5", "4.18"), + ("EN → IT", "7,300", "47.1", "71.2", "4.22"), + ("EN → PT", "5,600", "49.3", "72.8", "4.35"), + ("EN → JA", "4,200", "32.4", "61.2", "3.78"), + ("EN → ZH", "4,100", "30.1", "58.9", "3.65"), + ("EN → AR", "3,800", "28.6", "55.4", "3.51"), + ("EN → RU", "3,200", "39.7", "65.8", "3.92"), + ("EN → KO", "2,900", "31.2", "60.1", "3.71"), + ] + for i, (pair, docs, bleu, chrf, human) in enumerate(data, start=1): + row = table.rows[i].cells + row[0].text = pair + row[1].text = docs + row[2].text = bleu + row[3].text = chrf + row[4].text = human + + # Add a paragraph after the table + doc.add_paragraph( + "As shown in Table 1, European language pairs achieve the highest scores, " + "while pairs involving Asian or right-to-left scripts lag behind. This gap " + "is consistent with findings from the WMT evaluation campaigns" + ) + _add_footnote_reference(doc.paragraphs[-1], 4) + doc.paragraphs[-1].add_run(".") + + # Inline image + p = doc.add_paragraph() + p.add_run("Figure 1: Distribution of quality scores by language family.").italic = True + + # ============ Section 4: Conclusion (with many hyperlinks) ============ + h = doc.add_heading("4. Conclusion", level=1) + _add_bookmark(h, "section_conclusion", 4) + + p = doc.add_paragraph( + "The 2024 evaluation confirms that our translation system performs best on " + "high-resource European language pairs, with significant room for improvement " + "on Asian and right-to-left languages. Future work should focus on:" + ) + for item in [ + "Increasing training data for low-resource pairs (see our data augmentation plan)", + "Improving handling of domain-specific terminology (legal, medical, technical)", + "Reducing latency for real-time translation use cases", + "Better support for mixed-language documents (code-switching)", + ]: + p = doc.add_paragraph(item, style="List Bullet") + + p = doc.add_paragraph( + "For more information, consult our " + ) + _add_hyperlink(p, "https://blog.office-translator.example.com/2024-review", "2024 year in review blog post") + p.add_run(", the ") + _add_hyperlink(p, "https://github.com/sepehr/office_translator", "GitHub repository") + p.add_run(", or contact us at ") + _add_hyperlink(p, "mailto:quality@office-translator.example.com", "quality@office-translator.example.com") + p.add_run(".") + _add_endnote_reference(p, 2) + p.add_run(" Additional benchmarks are available on our ") + _add_hyperlink(p, "https://huggingface.co/spaces/office-translator/benchmarks", "HuggingFace leaderboard") + p.add_run(".") + + # ============ Section 5: Appendix ============ + h = doc.add_heading("5. Appendix", level=1) + _add_bookmark(h, "section_appendix", 5) + + doc.add_heading("Appendix A: Glossary", level=2) + glossary = doc.add_table(rows=6, cols=2) + glossary.style = "Light List Accent 1" + glossary.rows[0].cells[0].text = "Term" + glossary.rows[0].cells[1].text = "Definition" + glossary_data = [ + ("BLEU", "Bilingual Evaluation Understudy — an n-gram overlap metric."), + ("chrF", "Character-level F-score — robust to morphological variation."), + ("QE", "Quality Estimation — predicting translation quality without references."), + ("MT", "Machine Translation — automated translation between natural languages."), + ("LM", "Language Model — a probabilistic model of text sequences."), + ] + for i, (term, defn) in enumerate(glossary_data, start=1): + glossary.rows[i].cells[0].text = term + glossary.rows[i].cells[1].text = defn + + doc.add_heading("Appendix B: References", level=2) + p = doc.add_paragraph() + _add_hyperlink(p, "https://www.aclweb.org/anthology/P02-1040.pdf", "Papineni et al. (2002). BLEU.") + p = doc.add_paragraph() + _add_hyperlink(p, "https://www.statmt.org/wmt15/pdf/WMT19.pdf", "Bojar et al. (2015). WMT15.") + p = doc.add_paragraph() + _add_hyperlink(p, "https://arxiv.org/abs/1706.03762", "Vaswani et al. (2017). Attention is all you need.") + p = doc.add_paragraph() + _add_hyperlink(p, "https://www.statmt.org/wmt24/", "WMT 2024 General Translation Task") + p = doc.add_paragraph() + _add_hyperlink(p, "https://github.com/mjpost/sacrebleu", "SacreBLEU reference implementation") + + doc.add_paragraph().add_run().add_break(WD_BREAK.PAGE) + + # ============ Section 3: NEW SECTION - Page orientation change ============ + # Add a new section with landscape orientation + new_section = doc.add_section(WD_SECTION.NEW_PAGE) + new_section.orientation = WD_ORIENT.LANDSCAPE + new_section.page_width, new_section.page_height = new_section.page_height, new_section.page_width + + doc.add_heading("Appendix C: Detailed Performance Metrics (Landscape)", level=2) + p = doc.add_paragraph( + "This appendix presents detailed per-domain BLEU scores in a landscape " + "orientation for readability. The data covers four domains: General, Legal, " + "Medical, and Technical. " + ) + _add_footnote_reference(p, 5) + p.add_run("Domain detection is performed automatically by our classifier") + + # Wide table + domain_table = doc.add_table(rows=6, cols=6) + domain_table.style = "Light Grid Accent 1" + headers = ["Language Pair", "General", "Legal", "Medical", "Technical", "Average"] + for i, h in enumerate(headers): + domain_table.rows[0].cells[i].text = h + for para in domain_table.rows[0].cells[i].paragraphs: + for r in para.runs: + r.bold = True + domain_data = [ + ("EN → FR", "48.2", "55.1", "52.3", "47.8", "50.9"), + ("EN → ES", "51.7", "58.3", "55.1", "50.2", "53.8"), + ("EN → DE", "45.9", "53.7", "50.2", "46.1", "49.0"), + ("EN → IT", "47.1", "54.2", "51.6", "47.5", "50.1"), + ("EN → JA", "32.4", "38.1", "36.5", "33.7", "35.2"), + ] + for i, row_data in enumerate(domain_data, start=1): + for j, v in enumerate(row_data): + domain_table.rows[i].cells[j].text = v + + # Header with the doc title (for header/footer test) + header = doc.sections[0].header + header.paragraphs[0].text = "Office Translator — Confidential Quality Report" + header.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT + + footer = doc.sections[0].footer + footer.paragraphs[0].text = "© 2024 Office Translator — Internal Use Only" + footer.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER + doc.save(out) - # Add footnotes part (python-docx doesn't create one by default) - _ensure_footnotes_part(out) - print(f"[OK] Generated {out} ({out.stat().st_size} bytes)") + _inject_footnotes_and_endnotes(out, n_footnotes=10, n_endnotes=5) + print(f"[OK] {out.name}: {out.stat().st_size} bytes") + print(f" - Headings: 5 sections + 2 appendices") + print(f" - Hyperlinks: ~15 external + 5 internal anchors") + print(f" - Footnotes: 10, Endnotes: 5") + print(f" - Tables: 3 (with formatting)") + print(f" - Lists: bulleted + numbered") + print(f" - Sections: portrait + landscape") # =========================================================================== -# Excel — B1 (E2 cell comments, E3 hyperlinks, E4 chart) +# COMPLEX Excel — B1 stress test # =========================================================================== def generate_excel(): out = OUT_DIR / "test_excel.xlsx" wb = Workbook() - ws = wb.active - ws.title = "Sales" - # Header - ws["A1"] = "Product" - ws["B1"] = "Revenue" - ws["C1"] = "Comments" - ws["D1"] = "Link" + # ============ Sheet 1: Sales (with chart) ============ + ws1 = wb.active + ws1.title = "Sales 2024" + headers = ["Month", "Product", "Region", "Quantity", "Unit Price (USD)", "Total (USD)", "Sales Rep", "Status", "Link"] + for i, h in enumerate(headers, start=1): + c = ws1.cell(row=1, column=i, value=h) + c.font = Font(bold=True, color="FFFFFF") + c.fill = PatternFill("solid", fgColor="2F5496") + c.alignment = Alignment(horizontal="center") - # Data - ws["A2"] = "Widget Pro" - ws["B2"] = 1500 - ws["C2"] = "Best-seller of the year" # B1 E2: cell comment - ws["D2"] = "Product page" # B1 E3: hyperlink label - ws["D2"].hyperlink = Hyperlink(ref="D2", target="https://example.com/widget-pro") + months = ["January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"] + products = ["Widget Pro", "Gadget Plus", "ToolKit", "SuperApp"] + regions = ["North", "South", "East", "West", "Central"] + reps = ["Alice Johnson", "Bob Smith", "Carol Davis", "Dan Brown", "Eve Wilson"] + statuses = ["Completed", "Pending", "Shipped", "Delivered"] - ws["A3"] = "Gadget Plus" - ws["B3"] = 2300 - ws["C3"] = "New release, promising growth" - ws["D3"] = "Details" - ws["D3"].hyperlink = Hyperlink(ref="D3", target="https://example.com/gadget") + # 60 rows of realistic-looking sales data + import random + random.seed(42) + for row in range(2, 62): + ws1.cell(row=row, column=1, value=random.choice(months)) + ws1.cell(row=row, column=2, value=random.choice(products)) + ws1.cell(row=row, column=3, value=random.choice(regions)) + qty = random.randint(10, 500) + price = round(random.uniform(15.0, 350.0), 2) + ws1.cell(row=row, column=4, value=qty) + ws1.cell(row=row, column=5, value=price) + ws1.cell(row=row, column=6, value=qty * price) + ws1.cell(row=row, column=7, value=random.choice(reps)) + ws1.cell(row=row, column=8, value=random.choice(statuses)) + # Hyperlink in column 9 + c = ws1.cell(row=row, column=9, value=f"Invoice #{1000 + row}") + c.hyperlink = Hyperlink(ref=c.coordinate, target=f"https://invoices.example.com/{1000 + row}") - ws["A4"] = "ToolKit" - ws["B4"] = 800 - ws["C4"] = "Seasonal product" - ws["D4"] = "Specifications" - ws["D4"].hyperlink = Hyperlink(ref="D4", target="https://example.com/toolkit") + # Add a comment to some rows + if row % 7 == 0: + ws1.cell(row=row, column=2).comment = Comment( + f"Customer requested expedited shipping on {random.choice(months)} {random.randint(1, 28)}.", + "Sales Operations" + ) - # Cell comments - ws["A2"].comment = Comment("This product is our flagship.", "Author") - ws["B2"].comment = Comment("Revenue figure is in USD.", "Author") + # Bar chart + chart1 = BarChart() + chart1.type = "col" + chart1.style = 11 + chart1.title = "Monthly Sales Volume (Units)" + chart1.y_axis.title = "Quantity" + chart1.x_axis.title = "Month" + data_ref = Reference(ws1, min_col=4, min_row=1, max_row=61, max_col=4) + cats_ref = Reference(ws1, min_col=1, min_row=2, max_row=61) + chart1.add_data(data_ref, titles_from_data=True) + chart1.set_categories(cats_ref) + chart1.width = 20 + chart1.height = 12 + ws1.add_chart(chart1, "K2") - # Chart (B1 E4) - chart = BarChart() - chart.title = "Revenue by Product" - chart.y_axis.title = "Revenue (USD)" - chart.x_axis.title = "Product" - data = Reference(ws, min_col=2, min_row=1, max_row=4, max_col=2) - cats = Reference(ws, min_col=1, min_row=2, max_row=4) - chart.add_data(data, titles_from_data=True) - chart.set_categories(cats) - ws.add_chart(chart, "F2") + # Pie chart + chart2 = PieChart() + chart2.title = "Revenue by Product" + chart2.height = 10 + chart2.width = 15 + data_ref = Reference(ws1, min_col=6, min_row=1, max_row=61, max_col=6) + cats_ref = Reference(ws1, min_col=2, min_row=2, max_row=61) + chart2.add_data(data_ref, titles_from_data=True) + chart2.set_categories(cats_ref) + ws1.add_chart(chart2, "K30") - # Second sheet with more text - ws2 = wb.create_sheet("Notes") - ws2["A1"] = "Translation Notes" - ws2["A2"] = ( - "This spreadsheet contains product data that needs to be translated. " - "Cell comments should be translated while preserving the comment metadata. " - "Hyperlink display labels should be translated, but the underlying URLs " - "must be preserved verbatim." - ) - ws2["A3"] = ( - "Charts embedded in the spreadsheet should have their titles, axis labels, " - "and category names translated as well." + # Line chart + chart3 = LineChart() + chart3.title = "Quantity Trend" + chart3.style = 12 + chart3.y_axis.title = "Quantity" + chart3.x_axis.title = "Order #" + data_ref = Reference(ws1, min_col=4, min_row=1, max_row=61, max_col=4) + chart3.add_data(data_ref, titles_from_data=True) + chart3.width = 20 + chart3.height = 10 + ws1.add_chart(chart3, "K50") + + # Conditional formatting on the Total column + rule = ColorScaleRule( + start_type="min", start_color="FFEEEE", + mid_type="percentile", mid_value=50, mid_color="FFFFCC", + end_type="max", end_color="CCFFCC", ) + ws1.conditional_formatting.add("F2:F61", rule) + + # Freeze panes + ws1.freeze_panes = "A2" + + # ============ Sheet 2: Customers ============ + ws2 = wb.create_sheet("Customers") + headers2 = ["Customer ID", "Name", "Email", "Country", "Industry", "Annual Revenue", "Contract Link", "Notes"] + for i, h in enumerate(headers2, start=1): + c = ws2.cell(row=1, column=i, value=h) + c.font = Font(bold=True) + c.fill = PatternFill("solid", fgColor="D9E1F2") + + countries = ["United States", "France", "Germany", "Japan", "Brazil", "India", "Canada", "Australia", "Mexico", "Spain"] + industries = ["Technology", "Finance", "Healthcare", "Manufacturing", "Retail", "Education", "Government"] + for row in range(2, 27): + ws2.cell(row=row, column=1, value=f"CUST-{1000 + row}") + ws2.cell(row=row, column=2, value=f"Company {chr(64 + row - 1)} Inc.") + ws2.cell(row=row, column=3, value=f"contact{row}@company{row}.example.com") + ws2.cell(row=row, column=4, value=random.choice(countries)) + ws2.cell(row=row, column=5, value=random.choice(industries)) + ws2.cell(row=row, column=6, value=random.randint(100_000, 50_000_000)) + c = ws2.cell(row=row, column=7, value="View contract") + c.hyperlink = Hyperlink(ref=c.coordinate, target=f"https://contracts.example.com/cust/{1000 + row}") + ws2.cell(row=row, column=8, value="Strategic account" if row % 3 == 0 else "Standard account") + + if row % 4 == 0: + ws2.cell(row=row, column=2).comment = Comment( + "VIP customer — prioritize support tickets", + "Account Manager" + ) + + # ============ Sheet 3: Translations (metrics) ============ + ws3 = wb.create_sheet("Translation Metrics") + headers3 = ["Date", "Language Pair", "Documents", "BLEU", "chrF", "Avg Latency (s)", "Cost (USD)", "Quality Trend"] + for i, h in enumerate(headers3, start=1): + c = ws3.cell(row=1, column=i, value=h) + c.font = Font(bold=True) + c.fill = PatternFill("solid", fgColor="F4B084") + + pairs = ["EN→FR", "EN→ES", "EN→DE", "EN→IT", "EN→JA", "EN→ZH", "EN→AR", "EN→PT"] + for row in range(2, 22): + ws3.cell(row=row, column=1, value=f"2024-{(row-1) // 2 + 1:02d}-{(row % 28) + 1:02d}") + ws3.cell(row=row, column=2, value=random.choice(pairs)) + ws3.cell(row=row, column=3, value=random.randint(50, 5000)) + ws3.cell(row=row, column=4, value=round(random.uniform(25, 55), 1)) + ws3.cell(row=row, column=5, value=round(random.uniform(55, 75), 1)) + ws3.cell(row=row, column=6, value=round(random.uniform(0.5, 5.0), 2)) + ws3.cell(row=row, column=7, value=round(random.uniform(0.5, 50.0), 2)) + ws3.cell(row=row, column=8, value=random.choice(["↑", "↓", "→"])) + + # Add many more cells with text to stress translation + for row in range(23, 35): + ws3.cell(row=row, column=1, value=f"Quarterly review Q{(row-23)//3 + 1}") + ws3.cell(row=row, column=2, value=( + "Translation quality continued to improve across all language pairs. " + "The new neural architecture reduced latency by 35% while maintaining " + "the same BLEU score. Customer satisfaction increased significantly." + )) + + # ============ Sheet 4: Glossary ============ + ws4 = wb.create_sheet("Glossary") + ws4["A1"] = "English" + ws4["B1"] = "French" + ws4["C1"] = "Spanish" + ws4["D1"] = "German" + for c in ["A1", "B1", "C1", "D1"]: + ws4[c].font = Font(bold=True) + glossary = [ + ("invoice", "facture", "factura", "Rechnung"), + ("shipping", "expédition", "envío", "Versand"), + ("discount", "remise", "descuento", "Rabatt"), + ("warranty", "garantie", "garantía", "Garantie"), + ("refund", "remboursement", "reembolso", "Rückerstattung"), + ("delivery", "livraison", "entrega", "Lieferung"), + ("customer", "client", "cliente", "Kunde"), + ("order", "commande", "pedido", "Bestellung"), + ("payment", "paiement", "pago", "Zahlung"), + ("subscription", "abonnement", "suscripción", "Abonnement"), + ] + for i, row in enumerate(glossary, start=2): + for j, val in enumerate(row, start=1): + ws4.cell(row=i, column=j, value=val) wb.save(out) - print(f"[OK] Generated {out} ({out.stat().st_size} bytes)") + print(f"[OK] {out.name}: {out.stat().st_size} bytes") + print(f" - Sheets: 4 (Sales, Customers, Metrics, Glossary)") + print(f" - Charts: 3 (bar, pie, line)") + print(f" - Hyperlinks: ~80 (one per data row)") + print(f" - Comments: ~15") + print(f" - Conditional formatting, frozen panes") # =========================================================================== -# PowerPoint — B2 (SmartArt, chart, placeholders) +# COMPLEX PPTX — B2 stress test # =========================================================================== def generate_pptx(): out = OUT_DIR / "test_pptx.pptx" prs = Presentation() - prs.slide_width = PInches(10) + prs.slide_width = PInches(13.333) # Widescreen prs.slide_height = PInches(7.5) - # === Slide 1: real text + SmartArt diagram === - slide1 = prs.slides.add_slide(prs.slide_layouts[6]) # blank + def add_text(slide, text, x=1, y=1, w=8, h=1, size=18, bold=False, color=None): + tb = slide.shapes.add_textbox(PInches(x), PInches(y), PInches(w), PInches(h)) + tf = tb.text_frame + tf.word_wrap = True + p = tf.paragraphs[0] + run = p.add_run() + run.text = text + run.font.size = PPt(size) + run.font.bold = bold + if color: + run.font.color.rgb = color + return tb - # Title text - tb = slide1.shapes.add_textbox(PInches(0.5), PInches(0.3), PInches(9), PInches(0.7)) - tf = tb.text_frame - tf.text = "Project Status Overview" + def add_bullets(slide, items, x=1, y=2, w=10, h=4, size=16): + tb = slide.shapes.add_textbox(PInches(x), PInches(y), PInches(w), PInches(h)) + tf = tb.text_frame + tf.word_wrap = True + for i, item in enumerate(items): + p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() + p.text = f"• {item}" + p.font.size = PPt(size) + return tb - # Real text box (should be translated) - tb2 = slide1.shapes.add_textbox(PInches(0.5), PInches(1.2), PInches(9), PInches(2)) - tf2 = tb2.text_frame - tf2.text = ( - "This presentation summarizes the current project status. " - "The development team has completed the initial research phase " - "and is now moving into implementation." - ) + # ============ Slide 1: Title slide ============ + s1 = prs.slides.add_slide(prs.slide_layouts[0]) + title = s1.shapes.title + title.text = "Q4 2024 Product Strategy" + subtitle = s1.placeholders[1] + subtitle.text = "Office Translator — Internal Roadmap Review" - # Inject a SmartArt diagram by writing directly to the zip - # (python-pptx doesn't have a high-level SmartArt API) - slide1.save = lambda *a, **k: None # placeholder, will be replaced + # ============ Slide 2: Agenda ============ + s2 = prs.slides.add_slide(prs.slide_layouts[1]) + s2.shapes.title.text = "Agenda" + add_bullets(s2, [ + "Quality improvements: L0, L1, L2 evaluation layers", + "Format preservation: Word, Excel, PowerPoint, PDF", + "Performance: caching, async processing, parallelism", + "New features: glossaries, custom prompts, watermarks", + "Q1 2025 priorities and resource allocation", + ]) - # Save what we have so far + # ============ Slide 3: Quality metrics (with chart) ============ + s3 = prs.slides.add_slide(prs.slide_layouts[5]) + s3.shapes.title.text = "Quality Improvements Year-Over-Year" + add_text(s3, "BLEU scores have steadily improved across all language pairs since the introduction of neural architectures.", x=0.5, y=1.2, w=12, h=1, size=14) + + # Add a real bar chart + from pptx.chart.data import CategoryChartData + from pptx.enum.chart import XL_CHART_TYPE + chart_data = CategoryChartData() + chart_data.categories = ["2020", "2021", "2022", "2023", "2024"] + chart_data.add_series("EN→FR", (32.1, 36.5, 41.2, 45.8, 48.2)) + chart_data.add_series("EN→ES", (35.4, 39.7, 44.5, 49.1, 51.7)) + chart_data.add_series("EN→DE", (30.8, 34.2, 39.1, 43.5, 45.9)) + chart_data.add_series("EN→JA", (18.5, 22.1, 26.8, 30.2, 32.4)) + chart_data.add_series("EN→ZH", (16.2, 20.5, 24.9, 28.1, 30.1)) + chart = s3.shapes.add_chart( + XL_CHART_TYPE.COLUMN_CLUSTERED, + PInches(0.5), PInches(2.5), PInches(12.3), PInches(4.5), + chart_data + ).chart + chart.has_title = True + chart.chart_title.text_frame.text = "BLEU Score by Year (Higher is better)" + chart.has_legend = True + + # ============ Slide 4: SmartArt (manual injection) ============ + s4 = prs.slides.add_slide(prs.slide_layouts[5]) + s4.shapes.title.text = "Project Organization" + add_text(s4, "Our engineering team is organized into five cross-functional squads:", x=0.5, y=1.2, w=12, h=0.7, size=14) + + # ============ Slide 5: Process diagram (also SmartArt) ============ + s5 = prs.slides.add_slide(prs.slide_layouts[5]) + s5.shapes.title.text = "Translation Pipeline" + add_text(s5, "Documents flow through four stages before reaching the user:", x=0.5, y=1.2, w=12, h=0.7, size=14) + + # ============ Slide 6: Performance (line chart) ============ + s6 = prs.slides.add_slide(prs.slide_layouts[5]) + s6.shapes.title.text = "Latency Improvements" + chart_data2 = CategoryChartData() + chart_data2.categories = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + chart_data2.add_series("Avg latency (s)", (8.2, 7.5, 6.8, 5.9, 5.1, 4.4, 3.8, 3.2, 2.7, 2.3, 1.9, 1.6)) + chart2 = s6.shapes.add_chart( + XL_CHART_TYPE.LINE, + PInches(0.5), PInches(1.5), PInches(12.3), PInches(5.5), + chart_data2 + ).chart + chart2.has_title = True + chart2.chart_title.text_frame.text = "Average translation latency (seconds)" + chart2.has_legend = False + + # ============ Slide 7: Architecture diagram (third SmartArt) ============ + s7 = prs.slides.add_slide(prs.slide_layouts[5]) + s7.shapes.title.text = "System Architecture" + add_text(s7, "The platform is built on a microservices architecture:", x=0.5, y=1.2, w=12, h=0.7, size=14) + + # ============ Slide 8: Comparison (pie chart) ============ + s8 = prs.slides.add_slide(prs.slide_layouts[5]) + s8.shapes.title.text = "Market Share by Language Pair" + chart_data3 = CategoryChartData() + chart_data3.categories = ["EN→FR", "EN→ES", "EN→DE", "EN→JA", "EN→ZH", "Other"] + chart_data3.add_series("Volume", (28, 22, 18, 12, 10, 10)) + chart3 = s8.shapes.add_chart( + XL_CHART_TYPE.PIE, + PInches(2), PInches(1.5), PInches(9), PInches(5.5), + chart_data3 + ).chart + chart3.has_title = True + chart3.chart_title.text_frame.text = "Document volume by target language (%)" + chart3.has_legend = True + + # ============ Slide 9: Q1 priorities ============ + s9 = prs.slides.add_slide(prs.slide_layouts[1]) + s9.shapes.title.text = "Q1 2025 Priorities" + add_bullets(s9, [ + "Launch L2 Pro premium judge for enterprise customers", + "Roll out Redis cache cluster to reduce API costs by ~40%", + "Improve PDF hyperlink preservation (currently 78% success rate)", + "Add support for 12 additional language pairs (Arabic dialects, etc.)", + "Reduce average latency below 1 second for documents under 5 pages", + ]) + + # ============ Slide 10: Closing ============ + s10 = prs.slides.add_slide(prs.slide_layouts[0]) + s10.shapes.title.text = "Thank you" + s10.placeholders[1].text = "Questions? Reach out to the team." + + # Save first, then inject SmartArt tmp = OUT_DIR / "_intermediate.pptx" prs.save(tmp) - # Now inject the SmartArt diagram - import zipfile - diagram_xml = b""" + # Inject three SmartArt diagrams (one per slide that has them) + diagram_xml_1 = """ - Project Phases + Engineering Team - Research Complete + Quality Squad - Design Phase + Performance Squad - Implementation + Format Squad - Testing and Launch + Infrastructure Squad + + + Product Squad + + + +""" + diagram_xml_2 = """ + + + + Document Upload + + + Quality Check + + + Translation + + + Format Preservation + + + Delivery to User + + + +""" + diagram_xml_3 = """ + + + + Load Balancer + + + API Gateway + + + Translation Workers + + + PostgreSQL Database + + + Redis Cache + + + Object Storage + + + Monitoring Stack @@ -322,141 +887,418 @@ def generate_pptx(): with zipfile.ZipFile(tmp, "r") as zin, \ zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as zout: items = {n: zin.read(n) for n in zin.namelist()} - items["ppt/diagrams/data1.xml"] = diagram_xml + items["ppt/diagrams/data1.xml"] = diagram_xml_1.encode("utf-8") + items["ppt/diagrams/data2.xml"] = diagram_xml_2.encode("utf-8") + items["ppt/diagrams/data3.xml"] = diagram_xml_3.encode("utf-8") - # Update content types ct = items["[Content_Types].xml"].decode("utf-8") - override = ('') - if override not in ct: - ct = ct.replace("", f"{override}") + for i in range(1, 4): + override = ( + f'' + ) + if override not in ct: + ct = ct.replace("", f"{override}") items["[Content_Types].xml"] = ct.encode("utf-8") for n, data in items.items(): zout.writestr(n, data) tmp.unlink() - print(f"[OK] Generated {out} ({out.stat().st_size} bytes) with SmartArt + placeholders") + print(f"[OK] {out.name}: {out.stat().st_size} bytes") + print(f" - Slides: 10") + print(f" - SmartArt: 3 diagrams (5-7 nodes each)") + print(f" - Charts: 3 (column, line, pie)") + print(f" - Placeholders: each slide has date + slide # + footer (default)") # =========================================================================== -# PDF — B3 (hyperlinks, image preservation) +# COMPLEX PDF — B3 stress test # =========================================================================== def generate_pdf(): out = OUT_DIR / "test_pdf.pdf" doc = fitz.open() - # Create all pages first (so internal goto links can reference them). - # Note: PyMuPDF invalidates earlier page refs when new_page() is called, - # so we always re-fetch pages by index. - doc.new_page() - doc.new_page() + # Create all pages upfront + for _ in range(8): + doc.new_page() - # === Page 1: title + paragraphs + external hyperlink === + def add_text_block(page, x, y, text, fontsize=12, color=(0, 0, 0), bold=False): + # split text into lines for nicer rendering + from fitz import Point + lines = text.split("\n") + for i, line in enumerate(lines): + page.insert_text( + (x, y + i * (fontsize + 4)), + line, + fontsize=fontsize, + color=color, + ) + + # PHASE 1: add all text and images (no links yet) + # ============ Page 1: Title + TOC ============ p1 = doc[0] - p1.insert_text((72, 72), "Document Translation Test", fontsize=20) - p1.insert_text( - (72, 120), - "This is a test PDF with hyperlinks and images.", - fontsize=12, - ) - p1.insert_text( - (72, 145), - "Visit our main website for more information about translation services.", - fontsize=12, - ) - # External URI hyperlink - p1.insert_link({ - "kind": fitz.LINK_URI, - "from": fitz.Rect(72, 168, 250, 185), - "uri": "https://www.example.com", - }) - # Internal goto link to page 2 - p1.insert_text( - (72, 200), - "Continue reading on the next page (click here).", - fontsize=12, - ) - p1.insert_link({ - "kind": fitz.LINK_GOTO, - "from": fitz.Rect(72, 198, 150, 213), - "page": 1, # second page (0-indexed) - "to": fitz.Point(72, 72), - }) + p1.insert_text((72, 80), "Technical Specification: Office Translator v3.0", fontsize=22, color=(0.1, 0.2, 0.5)) + p1.insert_text((72, 115), "Document version 3.0.1 — Last updated December 2024", fontsize=11, color=(0.4, 0.4, 0.4)) + p1.insert_text((72, 160), "Table of Contents", fontsize=16, color=(0.1, 0.2, 0.5)) + toc_entries = [ + ("1. Introduction", 1), + ("2. Installation and Setup", 2), + ("3. Translation Engine", 3), + ("4. Format Preservation", 4), + ("5. Quality Assurance", 5), + ("6. Performance and Scaling", 6), + ("7. API Reference", 7), + ("8. Troubleshooting", 8), + ] + for i, (label, page_idx) in enumerate(toc_entries): + y = 190 + i * 22 + p1.insert_text((90, y), label, fontsize=12) + p1.insert_text((350, y), f".......... {page_idx}", fontsize=12, color=(0.5, 0.5, 0.5)) + p1.insert_text((72, 420), "For the latest version of this document, visit:", fontsize=11) + p1.insert_text((72, 440), "https://docs.office-translator.example.com/v3", fontsize=12, color=(0.1, 0.1, 0.8)) + p1.draw_rect(fitz.Rect(72, 480, 500, 550), color=(0.9, 0.95, 1.0), fill=(0.85, 0.92, 1.0)) + p1.insert_text((85, 500), "Important notice", fontsize=14, color=(0.1, 0.2, 0.5)) + p1.insert_text((85, 525), "This is a pre-release document.", fontsize=11) + p1.insert_text((85, 542), "Subject to change without notice.", fontsize=11) - # Add a colored rectangle (mimics an image for B3 image-preservation test) - p1.draw_rect(fitz.Rect(72, 240, 300, 320), color=(0.7, 0.3, 0.3), fill=(0.9, 0.6, 0.6)) - p1.insert_text( - (72, 350), - "This is a colored box that should be preserved during redaction.", - fontsize=10, - ) - - # === Page 2: more text + an actual image === + # ============ Page 2: Introduction ============ p2 = doc[1] - p2.insert_text((72, 72), "Page Two", fontsize=20) + p2.insert_text((72, 80), "1. Introduction", fontsize=20, color=(0.1, 0.2, 0.5)) p2.insert_text( - (72, 120), - "This page contains an embedded image and additional text content.", + (72, 130), + "Office Translator is a SaaS platform for translating office documents " + "(Word, Excel, PowerPoint, PDF) while preserving their formatting. The " + "platform supports 50+ language pairs and processes over 100,000 " + "documents per day.", fontsize=12, ) - # Embed a proper PNG (generated with PIL) + p2.insert_text( + (72, 220), + "This document describes the public API, configuration options, and " + "best practices for integrating Office Translator into your workflow. " + "For pricing and subscription information, see our website.", + fontsize=12, + ) + p2.insert_text((72, 295), "Pricing: https://office-translator.example.com/pricing", fontsize=11, color=(0.1, 0.1, 0.8)) try: - from PIL import Image - import io - # Create a 100x60 RGB image - img = Image.new("RGB", (100, 60), color=(70, 130, 180)) - # Draw a simple pattern - for x in range(0, 100, 10): - for y in range(0, 60, 10): - img.putpixel((x, y), (255, 200, 100)) + from PIL import Image, ImageDraw + img = Image.new("RGB", (300, 180), color=(255, 255, 255)) + d = ImageDraw.Draw(img) + for x in range(20, 280, 30): + h = 20 + (x % 80) + d.rectangle([x, 160 - h, x + 20, 160], fill=(70, 130, 180)) + d.text((20, 10), "Sample chart: Translation volume", fill=(0, 0, 0)) buf = io.BytesIO() img.save(buf, format="PNG") - p2.insert_image(fitz.Rect(72, 160, 200, 240), stream=buf.getvalue()) + p2.insert_image(fitz.Rect(72, 340, 372, 520), stream=buf.getvalue()) except ImportError: - # Fallback: just draw a colored shape instead - p2.draw_rect(fitz.Rect(72, 160, 200, 240), color=(0.3, 0.5, 0.7), fill=(0.3, 0.5, 0.7)) - p2.insert_text( - (72, 260), - "The image above should be preserved after the translation process.", - fontsize=10, - ) + p2.draw_rect(fitz.Rect(72, 340, 372, 520), color=(0.7, 0.7, 0.9), fill=(0.85, 0.85, 1.0)) - # Another external link on page 2 - p2.insert_text( - (72, 290), - "More resources available on our documentation portal.", + # ============ Page 3: Installation & Setup ============ + p3 = doc[2] + p3.insert_text((72, 80), "2. Installation and Setup", fontsize=20, color=(0.1, 0.2, 0.5)) + p3.insert_text( + (72, 130), + "To get started, create an account on our platform and obtain an API key. " + "The API key must be included in the Authorization header of every request.", fontsize=12, ) - p2.insert_link({ + p3.insert_text((72, 180), "2.1 Quick Start", fontsize=14, color=(0.1, 0.2, 0.5)) + p3.insert_text((72, 210), "Step 1: Sign up at https://app.office-translator.example.com", fontsize=11) + p3.insert_text((72, 240), "Step 2: Generate an API key in the dashboard", fontsize=11) + p3.insert_text((72, 270), "Step 3: Make your first translation request:", fontsize=11) + code_box = fitz.Rect(72, 295, 500, 410) + p3.draw_rect(code_box, color=(0.95, 0.95, 0.95), fill=(0.97, 0.97, 0.97)) + code_lines = [ + "curl -X POST https://api.office-translator.example.com/v1/translate \\", + " -H 'Authorization: Bearer YOUR_API_KEY' \\", + " -F 'file=@document.docx' \\", + " -F 'target_lang=fr' \\", + " -F 'source_lang=en'", + ] + for i, line in enumerate(code_lines): + p3.insert_text((85, 305 + i * 18), line, fontsize=10, color=(0.2, 0.2, 0.2)) + p3.insert_text((72, 440), "Step 4: Download the translated file when the job completes", fontsize=11) + p3.insert_text((72, 445), "(see the Quickstart guide for details)", fontsize=11, color=(0.5, 0.5, 0.5)) + + # ============ Page 4: Translation Engine ============ + p4 = doc[3] + p4.insert_text((72, 80), "3. Translation Engine", fontsize=20, color=(0.1, 0.2, 0.5)) + p4.insert_text( + (72, 130), + "Our translation engine combines multiple models and techniques to deliver " + "high-quality output across diverse content types. The engine is designed " + "to be fast, accurate, and cost-effective.", + fontsize=12, + ) + p4.insert_text((72, 220), "3.1 Supported Languages", fontsize=14, color=(0.1, 0.2, 0.5)) + languages = [ + "English, French, Spanish, German, Italian, Portuguese, Dutch", + "Russian, Polish, Czech, Romanian, Hungarian, Greek, Ukrainian", + "Arabic, Hebrew, Persian, Urdu, Turkish, Kurdish", + "Chinese (Simplified and Traditional), Japanese, Korean, Vietnamese, Thai", + "Hindi, Bengali, Tamil, Telugu, Marathi, Gujarati, Punjabi", + "Swahili, Afrikaans, Zulu, Amharic, Yoruba, Igbo, Hausa", + ] + for i, lang in enumerate(languages): + p4.insert_text((90, 250 + i * 20), f"• {lang}", fontsize=11) + p4.insert_text((72, 400), "3.2 Provider Selection", fontsize=14, color=(0.1, 0.2, 0.5)) + p4.insert_text( + (72, 430), + "You can choose between several translation providers depending on your " + "needs: Google Translate (free tier), DeepL (premium), OpenAI GPT-4 " + "(premium, best for technical content), or Anthropic Claude (premium, " + "best for literary content).", + fontsize=11, + ) + p4.insert_text((72, 540), "For a comparison of providers, see our benchmark report", fontsize=11, color=(0.1, 0.1, 0.8)) + + # ============ Page 5: Format Preservation ============ + p5 = doc[4] + p5.insert_text((72, 80), "4. Format Preservation", fontsize=20, color=(0.1, 0.2, 0.5)) + p5.insert_text( + (72, 130), + "Format preservation is a core feature of Office Translator. Unlike raw " + "translation APIs that return plain text, our platform preserves:", + fontsize=12, + ) + features = [ + "Hyperlinks (URLs are kept verbatim; only the visible text is translated)", + "Footnotes and endnotes (collected, translated, and re-injected)", + "Cell comments in Excel", + "Embedded charts (titles, axis labels, category names)", + "SmartArt diagrams in PowerPoint", + "Headers, footers, and page numbers", + "Tables (with formatting and merged cells)", + "Images (never rasterized away during redaction)", + ] + for i, feat in enumerate(features): + p5.insert_text((90, 180 + i * 25), f"• {feat}", fontsize=11) + p5.insert_text((72, 400), "For a complete list of supported features, see:", fontsize=11) + p5.insert_text((72, 420), "https://docs.office-translator.example.com/format-support", fontsize=11, color=(0.1, 0.1, 0.8)) + try: + from PIL import Image, ImageDraw + img = Image.new("RGB", (400, 200), color=(255, 255, 255)) + d = ImageDraw.Draw(img) + d.rectangle([10, 10, 390, 190], outline=(50, 50, 50), width=2) + d.text((20, 20), "Format preservation: before & after", fill=(0, 0, 0)) + d.rectangle([20, 50, 180, 90], outline=(0, 0, 0)) + d.text((30, 60), "Original", fill=(0, 0, 0)) + d.rectangle([200, 50, 380, 90], outline=(0, 100, 0)) + d.text((220, 60), "Translated", fill=(0, 100, 0)) + buf = io.BytesIO() + img.save(buf, format="PNG") + p5.insert_image(fitz.Rect(72, 470, 472, 670), stream=buf.getvalue()) + except ImportError: + p5.draw_rect(fitz.Rect(72, 470, 472, 670), color=(0.8, 0.9, 0.8), fill=(0.9, 1.0, 0.9)) + + # ============ Page 6: Quality Assurance ============ + p6 = doc[5] + p6.insert_text((72, 80), "5. Quality Assurance", fontsize=20, color=(0.1, 0.2, 0.5)) + p6.insert_text( + (72, 130), + "Every translation goes through three quality layers before being " + "delivered to the user:", + fontsize=12, + ) + layers = [ + ("L0 (Pattern detection)", "Detects language confusion, length anomalies, and prompt leaks. No API call."), + ("L1 (LLM judge)", "Sends 5 sample chunks to a cheap LLM (deepseek-chat) for a binary pass/fail verdict. ~$0.0003/job."), + ("L2 (Pro judge, 8 dimensions)", "Sends 15 sample chunks to gpt-4o for fine-grained evaluation across accuracy, fluency, terminology, style, completeness, and formatting. ~$0.005/job. Pro+ tier only."), + ] + for i, (title, desc) in enumerate(layers): + y = 180 + i * 80 + p6.insert_text((90, y), title, fontsize=13, color=(0.1, 0.2, 0.5)) + p6.insert_text((90, y + 22), desc, fontsize=11) + p6.insert_text((72, 460), "5.1 Metrics", fontsize=14, color=(0.1, 0.2, 0.5)) + p6.insert_text( + (72, 490), + "All quality scores are exposed as Prometheus metrics for monitoring " + "and alerting. See the metrics endpoint at /metrics for details.", + fontsize=11, + ) + + # ============ Page 7: Performance ============ + p7 = doc[6] + p7.insert_text((72, 80), "6. Performance and Scaling", fontsize=20, color=(0.1, 0.2, 0.5)) + p7.insert_text( + (72, 130), + "Office Translator is built on a horizontally scalable architecture. " + "Translation workers process jobs in parallel, with intelligent caching " + "to avoid redundant API calls.", + fontsize=12, + ) + perf_table_data = [ + ("Document size", "Avg latency (s)", "Throughput (docs/min)"), + ("< 1 page", "0.4", "150"), + ("1-5 pages", "1.2", "50"), + ("5-20 pages", "3.8", "16"), + ("20-100 pages", "12.5", "5"), + ("> 100 pages", "45.0", "1"), + ] + for i, row in enumerate(perf_table_data): + y = 200 + i * 25 + for j, cell in enumerate(row): + color = (0.1, 0.2, 0.5) if i == 0 else (0, 0, 0) + p7.insert_text((90 + j * 130, y), cell, fontsize=11, color=color) + p7.insert_text((72, 380), "6.1 Caching", fontsize=14, color=(0.1, 0.2, 0.5)) + p7.insert_text( + (72, 410), + "Repeated translations of the same content are served from cache, " + "reducing API costs by up to 40% on typical workloads. The cache key " + "includes the user, target language, and any custom prompt/glossary " + "to avoid cross-contamination.", + fontsize=11, + ) + + # ============ Page 8: API Reference + Troubleshooting ============ + p8 = doc[7] + p8.insert_text((72, 80), "7. API Reference", fontsize=20, color=(0.1, 0.2, 0.5)) + p8.insert_text( + (72, 130), + "The full API reference is available on our developer portal. Key endpoints:", + fontsize=12, + ) + endpoints = [ + ("POST /v1/translate", "Submit a document for translation"), + ("GET /v1/translations/{id}", "Get the status of a translation job"), + ("GET /v1/download/{id}", "Download the translated file"), + ("GET /v1/jobs", "List all jobs for the authenticated user"), + ("GET /v1/glossaries", "List available glossaries"), + ("POST /v1/glossaries", "Create a new glossary"), + ] + for i, (ep, desc) in enumerate(endpoints): + y = 180 + i * 30 + p8.insert_text((90, y), ep, fontsize=11, color=(0.1, 0.2, 0.5)) + p8.insert_text((290, y), desc, fontsize=11) + p8.insert_text((72, 400), "For interactive API documentation, see:", fontsize=11) + p8.insert_text((72, 420), "https://api.office-translator.example.com/docs", fontsize=11, color=(0.1, 0.1, 0.8)) + p8.insert_text((72, 480), "8. Troubleshooting", fontsize=20, color=(0.1, 0.2, 0.5)) + p8.insert_text( + (72, 530), + "Common issues and their solutions are documented in our troubleshooting " + "guide. If you encounter a problem not covered there, please contact support.", + fontsize=12, + ) + p8.insert_text((72, 600), "https://docs.office-translator.example.com/troubleshooting", fontsize=11, color=(0.1, 0.1, 0.8)) + + # Add a page header/footer-style band on every page + for i, page in enumerate(doc): + page.insert_text( + (72, 770), + f"Office Translator v3.0 Technical Specification — Page {i + 1} of {len(doc)}", + fontsize=8, + color=(0.5, 0.5, 0.5), + ) + + # Save and re-open — PyMuPDF requires the document to be flushed + # before we can resolve page references in goto links. + tmp_path = out.with_suffix(".tmp.pdf") + doc.save(str(tmp_path)) + doc.close() + + # PHASE 2: add links in a fresh document handle + doc2 = fitz.open(str(tmp_path)) + + # Page 1: TOC gotos + external link + p1 = doc2[0] + for i, (label, page_idx_display) in enumerate(toc_entries): + y = 190 + i * 22 + # PyMuPDF goto uses 0-indexed page numbers. + # "1. Introduction" is on page 1 in the TOC, which is page index 0 in the PDF. + target_page_idx = page_idx_display - 1 + p1.insert_link({ + "kind": fitz.LINK_GOTO, + "from": fitz.Rect(85, y - 12, 420, y + 5), + "page": target_page_idx, + "to": fitz.Point(72, 72), + }) + p1.insert_link({ "kind": fitz.LINK_URI, - "from": fitz.Rect(72, 313, 200, 330), - "uri": "https://docs.example.com/translation", + "from": fitz.Rect(70, 425, 380, 445), + "uri": "https://docs.office-translator.example.com/v3", }) - doc.save(str(out)) - doc.close() - print(f"[OK] Generated {out} ({out.stat().st_size} bytes) with hyperlinks + image") + # Page 2: pricing external link + doc2[1].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 280, 470, 300), + "uri": "https://office-translator.example.com/pricing", + }) + # Page 3: signup + quickstart links + doc2[2].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 195, 500, 215), + "uri": "https://app.office-translator.example.com", + }) + doc2[2].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 425, 500, 445), + "uri": "https://docs.office-translator.example.com/v3/quickstart", + }) + + # Page 4: providers link + doc2[3].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 425, 500, 500), + "uri": "https://docs.office-translator.example.com/providers", + }) + + # Page 5: format support link + doc2[4].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 415, 500, 435), + "uri": "https://docs.office-translator.example.com/format-support", + }) + + # Page 6: metrics docs link + doc2[5].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 485, 500, 540), + "uri": "https://docs.office-translator.example.com/metrics", + }) + + # Page 8: API docs + troubleshooting links + doc2[7].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 415, 500, 435), + "uri": "https://api.office-translator.example.com/docs", + }) + doc2[7].insert_link({ + "kind": fitz.LINK_URI, + "from": fitz.Rect(70, 525, 500, 590), + "uri": "https://docs.office-translator.example.com/troubleshooting", + }) + + doc2.save(str(out)) + doc2.close() + tmp_path.unlink() + + # Quick verify + doc_v = fitz.open(str(out)) + total_links = 0 + total_images = 0 + for p in doc_v: + total_links += len(p.get_links()) + total_images += len(p.get_images()) + n_pages = len(doc_v) + doc_v.close() + + print(f"[OK] {out.name}: {out.stat().st_size} bytes") + print(f" - Pages: {n_pages}") + print(f" - Total hyperlinks: {total_links} (URI + GOTO)") + print(f" - Total images: {total_images}") + print(f" - Tables, code blocks, TOC, headers/footers") -# =========================================================================== -# Main -# =========================================================================== if __name__ == "__main__": - print(f"Output directory: {OUT_DIR.resolve()}") - print() + print(f"Output directory: {OUT_DIR.resolve()}\n") generate_word() generate_excel() generate_pptx() generate_pdf() - print() - print("Test corpus generated. To test:") - print(" 1. Upload each file to your Office Translator instance") - print(" 2. Download the translated output") - print(" 3. Verify format preservation:") - print(" - test_word.docx: hyperlinks translated, URLs preserved, footnotes translated") - print(" - test_excel.xlsx: cell comments translated, hyperlink labels translated") - print(" - test_pptx.pptx: SmartArt nodes translated, chart text translated") - print(" - test_pdf.pdf: hyperlinks clickable, image preserved, no redaction artifacts") + print("\n[OK] Test corpus generated.") + print("Each file is now substantial enough to catch real format-preservation bugs.")