[{"data":1,"prerenderedAt":1184},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fadd-logo-image-to-excel-report-with-openpyxl":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fadd-logo-image-to-excel-report-with-openpyxl":1176},{"id":4,"title":5,"body":6,"dateModified":1153,"datePublished":1153,"description":1154,"extension":1155,"faq":1156,"meta":1167,"navigation":228,"path":1168,"seo":1169,"slug":1172,"stem":1173,"type":1174,"__hash__":1175},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fadd-logo-image-to-excel-report-with-openpyxl\u002Findex.md","Add a Logo Image to an Excel Report with openpyxl",{"type":7,"value":8,"toc":1141},"minimark",[9,19,142,147,155,183,186,190,193,287,291,302,393,397,412,624,628,631,768,772,783,891,898,902,1015,1029,1037,1041,1053,1059,1070,1083,1092,1096,1102,1106,1109,1118,1121,1137],[10,11,12,13,18],"p",{},"You have a report and you want your logo in the top-left corner. This page is the copy-paste recipe: create the image, anchor it at A1, size it to fit a header band, set a merged title next to it, and save — with every pitfall that bites people on the way. It is the hands-on companion to ",[14,15,17],"a",{"href":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002F","Inserting Images and Logos into Excel",", which covers the concepts; here we just build the thing.",[20,21,29,30,29,34,29,38,29,48,29,55,29,60,29,65,29,71,29,75,29,79,29,82,29,86,29,90,29,94,29,97,29,101,29,105,29,109,29,113,29,119,29,123,29,127,29,130,29,134,29,137],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"0 0 760 196","img",[25,26],"logo-flow-t","logo-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"Adding a logo to an Excel report with openpyxl",[35,36,37],"desc",{"id":26},"Four steps: start from a logo PNG, wrap it in an openpyxl Image and set its pixel size, anchor it at cell A1 beside a merged title, then save the workbook.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"rx":45,"fill":46,"stroke":47},"20","58","150","84","14","var(--brand-soft,rgba(91,92,240,0.12))","var(--line,#cdd5e6)",[49,50,54],"text",{"x":51,"y":52,"style":53},"95","40","font-size:12.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","1. source",[49,56,59],{"x":51,"y":57,"style":58},"96","font-size:14px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","logo.png",[49,61,64],{"x":51,"y":62,"style":63},"117","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","on disk",[39,66],{"x":67,"y":42,"width":43,"height":44,"rx":45,"fill":68,"stroke":69,"style":70},"205","var(--surface-muted,#eef2ff)","var(--brand,#5b5cf0)","stroke-width:2px",[49,72,74],{"x":73,"y":52,"style":53},"280","2. create",[49,76,78],{"x":73,"y":57,"style":77},"font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Image(path)",[49,80,81],{"x":73,"y":62,"style":63},"set width\u002Fheight",[39,83],{"x":84,"y":42,"width":43,"height":44,"rx":45,"fill":68,"stroke":85,"style":70},"390","var(--accent,#f43f8f)",[49,87,89],{"x":88,"y":52,"style":53},"465","3. anchor",[49,91,93],{"x":88,"y":57,"style":92},"font-size:13.5px;font-weight:700;fill:var(--accent,#f43f8f);text-anchor:middle","add_image",[49,95,96],{"x":88,"y":62,"style":63},"\"A1\" + title",[39,98],{"x":99,"y":42,"width":43,"height":44,"rx":45,"fill":69,"stroke":100},"575","var(--brand-strong,#4338ca)",[49,102,104],{"x":103,"y":52,"style":53},"650","4. save",[49,106,108],{"x":103,"y":57,"style":107},"font-size:14px;font-weight:700;fill:#ffffff;text-anchor:middle","report.xlsx",[49,110,112],{"x":103,"y":62,"style":111},"font-size:11.5px;fill:rgba(255,255,255,0.88);text-anchor:middle","wb.save()",[114,115],"line",{"x1":116,"y1":117,"x2":118,"y2":117,"stroke":69,"style":70},"172","100","203",[120,121],"polygon",{"points":122,"fill":69},"203,100 193,95 193,105",[114,124],{"x1":125,"y1":117,"x2":126,"y2":117,"stroke":69,"style":70},"357","388",[120,128],{"points":129,"fill":69},"388,100 378,95 378,105",[114,131],{"x1":132,"y1":117,"x2":133,"y2":117,"stroke":69,"style":70},"542","573",[120,135],{"points":136,"fill":69},"573,100 563,95 563,105",[49,138,141],{"x":139,"y":140,"style":63},"380","176","Pillow must be installed — openpyxl uses it to read and measure the image",[143,144,146],"h2",{"id":145},"prerequisites","Prerequisites",[10,148,149,150,154],{},"You need two libraries. openpyxl writes the workbook and its drawing XML; ",[151,152,153],"strong",{},"Pillow"," is what openpyxl uses to read and measure the image, so the logo step fails without it.",[156,157,162],"pre",{"className":158,"code":159,"language":160,"meta":161,"style":161},"language-bash shiki shiki-themes github-light github-dark","pip install openpyxl pillow\n","bash","",[163,164,165],"code",{"__ignoreMap":161},[166,167,169,173,177,180],"span",{"class":114,"line":168},1,[166,170,172],{"class":171},"sScJk","pip",[166,174,176],{"class":175},"sZZnC"," install",[166,178,179],{"class":175}," openpyxl",[166,181,182],{"class":175}," pillow\n",[10,184,185],{},"No Excel install is required — this runs unchanged on a server or CI runner.",[143,187,189],{"id":188},"step-1-create-a-sample-logo","Step 1: create a sample logo",[10,191,192],{},"So this guide runs end to end with nothing to download, generate a small placeholder PNG with Pillow. In your real report, skip this and point at your actual logo file.",[156,194,198],{"className":195,"code":196,"language":197,"meta":161,"style":161},"language-python shiki shiki-themes github-light github-dark","from PIL import Image as PILImage\n\nPILImage.new(\"RGB\", (180, 56), color=\"#4472C4\").save(\"logo.png\")\nprint(\"Wrote logo.png (180x56)\")\n","python",[163,199,200,223,230,273],{"__ignoreMap":161},[166,201,202,206,210,213,217,220],{"class":114,"line":168},[166,203,205],{"class":204},"szBVR","from",[166,207,209],{"class":208},"sj4cs"," PIL",[166,211,212],{"class":204}," import",[166,214,216],{"class":215},"sVt8B"," Image ",[166,218,219],{"class":204},"as",[166,221,222],{"class":215}," PILImage\n",[166,224,226],{"class":114,"line":225},2,[166,227,229],{"emptyLinePlaceholder":228},true,"\n",[166,231,233,236,239,242,245,248,251,254,258,261,264,267,270],{"class":114,"line":232},3,[166,234,235],{"class":215},"PILImage.new(",[166,237,238],{"class":175},"\"RGB\"",[166,240,241],{"class":215},", (",[166,243,244],{"class":208},"180",[166,246,247],{"class":215},", ",[166,249,250],{"class":208},"56",[166,252,253],{"class":215},"), ",[166,255,257],{"class":256},"s4XuR","color",[166,259,260],{"class":204},"=",[166,262,263],{"class":175},"\"#4472C4\"",[166,265,266],{"class":215},").save(",[166,268,269],{"class":175},"\"logo.png\"",[166,271,272],{"class":215},")\n",[166,274,276,279,282,285],{"class":114,"line":275},4,[166,277,278],{"class":208},"print",[166,280,281],{"class":215},"(",[166,283,284],{"class":175},"\"Wrote logo.png (180x56)\"",[166,286,272],{"class":215},[143,288,290],{"id":289},"step-2-open-a-workbook-and-reserve-a-header-band","Step 2: open a workbook and reserve a header band",[10,292,293,294,297,298,301],{},"Start a workbook (or ",[163,295,296],{},"load_workbook(\"report.xlsx\")"," for an existing one) and make the first row tall enough to hold the logo. Remember that the image will ",[151,299,300],{},"float"," over the grid — it does not push rows down — so reserving height by hand is what keeps it from sitting on top of your data.",[156,303,305],{"className":195,"code":304,"language":197,"meta":161,"style":161},"from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.title = \"Report\"\n\nws.row_dimensions[1].height = 50   # header band tall enough for the logo\nprint(\"Reserved a 50px header band on row 1\")\n",[163,306,307,320,324,334,344,355,360,381],{"__ignoreMap":161},[166,308,309,311,314,317],{"class":114,"line":168},[166,310,205],{"class":204},[166,312,313],{"class":215}," openpyxl ",[166,315,316],{"class":204},"import",[166,318,319],{"class":215}," Workbook\n",[166,321,322],{"class":114,"line":225},[166,323,229],{"emptyLinePlaceholder":228},[166,325,326,329,331],{"class":114,"line":232},[166,327,328],{"class":215},"wb ",[166,330,260],{"class":204},[166,332,333],{"class":215}," Workbook()\n",[166,335,336,339,341],{"class":114,"line":275},[166,337,338],{"class":215},"ws ",[166,340,260],{"class":204},[166,342,343],{"class":215}," wb.active\n",[166,345,347,350,352],{"class":114,"line":346},5,[166,348,349],{"class":215},"ws.title ",[166,351,260],{"class":204},[166,353,354],{"class":175}," \"Report\"\n",[166,356,358],{"class":114,"line":357},6,[166,359,229],{"emptyLinePlaceholder":228},[166,361,363,366,369,372,374,377],{"class":114,"line":362},7,[166,364,365],{"class":215},"ws.row_dimensions[",[166,367,368],{"class":208},"1",[166,370,371],{"class":215},"].height ",[166,373,260],{"class":204},[166,375,376],{"class":208}," 50",[166,378,380],{"class":379},"sJ8bj","   # header band tall enough for the logo\n",[166,382,384,386,388,391],{"class":114,"line":383},8,[166,385,278],{"class":208},[166,387,281],{"class":215},[166,389,390],{"class":175},"\"Reserved a 50px header band on row 1\"",[166,392,272],{"class":215},[143,394,396],{"id":395},"step-3-create-size-and-anchor-the-logo","Step 3: create, size, and anchor the logo",[10,398,399,400,403,404,407,408,411],{},"Build an ",[163,401,402],{},"openpyxl.drawing.image.Image",", scale it to fit the band ",[151,405,406],{},"without distorting it",", and anchor its top-left corner at A1 with ",[163,409,410],{},"ws.add_image",". To preserve the aspect ratio, read the source size with Pillow and scale by one factor instead of setting width and height blindly.",[156,413,415],{"className":195,"code":414,"language":197,"meta":161,"style":161},"from openpyxl.drawing.image import Image as XLImage\nfrom PIL import Image as PILImage\n\n# Measure the source so we scale proportionally\nwith PILImage.open(\"logo.png\") as src:\n    src_w, src_h = src.size\n\ntarget_h = 44                       # pixels, fits inside the 50px row\nscale = target_h \u002F src_h\nlogo = XLImage(\"logo.png\")\nlogo.height = target_h\nlogo.width = int(src_w * scale)     # same factor keeps proportions\n\nws.add_image(logo, \"A1\")            # anchor top-left at A1\nprint(f\"Logo sized to {logo.width}x{logo.height}px, anchored at A1\")\n",[163,416,417,433,447,451,456,474,484,488,501,518,533,544,567,572,587],{"__ignoreMap":161},[166,418,419,421,424,426,428,430],{"class":114,"line":168},[166,420,205],{"class":204},[166,422,423],{"class":215}," openpyxl.drawing.image ",[166,425,316],{"class":204},[166,427,216],{"class":215},[166,429,219],{"class":204},[166,431,432],{"class":215}," XLImage\n",[166,434,435,437,439,441,443,445],{"class":114,"line":225},[166,436,205],{"class":204},[166,438,209],{"class":208},[166,440,212],{"class":204},[166,442,216],{"class":215},[166,444,219],{"class":204},[166,446,222],{"class":215},[166,448,449],{"class":114,"line":232},[166,450,229],{"emptyLinePlaceholder":228},[166,452,453],{"class":114,"line":275},[166,454,455],{"class":379},"# Measure the source so we scale proportionally\n",[166,457,458,461,464,466,469,471],{"class":114,"line":346},[166,459,460],{"class":204},"with",[166,462,463],{"class":215}," PILImage.open(",[166,465,269],{"class":175},[166,467,468],{"class":215},") ",[166,470,219],{"class":204},[166,472,473],{"class":215}," src:\n",[166,475,476,479,481],{"class":114,"line":357},[166,477,478],{"class":215},"    src_w, src_h ",[166,480,260],{"class":204},[166,482,483],{"class":215}," src.size\n",[166,485,486],{"class":114,"line":362},[166,487,229],{"emptyLinePlaceholder":228},[166,489,490,493,495,498],{"class":114,"line":383},[166,491,492],{"class":215},"target_h ",[166,494,260],{"class":204},[166,496,497],{"class":208}," 44",[166,499,500],{"class":379},"                       # pixels, fits inside the 50px row\n",[166,502,504,507,509,512,515],{"class":114,"line":503},9,[166,505,506],{"class":215},"scale ",[166,508,260],{"class":204},[166,510,511],{"class":215}," target_h ",[166,513,514],{"class":204},"\u002F",[166,516,517],{"class":215}," src_h\n",[166,519,521,524,526,529,531],{"class":114,"line":520},10,[166,522,523],{"class":215},"logo ",[166,525,260],{"class":204},[166,527,528],{"class":215}," XLImage(",[166,530,269],{"class":175},[166,532,272],{"class":215},[166,534,536,539,541],{"class":114,"line":535},11,[166,537,538],{"class":215},"logo.height ",[166,540,260],{"class":204},[166,542,543],{"class":215}," target_h\n",[166,545,547,550,552,555,558,561,564],{"class":114,"line":546},12,[166,548,549],{"class":215},"logo.width ",[166,551,260],{"class":204},[166,553,554],{"class":208}," int",[166,556,557],{"class":215},"(src_w ",[166,559,560],{"class":204},"*",[166,562,563],{"class":215}," scale)     ",[166,565,566],{"class":379},"# same factor keeps proportions\n",[166,568,570],{"class":114,"line":569},13,[166,571,229],{"emptyLinePlaceholder":228},[166,573,575,578,581,584],{"class":114,"line":574},14,[166,576,577],{"class":215},"ws.add_image(logo, ",[166,579,580],{"class":175},"\"A1\"",[166,582,583],{"class":215},")            ",[166,585,586],{"class":379},"# anchor top-left at A1\n",[166,588,590,592,594,597,600,603,606,609,612,614,617,619,622],{"class":114,"line":589},15,[166,591,278],{"class":208},[166,593,281],{"class":215},[166,595,596],{"class":204},"f",[166,598,599],{"class":175},"\"Logo sized to ",[166,601,602],{"class":208},"{",[166,604,605],{"class":215},"logo.width",[166,607,608],{"class":208},"}",[166,610,611],{"class":175},"x",[166,613,602],{"class":208},[166,615,616],{"class":215},"logo.height",[166,618,608],{"class":208},[166,620,621],{"class":175},"px, anchored at A1\"",[166,623,272],{"class":215},[143,625,627],{"id":626},"step-4-add-a-merged-title-beside-the-logo","Step 4: add a merged title beside the logo",[10,629,630],{},"Put the report title to the right of the logo by merging a few cells in row 1 and styling the anchor cell. Merging gives the title room and centers it vertically against the tall band.",[156,632,634],{"className":195,"code":633,"language":197,"meta":161,"style":161},"from openpyxl.styles import Font, Alignment\n\nws.merge_cells(\"B1:F1\")\ntitle = ws[\"B1\"]\ntitle.value = \"Q2 Sales Report\"\ntitle.font = Font(bold=True, size=16, color=\"1F3864\")\ntitle.alignment = Alignment(horizontal=\"left\", vertical=\"center\")\nprint(\"Merged B1:F1 and set the title\")\n",[163,635,636,648,652,662,678,688,727,757],{"__ignoreMap":161},[166,637,638,640,643,645],{"class":114,"line":168},[166,639,205],{"class":204},[166,641,642],{"class":215}," openpyxl.styles ",[166,644,316],{"class":204},[166,646,647],{"class":215}," Font, Alignment\n",[166,649,650],{"class":114,"line":225},[166,651,229],{"emptyLinePlaceholder":228},[166,653,654,657,660],{"class":114,"line":232},[166,655,656],{"class":215},"ws.merge_cells(",[166,658,659],{"class":175},"\"B1:F1\"",[166,661,272],{"class":215},[166,663,664,667,669,672,675],{"class":114,"line":275},[166,665,666],{"class":215},"title ",[166,668,260],{"class":204},[166,670,671],{"class":215}," ws[",[166,673,674],{"class":175},"\"B1\"",[166,676,677],{"class":215},"]\n",[166,679,680,683,685],{"class":114,"line":346},[166,681,682],{"class":215},"title.value ",[166,684,260],{"class":204},[166,686,687],{"class":175}," \"Q2 Sales Report\"\n",[166,689,690,693,695,698,701,703,706,708,711,713,716,718,720,722,725],{"class":114,"line":357},[166,691,692],{"class":215},"title.font ",[166,694,260],{"class":204},[166,696,697],{"class":215}," Font(",[166,699,700],{"class":256},"bold",[166,702,260],{"class":204},[166,704,705],{"class":208},"True",[166,707,247],{"class":215},[166,709,710],{"class":256},"size",[166,712,260],{"class":204},[166,714,715],{"class":208},"16",[166,717,247],{"class":215},[166,719,257],{"class":256},[166,721,260],{"class":204},[166,723,724],{"class":175},"\"1F3864\"",[166,726,272],{"class":215},[166,728,729,732,734,737,740,742,745,747,750,752,755],{"class":114,"line":362},[166,730,731],{"class":215},"title.alignment ",[166,733,260],{"class":204},[166,735,736],{"class":215}," Alignment(",[166,738,739],{"class":256},"horizontal",[166,741,260],{"class":204},[166,743,744],{"class":175},"\"left\"",[166,746,247],{"class":215},[166,748,749],{"class":256},"vertical",[166,751,260],{"class":204},[166,753,754],{"class":175},"\"center\"",[166,756,272],{"class":215},[166,758,759,761,763,766],{"class":114,"line":383},[166,760,278],{"class":208},[166,762,281],{"class":215},[166,764,765],{"class":175},"\"Merged B1:F1 and set the title\"",[166,767,272],{"class":215},[143,769,771],{"id":770},"step-5-add-data-and-save","Step 5: add data and save",[10,773,774,775,778,779,782],{},"Write the table starting below the band so nothing overlaps, then save ",[151,776,777],{},"while the PNG still exists on disk"," — openpyxl reads the image bytes at save time, not when you construct the ",[163,780,781],{},"Image",".",[156,784,786],{"className":195,"code":785,"language":197,"meta":161,"style":161},"ws.append([])                       # spacer row 2\nws.append([\"Region\", \"Revenue\"])    # header row 3\nfor region, revenue in [(\"North\", 25640), (\"South\", 18890), (\"East\", 31200)]:\n    ws.append([region, revenue])\n\nwb.save(\"branded_report.xlsx\")\nprint(\"Saved branded_report.xlsx with logo and title\")\n",[163,787,788,796,815,861,866,870,880],{"__ignoreMap":161},[166,789,790,793],{"class":114,"line":168},[166,791,792],{"class":215},"ws.append([])                       ",[166,794,795],{"class":379},"# spacer row 2\n",[166,797,798,801,804,806,809,812],{"class":114,"line":225},[166,799,800],{"class":215},"ws.append([",[166,802,803],{"class":175},"\"Region\"",[166,805,247],{"class":215},[166,807,808],{"class":175},"\"Revenue\"",[166,810,811],{"class":215},"])    ",[166,813,814],{"class":379},"# header row 3\n",[166,816,817,820,823,826,829,832,834,837,840,843,845,848,850,853,855,858],{"class":114,"line":232},[166,818,819],{"class":204},"for",[166,821,822],{"class":215}," region, revenue ",[166,824,825],{"class":204},"in",[166,827,828],{"class":215}," [(",[166,830,831],{"class":175},"\"North\"",[166,833,247],{"class":215},[166,835,836],{"class":208},"25640",[166,838,839],{"class":215},"), (",[166,841,842],{"class":175},"\"South\"",[166,844,247],{"class":215},[166,846,847],{"class":208},"18890",[166,849,839],{"class":215},[166,851,852],{"class":175},"\"East\"",[166,854,247],{"class":215},[166,856,857],{"class":208},"31200",[166,859,860],{"class":215},")]:\n",[166,862,863],{"class":114,"line":275},[166,864,865],{"class":215},"    ws.append([region, revenue])\n",[166,867,868],{"class":114,"line":346},[166,869,229],{"emptyLinePlaceholder":228},[166,871,872,875,878],{"class":114,"line":357},[166,873,874],{"class":215},"wb.save(",[166,876,877],{"class":175},"\"branded_report.xlsx\"",[166,879,272],{"class":215},[166,881,882,884,886,889],{"class":114,"line":362},[166,883,278],{"class":208},[166,885,281],{"class":215},[166,887,888],{"class":175},"\"Saved branded_report.xlsx with logo and title\"",[166,890,272],{"class":215},[10,892,893,894,897],{},"Run steps 1-5 in one script and you get ",[163,895,896],{},"branded_report.xlsx",": a self-contained workbook with the logo anchored at A1, a title beside it, and the table below — emailable, with the logo embedded inside the file.",[143,899,901],{"id":900},"common-pitfalls","Common pitfalls",[903,904,905,921],"table",{},[906,907,908],"thead",{},[909,910,911,915,918],"tr",{},[912,913,914],"th",{},"Error \u002F symptom",[912,916,917],{},"Cause",[912,919,920],{},"Fix",[922,923,924,944,959,978,996],"tbody",{},[909,925,926,936,939],{},[927,928,929,932,933],"td",{},[163,930,931],{},"ImportError"," when building ",[163,934,935],{},"Image(...)",[927,937,938],{},"Pillow not installed",[927,940,941],{},[163,942,943],{},"pip install pillow",[909,945,946,949,952],{},[927,947,948],{},"Logo overlaps the data rows",[927,950,951],{},"Images float and never push cells down",[927,953,954,955,958],{},"Reserve room with ",[163,956,957],{},"ws.row_dimensions[1].height"," and start data below",[909,960,961,969,972],{},[927,962,963,966,967],{},[163,964,965],{},"FileNotFoundError"," on ",[163,968,112],{},[927,970,971],{},"Source PNG deleted before the save",[927,973,974,975,977],{},"Keep the file on disk until after ",[163,976,112],{}," runs",[909,979,980,983,993],{},[927,981,982],{},"Logo looks stretched",[927,984,985,988,989,992],{},[163,986,987],{},"width"," and ",[163,990,991],{},"height"," set independently",[927,994,995],{},"Scale both by one factor from the source size (Step 3)",[909,997,998,1001,1008],{},[927,999,1000],{},"Logo vanished after a later edit",[927,1002,1003,1004,1007],{},"File was rewritten with ",[163,1005,1006],{},"pandas.to_excel",", which drops drawings",[927,1009,1010,1011,1014],{},"Make openpyxl the ",[151,1012,1013],{},"last"," writer; never resave with pandas afterward",[10,1016,1017,1018,1021,1022,1024,1025,1028],{},"The deletion trap is worth stressing: openpyxl does not slurp the image when you call ",[163,1019,1020],{},"Image(\"logo.png\")"," — it reads the bytes during ",[163,1023,112],{},". If you generate a temp PNG and ",[163,1026,1027],{},"os.remove"," it before saving, the save fails or the image is missing. Delete temp files only after the workbook is written.",[10,1030,1031,1032,1036],{},"A quick note on the floating model: anchoring at A1 sets where the logo ",[1033,1034,1035],"em",{},"starts",", not which cells it covers. A logo wider than column A spills over B and C, which is fine for a header band but means widening column A alone will not \"contain\" it. Reserve space with row height and column width together if you need a tidy boxed logo.",[143,1038,1040],{"id":1039},"frequently-asked-questions","Frequently asked questions",[10,1042,1043,1046,1047,1049,1050,1052],{},[151,1044,1045],{},"Do I have to install Pillow just for one logo?","\nYes. openpyxl delegates all image reading to Pillow, so even a single PNG requires it. The ",[163,1048,935],{}," constructor raises ",[163,1051,931],{}," without it.",[10,1054,1055,1058],{},[151,1056,1057],{},"Why anchor at A1 instead of putting the image \"in\" the cell?","\nExcel has no concept of an image inside a cell. Images are floating drawings anchored to a corner cell. Anchoring at A1 places the logo's top-left there; the image then floats over whatever cells it spans.",[10,1060,1061,1064,1065,988,1067,1069],{},[151,1062,1063],{},"How do I keep the logo from looking squashed?","\nDo not set ",[163,1066,987],{},[163,1068,991],{}," to arbitrary numbers. Read the source dimensions with Pillow and multiply both by the same scale factor (Step 3). Setting one to a value and the other independently is what distorts it.",[10,1071,1072,1075,1076,1079,1080,1082],{},[151,1073,1074],{},"Can I do this on an existing report instead of a new workbook?","\nYes. Replace ",[163,1077,1078],{},"Workbook()"," in Step 2 with ",[163,1081,296],{}," and pick the sheet you want. openpyxl preserves the existing data and styles while you add the logo.",[10,1084,1085,1088,1089,1091],{},[151,1086,1087],{},"The logo disappeared after I regenerated the file — why?","\nSomething rewrote the workbook with ",[163,1090,1006],{},", which rebuilds the sheet from the DataFrame and discards images, charts, and most styling. Add the logo as the last step and never round-trip the file back through pandas.",[143,1093,1095],{"id":1094},"conclusion","Conclusion",[10,1097,1098,1099,1101],{},"Adding a logo is five steps: make (or point at) the PNG, reserve a header band with row height, build and proportionally size the ",[163,1100,781],{},", anchor it at A1 with a merged title beside it, and save while the source file still exists. Keep openpyxl as the last tool to write the file and your logo stays put. Pillow is non-negotiable, and the float model — images sit over cells, never inside them — explains every layout quirk you will hit.",[143,1103,1105],{"id":1104},"where-to-go-next","Where to go next",[10,1107,1108],{},"Up to the parent cluster:",[1110,1111,1112],"ul",{},[1113,1114,1115,1117],"li",{},[14,1116,17],{"href":16}," — the concepts behind anchoring, resizing, and pandas-safe sequencing.",[10,1119,1120],{},"Related pages:",[1110,1122,1123,1130],{},[1113,1124,1125,1129],{},[14,1126,1128],{"href":1127},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation"," — loading, editing, and saving workbooks, the foundation this recipe stands on.",[1113,1131,1132,1136],{},[14,1133,1135],{"href":1134},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002F","Building Multi-Sheet Excel Dashboards"," — assemble branded, logo-topped sheets into a single dashboard workbook.",[1138,1139,1140],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":161,"searchDepth":225,"depth":225,"links":1142},[1143,1144,1145,1146,1147,1148,1149,1150,1151,1152],{"id":145,"depth":225,"text":146},{"id":188,"depth":225,"text":189},{"id":289,"depth":225,"text":290},{"id":395,"depth":225,"text":396},{"id":626,"depth":225,"text":627},{"id":770,"depth":225,"text":771},{"id":900,"depth":225,"text":901},{"id":1039,"depth":225,"text":1040},{"id":1094,"depth":225,"text":1095},{"id":1104,"depth":225,"text":1105},"2026-06-18","Step-by-step openpyxl guide to add a logo to an Excel report: create the image, anchor it at A1, size it to a header band, add a merged title, and save.","md",[1157,1159,1161,1163,1165],{"q":1045,"a":1158},"Yes. openpyxl delegates all image reading to Pillow, so even a single PNG requires it. The Image(...) constructor raises ImportError without it.",{"q":1057,"a":1160},"Excel has no concept of an image inside a cell. Images are floating drawings anchored to a corner cell. Anchoring at A1 places the logo's top-left there; the image then floats over whatever cells it spans.",{"q":1063,"a":1162},"Do not set width and height to arbitrary numbers. Read the source dimensions with Pillow and multiply both by the same scale factor (Step 3). Setting one to a value and the other independently is what distorts it.",{"q":1074,"a":1164},"Yes. Replace Workbook() in Step 2 with load_workbook(\"report.xlsx\") and pick the sheet you want. openpyxl preserves the existing data and styles while you add the logo.",{"q":1087,"a":1166},"Something rewrote the workbook with pandas.to_excel, which rebuilds the sheet from the DataFrame and discards images, charts, and most styling. Add the logo as the last step and never round-trip the file back through pandas.",{},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fadd-logo-image-to-excel-report-with-openpyxl",{"title":1170,"description":1171},"Add a Logo to an Excel Report with openpyxl","Embed a logo in an Excel report using openpyxl and Pillow: build the Image, anchor at A1, resize to a header band, add a merged title, and avoid lost images.","add-logo-image-to-excel-report-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fadd-logo-image-to-excel-report-with-openpyxl\u002Findex","long_tail","PIgQmqIBA0qkywPvzad829VyLpNDcCOMkH3xVzXJA98",[1177,1180],{"title":17,"path":1178,"stem":1179,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel","formatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Findex",{"title":1181,"path":1182,"stem":1183,"children":-1},"Styling Excel Cells with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Findex",1781795518827]