[{"data":1,"prerenderedAt":814},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002Fexporting-multiple-qgis-layouts-to-pdf":3},{"id":4,"title":5,"body":6,"description":807,"extension":808,"meta":809,"navigation":85,"path":810,"seo":811,"stem":812,"__hash__":813},"docs\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002Fexporting-multiple-qgis-layouts-to-pdf\u002Findex.md","Exporting Multiple QGIS Layouts to PDF Programmatically",{"type":7,"value":8,"toc":800},"minimark",[9,13,28,33,36,486,490,543,547,640,644,647,743,747,750,793,796],[10,11,5],"h1",{"id":12},"exporting-multiple-qgis-layouts-to-pdf-programmatically",[14,15,16,17,21,22,27],"p",{},"To export multiple QGIS layouts to PDF programmatically, iterate through the active project’s layout manager using PyQGIS and call ",[18,19,20],"code",{},"QgsLayoutExporter.exportToPdf()"," for each composition. This approach removes manual UI clicks, enforces consistent file naming, and scales reliably for ",[23,24,26],"a",{"href":25},"\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002F","Automated Map Layout Generation"," pipelines.",[29,30,32],"h2",{"id":31},"complete-pyqgis-script","Complete PyQGIS Script",[14,34,35],{},"Run this directly in the QGIS Python Console or embed it in a standalone script:",[37,38,43],"pre",{"className":39,"code":40,"language":41,"meta":42,"style":42},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject, QgsLayoutExporter\nimport os\nimport re\n\ndef export_all_layouts_to_pdf(output_dir: str) -> None:\n \"\"\"Exports every print layout in the active QGIS project to individual PDFs.\"\"\"\n os.makedirs(output_dir, exist_ok=True)\n\n project = QgsProject.instance()\n manager = project.layoutManager()\n layouts = manager.printLayouts()\n\n if not layouts:\n print(\"No print layouts found in the current project.\")\n return\n\n for layout in layouts:\n name = layout.name()\n # Sanitize filename for cross-platform safety\n safe_name = re.sub(r'[^\\w\\-_ ]', '', name).strip()\n pdf_path = os.path.join(output_dir, f\"{safe_name}.pdf\")\n\n exporter = QgsLayoutExporter(layout)\n settings = QgsLayoutExporter.PdfExportSettings()\n settings.dpi = 300\n settings.forceVectorOutput = True\n\n result, error = exporter.exportToPdf(pdf_path, settings)\n if result == QgsLayoutExporter.Success:\n print(f\"✅ Exported: {pdf_path}\")\n else:\n print(f\"❌ Failed: {name} | Error: {error}\")\n\n# Usage in QGIS Python Console:\n# export_all_layouts_to_pdf(r\"C:\\QGIS_Exports\\PDFs\")\n","python","",[18,44,45,64,72,80,87,113,120,139,144,155,166,177,182,194,208,214,219,233,244,251,295,326,331,342,353,364,375,380,391,405,428,436,469,474,480],{"__ignoreMap":42},[46,47,50,54,58,61],"span",{"class":48,"line":49},"line",1,[46,51,53],{"class":52},"snl16","from",[46,55,57],{"class":56},"s95oV"," qgis.core ",[46,59,60],{"class":52},"import",[46,62,63],{"class":56}," QgsProject, QgsLayoutExporter\n",[46,65,67,69],{"class":48,"line":66},2,[46,68,60],{"class":52},[46,70,71],{"class":56}," os\n",[46,73,75,77],{"class":48,"line":74},3,[46,76,60],{"class":52},[46,78,79],{"class":56}," re\n",[46,81,83],{"class":48,"line":82},4,[46,84,86],{"emptyLinePlaceholder":85},true,"\n",[46,88,90,93,97,100,104,107,110],{"class":48,"line":89},5,[46,91,92],{"class":52},"def",[46,94,96],{"class":95},"svObZ"," export_all_layouts_to_pdf",[46,98,99],{"class":56},"(output_dir: ",[46,101,103],{"class":102},"sDLfK","str",[46,105,106],{"class":56},") -> ",[46,108,109],{"class":102},"None",[46,111,112],{"class":56},":\n",[46,114,116],{"class":48,"line":115},6,[46,117,119],{"class":118},"sU2Wk"," \"\"\"Exports every print layout in the active QGIS project to individual PDFs.\"\"\"\n",[46,121,123,126,130,133,136],{"class":48,"line":122},7,[46,124,125],{"class":56}," os.makedirs(output_dir, ",[46,127,129],{"class":128},"s9osk","exist_ok",[46,131,132],{"class":52},"=",[46,134,135],{"class":102},"True",[46,137,138],{"class":56},")\n",[46,140,142],{"class":48,"line":141},8,[46,143,86],{"emptyLinePlaceholder":85},[46,145,147,150,152],{"class":48,"line":146},9,[46,148,149],{"class":56}," project ",[46,151,132],{"class":52},[46,153,154],{"class":56}," QgsProject.instance()\n",[46,156,158,161,163],{"class":48,"line":157},10,[46,159,160],{"class":56}," manager ",[46,162,132],{"class":52},[46,164,165],{"class":56}," project.layoutManager()\n",[46,167,169,172,174],{"class":48,"line":168},11,[46,170,171],{"class":56}," layouts ",[46,173,132],{"class":52},[46,175,176],{"class":56}," manager.printLayouts()\n",[46,178,180],{"class":48,"line":179},12,[46,181,86],{"emptyLinePlaceholder":85},[46,183,185,188,191],{"class":48,"line":184},13,[46,186,187],{"class":52}," if",[46,189,190],{"class":52}," not",[46,192,193],{"class":56}," layouts:\n",[46,195,197,200,203,206],{"class":48,"line":196},14,[46,198,199],{"class":102}," print",[46,201,202],{"class":56},"(",[46,204,205],{"class":118},"\"No print layouts found in the current project.\"",[46,207,138],{"class":56},[46,209,211],{"class":48,"line":210},15,[46,212,213],{"class":52}," return\n",[46,215,217],{"class":48,"line":216},16,[46,218,86],{"emptyLinePlaceholder":85},[46,220,222,225,228,231],{"class":48,"line":221},17,[46,223,224],{"class":52}," for",[46,226,227],{"class":56}," layout ",[46,229,230],{"class":52},"in",[46,232,193],{"class":56},[46,234,236,239,241],{"class":48,"line":235},18,[46,237,238],{"class":56}," name ",[46,240,132],{"class":52},[46,242,243],{"class":56}," layout.name()\n",[46,245,247],{"class":48,"line":246},19,[46,248,250],{"class":249},"sAwPA"," # Sanitize filename for cross-platform safety\n",[46,252,254,257,259,262,265,268,271,274,277,281,284,286,289,292],{"class":48,"line":253},20,[46,255,256],{"class":56}," safe_name ",[46,258,132],{"class":52},[46,260,261],{"class":56}," re.sub(",[46,263,264],{"class":52},"r",[46,266,267],{"class":118},"'",[46,269,270],{"class":102},"[",[46,272,273],{"class":52},"^",[46,275,276],{"class":102},"\\w",[46,278,280],{"class":279},"sRjNt","\\-",[46,282,283],{"class":102},"_ ]",[46,285,267],{"class":118},[46,287,288],{"class":56},", ",[46,290,291],{"class":118},"''",[46,293,294],{"class":56},", name).strip()\n",[46,296,298,301,303,306,309,312,315,318,321,324],{"class":48,"line":297},21,[46,299,300],{"class":56}," pdf_path ",[46,302,132],{"class":52},[46,304,305],{"class":56}," os.path.join(output_dir, ",[46,307,308],{"class":52},"f",[46,310,311],{"class":118},"\"",[46,313,314],{"class":102},"{",[46,316,317],{"class":56},"safe_name",[46,319,320],{"class":102},"}",[46,322,323],{"class":118},".pdf\"",[46,325,138],{"class":56},[46,327,329],{"class":48,"line":328},22,[46,330,86],{"emptyLinePlaceholder":85},[46,332,334,337,339],{"class":48,"line":333},23,[46,335,336],{"class":56}," exporter ",[46,338,132],{"class":52},[46,340,341],{"class":56}," QgsLayoutExporter(layout)\n",[46,343,345,348,350],{"class":48,"line":344},24,[46,346,347],{"class":56}," settings ",[46,349,132],{"class":52},[46,351,352],{"class":56}," QgsLayoutExporter.PdfExportSettings()\n",[46,354,356,359,361],{"class":48,"line":355},25,[46,357,358],{"class":56}," settings.dpi ",[46,360,132],{"class":52},[46,362,363],{"class":102}," 300\n",[46,365,367,370,372],{"class":48,"line":366},26,[46,368,369],{"class":56}," settings.forceVectorOutput ",[46,371,132],{"class":52},[46,373,374],{"class":102}," True\n",[46,376,378],{"class":48,"line":377},27,[46,379,86],{"emptyLinePlaceholder":85},[46,381,383,386,388],{"class":48,"line":382},28,[46,384,385],{"class":56}," result, error ",[46,387,132],{"class":52},[46,389,390],{"class":56}," exporter.exportToPdf(pdf_path, settings)\n",[46,392,394,396,399,402],{"class":48,"line":393},29,[46,395,187],{"class":52},[46,397,398],{"class":56}," result ",[46,400,401],{"class":52},"==",[46,403,404],{"class":56}," QgsLayoutExporter.Success:\n",[46,406,408,410,412,414,417,419,422,424,426],{"class":48,"line":407},30,[46,409,199],{"class":102},[46,411,202],{"class":56},[46,413,308],{"class":52},[46,415,416],{"class":118},"\"✅ Exported: ",[46,418,314],{"class":102},[46,420,421],{"class":56},"pdf_path",[46,423,320],{"class":102},[46,425,311],{"class":118},[46,427,138],{"class":56},[46,429,431,434],{"class":48,"line":430},31,[46,432,433],{"class":52}," else",[46,435,112],{"class":56},[46,437,439,441,443,445,448,450,453,455,458,460,463,465,467],{"class":48,"line":438},32,[46,440,199],{"class":102},[46,442,202],{"class":56},[46,444,308],{"class":52},[46,446,447],{"class":118},"\"❌ Failed: ",[46,449,314],{"class":102},[46,451,452],{"class":56},"name",[46,454,320],{"class":102},[46,456,457],{"class":118}," | Error: ",[46,459,314],{"class":102},[46,461,462],{"class":56},"error",[46,464,320],{"class":102},[46,466,311],{"class":118},[46,468,138],{"class":56},[46,470,472],{"class":48,"line":471},33,[46,473,86],{"emptyLinePlaceholder":85},[46,475,477],{"class":48,"line":476},34,[46,478,479],{"class":249},"# Usage in QGIS Python Console:\n",[46,481,483],{"class":48,"line":482},35,[46,484,485],{"class":249},"# export_all_layouts_to_pdf(r\"C:\\QGIS_Exports\\PDFs\")\n",[29,487,489],{"id":488},"compatibility-requirements","Compatibility & Requirements",[491,492,493,509,519,529],"ul",{},[494,495,496,500,501,504,505,508],"li",{},[497,498,499],"strong",{},"QGIS Version:"," 3.10+ (LTR 3.28+ recommended). The ",[18,502,503],{},"QgsLayoutExporter"," API replaces the legacy ",[18,506,507],{},"QgsComposition"," workflow from QGIS 2.x.",[494,510,511,514,515,518],{},[497,512,513],{},"Python Environment:"," Runs natively in the QGIS Python Console (Python 3.8+). Standalone scripts require explicit ",[18,516,517],{},"QgsApplication"," bootstrapping (see headless section below).",[494,520,521,524,525,528],{},[497,522,523],{},"Path Handling:"," Windows requires raw strings (",[18,526,527],{},"r\"C:\\path\"",") or escaped backslashes. Linux\u002FmacOS accept standard POSIX paths. Verify directory write permissions before execution.",[494,530,531,534,535,538,539,542],{},[497,532,533],{},"Output Quality:"," ",[18,536,537],{},"settings.forceVectorOutput = True"," preserves crisp vector geometry. Raster layers (satellite imagery, hillshades) still render at the configured ",[18,540,541],{},"dpi"," value.",[29,544,546],{"id":545},"troubleshooting-common-errors","Troubleshooting Common Errors",[548,549,550,566],"table",{},[551,552,553],"thead",{},[554,555,556,560,563],"tr",{},[557,558,559],"th",{},"Error Code",[557,561,562],{},"Likely Cause",[557,564,565],{},"Fix",[567,568,569,587,604,619],"tbody",{},[554,570,571,581,584],{},[572,573,574,577,578],"td",{},[18,575,576],{},"Canceled"," \u002F ",[18,579,580],{},"FileError",[572,582,583],{},"Directory locked by antivirus, cloud sync, or missing write permissions",[572,585,586],{},"Export to a local temp folder first, then move files. Disable real-time scanning for the target path.",[554,588,589,594,597],{},[572,590,591],{},[18,592,593],{},"PrintError",[572,595,596],{},"Uninitialized layout or missing page dimensions",[572,598,599,600,603],{},"Call ",[18,601,602],{},"layout.initializeDefaults()"," on dynamically created layouts before exporting.",[554,605,606,609,612],{},[572,607,608],{},"Blank\u002FPartial PDF",[572,610,611],{},"Broken data sources or missing system fonts",[572,613,614,615,618],{},"Validate layers with ",[18,616,617],{},"layer.isValid()"," before export. Install required fonts or enable font fallback in QGIS settings.",[554,620,621,626,629],{},[572,622,623],{},[18,624,625],{},"OutOfMemory",[572,627,628],{},"High DPI + complex atlas pages",[572,630,631,632,635,636,639],{},"Lower ",[18,633,634],{},"settings.dpi"," to 150 for tests. Insert ",[18,637,638],{},"QgsApplication.processEvents()"," between iterations to flush Qt event queues and free RAM.",[29,641,643],{"id":642},"running-headless-cicd-docker","Running Headless (CI\u002FCD & Docker)",[14,645,646],{},"For automated pipelines, GitHub Actions, or cron jobs, you must initialize the QGIS application environment before loading the project:",[37,648,650],{"className":39,"code":649,"language":41,"meta":42,"style":42},"from qgis.core import QgsApplication, QgsProject\n\n# 1. Bootstrap QGIS (adjust prefix path to your OS\u002Finstall)\nQgsApplication.setPrefixPath(\"\u002Fusr\u002Fshare\u002Fqgis\", True)\nQgsApplication.initQgis()\n\n# 2. Load project & run export\nproject = QgsProject.instance()\nproject.read(\"\u002Fpath\u002Fto\u002Fyour\u002Fproject.qgz\")\nexport_all_layouts_to_pdf(\"\u002Fpath\u002Fto\u002Foutput\")\n\n# 3. Clean exit\nQgsApplication.exitQgis()\n",[18,651,652,663,667,672,686,691,695,700,709,719,729,733,738],{"__ignoreMap":42},[46,653,654,656,658,660],{"class":48,"line":49},[46,655,53],{"class":52},[46,657,57],{"class":56},[46,659,60],{"class":52},[46,661,662],{"class":56}," QgsApplication, QgsProject\n",[46,664,665],{"class":48,"line":66},[46,666,86],{"emptyLinePlaceholder":85},[46,668,669],{"class":48,"line":74},[46,670,671],{"class":249},"# 1. Bootstrap QGIS (adjust prefix path to your OS\u002Finstall)\n",[46,673,674,677,680,682,684],{"class":48,"line":82},[46,675,676],{"class":56},"QgsApplication.setPrefixPath(",[46,678,679],{"class":118},"\"\u002Fusr\u002Fshare\u002Fqgis\"",[46,681,288],{"class":56},[46,683,135],{"class":102},[46,685,138],{"class":56},[46,687,688],{"class":48,"line":89},[46,689,690],{"class":56},"QgsApplication.initQgis()\n",[46,692,693],{"class":48,"line":115},[46,694,86],{"emptyLinePlaceholder":85},[46,696,697],{"class":48,"line":122},[46,698,699],{"class":249},"# 2. Load project & run export\n",[46,701,702,705,707],{"class":48,"line":141},[46,703,704],{"class":56},"project ",[46,706,132],{"class":52},[46,708,154],{"class":56},[46,710,711,714,717],{"class":48,"line":146},[46,712,713],{"class":56},"project.read(",[46,715,716],{"class":118},"\"\u002Fpath\u002Fto\u002Fyour\u002Fproject.qgz\"",[46,718,138],{"class":56},[46,720,721,724,727],{"class":48,"line":157},[46,722,723],{"class":56},"export_all_layouts_to_pdf(",[46,725,726],{"class":118},"\"\u002Fpath\u002Fto\u002Foutput\"",[46,728,138],{"class":56},[46,730,731],{"class":48,"line":168},[46,732,86],{"emptyLinePlaceholder":85},[46,734,735],{"class":48,"line":179},[46,736,737],{"class":249},"# 3. Clean exit\n",[46,739,740],{"class":48,"line":184},[46,741,742],{"class":56},"QgsApplication.exitQgis()\n",[29,744,746],{"id":745},"performance-best-practices","Performance & Best Practices",[14,748,749],{},"Batch rendering can bottleneck on disk I\u002FO and Qt threads. Optimize throughput by:",[491,751,752,762,772,787],{},[494,753,754,757,758,761],{},[497,755,756],{},"Pre-validating paths:"," Use ",[18,759,760],{},"os.path.abspath()"," to resolve relative directories and prevent silent export failures.",[494,763,764,767,768,771],{},[497,765,766],{},"Disabling canvas rendering:"," When running inside the QGIS desktop, call ",[18,769,770],{},"iface.mapCanvas().setRenderFlag(False)"," to free GPU\u002FCPU cycles for background exports.",[494,773,774,777,778,781,782,786],{},[497,775,776],{},"Structured logging:"," Replace ",[18,779,780],{},"print()"," statements with CSV or JSON logging for reliable audit trails in production ",[23,783,785],{"href":784},"\u002Fspatial-data-processing-automation\u002F","Spatial Data Processing & Automation"," environments.",[494,788,789,792],{},[497,790,791],{},"CRS alignment:"," Ensure all map items share a consistent projected coordinate system. Mixed CRS definitions force on-the-fly transformations that can distort scale bars and grid lines during export.",[14,794,795],{},"Standardizing DPI, output paths, and error handling removes manual variability and guarantees cartographic consistency across team deliverables.",[797,798,799],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sRjNt, html code.shiki .sRjNt{--shiki-default:#85E89D;--shiki-default-font-weight:bold}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);}",{"title":42,"searchDepth":66,"depth":66,"links":801},[802,803,804,805,806],{"id":31,"depth":66,"text":32},{"id":488,"depth":66,"text":489},{"id":545,"depth":66,"text":546},{"id":642,"depth":66,"text":643},{"id":745,"depth":66,"text":746},"To export multiple QGIS layouts to PDF programmatically, iterate through the active project’s layout manager using PyQGIS and call QgsLayoutExporter.exportToPdf() for each composition. This approach removes manual UI clicks, enforces consistent file naming, and scales reliably for Automated Map Layout Generation pipelines.","md",{},"\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002Fexporting-multiple-qgis-layouts-to-pdf",{"title":5,"description":807},"spatial-data-processing-automation\u002Fautomated-map-layout-generation\u002Fexporting-multiple-qgis-layouts-to-pdf\u002Findex","OvIOF54lQE5EY1vxflZMBB4kZ7LYZXNhxN_6HgZ7BTc",1777824788985]