[{"data":1,"prerenderedAt":2098},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl":2065,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl":2074,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl":2079,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl":2080},{"id":4,"title":5,"body":6,"dateModified":2032,"datePublished":2032,"description":2033,"extension":2034,"faq":2035,"keyword":2044,"kind":2045,"meta":2046,"navigation":260,"path":2054,"seo":2055,"slug":2058,"stem":2059,"topics":2060,"type":2063,"__hash__":2064},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl\u002Findex.md","Wrap Text and Align Cells with openpyxl",{"type":7,"value":8,"toc":2020},"minimark",[9,24,191,196,221,224,509,513,527,632,647,651,654,939,947,1089,1093,1096,1259,1270,1274,1285,1554,1560,1674,1678,1685,1870,1885,1889,1937,1941,1951,1955,1961,1971,1977,1983,1987,2016],[10,11,12,13,17,18,23],"p",{},"Long text in a report — comments, descriptions, addresses, ticket summaries — either spills across neighbouring empty cells or, once the neighbour holds a value, is cut off at the column edge. Wrapping fixes that, but it brings its own questions: how tall should the row be, where should text sit in a tall row, and what about the short cells next to it? openpyxl controls all of it through the cell's ",[14,15,16],"code",{},"Alignment",". This guide, part of ",[19,20,22],"a",{"href":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002F","Styling Excel Cells with openpyxl",", wraps and aligns a ticket list, sizes the rows, and covers indentation, rotation and centring without merging.",[25,26,34,35,34,39,34,43,34,51,34,60,34,64,34,71,34,75,34,79,34,83,34,87,34,91,34,96,34,101,34,105,34,107,34,110,34,112,34,115,34,118,34,122,34,124,34,128,34,130,34,133,34,137,34,141,34,144,34,148,34,150,34,153,34,155,34,158,34,160,34,163,34,166,34,170,34,172,34,175,34,177,34,180,34,182,34,185],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"58 0 644 194","img",[30,31],"wr-sheet-t","wr-sheet-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:644px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[36,37,38],"title",{"id":30},"A ticket list with wrapped, top-aligned descriptions",[40,41,42],"desc",{"id":31},"Three tickets. T-1 has a long description wrapped over three lines, T-2 a short note on one line and T-3 a note with explicit line breaks over three lines. All cells are aligned to the top, so the ticket numbers sit level with the first line of their descriptions.",[44,45],"rect",{"x":46,"y":47,"width":48,"height":49,"fill":50},"58","0","644","194","#ffffff",[44,52],{"x":53,"y":54,"width":55,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"74.3","16.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[44,61],{"x":62,"y":54,"width":63,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"106.3","62.7",[65,66,70],"text",{"x":67,"y":68,"style":69},"137.6","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[44,72],{"x":73,"y":54,"width":74,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"169.0","74.0",[65,76,78],{"x":77,"y":68,"style":69},"206.0","B",[44,80],{"x":81,"y":54,"width":82,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"242.9","442.7",[65,84,86],{"x":85,"y":68,"style":69},"464.3","C",[44,88],{"x":53,"y":89,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"40.0","28.0",[65,92,95],{"x":93,"y":94,"style":69},"90.3","59.0","1",[44,97],{"x":62,"y":89,"width":63,"height":90,"rx":47,"fill":98,"stroke":99,"style":100},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[65,102,104],{"x":67,"y":94,"style":103},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Ticket",[44,106],{"x":73,"y":89,"width":74,"height":90,"rx":47,"fill":98,"stroke":99,"style":100},[65,108,109],{"x":77,"y":94,"style":103},"Priority",[44,111],{"x":81,"y":89,"width":82,"height":90,"rx":47,"fill":98,"stroke":99,"style":100},[65,113,114],{"x":85,"y":94,"style":103},"Description",[44,116],{"x":53,"y":117,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"68.0",[65,119,121],{"x":93,"y":120,"style":69},"87.0","2",[44,123],{"x":62,"y":117,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,125,127],{"x":67,"y":120,"style":126},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","T-1",[44,129],{"x":73,"y":117,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,131,132],{"x":77,"y":120,"style":126},"High",[44,134],{"x":81,"y":117,"width":82,"height":90,"rx":47,"fill":135,"stroke":136,"style":100},"#d9f4f1","var(--teal,#0f9488)",[65,138,140],{"x":85,"y":120,"style":139},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Customer reports that the ↵ monthly export stops at ↵ row 65,536 …",[44,142],{"x":53,"y":143,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"96.0",[65,145,147],{"x":93,"y":146,"style":69},"115.0","3",[44,149],{"x":62,"y":143,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,151,152],{"x":67,"y":146,"style":126},"T-2",[44,154],{"x":73,"y":143,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,156,157],{"x":77,"y":146,"style":126},"Low",[44,159],{"x":81,"y":143,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,161,162],{"x":85,"y":146,"style":126},"Short note",[44,164],{"x":53,"y":165,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"124.0",[65,167,169],{"x":93,"y":168,"style":69},"143.0","4",[44,171],{"x":62,"y":165,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,173,174],{"x":67,"y":168,"style":126},"T-3",[44,176],{"x":73,"y":165,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,178,179],{"x":77,"y":168,"style":126},"Medium",[44,181],{"x":81,"y":165,"width":82,"height":90,"rx":47,"fill":135,"stroke":136,"style":100},[65,183,184],{"x":85,"y":168,"style":139},"Line one ↵ Line two ↵ Line three",[65,186,190],{"x":187,"y":188,"style":189},"380.0","174.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","wrap_text breaks at the column width and at newlines; vertical=\"top\" keeps rows aligned",[192,193,195],"h2",{"id":194},"prerequisites","Prerequisites",[197,198,203],"pre",{"className":199,"code":200,"language":201,"meta":202,"style":202},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,204,205],{"__ignoreMap":202},[206,207,210,214,218],"span",{"class":208,"line":209},"line",1,[206,211,213],{"class":212},"sMTad","pip",[206,215,217],{"class":216},"srMev"," install",[206,219,220],{"class":216}," openpyxl\n",[10,222,223],{},"A ticket list with a mix of long, short and multi-line text:",[197,225,229],{"className":226,"code":227,"language":228,"meta":202,"style":202},"language-python shiki shiki-themes github-light github-dark-high-contrast","import math\nfrom copy import copy\n\nfrom openpyxl import Workbook\nfrom openpyxl.styles import Alignment, Font\n\ntickets = [\n    (\"T-1\", \"High\", \"Customer reports that the monthly export stops at row 65,536 when opened in the old viewer.\"),\n    (\"T-2\", \"Low\", \"Short note\"),\n    (\"T-3\", \"Medium\", \"Line one\\nLine two\\nLine three\"),\n]\nwb = Workbook()\nws = wb.active\nws.title = \"Tickets\"\nws.append([\"Ticket\", \"Priority\", \"Description\"])\nfor ticket in tickets:\n    ws.append(ticket)\nws.column_dimensions[\"A\"].width = 10\nws.column_dimensions[\"B\"].width = 10\nws.column_dimensions[\"C\"].width = 34\n","python",[14,230,231,241,255,262,275,288,293,305,328,348,380,386,397,408,419,441,456,462,480,494],{"__ignoreMap":202},[206,232,233,237],{"class":208,"line":209},[206,234,236],{"class":235},"s-kum","import",[206,238,240],{"class":239},"skGVy"," math\n",[206,242,244,247,250,252],{"class":208,"line":243},2,[206,245,246],{"class":235},"from",[206,248,249],{"class":239}," copy ",[206,251,236],{"class":235},[206,253,254],{"class":239}," copy\n",[206,256,258],{"class":208,"line":257},3,[206,259,261],{"emptyLinePlaceholder":260},true,"\n",[206,263,265,267,270,272],{"class":208,"line":264},4,[206,266,246],{"class":235},[206,268,269],{"class":239}," openpyxl ",[206,271,236],{"class":235},[206,273,274],{"class":239}," Workbook\n",[206,276,278,280,283,285],{"class":208,"line":277},5,[206,279,246],{"class":235},[206,281,282],{"class":239}," openpyxl.styles ",[206,284,236],{"class":235},[206,286,287],{"class":239}," Alignment, Font\n",[206,289,291],{"class":208,"line":290},6,[206,292,261],{"emptyLinePlaceholder":260},[206,294,296,299,302],{"class":208,"line":295},7,[206,297,298],{"class":239},"tickets ",[206,300,301],{"class":235},"=",[206,303,304],{"class":239}," [\n",[206,306,308,311,314,317,320,322,325],{"class":208,"line":307},8,[206,309,310],{"class":239},"    (",[206,312,313],{"class":216},"\"T-1\"",[206,315,316],{"class":239},", ",[206,318,319],{"class":216},"\"High\"",[206,321,316],{"class":239},[206,323,324],{"class":216},"\"Customer reports that the monthly export stops at row 65,536 when opened in the old viewer.\"",[206,326,327],{"class":239},"),\n",[206,329,331,333,336,338,341,343,346],{"class":208,"line":330},9,[206,332,310],{"class":239},[206,334,335],{"class":216},"\"T-2\"",[206,337,316],{"class":239},[206,339,340],{"class":216},"\"Low\"",[206,342,316],{"class":239},[206,344,345],{"class":216},"\"Short note\"",[206,347,327],{"class":239},[206,349,351,353,356,358,361,363,366,370,373,375,378],{"class":208,"line":350},10,[206,352,310],{"class":239},[206,354,355],{"class":216},"\"T-3\"",[206,357,316],{"class":239},[206,359,360],{"class":216},"\"Medium\"",[206,362,316],{"class":239},[206,364,365],{"class":216},"\"Line one",[206,367,369],{"class":368},"sSjpA","\\n",[206,371,372],{"class":216},"Line two",[206,374,369],{"class":368},[206,376,377],{"class":216},"Line three\"",[206,379,327],{"class":239},[206,381,383],{"class":208,"line":382},11,[206,384,385],{"class":239},"]\n",[206,387,389,392,394],{"class":208,"line":388},12,[206,390,391],{"class":239},"wb ",[206,393,301],{"class":235},[206,395,396],{"class":239}," Workbook()\n",[206,398,400,403,405],{"class":208,"line":399},13,[206,401,402],{"class":239},"ws ",[206,404,301],{"class":235},[206,406,407],{"class":239}," wb.active\n",[206,409,411,414,416],{"class":208,"line":410},14,[206,412,413],{"class":239},"ws.title ",[206,415,301],{"class":235},[206,417,418],{"class":216}," \"Tickets\"\n",[206,420,422,425,428,430,433,435,438],{"class":208,"line":421},15,[206,423,424],{"class":239},"ws.append([",[206,426,427],{"class":216},"\"Ticket\"",[206,429,316],{"class":239},[206,431,432],{"class":216},"\"Priority\"",[206,434,316],{"class":239},[206,436,437],{"class":216},"\"Description\"",[206,439,440],{"class":239},"])\n",[206,442,444,447,450,453],{"class":208,"line":443},16,[206,445,446],{"class":235},"for",[206,448,449],{"class":239}," ticket ",[206,451,452],{"class":235},"in",[206,454,455],{"class":239}," tickets:\n",[206,457,459],{"class":208,"line":458},17,[206,460,461],{"class":239},"    ws.append(ticket)\n",[206,463,465,468,471,474,476],{"class":208,"line":464},18,[206,466,467],{"class":239},"ws.column_dimensions[",[206,469,470],{"class":216},"\"A\"",[206,472,473],{"class":239},"].width ",[206,475,301],{"class":235},[206,477,479],{"class":478},"sP0c6"," 10\n",[206,481,483,485,488,490,492],{"class":208,"line":482},19,[206,484,467],{"class":239},[206,486,487],{"class":216},"\"B\"",[206,489,473],{"class":239},[206,491,301],{"class":235},[206,493,479],{"class":478},[206,495,497,499,502,504,506],{"class":208,"line":496},20,[206,498,467],{"class":239},[206,500,501],{"class":216},"\"C\"",[206,503,473],{"class":239},[206,505,301],{"class":235},[206,507,508],{"class":478}," 34\n",[192,510,512],{"id":511},"step-1-wrap-the-text-and-align-it-to-the-top","Step 1 — Wrap the text and align it to the top",[10,514,515,516,518,519,522,523,526],{},"Give every body cell an ",[14,517,16],{}," with ",[14,520,521],{},"wrap_text=True"," and ",[14,524,525],{},"vertical=\"top\"",". Wrapping breaks the text at the column width and at newline characters; top alignment makes every cell in a tall row start on the same line:",[197,528,530],{"className":226,"code":529,"language":228,"meta":202,"style":202},"for row in ws.iter_rows(min_row=2):\n    for cell in row:\n        cell.alignment = Alignment(wrap_text=True, vertical=\"top\")\nwb.save(\"tickets.xlsx\")\nprint(ws[\"C4\"].alignment.wrap_text, ws[\"C4\"].alignment.vertical)     # True top\n",[14,531,532,555,568,599,609],{"__ignoreMap":202},[206,533,534,536,539,541,544,548,550,552],{"class":208,"line":209},[206,535,446],{"class":235},[206,537,538],{"class":239}," row ",[206,540,452],{"class":235},[206,542,543],{"class":239}," ws.iter_rows(",[206,545,547],{"class":546},"sa561","min_row",[206,549,301],{"class":235},[206,551,121],{"class":478},[206,553,554],{"class":239},"):\n",[206,556,557,560,563,565],{"class":208,"line":243},[206,558,559],{"class":235},"    for",[206,561,562],{"class":239}," cell ",[206,564,452],{"class":235},[206,566,567],{"class":239}," row:\n",[206,569,570,573,575,578,581,583,586,588,591,593,596],{"class":208,"line":257},[206,571,572],{"class":239},"        cell.alignment ",[206,574,301],{"class":235},[206,576,577],{"class":239}," Alignment(",[206,579,580],{"class":546},"wrap_text",[206,582,301],{"class":235},[206,584,585],{"class":478},"True",[206,587,316],{"class":239},[206,589,590],{"class":546},"vertical",[206,592,301],{"class":235},[206,594,595],{"class":216},"\"top\"",[206,597,598],{"class":239},")\n",[206,600,601,604,607],{"class":208,"line":264},[206,602,603],{"class":239},"wb.save(",[206,605,606],{"class":216},"\"tickets.xlsx\"",[206,608,598],{"class":239},[206,610,611,614,617,620,623,625,628],{"class":208,"line":277},[206,612,613],{"class":478},"print",[206,615,616],{"class":239},"(ws[",[206,618,619],{"class":216},"\"C4\"",[206,621,622],{"class":239},"].alignment.wrap_text, ws[",[206,624,619],{"class":216},[206,626,627],{"class":239},"].alignment.vertical)     ",[206,629,631],{"class":630},"s-wDw","# True top\n",[10,633,634,635,637,638,642,643,646],{},"Without ",[14,636,525],{},", Excel's default bottom alignment puts \"T-1\" and \"High\" level with the ",[639,640,641],"em",{},"last"," line of a three-line description, which reads as though they belong to the row below. Top alignment is almost always right for lists with wrapped text. Newlines in the data — ",[14,644,645],{},"\"Line one\\nLine two\""," — only show as line breaks when wrapping is on; otherwise the text runs together.",[192,648,650],{"id":649},"step-2-set-row-heights-that-fit","Step 2 — Set row heights that fit",[10,652,653],{},"Whether rows grow to fit wrapped text depends on the application: rendering this file in LibreOffice adjusted the heights automatically, while Excel does not always recalculate heights for files it did not write itself. When the layout matters, set the heights. A good estimate counts the lines each cell needs at its column width:",[197,655,657],{"className":226,"code":656,"language":228,"meta":202,"style":202},"def lines_needed(text, width_chars):\n    if text is None:\n        return 1\n    return sum(max(1, math.ceil(len(part) \u002F max(width_chars - 1, 1))) for part in str(text).split(\"\\n\"))\n\ndef fit_row_heights(ws, line_height=15, min_row=2):\n    for row in ws.iter_rows(min_row=min_row):\n        lines = 1\n        for cell in row:\n            if cell.alignment.wrap_text:\n                width = ws.column_dimensions[cell.column_letter].width or 8.43\n                lines = max(lines, lines_needed(cell.value, width))\n        ws.row_dimensions[row[0].row].height = lines * line_height\n\nfit_row_heights(ws)\nwb.save(\"tickets.xlsx\")\nprint([ws.row_dimensions[r].height for r in range(2, 5)])       # [45.0, 15.0, 45.0]\n",[14,658,659,671,688,696,768,772,796,813,822,833,841,857,869,890,894,899,907],{"__ignoreMap":202},[206,660,661,664,668],{"class":208,"line":209},[206,662,663],{"class":235},"def",[206,665,667],{"class":666},"s_Opv"," lines_needed",[206,669,670],{"class":239},"(text, width_chars):\n",[206,672,673,676,679,682,685],{"class":208,"line":243},[206,674,675],{"class":235},"    if",[206,677,678],{"class":239}," text ",[206,680,681],{"class":235},"is",[206,683,684],{"class":478}," None",[206,686,687],{"class":239},":\n",[206,689,690,693],{"class":208,"line":257},[206,691,692],{"class":235},"        return",[206,694,695],{"class":478}," 1\n",[206,697,698,701,704,707,710,712,714,717,720,723,726,729,732,735,738,740,742,745,747,750,752,755,758,761,763,765],{"class":208,"line":264},[206,699,700],{"class":235},"    return",[206,702,703],{"class":478}," sum",[206,705,706],{"class":239},"(",[206,708,709],{"class":478},"max",[206,711,706],{"class":239},[206,713,95],{"class":478},[206,715,716],{"class":239},", math.ceil(",[206,718,719],{"class":478},"len",[206,721,722],{"class":239},"(part) ",[206,724,725],{"class":235},"\u002F",[206,727,728],{"class":478}," max",[206,730,731],{"class":239},"(width_chars ",[206,733,734],{"class":235},"-",[206,736,737],{"class":478}," 1",[206,739,316],{"class":239},[206,741,95],{"class":478},[206,743,744],{"class":239},"))) ",[206,746,446],{"class":235},[206,748,749],{"class":239}," part ",[206,751,452],{"class":235},[206,753,754],{"class":478}," str",[206,756,757],{"class":239},"(text).split(",[206,759,760],{"class":216},"\"",[206,762,369],{"class":368},[206,764,760],{"class":216},[206,766,767],{"class":239},"))\n",[206,769,770],{"class":208,"line":277},[206,771,261],{"emptyLinePlaceholder":260},[206,773,774,776,779,782,784,787,790,792,794],{"class":208,"line":290},[206,775,663],{"class":235},[206,777,778],{"class":666}," fit_row_heights",[206,780,781],{"class":239},"(ws, line_height",[206,783,301],{"class":235},[206,785,786],{"class":478},"15",[206,788,789],{"class":239},", min_row",[206,791,301],{"class":235},[206,793,121],{"class":478},[206,795,554],{"class":239},[206,797,798,800,802,804,806,808,810],{"class":208,"line":295},[206,799,559],{"class":235},[206,801,538],{"class":239},[206,803,452],{"class":235},[206,805,543],{"class":239},[206,807,547],{"class":546},[206,809,301],{"class":235},[206,811,812],{"class":239},"min_row):\n",[206,814,815,818,820],{"class":208,"line":307},[206,816,817],{"class":239},"        lines ",[206,819,301],{"class":235},[206,821,695],{"class":478},[206,823,824,827,829,831],{"class":208,"line":330},[206,825,826],{"class":235},"        for",[206,828,562],{"class":239},[206,830,452],{"class":235},[206,832,567],{"class":239},[206,834,835,838],{"class":208,"line":350},[206,836,837],{"class":235},"            if",[206,839,840],{"class":239}," cell.alignment.wrap_text:\n",[206,842,843,846,848,851,854],{"class":208,"line":382},[206,844,845],{"class":239},"                width ",[206,847,301],{"class":235},[206,849,850],{"class":239}," ws.column_dimensions[cell.column_letter].width ",[206,852,853],{"class":235},"or",[206,855,856],{"class":478}," 8.43\n",[206,858,859,862,864,866],{"class":208,"line":388},[206,860,861],{"class":239},"                lines ",[206,863,301],{"class":235},[206,865,728],{"class":478},[206,867,868],{"class":239},"(lines, lines_needed(cell.value, width))\n",[206,870,871,874,876,879,881,884,887],{"class":208,"line":399},[206,872,873],{"class":239},"        ws.row_dimensions[row[",[206,875,47],{"class":478},[206,877,878],{"class":239},"].row].height ",[206,880,301],{"class":235},[206,882,883],{"class":239}," lines ",[206,885,886],{"class":235},"*",[206,888,889],{"class":239}," line_height\n",[206,891,892],{"class":208,"line":410},[206,893,261],{"emptyLinePlaceholder":260},[206,895,896],{"class":208,"line":421},[206,897,898],{"class":239},"fit_row_heights(ws)\n",[206,900,901,903,905],{"class":208,"line":443},[206,902,603],{"class":239},[206,904,606],{"class":216},[206,906,598],{"class":239},[206,908,909,911,914,916,919,921,924,926,928,930,933,936],{"class":208,"line":458},[206,910,613],{"class":478},[206,912,913],{"class":239},"([ws.row_dimensions[r].height ",[206,915,446],{"class":235},[206,917,918],{"class":239}," r ",[206,920,452],{"class":235},[206,922,923],{"class":478}," range",[206,925,706],{"class":239},[206,927,121],{"class":478},[206,929,316],{"class":239},[206,931,932],{"class":478},"5",[206,934,935],{"class":239},")])       ",[206,937,938],{"class":630},"# [45.0, 15.0, 45.0]\n",[10,940,941,942,946],{},"Column width in openpyxl is measured in characters of the default font, and a line of 11-point Calibri is about 15 points tall, so the estimate is close for ordinary text. Wide letters and bold text need a little more room; add a line of slack for descriptions where clipping would hide information. Column widths themselves are covered in ",[19,943,945],{"href":944},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fset-column-width-and-row-height-in-openpyxl\u002F","Set Column Width and Row Height in openpyxl",".",[25,948,34,954,34,957,34,960,34,964,34,973,34,984,34,990,34,995,34,1001,34,1008,34,1012,34,1017,34,1021,34,1028,34,1032,34,1038,34,1041,34,1047,34,1052,34,1056,34,1060,34,1065,34,1068,34,1073,34,1076,34,1081,34,1084],{"viewBox":949,"role":28,"ariaLabelledBy":950,"xmlns":32,"style":953},"4 0 752 233",[951,952],"wr-heights-t","wr-heights-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,955,956],{"id":951},"Letting the application size rows or setting heights",[40,958,959],{"id":952},"Leaving heights unset relies on the application to autofit wrapped rows, which LibreOffice did in our test but Excel does not always do for generated files. Setting heights from an estimate of wrapped lines gives the same layout everywhere, at the cost of occasionally too much or too little space for unusual text.",[44,961],{"x":169,"y":47,"width":962,"height":963,"fill":50},"752","233",[44,965],{"x":966,"y":54,"width":967,"height":968,"rx":969,"fill":970,"stroke":971,"style":972},"20.0","351.0","201.0","14","#fdefd8","var(--gold,#b4740a)","stroke-width:2px",[974,975,977],"g",{"transform":976},"translate(182.5,29.0) scale(1.083)",[978,979],"path",{"d":980,"fill":981,"stroke":982,"style":983},"M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z M12 9a3 3 0 1 0 .01 0z","none","var(--gold-ink,#7a4e06)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[65,985,989],{"x":986,"y":987,"style":988},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Leave heights unset",[208,991],{"x1":992,"y1":993,"x2":994,"y2":993,"stroke":971,"style":59},"36.0","91.0","355.0",[65,996,1000],{"x":997,"y":998,"style":999},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","no code",[974,1002,1004],{"transform":1003},"translate(38.5,99.5) scale(0.625)",[978,1005],{"d":1006,"fill":981,"stroke":136,"style":1007},"M5 12.5l4.5 4.5L19 7","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[65,1009,1011],{"x":997,"y":1010,"style":999},"137.0","exact fit where autofit runs",[974,1013,1015],{"transform":1014},"translate(38.5,125.5) scale(0.625)",[978,1016],{"d":1006,"fill":981,"stroke":136,"style":1007},[65,1018,1020],{"x":997,"y":1019,"style":999},"163.0","rows may stay one line tall",[974,1022,1024],{"transform":1023},"translate(38.5,151.5) scale(0.625)",[978,1025],{"d":1026,"fill":981,"stroke":1027,"style":1007},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)",[65,1029,1031],{"x":997,"y":1030,"style":999},"189.0","depends on the application",[974,1033,1035],{"transform":1034},"translate(38.5,177.5) scale(0.625)",[978,1036],{"d":1037,"fill":981,"stroke":971,"style":1007},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4",[44,1039],{"x":1040,"y":54,"width":967,"height":968,"rx":969,"fill":135,"stroke":136,"style":972},"389.0",[974,1042,1044],{"transform":1043},"translate(551.5,29.0) scale(1.083)",[978,1045],{"d":1006,"fill":981,"stroke":1046,"style":983},"var(--teal-ink,#0b6157)",[65,1048,1051],{"x":1049,"y":987,"style":1050},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Set heights from an estimate",[208,1053],{"x1":1054,"y1":993,"x2":1055,"y2":993,"stroke":136,"style":59},"405.0","724.0",[65,1057,1059],{"x":1058,"y":998,"style":999},"429.0","same layout everywhere",[974,1061,1063],{"transform":1062},"translate(407.5,99.5) scale(0.625)",[978,1064],{"d":1006,"fill":981,"stroke":136,"style":1007},[65,1066,1067],{"x":1058,"y":1010,"style":999},"predictable printouts",[974,1069,1071],{"transform":1070},"translate(407.5,125.5) scale(0.625)",[978,1072],{"d":1006,"fill":981,"stroke":136,"style":1007},[65,1074,1075],{"x":1058,"y":1019,"style":999},"estimate, not a measurement",[974,1077,1079],{"transform":1078},"translate(407.5,151.5) scale(0.625)",[978,1080],{"d":1037,"fill":981,"stroke":971,"style":1007},[65,1082,1083],{"x":1058,"y":1030,"style":999},"update if widths change",[974,1085,1087],{"transform":1086},"translate(407.5,177.5) scale(0.625)",[978,1088],{"d":1026,"fill":981,"stroke":1027,"style":1007},[192,1090,1092],{"id":1091},"step-3-horizontal-alignment-that-matches-the-content","Step 3 — Horizontal alignment that matches the content",[10,1094,1095],{},"Text reads best left-aligned and numbers right-aligned — which is Excel's default for each type — so the main job is to leave them alone and fix the exceptions. Short codes such as priorities look tidier centred, and headers should align with the data beneath them:",[197,1097,1099],{"className":226,"code":1098,"language":228,"meta":202,"style":202},"ws[\"B1\"].alignment = Alignment(horizontal=\"center\", vertical=\"bottom\")\nfor (cell,) in ws.iter_rows(min_row=2, min_col=2, max_col=2):\n    cell.alignment = Alignment(horizontal=\"center\", vertical=\"top\", wrap_text=True)\nfor cell in ws[1]:\n    font = copy(cell.font)\n    font.bold = True\n    cell.font = font\nwb.save(\"tickets.xlsx\")\n",[14,1100,1101,1135,1172,1205,1221,1231,1241,1251],{"__ignoreMap":202},[206,1102,1103,1106,1109,1112,1114,1116,1119,1121,1124,1126,1128,1130,1133],{"class":208,"line":209},[206,1104,1105],{"class":239},"ws[",[206,1107,1108],{"class":216},"\"B1\"",[206,1110,1111],{"class":239},"].alignment ",[206,1113,301],{"class":235},[206,1115,577],{"class":239},[206,1117,1118],{"class":546},"horizontal",[206,1120,301],{"class":235},[206,1122,1123],{"class":216},"\"center\"",[206,1125,316],{"class":239},[206,1127,590],{"class":546},[206,1129,301],{"class":235},[206,1131,1132],{"class":216},"\"bottom\"",[206,1134,598],{"class":239},[206,1136,1137,1139,1142,1144,1146,1148,1150,1152,1154,1157,1159,1161,1163,1166,1168,1170],{"class":208,"line":243},[206,1138,446],{"class":235},[206,1140,1141],{"class":239}," (cell,) ",[206,1143,452],{"class":235},[206,1145,543],{"class":239},[206,1147,547],{"class":546},[206,1149,301],{"class":235},[206,1151,121],{"class":478},[206,1153,316],{"class":239},[206,1155,1156],{"class":546},"min_col",[206,1158,301],{"class":235},[206,1160,121],{"class":478},[206,1162,316],{"class":239},[206,1164,1165],{"class":546},"max_col",[206,1167,301],{"class":235},[206,1169,121],{"class":478},[206,1171,554],{"class":239},[206,1173,1174,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199,1201,1203],{"class":208,"line":257},[206,1175,1176],{"class":239},"    cell.alignment ",[206,1178,301],{"class":235},[206,1180,577],{"class":239},[206,1182,1118],{"class":546},[206,1184,301],{"class":235},[206,1186,1123],{"class":216},[206,1188,316],{"class":239},[206,1190,590],{"class":546},[206,1192,301],{"class":235},[206,1194,595],{"class":216},[206,1196,316],{"class":239},[206,1198,580],{"class":546},[206,1200,301],{"class":235},[206,1202,585],{"class":478},[206,1204,598],{"class":239},[206,1206,1207,1209,1211,1213,1216,1218],{"class":208,"line":264},[206,1208,446],{"class":235},[206,1210,562],{"class":239},[206,1212,452],{"class":235},[206,1214,1215],{"class":239}," ws[",[206,1217,95],{"class":478},[206,1219,1220],{"class":239},"]:\n",[206,1222,1223,1226,1228],{"class":208,"line":277},[206,1224,1225],{"class":239},"    font ",[206,1227,301],{"class":235},[206,1229,1230],{"class":239}," copy(cell.font)\n",[206,1232,1233,1236,1238],{"class":208,"line":290},[206,1234,1235],{"class":239},"    font.bold ",[206,1237,301],{"class":235},[206,1239,1240],{"class":478}," True\n",[206,1242,1243,1246,1248],{"class":208,"line":295},[206,1244,1245],{"class":239},"    cell.font ",[206,1247,301],{"class":235},[206,1249,1250],{"class":239}," font\n",[206,1252,1253,1255,1257],{"class":208,"line":307},[206,1254,603],{"class":239},[206,1256,606],{"class":216},[206,1258,598],{"class":239},[10,1260,1261,1262,1264,1265,522,1267,1269],{},"Every assignment replaces the whole ",[14,1263,16],{},", so repeat ",[14,1266,525],{},[14,1268,580],{}," when changing only the horizontal setting — or copy the existing alignment and change one attribute, the same pattern used for fonts. A header over a numeric column should be right-aligned so it sits above the digits it describes; a centred header over right-aligned numbers looks slightly off, especially when the values vary in length.",[192,1271,1273],{"id":1272},"step-4-indent-hierarchies-and-rotate-narrow-headers","Step 4 — Indent hierarchies and rotate narrow headers",[10,1275,1276,1277,1280,1281,1284],{},"Two less common settings solve specific layout problems. ",[14,1278,1279],{},"indent"," shows a hierarchy in one column — categories and their items — without extra columns. ",[14,1282,1283],{},"text_rotation"," turns long headers above narrow columns sideways:",[197,1286,1288],{"className":226,"code":1287,"language":228,"meta":202,"style":202},"ws2 = wb.create_sheet(\"Budget\")\nfor label, level in [(\"Operations\", 0), (\"Rent\", 1), (\"Utilities\", 1), (\"Staff\", 0), (\"Salaries\", 1)]:\n    ws2.append([label])\n    ws2.cell(ws2.max_row, 1).alignment = Alignment(horizontal=\"left\", indent=level * 2)\nws2[\"C1\"], ws2[\"D1\"] = \"Actual this month\", \"Budget this month\"\nfor cell in (ws2[\"C1\"], ws2[\"D1\"]):\n    cell.alignment = Alignment(text_rotation=90, horizontal=\"center\", vertical=\"bottom\")\nws2.row_dimensions[1].height = 110\nws2.column_dimensions[\"C\"].width = ws2.column_dimensions[\"D\"].width = 6\nwb.save(\"tickets.xlsx\")\nprint(ws2[\"A2\"].alignment.indent, ws2[\"C1\"].alignment.text_rotation)     # 2.0 90\n",[14,1289,1290,1305,1364,1369,1406,1433,1453,1486,1501,1525,1533],{"__ignoreMap":202},[206,1291,1292,1295,1297,1300,1303],{"class":208,"line":209},[206,1293,1294],{"class":239},"ws2 ",[206,1296,301],{"class":235},[206,1298,1299],{"class":239}," wb.create_sheet(",[206,1301,1302],{"class":216},"\"Budget\"",[206,1304,598],{"class":239},[206,1306,1307,1309,1312,1314,1317,1320,1322,1324,1327,1330,1332,1334,1336,1339,1341,1343,1345,1348,1350,1352,1354,1357,1359,1361],{"class":208,"line":243},[206,1308,446],{"class":235},[206,1310,1311],{"class":239}," label, level ",[206,1313,452],{"class":235},[206,1315,1316],{"class":239}," [(",[206,1318,1319],{"class":216},"\"Operations\"",[206,1321,316],{"class":239},[206,1323,47],{"class":478},[206,1325,1326],{"class":239},"), (",[206,1328,1329],{"class":216},"\"Rent\"",[206,1331,316],{"class":239},[206,1333,95],{"class":478},[206,1335,1326],{"class":239},[206,1337,1338],{"class":216},"\"Utilities\"",[206,1340,316],{"class":239},[206,1342,95],{"class":478},[206,1344,1326],{"class":239},[206,1346,1347],{"class":216},"\"Staff\"",[206,1349,316],{"class":239},[206,1351,47],{"class":478},[206,1353,1326],{"class":239},[206,1355,1356],{"class":216},"\"Salaries\"",[206,1358,316],{"class":239},[206,1360,95],{"class":478},[206,1362,1363],{"class":239},")]:\n",[206,1365,1366],{"class":208,"line":257},[206,1367,1368],{"class":239},"    ws2.append([label])\n",[206,1370,1371,1374,1376,1379,1381,1383,1385,1387,1390,1392,1394,1396,1399,1401,1404],{"class":208,"line":264},[206,1372,1373],{"class":239},"    ws2.cell(ws2.max_row, ",[206,1375,95],{"class":478},[206,1377,1378],{"class":239},").alignment ",[206,1380,301],{"class":235},[206,1382,577],{"class":239},[206,1384,1118],{"class":546},[206,1386,301],{"class":235},[206,1388,1389],{"class":216},"\"left\"",[206,1391,316],{"class":239},[206,1393,1279],{"class":546},[206,1395,301],{"class":235},[206,1397,1398],{"class":239},"level ",[206,1400,886],{"class":235},[206,1402,1403],{"class":478}," 2",[206,1405,598],{"class":239},[206,1407,1408,1411,1414,1417,1420,1423,1425,1428,1430],{"class":208,"line":277},[206,1409,1410],{"class":239},"ws2[",[206,1412,1413],{"class":216},"\"C1\"",[206,1415,1416],{"class":239},"], ws2[",[206,1418,1419],{"class":216},"\"D1\"",[206,1421,1422],{"class":239},"] ",[206,1424,301],{"class":235},[206,1426,1427],{"class":216}," \"Actual this month\"",[206,1429,316],{"class":239},[206,1431,1432],{"class":216},"\"Budget this month\"\n",[206,1434,1435,1437,1439,1441,1444,1446,1448,1450],{"class":208,"line":290},[206,1436,446],{"class":235},[206,1438,562],{"class":239},[206,1440,452],{"class":235},[206,1442,1443],{"class":239}," (ws2[",[206,1445,1413],{"class":216},[206,1447,1416],{"class":239},[206,1449,1419],{"class":216},[206,1451,1452],{"class":239},"]):\n",[206,1454,1455,1457,1459,1461,1463,1465,1468,1470,1472,1474,1476,1478,1480,1482,1484],{"class":208,"line":295},[206,1456,1176],{"class":239},[206,1458,301],{"class":235},[206,1460,577],{"class":239},[206,1462,1283],{"class":546},[206,1464,301],{"class":235},[206,1466,1467],{"class":478},"90",[206,1469,316],{"class":239},[206,1471,1118],{"class":546},[206,1473,301],{"class":235},[206,1475,1123],{"class":216},[206,1477,316],{"class":239},[206,1479,590],{"class":546},[206,1481,301],{"class":235},[206,1483,1132],{"class":216},[206,1485,598],{"class":239},[206,1487,1488,1491,1493,1496,1498],{"class":208,"line":307},[206,1489,1490],{"class":239},"ws2.row_dimensions[",[206,1492,95],{"class":478},[206,1494,1495],{"class":239},"].height ",[206,1497,301],{"class":235},[206,1499,1500],{"class":478}," 110\n",[206,1502,1503,1506,1508,1510,1512,1515,1518,1520,1522],{"class":208,"line":330},[206,1504,1505],{"class":239},"ws2.column_dimensions[",[206,1507,501],{"class":216},[206,1509,473],{"class":239},[206,1511,301],{"class":235},[206,1513,1514],{"class":239}," ws2.column_dimensions[",[206,1516,1517],{"class":216},"\"D\"",[206,1519,473],{"class":239},[206,1521,301],{"class":235},[206,1523,1524],{"class":478}," 6\n",[206,1526,1527,1529,1531],{"class":208,"line":350},[206,1528,603],{"class":239},[206,1530,606],{"class":216},[206,1532,598],{"class":239},[206,1534,1535,1537,1540,1543,1546,1548,1551],{"class":208,"line":382},[206,1536,613],{"class":478},[206,1538,1539],{"class":239},"(ws2[",[206,1541,1542],{"class":216},"\"A2\"",[206,1544,1545],{"class":239},"].alignment.indent, ws2[",[206,1547,1413],{"class":216},[206,1549,1550],{"class":239},"].alignment.text_rotation)     ",[206,1552,1553],{"class":630},"# 2.0 90\n",[10,1555,1556,1557,1559],{},"Indent levels are counted in steps of about three characters; two levels per hierarchy step reads clearly. Rotated headers need a taller header row — set it explicitly, because the rotated text will not grow the row in every application. ",[14,1558,1283],{}," takes 0 to 90 for text rising to the right and 91 to 180 for text falling, where 180 means −90°; 255 stacks the letters vertically, one under another, which is rarely easier to read than simple rotation.",[25,1561,34,1567,34,1570,34,1573,34,1577,34,1580,34,1585,34,1591,34,1595,34,1598,34,1600,34,1605,34,1608,34,1611,34,1613,34,1616,34,1618,34,1622,34,1624,34,1628,34,1631,34,1635,34,1637,34,1641,34,1644,34,1648,34,1650,34,1654,34,1657,34,1661,34,1664,34,1668,34,1670],{"viewBox":1562,"role":28,"ariaLabelledBy":1563,"xmlns":32,"style":1566},"4 0 1240 276",[1564,1565],"wr-options-t","wr-options-d","width:100%;max-width:1240px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,1568,1569],{"id":1564},"Alignment settings and what they solve",[40,1571,1572],{"id":1565},"wrap_text fits long text in a column. vertical top keeps multi-line rows aligned. horizontal centerContinuous centres a title across columns without merging. indent shows hierarchy in a single column. text_rotation fits long headers over narrow columns. shrink_to_fit makes text smaller instead of wrapping it.",[44,1574],{"x":169,"y":47,"width":1575,"height":1576,"fill":50},"1240","276",[44,1578],{"x":966,"y":54,"width":1579,"height":89,"rx":47,"fill":50,"stroke":58,"style":59},"232.2",[44,1581],{"x":1582,"y":54,"width":1583,"height":89,"rx":47,"fill":98,"stroke":99,"style":1584},"252.2","487.8","stroke-width:1.2px",[65,1586,1590],{"x":1587,"y":1588,"style":1589},"496.1","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Solves",[44,1592],{"x":966,"y":1593,"width":1579,"height":1594,"rx":47,"fill":57,"stroke":58,"style":59},"56.0","34.0",[65,1596,521],{"x":55,"y":987,"style":1597},"font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start",[44,1599],{"x":1582,"y":1593,"width":1583,"height":1594,"rx":47,"fill":50,"stroke":58,"style":59},[65,1601,1604],{"x":1587,"y":1602,"style":1603},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","long text in a column",[44,1606],{"x":966,"y":1607,"width":1579,"height":1594,"rx":47,"fill":57,"stroke":58,"style":59},"90.0",[65,1609,525],{"x":55,"y":1610,"style":1597},"112.0",[44,1612],{"x":1582,"y":1607,"width":1583,"height":1594,"rx":47,"fill":50,"stroke":58,"style":59},[65,1614,1615],{"x":1587,"y":998,"style":1603},"misaligned multi-line rows",[44,1617],{"x":966,"y":165,"width":1579,"height":1594,"rx":47,"fill":57,"stroke":58,"style":59},[65,1619,1621],{"x":55,"y":1620,"style":1597},"146.0","horizontal=\"centerContinuous\"",[44,1623],{"x":1582,"y":165,"width":1583,"height":1594,"rx":47,"fill":50,"stroke":58,"style":59},[65,1625,1627],{"x":1587,"y":1626,"style":1603},"145.0","titles without merging",[44,1629],{"x":966,"y":1630,"width":1579,"height":1594,"rx":47,"fill":57,"stroke":58,"style":59},"158.0",[65,1632,1634],{"x":55,"y":1633,"style":1597},"180.0","indent=n",[44,1636],{"x":1582,"y":1630,"width":1583,"height":1594,"rx":47,"fill":50,"stroke":58,"style":59},[65,1638,1640],{"x":1587,"y":1639,"style":1603},"179.0","hierarchy in one column",[44,1642],{"x":966,"y":1643,"width":1579,"height":1594,"rx":47,"fill":57,"stroke":58,"style":59},"192.0",[65,1645,1647],{"x":55,"y":1646,"style":1597},"214.0","text_rotation=90",[44,1649],{"x":1582,"y":1643,"width":1583,"height":1594,"rx":47,"fill":50,"stroke":58,"style":59},[65,1651,1653],{"x":1587,"y":1652,"style":1603},"213.0","long headers",[44,1655],{"x":1656,"y":1643,"width":1583,"height":1594,"rx":47,"fill":50,"stroke":58,"style":59},"740.0",[65,1658,1660],{"x":1659,"y":1652,"style":1603},"983.9","narrow columns",[44,1662],{"x":966,"y":1663,"width":1579,"height":1594,"rx":47,"fill":57,"stroke":58,"style":59},"226.0",[65,1665,1667],{"x":55,"y":1666,"style":1597},"248.0","shrink_to_fit=True",[44,1669],{"x":1582,"y":1663,"width":1583,"height":1594,"rx":47,"fill":50,"stroke":58,"style":59},[65,1671,1673],{"x":1587,"y":1672,"style":1603},"247.0","slightly too long labels",[192,1675,1677],{"id":1676},"step-5-centre-a-title-without-merging","Step 5 — Centre a title without merging",[10,1679,1680,1681,1684],{},"A report title centred over the table is usually made with merged cells, which then get in the way of sorting, selecting and copying. ",[14,1682,1683],{},"centerContinuous"," centres text across the selected cells while keeping them separate — set it on the title cell and on the empty cells it should span:",[197,1686,1688],{"className":226,"code":1687,"language":228,"meta":202,"style":202},"ws.insert_rows(1)\nws[\"A1\"] = \"Open tickets\"\nfor col in \"ABC\":\n    ws[f\"{col}1\"].alignment = Alignment(horizontal=\"centerContinuous\")\nfor r in range(2, ws.max_row + 1):                         # heights did not move with the rows\n    ws.row_dimensions[r].height = None\nfit_row_heights(ws, min_row=3)\nwb.save(\"tickets.xlsx\")\nprint(ws[\"A1\"].value, ws[\"C1\"].alignment.horizontal)      # Open tickets centerContinuous\nprint([ws.row_dimensions[r].height for r in range(3, 6)])  # [45.0, 15.0, 45.0]\n",[14,1689,1690,1699,1713,1727,1764,1792,1802,1815,1823,1842],{"__ignoreMap":202},[206,1691,1692,1695,1697],{"class":208,"line":209},[206,1693,1694],{"class":239},"ws.insert_rows(",[206,1696,95],{"class":478},[206,1698,598],{"class":239},[206,1700,1701,1703,1706,1708,1710],{"class":208,"line":243},[206,1702,1105],{"class":239},[206,1704,1705],{"class":216},"\"A1\"",[206,1707,1422],{"class":239},[206,1709,301],{"class":235},[206,1711,1712],{"class":216}," \"Open tickets\"\n",[206,1714,1715,1717,1720,1722,1725],{"class":208,"line":257},[206,1716,446],{"class":235},[206,1718,1719],{"class":239}," col ",[206,1721,452],{"class":235},[206,1723,1724],{"class":216}," \"ABC\"",[206,1726,687],{"class":239},[206,1728,1729,1732,1735,1737,1740,1743,1746,1749,1751,1753,1755,1757,1759,1762],{"class":208,"line":264},[206,1730,1731],{"class":239},"    ws[",[206,1733,1734],{"class":235},"f",[206,1736,760],{"class":216},[206,1738,1739],{"class":368},"{",[206,1741,1742],{"class":239},"col",[206,1744,1745],{"class":368},"}",[206,1747,1748],{"class":216},"1\"",[206,1750,1111],{"class":239},[206,1752,301],{"class":235},[206,1754,577],{"class":239},[206,1756,1118],{"class":546},[206,1758,301],{"class":235},[206,1760,1761],{"class":216},"\"centerContinuous\"",[206,1763,598],{"class":239},[206,1765,1766,1768,1770,1772,1774,1776,1778,1781,1784,1786,1789],{"class":208,"line":277},[206,1767,446],{"class":235},[206,1769,918],{"class":239},[206,1771,452],{"class":235},[206,1773,923],{"class":478},[206,1775,706],{"class":239},[206,1777,121],{"class":478},[206,1779,1780],{"class":239},", ws.max_row ",[206,1782,1783],{"class":235},"+",[206,1785,737],{"class":478},[206,1787,1788],{"class":239},"):                         ",[206,1790,1791],{"class":630},"# heights did not move with the rows\n",[206,1793,1794,1797,1799],{"class":208,"line":290},[206,1795,1796],{"class":239},"    ws.row_dimensions[r].height ",[206,1798,301],{"class":235},[206,1800,1801],{"class":478}," None\n",[206,1803,1804,1807,1809,1811,1813],{"class":208,"line":295},[206,1805,1806],{"class":239},"fit_row_heights(ws, ",[206,1808,547],{"class":546},[206,1810,301],{"class":235},[206,1812,147],{"class":478},[206,1814,598],{"class":239},[206,1816,1817,1819,1821],{"class":208,"line":307},[206,1818,603],{"class":239},[206,1820,606],{"class":216},[206,1822,598],{"class":239},[206,1824,1825,1827,1829,1831,1834,1836,1839],{"class":208,"line":330},[206,1826,613],{"class":478},[206,1828,616],{"class":239},[206,1830,1705],{"class":216},[206,1832,1833],{"class":239},"].value, ws[",[206,1835,1413],{"class":216},[206,1837,1838],{"class":239},"].alignment.horizontal)      ",[206,1840,1841],{"class":630},"# Open tickets centerContinuous\n",[206,1843,1844,1846,1848,1850,1852,1854,1856,1858,1860,1862,1865,1868],{"class":208,"line":350},[206,1845,613],{"class":478},[206,1847,913],{"class":239},[206,1849,446],{"class":235},[206,1851,918],{"class":239},[206,1853,452],{"class":235},[206,1855,923],{"class":478},[206,1857,706],{"class":239},[206,1859,147],{"class":478},[206,1861,316],{"class":239},[206,1863,1864],{"class":478},"6",[206,1866,1867],{"class":239},")])  ",[206,1869,938],{"class":630},[10,1871,1872,1873,1876,1877,1881,1882,1884],{},"The title appears centred across A1:C1, yet clicking C1 selects an ordinary empty cell. Note the extra lines: ",[14,1874,1875],{},"insert_rows"," moves cell values and styles down but leaves row heights at their old row numbers, so the 45-point height meant for T-1 would now sit on the header row. Clearing the heights and fitting them again after any row insertion keeps them with their rows. The trade-offs against a real merge are covered in ",[19,1878,1880],{"href":1879},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fmerge-cells-and-centre-a-report-title-with-openpyxl\u002F","Merge Cells and Centre a Report Title with openpyxl",". The last option, ",[14,1883,1667],{},", reduces the font size until the text fits the cell; it suits labels that are only slightly too long, and becomes unreadable for anything longer — wrap instead.",[192,1886,1888],{"id":1887},"common-pitfalls","Common pitfalls",[1890,1891,1892,1900,1906,1921,1927],"ul",{},[1893,1894,1895,1899],"li",{},[1896,1897,1898],"strong",{},"Vertical alignment on the header only."," Aligning body cells to the top but leaving the header at the bottom is fine; the reverse — body at the bottom — is what makes tall rows hard to scan.",[1893,1901,1902,1905],{},[1896,1903,1904],{},"Wrapping without widths."," A wrapped cell in a default-width column breaks after eight characters. Set a sensible column width first.",[1893,1907,1908,1911,1912,1915,1916,522,1918,1920],{},[1896,1909,1910],{},"Replacing alignment piecemeal."," ",[14,1913,1914],{},"Alignment(horizontal=\"center\")"," resets ",[14,1917,580],{},[14,1919,590],{},". Pass all the settings a cell needs, or copy and modify.",[1893,1922,1923,1926],{},[1896,1924,1925],{},"Inserting rows after sizing them."," Row heights stay at their row numbers when rows are inserted or deleted. Size rows last, after the sheet's structure is final.",[1893,1928,1929,1932,1933,1936],{},[1896,1930,1931],{},"Wrapping numbers."," Wrapping a numeric column never helps; a number too wide for its column shows as ",[14,1934,1935],{},"####",". Widen the column or change the number format.",[192,1938,1940],{"id":1939},"conclusion","Conclusion",[10,1942,1943,1944,1947,1948,1950],{},"Wrap long text with ",[14,1945,1946],{},"Alignment(wrap_text=True, vertical=\"top\")",", and set row heights from an estimate of the wrapped lines so the layout does not depend on whether the application autofits. Keep text left and numbers right, centre short codes, indent hierarchies, rotate headers over narrow columns and centre titles with ",[14,1949,1683],{}," instead of merging. Remember that each assignment replaces the whole alignment, so pass every setting a cell needs.",[192,1952,1954],{"id":1953},"frequently-asked-questions","Frequently asked questions",[10,1956,1957,1960],{},[1896,1958,1959],{},"How do I wrap text in a cell with openpyxl?","\nAssign an Alignment with wrap_text=True, for example cell.alignment = Alignment(wrap_text=True, vertical=\"top\"). The text then breaks at the column width and at any newline characters in the value.",[10,1962,1963,1966,1967,1970],{},[1896,1964,1965],{},"Why do my wrapped rows not get taller?","\nWhether rows grow automatically depends on the application. LibreOffice adjusted rows without a set height in our test; Excel does not always recalculate heights for files it did not write. Set row_dimensions",[206,1968,1969],{},"row",".height explicitly when the layout matters.",[10,1972,1973,1976],{},[1896,1974,1975],{},"How do I centre a title across several columns without merging?","\nPut the text in the first cell and set horizontal=\"centerContinuous\" on that cell and the empty cells to its right. The title is centred across them, while the cells stay separate for sorting and selection.",[10,1978,1979,1982],{},[1896,1980,1981],{},"How do I indent text in a cell?","\nUse Alignment(indent=n) with a horizontal alignment of left or right. Each level indents by roughly the width of three characters, which suits category and sub-category labels.",[192,1984,1986],{"id":1985},"related","Related",[1890,1988,1989,1995,2002,2009],{},[1893,1990,1991,1992,1994],{},"Up one level: ",[19,1993,22],{"href":21}," — fonts, fills, borders and layout.",[1893,1996,1997,2001],{},[19,1998,2000],{"href":1999},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fadd-borders-to-a-range-with-openpyxl\u002F","Add Borders to a Range with openpyxl"," — structure for the same tables.",[1893,2003,2004,2008],{},[19,2005,2007],{"href":2006},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Ffreeze-header-row-in-excel-with-openpyxl\u002F","Freeze the Header Row in Excel with openpyxl"," — keeping headers visible over long lists.",[1893,2010,2011,2015],{},[19,2012,2014],{"href":2013},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-rich-text-in-a-cell-with-xlsxwriter\u002F","Write Rich Text with Mixed Formats in One Cell with xlsxwriter"," — multi-line cells with mixed formatting.",[2017,2018,2019],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":202,"searchDepth":243,"depth":243,"links":2021},[2022,2023,2024,2025,2026,2027,2028,2029,2030,2031],{"id":194,"depth":243,"text":195},{"id":511,"depth":243,"text":512},{"id":649,"depth":243,"text":650},{"id":1091,"depth":243,"text":1092},{"id":1272,"depth":243,"text":1273},{"id":1676,"depth":243,"text":1677},{"id":1887,"depth":243,"text":1888},{"id":1939,"depth":243,"text":1940},{"id":1953,"depth":243,"text":1954},{"id":1985,"depth":243,"text":1986},"2026-09-23","Make long text readable in a generated Excel report — wrap text inside cells, set row heights that fit the wrapped lines, align text to the top so multi-line rows read cleanly, indent hierarchies, rotate narrow headers, centre a title across columns without merging and shrink text as a last resort.","md",[2036,2038,2040,2042],{"q":1959,"a":2037},"Assign an Alignment with wrap_text=True, for example cell.alignment = Alignment(wrap_text=True, vertical=\"top\"). The text then breaks at the column width and at any newline characters in the value.",{"q":1965,"a":2039},"Whether rows grow automatically depends on the application. LibreOffice adjusted rows without a set height in our test; Excel does not always recalculate heights for files it did not write. Set row_dimensions[row].height explicitly when the layout matters.",{"q":1975,"a":2041},"Put the text in the first cell and set horizontal=\"centerContinuous\" on that cell and the empty cells to its right. The title is centred across them, while the cells stay separate for sorting and selection.",{"q":1981,"a":2043},"Use Alignment(indent=n) with a horizontal alignment of left or right. Each level indents by roughly the width of three characters, which suits category and sub-category labels.","wrap text and align cells with openpyxl",null,{"breadcrumb":2047},[2048,2050,2053],{"name":2049,"item":725},"Home",{"name":2051,"item":2052},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":22,"item":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl",{"title":2056,"description":2057},"Wrap Text and Align Cells with openpyxl (Alignment, row height)","openpyxl Alignment in practice — wrap_text, estimating row heights for wrapped cells, vertical top alignment, horizontal alignment for numbers and text, indent levels, text_rotation for headers, centerContinuous instead of merging and shrink_to_fit.","wrap-text-and-align-cells-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl\u002Findex",[2061,2062],"cell-styling","openpyxl","how-to","VcZQTLqaYrKwzeMTYYe30UaiT6JmH1vKlTEgxFKVKio",[2066,2070],{"title":2067,"path":2068,"stem":2069,"children":-1},"Style Whole Columns and Rows Efficiently with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fstyle-whole-columns-and-rows-efficiently-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fstyle-whole-columns-and-rows-efficiently-with-openpyxl\u002Findex",{"title":2071,"path":2072,"stem":2073,"children":-1},"Write Rich Text in a Cell with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrite-rich-text-in-a-cell-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrite-rich-text-in-a-cell-with-openpyxl\u002Findex",[2075,2077],{"path":2076,"title":2051},"\u002Fformatting-and-charting-excel-reports-with-python",{"path":2078,"title":22},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl",[],[2081,2083,2086,2089,2092,2095],{"path":2082,"title":2014,"kind":2063},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-rich-text-in-a-cell-with-xlsxwriter",{"path":2084,"title":2085,"kind":2063},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fcopy-cell-styles-with-openpyxl","Copy Cell Styles from One Cell to Another with openpyxl",{"path":2087,"title":2088,"kind":2063},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fauto-fit-column-widths-when-writing-with-pandas","Auto-Fit Column Widths When Writing with pandas",{"path":2090,"title":2091,"kind":2063},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fexport-a-styled-pandas-dataframe-to-excel","Export a Styled pandas DataFrame to Excel",{"path":2093,"title":2094,"kind":2063},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fgenerate-a-staff-rota-in-excel-with-python","Generate a Staff Rota in Excel with Python",{"path":2096,"title":2097,"kind":2063},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel","Add a Table of Contents Sheet with Hyperlinks in Excel",1790972118709]