[{"data":1,"prerenderedAt":1912},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel":3,"surround:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel":1879,"ancestors:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel":1888,"children:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel":1893,"related:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel":1894},{"id":4,"title":5,"body":6,"dateModified":1845,"datePublished":1845,"description":1846,"extension":1847,"faq":1848,"keyword":1857,"kind":1858,"meta":1859,"navigation":285,"path":1868,"seo":1869,"slug":1872,"stem":1873,"topics":1874,"type":1877,"__hash__":1878},"docs\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel\u002Findex.md","Password-Protect a PDF Exported from Excel",{"type":7,"value":8,"toc":1832},"minimark",[9,29,34,68,85,89,92,118,121,223,227,230,528,537,541,544,800,806,863,867,874,1277,1294,1297,1301,1304,1558,1580,1701,1705,1715,1719,1753,1757,1767,1771,1777,1783,1789,1795,1799,1828],[10,11,12,13,17,18,23,24,28],"p",{},"Payslips, customer statements and board packs often leave the building as PDFs converted from Excel, and the people receiving them expect the file to be locked. Python can encrypt the PDF after conversion with ",[14,15,16],"code",{},"pypdf",", using AES-256, or ask LibreOffice to encrypt during the export. The two are not equal — LibreOffice's export uses an older 128-bit RC4 scheme — and both distinguish between a password that opens the document and one that guards its permissions. This page walks through both routes, generates a password per recipient, verifies that the result really requires a password, and is honest about what permission flags can stop. Converting the workbook in the first place is covered in ",[19,20,22],"a",{"href":21},"\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",". It belongs to ",[19,25,27],{"href":26},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002F","Exporting Excel Reports to PDF",".",[30,31,33],"h2",{"id":32},"prerequisites","Prerequisites",[35,36,41],"pre",{"className":37,"code":38,"language":39,"meta":40,"style":40},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pypdf cryptography pandas openpyxl\n","bash","",[14,42,43],{"__ignoreMap":40},[44,45,48,52,56,59,62,65],"span",{"class":46,"line":47},"line",1,[44,49,51],{"class":50},"sMTad","pip",[44,53,55],{"class":54},"srMev"," install",[44,57,58],{"class":54}," pypdf",[44,60,61],{"class":54}," cryptography",[44,63,64],{"class":54}," pandas",[44,66,67],{"class":54}," openpyxl\n",[10,69,70,72,73,76,77,80,81,84],{},[14,71,16],{}," reads and writes PDFs; AES encryption needs the ",[14,74,75],{},"cryptography"," package installed alongside it (pypdf raises an error asking for it otherwise). LibreOffice (",[14,78,79],{},"soffice",") converts ",[14,82,83],{},"report.xlsx",", a three-row revenue table, to PDF.",[30,86,88],{"id":87},"two-passwords-two-jobs","Two passwords, two jobs",[10,90,91],{},"A PDF can carry two passwords, and mixing them up is the most common mistake:",[93,94,95,109],"ul",{},[96,97,98,99,103,104,108],"li",{},"The ",[100,101,102],"strong",{},"user password"," (or ",[105,106,107],"em",{},"open password",") is needed to open the file at all. Without it, the content is encrypted and unreadable.",[96,110,98,111,103,114,117],{},[100,112,113],{},"owner password",[105,115,116],{},"permissions password",") unlocks the permission settings — printing, copying text, editing. A reader who opens the file with the user password gets the restricted permissions; one who supplies the owner password gets full control.",[10,119,120],{},"A file with only an owner password opens for anyone; it only carries permission flags. For confidential reports you need a user password.",[122,123,131,132,131,136,131,140,131,148,131,158,131,169,131,175,131,181,131,186,131,190,131,194,131,199,131,206,131,210,131,214,131,217,131,220],"svg",{"viewBox":124,"role":125,"ariaLabelledBy":126,"xmlns":129,"style":130},"4 0 752 207","img",[127,128],"pw-two-t","pw-two-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  ",[133,134,135],"title",{"id":127},"The open password and the permissions password",[137,138,139],"desc",{"id":128},"The user password is required to open and decrypt the file. The owner password unlocks the permission settings such as printing and copying. A file with only an owner password opens for anyone and relies on the reader software to respect the flags.",[141,142],"rect",{"x":143,"y":144,"width":145,"height":146,"fill":147},"4","0","752","207","#ffffff",[141,149],{"x":150,"y":151,"width":152,"height":153,"rx":154,"fill":155,"stroke":156,"style":157},"20.0","16.0","351.0","175.0","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[159,160,162],"g",{"transform":161},"translate(182.5,29.0) scale(1.083)",[163,164],"path",{"d":165,"fill":166,"stroke":167,"style":168},"M6 11h12v9H6z M8.5 11V8a3.5 3.5 0 0 1 7 0v3","none","var(--brand-strong,#4338ca)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[170,171,102],"text",{"x":172,"y":173,"style":174},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[46,176],{"x1":177,"y1":178,"x2":179,"y2":178,"stroke":156,"style":180},"36.0","91.0","355.0","stroke-width:1px",[170,182,185],{"x":172,"y":183,"style":184},"111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","needed to open the file",[170,187,189],{"x":172,"y":188,"style":184},"137.0","content encrypted without it",[170,191,193],{"x":172,"y":192,"style":184},"163.0","what keeps a report private",[141,195],{"x":196,"y":151,"width":152,"height":153,"rx":154,"fill":197,"stroke":198,"style":157},"389.0","#fdefd8","var(--gold,#b4740a)",[159,200,202],{"transform":201},"translate(551.5,29.0) scale(1.083)",[163,203],{"d":204,"fill":166,"stroke":205,"style":168},"M8 11a4 4 0 1 0 .01 0z M11 13l9-9 M17 7l2 2 M15 9l2 2","var(--gold-ink,#7a4e06)",[170,207,113],{"x":208,"y":173,"style":209},"564.5","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[46,211],{"x1":212,"y1":178,"x2":213,"y2":178,"stroke":198,"style":180},"405.0","724.0",[170,215,216],{"x":208,"y":183,"style":184},"unlocks permission flags",[170,218,219],{"x":208,"y":188,"style":184},"print, copy, edit settings",[170,221,222],{"x":208,"y":192,"style":184},"flags rely on the reader app",[30,224,226],{"id":225},"step-1-convert-then-encrypt-with-pypdf-aes-256","Step 1 — Convert, then encrypt with pypdf (AES-256)",[10,228,229],{},"The cleanest pipeline converts the workbook to a plain PDF, then encrypts it with pypdf, which supports AES-256 — the strongest scheme in the PDF standard:",[35,231,235],{"className":232,"code":233,"language":234,"meta":40,"style":40},"language-python shiki shiki-themes github-light github-dark-high-contrast","import subprocess\nfrom pypdf import PdfReader, PdfWriter\nfrom pypdf.constants import UserAccessPermissions as Perm\n\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"pdf\", \"--outdir\", \"plain\", \"report.xlsx\"],\n               check=True, capture_output=True)\n\nwriter = PdfWriter(clone_from=\"plain\u002Freport.pdf\")\nwriter.encrypt(\n    user_password=\"open-7Kp2\",\n    owner_password=\"owner-Qz94-long-random\",\n    algorithm=\"AES-256\",\n    permissions_flag=Perm.PRINT | Perm.PRINT_TO_REPRESENTATION,\n)\nwriter.write(\"report_locked.pdf\")\n\nenc = PdfReader(\"report_locked.pdf\").trailer[\"\u002FEncrypt\"].get_object()\nprint(\"version\", enc[\"\u002FV\"], \"revision\", enc[\"\u002FR\"])\n","python",[14,236,237,247,261,280,287,330,356,361,382,388,402,415,428,453,458,469,474,496],{"__ignoreMap":40},[44,238,239,243],{"class":46,"line":47},[44,240,242],{"class":241},"s-kum","import",[44,244,246],{"class":245},"skGVy"," subprocess\n",[44,248,250,253,256,258],{"class":46,"line":249},2,[44,251,252],{"class":241},"from",[44,254,255],{"class":245}," pypdf ",[44,257,242],{"class":241},[44,259,260],{"class":245}," PdfReader, PdfWriter\n",[44,262,264,266,269,271,274,277],{"class":46,"line":263},3,[44,265,252],{"class":241},[44,267,268],{"class":245}," pypdf.constants ",[44,270,242],{"class":241},[44,272,273],{"class":245}," UserAccessPermissions ",[44,275,276],{"class":241},"as",[44,278,279],{"class":245}," Perm\n",[44,281,283],{"class":46,"line":282},4,[44,284,286],{"emptyLinePlaceholder":285},true,"\n",[44,288,290,293,296,299,302,304,307,309,312,314,317,319,322,324,327],{"class":46,"line":289},5,[44,291,292],{"class":245},"subprocess.run([",[44,294,295],{"class":54},"\"soffice\"",[44,297,298],{"class":245},", ",[44,300,301],{"class":54},"\"--headless\"",[44,303,298],{"class":245},[44,305,306],{"class":54},"\"--convert-to\"",[44,308,298],{"class":245},[44,310,311],{"class":54},"\"pdf\"",[44,313,298],{"class":245},[44,315,316],{"class":54},"\"--outdir\"",[44,318,298],{"class":245},[44,320,321],{"class":54},"\"plain\"",[44,323,298],{"class":245},[44,325,326],{"class":54},"\"report.xlsx\"",[44,328,329],{"class":245},"],\n",[44,331,333,337,340,344,346,349,351,353],{"class":46,"line":332},6,[44,334,336],{"class":335},"sa561","               check",[44,338,339],{"class":241},"=",[44,341,343],{"class":342},"sP0c6","True",[44,345,298],{"class":245},[44,347,348],{"class":335},"capture_output",[44,350,339],{"class":241},[44,352,343],{"class":342},[44,354,355],{"class":245},")\n",[44,357,359],{"class":46,"line":358},7,[44,360,286],{"emptyLinePlaceholder":285},[44,362,364,367,369,372,375,377,380],{"class":46,"line":363},8,[44,365,366],{"class":245},"writer ",[44,368,339],{"class":241},[44,370,371],{"class":245}," PdfWriter(",[44,373,374],{"class":335},"clone_from",[44,376,339],{"class":241},[44,378,379],{"class":54},"\"plain\u002Freport.pdf\"",[44,381,355],{"class":245},[44,383,385],{"class":46,"line":384},9,[44,386,387],{"class":245},"writer.encrypt(\n",[44,389,391,394,396,399],{"class":46,"line":390},10,[44,392,393],{"class":335},"    user_password",[44,395,339],{"class":241},[44,397,398],{"class":54},"\"open-7Kp2\"",[44,400,401],{"class":245},",\n",[44,403,405,408,410,413],{"class":46,"line":404},11,[44,406,407],{"class":335},"    owner_password",[44,409,339],{"class":241},[44,411,412],{"class":54},"\"owner-Qz94-long-random\"",[44,414,401],{"class":245},[44,416,418,421,423,426],{"class":46,"line":417},12,[44,419,420],{"class":335},"    algorithm",[44,422,339],{"class":241},[44,424,425],{"class":54},"\"AES-256\"",[44,427,401],{"class":245},[44,429,431,434,436,439,442,445,448,451],{"class":46,"line":430},13,[44,432,433],{"class":335},"    permissions_flag",[44,435,339],{"class":241},[44,437,438],{"class":245},"Perm.",[44,440,441],{"class":342},"PRINT",[44,443,444],{"class":241}," |",[44,446,447],{"class":245}," Perm.",[44,449,450],{"class":342},"PRINT_TO_REPRESENTATION",[44,452,401],{"class":245},[44,454,456],{"class":46,"line":455},14,[44,457,355],{"class":245},[44,459,461,464,467],{"class":46,"line":460},15,[44,462,463],{"class":245},"writer.write(",[44,465,466],{"class":54},"\"report_locked.pdf\"",[44,468,355],{"class":245},[44,470,472],{"class":46,"line":471},16,[44,473,286],{"emptyLinePlaceholder":285},[44,475,477,480,482,485,487,490,493],{"class":46,"line":476},17,[44,478,479],{"class":245},"enc ",[44,481,339],{"class":241},[44,483,484],{"class":245}," PdfReader(",[44,486,466],{"class":54},[44,488,489],{"class":245},").trailer[",[44,491,492],{"class":54},"\"\u002FEncrypt\"",[44,494,495],{"class":245},"].get_object()\n",[44,497,499,502,505,508,511,514,517,520,522,525],{"class":46,"line":498},18,[44,500,501],{"class":342},"print",[44,503,504],{"class":245},"(",[44,506,507],{"class":54},"\"version\"",[44,509,510],{"class":245},", enc[",[44,512,513],{"class":54},"\"\u002FV\"",[44,515,516],{"class":245},"], ",[44,518,519],{"class":54},"\"revision\"",[44,521,510],{"class":245},[44,523,524],{"class":54},"\"\u002FR\"",[44,526,527],{"class":245},"])\n",[10,529,530,532,533,536],{},[14,531,374],{}," copies the whole document, including metadata and any bookmarks, into the writer. Version 5, revision 6 in the encryption dictionary is AES-256 as defined in PDF 2.0. ",[14,534,535],{},"permissions_flag"," lists what someone with only the user password may do — here, print at full quality but not copy text or edit. Always set a separate, long owner password: if you leave it out, pypdf uses the user password for both, and anyone who can open the file can lift the restrictions too.",[30,538,540],{"id":539},"step-2-or-encrypt-in-the-libreoffice-export","Step 2 — Or encrypt in the LibreOffice export",[10,542,543],{},"LibreOffice's PDF export filter can encrypt the file itself. Its options are passed as JSON after the filter name:",[35,545,547],{"className":232,"code":546,"language":234,"meta":40,"style":40},"import json\n\nopts = {\n    \"EncryptFile\": {\"type\": \"boolean\", \"value\": \"true\"},\n    \"DocumentOpenPassword\": {\"type\": \"string\", \"value\": \"open-7Kp2\"},\n    \"RestrictPermissions\": {\"type\": \"boolean\", \"value\": \"true\"},\n    \"PermissionPassword\": {\"type\": \"string\", \"value\": \"owner-Qz94-long-random\"},\n    \"EnableCopyingOfContent\": {\"type\": \"boolean\", \"value\": \"false\"},\n}\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"pdf:calc_pdf_Export:\" + json.dumps(opts),\n                \"--outdir\", \"lo\", \"report.xlsx\"], check=True, capture_output=True)\n\nenc = PdfReader(\"lo\u002Freport.pdf\").trailer[\"\u002FEncrypt\"].get_object()\nprint(\"version\", enc[\"\u002FV\"], \"revision\", enc[\"\u002FR\"])\n",[14,548,549,556,560,570,600,624,647,670,694,699,724,757,761,778],{"__ignoreMap":40},[44,550,551,553],{"class":46,"line":47},[44,552,242],{"class":241},[44,554,555],{"class":245}," json\n",[44,557,558],{"class":46,"line":249},[44,559,286],{"emptyLinePlaceholder":285},[44,561,562,565,567],{"class":46,"line":263},[44,563,564],{"class":245},"opts ",[44,566,339],{"class":241},[44,568,569],{"class":245}," {\n",[44,571,572,575,578,581,584,587,589,592,594,597],{"class":46,"line":282},[44,573,574],{"class":54},"    \"EncryptFile\"",[44,576,577],{"class":245},": {",[44,579,580],{"class":54},"\"type\"",[44,582,583],{"class":245},": ",[44,585,586],{"class":54},"\"boolean\"",[44,588,298],{"class":245},[44,590,591],{"class":54},"\"value\"",[44,593,583],{"class":245},[44,595,596],{"class":54},"\"true\"",[44,598,599],{"class":245},"},\n",[44,601,602,605,607,609,611,614,616,618,620,622],{"class":46,"line":289},[44,603,604],{"class":54},"    \"DocumentOpenPassword\"",[44,606,577],{"class":245},[44,608,580],{"class":54},[44,610,583],{"class":245},[44,612,613],{"class":54},"\"string\"",[44,615,298],{"class":245},[44,617,591],{"class":54},[44,619,583],{"class":245},[44,621,398],{"class":54},[44,623,599],{"class":245},[44,625,626,629,631,633,635,637,639,641,643,645],{"class":46,"line":332},[44,627,628],{"class":54},"    \"RestrictPermissions\"",[44,630,577],{"class":245},[44,632,580],{"class":54},[44,634,583],{"class":245},[44,636,586],{"class":54},[44,638,298],{"class":245},[44,640,591],{"class":54},[44,642,583],{"class":245},[44,644,596],{"class":54},[44,646,599],{"class":245},[44,648,649,652,654,656,658,660,662,664,666,668],{"class":46,"line":358},[44,650,651],{"class":54},"    \"PermissionPassword\"",[44,653,577],{"class":245},[44,655,580],{"class":54},[44,657,583],{"class":245},[44,659,613],{"class":54},[44,661,298],{"class":245},[44,663,591],{"class":54},[44,665,583],{"class":245},[44,667,412],{"class":54},[44,669,599],{"class":245},[44,671,672,675,677,679,681,683,685,687,689,692],{"class":46,"line":363},[44,673,674],{"class":54},"    \"EnableCopyingOfContent\"",[44,676,577],{"class":245},[44,678,580],{"class":54},[44,680,583],{"class":245},[44,682,586],{"class":54},[44,684,298],{"class":245},[44,686,591],{"class":54},[44,688,583],{"class":245},[44,690,691],{"class":54},"\"false\"",[44,693,599],{"class":245},[44,695,696],{"class":46,"line":384},[44,697,698],{"class":245},"}\n",[44,700,701,703,705,707,709,711,713,715,718,721],{"class":46,"line":390},[44,702,292],{"class":245},[44,704,295],{"class":54},[44,706,298],{"class":245},[44,708,301],{"class":54},[44,710,298],{"class":245},[44,712,306],{"class":54},[44,714,298],{"class":245},[44,716,717],{"class":54},"\"pdf:calc_pdf_Export:\"",[44,719,720],{"class":241}," +",[44,722,723],{"class":245}," json.dumps(opts),\n",[44,725,726,729,731,734,736,738,740,743,745,747,749,751,753,755],{"class":46,"line":404},[44,727,728],{"class":54},"                \"--outdir\"",[44,730,298],{"class":245},[44,732,733],{"class":54},"\"lo\"",[44,735,298],{"class":245},[44,737,326],{"class":54},[44,739,516],{"class":245},[44,741,742],{"class":335},"check",[44,744,339],{"class":241},[44,746,343],{"class":342},[44,748,298],{"class":245},[44,750,348],{"class":335},[44,752,339],{"class":241},[44,754,343],{"class":342},[44,756,355],{"class":245},[44,758,759],{"class":46,"line":417},[44,760,286],{"emptyLinePlaceholder":285},[44,762,763,765,767,769,772,774,776],{"class":46,"line":430},[44,764,479],{"class":245},[44,766,339],{"class":241},[44,768,484],{"class":245},[44,770,771],{"class":54},"\"lo\u002Freport.pdf\"",[44,773,489],{"class":245},[44,775,492],{"class":54},[44,777,495],{"class":245},[44,779,780,782,784,786,788,790,792,794,796,798],{"class":46,"line":455},[44,781,501],{"class":342},[44,783,504],{"class":245},[44,785,507],{"class":54},[44,787,510],{"class":245},[44,789,513],{"class":54},[44,791,516],{"class":245},[44,793,519],{"class":54},[44,795,510],{"class":245},[44,797,524],{"class":54},[44,799,527],{"class":245},[10,801,802,803,805],{},"This saves a step, but the file comes out with version 2, revision 3 encryption — 128-bit RC4, the scheme of PDF 1.4. RC4 is considered broken and short passwords protected with it can be recovered far more quickly than with AES-256. Note also that the passwords appear on the ",[14,804,79],{}," command line, where other users of the machine can see them in the process list. Prefer Step 1 for anything sensitive; the LibreOffice route is fine when the PDF only needs to deter casual opening.",[122,807,131,813,131,816,131,819,131,823,131,830,131,836,131,843,131,848,131,851,131,855,131,858],{"viewBox":808,"role":125,"ariaLabelledBy":809,"xmlns":129,"style":812},"4 0 744 100",[810,811],"pw-strength-t","pw-strength-d","width:100%;max-width:744px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[133,814,815],{"id":810},"Encryption the two routes produce",[137,817,818],{"id":811},"The pypdf route writes version 5 revision 6 encryption, which is AES-256. The LibreOffice export filter writes version 2 revision 3 encryption, which is 128-bit RC4, an older and weaker scheme.",[141,820],{"x":143,"y":144,"width":821,"height":822,"fill":147},"744","100",[141,824],{"x":150,"y":151,"width":825,"height":826,"rx":827,"fill":828,"stroke":829,"style":180},"189.5","28.0","7","#e7ebef","var(--line,#cdd5e6)",[170,831,835],{"x":832,"y":833,"style":834},"114.8","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","pypdf encrypt (AES-256)",[141,837],{"x":838,"y":151,"width":839,"height":826,"rx":827,"fill":840,"stroke":841,"style":842},"219.5","383.5","#d9f4f1","var(--teal,#0f9488)","stroke-width:1.5px",[170,844,847],{"x":845,"y":833,"style":846},"611.0","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","V5 R6 · AES 256-bit",[141,849],{"x":150,"y":850,"width":825,"height":826,"rx":827,"fill":828,"stroke":829,"style":180},"56.0",[170,852,854],{"x":832,"y":853,"style":834},"74.5","LibreOffice export filter",[141,856],{"x":838,"y":850,"width":857,"height":826,"rx":827,"fill":197,"stroke":198,"style":842},"191.7",[170,859,862],{"x":860,"y":853,"style":861},"419.3","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","V2 R3 · RC4 128-bit",[30,864,866],{"id":865},"step-3-one-password-per-recipient","Step 3 — One password per recipient",[10,868,869,870,873],{},"When each customer or employee gets their own PDF, each file should have its own password — otherwise one recipient could open everyone else's. Generate a random password per file with ",[14,871,872],{},"secrets"," and record it for the separate channel you use to deliver it:",[35,875,877],{"className":232,"code":876,"language":234,"meta":40,"style":40},"import csv\nimport secrets\nimport string\n\nALPHABET = string.ascii_letters + string.digits\n\ndef new_password(length=12):\n    return \"\".join(secrets.choice(ALPHABET) for _ in range(length))\n\nrecipients = [\"north@example.com\", \"south@example.com\", \"east@example.com\"]\nissued = []\nfor email in recipients:\n    pwd = new_password()\n    w = PdfWriter(clone_from=\"plain\u002Freport.pdf\")\n    w.encrypt(user_password=pwd, owner_password=new_password(32), algorithm=\"AES-256\",\n              permissions_flag=Perm.PRINT | Perm.PRINT_TO_REPRESENTATION)\n    out = f\"statement_{email.split('@')[0]}.pdf\"\n    w.write(out)\n    issued.append({\"recipient\": email, \"file\": out, \"password\": pwd})\n\nwith open(\"passwords.csv\", \"w\", newline=\"\") as fh:\n    csv.DictWriter(fh, fieldnames=[\"recipient\", \"file\", \"password\"]).writerows(issued)\nprint(len(issued), \"files, password length\", len(issued[0][\"password\"]))\n",[14,878,879,886,893,900,904,921,925,945,976,980,1006,1016,1028,1038,1055,1091,1110,1147,1152,1176,1181,1217,1244],{"__ignoreMap":40},[44,880,881,883],{"class":46,"line":47},[44,882,242],{"class":241},[44,884,885],{"class":245}," csv\n",[44,887,888,890],{"class":46,"line":249},[44,889,242],{"class":241},[44,891,892],{"class":245}," secrets\n",[44,894,895,897],{"class":46,"line":263},[44,896,242],{"class":241},[44,898,899],{"class":245}," string\n",[44,901,902],{"class":46,"line":282},[44,903,286],{"emptyLinePlaceholder":285},[44,905,906,909,912,915,918],{"class":46,"line":289},[44,907,908],{"class":342},"ALPHABET",[44,910,911],{"class":241}," =",[44,913,914],{"class":245}," string.ascii_letters ",[44,916,917],{"class":241},"+",[44,919,920],{"class":245}," string.digits\n",[44,922,923],{"class":46,"line":332},[44,924,286],{"emptyLinePlaceholder":285},[44,926,927,930,934,937,939,942],{"class":46,"line":358},[44,928,929],{"class":241},"def",[44,931,933],{"class":932},"s_Opv"," new_password",[44,935,936],{"class":245},"(length",[44,938,339],{"class":241},[44,940,941],{"class":342},"12",[44,943,944],{"class":245},"):\n",[44,946,947,950,953,956,958,961,964,967,970,973],{"class":46,"line":363},[44,948,949],{"class":241},"    return",[44,951,952],{"class":54}," \"\"",[44,954,955],{"class":245},".join(secrets.choice(",[44,957,908],{"class":342},[44,959,960],{"class":245},") ",[44,962,963],{"class":241},"for",[44,965,966],{"class":245}," _ ",[44,968,969],{"class":241},"in",[44,971,972],{"class":342}," range",[44,974,975],{"class":245},"(length))\n",[44,977,978],{"class":46,"line":384},[44,979,286],{"emptyLinePlaceholder":285},[44,981,982,985,987,990,993,995,998,1000,1003],{"class":46,"line":390},[44,983,984],{"class":245},"recipients ",[44,986,339],{"class":241},[44,988,989],{"class":245}," [",[44,991,992],{"class":54},"\"north@example.com\"",[44,994,298],{"class":245},[44,996,997],{"class":54},"\"south@example.com\"",[44,999,298],{"class":245},[44,1001,1002],{"class":54},"\"east@example.com\"",[44,1004,1005],{"class":245},"]\n",[44,1007,1008,1011,1013],{"class":46,"line":404},[44,1009,1010],{"class":245},"issued ",[44,1012,339],{"class":241},[44,1014,1015],{"class":245}," []\n",[44,1017,1018,1020,1023,1025],{"class":46,"line":417},[44,1019,963],{"class":241},[44,1021,1022],{"class":245}," email ",[44,1024,969],{"class":241},[44,1026,1027],{"class":245}," recipients:\n",[44,1029,1030,1033,1035],{"class":46,"line":430},[44,1031,1032],{"class":245},"    pwd ",[44,1034,339],{"class":241},[44,1036,1037],{"class":245}," new_password()\n",[44,1039,1040,1043,1045,1047,1049,1051,1053],{"class":46,"line":455},[44,1041,1042],{"class":245},"    w ",[44,1044,339],{"class":241},[44,1046,371],{"class":245},[44,1048,374],{"class":335},[44,1050,339],{"class":241},[44,1052,379],{"class":54},[44,1054,355],{"class":245},[44,1056,1057,1060,1063,1065,1068,1071,1073,1076,1079,1082,1085,1087,1089],{"class":46,"line":460},[44,1058,1059],{"class":245},"    w.encrypt(",[44,1061,1062],{"class":335},"user_password",[44,1064,339],{"class":241},[44,1066,1067],{"class":245},"pwd, ",[44,1069,1070],{"class":335},"owner_password",[44,1072,339],{"class":241},[44,1074,1075],{"class":245},"new_password(",[44,1077,1078],{"class":342},"32",[44,1080,1081],{"class":245},"), ",[44,1083,1084],{"class":335},"algorithm",[44,1086,339],{"class":241},[44,1088,425],{"class":54},[44,1090,401],{"class":245},[44,1092,1093,1096,1098,1100,1102,1104,1106,1108],{"class":46,"line":471},[44,1094,1095],{"class":335},"              permissions_flag",[44,1097,339],{"class":241},[44,1099,438],{"class":245},[44,1101,441],{"class":342},[44,1103,444],{"class":241},[44,1105,447],{"class":245},[44,1107,450],{"class":342},[44,1109,355],{"class":245},[44,1111,1112,1115,1117,1120,1123,1127,1130,1133,1136,1138,1141,1144],{"class":46,"line":476},[44,1113,1114],{"class":245},"    out ",[44,1116,339],{"class":241},[44,1118,1119],{"class":241}," f",[44,1121,1122],{"class":54},"\"statement_",[44,1124,1126],{"class":1125},"sSjpA","{",[44,1128,1129],{"class":245},"email.split(",[44,1131,1132],{"class":54},"'@'",[44,1134,1135],{"class":245},")[",[44,1137,144],{"class":342},[44,1139,1140],{"class":245},"]",[44,1142,1143],{"class":1125},"}",[44,1145,1146],{"class":54},".pdf\"\n",[44,1148,1149],{"class":46,"line":498},[44,1150,1151],{"class":245},"    w.write(out)\n",[44,1153,1155,1158,1161,1164,1167,1170,1173],{"class":46,"line":1154},19,[44,1156,1157],{"class":245},"    issued.append({",[44,1159,1160],{"class":54},"\"recipient\"",[44,1162,1163],{"class":245},": email, ",[44,1165,1166],{"class":54},"\"file\"",[44,1168,1169],{"class":245},": out, ",[44,1171,1172],{"class":54},"\"password\"",[44,1174,1175],{"class":245},": pwd})\n",[44,1177,1179],{"class":46,"line":1178},20,[44,1180,286],{"emptyLinePlaceholder":285},[44,1182,1184,1187,1190,1192,1195,1197,1200,1202,1205,1207,1210,1212,1214],{"class":46,"line":1183},21,[44,1185,1186],{"class":241},"with",[44,1188,1189],{"class":342}," open",[44,1191,504],{"class":245},[44,1193,1194],{"class":54},"\"passwords.csv\"",[44,1196,298],{"class":245},[44,1198,1199],{"class":54},"\"w\"",[44,1201,298],{"class":245},[44,1203,1204],{"class":335},"newline",[44,1206,339],{"class":241},[44,1208,1209],{"class":54},"\"\"",[44,1211,960],{"class":245},[44,1213,276],{"class":241},[44,1215,1216],{"class":245}," fh:\n",[44,1218,1220,1223,1226,1228,1231,1233,1235,1237,1239,1241],{"class":46,"line":1219},22,[44,1221,1222],{"class":245},"    csv.DictWriter(fh, ",[44,1224,1225],{"class":335},"fieldnames",[44,1227,339],{"class":241},[44,1229,1230],{"class":245},"[",[44,1232,1160],{"class":54},[44,1234,298],{"class":245},[44,1236,1166],{"class":54},[44,1238,298],{"class":245},[44,1240,1172],{"class":54},[44,1242,1243],{"class":245},"]).writerows(issued)\n",[44,1245,1247,1249,1251,1254,1257,1260,1262,1264,1267,1269,1272,1274],{"class":46,"line":1246},23,[44,1248,501],{"class":342},[44,1250,504],{"class":245},[44,1252,1253],{"class":342},"len",[44,1255,1256],{"class":245},"(issued), ",[44,1258,1259],{"class":54},"\"files, password length\"",[44,1261,298],{"class":245},[44,1263,1253],{"class":342},[44,1265,1266],{"class":245},"(issued[",[44,1268,144],{"class":342},[44,1270,1271],{"class":245},"][",[44,1273,1172],{"class":54},[44,1275,1276],{"class":245},"]))\n",[10,1278,1279,1281,1282,1285,1286,1289,1290,28],{},[14,1280,872],{}," uses the operating system's cryptographic random source, unlike ",[14,1283,1284],{},"random",". Twelve characters from 62 letters and digits give about 71 bits of randomness, which is strong for AES-256 files. Never send the password in the same email as the PDF — send it by SMS, a separate message or a portal — and treat ",[14,1287,1288],{},"passwords.csv"," as a secret itself: write it somewhere access-controlled and delete it once delivered. A per-recipient sending loop is shown in ",[19,1291,1293],{"href":1292},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Fsend-each-recipient-their-own-excel-report\u002F","Send Each Recipient Their Own Excel Report",[10,1295,1296],{},"Some organisations instead use a password the recipient already knows — the last digits of an account number plus a birth date, say. That avoids a second channel but makes passwords guessable by anyone who knows those facts, and short numeric passwords fall quickly to brute force. Random passwords are the stronger choice.",[30,1298,1300],{"id":1299},"step-4-verify-the-pdf-is-really-locked","Step 4 — Verify the PDF is really locked",[10,1302,1303],{},"A test should prove that the file cannot be read without the password, that the right password works, and that the owner password is different:",[35,1305,1307],{"className":232,"code":1306,"language":234,"meta":40,"style":40},"from pypdf.errors import FileNotDecryptedError\nfrom pypdf import PasswordType\n\ndef check_locked(path, user_pw, owner_pw):\n    r = PdfReader(path)\n    assert r.is_encrypted\n    try:\n        r.pages[0].extract_text()\n        raise AssertionError(\"readable without a password\")\n    except FileNotDecryptedError:\n        pass\n    assert PdfReader(path).decrypt(\"wrong\") == PasswordType.NOT_DECRYPTED\n    assert PdfReader(path).decrypt(user_pw) == PasswordType.USER_PASSWORD\n    assert PdfReader(path).decrypt(owner_pw) == PasswordType.OWNER_PASSWORD\n    unlocked = PdfReader(path)\n    unlocked.decrypt(user_pw)\n    return unlocked.pages[0].extract_text().split(\"\\n\")[0]\n\nprint(check_locked(\"report_locked.pdf\", \"open-7Kp2\", \"owner-Qz94-long-random\"))\nfor row in issued:\n    print(row[\"file\"], PdfReader(row[\"file\"]).decrypt(row[\"password\"]).name)\n",[14,1308,1309,1321,1332,1336,1346,1356,1364,1372,1382,1397,1405,1410,1431,1445,1459,1468,1473,1499,1503,1523,1535],{"__ignoreMap":40},[44,1310,1311,1313,1316,1318],{"class":46,"line":47},[44,1312,252],{"class":241},[44,1314,1315],{"class":245}," pypdf.errors ",[44,1317,242],{"class":241},[44,1319,1320],{"class":245}," FileNotDecryptedError\n",[44,1322,1323,1325,1327,1329],{"class":46,"line":249},[44,1324,252],{"class":241},[44,1326,255],{"class":245},[44,1328,242],{"class":241},[44,1330,1331],{"class":245}," PasswordType\n",[44,1333,1334],{"class":46,"line":263},[44,1335,286],{"emptyLinePlaceholder":285},[44,1337,1338,1340,1343],{"class":46,"line":282},[44,1339,929],{"class":241},[44,1341,1342],{"class":932}," check_locked",[44,1344,1345],{"class":245},"(path, user_pw, owner_pw):\n",[44,1347,1348,1351,1353],{"class":46,"line":289},[44,1349,1350],{"class":245},"    r ",[44,1352,339],{"class":241},[44,1354,1355],{"class":245}," PdfReader(path)\n",[44,1357,1358,1361],{"class":46,"line":332},[44,1359,1360],{"class":241},"    assert",[44,1362,1363],{"class":245}," r.is_encrypted\n",[44,1365,1366,1369],{"class":46,"line":358},[44,1367,1368],{"class":241},"    try",[44,1370,1371],{"class":245},":\n",[44,1373,1374,1377,1379],{"class":46,"line":363},[44,1375,1376],{"class":245},"        r.pages[",[44,1378,144],{"class":342},[44,1380,1381],{"class":245},"].extract_text()\n",[44,1383,1384,1387,1390,1392,1395],{"class":46,"line":384},[44,1385,1386],{"class":241},"        raise",[44,1388,1389],{"class":342}," AssertionError",[44,1391,504],{"class":245},[44,1393,1394],{"class":54},"\"readable without a password\"",[44,1396,355],{"class":245},[44,1398,1399,1402],{"class":46,"line":390},[44,1400,1401],{"class":241},"    except",[44,1403,1404],{"class":245}," FileNotDecryptedError:\n",[44,1406,1407],{"class":46,"line":404},[44,1408,1409],{"class":241},"        pass\n",[44,1411,1412,1414,1417,1420,1422,1425,1428],{"class":46,"line":417},[44,1413,1360],{"class":241},[44,1415,1416],{"class":245}," PdfReader(path).decrypt(",[44,1418,1419],{"class":54},"\"wrong\"",[44,1421,960],{"class":245},[44,1423,1424],{"class":241},"==",[44,1426,1427],{"class":245}," PasswordType.",[44,1429,1430],{"class":342},"NOT_DECRYPTED\n",[44,1432,1433,1435,1438,1440,1442],{"class":46,"line":430},[44,1434,1360],{"class":241},[44,1436,1437],{"class":245}," PdfReader(path).decrypt(user_pw) ",[44,1439,1424],{"class":241},[44,1441,1427],{"class":245},[44,1443,1444],{"class":342},"USER_PASSWORD\n",[44,1446,1447,1449,1452,1454,1456],{"class":46,"line":455},[44,1448,1360],{"class":241},[44,1450,1451],{"class":245}," PdfReader(path).decrypt(owner_pw) ",[44,1453,1424],{"class":241},[44,1455,1427],{"class":245},[44,1457,1458],{"class":342},"OWNER_PASSWORD\n",[44,1460,1461,1464,1466],{"class":46,"line":460},[44,1462,1463],{"class":245},"    unlocked ",[44,1465,339],{"class":241},[44,1467,1355],{"class":245},[44,1469,1470],{"class":46,"line":471},[44,1471,1472],{"class":245},"    unlocked.decrypt(user_pw)\n",[44,1474,1475,1477,1480,1482,1485,1488,1491,1493,1495,1497],{"class":46,"line":476},[44,1476,949],{"class":241},[44,1478,1479],{"class":245}," unlocked.pages[",[44,1481,144],{"class":342},[44,1483,1484],{"class":245},"].extract_text().split(",[44,1486,1487],{"class":54},"\"",[44,1489,1490],{"class":1125},"\\n",[44,1492,1487],{"class":54},[44,1494,1135],{"class":245},[44,1496,144],{"class":342},[44,1498,1005],{"class":245},[44,1500,1501],{"class":46,"line":498},[44,1502,286],{"emptyLinePlaceholder":285},[44,1504,1505,1507,1510,1512,1514,1516,1518,1520],{"class":46,"line":1154},[44,1506,501],{"class":342},[44,1508,1509],{"class":245},"(check_locked(",[44,1511,466],{"class":54},[44,1513,298],{"class":245},[44,1515,398],{"class":54},[44,1517,298],{"class":245},[44,1519,412],{"class":54},[44,1521,1522],{"class":245},"))\n",[44,1524,1525,1527,1530,1532],{"class":46,"line":1178},[44,1526,963],{"class":241},[44,1528,1529],{"class":245}," row ",[44,1531,969],{"class":241},[44,1533,1534],{"class":245}," issued:\n",[44,1536,1537,1540,1543,1545,1548,1550,1553,1555],{"class":46,"line":1183},[44,1538,1539],{"class":342},"    print",[44,1541,1542],{"class":245},"(row[",[44,1544,1166],{"class":54},[44,1546,1547],{"class":245},"], PdfReader(row[",[44,1549,1166],{"class":54},[44,1551,1552],{"class":245},"]).decrypt(row[",[44,1554,1172],{"class":54},[44,1556,1557],{"class":245},"]).name)\n",[10,1559,1560,1561,1564,1565,1568,1569,298,1572,1575,1576,1579],{},"Reading a page before decrypting raises ",[14,1562,1563],{},"FileNotDecryptedError","; ",[14,1566,1567],{},"decrypt()"," returns which password matched — ",[14,1570,1571],{},"NOT_DECRYPTED",[14,1573,1574],{},"USER_PASSWORD"," or ",[14,1577,1578],{},"OWNER_PASSWORD",". After decrypting with the user password the first line of the page reads \"Region Revenue\", proving the content survived, and each per-recipient file opens with its own password. Running this in the report pipeline's tests catches a file that was accidentally written unencrypted.",[122,1581,131,1586,131,1589,131,1592,131,1595,131,1599,131,1607,131,1613,131,1618,131,1623,131,1628,131,1631,131,1636,131,1641,131,1644,131,1647,131,1650,131,1653,131,1660,131,1666,131,1670,131,1674,131,1678,131,1681,131,1684,131,1690,131,1695,131,1698],{"viewBox":1582,"role":125,"ariaLabelledBy":1583,"xmlns":129,"style":130},"4 0 752 142",[1584,1585],"pw-pipeline-t","pw-pipeline-d",[133,1587,1588],{"id":1584},"A locked-report pipeline",[137,1590,1591],{"id":1585},"The workbook is converted to a plain PDF, the PDF is encrypted with AES-256 and a per-recipient password, a check confirms it cannot be read without the password, and the file and password are sent through separate channels.",[141,1593],{"x":143,"y":144,"width":145,"height":1594,"fill":147},"142",[141,1596],{"x":150,"y":151,"width":1597,"height":1598,"rx":941,"fill":828,"stroke":829,"style":157},"153.0","110.5",[159,1600,1602],{"transform":1601},"translate(84.5,37.8) scale(1.000)",[163,1603],{"d":1604,"fill":166,"stroke":1605,"style":1606},"M6 3h9l4 4v14H6z M15 3v4h4 M9 12h6 M9 15.5h6 M9 19h4","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[170,1608,1612],{"x":1609,"y":1610,"style":1611},"96.5","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Convert",[170,1614,1617],{"x":1609,"y":1615,"style":1616},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","soffice to plain PDF",[46,1619],{"x1":1620,"y1":1621,"x2":1622,"y2":1621,"stroke":156,"style":157},"178.0","71.2","200.0",[1624,1625],"polygon",{"points":1626,"fill":1627},"204.0,71.2 195.0,76.2 195.0,66.2","#5b5cf0",[141,1629],{"x":1630,"y":151,"width":1597,"height":1598,"rx":941,"fill":155,"stroke":156,"style":157},"209.0",[159,1632,1634],{"transform":1633},"translate(273.5,37.8) scale(1.000)",[163,1635],{"d":165,"fill":166,"stroke":167,"style":1606},[170,1637,1640],{"x":1638,"y":1610,"style":1639},"285.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Encrypt",[170,1642,1643],{"x":1638,"y":1615,"style":1616},"pypdf AES-256",[46,1645],{"x1":1646,"y1":1621,"x2":196,"y2":1621,"stroke":156,"style":157},"367.0",[1624,1648],{"points":1649,"fill":1627},"393.0,71.2 384.0,76.2 384.0,66.2",[141,1651],{"x":1652,"y":151,"width":1597,"height":1598,"rx":941,"fill":840,"stroke":841,"style":157},"398.0",[159,1654,1656],{"transform":1655},"translate(462.5,29.0) scale(1.000)",[163,1657],{"d":1658,"fill":166,"stroke":1659,"style":1606},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[170,1661,1665],{"x":1662,"y":1663,"style":1664},"474.5","75.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Verify",[170,1667,1669],{"x":1662,"y":1668,"style":1616},"95.0","unreadable without",[170,1671,1673],{"x":1662,"y":1672,"style":1616},"112.5","password",[46,1675],{"x1":1676,"y1":1621,"x2":1677,"y2":1621,"stroke":156,"style":157},"556.0","578.0",[1624,1679],{"points":1680,"fill":1627},"582.0,71.2 573.0,76.2 573.0,66.2",[141,1682],{"x":1683,"y":151,"width":1597,"height":1598,"rx":941,"fill":197,"stroke":198,"style":157},"587.0",[159,1685,1687],{"transform":1686},"translate(651.5,29.0) scale(1.000)",[163,1688],{"d":1689,"fill":166,"stroke":205,"style":1606},"M3 11l18-8-8 18-2-8z",[170,1691,1694],{"x":1692,"y":1663,"style":1693},"663.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Deliver",[170,1696,1697],{"x":1692,"y":1668,"style":1616},"file and password",[170,1699,1700],{"x":1692,"y":1672,"style":1616},"apart",[30,1702,1704],{"id":1703},"what-permissions-can-and-cannot-stop","What permissions can and cannot stop",[10,1706,1707,1708,1710,1711,28],{},"The user password is real protection: without it, the content is encrypted with AES and cannot be read. Permission flags are different. Once a file is opened with the user password, the content is decrypted on the reader's machine, and the \"no copying\" or \"no editing\" flags are only honoured if the reading software chooses to honour them. Mainstream viewers do; libraries and some tools do not — ",[14,1709,16],{}," itself extracted the text above after decrypting with the user password, even though copying was not permitted. Treat permissions as a polite request that stops casual copying, not as a security boundary. If recipients must not be able to extract figures, do not send them the figures. Clean the source workbook of hidden sheets and comments before exporting as well; see ",[19,1712,1714],{"href":1713},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fremove-hidden-data-before-sharing-an-excel-file\u002F","Remove Hidden Data Before Sharing an Excel File",[30,1716,1718],{"id":1717},"common-pitfalls","Common pitfalls",[93,1720,1721,1727,1733,1739,1745],{},[96,1722,1723,1726],{},[100,1724,1725],{},"Only an owner password."," The file opens for anyone; set a user password for confidentiality.",[96,1728,1729,1732],{},[100,1730,1731],{},"Owner password equal to the user password."," Anyone who opens the file can remove its restrictions.",[96,1734,1735,1738],{},[100,1736,1737],{},"RC4 from the LibreOffice filter."," Use pypdf's AES-256 for sensitive files.",[96,1740,1741,1744],{},[100,1742,1743],{},"The password in the same email."," Deliver it through a separate channel.",[96,1746,1747,1752],{},[100,1748,1749,1750,28],{},"Missing ",[14,1751,75],{}," pypdf needs it for AES; install it with pypdf.",[30,1754,1756],{"id":1755},"conclusion","Conclusion",[10,1758,1759,1760,1763,1764,1766],{},"To lock a PDF exported from Excel, convert the workbook to a plain PDF and encrypt it with pypdf's ",[14,1761,1762],{},"encrypt()"," using AES-256, a user password to open it and a separate, long owner password for permissions. LibreOffice can encrypt during export, but only with 128-bit RC4 and passwords visible on the command line. Generate a random password per recipient with ",[14,1765,872],{},", deliver it separately, verify in tests that the file cannot be read without it, and remember that permission flags only restrain cooperative software.",[30,1768,1770],{"id":1769},"frequently-asked-questions","Frequently asked questions",[10,1772,1773,1776],{},[100,1774,1775],{},"How do I password-protect a PDF with Python?","\nOpen the PDF with pypdf's PdfWriter using clone_from, call encrypt with a user password, a separate owner password and algorithm AES-256, then write the result. The cryptography package must be installed.",[10,1778,1779,1782],{},[100,1780,1781],{},"Can LibreOffice encrypt a PDF when converting from Excel?","\nYes. Pass calc_pdf_Export options such as EncryptFile and DocumentOpenPassword as JSON after the filter name. The result uses 128-bit RC4, weaker than the AES-256 pypdf writes.",[10,1784,1785,1788],{},[100,1786,1787],{},"What is the difference between the user and owner password?","\nThe user password is needed to open and decrypt the file. The owner password unlocks permission settings such as printing and copying.",[10,1790,1791,1794],{},[100,1792,1793],{},"Do PDF permissions stop people copying text?","\nOnly in software that respects them. After a file is opened with the user password, tools can ignore the flags, so permissions deter casual copying but are not a security boundary.",[30,1796,1798],{"id":1797},"related","Related",[93,1800,1801,1807,1814,1821],{},[96,1802,1803,1804,1806],{},"Up one level: ",[19,1805,27],{"href":26}," — turning workbooks into PDFs.",[96,1808,1809,1813],{},[19,1810,1812],{"href":1811},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fmerge-excel-pdf-exports-into-one-file\u002F","Merge Excel PDF Exports into One File"," — combine before encrypting.",[96,1815,1816,1820],{},[19,1817,1819],{"href":1818},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fpassword-protect-an-excel-file-with-python\u002F","Password-Protect an Excel File with Python"," — encrypting the workbook itself.",[96,1822,1823,1827],{},[19,1824,1826],{"href":1825},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fadd-bookmarks-and-a-cover-page-to-an-excel-pdf-export\u002F","Add Bookmarks and a Cover Page to an Excel PDF Export"," — finishing the PDF before locking it.",[1829,1830,1831],"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 .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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}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":40,"searchDepth":249,"depth":249,"links":1833},[1834,1835,1836,1837,1838,1839,1840,1841,1842,1843,1844],{"id":32,"depth":249,"text":33},{"id":87,"depth":249,"text":88},{"id":225,"depth":249,"text":226},{"id":539,"depth":249,"text":540},{"id":865,"depth":249,"text":866},{"id":1299,"depth":249,"text":1300},{"id":1703,"depth":249,"text":1704},{"id":1717,"depth":249,"text":1718},{"id":1755,"depth":249,"text":1756},{"id":1769,"depth":249,"text":1770},{"id":1797,"depth":249,"text":1798},"2026-10-02","Encrypt the PDF your Excel report becomes: AES-256 with pypdf after conversion, or encryption set directly in the LibreOffice export filter (and why it is weaker), the difference between open and permissions passwords, one password per recipient, verifying the file really is locked, and what PDF permissions can and cannot stop.","md",[1849,1851,1853,1855],{"q":1775,"a":1850},"Open the PDF with pypdf's PdfWriter using clone_from, call encrypt with a user password, a separate owner password and algorithm AES-256, then write the result. The cryptography package must be installed.",{"q":1781,"a":1852},"Yes. Pass calc_pdf_Export options such as EncryptFile and DocumentOpenPassword as JSON after the filter name. The result uses 128-bit RC4, weaker than the AES-256 pypdf writes.",{"q":1787,"a":1854},"The user password is needed to open and decrypt the file. The owner password unlocks permission settings such as printing and copying.",{"q":1793,"a":1856},"Only in software that respects them. After a file is opened with the user password, tools can ignore the flags, so permissions deter casual copying but are not a security boundary.","password-protect a pdf exported from excel",null,{"breadcrumb":1860},[1861,1864,1867],{"name":1862,"item":1863},"Home","\u002F",{"name":1865,"item":1866},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":27,"item":26},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel",{"title":1870,"description":1871},"Password-Protect a PDF Exported from Excel with Python","Lock an Excel report PDF with Python: pypdf AES-256 encryption, LibreOffice calc_pdf_Export password options, user vs owner passwords, per-recipient passwords, and a check that the PDF needs its password.","password-protect-a-pdf-exported-from-excel","automating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fpassword-protect-a-pdf-exported-from-excel\u002Findex",[1875,1876],"pdf-export","protection-and-sharing","how-to","OjcqW16rVt3OhIGRUrVBhudbTSswldmGhjCVColH_OA",[1880,1884],{"title":1881,"path":1882,"stem":1883,"children":-1},"Merge Several Excel PDF Exports into One File","\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fmerge-excel-pdf-exports-into-one-file","automating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fmerge-excel-pdf-exports-into-one-file\u002Findex",{"title":1885,"path":1886,"stem":1887,"children":-1},"Sign a PDF Report Digitally with Python","\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fsign-a-pdf-report-digitally-with-python","automating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fsign-a-pdf-report-digitally-with-python\u002Findex",[1889,1891],{"path":1890,"title":27},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf",{"path":1892,"title":1865},"\u002Fautomating-reporting-workflows",[],[1895,1898,1901,1903,1906,1909],{"path":1896,"title":1897,"kind":1877},"\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":1899,"title":1900,"kind":1877},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-one-pdf-per-excel-row","Generate One PDF per Excel Row",{"path":1902,"title":1826,"kind":1877},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fadd-bookmarks-and-a-cover-page-to-an-excel-pdf-export",{"path":1904,"title":1905,"kind":1877},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fadd-headers-footers-and-page-numbers-to-an-excel-pdf","Add Headers, Footers and Page Numbers to an Excel PDF",{"path":1907,"title":1908,"kind":1877},"\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":1910,"title":1911,"kind":1877},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-file-to-pdf-with-python","Convert an Excel File to PDF with Python",1790972137424]