[{"data":1,"prerenderedAt":1614},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images":3,"surround:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images":1582,"ancestors:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images":1590,"children:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images":1595,"related:\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images":1596},{"id":4,"title":5,"body":6,"dateModified":1549,"datePublished":1549,"description":1550,"extension":1551,"faq":1552,"keyword":1561,"kind":1562,"meta":1563,"navigation":141,"path":1571,"seo":1572,"slug":1575,"stem":1576,"topics":1577,"type":1580,"__hash__":1581},"docs\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images\u002Findex.md","Turn PDF Report Pages into Images",{"type":7,"value":8,"toc":1537},"minimark",[9,19,84,89,108,111,115,356,366,568,572,824,835,839,1123,1130,1133,1210,1214,1221,1265,1283,1398,1402,1410,1414,1449,1453,1463,1467,1476,1482,1488,1500,1504,1533],[10,11,12,13,18],"p",{},"A PDF is the right format for a report that will be printed or archived, but not for every place a report is seen. A chat message, an intranet page, a ticket or the body of an email shows an image straight away, while a PDF attachment needs a click and often a download. Turning the first page — or every page — of a report PDF into an image makes it visible where people already are: a summary page as a PNG in a team channel, a thumbnail of each monthly report on an index page, a picture of the key page pasted into a slide. This page renders PDF pages to images with pypdfium2, a Python binding for the PDF engine in Chrome that installs with pip and needs nothing else, and covers the decisions that matter: resolution, format, trimming the margins and thumbnails. 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,26],"pre",{"className":22,"code":23,"language":24,"meta":25,"style":25},"language-bash shiki shiki-themes github-light github-dark-high-contrast","soffice --headless --convert-to pdf sales_report.xlsx > \u002Fdev\u002Fnull 2>&1 && pdfinfo sales_report.pdf | grep -E \"Pages|Page size\"\n","bash","",[27,28,29],"code",{"__ignoreMap":25},[30,31,34,38,42,45,49,52,56,59,62,66,69,72,75,78,81],"span",{"class":32,"line":33},"line",1,[30,35,37],{"class":36},"sMTad","soffice",[30,39,41],{"class":40},"sP0c6"," --headless",[30,43,44],{"class":40}," --convert-to",[30,46,48],{"class":47},"srMev"," pdf",[30,50,51],{"class":47}," sales_report.xlsx",[30,53,55],{"class":54},"s-kum"," >",[30,57,58],{"class":47}," \u002Fdev\u002Fnull",[30,60,61],{"class":54}," 2>&1",[30,63,65],{"class":64},"skGVy"," && ",[30,67,68],{"class":36},"pdfinfo",[30,70,71],{"class":47}," sales_report.pdf",[30,73,74],{"class":54}," |",[30,76,77],{"class":36}," grep",[30,79,80],{"class":40}," -E",[30,82,83],{"class":47}," \"Pages|Page size\"\n",[85,86,88],"h2",{"id":87},"prerequisites","Prerequisites",[20,90,92],{"className":22,"code":91,"language":24,"meta":25,"style":25},"pip install pypdfium2 pillow\n",[27,93,94],{"__ignoreMap":25},[30,95,96,99,102,105],{"class":32,"line":33},[30,97,98],{"class":36},"pip",[30,100,101],{"class":47}," install",[30,103,104],{"class":47}," pypdfium2",[30,106,107],{"class":47}," pillow\n",[10,109,110],{},"pypdfium2 ships the PDFium library inside its wheel, so it works the same on Windows, macOS and Linux without installing Poppler or Ghostscript. Pillow handles saving, trimming and resizing. The PDF here is a short A4 landscape sales report exported from Excel with LibreOffice: a summary table with a chart on the first page, then the detail rows.",[85,112,114],{"id":113},"step-1-render-each-page-at-a-chosen-resolution","Step 1 — Render each page at a chosen resolution",[20,116,120],{"className":117,"code":118,"language":119,"meta":25,"style":25},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pypdfium2 as pdfium\n\npdf = pdfium.PdfDocument(\"sales_report.pdf\")\ndpi = 150\nfor i, page in enumerate(pdf, start=1):\n    w_pt, h_pt = page.get_size()\n    image = page.render(scale=dpi \u002F 72).to_pil()\n    image.save(f\"sales_report_p{i}.png\", optimize=True)\n    print(f\"page {i}: {w_pt:.0f} x {h_pt:.0f} pt -> {image.size[0]} x {image.size[1]} px\")\n","python",[27,121,122,136,143,161,172,202,213,240,278],{"__ignoreMap":25},[30,123,124,127,130,133],{"class":32,"line":33},[30,125,126],{"class":54},"import",[30,128,129],{"class":64}," pypdfium2 ",[30,131,132],{"class":54},"as",[30,134,135],{"class":64}," pdfium\n",[30,137,139],{"class":32,"line":138},2,[30,140,142],{"emptyLinePlaceholder":141},true,"\n",[30,144,146,149,152,155,158],{"class":32,"line":145},3,[30,147,148],{"class":64},"pdf ",[30,150,151],{"class":54},"=",[30,153,154],{"class":64}," pdfium.PdfDocument(",[30,156,157],{"class":47},"\"sales_report.pdf\"",[30,159,160],{"class":64},")\n",[30,162,164,167,169],{"class":32,"line":163},4,[30,165,166],{"class":64},"dpi ",[30,168,151],{"class":54},[30,170,171],{"class":40}," 150\n",[30,173,175,178,181,184,187,190,194,196,199],{"class":32,"line":174},5,[30,176,177],{"class":54},"for",[30,179,180],{"class":64}," i, page ",[30,182,183],{"class":54},"in",[30,185,186],{"class":40}," enumerate",[30,188,189],{"class":64},"(pdf, ",[30,191,193],{"class":192},"sa561","start",[30,195,151],{"class":54},[30,197,198],{"class":40},"1",[30,200,201],{"class":64},"):\n",[30,203,205,208,210],{"class":32,"line":204},6,[30,206,207],{"class":64},"    w_pt, h_pt ",[30,209,151],{"class":54},[30,211,212],{"class":64}," page.get_size()\n",[30,214,216,219,221,224,227,229,231,234,237],{"class":32,"line":215},7,[30,217,218],{"class":64},"    image ",[30,220,151],{"class":54},[30,222,223],{"class":64}," page.render(",[30,225,226],{"class":192},"scale",[30,228,151],{"class":54},[30,230,166],{"class":64},[30,232,233],{"class":54},"\u002F",[30,235,236],{"class":40}," 72",[30,238,239],{"class":64},").to_pil()\n",[30,241,243,246,249,252,256,259,262,265,268,271,273,276],{"class":32,"line":242},8,[30,244,245],{"class":64},"    image.save(",[30,247,248],{"class":54},"f",[30,250,251],{"class":47},"\"sales_report_p",[30,253,255],{"class":254},"sSjpA","{",[30,257,258],{"class":64},"i",[30,260,261],{"class":254},"}",[30,263,264],{"class":47},".png\"",[30,266,267],{"class":64},", ",[30,269,270],{"class":192},"optimize",[30,272,151],{"class":54},[30,274,275],{"class":40},"True",[30,277,160],{"class":64},[30,279,281,284,287,289,292,294,296,298,301,303,306,309,311,314,316,319,321,323,326,328,331,334,337,339,341,343,345,347,349,351,354],{"class":32,"line":280},9,[30,282,283],{"class":40},"    print",[30,285,286],{"class":64},"(",[30,288,248],{"class":54},[30,290,291],{"class":47},"\"page ",[30,293,255],{"class":254},[30,295,258],{"class":64},[30,297,261],{"class":254},[30,299,300],{"class":47},": ",[30,302,255],{"class":254},[30,304,305],{"class":64},"w_pt",[30,307,308],{"class":54},":.0f",[30,310,261],{"class":254},[30,312,313],{"class":47}," x ",[30,315,255],{"class":254},[30,317,318],{"class":64},"h_pt",[30,320,308],{"class":54},[30,322,261],{"class":254},[30,324,325],{"class":47}," pt -> ",[30,327,255],{"class":254},[30,329,330],{"class":64},"image.size[",[30,332,333],{"class":40},"0",[30,335,336],{"class":64},"]",[30,338,261],{"class":254},[30,340,313],{"class":47},[30,342,255],{"class":254},[30,344,330],{"class":64},[30,346,198],{"class":40},[30,348,336],{"class":64},[30,350,261],{"class":254},[30,352,353],{"class":47}," px\"",[30,355,160],{"class":64},[10,357,358,359,361,362,365],{},"PDF pages are measured in points, 72 to the inch, so the ",[27,360,226],{}," passed to ",[27,363,364],{},"render"," is the target resolution divided by 72. At 150 DPI an A4 landscape page becomes about 1750 × 1240 pixels, which is sharp on a normal screen and readable when zoomed. The right resolution depends on where the image is going: 96 to 110 DPI is enough for a picture that will be shown at page width on screen, 150 suits images that people will zoom into, and 300 is for printing. Every doubling of the resolution makes four times as many pixels, so the file size and the time to render grow quickly — render at the resolution you need, not the highest you can.",[367,368,376,377,376,381,376,385,376,392,376,401,376,405,376,412,376,416,376,420,376,424,376,428,376,432,376,436,376,440,376,444,376,449,376,453,376,455,376,458,376,460,376,463,376,465,376,468,376,472,376,474,376,478,376,480,376,483,376,485,376,488,376,490,376,493,376,497,376,499,376,502,376,504,376,507,376,509,376,512,376,514,376,517,376,521,376,525,376,529,376,531,376,534,376,536,376,539,376,541,376,544,376,548,376,550,376,553,376,555,376,558,376,560,376,563,376,565],"svg",{"viewBox":369,"role":370,"ariaLabelledBy":371,"xmlns":374,"style":375},"140 0 479 196","img",[372,373],"img-dpi-t","img-dpi-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:479px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[378,379,380],"title",{"id":372},"Choosing a resolution for an A4 page",[382,383,384],"desc",{"id":373},"At 72 DPI an A4 landscape page is about 842 by 595 pixels, suitable only for thumbnails. At 110 DPI it is about 1286 by 909 pixels, fine for screen. At 150 DPI it is about 1754 by 1240 for zooming, and at 300 DPI about 3508 by 2480 for print.",[386,387],"rect",{"x":388,"y":333,"width":389,"height":390,"fill":391},"140","479","196","#ffffff",[386,393],{"x":394,"y":395,"width":396,"height":397,"rx":333,"fill":398,"stroke":399,"style":400},"156.7","16.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[386,402],{"x":403,"y":395,"width":404,"height":397,"rx":333,"fill":398,"stroke":399,"style":400},"188.7","49.1",[406,407,411],"text",{"x":408,"y":409,"style":410},"213.2","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[386,413],{"x":414,"y":395,"width":415,"height":397,"rx":333,"fill":398,"stroke":399,"style":400},"237.8","158.0",[406,417,419],{"x":418,"y":409,"style":410},"316.8","B",[386,421],{"x":422,"y":395,"width":423,"height":397,"rx":333,"fill":398,"stroke":399,"style":400},"395.8","147.1",[406,425,427],{"x":426,"y":409,"style":410},"469.4","C",[386,429],{"x":430,"y":395,"width":431,"height":397,"rx":333,"fill":398,"stroke":399,"style":400},"543.0","60.4",[406,433,435],{"x":434,"y":409,"style":410},"573.1","D",[386,437],{"x":394,"y":438,"width":396,"height":439,"rx":333,"fill":398,"stroke":399,"style":400},"40.0","28.0",[406,441,198],{"x":442,"y":443,"style":410},"172.7","59.0",[386,445],{"x":403,"y":438,"width":404,"height":439,"rx":333,"fill":446,"stroke":447,"style":448},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[406,450,452],{"x":408,"y":443,"style":451},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","DPI",[386,454],{"x":414,"y":438,"width":415,"height":439,"rx":333,"fill":446,"stroke":447,"style":448},[406,456,457],{"x":418,"y":443,"style":451},"Pixels (A4 landscape)",[386,459],{"x":422,"y":438,"width":423,"height":439,"rx":333,"fill":446,"stroke":447,"style":448},[406,461,462],{"x":426,"y":443,"style":451},"Good for",[386,464],{"x":430,"y":438,"width":431,"height":439,"rx":333,"fill":446,"stroke":447,"style":448},[386,466],{"x":394,"y":467,"width":396,"height":439,"rx":333,"fill":398,"stroke":399,"style":400},"68.0",[406,469,471],{"x":442,"y":470,"style":410},"87.0","2",[386,473],{"x":403,"y":467,"width":404,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,475,477],{"x":408,"y":470,"style":476},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","72",[386,479],{"x":414,"y":467,"width":415,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,481,482],{"x":418,"y":470,"style":476},"842 × 595",[386,484],{"x":422,"y":467,"width":423,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,486,487],{"x":426,"y":470,"style":476},"thumbnails",[386,489],{"x":430,"y":467,"width":431,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[386,491],{"x":394,"y":492,"width":396,"height":439,"rx":333,"fill":398,"stroke":399,"style":400},"96.0",[406,494,496],{"x":442,"y":495,"style":410},"115.0","3",[386,498],{"x":403,"y":492,"width":404,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,500,501],{"x":408,"y":495,"style":476},"110",[386,503],{"x":414,"y":492,"width":415,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,505,506],{"x":418,"y":495,"style":476},"1286 × 909",[386,508],{"x":422,"y":492,"width":423,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,510,511],{"x":426,"y":495,"style":476},"chat, email body",[386,513],{"x":430,"y":492,"width":431,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[386,515],{"x":394,"y":516,"width":396,"height":439,"rx":333,"fill":398,"stroke":399,"style":400},"124.0",[406,518,520],{"x":442,"y":519,"style":410},"143.0","4",[386,522],{"x":403,"y":516,"width":404,"height":439,"rx":333,"fill":523,"stroke":524,"style":448},"#d9f4f1","var(--teal,#0f9488)",[406,526,528],{"x":408,"y":519,"style":527},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","150",[386,530],{"x":414,"y":516,"width":415,"height":439,"rx":333,"fill":523,"stroke":524,"style":448},[406,532,533],{"x":418,"y":519,"style":527},"1754 × 1240",[386,535],{"x":422,"y":516,"width":423,"height":439,"rx":333,"fill":523,"stroke":524,"style":448},[406,537,538],{"x":426,"y":519,"style":527},"intranet, zooming in",[386,540],{"x":430,"y":516,"width":431,"height":439,"rx":333,"fill":523,"stroke":524,"style":448},[386,542],{"x":394,"y":543,"width":396,"height":439,"rx":333,"fill":398,"stroke":399,"style":400},"152.0",[406,545,547],{"x":442,"y":546,"style":410},"171.0","5",[386,549],{"x":403,"y":543,"width":404,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,551,552],{"x":408,"y":546,"style":476},"300",[386,554],{"x":414,"y":543,"width":415,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,556,557],{"x":418,"y":546,"style":476},"3508 × 2480",[386,559],{"x":422,"y":543,"width":423,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,561,562],{"x":426,"y":546,"style":476},"print",[386,564],{"x":430,"y":543,"width":431,"height":439,"rx":333,"fill":391,"stroke":399,"style":400},[406,566,567],{"x":434,"y":546,"style":476},"slides",[85,569,571],{"id":570},"step-2-trim-the-white-margins","Step 2 — Trim the white margins",[20,573,575],{"className":117,"code":574,"language":119,"meta":25,"style":25},"from PIL import Image, ImageChops\n\ndef trim(image, pad=24):\n    background = Image.new(image.mode, image.size, (255, 255, 255))\n    box = ImageChops.difference(image, background).getbbox()      # bounding box of non-white pixels\n    if not box:\n        return image\n    l, t, r, b = box\n    return image.crop((max(l - pad, 0), max(t - pad, 0), min(r + pad, image.width), min(b + pad, image.height)))\n\nfirst = pdf[0].render(scale=150 \u002F 72).to_pil()\ntrimmed = trim(first)\ntrimmed.save(\"sales_summary.png\", optimize=True)\nprint(\"full page\", first.size, \"-> trimmed\", trimmed.size)\n",[27,576,577,591,595,614,638,652,663,671,681,741,746,775,786,805],{"__ignoreMap":25},[30,578,579,582,585,588],{"class":32,"line":33},[30,580,581],{"class":54},"from",[30,583,584],{"class":40}," PIL",[30,586,587],{"class":54}," import",[30,589,590],{"class":64}," Image, ImageChops\n",[30,592,593],{"class":32,"line":138},[30,594,142],{"emptyLinePlaceholder":141},[30,596,597,600,604,607,609,612],{"class":32,"line":145},[30,598,599],{"class":54},"def",[30,601,603],{"class":602},"s_Opv"," trim",[30,605,606],{"class":64},"(image, pad",[30,608,151],{"class":54},[30,610,611],{"class":40},"24",[30,613,201],{"class":64},[30,615,616,619,621,624,627,629,631,633,635],{"class":32,"line":163},[30,617,618],{"class":64},"    background ",[30,620,151],{"class":54},[30,622,623],{"class":64}," Image.new(image.mode, image.size, (",[30,625,626],{"class":40},"255",[30,628,267],{"class":64},[30,630,626],{"class":40},[30,632,267],{"class":64},[30,634,626],{"class":40},[30,636,637],{"class":64},"))\n",[30,639,640,643,645,648],{"class":32,"line":174},[30,641,642],{"class":64},"    box ",[30,644,151],{"class":54},[30,646,647],{"class":64}," ImageChops.difference(image, background).getbbox()      ",[30,649,651],{"class":650},"s-wDw","# bounding box of non-white pixels\n",[30,653,654,657,660],{"class":32,"line":204},[30,655,656],{"class":54},"    if",[30,658,659],{"class":54}," not",[30,661,662],{"class":64}," box:\n",[30,664,665,668],{"class":32,"line":215},[30,666,667],{"class":54},"        return",[30,669,670],{"class":64}," image\n",[30,672,673,676,678],{"class":32,"line":242},[30,674,675],{"class":64},"    l, t, r, b ",[30,677,151],{"class":54},[30,679,680],{"class":64}," box\n",[30,682,683,686,689,692,695,698,701,703,706,708,711,713,715,717,719,722,725,728,731,733,736,738],{"class":32,"line":280},[30,684,685],{"class":54},"    return",[30,687,688],{"class":64}," image.crop((",[30,690,691],{"class":40},"max",[30,693,694],{"class":64},"(l ",[30,696,697],{"class":54},"-",[30,699,700],{"class":64}," pad, ",[30,702,333],{"class":40},[30,704,705],{"class":64},"), ",[30,707,691],{"class":40},[30,709,710],{"class":64},"(t ",[30,712,697],{"class":54},[30,714,700],{"class":64},[30,716,333],{"class":40},[30,718,705],{"class":64},[30,720,721],{"class":40},"min",[30,723,724],{"class":64},"(r ",[30,726,727],{"class":54},"+",[30,729,730],{"class":64}," pad, image.width), ",[30,732,721],{"class":40},[30,734,735],{"class":64},"(b ",[30,737,727],{"class":54},[30,739,740],{"class":64}," pad, image.height)))\n",[30,742,744],{"class":32,"line":743},10,[30,745,142],{"emptyLinePlaceholder":141},[30,747,749,752,754,757,759,762,764,766,768,771,773],{"class":32,"line":748},11,[30,750,751],{"class":64},"first ",[30,753,151],{"class":54},[30,755,756],{"class":64}," pdf[",[30,758,333],{"class":40},[30,760,761],{"class":64},"].render(",[30,763,226],{"class":192},[30,765,151],{"class":54},[30,767,528],{"class":40},[30,769,770],{"class":54}," \u002F",[30,772,236],{"class":40},[30,774,239],{"class":64},[30,776,778,781,783],{"class":32,"line":777},12,[30,779,780],{"class":64},"trimmed ",[30,782,151],{"class":54},[30,784,785],{"class":64}," trim(first)\n",[30,787,789,792,795,797,799,801,803],{"class":32,"line":788},13,[30,790,791],{"class":64},"trimmed.save(",[30,793,794],{"class":47},"\"sales_summary.png\"",[30,796,267],{"class":64},[30,798,270],{"class":192},[30,800,151],{"class":54},[30,802,275],{"class":40},[30,804,160],{"class":64},[30,806,808,810,812,815,818,821],{"class":32,"line":807},14,[30,809,562],{"class":40},[30,811,286],{"class":64},[30,813,814],{"class":47},"\"full page\"",[30,816,817],{"class":64},", first.size, ",[30,819,820],{"class":47},"\"-> trimmed\"",[30,822,823],{"class":64},", trimmed.size)\n",[10,825,826,827,830,831,834],{},"A report page rendered as an image carries its print margins with it — a wide white border that wastes space in a chat window and makes the content smaller when the image is scaled to fit. Trimming crops the image to the area that actually contains ink, plus a small padding so the content does not touch the edge. ",[27,828,829],{},"ImageChops.difference"," against a plain white image leaves only the non-white pixels, and ",[27,832,833],{},"getbbox()"," returns the rectangle around them. For a summary page that only half fills the sheet, the trimmed image can be a fraction of the original height, and every pixel of it is content. Here the summary page shrinks from 1754 × 1241 pixels to 993 × 764 — less than 35% of the original area — because the table and chart occupy only the top-left part of the landscape sheet.",[85,836,838],{"id":837},"step-3-png-or-jpeg-and-thumbnails","Step 3 — PNG or JPEG, and thumbnails",[20,840,842],{"className":117,"code":841,"language":119,"meta":25,"style":25},"from pathlib import Path\n\ntrimmed.save(\"sales_summary.jpg\", quality=85, optimize=True)\nfor f in [\"sales_summary.png\", \"sales_summary.jpg\"]:\n    print(f, f\"{Path(f).stat().st_size \u002F 1024:.0f} KB\")\n\nthumb = trimmed.copy()\nthumb.thumbnail((400, 400))                     # keeps the aspect ratio, never enlarges\nthumb.save(\"sales_summary_thumb.png\", optimize=True)\nprint(\"thumbnail\", thumb.size, f\"{Path('sales_summary_thumb.png').stat().st_size \u002F 1024:.0f} KB\")\n\nsmall = trimmed.quantize(colors=64, method=Image.Quantize.MEDIANCUT)   # palette PNG\nsmall.save(\"sales_summary_64.png\", optimize=True)\nprint(\"64-colour PNG\", f\"{Path('sales_summary_64.png').stat().st_size \u002F 1024:.0f} KB\")\n",[27,843,844,856,860,887,908,939,943,953,971,989,1028,1032,1069,1087],{"__ignoreMap":25},[30,845,846,848,851,853],{"class":32,"line":33},[30,847,581],{"class":54},[30,849,850],{"class":64}," pathlib ",[30,852,126],{"class":54},[30,854,855],{"class":64}," Path\n",[30,857,858],{"class":32,"line":138},[30,859,142],{"emptyLinePlaceholder":141},[30,861,862,864,867,869,872,874,877,879,881,883,885],{"class":32,"line":145},[30,863,791],{"class":64},[30,865,866],{"class":47},"\"sales_summary.jpg\"",[30,868,267],{"class":64},[30,870,871],{"class":192},"quality",[30,873,151],{"class":54},[30,875,876],{"class":40},"85",[30,878,267],{"class":64},[30,880,270],{"class":192},[30,882,151],{"class":54},[30,884,275],{"class":40},[30,886,160],{"class":64},[30,888,889,891,894,896,899,901,903,905],{"class":32,"line":163},[30,890,177],{"class":54},[30,892,893],{"class":64}," f ",[30,895,183],{"class":54},[30,897,898],{"class":64}," [",[30,900,794],{"class":47},[30,902,267],{"class":64},[30,904,866],{"class":47},[30,906,907],{"class":64},"]:\n",[30,909,910,912,915,917,920,922,925,927,930,932,934,937],{"class":32,"line":174},[30,911,283],{"class":40},[30,913,914],{"class":64},"(f, ",[30,916,248],{"class":54},[30,918,919],{"class":47},"\"",[30,921,255],{"class":254},[30,923,924],{"class":64},"Path(f).stat().st_size ",[30,926,233],{"class":54},[30,928,929],{"class":40}," 1024",[30,931,308],{"class":54},[30,933,261],{"class":254},[30,935,936],{"class":47}," KB\"",[30,938,160],{"class":64},[30,940,941],{"class":32,"line":204},[30,942,142],{"emptyLinePlaceholder":141},[30,944,945,948,950],{"class":32,"line":215},[30,946,947],{"class":64},"thumb ",[30,949,151],{"class":54},[30,951,952],{"class":64}," trimmed.copy()\n",[30,954,955,958,961,963,965,968],{"class":32,"line":242},[30,956,957],{"class":64},"thumb.thumbnail((",[30,959,960],{"class":40},"400",[30,962,267],{"class":64},[30,964,960],{"class":40},[30,966,967],{"class":64},"))                     ",[30,969,970],{"class":650},"# keeps the aspect ratio, never enlarges\n",[30,972,973,976,979,981,983,985,987],{"class":32,"line":280},[30,974,975],{"class":64},"thumb.save(",[30,977,978],{"class":47},"\"sales_summary_thumb.png\"",[30,980,267],{"class":64},[30,982,270],{"class":192},[30,984,151],{"class":54},[30,986,275],{"class":40},[30,988,160],{"class":64},[30,990,991,993,995,998,1001,1003,1005,1007,1010,1013,1016,1018,1020,1022,1024,1026],{"class":32,"line":743},[30,992,562],{"class":40},[30,994,286],{"class":64},[30,996,997],{"class":47},"\"thumbnail\"",[30,999,1000],{"class":64},", thumb.size, ",[30,1002,248],{"class":54},[30,1004,919],{"class":47},[30,1006,255],{"class":254},[30,1008,1009],{"class":64},"Path(",[30,1011,1012],{"class":47},"'sales_summary_thumb.png'",[30,1014,1015],{"class":64},").stat().st_size ",[30,1017,233],{"class":54},[30,1019,929],{"class":40},[30,1021,308],{"class":54},[30,1023,261],{"class":254},[30,1025,936],{"class":47},[30,1027,160],{"class":64},[30,1029,1030],{"class":32,"line":748},[30,1031,142],{"emptyLinePlaceholder":141},[30,1033,1034,1037,1039,1042,1045,1047,1050,1052,1055,1057,1060,1063,1066],{"class":32,"line":777},[30,1035,1036],{"class":64},"small ",[30,1038,151],{"class":54},[30,1040,1041],{"class":64}," trimmed.quantize(",[30,1043,1044],{"class":192},"colors",[30,1046,151],{"class":54},[30,1048,1049],{"class":40},"64",[30,1051,267],{"class":64},[30,1053,1054],{"class":192},"method",[30,1056,151],{"class":54},[30,1058,1059],{"class":64},"Image.Quantize.",[30,1061,1062],{"class":40},"MEDIANCUT",[30,1064,1065],{"class":64},")   ",[30,1067,1068],{"class":650},"# palette PNG\n",[30,1070,1071,1074,1077,1079,1081,1083,1085],{"class":32,"line":788},[30,1072,1073],{"class":64},"small.save(",[30,1075,1076],{"class":47},"\"sales_summary_64.png\"",[30,1078,267],{"class":64},[30,1080,270],{"class":192},[30,1082,151],{"class":54},[30,1084,275],{"class":40},[30,1086,160],{"class":64},[30,1088,1089,1091,1093,1096,1098,1100,1102,1104,1106,1109,1111,1113,1115,1117,1119,1121],{"class":32,"line":807},[30,1090,562],{"class":40},[30,1092,286],{"class":64},[30,1094,1095],{"class":47},"\"64-colour PNG\"",[30,1097,267],{"class":64},[30,1099,248],{"class":54},[30,1101,919],{"class":47},[30,1103,255],{"class":254},[30,1105,1009],{"class":64},[30,1107,1108],{"class":47},"'sales_summary_64.png'",[30,1110,1015],{"class":64},[30,1112,233],{"class":54},[30,1114,929],{"class":40},[30,1116,308],{"class":54},[30,1118,261],{"class":254},[30,1120,936],{"class":47},[30,1122,160],{"class":64},[10,1124,1125,1126,1129],{},"For report pages — text, table lines and flat-coloured charts on white — PNG is usually the better format: it is lossless, so text stays crisp, and large areas of a single colour compress very well. JPEG is designed for photographs; on text it produces faint smudges around letters, and its files are often no smaller for this kind of content. On the summary page above, the JPEG at quality 85 saves only 2 KB over the PNG (85 KB against 87 KB), and it is the PNG whose text looks clean when zoomed. Use JPEG only when a page contains photographs or large gradients. Thumbnails are best made from the rendered image with Pillow's ",[27,1127,1128],{},"thumbnail",", which keeps the proportions and uses a high-quality filter, rather than rendering the PDF at a very low resolution, where text breaks up into unreadable pixels.",[10,1131,1132],{},"The last lines show the most effective way to make a report image smaller. A rendered page uses only a handful of real colours — black text, a few chart colours, white — plus the grey shades of anti-aliased edges. Reducing it to a 64-colour palette keeps the text smooth while cutting the PNG to a fraction of its full-colour size, which matters when the image is going into an email or a chat message with a size limit. Check the result by eye the first time: a chart with gradients or many colours may need 128 or 256 colours to look right.",[367,1134,376,1140,376,1143,376,1146,376,1150,376,1157,376,1163,376,1168,376,1172,376,1176,376,1180,376,1184,376,1189,376,1194,376,1198,376,1201,376,1204,376,1207],{"viewBox":1135,"role":370,"ariaLabelledBy":1136,"xmlns":374,"style":1139},"4 0 752 199",[1137,1138],"img-format-t","img-format-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[378,1141,1142],{"id":1137},"PNG or JPEG for report pages",[382,1144,1145],{"id":1138},"PNG is lossless, keeps text and lines sharp and compresses flat colour well, so it suits tables and charts. JPEG suits photographs and gradients but blurs the edges of text.",[386,1147],{"x":520,"y":333,"width":1148,"height":1149,"fill":391},"752","199",[386,1151],{"x":1152,"y":395,"width":1153,"height":1154,"rx":1155,"fill":523,"stroke":524,"style":1156},"20.0","351.0","167.0","14","stroke-width:2px",[406,1158,1162],{"x":1159,"y":1160,"style":1161},"195.5","44.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","PNG",[32,1164],{"x1":1165,"y1":1166,"x2":1167,"y2":1166,"stroke":524,"style":400},"36.0","57.0","355.0",[406,1169,1171],{"x":1159,"y":1170,"style":476},"77.0","lossless, text stays sharp",[406,1173,1175],{"x":1159,"y":1174,"style":476},"103.0","flat colours compress well",[406,1177,1179],{"x":1159,"y":1178,"style":476},"129.0","tables, charts, summaries",[406,1181,1183],{"x":1159,"y":1182,"style":476},"155.0","transparent background possible",[386,1185],{"x":1186,"y":395,"width":1153,"height":1154,"rx":1155,"fill":1187,"stroke":1188,"style":1156},"389.0","#fdefd8","var(--gold,#b4740a)",[406,1190,1193],{"x":1191,"y":1160,"style":1192},"564.5","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","JPEG",[32,1195],{"x1":1196,"y1":1166,"x2":1197,"y2":1166,"stroke":1188,"style":400},"405.0","724.0",[406,1199,1200],{"x":1191,"y":1170,"style":476},"lossy, smudges around text",[406,1202,1203],{"x":1191,"y":1174,"style":476},"good for photographs",[406,1205,1206],{"x":1191,"y":1178,"style":476},"pages with images or gradients",[406,1208,1209],{"x":1191,"y":1182,"style":476},"quality 80–90 if used",[85,1211,1213],{"id":1212},"the-command-line-alternative","The command-line alternative",[10,1215,1216,1217,1220],{},"When Poppler is installed — it is on most Linux servers, and available for Windows and macOS — ",[27,1218,1219],{},"pdftoppm"," renders pages without any Python code. It is handy in shell scripts and for checking what a PDF looks like on a server with no screen:",[20,1222,1224],{"className":22,"code":1223,"language":24,"meta":25,"style":25},"pdftoppm -r 110 -png -f 1 -l 1 sales_report.pdf page && ls page*\n",[27,1225,1226],{"__ignoreMap":25},[30,1227,1228,1230,1233,1236,1239,1242,1245,1248,1250,1252,1255,1257,1260,1262],{"class":32,"line":33},[30,1229,1219],{"class":36},[30,1231,1232],{"class":40}," -r",[30,1234,1235],{"class":40}," 110",[30,1237,1238],{"class":40}," -png",[30,1240,1241],{"class":40}," -f",[30,1243,1244],{"class":40}," 1",[30,1246,1247],{"class":40}," -l",[30,1249,1244],{"class":40},[30,1251,71],{"class":47},[30,1253,1254],{"class":47}," page",[30,1256,65],{"class":64},[30,1258,1259],{"class":36},"ls",[30,1261,1254],{"class":47},[30,1263,1264],{"class":40},"*\n",[10,1266,1267,1270,1271,1274,1275,1278,1279,1282],{},[27,1268,1269],{},"-r"," sets the resolution, ",[27,1272,1273],{},"-f"," and ",[27,1276,1277],{},"-l"," the first and last page, and the output files are numbered automatically. The Python libraries ",[27,1280,1281],{},"pdf2image"," wrap this command; pypdfium2 does the same rendering in-process without needing Poppler, which makes it the simpler dependency when the script runs on several kinds of machine. PyMuPDF is another fast option with many extra features, but it is licensed under the AGPL, which matters if the code is distributed.",[367,1284,376,1289,376,1292,376,1295,376,1298,376,1303,376,1309,376,1314,376,1319,376,1324,376,1327,376,1333,376,1337,376,1341,376,1345,376,1348,376,1351,376,1356,376,1359,376,1361,376,1365,376,1368,376,1371,376,1376,376,1379,376,1383,376,1386,376,1389,376,1393,376,1395],{"viewBox":1285,"role":370,"ariaLabelledBy":1286,"xmlns":374,"style":1139},"4 0 752 108",[1287,1288],"img-flow-t","img-flow-d",[378,1290,1291],{"id":1287},"From workbook to images",[382,1293,1294],{"id":1288},"The workbook is exported to PDF, each page is rendered at the chosen resolution, the margins are trimmed, and the result is saved as PNG with an optional small thumbnail.",[386,1296],{"x":520,"y":333,"width":1148,"height":1297,"fill":391},"108",[386,1299],{"x":1152,"y":395,"width":1300,"height":1301,"rx":1302,"fill":398,"stroke":399,"style":1156},"115.2","76.5","12",[406,1304,1308],{"x":1305,"y":1306,"style":1307},"77.6","50.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Workbook",[406,1310,1313],{"x":1305,"y":1311,"style":1312},"69.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","report.xlsx",[32,1315],{"x1":1316,"y1":1317,"x2":1318,"y2":1317,"stroke":447,"style":1156},"140.2","54.2","162.2",[1320,1321],"polygon",{"points":1322,"fill":1323},"166.2,54.2 157.2,59.2 157.2,49.2","#5b5cf0",[386,1325],{"x":1326,"y":395,"width":1300,"height":1301,"rx":1302,"fill":446,"stroke":447,"style":1156},"171.2",[406,1328,1332],{"x":1329,"y":1330,"style":1331},"228.8","41.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","PDF export",[406,1334,1336],{"x":1329,"y":1335,"style":1312},"61.0","LibreOffice or",[406,1338,1340],{"x":1329,"y":1339,"style":1312},"78.5","Excel",[32,1342],{"x1":1343,"y1":1317,"x2":1344,"y2":1317,"stroke":447,"style":1156},"291.4","313.4",[1320,1346],{"points":1347,"fill":1323},"317.4,54.2 308.4,59.2 308.4,49.2",[386,1349],{"x":1350,"y":395,"width":1300,"height":1301,"rx":1302,"fill":1187,"stroke":1188,"style":1156},"322.4",[406,1352,1355],{"x":1353,"y":1330,"style":1354},"380.0","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Render page",[406,1357,1358],{"x":1353,"y":1335,"style":1312},"pypdfium2 at N",[406,1360,452],{"x":1353,"y":1339,"style":1312},[32,1362],{"x1":1363,"y1":1317,"x2":1364,"y2":1317,"stroke":447,"style":1156},"442.6","464.6",[1320,1366],{"points":1367,"fill":1323},"468.6,54.2 459.6,59.2 459.6,49.2",[386,1369],{"x":1370,"y":395,"width":1300,"height":1301,"rx":1302,"fill":523,"stroke":524,"style":1156},"473.6",[406,1372,1375],{"x":1373,"y":1306,"style":1374},"531.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Trim margins",[406,1377,1378],{"x":1373,"y":1311,"style":1312},"Pillow getbbox",[32,1380],{"x1":1381,"y1":1317,"x2":1382,"y2":1317,"stroke":447,"style":1156},"593.8","615.8",[1320,1384],{"points":1385,"fill":1323},"619.8,54.2 610.8,59.2 610.8,49.2",[386,1387],{"x":1388,"y":395,"width":1300,"height":1301,"rx":1302,"fill":398,"stroke":399,"style":1156},"624.8",[406,1390,1392],{"x":1391,"y":1330,"style":1307},"682.4","PNG +",[406,1394,1128],{"x":1391,"y":443,"style":1307},[406,1396,1397],{"x":1391,"y":1339,"style":1312},"ready to share",[85,1399,1401],{"id":1400},"where-the-images-go","Where the images go",[10,1403,1404,1405,1409],{},"Once the summary page is an image, it can travel anywhere a picture can. Embedded in the body of a report email with a content ID, it shows the headline numbers before anyone opens the attachment — see ",[14,1406,1408],{"href":1407},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Fembed-a-chart-image-in-the-body-of-a-report-email\u002F","Embed a Chart Image in the Body of a Report Email",". Posted to a team chat with a webhook, it gives the channel the figures without a download. On an index page of past reports, a row of thumbnails, each linking to its PDF, makes it easy to find the right month by eye. In every case keep the PDF as the authoritative copy and the image as a preview: an image cannot be searched, its text cannot be copied, and readers with screen readers need the PDF or a text summary alongside it.",[85,1411,1413],{"id":1412},"common-pitfalls","Common pitfalls",[1415,1416,1417,1425,1431,1437,1443],"ul",{},[1418,1419,1420,1424],"li",{},[1421,1422,1423],"strong",{},"Rendering at 72 DPI and scaling up."," Text becomes blurry; render at the resolution you need.",[1418,1426,1427,1430],{},[1421,1428,1429],{},"JPEG for text pages."," Use PNG; JPEG smudges the edges of characters.",[1418,1432,1433,1436],{},[1421,1434,1435],{},"Untrimmed margins."," Crop to the content so the image is not mostly white.",[1418,1438,1439,1442],{},[1421,1440,1441],{},"Thumbnails from low-resolution renders."," Render normally, then shrink with Pillow.",[1418,1444,1445,1448],{},[1421,1446,1447],{},"Images instead of the PDF."," Share images as previews alongside the PDF, not instead of it.",[85,1450,1452],{"id":1451},"conclusion","Conclusion",[10,1454,1455,1456,1459,1460,1462],{},"To turn report PDF pages into images, open the PDF with pypdfium2, render each page with ",[27,1457,1458],{},"scale = dpi \u002F 72"," at a resolution suited to where the image will be seen, trim the white margins with Pillow, save as PNG for text and charts, and make thumbnails by shrinking the rendered image. ",[27,1461,1219],{}," does the same from the command line where Poppler is installed.",[85,1464,1466],{"id":1465},"frequently-asked-questions","Frequently asked questions",[10,1468,1469,1472,1473,18],{},[1421,1470,1471],{},"How do I convert a PDF page to PNG in Python without Poppler?","\nUse pypdfium2: ",[27,1474,1475],{},"pdfium.PdfDocument(path)[0].render(scale=150 \u002F 72).to_pil().save(\"page.png\")",[10,1477,1478,1481],{},[1421,1479,1480],{},"What DPI should I use when converting PDF to images?","\nAbout 110 DPI for on-screen viewing, 150 if readers will zoom, and 300 for printing.",[10,1483,1484,1487],{},[1421,1485,1486],{},"Is PNG or JPEG better for report pages?","\nPNG, because it keeps text and lines sharp and compresses areas of flat colour well.",[10,1489,1490,1493,1494,1496,1497,1499],{},[1421,1491,1492],{},"How do I remove the white border from a rendered page?","\nCompare the image with a white image using ",[27,1495,829],{},", take ",[27,1498,833],{}," and crop to that box with a small margin.",[85,1501,1503],{"id":1502},"related","Related",[1415,1505,1506,1512,1519,1526],{},[1418,1507,1508,1509,1511],{},"Up one level: ",[14,1510,17],{"href":16}," — PDF output from Excel.",[1418,1513,1514,1518],{},[14,1515,1517],{"href":1516},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-file-to-pdf-with-python\u002F","Convert an Excel File to PDF with Python"," — producing the PDF.",[1418,1520,1521,1525],{},[14,1522,1524],{"href":1523},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fexport-an-excel-chart-as-an-image\u002F","Export an Excel Chart as an Image with Python"," — just the chart.",[1418,1527,1528,1532],{},[14,1529,1531],{"href":1530},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fsign-a-pdf-report-digitally-with-python\u002F","Sign a PDF Report Digitally with Python"," — sealing the PDF itself.",[1534,1535,1536],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}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 .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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":25,"searchDepth":138,"depth":138,"links":1538},[1539,1540,1541,1542,1543,1544,1545,1546,1547,1548],{"id":87,"depth":138,"text":88},{"id":113,"depth":138,"text":114},{"id":570,"depth":138,"text":571},{"id":837,"depth":138,"text":838},{"id":1212,"depth":138,"text":1213},{"id":1400,"depth":138,"text":1401},{"id":1412,"depth":138,"text":1413},{"id":1451,"depth":138,"text":1452},{"id":1465,"depth":138,"text":1466},{"id":1502,"depth":138,"text":1503},"2026-10-02","Render the pages of a PDF exported from an Excel report as PNG or JPEG images with pypdfium2, which installs with pip and needs no Poppler: choose the resolution for screen, email or print, trim the white margins, make small thumbnails for a report index, and compare file sizes and formats — with pdftoppm as the command-line alternative.","md",[1553,1555,1557,1559],{"q":1471,"a":1554},"Use pypdfium2: pdfium.PdfDocument(path)[0].render(scale=150 \u002F 72).to_pil().save(\"page.png\").",{"q":1480,"a":1556},"About 110 DPI for on-screen viewing, 150 if readers will zoom, and 300 for printing.",{"q":1486,"a":1558},"PNG, because it keeps text and lines sharp and compresses areas of flat colour well.",{"q":1492,"a":1560},"Compare the image with a white image using ImageChops.difference, take getbbox() and crop to that box with a small margin.","pdf pages to images python",null,{"breadcrumb":1564},[1565,1567,1570],{"name":1566,"item":233},"Home",{"name":1568,"item":1569},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":17,"item":16},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images",{"title":1573,"description":1574},"Turn PDF Report Pages into Images with Python","Render PDF report pages to PNG or JPEG with pypdfium2: DPI for screen, email and print, trimming margins, thumbnails, PNG versus JPEG sizes and the pdftoppm alternative.","turn-pdf-report-pages-into-images","automating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fturn-pdf-report-pages-into-images\u002Findex",[1578,1579],"pdf-export","charts-and-images","how-to","y1-XDxeXabvaw5Q9yBnupJcpqpVbCvyfrnrkcZHubN4",[1583,1586],{"title":1531,"path":1584,"stem":1585,"children":-1},"\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",{"title":1587,"path":1588,"stem":1589,"children":-1},"Generating Excel Reports from Templates","\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates","automating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Findex",[1591,1593],{"path":1592,"title":17},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf",{"path":1594,"title":1568},"\u002Fautomating-reporting-workflows",[],[1597,1600,1603,1606,1609,1612],{"path":1598,"title":1599,"kind":1580},"\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":1601,"title":1602,"kind":1580},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-one-pdf-per-excel-row","Generate One PDF per Excel Row",{"path":1604,"title":1605,"kind":1580},"\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":1607,"title":1608,"kind":1580},"\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":1610,"title":1611,"kind":1580},"\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":1613,"title":1517,"kind":1580},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-file-to-pdf-with-python",1790972137885]