[{"data":1,"prerenderedAt":2766},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data":3,"surround:\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data":2732,"ancestors:\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data":2740,"children:\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data":2744,"related:\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data":2745},{"id":4,"title":5,"body":6,"dateModified":2700,"datePublished":2700,"description":2701,"extension":2702,"faq":2703,"keyword":2712,"kind":2713,"meta":2714,"navigation":99,"path":2722,"seo":2723,"slug":2726,"stem":2727,"topics":2728,"type":2730,"__hash__":2731},"docs\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data\u002Findex.md","Build a Word Report with Tables from Excel Data",{"type":7,"value":8,"toc":2688},"minimark",[9,24,29,64,67,71,360,363,530,534,537,1052,1067,1293,1297,1300,1802,1809,1813,1816,2146,2157,2240,2244,2247,2564,2567,2571,2619,2623,2626,2630,2636,2642,2648,2654,2658,2685],[10,11,12,13,18,19,23],"p",{},"Plenty of reporting still ends in Word: a board paper, a monthly management report, a client review — documents with prose around the numbers, which people want to edit, comment on and track changes in before they go out. Copying tables from Excel into Word by hand every month is slow, error-prone and produces tables that break across pages badly. python-docx can write the whole document from your data instead. This page reads an Excel workbook with pandas, writes a report with headings and commentary generated from the figures, converts DataFrames into Word tables that look deliberate — a table style, right-aligned numbers, fixed column widths, a header row repeated on every page — puts a wide table in its own landscape section, inserts a chart with a caption, and checks the result. For one document per row — letters and statements — see ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-word-documents-from-excel-rows\u002F","Generate Word Documents from Excel Rows with Python","; this page is about one report built from a whole dataset. It belongs to ",[14,20,22],{"href":21},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002F","Generating Excel Reports from Templates",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install python-docx pandas openpyxl matplotlib\n","bash","",[37,38,39],"code",{"__ignoreMap":35},[40,41,44,48,52,55,58,61],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sMTad","pip",[40,49,51],{"class":50},"srMev"," install",[40,53,54],{"class":50}," python-docx",[40,56,57],{"class":50}," pandas",[40,59,60],{"class":50}," openpyxl",[40,62,63],{"class":50}," matplotlib\n",[10,65,66],{},"The input is a workbook with one row per region and month — revenue, cost and order counts for six months. LibreOffice is used at the end only to render the document to PDF for a page-level check.",[25,68,70],{"id":69},"step-1-summarise-the-data","Step 1 — Summarise the data",[30,72,76],{"className":73,"code":74,"language":75,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nmonthly = pd.read_excel(\"regional_h1.xlsx\", sheet_name=\"Monthly\")\nmonthly[\"Margin\"] = monthly[\"Revenue\"] - monthly[\"Cost\"]\nsummary = (monthly.groupby(\"Region\", as_index=False)[[\"Revenue\", \"Cost\", \"Margin\", \"Orders\"]].sum()\n           .sort_values(\"Revenue\", ascending=False))\nsummary[\"Margin %\"] = summary[\"Margin\"] \u002F summary[\"Revenue\"]\nwide = monthly.pivot_table(index=\"Region\", columns=monthly[\"Month\"].dt.strftime(\"%b\"), values=\"Revenue\", sort=False)\ntotal = summary[\"Revenue\"].sum()\nprint(summary.round(3).to_string(index=False))\n","python",[37,77,78,94,101,131,164,210,230,259,322,337],{"__ignoreMap":35},[40,79,80,84,88,91],{"class":42,"line":43},[40,81,83],{"class":82},"s-kum","import",[40,85,87],{"class":86},"skGVy"," pandas ",[40,89,90],{"class":82},"as",[40,92,93],{"class":86}," pd\n",[40,95,97],{"class":42,"line":96},2,[40,98,100],{"emptyLinePlaceholder":99},true,"\n",[40,102,104,107,110,113,116,119,123,125,128],{"class":42,"line":103},3,[40,105,106],{"class":86},"monthly ",[40,108,109],{"class":82},"=",[40,111,112],{"class":86}," pd.read_excel(",[40,114,115],{"class":50},"\"regional_h1.xlsx\"",[40,117,118],{"class":86},", ",[40,120,122],{"class":121},"sa561","sheet_name",[40,124,109],{"class":82},[40,126,127],{"class":50},"\"Monthly\"",[40,129,130],{"class":86},")\n",[40,132,134,137,140,143,145,148,151,153,156,158,161],{"class":42,"line":133},4,[40,135,136],{"class":86},"monthly[",[40,138,139],{"class":50},"\"Margin\"",[40,141,142],{"class":86},"] ",[40,144,109],{"class":82},[40,146,147],{"class":86}," monthly[",[40,149,150],{"class":50},"\"Revenue\"",[40,152,142],{"class":86},[40,154,155],{"class":82},"-",[40,157,147],{"class":86},[40,159,160],{"class":50},"\"Cost\"",[40,162,163],{"class":86},"]\n",[40,165,167,170,172,175,178,180,183,185,189,192,194,196,198,200,202,204,207],{"class":42,"line":166},5,[40,168,169],{"class":86},"summary ",[40,171,109],{"class":82},[40,173,174],{"class":86}," (monthly.groupby(",[40,176,177],{"class":50},"\"Region\"",[40,179,118],{"class":86},[40,181,182],{"class":121},"as_index",[40,184,109],{"class":82},[40,186,188],{"class":187},"sP0c6","False",[40,190,191],{"class":86},")[[",[40,193,150],{"class":50},[40,195,118],{"class":86},[40,197,160],{"class":50},[40,199,118],{"class":86},[40,201,139],{"class":50},[40,203,118],{"class":86},[40,205,206],{"class":50},"\"Orders\"",[40,208,209],{"class":86},"]].sum()\n",[40,211,213,216,218,220,223,225,227],{"class":42,"line":212},6,[40,214,215],{"class":86},"           .sort_values(",[40,217,150],{"class":50},[40,219,118],{"class":86},[40,221,222],{"class":121},"ascending",[40,224,109],{"class":82},[40,226,188],{"class":187},[40,228,229],{"class":86},"))\n",[40,231,233,236,239,241,243,246,248,250,253,255,257],{"class":42,"line":232},7,[40,234,235],{"class":86},"summary[",[40,237,238],{"class":50},"\"Margin %\"",[40,240,142],{"class":86},[40,242,109],{"class":82},[40,244,245],{"class":86}," summary[",[40,247,139],{"class":50},[40,249,142],{"class":86},[40,251,252],{"class":82},"\u002F",[40,254,245],{"class":86},[40,256,150],{"class":50},[40,258,163],{"class":86},[40,260,262,265,267,270,273,275,277,279,282,284,286,289,292,295,299,301,304,307,309,311,313,316,318,320],{"class":42,"line":261},8,[40,263,264],{"class":86},"wide ",[40,266,109],{"class":82},[40,268,269],{"class":86}," monthly.pivot_table(",[40,271,272],{"class":121},"index",[40,274,109],{"class":82},[40,276,177],{"class":50},[40,278,118],{"class":86},[40,280,281],{"class":121},"columns",[40,283,109],{"class":82},[40,285,136],{"class":86},[40,287,288],{"class":50},"\"Month\"",[40,290,291],{"class":86},"].dt.strftime(",[40,293,294],{"class":50},"\"",[40,296,298],{"class":297},"sSjpA","%b",[40,300,294],{"class":50},[40,302,303],{"class":86},"), ",[40,305,306],{"class":121},"values",[40,308,109],{"class":82},[40,310,150],{"class":50},[40,312,118],{"class":86},[40,314,315],{"class":121},"sort",[40,317,109],{"class":82},[40,319,188],{"class":187},[40,321,130],{"class":86},[40,323,325,328,330,332,334],{"class":42,"line":324},9,[40,326,327],{"class":86},"total ",[40,329,109],{"class":82},[40,331,245],{"class":86},[40,333,150],{"class":50},[40,335,336],{"class":86},"].sum()\n",[40,338,340,343,346,349,352,354,356,358],{"class":42,"line":339},10,[40,341,342],{"class":187},"print",[40,344,345],{"class":86},"(summary.round(",[40,347,348],{"class":187},"3",[40,350,351],{"class":86},").to_string(",[40,353,272],{"class":121},[40,355,109],{"class":82},[40,357,188],{"class":187},[40,359,229],{"class":86},[10,361,362],{},"Central and South are almost level on revenue, but South's margin is ten points higher — the kind of contrast the commentary should draw out. The summary table and the month-by-region pivot are ordinary DataFrames. Everything the document says will come from them — including the sentences — so the document can never contradict its own tables.",[364,365,373,374,373,378,373,382,373,390,373,400,373,411,373,417,373,421,373,425,373,428,373,433,373,442,373,447,373,453,373,459,373,463,373,468,373,470,373,473,373,477,373,480,373,485,373,492,373,496,373,499,373,501,373,504,373,508,373,511,373,516,373,523,373,527],"svg",{"viewBox":366,"role":367,"ariaLabelledBy":368,"xmlns":371,"style":372},"4 0 752 272","img",[369,370],"wd-format-t","wd-format-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  ",[375,376,377],"title",{"id":369},"Word, PDF or the workbook itself",[379,380,381],"desc",{"id":370},"If readers will edit, comment on or track changes in the report before it goes out, produce a Word document. If it is final and must look identical everywhere, produce a PDF. If readers need to work with the numbers, send the workbook, possibly alongside either.",[383,384],"rect",{"x":385,"y":386,"width":387,"height":388,"fill":389},"4","0","752","272","#ffffff",[383,391],{"x":392,"y":393,"width":394,"height":395,"rx":396,"fill":397,"stroke":398,"style":399},"240.0","16.0","280.0","75.5","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[401,402,404],"g",{"transform":403},"translate(368.0,29.0) scale(1.000)",[405,406],"path",{"d":407,"fill":408,"stroke":409,"style":410},"M9 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z M2.5 20c0-3.6 2.9-6 6.5-6s6.5 2.4 6.5 6 M16 4.5a3.3 3.3 0 0 1 0 6.3 M18 14.3c2.2.7 3.5 2.8 3.5 5.7","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[412,413,416],"text",{"x":414,"y":395,"style":415},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","What will readers do with it?",[42,418],{"x1":414,"y1":419,"x2":414,"y2":420,"stroke":398,"style":399},"91.5","111.5",[42,422],{"x1":423,"y1":420,"x2":424,"y2":420,"stroke":398,"style":399},"134.7","625.3",[42,426],{"x1":423,"y1":420,"x2":423,"y2":427,"stroke":398,"style":399},"157.5",[429,430],"polygon",{"points":431,"fill":432},"134.7,161.5 129.7,152.5 139.7,152.5","#5b5cf0",[383,434],{"x":435,"y":436,"width":437,"height":438,"rx":439,"fill":389,"stroke":440,"style":441},"72.6","121.5","124.1","21.0","10","var(--line,#cdd5e6)","stroke-width:1.2px",[412,443,446],{"x":423,"y":444,"style":445},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","edit and comment",[383,448],{"x":449,"y":450,"width":451,"height":452,"rx":396,"fill":397,"stroke":398,"style":399},"20.0","163.5","229.3","93.0",[401,454,456],{"transform":455},"translate(122.7,176.5) scale(1.000)",[405,457],{"d":458,"fill":408,"stroke":409,"style":410},"M6 3h9l4 4v14H6z M15 3v4h4",[412,460,462],{"x":423,"y":461,"style":415},"223.0","Word document",[412,464,467],{"x":423,"y":465,"style":466},"242.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","python-docx",[42,469],{"x1":414,"y1":420,"x2":414,"y2":427,"stroke":398,"style":399},[429,471],{"points":472,"fill":432},"380.0,161.5 375.0,152.5 385.0,152.5",[383,474],{"x":475,"y":436,"width":476,"height":438,"rx":439,"fill":389,"stroke":440,"style":441},"343.5","73.1",[412,478,479],{"x":414,"y":444,"style":445},"read only",[383,481],{"x":482,"y":450,"width":451,"height":452,"rx":396,"fill":483,"stroke":484,"style":399},"265.3","#d9f4f1","var(--teal,#0f9488)",[401,486,488],{"transform":487},"translate(368.0,176.5) scale(1.000)",[405,489],{"d":490,"fill":408,"stroke":491,"style":410},"M6 3h9l4 4v14H6z M15 3v4h4 M9 12h6 M9 15.5h6 M9 19h4","var(--teal-ink,#0b6157)",[412,493,495],{"x":414,"y":461,"style":494},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","PDF",[412,497,498],{"x":414,"y":465,"style":466},"fixed layout",[42,500],{"x1":424,"y1":420,"x2":424,"y2":427,"stroke":398,"style":399},[429,502],{"points":503,"fill":432},"625.3,161.5 620.3,152.5 630.3,152.5",[383,505],{"x":506,"y":436,"width":507,"height":438,"rx":439,"fill":389,"stroke":440,"style":441},"593.3","64.1",[412,509,510],{"x":424,"y":444,"style":445},"analyse",[383,512],{"x":513,"y":450,"width":451,"height":452,"rx":396,"fill":514,"stroke":515,"style":399},"510.7","#fdefd8","var(--gold,#b4740a)",[401,517,519],{"transform":518},"translate(613.3,176.5) scale(1.000)",[405,520],{"d":521,"fill":408,"stroke":522,"style":410},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--gold-ink,#7a4e06)",[412,524,526],{"x":424,"y":461,"style":525},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","the workbook",[412,528,529],{"x":424,"y":465,"style":466},"with or alongside",[25,531,533],{"id":532},"step-2-a-helper-that-writes-a-dataframe-as-a-word-table","Step 2 — A helper that writes a DataFrame as a Word table",[10,535,536],{},"python-docx works at the level of rows and cells, so a small helper does the conversion once, with every formatting decision in one place:",[30,538,540],{"className":73,"code":539,"language":75,"meta":35,"style":35},"from docx import Document\nfrom docx.enum.table import WD_TABLE_ALIGNMENT\nfrom docx.enum.text import WD_ALIGN_PARAGRAPH\nfrom docx.oxml import OxmlElement\nfrom docx.oxml.ns import qn\nfrom docx.shared import Cm, Pt\n\ndef repeat_header(row):\n    tr_pr = row._tr.get_or_add_trPr()\n    flag = OxmlElement(\"w:tblHeader\")\n    flag.set(qn(\"w:val\"), \"true\")\n    tr_pr.append(flag)\n\ndef df_to_table(doc, df, formats, widths_cm, style=\"Light Grid Accent 1\"):\n    table = doc.add_table(rows=1, cols=len(df.columns))\n    table.style, table.alignment, table.autofit = style, WD_TABLE_ALIGNMENT.CENTER, False\n    for cell, name in zip(table.rows[0].cells, df.columns):\n        cell.text = str(name)\n    repeat_header(table.rows[0])\n    for values in df.itertuples(index=False):\n        cells = table.add_row().cells\n        for cell, name, value in zip(cells, df.columns, values):\n            fmt = formats.get(name)\n            cell.text = fmt.format(value) if fmt else str(value)\n            if fmt:\n                cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT\n    for row in table.rows:\n        for cell, width in zip(row.cells, widths_cm):\n            cell.width = Cm(width)\n            for run in cell.paragraphs[0].runs:\n                run.font.size = Pt(9)\n    for cell, name in zip(table.rows[0].cells, df.columns):\n        if name in formats:\n            cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT\n    return table\n",[37,541,542,555,567,579,591,603,615,619,631,641,656,672,678,683,702,734,758,781,795,806,827,838,854,865,890,899,920,933,948,959,978,994,1011,1025,1043],{"__ignoreMap":35},[40,543,544,547,550,552],{"class":42,"line":43},[40,545,546],{"class":82},"from",[40,548,549],{"class":86}," docx ",[40,551,83],{"class":82},[40,553,554],{"class":86}," Document\n",[40,556,557,559,562,564],{"class":42,"line":96},[40,558,546],{"class":82},[40,560,561],{"class":86}," docx.enum.table ",[40,563,83],{"class":82},[40,565,566],{"class":187}," WD_TABLE_ALIGNMENT\n",[40,568,569,571,574,576],{"class":42,"line":103},[40,570,546],{"class":82},[40,572,573],{"class":86}," docx.enum.text ",[40,575,83],{"class":82},[40,577,578],{"class":187}," WD_ALIGN_PARAGRAPH\n",[40,580,581,583,586,588],{"class":42,"line":133},[40,582,546],{"class":82},[40,584,585],{"class":86}," docx.oxml ",[40,587,83],{"class":82},[40,589,590],{"class":86}," OxmlElement\n",[40,592,593,595,598,600],{"class":42,"line":166},[40,594,546],{"class":82},[40,596,597],{"class":86}," docx.oxml.ns ",[40,599,83],{"class":82},[40,601,602],{"class":86}," qn\n",[40,604,605,607,610,612],{"class":42,"line":212},[40,606,546],{"class":82},[40,608,609],{"class":86}," docx.shared ",[40,611,83],{"class":82},[40,613,614],{"class":86}," Cm, Pt\n",[40,616,617],{"class":42,"line":232},[40,618,100],{"emptyLinePlaceholder":99},[40,620,621,624,628],{"class":42,"line":261},[40,622,623],{"class":82},"def",[40,625,627],{"class":626},"s_Opv"," repeat_header",[40,629,630],{"class":86},"(row):\n",[40,632,633,636,638],{"class":42,"line":324},[40,634,635],{"class":86},"    tr_pr ",[40,637,109],{"class":82},[40,639,640],{"class":86}," row._tr.get_or_add_trPr()\n",[40,642,643,646,648,651,654],{"class":42,"line":339},[40,644,645],{"class":86},"    flag ",[40,647,109],{"class":82},[40,649,650],{"class":86}," OxmlElement(",[40,652,653],{"class":50},"\"w:tblHeader\"",[40,655,130],{"class":86},[40,657,659,662,665,667,670],{"class":42,"line":658},11,[40,660,661],{"class":86},"    flag.set(qn(",[40,663,664],{"class":50},"\"w:val\"",[40,666,303],{"class":86},[40,668,669],{"class":50},"\"true\"",[40,671,130],{"class":86},[40,673,675],{"class":42,"line":674},12,[40,676,677],{"class":86},"    tr_pr.append(flag)\n",[40,679,681],{"class":42,"line":680},13,[40,682,100],{"emptyLinePlaceholder":99},[40,684,686,688,691,694,696,699],{"class":42,"line":685},14,[40,687,623],{"class":82},[40,689,690],{"class":626}," df_to_table",[40,692,693],{"class":86},"(doc, df, formats, widths_cm, style",[40,695,109],{"class":82},[40,697,698],{"class":50},"\"Light Grid Accent 1\"",[40,700,701],{"class":86},"):\n",[40,703,705,708,710,713,716,718,721,723,726,728,731],{"class":42,"line":704},15,[40,706,707],{"class":86},"    table ",[40,709,109],{"class":82},[40,711,712],{"class":86}," doc.add_table(",[40,714,715],{"class":121},"rows",[40,717,109],{"class":82},[40,719,720],{"class":187},"1",[40,722,118],{"class":86},[40,724,725],{"class":121},"cols",[40,727,109],{"class":82},[40,729,730],{"class":187},"len",[40,732,733],{"class":86},"(df.columns))\n",[40,735,737,740,742,745,748,750,753,755],{"class":42,"line":736},16,[40,738,739],{"class":86},"    table.style, table.alignment, table.autofit ",[40,741,109],{"class":82},[40,743,744],{"class":86}," style, ",[40,746,747],{"class":187},"WD_TABLE_ALIGNMENT",[40,749,23],{"class":86},[40,751,752],{"class":187},"CENTER",[40,754,118],{"class":86},[40,756,757],{"class":187},"False\n",[40,759,761,764,767,770,773,776,778],{"class":42,"line":760},17,[40,762,763],{"class":82},"    for",[40,765,766],{"class":86}," cell, name ",[40,768,769],{"class":82},"in",[40,771,772],{"class":187}," zip",[40,774,775],{"class":86},"(table.rows[",[40,777,386],{"class":187},[40,779,780],{"class":86},"].cells, df.columns):\n",[40,782,784,787,789,792],{"class":42,"line":783},18,[40,785,786],{"class":86},"        cell.text ",[40,788,109],{"class":82},[40,790,791],{"class":187}," str",[40,793,794],{"class":86},"(name)\n",[40,796,798,801,803],{"class":42,"line":797},19,[40,799,800],{"class":86},"    repeat_header(table.rows[",[40,802,386],{"class":187},[40,804,805],{"class":86},"])\n",[40,807,809,811,814,816,819,821,823,825],{"class":42,"line":808},20,[40,810,763],{"class":82},[40,812,813],{"class":86}," values ",[40,815,769],{"class":82},[40,817,818],{"class":86}," df.itertuples(",[40,820,272],{"class":121},[40,822,109],{"class":82},[40,824,188],{"class":187},[40,826,701],{"class":86},[40,828,830,833,835],{"class":42,"line":829},21,[40,831,832],{"class":86},"        cells ",[40,834,109],{"class":82},[40,836,837],{"class":86}," table.add_row().cells\n",[40,839,841,844,847,849,851],{"class":42,"line":840},22,[40,842,843],{"class":82},"        for",[40,845,846],{"class":86}," cell, name, value ",[40,848,769],{"class":82},[40,850,772],{"class":187},[40,852,853],{"class":86},"(cells, df.columns, values):\n",[40,855,857,860,862],{"class":42,"line":856},23,[40,858,859],{"class":86},"            fmt ",[40,861,109],{"class":82},[40,863,864],{"class":86}," formats.get(name)\n",[40,866,868,871,873,876,879,882,885,887],{"class":42,"line":867},24,[40,869,870],{"class":86},"            cell.text ",[40,872,109],{"class":82},[40,874,875],{"class":86}," fmt.format(value) ",[40,877,878],{"class":82},"if",[40,880,881],{"class":86}," fmt ",[40,883,884],{"class":82},"else",[40,886,791],{"class":187},[40,888,889],{"class":86},"(value)\n",[40,891,893,896],{"class":42,"line":892},25,[40,894,895],{"class":82},"            if",[40,897,898],{"class":86}," fmt:\n",[40,900,902,905,907,910,912,915,917],{"class":42,"line":901},26,[40,903,904],{"class":86},"                cell.paragraphs[",[40,906,386],{"class":187},[40,908,909],{"class":86},"].alignment ",[40,911,109],{"class":82},[40,913,914],{"class":187}," WD_ALIGN_PARAGRAPH",[40,916,23],{"class":86},[40,918,919],{"class":187},"RIGHT\n",[40,921,923,925,928,930],{"class":42,"line":922},27,[40,924,763],{"class":82},[40,926,927],{"class":86}," row ",[40,929,769],{"class":82},[40,931,932],{"class":86}," table.rows:\n",[40,934,936,938,941,943,945],{"class":42,"line":935},28,[40,937,843],{"class":82},[40,939,940],{"class":86}," cell, width ",[40,942,769],{"class":82},[40,944,772],{"class":187},[40,946,947],{"class":86},"(row.cells, widths_cm):\n",[40,949,951,954,956],{"class":42,"line":950},29,[40,952,953],{"class":86},"            cell.width ",[40,955,109],{"class":82},[40,957,958],{"class":86}," Cm(width)\n",[40,960,962,965,968,970,973,975],{"class":42,"line":961},30,[40,963,964],{"class":82},"            for",[40,966,967],{"class":86}," run ",[40,969,769],{"class":82},[40,971,972],{"class":86}," cell.paragraphs[",[40,974,386],{"class":187},[40,976,977],{"class":86},"].runs:\n",[40,979,981,984,986,989,992],{"class":42,"line":980},31,[40,982,983],{"class":86},"                run.font.size ",[40,985,109],{"class":82},[40,987,988],{"class":86}," Pt(",[40,990,991],{"class":187},"9",[40,993,130],{"class":86},[40,995,997,999,1001,1003,1005,1007,1009],{"class":42,"line":996},32,[40,998,763],{"class":82},[40,1000,766],{"class":86},[40,1002,769],{"class":82},[40,1004,772],{"class":187},[40,1006,775],{"class":86},[40,1008,386],{"class":187},[40,1010,780],{"class":86},[40,1012,1014,1017,1020,1022],{"class":42,"line":1013},33,[40,1015,1016],{"class":82},"        if",[40,1018,1019],{"class":86}," name ",[40,1021,769],{"class":82},[40,1023,1024],{"class":86}," formats:\n",[40,1026,1028,1031,1033,1035,1037,1039,1041],{"class":42,"line":1027},34,[40,1029,1030],{"class":86},"            cell.paragraphs[",[40,1032,386],{"class":187},[40,1034,909],{"class":86},[40,1036,109],{"class":82},[40,1038,914],{"class":187},[40,1040,23],{"class":86},[40,1042,919],{"class":187},[40,1044,1046,1049],{"class":42,"line":1045},35,[40,1047,1048],{"class":82},"    return",[40,1050,1051],{"class":86}," table\n",[10,1053,1054,1055,1058,1059,1062,1063,1066],{},"A few details make the difference between a table that looks pasted and one that looks designed. A built-in table style (",[37,1056,1057],{},"Light Grid Accent 1"," is one of those in python-docx's default template) gives consistent borders and a shaded header. Numbers are formatted to strings with a format per column and right-aligned, header included, so the column's figures line up. Widths are set on every cell — Word takes column widths from its cells, and setting only the column object is not reliably honoured — with ",[37,1060,1061],{},"autofit"," off so Word does not resize them. And the ",[37,1064,1065],{},"w:tblHeader"," flag on the first row, which python-docx has no property for, makes Word repeat that row at the top of every page the table runs onto.",[364,1068,373,1074,373,1077,373,1080,373,1084,373,1092,373,1096,373,1102,373,1106,373,1110,373,1114,373,1118,373,1121,373,1125,373,1127,373,1131,373,1133,373,1136,373,1138,373,1141,373,1144,373,1148,373,1150,373,1154,373,1156,373,1159,373,1161,373,1164,373,1167,373,1170,373,1172,373,1175,373,1177,373,1180,373,1182,373,1185,373,1188,373,1191,373,1194,373,1198,373,1201,373,1204,373,1207,373,1209,373,1212,373,1215,373,1218,373,1220,373,1222,373,1224,373,1226,373,1228,373,1230,373,1232,373,1234,373,1236,373,1239,373,1241,373,1244,373,1246,373,1248,373,1250,373,1252,373,1254,373,1257,373,1259,373,1262,373,1268,373,1272,373,1277,373,1280,373,1287],{"viewBox":1069,"role":367,"ariaLabelledBy":1070,"xmlns":371,"style":1073},"12 0 736 164",[1071,1072],"wd-table-t","wd-table-d","width:100%;max-width:736px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[375,1075,1076],{"id":1071},"From DataFrame to Word table",[379,1078,1079],{"id":1072},"The summary DataFrame holds raw numbers. The Word table shows them as text formatted per column, right-aligned, with a styled header row that repeats on each page and fixed column widths.",[383,1081],{"x":396,"y":386,"width":1082,"height":1083,"fill":389},"736","164",[383,1085],{"x":1086,"y":1087,"width":1088,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"28.0","40.0","32.0","24.0","#e7ebef","stroke-width:1px",[383,1093],{"x":1094,"y":1087,"width":1095,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"60.0","69.2",[412,1097,1101],{"x":1098,"y":1099,"style":1100},"94.6","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[383,1103],{"x":1104,"y":1087,"width":1105,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"129.2","92.2",[412,1107,1109],{"x":1108,"y":1099,"style":1100},"175.3","B",[383,1111],{"x":1112,"y":1087,"width":1113,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"221.4","83.2",[412,1115,1117],{"x":1116,"y":1099,"style":1100},"263.0","C",[383,1119],{"x":1086,"y":1120,"width":1088,"height":1086,"rx":386,"fill":1090,"stroke":440,"style":1091},"64.0",[412,1122,720],{"x":1123,"y":1124,"style":1100},"44.0","83.0",[383,1126],{"x":1094,"y":1120,"width":1095,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1128,1130],{"x":1098,"y":1124,"style":1129},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Region",[383,1132],{"x":1104,"y":1120,"width":1105,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1134,1135],{"x":1108,"y":1124,"style":1129},"Revenue",[383,1137],{"x":1112,"y":1120,"width":1113,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1139,1140],{"x":1116,"y":1124,"style":1129},"Margin %",[383,1142],{"x":1086,"y":1143,"width":1088,"height":1086,"rx":386,"fill":1090,"stroke":440,"style":1091},"92.0",[412,1145,1147],{"x":1123,"y":1146,"style":1100},"111.0","2",[383,1149],{"x":1094,"y":1143,"width":1095,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1151,1153],{"x":1098,"y":1146,"style":1152},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Central",[383,1155],{"x":1104,"y":1143,"width":1105,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1157,1158],{"x":1108,"y":1146,"style":1152},"793568.62",[383,1160],{"x":1112,"y":1143,"width":1113,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1162,1163],{"x":1116,"y":1146,"style":1152},"0.3539",[383,1165],{"x":1086,"y":1166,"width":1088,"height":1086,"rx":386,"fill":1090,"stroke":440,"style":1091},"120.0",[412,1168,348],{"x":1123,"y":1169,"style":1100},"139.0",[383,1171],{"x":1094,"y":1166,"width":1095,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1173,1174],{"x":1098,"y":1169,"style":1152},"South",[383,1176],{"x":1104,"y":1166,"width":1105,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1178,1179],{"x":1108,"y":1169,"style":1152},"790641.19",[383,1181],{"x":1112,"y":1166,"width":1113,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1183,1184],{"x":1116,"y":1169,"style":1152},"0.447",[383,1186],{"x":1187,"y":1087,"width":1088,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"469.5",[383,1189],{"x":1190,"y":1087,"width":1095,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"501.5",[412,1192,1101],{"x":1193,"y":1099,"style":1100},"536.1",[383,1195],{"x":1196,"y":1087,"width":1197,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"570.7","78.1",[412,1199,1109],{"x":1200,"y":1099,"style":1100},"609.7",[383,1202],{"x":1203,"y":1087,"width":1113,"height":1089,"rx":386,"fill":1090,"stroke":440,"style":1091},"648.8",[412,1205,1117],{"x":1206,"y":1099,"style":1100},"690.4",[383,1208],{"x":1187,"y":1120,"width":1088,"height":1086,"rx":386,"fill":1090,"stroke":440,"style":1091},[412,1210,720],{"x":1211,"y":1124,"style":1100},"485.5",[383,1213],{"x":1190,"y":1120,"width":1095,"height":1086,"rx":386,"fill":397,"stroke":398,"style":1214},"stroke-width:1.5px",[412,1216,1130],{"x":1193,"y":1124,"style":1217},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[383,1219],{"x":1196,"y":1120,"width":1197,"height":1086,"rx":386,"fill":397,"stroke":398,"style":1214},[412,1221,1135],{"x":1200,"y":1124,"style":1217},[383,1223],{"x":1203,"y":1120,"width":1113,"height":1086,"rx":386,"fill":397,"stroke":398,"style":1214},[412,1225,1140],{"x":1206,"y":1124,"style":1217},[383,1227],{"x":1187,"y":1143,"width":1088,"height":1086,"rx":386,"fill":1090,"stroke":440,"style":1091},[412,1229,1147],{"x":1211,"y":1146,"style":1100},[383,1231],{"x":1190,"y":1143,"width":1095,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1233,1153],{"x":1193,"y":1146,"style":1152},[383,1235],{"x":1196,"y":1143,"width":1197,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1237,1238],{"x":1200,"y":1146,"style":1152},"793,569",[383,1240],{"x":1203,"y":1143,"width":1113,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1242,1243],{"x":1206,"y":1146,"style":1152},"35.4%",[383,1245],{"x":1187,"y":1166,"width":1088,"height":1086,"rx":386,"fill":1090,"stroke":440,"style":1091},[412,1247,348],{"x":1211,"y":1169,"style":1100},[383,1249],{"x":1190,"y":1166,"width":1095,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1251,1174],{"x":1193,"y":1169,"style":1152},[383,1253],{"x":1196,"y":1166,"width":1197,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1255,1256],{"x":1200,"y":1169,"style":1152},"790,641",[383,1258],{"x":1203,"y":1166,"width":1113,"height":1086,"rx":386,"fill":389,"stroke":440,"style":1091},[412,1260,1261],{"x":1206,"y":1169,"style":1152},"44.7%",[412,1263,1267],{"x":1264,"y":1265,"style":1266},"166.3","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[412,1269,1271],{"x":1270,"y":1265,"style":1266},"600.8","after",[42,1273],{"x1":1274,"y1":1275,"x2":1276,"y2":1275,"stroke":398,"style":399},"314.6","94.0","455.5",[429,1278],{"points":1279,"fill":432},"459.5,94.0 450.5,99.0 450.5,89.0",[383,1281],{"x":1282,"y":1283,"width":1284,"height":1285,"rx":1286,"fill":389,"stroke":440,"style":441},"337.1","61.0","100.0","22.0","11",[412,1288,1292],{"x":1289,"y":1290,"style":1291},"387.1","76.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","df_to_table",[25,1294,1296],{"id":1295},"step-3-write-the-report","Step 3 — Write the report",[10,1298,1299],{},"Headings structure the document (and its navigation pane and table of contents); paragraphs carry commentary generated from the summary; the tables come from the helper:",[30,1301,1303],{"className":73,"code":1302,"language":75,"meta":35,"style":35},"from docx.enum.section import WD_ORIENT, WD_SECTION\n\ndoc = Document()\ndoc.core_properties.title, doc.core_properties.author = \"Regional performance, April–September 2026\", \"Finance\"\ndoc.add_heading(\"Regional performance, April–September 2026\", level=0)\nbest, worst = summary.iloc[0], summary.sort_values(\"Margin %\").iloc[0]\ndoc.add_paragraph(\n    f\"Revenue for the half-year was {total:,.0f} across {len(summary)} regions. {best.Region} led with \"\n    f\"{best.Revenue:,.0f} ({best.Revenue \u002F total:.0%} of the total); {worst.Region} had the lowest margin at {worst['Margin %']:.1%}.\")\ndoc.add_heading(\"Summary by region\", level=1)\ndf_to_table(doc, summary, {\"Revenue\": \"{:,.0f}\", \"Cost\": \"{:,.0f}\", \"Margin\": \"{:,.0f}\", \"Orders\": \"{:,}\", \"Margin %\": \"{:.1%}\"},\n            [3.0, 2.6, 2.6, 2.6, 2.0, 2.2])\n\nlandscape = doc.add_section(WD_SECTION.NEW_PAGE)\nlandscape.orientation = WD_ORIENT.LANDSCAPE\nlandscape.page_width, landscape.page_height = landscape.page_height, landscape.page_width\ndoc.add_heading(\"Monthly revenue by region\", level=1)\nmonth_table = wide.reset_index()\ndf_to_table(doc, month_table, {m: \"{:,.0f}\" for m in wide.columns}, [3.2] + [2.8] * len(wide.columns))\nprint(len(doc.tables), \"tables;\", len(doc.sections), \"sections\")\n",[37,1304,1305,1322,1326,1336,1351,1370,1394,1399,1444,1511,1528,1616,1649,1653,1673,1687,1697,1714,1724,1775],{"__ignoreMap":35},[40,1306,1307,1309,1312,1314,1317,1319],{"class":42,"line":43},[40,1308,546],{"class":82},[40,1310,1311],{"class":86}," docx.enum.section ",[40,1313,83],{"class":82},[40,1315,1316],{"class":187}," WD_ORIENT",[40,1318,118],{"class":86},[40,1320,1321],{"class":187},"WD_SECTION\n",[40,1323,1324],{"class":42,"line":96},[40,1325,100],{"emptyLinePlaceholder":99},[40,1327,1328,1331,1333],{"class":42,"line":103},[40,1329,1330],{"class":86},"doc ",[40,1332,109],{"class":82},[40,1334,1335],{"class":86}," Document()\n",[40,1337,1338,1341,1343,1346,1348],{"class":42,"line":133},[40,1339,1340],{"class":86},"doc.core_properties.title, doc.core_properties.author ",[40,1342,109],{"class":82},[40,1344,1345],{"class":50}," \"Regional performance, April–September 2026\"",[40,1347,118],{"class":86},[40,1349,1350],{"class":50},"\"Finance\"\n",[40,1352,1353,1356,1359,1361,1364,1366,1368],{"class":42,"line":166},[40,1354,1355],{"class":86},"doc.add_heading(",[40,1357,1358],{"class":50},"\"Regional performance, April–September 2026\"",[40,1360,118],{"class":86},[40,1362,1363],{"class":121},"level",[40,1365,109],{"class":82},[40,1367,386],{"class":187},[40,1369,130],{"class":86},[40,1371,1372,1375,1377,1380,1382,1385,1387,1390,1392],{"class":42,"line":212},[40,1373,1374],{"class":86},"best, worst ",[40,1376,109],{"class":82},[40,1378,1379],{"class":86}," summary.iloc[",[40,1381,386],{"class":187},[40,1383,1384],{"class":86},"], summary.sort_values(",[40,1386,238],{"class":50},[40,1388,1389],{"class":86},").iloc[",[40,1391,386],{"class":187},[40,1393,163],{"class":86},[40,1395,1396],{"class":42,"line":232},[40,1397,1398],{"class":86},"doc.add_paragraph(\n",[40,1400,1401,1404,1407,1410,1413,1416,1419,1422,1424,1426,1429,1431,1434,1436,1439,1441],{"class":42,"line":261},[40,1402,1403],{"class":82},"    f",[40,1405,1406],{"class":50},"\"Revenue for the half-year was ",[40,1408,1409],{"class":297},"{",[40,1411,1412],{"class":86},"total",[40,1414,1415],{"class":82},":,.0f",[40,1417,1418],{"class":297},"}",[40,1420,1421],{"class":50}," across ",[40,1423,1409],{"class":297},[40,1425,730],{"class":187},[40,1427,1428],{"class":86},"(summary)",[40,1430,1418],{"class":297},[40,1432,1433],{"class":50}," regions. ",[40,1435,1409],{"class":297},[40,1437,1438],{"class":86},"best.Region",[40,1440,1418],{"class":297},[40,1442,1443],{"class":50}," led with \"\n",[40,1445,1446,1448,1450,1452,1455,1457,1459,1462,1464,1467,1469,1472,1475,1477,1480,1482,1485,1487,1490,1492,1495,1498,1501,1504,1506,1509],{"class":42,"line":324},[40,1447,1403],{"class":82},[40,1449,294],{"class":50},[40,1451,1409],{"class":297},[40,1453,1454],{"class":86},"best.Revenue",[40,1456,1415],{"class":82},[40,1458,1418],{"class":297},[40,1460,1461],{"class":50}," (",[40,1463,1409],{"class":297},[40,1465,1466],{"class":86},"best.Revenue ",[40,1468,252],{"class":82},[40,1470,1471],{"class":86}," total",[40,1473,1474],{"class":82},":.0%",[40,1476,1418],{"class":297},[40,1478,1479],{"class":50}," of the total); ",[40,1481,1409],{"class":297},[40,1483,1484],{"class":86},"worst.Region",[40,1486,1418],{"class":297},[40,1488,1489],{"class":50}," had the lowest margin at ",[40,1491,1409],{"class":297},[40,1493,1494],{"class":86},"worst[",[40,1496,1497],{"class":50},"'Margin %'",[40,1499,1500],{"class":86},"]",[40,1502,1503],{"class":82},":.1%",[40,1505,1418],{"class":297},[40,1507,1508],{"class":50},".\"",[40,1510,130],{"class":86},[40,1512,1513,1515,1518,1520,1522,1524,1526],{"class":42,"line":339},[40,1514,1355],{"class":86},[40,1516,1517],{"class":50},"\"Summary by region\"",[40,1519,118],{"class":86},[40,1521,1363],{"class":121},[40,1523,109],{"class":82},[40,1525,720],{"class":187},[40,1527,130],{"class":86},[40,1529,1530,1533,1535,1538,1540,1542,1544,1546,1548,1550,1552,1554,1556,1558,1560,1562,1564,1566,1568,1570,1572,1574,1576,1578,1580,1582,1584,1586,1588,1590,1593,1595,1597,1599,1601,1603,1605,1607,1609,1611,1613],{"class":42,"line":658},[40,1531,1532],{"class":86},"df_to_table(doc, summary, {",[40,1534,150],{"class":50},[40,1536,1537],{"class":86},": ",[40,1539,294],{"class":50},[40,1541,1409],{"class":297},[40,1543,1415],{"class":82},[40,1545,1418],{"class":297},[40,1547,294],{"class":50},[40,1549,118],{"class":86},[40,1551,160],{"class":50},[40,1553,1537],{"class":86},[40,1555,294],{"class":50},[40,1557,1409],{"class":297},[40,1559,1415],{"class":82},[40,1561,1418],{"class":297},[40,1563,294],{"class":50},[40,1565,118],{"class":86},[40,1567,139],{"class":50},[40,1569,1537],{"class":86},[40,1571,294],{"class":50},[40,1573,1409],{"class":297},[40,1575,1415],{"class":82},[40,1577,1418],{"class":297},[40,1579,294],{"class":50},[40,1581,118],{"class":86},[40,1583,206],{"class":50},[40,1585,1537],{"class":86},[40,1587,294],{"class":50},[40,1589,1409],{"class":297},[40,1591,1592],{"class":82},":,",[40,1594,1418],{"class":297},[40,1596,294],{"class":50},[40,1598,118],{"class":86},[40,1600,238],{"class":50},[40,1602,1537],{"class":86},[40,1604,294],{"class":50},[40,1606,1409],{"class":297},[40,1608,1503],{"class":82},[40,1610,1418],{"class":297},[40,1612,294],{"class":50},[40,1614,1615],{"class":86},"},\n",[40,1617,1618,1621,1624,1626,1629,1631,1633,1635,1637,1639,1642,1644,1647],{"class":42,"line":674},[40,1619,1620],{"class":86},"            [",[40,1622,1623],{"class":187},"3.0",[40,1625,118],{"class":86},[40,1627,1628],{"class":187},"2.6",[40,1630,118],{"class":86},[40,1632,1628],{"class":187},[40,1634,118],{"class":86},[40,1636,1628],{"class":187},[40,1638,118],{"class":86},[40,1640,1641],{"class":187},"2.0",[40,1643,118],{"class":86},[40,1645,1646],{"class":187},"2.2",[40,1648,805],{"class":86},[40,1650,1651],{"class":42,"line":680},[40,1652,100],{"emptyLinePlaceholder":99},[40,1654,1655,1658,1660,1663,1666,1668,1671],{"class":42,"line":685},[40,1656,1657],{"class":86},"landscape ",[40,1659,109],{"class":82},[40,1661,1662],{"class":86}," doc.add_section(",[40,1664,1665],{"class":187},"WD_SECTION",[40,1667,23],{"class":86},[40,1669,1670],{"class":187},"NEW_PAGE",[40,1672,130],{"class":86},[40,1674,1675,1678,1680,1682,1684],{"class":42,"line":704},[40,1676,1677],{"class":86},"landscape.orientation ",[40,1679,109],{"class":82},[40,1681,1316],{"class":187},[40,1683,23],{"class":86},[40,1685,1686],{"class":187},"LANDSCAPE\n",[40,1688,1689,1692,1694],{"class":42,"line":736},[40,1690,1691],{"class":86},"landscape.page_width, landscape.page_height ",[40,1693,109],{"class":82},[40,1695,1696],{"class":86}," landscape.page_height, landscape.page_width\n",[40,1698,1699,1701,1704,1706,1708,1710,1712],{"class":42,"line":760},[40,1700,1355],{"class":86},[40,1702,1703],{"class":50},"\"Monthly revenue by region\"",[40,1705,118],{"class":86},[40,1707,1363],{"class":121},[40,1709,109],{"class":82},[40,1711,720],{"class":187},[40,1713,130],{"class":86},[40,1715,1716,1719,1721],{"class":42,"line":783},[40,1717,1718],{"class":86},"month_table ",[40,1720,109],{"class":82},[40,1722,1723],{"class":86}," wide.reset_index()\n",[40,1725,1726,1729,1731,1733,1735,1737,1739,1742,1745,1747,1750,1753,1755,1758,1761,1764,1766,1769,1772],{"class":42,"line":797},[40,1727,1728],{"class":86},"df_to_table(doc, month_table, {m: ",[40,1730,294],{"class":50},[40,1732,1409],{"class":297},[40,1734,1415],{"class":82},[40,1736,1418],{"class":297},[40,1738,294],{"class":50},[40,1740,1741],{"class":82}," for",[40,1743,1744],{"class":86}," m ",[40,1746,769],{"class":82},[40,1748,1749],{"class":86}," wide.columns}, [",[40,1751,1752],{"class":187},"3.2",[40,1754,142],{"class":86},[40,1756,1757],{"class":82},"+",[40,1759,1760],{"class":86}," [",[40,1762,1763],{"class":187},"2.8",[40,1765,142],{"class":86},[40,1767,1768],{"class":82},"*",[40,1770,1771],{"class":187}," len",[40,1773,1774],{"class":86},"(wide.columns))\n",[40,1776,1777,1779,1782,1784,1787,1790,1792,1794,1797,1800],{"class":42,"line":808},[40,1778,342],{"class":187},[40,1780,1781],{"class":86},"(",[40,1783,730],{"class":187},[40,1785,1786],{"class":86},"(doc.tables), ",[40,1788,1789],{"class":50},"\"tables;\"",[40,1791,118],{"class":86},[40,1793,730],{"class":187},[40,1795,1796],{"class":86},"(doc.sections), ",[40,1798,1799],{"class":50},"\"sections\"",[40,1801,130],{"class":86},[10,1803,1804,1805,1808],{},"The month-by-region table is wider than a portrait page can hold comfortably, so it goes in a new section with landscape orientation. Changing ",[37,1806,1807],{},"orientation"," alone is not enough: python-docx does not swap the page dimensions, so the width and height are exchanged explicitly. Sections also carry their own headers, footers and margins, which is how a report mixes portrait narrative with landscape appendices.",[25,1810,1812],{"id":1811},"step-4-a-chart-with-a-caption","Step 4 — A chart with a caption",[10,1814,1815],{},"Charts are inserted as images. Render a matplotlib figure to memory at a resolution that prints well and size it to the page:",[30,1817,1819],{"className":73,"code":1818,"language":75,"meta":35,"style":35},"from io import BytesIO\nimport matplotlib\nmatplotlib.use(\"Agg\")\nimport matplotlib.pyplot as plt\n\nfig, ax = plt.subplots(figsize=(8, 3.2))\nfor region, row in wide.iterrows():\n    ax.plot(list(wide.columns), row.values \u002F 1000, marker=\"o\", label=region)\nax.set_ylabel(\"Revenue (thousands)\")\nax.legend(ncol=5, fontsize=8, loc=\"upper center\", bbox_to_anchor=(0.5, -0.12), frameon=False)\nfig.tight_layout()\npng = BytesIO()\nfig.savefig(png, format=\"png\", dpi=200)\nplt.close(fig)\n\ndoc.add_picture(png, width=Cm(22))\ncaption = doc.add_paragraph(\"Figure 1. Monthly revenue by region, April–September 2026.\", style=\"Caption\")\ndoc.save(\"regional_report.docx\")\nprint(f\"chart image: {png.getbuffer().nbytes \u002F 1024:.0f} KB\")\n",[37,1820,1821,1833,1839,1849,1861,1865,1891,1904,1940,1950,2012,2017,2027,2052,2057,2061,2079,2104,2114],{"__ignoreMap":35},[40,1822,1823,1825,1828,1830],{"class":42,"line":43},[40,1824,546],{"class":82},[40,1826,1827],{"class":86}," io ",[40,1829,83],{"class":82},[40,1831,1832],{"class":86}," BytesIO\n",[40,1834,1835,1837],{"class":42,"line":96},[40,1836,83],{"class":82},[40,1838,63],{"class":86},[40,1840,1841,1844,1847],{"class":42,"line":103},[40,1842,1843],{"class":86},"matplotlib.use(",[40,1845,1846],{"class":50},"\"Agg\"",[40,1848,130],{"class":86},[40,1850,1851,1853,1856,1858],{"class":42,"line":133},[40,1852,83],{"class":82},[40,1854,1855],{"class":86}," matplotlib.pyplot ",[40,1857,90],{"class":82},[40,1859,1860],{"class":86}," plt\n",[40,1862,1863],{"class":42,"line":166},[40,1864,100],{"emptyLinePlaceholder":99},[40,1866,1867,1870,1872,1875,1878,1880,1882,1885,1887,1889],{"class":42,"line":212},[40,1868,1869],{"class":86},"fig, ax ",[40,1871,109],{"class":82},[40,1873,1874],{"class":86}," plt.subplots(",[40,1876,1877],{"class":121},"figsize",[40,1879,109],{"class":82},[40,1881,1781],{"class":86},[40,1883,1884],{"class":187},"8",[40,1886,118],{"class":86},[40,1888,1752],{"class":187},[40,1890,229],{"class":86},[40,1892,1893,1896,1899,1901],{"class":42,"line":232},[40,1894,1895],{"class":82},"for",[40,1897,1898],{"class":86}," region, row ",[40,1900,769],{"class":82},[40,1902,1903],{"class":86}," wide.iterrows():\n",[40,1905,1906,1909,1912,1915,1917,1920,1922,1925,1927,1930,1932,1935,1937],{"class":42,"line":261},[40,1907,1908],{"class":86},"    ax.plot(",[40,1910,1911],{"class":187},"list",[40,1913,1914],{"class":86},"(wide.columns), row.values ",[40,1916,252],{"class":82},[40,1918,1919],{"class":187}," 1000",[40,1921,118],{"class":86},[40,1923,1924],{"class":121},"marker",[40,1926,109],{"class":82},[40,1928,1929],{"class":50},"\"o\"",[40,1931,118],{"class":86},[40,1933,1934],{"class":121},"label",[40,1936,109],{"class":82},[40,1938,1939],{"class":86},"region)\n",[40,1941,1942,1945,1948],{"class":42,"line":324},[40,1943,1944],{"class":86},"ax.set_ylabel(",[40,1946,1947],{"class":50},"\"Revenue (thousands)\"",[40,1949,130],{"class":86},[40,1951,1952,1955,1958,1960,1963,1965,1968,1970,1972,1974,1977,1979,1982,1984,1987,1989,1991,1994,1996,1998,2001,2003,2006,2008,2010],{"class":42,"line":339},[40,1953,1954],{"class":86},"ax.legend(",[40,1956,1957],{"class":121},"ncol",[40,1959,109],{"class":82},[40,1961,1962],{"class":187},"5",[40,1964,118],{"class":86},[40,1966,1967],{"class":121},"fontsize",[40,1969,109],{"class":82},[40,1971,1884],{"class":187},[40,1973,118],{"class":86},[40,1975,1976],{"class":121},"loc",[40,1978,109],{"class":82},[40,1980,1981],{"class":50},"\"upper center\"",[40,1983,118],{"class":86},[40,1985,1986],{"class":121},"bbox_to_anchor",[40,1988,109],{"class":82},[40,1990,1781],{"class":86},[40,1992,1993],{"class":187},"0.5",[40,1995,118],{"class":86},[40,1997,155],{"class":82},[40,1999,2000],{"class":187},"0.12",[40,2002,303],{"class":86},[40,2004,2005],{"class":121},"frameon",[40,2007,109],{"class":82},[40,2009,188],{"class":187},[40,2011,130],{"class":86},[40,2013,2014],{"class":42,"line":658},[40,2015,2016],{"class":86},"fig.tight_layout()\n",[40,2018,2019,2022,2024],{"class":42,"line":674},[40,2020,2021],{"class":86},"png ",[40,2023,109],{"class":82},[40,2025,2026],{"class":86}," BytesIO()\n",[40,2028,2029,2032,2035,2037,2040,2042,2045,2047,2050],{"class":42,"line":680},[40,2030,2031],{"class":86},"fig.savefig(png, ",[40,2033,2034],{"class":121},"format",[40,2036,109],{"class":82},[40,2038,2039],{"class":50},"\"png\"",[40,2041,118],{"class":86},[40,2043,2044],{"class":121},"dpi",[40,2046,109],{"class":82},[40,2048,2049],{"class":187},"200",[40,2051,130],{"class":86},[40,2053,2054],{"class":42,"line":685},[40,2055,2056],{"class":86},"plt.close(fig)\n",[40,2058,2059],{"class":42,"line":704},[40,2060,100],{"emptyLinePlaceholder":99},[40,2062,2063,2066,2069,2071,2074,2077],{"class":42,"line":736},[40,2064,2065],{"class":86},"doc.add_picture(png, ",[40,2067,2068],{"class":121},"width",[40,2070,109],{"class":82},[40,2072,2073],{"class":86},"Cm(",[40,2075,2076],{"class":187},"22",[40,2078,229],{"class":86},[40,2080,2081,2084,2086,2089,2092,2094,2097,2099,2102],{"class":42,"line":760},[40,2082,2083],{"class":86},"caption ",[40,2085,109],{"class":82},[40,2087,2088],{"class":86}," doc.add_paragraph(",[40,2090,2091],{"class":50},"\"Figure 1. Monthly revenue by region, April–September 2026.\"",[40,2093,118],{"class":86},[40,2095,2096],{"class":121},"style",[40,2098,109],{"class":82},[40,2100,2101],{"class":50},"\"Caption\"",[40,2103,130],{"class":86},[40,2105,2106,2109,2112],{"class":42,"line":783},[40,2107,2108],{"class":86},"doc.save(",[40,2110,2111],{"class":50},"\"regional_report.docx\"",[40,2113,130],{"class":86},[40,2115,2116,2118,2120,2123,2126,2128,2131,2133,2136,2139,2141,2144],{"class":42,"line":797},[40,2117,342],{"class":187},[40,2119,1781],{"class":86},[40,2121,2122],{"class":82},"f",[40,2124,2125],{"class":50},"\"chart image: ",[40,2127,1409],{"class":297},[40,2129,2130],{"class":86},"png.getbuffer().nbytes ",[40,2132,252],{"class":82},[40,2134,2135],{"class":187}," 1024",[40,2137,2138],{"class":82},":.0f",[40,2140,1418],{"class":297},[40,2142,2143],{"class":50}," KB\"",[40,2145,130],{"class":86},[10,2147,2148,2149,2152,2153,23],{},"The chart image is about 120 KB. At 200 dpi the chart stays sharp when printed and the image remains small. The ",[37,2150,2151],{},"Caption"," style is Word's own, so captions look consistent and can be collected by Word into a table of figures. Keep chart images out of table cells: they make row heights unpredictable and are awkward to edit. If readers would rather have charts they can restyle in Word, a native chart needs a library beyond python-docx; an image is the dependable default, and the same figure can go into the workbook as shown in ",[14,2154,2156],{"href":2155},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fembed-a-matplotlib-chart-in-an-excel-report\u002F","Embed a matplotlib Chart in an Excel Report",[364,2158,373,2164,373,2167,373,2170,373,2175,373,2179,373,2183,373,2186,373,2190,373,2193,373,2195,373,2199,373,2203,373,2207,373,2210,373,2213,373,2217,373,2221,373,2225,373,2228,373,2231,373,2236],{"viewBox":2159,"role":367,"ariaLabelledBy":2160,"xmlns":371,"style":2163},"84 0 592 376",[2161,2162],"wd-structure-t","wd-structure-d","width:100%;max-width:592px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[375,2165,2166],{"id":2161},"The structure of the generated report",[379,2168,2169],{"id":2162},"Section one is portrait with a title, generated commentary and the summary table. Section two is landscape with the monthly revenue table and the chart with its caption. Headings give the document its navigation structure.",[383,2171],{"x":2172,"y":386,"width":2173,"height":2174,"fill":389},"84","592","376",[383,2176],{"x":1284,"y":393,"width":2177,"height":2178,"rx":396,"fill":397,"stroke":398,"style":399},"560.0","59.0",[412,2180,2182],{"x":414,"y":2181,"style":415},"41.5","Section 1 (portrait)",[412,2184,2185],{"x":414,"y":1283,"style":466},"title, commentary, summary table",[42,2187],{"x1":414,"y1":2188,"x2":414,"y2":2189,"stroke":398,"style":399},"80.0","102.0",[429,2191],{"points":2192,"fill":432},"380.0,106.0 375.0,97.0 385.0,97.0",[383,2194],{"x":1284,"y":1146,"width":2177,"height":2178,"rx":396,"fill":483,"stroke":484,"style":399},[412,2196,2198],{"x":414,"y":2197,"style":494},"136.5","Section 2 (landscape)",[412,2200,2202],{"x":414,"y":2201,"style":466},"156.0","monthly revenue table",[42,2204],{"x1":414,"y1":2205,"x2":414,"y2":2206,"stroke":398,"style":399},"175.0","197.0",[429,2208],{"points":2209,"fill":432},"380.0,201.0 375.0,192.0 385.0,192.0",[383,2211],{"x":1284,"y":2212,"width":2177,"height":2178,"rx":396,"fill":514,"stroke":515,"style":399},"206.0",[412,2214,2216],{"x":414,"y":2215,"style":525},"231.5","Chart",[412,2218,2220],{"x":414,"y":2219,"style":466},"251.0","200 dpi PNG, Caption style",[42,2222],{"x1":414,"y1":2223,"x2":414,"y2":2224,"stroke":398,"style":399},"270.0","292.0",[429,2226],{"points":2227,"fill":432},"380.0,296.0 375.0,287.0 385.0,287.0",[383,2229],{"x":1284,"y":2230,"width":2177,"height":2178,"rx":396,"fill":1090,"stroke":440,"style":399},"301.0",[412,2232,2235],{"x":414,"y":2233,"style":2234},"326.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Properties",[412,2237,2239],{"x":414,"y":2238,"style":466},"346.0","title and author",[25,2241,2243],{"id":2242},"step-5-check-the-document","Step 5 — Check the document",[10,2245,2246],{},"Open the saved file again and check what matters, then render it to PDF to see how the tables fall on pages:",[30,2248,2250],{"className":73,"code":2249,"language":75,"meta":35,"style":35},"import subprocess\nimport pdfplumber\n\ncheck = Document(\"regional_report.docx\")\nt1, t2 = check.tables\nprint(\"summary rows:\", len(t1.rows) - 1, \"| header repeats:\", t1.rows[0]._tr.trPr.find(qn(\"w:tblHeader\")) is not None)\nprint(\"numbers right-aligned:\", all(c.paragraphs[0].alignment == WD_ALIGN_PARAGRAPH.RIGHT for c in t1.columns[1].cells))\nprint(\"section 2:\", check.sections[1].orientation, f\"{check.sections[1].page_width.cm:.1f} x {check.sections[1].page_height.cm:.1f} cm\")\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"pdf\", \"regional_report.docx\"], check=True, capture_output=True, timeout=180)\nwith pdfplumber.open(\"regional_report.pdf\") as pdf:\n    print(\"PDF pages:\", len(pdf.pages), \"| page 2 is landscape:\", pdf.pages[1].width > pdf.pages[1].height)\n",[37,2251,2252,2259,2266,2270,2284,2294,2344,2390,2447,2506,2525],{"__ignoreMap":35},[40,2253,2254,2256],{"class":42,"line":43},[40,2255,83],{"class":82},[40,2257,2258],{"class":86}," subprocess\n",[40,2260,2261,2263],{"class":42,"line":96},[40,2262,83],{"class":82},[40,2264,2265],{"class":86}," pdfplumber\n",[40,2267,2268],{"class":42,"line":103},[40,2269,100],{"emptyLinePlaceholder":99},[40,2271,2272,2275,2277,2280,2282],{"class":42,"line":133},[40,2273,2274],{"class":86},"check ",[40,2276,109],{"class":82},[40,2278,2279],{"class":86}," Document(",[40,2281,2111],{"class":50},[40,2283,130],{"class":86},[40,2285,2286,2289,2291],{"class":42,"line":166},[40,2287,2288],{"class":86},"t1, t2 ",[40,2290,109],{"class":82},[40,2292,2293],{"class":86}," check.tables\n",[40,2295,2296,2298,2300,2303,2305,2307,2310,2312,2315,2317,2320,2323,2325,2328,2330,2333,2336,2339,2342],{"class":42,"line":212},[40,2297,342],{"class":187},[40,2299,1781],{"class":86},[40,2301,2302],{"class":50},"\"summary rows:\"",[40,2304,118],{"class":86},[40,2306,730],{"class":187},[40,2308,2309],{"class":86},"(t1.rows) ",[40,2311,155],{"class":82},[40,2313,2314],{"class":187}," 1",[40,2316,118],{"class":86},[40,2318,2319],{"class":50},"\"| header repeats:\"",[40,2321,2322],{"class":86},", t1.rows[",[40,2324,386],{"class":187},[40,2326,2327],{"class":86},"]._tr.trPr.find(qn(",[40,2329,653],{"class":50},[40,2331,2332],{"class":86},")) ",[40,2334,2335],{"class":82},"is",[40,2337,2338],{"class":82}," not",[40,2340,2341],{"class":187}," None",[40,2343,130],{"class":86},[40,2345,2346,2348,2350,2353,2355,2358,2361,2363,2365,2368,2370,2372,2375,2377,2380,2382,2385,2387],{"class":42,"line":232},[40,2347,342],{"class":187},[40,2349,1781],{"class":86},[40,2351,2352],{"class":50},"\"numbers right-aligned:\"",[40,2354,118],{"class":86},[40,2356,2357],{"class":187},"all",[40,2359,2360],{"class":86},"(c.paragraphs[",[40,2362,386],{"class":187},[40,2364,909],{"class":86},[40,2366,2367],{"class":82},"==",[40,2369,914],{"class":187},[40,2371,23],{"class":86},[40,2373,2374],{"class":187},"RIGHT",[40,2376,1741],{"class":82},[40,2378,2379],{"class":86}," c ",[40,2381,769],{"class":82},[40,2383,2384],{"class":86}," t1.columns[",[40,2386,720],{"class":187},[40,2388,2389],{"class":86},"].cells))\n",[40,2391,2392,2394,2396,2399,2402,2404,2407,2409,2411,2413,2416,2418,2421,2424,2426,2429,2431,2433,2435,2438,2440,2442,2445],{"class":42,"line":261},[40,2393,342],{"class":187},[40,2395,1781],{"class":86},[40,2397,2398],{"class":50},"\"section 2:\"",[40,2400,2401],{"class":86},", check.sections[",[40,2403,720],{"class":187},[40,2405,2406],{"class":86},"].orientation, ",[40,2408,2122],{"class":82},[40,2410,294],{"class":50},[40,2412,1409],{"class":297},[40,2414,2415],{"class":86},"check.sections[",[40,2417,720],{"class":187},[40,2419,2420],{"class":86},"].page_width.cm",[40,2422,2423],{"class":82},":.1f",[40,2425,1418],{"class":297},[40,2427,2428],{"class":50}," x ",[40,2430,1409],{"class":297},[40,2432,2415],{"class":86},[40,2434,720],{"class":187},[40,2436,2437],{"class":86},"].page_height.cm",[40,2439,2423],{"class":82},[40,2441,1418],{"class":297},[40,2443,2444],{"class":50}," cm\"",[40,2446,130],{"class":86},[40,2448,2449,2452,2455,2457,2460,2462,2465,2467,2470,2472,2474,2477,2480,2482,2485,2487,2490,2492,2494,2496,2499,2501,2504],{"class":42,"line":324},[40,2450,2451],{"class":86},"subprocess.run([",[40,2453,2454],{"class":50},"\"soffice\"",[40,2456,118],{"class":86},[40,2458,2459],{"class":50},"\"--headless\"",[40,2461,118],{"class":86},[40,2463,2464],{"class":50},"\"--convert-to\"",[40,2466,118],{"class":86},[40,2468,2469],{"class":50},"\"pdf\"",[40,2471,118],{"class":86},[40,2473,2111],{"class":50},[40,2475,2476],{"class":86},"], ",[40,2478,2479],{"class":121},"check",[40,2481,109],{"class":82},[40,2483,2484],{"class":187},"True",[40,2486,118],{"class":86},[40,2488,2489],{"class":121},"capture_output",[40,2491,109],{"class":82},[40,2493,2484],{"class":187},[40,2495,118],{"class":86},[40,2497,2498],{"class":121},"timeout",[40,2500,109],{"class":82},[40,2502,2503],{"class":187},"180",[40,2505,130],{"class":86},[40,2507,2508,2511,2514,2517,2520,2522],{"class":42,"line":339},[40,2509,2510],{"class":82},"with",[40,2512,2513],{"class":86}," pdfplumber.open(",[40,2515,2516],{"class":50},"\"regional_report.pdf\"",[40,2518,2519],{"class":86},") ",[40,2521,90],{"class":82},[40,2523,2524],{"class":86}," pdf:\n",[40,2526,2527,2530,2532,2535,2537,2539,2542,2545,2548,2550,2553,2556,2559,2561],{"class":42,"line":658},[40,2528,2529],{"class":187},"    print",[40,2531,1781],{"class":86},[40,2533,2534],{"class":50},"\"PDF pages:\"",[40,2536,118],{"class":86},[40,2538,730],{"class":187},[40,2540,2541],{"class":86},"(pdf.pages), ",[40,2543,2544],{"class":50},"\"| page 2 is landscape:\"",[40,2546,2547],{"class":86},", pdf.pages[",[40,2549,720],{"class":187},[40,2551,2552],{"class":86},"].width ",[40,2554,2555],{"class":82},">",[40,2557,2558],{"class":86}," pdf.pages[",[40,2560,720],{"class":187},[40,2562,2563],{"class":86},"].height)\n",[10,2565,2566],{},"The summary has one row per region with the header flagged to repeat; every cell in the Revenue column is right-aligned; the second section is landscape, 27.9 by 21.6 cm — US Letter turned sideways, the size of python-docx's default template; set the page size explicitly for A4. The PDF rendering has the landscape page where expected. The whole document fits on two pages: the portrait summary and the landscape appendix with the monthly table and chart. Checks like these run in the job, and the PDF rendering can be attached for readers who do not need to edit the document.",[25,2568,2570],{"id":2569},"common-pitfalls","Common pitfalls",[2572,2573,2574,2585,2594,2607,2613],"ul",{},[2575,2576,2577,2581,2582,2584],"li",{},[2578,2579,2580],"strong",{},"Column widths ignored."," Set the width on every cell and turn ",[37,2583,1061],{}," off.",[2575,2586,2587,2590,2591,2593],{},[2578,2588,2589],{},"Header not repeating."," Add the ",[37,2592,1065],{}," flag to the first row.",[2575,2595,2596,2599,2600,2603,2604,23],{},[2578,2597,2598],{},"Landscape section that is still portrait-sized."," Swap ",[37,2601,2602],{},"page_width"," and ",[37,2605,2606],{},"page_height",[2575,2608,2609,2612],{},[2578,2610,2611],{},"Very large tables."," python-docx slows down with thousands of rows; summarise or attach the workbook.",[2575,2614,2615,2618],{},[2578,2616,2617],{},"Default page size."," python-docx's template is US Letter; set A4 if that is your standard.",[25,2620,2622],{"id":2621},"conclusion","Conclusion",[10,2624,2625],{},"To build a Word report from Excel data, summarise the workbook with pandas, generate commentary from the same figures, and write each DataFrame through one helper that applies a table style, formats and right-aligns numbers, fixes column widths on every cell and flags the header row to repeat. Put wide tables in landscape sections with swapped page dimensions, insert charts as 200 dpi images with captions, and reopen the document — and its PDF rendering — to check the result.",[25,2627,2629],{"id":2628},"frequently-asked-questions","Frequently asked questions",[10,2631,2632,2635],{},[2578,2633,2634],{},"How do I write a pandas DataFrame to a Word table?","\nCreate a table with python-docx, fill the header from the column names, then add a row per DataFrame row, formatting each value as text; a helper function keeps the formatting consistent.",[10,2637,2638,2641],{},[2578,2639,2640],{},"How do I repeat a table header row on every page in python-docx?","\nAdd a w:tblHeader element with value true to the first row's properties; python-docx has no direct property for it.",[10,2643,2644,2647],{},[2578,2645,2646],{},"Why are my python-docx column widths ignored?","\nWord reads widths from the cells, so set cell.width on every cell in the column and set table.autofit to False.",[10,2649,2650,2653],{},[2578,2651,2652],{},"How do I make one page landscape in a Word document with Python?","\nAdd a new section, set its orientation to landscape and swap its page_width and page_height.",[25,2655,2657],{"id":2656},"related","Related",[2572,2659,2660,2666,2671,2678],{},[2575,2661,2662,2663,2665],{},"Up one level: ",[14,2664,22],{"href":21}," — reports from templates and data.",[2575,2667,2668,2670],{},[14,2669,17],{"href":16}," — one document per row.",[2575,2672,2673,2677],{},[14,2674,2676],{"href":2675},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-word-document-tables-to-excel-with-python\u002F","Convert Word Document Tables to Excel with Python"," — the reverse direction.",[2575,2679,2680,2684],{},[14,2681,2683],{"href":2682},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fcreate-a-pdf-report-from-excel-data-with-reportlab\u002F","Create a PDF Report from Excel Data with ReportLab"," — when the output should be a PDF.",[2096,2686,2687],{},"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 .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}",{"title":35,"searchDepth":96,"depth":96,"links":2689},[2690,2691,2692,2693,2694,2695,2696,2697,2698,2699],{"id":27,"depth":96,"text":28},{"id":69,"depth":96,"text":70},{"id":532,"depth":96,"text":533},{"id":1295,"depth":96,"text":1296},{"id":1811,"depth":96,"text":1812},{"id":2242,"depth":96,"text":2243},{"id":2569,"depth":96,"text":2570},{"id":2621,"depth":96,"text":2622},{"id":2628,"depth":96,"text":2629},{"id":2656,"depth":96,"text":2657},"2026-10-02","Turn Excel data into a Word report with python-docx: headings and generated commentary, DataFrames written as properly styled Word tables with right-aligned numbers, fixed column widths and a header row that repeats on every page, a landscape section for a wide table, a chart image with a caption, and checks of the finished document.","md",[2704,2706,2708,2710],{"q":2634,"a":2705},"Create a table with python-docx, fill the header from the column names, then add a row per DataFrame row, formatting each value as text; a helper function keeps the formatting consistent.",{"q":2640,"a":2707},"Add a w:tblHeader element with value true to the first row's properties; python-docx has no direct property for it.",{"q":2646,"a":2709},"Word reads widths from the cells, so set cell.width on every cell in the column and set table.autofit to False.",{"q":2652,"a":2711},"Add a new section, set its orientation to landscape and swap its page_width and page_height.","build a word report with tables from excel data",null,{"breadcrumb":2715},[2716,2718,2721],{"name":2717,"item":252},"Home",{"name":2719,"item":2720},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":22,"item":21},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data",{"title":2724,"description":2725},"Build a Word Report with Tables from Excel Data in Python","python-docx report from Excel data: DataFrame to Word table helper, table styles, number alignment, column widths, repeating header rows, landscape sections, matplotlib charts with captions and verifying the .docx.","build-a-word-report-with-tables-from-excel-data","automating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fbuild-a-word-report-with-tables-from-excel-data\u002Findex",[2729],"report-layouts","how-to","Ujb97TRNfNRtErjp0aZGattV1294AuuMa44x1QW1DqY",[2733,2736],{"title":22,"path":2734,"stem":2735,"children":-1},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates","automating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Findex",{"title":2737,"path":2738,"stem":2739,"children":-1},"Fill an Excel Template with Python and openpyxl","\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Ffill-excel-template-with-python-openpyxl","automating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Ffill-excel-template-with-python-openpyxl\u002Findex",[2741,2742],{"path":2734,"title":22},{"path":2743,"title":2719},"\u002Fautomating-reporting-workflows",[],[2746,2750,2753,2756,2759,2762],{"path":2747,"title":2748,"kind":2749},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports","Building Finance and Operations Reports in Excel with Python","guide",{"path":2751,"title":2752,"kind":2730},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fallocate-shared-costs-across-departments","Allocate Shared Costs Across Departments with Python",{"path":2754,"title":2755,"kind":2730},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report","Build a Bank Reconciliation Report with Python and Excel",{"path":2757,"title":2758,"kind":2730},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bill-of-materials-cost-rollup","Build a Bill of Materials Cost Rollup",{"path":2760,"title":2761,"kind":2730},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-break-even-analysis-in-excel-with-python","Build a Break-Even Analysis in Excel with Python",{"path":2763,"title":2764,"kind":2765},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-budget-vs-actual-variance-report","Build a Budget vs Actual Variance Report in Excel with Python","compare",1790972137908]