[{"data":1,"prerenderedAt":2032},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving":3,"surround:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving":2000,"ancestors:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving":2009,"children:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving":2014,"related:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving":2015},{"id":4,"title":5,"body":6,"dateModified":1968,"datePublished":1968,"description":1969,"extension":1970,"faq":1971,"keyword":1980,"kind":1981,"meta":1982,"navigation":110,"path":1990,"seo":1991,"slug":1994,"stem":1995,"topics":1996,"type":1998,"__hash__":1999},"docs\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving\u002Findex.md","Export an Excel Report to PDF\u002FA for Archiving",{"type":7,"value":8,"toc":1957},"minimark",[9,19,24,60,63,67,70,445,452,456,474,783,799,934,938,941,1414,1421,1424,1514,1517,1521,1524,1659,1671,1835,1839,1876,1880,1892,1896,1902,1908,1914,1920,1924,1953],[10,11,12,13,18],"p",{},"Month-end packs, statutory reports and signed-off reconciliations often have to be kept for years — seven or ten is common for finance records — and auditors, regulators or records-management systems may ask for them in PDF\u002FA. PDF\u002FA is an ISO-standardised subset of PDF designed so a file will display the same way decades from now: every font is embedded, colours are defined device-independently, and features that depend on something outside the file — external links to content, JavaScript, encryption — are forbidden. An ordinary \"save as PDF\" usually breaks at least one rule, typically by not declaring an output colour profile. This page exports a workbook to PDF\u002FA with LibreOffice from Python, sets the metadata an archive will index, checks the result for the markers a conforming file must carry, and shows how easily a later processing step can undo conformance. It belongs to ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002F","Exporting Excel Reports to PDF",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pypdf\n# LibreOffice 7.4 or later for the JSON-style export options used below\n","bash","",[32,33,34,53],"code",{"__ignoreMap":30},[35,36,39,43,47,50],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," openpyxl",[35,51,52],{"class":45}," pypdf\n",[35,54,56],{"class":37,"line":55},2,[35,57,59],{"class":58},"s-wDw","# LibreOffice 7.4 or later for the JSON-style export options used below\n",[10,61,62],{},"LibreOffice does the conversion; pypdf reads the result to check it. The checks below test the most common failure points, but they are not a full validation — for that, run the free veraPDF validator, the reference implementation used by archives, on a sample of your exports when you set the process up and whenever it changes.",[20,64,66],{"id":65},"step-1-a-report-with-proper-document-properties","Step 1 — A report with proper document properties",[10,68,69],{},"Archive systems index a PDF by its metadata, and LibreOffice copies the workbook's title and author into the PDF. Set them deliberately rather than leaving the library's defaults:",[25,71,75],{"className":72,"code":73,"language":74,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.styles import Font\n\nwb = Workbook()\nws = wb.active\nws.title = \"Trial balance\"\nws.append([\"Account\", \"Name\", \"Debit\", \"Credit\"])\nfor c in ws[1]:\n    c.font = Font(bold=True)\nfor row in [[1000, \"Cash at bank\", 182450.10, None], [1200, \"Trade debtors\", 96210.00, None],\n            [2100, \"Trade creditors\", None, 71320.55], [3000, \"Share capital\", None, 50000.00],\n            [4000, \"Sales — Café Señor account\", None, 157339.55]]:\n    ws.append(row)\nfor col, width in zip(\"ABCD\", (10, 30, 14, 14)):\n    ws.column_dimensions[col].width = width\nwb.properties.title = \"Trial balance — September 2026\"\nwb.properties.creator = \"Finance reporting\"\nwb.properties.subject = \"Month-end close\"\nwb.save(\"trial_balance.xlsx\")\n","python",[32,76,77,93,105,112,124,135,146,174,196,219,273,317,342,348,390,401,412,423,434],{"__ignoreMap":30},[35,78,79,83,87,90],{"class":37,"line":38},[35,80,82],{"class":81},"s-kum","from",[35,84,86],{"class":85},"skGVy"," openpyxl ",[35,88,89],{"class":81},"import",[35,91,92],{"class":85}," Workbook\n",[35,94,95,97,100,102],{"class":37,"line":55},[35,96,82],{"class":81},[35,98,99],{"class":85}," openpyxl.styles ",[35,101,89],{"class":81},[35,103,104],{"class":85}," Font\n",[35,106,108],{"class":37,"line":107},3,[35,109,111],{"emptyLinePlaceholder":110},true,"\n",[35,113,115,118,121],{"class":37,"line":114},4,[35,116,117],{"class":85},"wb ",[35,119,120],{"class":81},"=",[35,122,123],{"class":85}," Workbook()\n",[35,125,127,130,132],{"class":37,"line":126},5,[35,128,129],{"class":85},"ws ",[35,131,120],{"class":81},[35,133,134],{"class":85}," wb.active\n",[35,136,138,141,143],{"class":37,"line":137},6,[35,139,140],{"class":85},"ws.title ",[35,142,120],{"class":81},[35,144,145],{"class":45}," \"Trial balance\"\n",[35,147,149,152,155,158,161,163,166,168,171],{"class":37,"line":148},7,[35,150,151],{"class":85},"ws.append([",[35,153,154],{"class":45},"\"Account\"",[35,156,157],{"class":85},", ",[35,159,160],{"class":45},"\"Name\"",[35,162,157],{"class":85},[35,164,165],{"class":45},"\"Debit\"",[35,167,157],{"class":85},[35,169,170],{"class":45},"\"Credit\"",[35,172,173],{"class":85},"])\n",[35,175,177,180,183,186,189,193],{"class":37,"line":176},8,[35,178,179],{"class":81},"for",[35,181,182],{"class":85}," c ",[35,184,185],{"class":81},"in",[35,187,188],{"class":85}," ws[",[35,190,192],{"class":191},"sP0c6","1",[35,194,195],{"class":85},"]:\n",[35,197,199,202,204,207,211,213,216],{"class":37,"line":198},9,[35,200,201],{"class":85},"    c.font ",[35,203,120],{"class":81},[35,205,206],{"class":85}," Font(",[35,208,210],{"class":209},"sa561","bold",[35,212,120],{"class":81},[35,214,215],{"class":191},"True",[35,217,218],{"class":85},")\n",[35,220,222,224,227,229,232,235,237,240,242,245,247,250,253,256,258,261,263,266,268,270],{"class":37,"line":221},10,[35,223,179],{"class":81},[35,225,226],{"class":85}," row ",[35,228,185],{"class":81},[35,230,231],{"class":85}," [[",[35,233,234],{"class":191},"1000",[35,236,157],{"class":85},[35,238,239],{"class":45},"\"Cash at bank\"",[35,241,157],{"class":85},[35,243,244],{"class":191},"182450.10",[35,246,157],{"class":85},[35,248,249],{"class":191},"None",[35,251,252],{"class":85},"], [",[35,254,255],{"class":191},"1200",[35,257,157],{"class":85},[35,259,260],{"class":45},"\"Trade debtors\"",[35,262,157],{"class":85},[35,264,265],{"class":191},"96210.00",[35,267,157],{"class":85},[35,269,249],{"class":191},[35,271,272],{"class":85},"],\n",[35,274,276,279,282,284,287,289,291,293,296,298,301,303,306,308,310,312,315],{"class":37,"line":275},11,[35,277,278],{"class":85},"            [",[35,280,281],{"class":191},"2100",[35,283,157],{"class":85},[35,285,286],{"class":45},"\"Trade creditors\"",[35,288,157],{"class":85},[35,290,249],{"class":191},[35,292,157],{"class":85},[35,294,295],{"class":191},"71320.55",[35,297,252],{"class":85},[35,299,300],{"class":191},"3000",[35,302,157],{"class":85},[35,304,305],{"class":45},"\"Share capital\"",[35,307,157],{"class":85},[35,309,249],{"class":191},[35,311,157],{"class":85},[35,313,314],{"class":191},"50000.00",[35,316,272],{"class":85},[35,318,320,322,325,327,330,332,334,336,339],{"class":37,"line":319},12,[35,321,278],{"class":85},[35,323,324],{"class":191},"4000",[35,326,157],{"class":85},[35,328,329],{"class":45},"\"Sales — Café Señor account\"",[35,331,157],{"class":85},[35,333,249],{"class":191},[35,335,157],{"class":85},[35,337,338],{"class":191},"157339.55",[35,340,341],{"class":85},"]]:\n",[35,343,345],{"class":37,"line":344},13,[35,346,347],{"class":85},"    ws.append(row)\n",[35,349,351,353,356,358,361,364,367,370,373,375,378,380,383,385,387],{"class":37,"line":350},14,[35,352,179],{"class":81},[35,354,355],{"class":85}," col, width ",[35,357,185],{"class":81},[35,359,360],{"class":191}," zip",[35,362,363],{"class":85},"(",[35,365,366],{"class":45},"\"ABCD\"",[35,368,369],{"class":85},", (",[35,371,372],{"class":191},"10",[35,374,157],{"class":85},[35,376,377],{"class":191},"30",[35,379,157],{"class":85},[35,381,382],{"class":191},"14",[35,384,157],{"class":85},[35,386,382],{"class":191},[35,388,389],{"class":85},")):\n",[35,391,393,396,398],{"class":37,"line":392},15,[35,394,395],{"class":85},"    ws.column_dimensions[col].width ",[35,397,120],{"class":81},[35,399,400],{"class":85}," width\n",[35,402,404,407,409],{"class":37,"line":403},16,[35,405,406],{"class":85},"wb.properties.title ",[35,408,120],{"class":81},[35,410,411],{"class":45}," \"Trial balance — September 2026\"\n",[35,413,415,418,420],{"class":37,"line":414},17,[35,416,417],{"class":85},"wb.properties.creator ",[35,419,120],{"class":81},[35,421,422],{"class":45}," \"Finance reporting\"\n",[35,424,426,429,431],{"class":37,"line":425},18,[35,427,428],{"class":85},"wb.properties.subject ",[35,430,120],{"class":81},[35,432,433],{"class":45}," \"Month-end close\"\n",[35,435,437,440,443],{"class":37,"line":436},19,[35,438,439],{"class":85},"wb.save(",[35,441,442],{"class":45},"\"trial_balance.xlsx\"",[35,444,218],{"class":85},[10,446,447,448,451],{},"Without ",[32,449,450],{},"creator",", openpyxl writes \"openpyxl\" as the author, and that is what the archive would record. The accented account name is there on purpose: characters outside plain ASCII are where font embedding problems show up first.",[20,453,455],{"id":454},"step-2-export-with-the-pdfa-option","Step 2 — Export with the PDF\u002FA option",[10,457,458,459,462,463,465,466,469,470,473],{},"LibreOffice's PDF export takes a ",[32,460,461],{},"SelectPdfVersion"," option: ",[32,464,192],{}," for PDF\u002FA-1b, ",[32,467,468],{},"2"," for PDF\u002FA-2b and ",[32,471,472],{},"3"," for PDF\u002FA-3b. From LibreOffice 7.4 options can be passed as JSON after the filter name:",[25,475,477],{"className":72,"code":476,"language":74,"meta":30,"style":30},"import json\nimport subprocess\n\ndef to_pdfa(xlsx, outdir=\"pdf\", part=2):\n    options = {\"SelectPdfVersion\": {\"type\": \"long\", \"value\": str(part)}}\n    target = f\"pdf:calc_pdf_Export:{json.dumps(options)}\"\n    subprocess.run([\"soffice\", \"--headless\", \"--convert-to\", target, \"--outdir\", outdir, xlsx],\n                   check=True, capture_output=True, timeout=180)\n    return f\"{outdir}\u002F{xlsx.rsplit('.', 1)[0]}.pdf\"\n\narchived = to_pdfa(\"trial_balance.xlsx\")\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"pdf\", \"--outdir\", \"plain\", \"trial_balance.xlsx\"],\n               check=True, capture_output=True, timeout=180)\nprint(archived, \"and plain\u002Ftrial_balance.pdf written\")\n",[32,478,479,486,493,497,524,562,588,615,645,691,695,709,743,770],{"__ignoreMap":30},[35,480,481,483],{"class":37,"line":38},[35,482,89],{"class":81},[35,484,485],{"class":85}," json\n",[35,487,488,490],{"class":37,"line":55},[35,489,89],{"class":81},[35,491,492],{"class":85}," subprocess\n",[35,494,495],{"class":37,"line":107},[35,496,111],{"emptyLinePlaceholder":110},[35,498,499,502,506,509,511,514,517,519,521],{"class":37,"line":114},[35,500,501],{"class":81},"def",[35,503,505],{"class":504},"s_Opv"," to_pdfa",[35,507,508],{"class":85},"(xlsx, outdir",[35,510,120],{"class":81},[35,512,513],{"class":45},"\"pdf\"",[35,515,516],{"class":85},", part",[35,518,120],{"class":81},[35,520,468],{"class":191},[35,522,523],{"class":85},"):\n",[35,525,526,529,531,534,537,540,543,546,549,551,554,556,559],{"class":37,"line":126},[35,527,528],{"class":85},"    options ",[35,530,120],{"class":81},[35,532,533],{"class":85}," {",[35,535,536],{"class":45},"\"SelectPdfVersion\"",[35,538,539],{"class":85},": {",[35,541,542],{"class":45},"\"type\"",[35,544,545],{"class":85},": ",[35,547,548],{"class":45},"\"long\"",[35,550,157],{"class":85},[35,552,553],{"class":45},"\"value\"",[35,555,545],{"class":85},[35,557,558],{"class":191},"str",[35,560,561],{"class":85},"(part)}}\n",[35,563,564,567,569,572,575,579,582,585],{"class":37,"line":137},[35,565,566],{"class":85},"    target ",[35,568,120],{"class":81},[35,570,571],{"class":81}," f",[35,573,574],{"class":45},"\"pdf:calc_pdf_Export:",[35,576,578],{"class":577},"sSjpA","{",[35,580,581],{"class":85},"json.dumps(options)",[35,583,584],{"class":577},"}",[35,586,587],{"class":45},"\"\n",[35,589,590,593,596,598,601,603,606,609,612],{"class":37,"line":148},[35,591,592],{"class":85},"    subprocess.run([",[35,594,595],{"class":45},"\"soffice\"",[35,597,157],{"class":85},[35,599,600],{"class":45},"\"--headless\"",[35,602,157],{"class":85},[35,604,605],{"class":45},"\"--convert-to\"",[35,607,608],{"class":85},", target, ",[35,610,611],{"class":45},"\"--outdir\"",[35,613,614],{"class":85},", outdir, xlsx],\n",[35,616,617,620,622,624,626,629,631,633,635,638,640,643],{"class":37,"line":176},[35,618,619],{"class":209},"                   check",[35,621,120],{"class":81},[35,623,215],{"class":191},[35,625,157],{"class":85},[35,627,628],{"class":209},"capture_output",[35,630,120],{"class":81},[35,632,215],{"class":191},[35,634,157],{"class":85},[35,636,637],{"class":209},"timeout",[35,639,120],{"class":81},[35,641,642],{"class":191},"180",[35,644,218],{"class":85},[35,646,647,650,652,655,657,660,662,665,667,670,673,675,677,680,683,686,688],{"class":37,"line":198},[35,648,649],{"class":81},"    return",[35,651,571],{"class":81},[35,653,654],{"class":45},"\"",[35,656,578],{"class":577},[35,658,659],{"class":85},"outdir",[35,661,584],{"class":577},[35,663,664],{"class":45},"\u002F",[35,666,578],{"class":577},[35,668,669],{"class":85},"xlsx.rsplit(",[35,671,672],{"class":45},"'.'",[35,674,157],{"class":85},[35,676,192],{"class":191},[35,678,679],{"class":85},")[",[35,681,682],{"class":191},"0",[35,684,685],{"class":85},"]",[35,687,584],{"class":577},[35,689,690],{"class":45},".pdf\"\n",[35,692,693],{"class":37,"line":221},[35,694,111],{"emptyLinePlaceholder":110},[35,696,697,700,702,705,707],{"class":37,"line":275},[35,698,699],{"class":85},"archived ",[35,701,120],{"class":81},[35,703,704],{"class":85}," to_pdfa(",[35,706,442],{"class":45},[35,708,218],{"class":85},[35,710,711,714,716,718,720,722,724,726,728,730,732,734,737,739,741],{"class":37,"line":319},[35,712,713],{"class":85},"subprocess.run([",[35,715,595],{"class":45},[35,717,157],{"class":85},[35,719,600],{"class":45},[35,721,157],{"class":85},[35,723,605],{"class":45},[35,725,157],{"class":85},[35,727,513],{"class":45},[35,729,157],{"class":85},[35,731,611],{"class":45},[35,733,157],{"class":85},[35,735,736],{"class":45},"\"plain\"",[35,738,157],{"class":85},[35,740,442],{"class":45},[35,742,272],{"class":85},[35,744,745,748,750,752,754,756,758,760,762,764,766,768],{"class":37,"line":344},[35,746,747],{"class":209},"               check",[35,749,120],{"class":81},[35,751,215],{"class":191},[35,753,157],{"class":85},[35,755,628],{"class":209},[35,757,120],{"class":81},[35,759,215],{"class":191},[35,761,157],{"class":85},[35,763,637],{"class":209},[35,765,120],{"class":81},[35,767,642],{"class":191},[35,769,218],{"class":85},[35,771,772,775,778,781],{"class":37,"line":350},[35,773,774],{"class":191},"print",[35,776,777],{"class":85},"(archived, ",[35,779,780],{"class":45},"\"and plain\u002Ftrial_balance.pdf written\"",[35,782,218],{"class":85},[10,784,785,786,790,791,794,795,18],{},"Which part to choose depends on who will read the file. PDF\u002FA-1b, based on PDF 1.4, is the most widely accepted and the most restrictive — no transparency, no layers. PDF\u002FA-2b, based on PDF 1.7, allows transparency and compression improvements and is a sensible default when the archive accepts it. PDF\u002FA-3b is PDF\u002FA-2 plus the ability to embed arbitrary files, such as the source workbook; it exists for that purpose and some archives deliberately reject it. The \"b\" means ",[787,788,789],"em",{},"basic"," conformance — the file is guaranteed to look right — as opposed to \"a\" (",[787,792,793],{},"accessible","), which additionally requires a complete tagged structure. The rest of the conversion process — profiles, timeouts, running several conversions at once — is covered in ",[14,796,798],{"href":797},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-to-pdf-on-linux-with-libreoffice\u002F","Convert Excel to PDF on Linux with LibreOffice",[800,801,809,810,809,814,809,818,809,825,809,833,809,840,809,847,809,850,809,854,809,857,809,861,809,866,809,872,809,874,809,879,809,881,809,884,809,886,809,889,809,892,809,896,809,898,809,902,809,904,809,907,809,909,809,912,809,915,809,919,809,921,809,924,809,926,809,929,809,931],"svg",{"viewBox":802,"role":803,"ariaLabelledBy":804,"xmlns":807,"style":808},"4 0 752 174","img",[805,806],"pa-parts-t","pa-parts-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[811,812,813],"title",{"id":805},"Choosing a PDF\u002FA part",[815,816,817],"desc",{"id":806},"PDF\u002FA-1b is based on PDF 1.4, forbids transparency and is the most widely accepted. PDF\u002FA-2b is based on PDF 1.7, allows transparency and is a good default. PDF\u002FA-3b adds embedded files such as the source workbook but some archives refuse it.",[819,820],"rect",{"x":821,"y":682,"width":822,"height":823,"fill":824},"4","752","174","#ffffff",[819,826],{"x":827,"y":828,"width":829,"height":830,"rx":682,"fill":824,"stroke":831,"style":832},"20.0","16.0","113.0","40.0","var(--line,#cdd5e6)","stroke-width:1px",[819,834],{"x":835,"y":828,"width":836,"height":830,"rx":682,"fill":837,"stroke":838,"style":839},"133.0","202.3","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[841,842,846],"text",{"x":843,"y":844,"style":845},"234.1","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","based on",[819,848],{"x":849,"y":828,"width":836,"height":830,"rx":682,"fill":837,"stroke":838,"style":839},"335.3",[841,851,853],{"x":852,"y":844,"style":845},"436.5","allows",[819,855],{"x":856,"y":828,"width":836,"height":830,"rx":682,"fill":837,"stroke":838,"style":839},"537.7",[841,858,860],{"x":859,"y":844,"style":845},"638.8","use when",[819,862],{"x":827,"y":863,"width":829,"height":864,"rx":682,"fill":865,"stroke":831,"style":832},"56.0","34.0","#e7ebef",[841,867,871],{"x":868,"y":869,"style":870},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","PDF\u002FA-1b (1)",[819,873],{"x":835,"y":863,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,875,878],{"x":843,"y":876,"style":877},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","PDF 1.4",[819,880],{"x":849,"y":863,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,882,883],{"x":852,"y":876,"style":877},"no transparency",[819,885],{"x":856,"y":863,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,887,888],{"x":859,"y":876,"style":877},"archive demands it",[819,890],{"x":827,"y":891,"width":829,"height":864,"rx":682,"fill":865,"stroke":831,"style":832},"90.0",[841,893,895],{"x":868,"y":894,"style":870},"112.0","PDF\u002FA-2b (2)",[819,897],{"x":835,"y":891,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,899,901],{"x":843,"y":900,"style":877},"111.0","PDF 1.7",[819,903],{"x":849,"y":891,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,905,906],{"x":852,"y":900,"style":877},"transparency",[819,908],{"x":856,"y":891,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,910,911],{"x":859,"y":900,"style":877},"default choice",[819,913],{"x":827,"y":914,"width":829,"height":864,"rx":682,"fill":865,"stroke":831,"style":832},"124.0",[841,916,918],{"x":868,"y":917,"style":870},"146.0","PDF\u002FA-3b (3)",[819,920],{"x":835,"y":914,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,922,901],{"x":843,"y":923,"style":877},"145.0",[819,925],{"x":849,"y":914,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,927,928],{"x":852,"y":923,"style":877},"embedded files",[819,930],{"x":856,"y":914,"width":836,"height":864,"rx":682,"fill":824,"stroke":831,"style":832},[841,932,933],{"x":859,"y":923,"style":877},"source must travel",[20,935,937],{"id":936},"step-3-check-the-markers-a-pdfa-file-must-carry","Step 3 — Check the markers a PDF\u002FA file must carry",[10,939,940],{},"A conforming file identifies itself in its XMP metadata, declares an output intent with an embedded colour profile, embeds every font, and is not encrypted. pypdf can read all four:",[25,942,944],{"className":72,"code":943,"language":74,"meta":30,"style":30},"import re\nfrom pypdf import PdfReader\n\ndef pdfa_report(path):\n    reader = PdfReader(path)\n    encrypted = reader.is_encrypted\n    if encrypted:\n        reader.decrypt(\"\")                      # an empty user password still opens it for reading\n    root = reader.trailer[\"\u002FRoot\"]\n    meta = root.get(\"\u002FMetadata\")\n    xmp = meta.get_object().get_data().decode(\"utf-8\", \"replace\") if meta else \"\"\n    part = re.search(r\"\u003Cpdfaid:part>(\\d)\u003C\u002Fpdfaid:part>\", xmp)\n    conf = re.search(r\"\u003Cpdfaid:conformance>(\\w)\u003C\u002Fpdfaid:conformance>\", xmp)\n    fonts = {}\n    for page in reader.pages:\n        for font in page[\"\u002FResources\"].get(\"\u002FFont\", {}).values():\n            font = font.get_object()\n            desc = font.get(\"\u002FFontDescriptor\") or font[\"\u002FDescendantFonts\"][0].get_object()[\"\u002FFontDescriptor\"]\n            fonts[font[\"\u002FBaseFont\"]] = any(k.startswith(\"\u002FFontFile\") for k in desc.get_object())\n    return {\"pdfa\": f\"{part.group(1)}{conf.group(1).lower()}\" if part and conf else None,\n            \"output intent\": \"\u002FOutputIntents\" in root, \"fonts embedded\": all(fonts.values()) if fonts else None,\n            \"encrypted\": encrypted, \"title\": reader.metadata.title, \"author\": reader.metadata.author}\n\nfor path in [archived, \"plain\u002Ftrial_balance.pdf\"]:\n    print(path, pdfa_report(path))\n",[32,945,946,953,965,969,979,989,999,1007,1021,1037,1052,1085,1110,1132,1142,1155,1180,1190,1226,1260,1322,1361,1382,1387,1405],{"__ignoreMap":30},[35,947,948,950],{"class":37,"line":38},[35,949,89],{"class":81},[35,951,952],{"class":85}," re\n",[35,954,955,957,960,962],{"class":37,"line":55},[35,956,82],{"class":81},[35,958,959],{"class":85}," pypdf ",[35,961,89],{"class":81},[35,963,964],{"class":85}," PdfReader\n",[35,966,967],{"class":37,"line":107},[35,968,111],{"emptyLinePlaceholder":110},[35,970,971,973,976],{"class":37,"line":114},[35,972,501],{"class":81},[35,974,975],{"class":504}," pdfa_report",[35,977,978],{"class":85},"(path):\n",[35,980,981,984,986],{"class":37,"line":126},[35,982,983],{"class":85},"    reader ",[35,985,120],{"class":81},[35,987,988],{"class":85}," PdfReader(path)\n",[35,990,991,994,996],{"class":37,"line":137},[35,992,993],{"class":85},"    encrypted ",[35,995,120],{"class":81},[35,997,998],{"class":85}," reader.is_encrypted\n",[35,1000,1001,1004],{"class":37,"line":148},[35,1002,1003],{"class":81},"    if",[35,1005,1006],{"class":85}," encrypted:\n",[35,1008,1009,1012,1015,1018],{"class":37,"line":176},[35,1010,1011],{"class":85},"        reader.decrypt(",[35,1013,1014],{"class":45},"\"\"",[35,1016,1017],{"class":85},")                      ",[35,1019,1020],{"class":58},"# an empty user password still opens it for reading\n",[35,1022,1023,1026,1028,1031,1034],{"class":37,"line":198},[35,1024,1025],{"class":85},"    root ",[35,1027,120],{"class":81},[35,1029,1030],{"class":85}," reader.trailer[",[35,1032,1033],{"class":45},"\"\u002FRoot\"",[35,1035,1036],{"class":85},"]\n",[35,1038,1039,1042,1044,1047,1050],{"class":37,"line":221},[35,1040,1041],{"class":85},"    meta ",[35,1043,120],{"class":81},[35,1045,1046],{"class":85}," root.get(",[35,1048,1049],{"class":45},"\"\u002FMetadata\"",[35,1051,218],{"class":85},[35,1053,1054,1057,1059,1062,1065,1067,1070,1073,1076,1079,1082],{"class":37,"line":275},[35,1055,1056],{"class":85},"    xmp ",[35,1058,120],{"class":81},[35,1060,1061],{"class":85}," meta.get_object().get_data().decode(",[35,1063,1064],{"class":45},"\"utf-8\"",[35,1066,157],{"class":85},[35,1068,1069],{"class":45},"\"replace\"",[35,1071,1072],{"class":85},") ",[35,1074,1075],{"class":81},"if",[35,1077,1078],{"class":85}," meta ",[35,1080,1081],{"class":81},"else",[35,1083,1084],{"class":45}," \"\"\n",[35,1086,1087,1090,1092,1095,1098,1101,1104,1107],{"class":37,"line":319},[35,1088,1089],{"class":85},"    part ",[35,1091,120],{"class":81},[35,1093,1094],{"class":85}," re.search(",[35,1096,1097],{"class":81},"r",[35,1099,1100],{"class":45},"\"\u003Cpdfaid:part>",[35,1102,1103],{"class":191},"(\\d)",[35,1105,1106],{"class":45},"\u003C\u002Fpdfaid:part>\"",[35,1108,1109],{"class":85},", xmp)\n",[35,1111,1112,1115,1117,1119,1121,1124,1127,1130],{"class":37,"line":344},[35,1113,1114],{"class":85},"    conf ",[35,1116,120],{"class":81},[35,1118,1094],{"class":85},[35,1120,1097],{"class":81},[35,1122,1123],{"class":45},"\"\u003Cpdfaid:conformance>",[35,1125,1126],{"class":191},"(\\w)",[35,1128,1129],{"class":45},"\u003C\u002Fpdfaid:conformance>\"",[35,1131,1109],{"class":85},[35,1133,1134,1137,1139],{"class":37,"line":350},[35,1135,1136],{"class":85},"    fonts ",[35,1138,120],{"class":81},[35,1140,1141],{"class":85}," {}\n",[35,1143,1144,1147,1150,1152],{"class":37,"line":392},[35,1145,1146],{"class":81},"    for",[35,1148,1149],{"class":85}," page ",[35,1151,185],{"class":81},[35,1153,1154],{"class":85}," reader.pages:\n",[35,1156,1157,1160,1163,1165,1168,1171,1174,1177],{"class":37,"line":403},[35,1158,1159],{"class":81},"        for",[35,1161,1162],{"class":85}," font ",[35,1164,185],{"class":81},[35,1166,1167],{"class":85}," page[",[35,1169,1170],{"class":45},"\"\u002FResources\"",[35,1172,1173],{"class":85},"].get(",[35,1175,1176],{"class":45},"\"\u002FFont\"",[35,1178,1179],{"class":85},", {}).values():\n",[35,1181,1182,1185,1187],{"class":37,"line":414},[35,1183,1184],{"class":85},"            font ",[35,1186,120],{"class":81},[35,1188,1189],{"class":85}," font.get_object()\n",[35,1191,1192,1195,1197,1200,1203,1205,1208,1211,1214,1217,1219,1222,1224],{"class":37,"line":425},[35,1193,1194],{"class":85},"            desc ",[35,1196,120],{"class":81},[35,1198,1199],{"class":85}," font.get(",[35,1201,1202],{"class":45},"\"\u002FFontDescriptor\"",[35,1204,1072],{"class":85},[35,1206,1207],{"class":81},"or",[35,1209,1210],{"class":85}," font[",[35,1212,1213],{"class":45},"\"\u002FDescendantFonts\"",[35,1215,1216],{"class":85},"][",[35,1218,682],{"class":191},[35,1220,1221],{"class":85},"].get_object()[",[35,1223,1202],{"class":45},[35,1225,1036],{"class":85},[35,1227,1228,1231,1234,1237,1239,1242,1245,1248,1250,1252,1255,1257],{"class":37,"line":436},[35,1229,1230],{"class":85},"            fonts[font[",[35,1232,1233],{"class":45},"\"\u002FBaseFont\"",[35,1235,1236],{"class":85},"]] ",[35,1238,120],{"class":81},[35,1240,1241],{"class":191}," any",[35,1243,1244],{"class":85},"(k.startswith(",[35,1246,1247],{"class":45},"\"\u002FFontFile\"",[35,1249,1072],{"class":85},[35,1251,179],{"class":81},[35,1253,1254],{"class":85}," k ",[35,1256,185],{"class":81},[35,1258,1259],{"class":85}," desc.get_object())\n",[35,1261,1263,1265,1267,1270,1272,1275,1277,1279,1282,1284,1287,1290,1293,1295,1298,1300,1302,1305,1308,1311,1314,1316,1319],{"class":37,"line":1262},20,[35,1264,649],{"class":81},[35,1266,533],{"class":85},[35,1268,1269],{"class":45},"\"pdfa\"",[35,1271,545],{"class":85},[35,1273,1274],{"class":81},"f",[35,1276,654],{"class":45},[35,1278,578],{"class":577},[35,1280,1281],{"class":85},"part.group(",[35,1283,192],{"class":191},[35,1285,1286],{"class":85},")",[35,1288,1289],{"class":577},"}{",[35,1291,1292],{"class":85},"conf.group(",[35,1294,192],{"class":191},[35,1296,1297],{"class":85},").lower()",[35,1299,584],{"class":577},[35,1301,654],{"class":45},[35,1303,1304],{"class":81}," if",[35,1306,1307],{"class":85}," part ",[35,1309,1310],{"class":81},"and",[35,1312,1313],{"class":85}," conf ",[35,1315,1081],{"class":81},[35,1317,1318],{"class":191}," None",[35,1320,1321],{"class":85},",\n",[35,1323,1325,1328,1330,1333,1336,1339,1342,1344,1347,1350,1352,1355,1357,1359],{"class":37,"line":1324},21,[35,1326,1327],{"class":45},"            \"output intent\"",[35,1329,545],{"class":85},[35,1331,1332],{"class":45},"\"\u002FOutputIntents\"",[35,1334,1335],{"class":81}," in",[35,1337,1338],{"class":85}," root, ",[35,1340,1341],{"class":45},"\"fonts embedded\"",[35,1343,545],{"class":85},[35,1345,1346],{"class":191},"all",[35,1348,1349],{"class":85},"(fonts.values()) ",[35,1351,1075],{"class":81},[35,1353,1354],{"class":85}," fonts ",[35,1356,1081],{"class":81},[35,1358,1318],{"class":191},[35,1360,1321],{"class":85},[35,1362,1364,1367,1370,1373,1376,1379],{"class":37,"line":1363},22,[35,1365,1366],{"class":45},"            \"encrypted\"",[35,1368,1369],{"class":85},": encrypted, ",[35,1371,1372],{"class":45},"\"title\"",[35,1374,1375],{"class":85},": reader.metadata.title, ",[35,1377,1378],{"class":45},"\"author\"",[35,1380,1381],{"class":85},": reader.metadata.author}\n",[35,1383,1385],{"class":37,"line":1384},23,[35,1386,111],{"emptyLinePlaceholder":110},[35,1388,1390,1392,1395,1397,1400,1403],{"class":37,"line":1389},24,[35,1391,179],{"class":81},[35,1393,1394],{"class":85}," path ",[35,1396,185],{"class":81},[35,1398,1399],{"class":85}," [archived, ",[35,1401,1402],{"class":45},"\"plain\u002Ftrial_balance.pdf\"",[35,1404,195],{"class":85},[35,1406,1408,1411],{"class":37,"line":1407},25,[35,1409,1410],{"class":191},"    print",[35,1412,1413],{"class":85},"(path, pdfa_report(path))\n",[10,1415,1416,1417,1420],{},"The PDF\u002FA export identifies itself as part 2, conformance B, has an output intent and embedded fonts, is not encrypted, and carries the title and author from the workbook. The plain export embeds the fonts too — LibreOffice always subsets and embeds them — but has no PDF\u002FA identification and no output intent, so an archive's validator would reject it. The ",[32,1418,1419],{},"decrypt(\"\")"," line lets the same function inspect files that are encrypted with only an owner password, which opens them for reading but still counts as encryption.",[10,1422,1423],{},"A check like this belongs in the job itself, after every export: it costs milliseconds, and it turns \"we assume the archive accepted it\" into a logged fact.",[800,1425,809,1430,809,1433,809,1436,809,1439,809,1446,809,1457,809,1462,809,1467,809,1471,809,1475,809,1479,809,1483,809,1488,809,1495,809,1500,809,1504,809,1506,809,1508,809,1511],{"viewBox":1426,"role":803,"ariaLabelledBy":1427,"xmlns":807,"style":808},"4 0 752 233",[1428,1429],"pa-vs-t","pa-vs-d",[811,1431,1432],{"id":1428},"A plain export compared with a PDF\u002FA export",[815,1434,1435],{"id":1429},"Both exports embed their fonts and carry the title and author. Only the PDF\u002FA export declares its PDF\u002FA part and conformance in XMP metadata and includes an output intent with an embedded colour profile, which archive validators require.",[819,1437],{"x":821,"y":682,"width":822,"height":1438,"fill":824},"233",[819,1440],{"x":827,"y":828,"width":1441,"height":1442,"rx":382,"fill":1443,"stroke":1444,"style":1445},"351.0","201.0","#fdefd8","var(--gold,#b4740a)","stroke-width:2px",[1447,1448,1450],"g",{"transform":1449},"translate(182.5,29.0) scale(1.083)",[1451,1452],"path",{"d":1453,"fill":1454,"stroke":1455,"style":1456},"M6 3h9l4 4v14H6z M15 3v4h4 M9 12h6 M9 15.5h6 M9 19h4","none","var(--gold-ink,#7a4e06)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[841,1458,1461],{"x":1459,"y":869,"style":1460},"195.5","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Plain PDF export",[37,1463],{"x1":1464,"y1":1465,"x2":1466,"y2":1465,"stroke":1444,"style":832},"36.0","91.0","355.0",[841,1468,1470],{"x":1459,"y":900,"style":1469},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","fonts embedded",[841,1472,1474],{"x":1459,"y":1473,"style":1469},"137.0","title and author",[841,1476,1478],{"x":1459,"y":1477,"style":1469},"163.0","no pdfaid identification",[841,1480,1482],{"x":1459,"y":1481,"style":1469},"189.0","no output intent",[819,1484],{"x":1485,"y":828,"width":1441,"height":1442,"rx":382,"fill":1486,"stroke":1487,"style":1445},"389.0","#d9f4f1","var(--teal,#0f9488)",[1447,1489,1491],{"transform":1490},"translate(551.5,29.0) scale(1.083)",[1451,1492],{"d":1493,"fill":1454,"stroke":1494,"style":1456},"M6 11h12v9H6z M8.5 11V8a3.5 3.5 0 0 1 7 0v3","var(--teal-ink,#0b6157)",[841,1496,1499],{"x":1497,"y":869,"style":1498},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","PDF\u002FA-2b export",[37,1501],{"x1":1502,"y1":1465,"x2":1503,"y2":1465,"stroke":1487,"style":832},"405.0","724.0",[841,1505,1470],{"x":1497,"y":900,"style":1469},[841,1507,1474],{"x":1497,"y":1473,"style":1469},[841,1509,1510],{"x":1497,"y":1477,"style":1469},"pdfaid part 2, conformance B",[841,1512,1513],{"x":1497,"y":1481,"style":1469},"sRGB output intent",[10,1515,1516],{},"Two content issues are worth knowing about even though they do not show up in these checks. Images in the workbook — a logo in the header, a chart — are converted along with the cells; transparency in them is fine for PDF\u002FA-2 and later but has to be flattened for PDF\u002FA-1, which LibreOffice does automatically at some cost in quality, another reason to prefer part 2. And hyperlinks to web pages remain clickable but the content they point to is, by definition, not archived: if a figure in the report depends on a linked document, put that document in the archive too rather than relying on the link.",[20,1518,1520],{"id":1519},"step-4-do-not-break-it-afterwards","Step 4 — Do not break it afterwards",[10,1522,1523],{},"Conformance is a property of the exact bytes. Many ordinary post-processing steps produce a new file that is no longer PDF\u002FA, without any error:",[25,1525,1527],{"className":72,"code":1526,"language":74,"meta":30,"style":30},"from pypdf import PdfWriter\n\nwriter = PdfWriter()\nwriter.append(archived)\nwriter.append(archived)\nwriter.write(\"merged.pdf\")\nprint(\"merged:\", pdfa_report(\"merged.pdf\"))\n\nwriter = PdfWriter(clone_from=archived)\nwriter.encrypt(user_password=\"\", owner_password=\"owner-secret\")\nwriter.write(\"encrypted.pdf\")\nprint(\"encrypted:\", pdfa_report(\"encrypted.pdf\"))\n",[32,1528,1529,1540,1544,1554,1559,1563,1573,1590,1594,1611,1635,1644],{"__ignoreMap":30},[35,1530,1531,1533,1535,1537],{"class":37,"line":38},[35,1532,82],{"class":81},[35,1534,959],{"class":85},[35,1536,89],{"class":81},[35,1538,1539],{"class":85}," PdfWriter\n",[35,1541,1542],{"class":37,"line":55},[35,1543,111],{"emptyLinePlaceholder":110},[35,1545,1546,1549,1551],{"class":37,"line":107},[35,1547,1548],{"class":85},"writer ",[35,1550,120],{"class":81},[35,1552,1553],{"class":85}," PdfWriter()\n",[35,1555,1556],{"class":37,"line":114},[35,1557,1558],{"class":85},"writer.append(archived)\n",[35,1560,1561],{"class":37,"line":126},[35,1562,1558],{"class":85},[35,1564,1565,1568,1571],{"class":37,"line":137},[35,1566,1567],{"class":85},"writer.write(",[35,1569,1570],{"class":45},"\"merged.pdf\"",[35,1572,218],{"class":85},[35,1574,1575,1577,1579,1582,1585,1587],{"class":37,"line":148},[35,1576,774],{"class":191},[35,1578,363],{"class":85},[35,1580,1581],{"class":45},"\"merged:\"",[35,1583,1584],{"class":85},", pdfa_report(",[35,1586,1570],{"class":45},[35,1588,1589],{"class":85},"))\n",[35,1591,1592],{"class":37,"line":176},[35,1593,111],{"emptyLinePlaceholder":110},[35,1595,1596,1598,1600,1603,1606,1608],{"class":37,"line":198},[35,1597,1548],{"class":85},[35,1599,120],{"class":81},[35,1601,1602],{"class":85}," PdfWriter(",[35,1604,1605],{"class":209},"clone_from",[35,1607,120],{"class":81},[35,1609,1610],{"class":85},"archived)\n",[35,1612,1613,1616,1619,1621,1623,1625,1628,1630,1633],{"class":37,"line":221},[35,1614,1615],{"class":85},"writer.encrypt(",[35,1617,1618],{"class":209},"user_password",[35,1620,120],{"class":81},[35,1622,1014],{"class":45},[35,1624,157],{"class":85},[35,1626,1627],{"class":209},"owner_password",[35,1629,120],{"class":81},[35,1631,1632],{"class":45},"\"owner-secret\"",[35,1634,218],{"class":85},[35,1636,1637,1639,1642],{"class":37,"line":275},[35,1638,1567],{"class":85},[35,1640,1641],{"class":45},"\"encrypted.pdf\"",[35,1643,218],{"class":85},[35,1645,1646,1648,1650,1653,1655,1657],{"class":37,"line":319},[35,1647,774],{"class":191},[35,1649,363],{"class":85},[35,1651,1652],{"class":45},"\"encrypted:\"",[35,1654,1584],{"class":85},[35,1656,1641],{"class":45},[35,1658,1589],{"class":85},[10,1660,1661,1662,1665,1666,1670],{},"Merging two PDF\u002FA files with pypdf produces a file with no PDF\u002FA identification, no output intent and not even the title and author — all of them live in the document catalogue and information dictionary, which a merge rebuilds from scratch. Cloning the whole document keeps them, but encrypting it, even with an empty user password just to restrict printing, violates the standard regardless of what the metadata still claims; the check above flags it. The same applies to stamping, page numbering and watermarking with general PDF tools. The rule for archive copies is simple: make every change in the ",[787,1663,1664],{},"workbook"," — headers, footers, page numbers, a cover sheet — and convert last. If several reports must become one archive file, combine the sheets into one workbook and export that, rather than merging PDFs; ",[14,1667,1669],{"href":1668},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fmerge-excel-pdf-exports-into-one-file\u002F","Merge Several Excel PDF Exports into One File"," is the right approach for distribution copies, not archive copies.",[800,1672,809,1677,809,1680,809,1683,809,1686,809,1691,809,1699,809,1705,809,1708,809,1712,809,1716,809,1721,809,1726,809,1729,809,1735,809,1740,809,1743,809,1745,809,1747,809,1751,809,1754,809,1757,809,1763,809,1768,809,1771,809,1774,809,1777,809,1781,809,1784,809,1787,809,1793,809,1799,809,1803,809,1807,809,1811,809,1814,809,1817,809,1822,809,1826,809,1829,809,1832],{"viewBox":1673,"role":803,"ariaLabelledBy":1674,"xmlns":807,"style":808},"4 0 752 160",[1675,1676],"pa-flow-t","pa-flow-d",[811,1678,1679],{"id":1675},"An archive-safe export pipeline",[815,1681,1682],{"id":1676},"Set the document properties and every visible change in the workbook, export once with the PDF\u002FA option, check the identification, output intent, fonts and encryption, validate samples with veraPDF, and store the file without further modification.",[819,1684],{"x":821,"y":682,"width":822,"height":1685,"fill":824},"160",[819,1687],{"x":827,"y":828,"width":1688,"height":1689,"rx":1690,"fill":865,"stroke":831,"style":1445},"115.2","128.0","12",[1447,1692,1694],{"transform":1693},"translate(65.6,29.0) scale(1.000)",[1451,1695],{"d":1696,"fill":1454,"stroke":1697,"style":1698},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[841,1700,1704],{"x":1701,"y":1702,"style":1703},"77.6","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Finish the",[841,1706,1664],{"x":1701,"y":1707,"style":1703},"93.0",[841,1709,1711],{"x":1701,"y":1710,"style":877},"112.5","properties,",[841,1713,1715],{"x":1701,"y":1714,"style":877},"130.0","headers, cover",[37,1717],{"x1":1718,"y1":1719,"x2":1720,"y2":1719,"stroke":838,"style":1445},"140.2","80.0","162.2",[1722,1723],"polygon",{"points":1724,"fill":1725},"166.2,80.0 157.2,85.0 157.2,75.0","#5b5cf0",[819,1727],{"x":1728,"y":828,"width":1688,"height":1689,"rx":1690,"fill":837,"stroke":838,"style":1445},"171.2",[1447,1730,1732],{"transform":1731},"translate(216.8,29.0) scale(1.000)",[1451,1733],{"d":1453,"fill":1454,"stroke":1734,"style":1698},"var(--brand-strong,#4338ca)",[841,1736,1739],{"x":1737,"y":1702,"style":1738},"228.8","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Export as",[841,1741,1742],{"x":1737,"y":1707,"style":1738},"PDF\u002FA",[841,1744,461],{"x":1737,"y":1710,"style":877},[841,1746,468],{"x":1737,"y":1714,"style":877},[37,1748],{"x1":1749,"y1":1719,"x2":1750,"y2":1719,"stroke":838,"style":1445},"291.4","313.4",[1722,1752],{"points":1753,"fill":1725},"317.4,80.0 308.4,85.0 308.4,75.0",[819,1755],{"x":1756,"y":828,"width":1688,"height":1689,"rx":1690,"fill":1486,"stroke":1487,"style":1445},"322.4",[1447,1758,1760],{"transform":1759},"translate(368.0,29.0) scale(1.000)",[1451,1761],{"d":1762,"fill":1454,"stroke":1494,"style":1698},"M5 12.5l4.5 4.5L19 7",[841,1764,1767],{"x":1765,"y":1702,"style":1766},"380.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Check",[841,1769,1770],{"x":1765,"y":1707,"style":1766},"markers",[841,1772,1773],{"x":1765,"y":1710,"style":877},"pdfaid, intent,",[841,1775,1776],{"x":1765,"y":1714,"style":877},"fonts",[37,1778],{"x1":1779,"y1":1719,"x2":1780,"y2":1719,"stroke":838,"style":1445},"442.6","464.6",[1722,1782],{"points":1783,"fill":1725},"468.6,80.0 459.6,85.0 459.6,75.0",[819,1785],{"x":1786,"y":828,"width":1688,"height":1689,"rx":1690,"fill":1443,"stroke":1444,"style":1445},"473.6",[1447,1788,1790],{"transform":1789},"translate(519.2,37.8) scale(1.000)",[1451,1791],{"d":1792,"fill":1454,"stroke":1455,"style":1698},"M9 3h6 M10 3v6l-5 9a2 2 0 0 0 1.8 3h10.4a2 2 0 0 0 1.8-3l-5-9V3",[841,1794,1798],{"x":1795,"y":1796,"style":1797},"531.2","84.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Validate",[841,1800,1802],{"x":1795,"y":1801,"style":1797},"101.8","samples",[841,1804,1806],{"x":1795,"y":1805,"style":877},"121.2","veraPDF",[37,1808],{"x1":1809,"y1":1719,"x2":1810,"y2":1719,"stroke":838,"style":1445},"593.8","615.8",[1722,1812],{"points":1813,"fill":1725},"619.8,80.0 610.8,85.0 610.8,75.0",[819,1815],{"x":1816,"y":828,"width":1688,"height":1689,"rx":1690,"fill":865,"stroke":831,"style":1445},"624.8",[1447,1818,1820],{"transform":1819},"translate(670.4,29.0) scale(1.000)",[1451,1821],{"d":1493,"fill":1454,"stroke":1697,"style":1698},[841,1823,1825],{"x":1824,"y":1702,"style":1703},"682.4","Store",[841,1827,1828],{"x":1824,"y":1707,"style":1703},"unchanged",[841,1830,1831],{"x":1824,"y":1710,"style":877},"no merging or",[841,1833,1834],{"x":1824,"y":1714,"style":877},"stamping",[20,1836,1838],{"id":1837},"common-pitfalls","Common pitfalls",[1840,1841,1842,1850,1858,1864,1870],"ul",{},[1843,1844,1845,1849],"li",{},[1846,1847,1848],"strong",{},"Assuming any PDF is archival."," Without the PDF\u002FA option there is no output intent or identification.",[1843,1851,1852,1855,1856,18],{},[1846,1853,1854],{},"Default metadata."," openpyxl writes \"openpyxl\" as the author unless you set ",[32,1857,450],{},[1843,1859,1860,1863],{},[1846,1861,1862],{},"Post-processing the archive copy."," Merging, stamping or encrypting can break conformance.",[1843,1865,1866,1869],{},[1846,1867,1868],{},"Password protection."," Encryption is forbidden in every PDF\u002FA part.",[1843,1871,1872,1875],{},[1846,1873,1874],{},"Trusting the marker alone."," The identification is a claim; validate samples with veraPDF.",[20,1877,1879],{"id":1878},"conclusion","Conclusion",[10,1881,1882,1883,1885,1886,1888,1889,1891],{},"To archive Excel reports as PDF\u002FA, set the workbook's title and author, then export with LibreOffice using ",[32,1884,461],{}," — ",[32,1887,468],{}," for PDF\u002FA-2b is a good default, ",[32,1890,192],{}," when the archive demands PDF\u002FA-1b. Check each export for the PDF\u002FA identification, an output intent, embedded fonts and no encryption, validate samples with veraPDF, and make every change in the workbook before converting, because merging, stamping or encrypting the PDF afterwards breaks conformance.",[20,1893,1895],{"id":1894},"frequently-asked-questions","Frequently asked questions",[10,1897,1898,1901],{},[1846,1899,1900],{},"How do I convert an Excel file to PDF\u002FA in Python?","\nRun LibreOffice headless with a convert target such as pdf:calc_pdf_Export with the SelectPdfVersion option set to 1, 2 or 3 for PDF\u002FA-1b, 2b or 3b.",[10,1903,1904,1907],{},[1846,1905,1906],{},"Which PDF\u002FA version should I use for reports?","\nPDF\u002FA-2b is a good default; use PDF\u002FA-1b if the archive requires it, and PDF\u002FA-3b only when the source file must be embedded and the archive accepts it.",[10,1909,1910,1913],{},[1846,1911,1912],{},"How can I check whether a PDF is PDF\u002FA?","\nLook for pdfaid part and conformance in the XMP metadata and an OutputIntents entry in the catalogue, check the fonts are embedded and the file is not encrypted, and validate with veraPDF.",[10,1915,1916,1919],{},[1846,1917,1918],{},"Can I password-protect a PDF\u002FA file?","\nNo. Encryption is not allowed in any PDF\u002FA part; control access through where the file is stored instead.",[20,1921,1923],{"id":1922},"related","Related",[1840,1925,1926,1932,1939,1946],{},[1843,1927,1928,1929,1931],{},"Up one level: ",[14,1930,17],{"href":16}," — PDFs from Excel in Python.",[1843,1933,1934,1938],{},[14,1935,1937],{"href":1936},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-file-to-pdf-with-python\u002F","Convert an Excel File to PDF with Python"," — the conversion options.",[1843,1940,1941,1945],{},[14,1942,1944],{"href":1943},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fadd-headers-footers-and-page-numbers-to-an-excel-pdf\u002F","Add Headers, Footers and Page Numbers to an Excel PDF"," — changes to make before converting.",[1843,1947,1948,1952],{},[14,1949,1951],{"href":1950},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel\u002F","Password-Protect a PDF Exported from Excel"," — for distribution copies only.",[1954,1955,1956],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":30,"searchDepth":55,"depth":55,"links":1958},[1959,1960,1961,1962,1963,1964,1965,1966,1967],{"id":22,"depth":55,"text":23},{"id":65,"depth":55,"text":66},{"id":454,"depth":55,"text":455},{"id":936,"depth":55,"text":937},{"id":1519,"depth":55,"text":1520},{"id":1837,"depth":55,"text":1838},{"id":1878,"depth":55,"text":1879},{"id":1894,"depth":55,"text":1895},{"id":1922,"depth":55,"text":1923},"2026-10-02","Export Excel reports to PDF\u002FA from Python with LibreOffice for long-term archiving: choose between PDF\u002FA-1b, 2b and 3b, pass the export option on the command line, set the document title and author, check the result for the PDF\u002FA identification, output intent and embedded fonts, and avoid the post-processing steps that silently break conformance.","md",[1972,1974,1976,1978],{"q":1900,"a":1973},"Run LibreOffice headless with a convert target such as pdf:calc_pdf_Export with the SelectPdfVersion option set to 1, 2 or 3 for PDF\u002FA-1b, 2b or 3b.",{"q":1906,"a":1975},"PDF\u002FA-2b is a good default; use PDF\u002FA-1b if the archive requires it, and PDF\u002FA-3b only when the source file must be embedded and the archive accepts it.",{"q":1912,"a":1977},"Look for pdfaid part and conformance in the XMP metadata and an OutputIntents entry in the catalogue, check the fonts are embedded and the file is not encrypted, and validate with veraPDF.",{"q":1918,"a":1979},"No. Encryption is not allowed in any PDF\u002FA part; control access through where the file is stored instead.","export excel report to pdf\u002Fa for archiving",null,{"breadcrumb":1983},[1984,1986,1989],{"name":1985,"item":664},"Home",{"name":1987,"item":1988},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":17,"item":16},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving",{"title":1992,"description":1993},"Export an Excel Report to PDF\u002FA for Archiving with Python","Excel to PDF\u002FA with LibreOffice from Python: SelectPdfVersion 1, 2 or 3, title metadata, checking pdfaid, OutputIntents and embedded fonts with pypdf, and why merging or encrypting breaks PDF\u002FA.","export-an-excel-report-to-pdf-a-for-archiving","automating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fexport-an-excel-report-to-pdf-a-for-archiving\u002Findex",[1997],"pdf-export","how-to","OtD1VWU4olKQj5OcjGCwF3J8dUFUpZ3e0fZJ86N8Wc8",[2001,2005],{"title":2002,"path":2003,"stem":2004,"children":-1},"Create a PDF Report from Excel Data with ReportLab","\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fcreate-a-pdf-report-from-excel-data-with-reportlab","automating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fcreate-a-pdf-report-from-excel-data-with-reportlab\u002Findex",{"title":2006,"path":2007,"stem":2008,"children":-1},"Fit an Excel Sheet to One Page Wide When Exporting to PDF","\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Ffit-an-excel-sheet-to-one-page-in-pdf","automating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Ffit-an-excel-sheet-to-one-page-in-pdf\u002Findex",[2010,2012],{"path":2011,"title":17},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf",{"path":2013,"title":1987},"\u002Fautomating-reporting-workflows",[],[2016,2019,2022,2025,2027,2030],{"path":2017,"title":2018,"kind":1998},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Ffill-pdf-forms-from-excel-rows-with-python","Fill PDF Forms from Excel Rows with Python",{"path":2020,"title":2021,"kind":1998},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-one-pdf-per-excel-row","Generate One PDF per Excel Row",{"path":2023,"title":2024,"kind":1998},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fadd-bookmarks-and-a-cover-page-to-an-excel-pdf-export","Add Bookmarks and a Cover Page to an Excel PDF Export",{"path":2026,"title":1944,"kind":1998},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fadd-headers-footers-and-page-numbers-to-an-excel-pdf",{"path":2028,"title":2029,"kind":1998},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fcheck-a-pdf-export-for-cut-off-columns","Check a PDF Export for Cut-Off Columns",{"path":2031,"title":1937,"kind":1998},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-file-to-pdf-with-python",1790972136792]