[{"data":1,"prerenderedAt":1736},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel":1728},{"id":4,"title":5,"body":6,"dateModified":1704,"datePublished":1704,"description":1705,"extension":1706,"faq":1707,"meta":1719,"navigation":309,"path":1720,"seo":1721,"slug":1724,"stem":1725,"type":1726,"__hash__":1727},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Findex.md","Inserting Images and Logos into Excel",{"type":7,"value":8,"toc":1693},"minimark",[9,33,36,166,171,190,217,220,224,243,464,478,482,497,693,712,716,727,1100,1111,1115,1129,1420,1429,1433,1443,1531,1542,1546,1566,1579,1593,1607,1623,1627,1637,1641,1644,1653,1656,1663,1666,1689],[10,11,12,13,17,18,22,23,26,27,32],"p",{},"A branded report needs a logo in the top-left corner, and a ",[14,15,16],"code",{},".xlsx"," file can hold one — but not the way you might expect. An image in Excel is not a cell value. It is a ",[19,20,21],"em",{},"floating drawing"," that sits on top of the grid, anchored to a cell but living in its own layer. That single fact explains every quirk covered here: images do not push cells aside, they do not appear in ",[14,24,25],{},"ws[\"A1\"].value",", and they vanish the moment a tool that does not understand drawings rewrites the file. This page is the practical guide to getting a logo in, sized right, and kept there. It is part of ",[28,29,31],"a",{"href":30},"\u002Fformatting-and-charting-excel-reports-with-python\u002F","Formatting and Charting Excel Reports with Python",".",[10,34,35],{},"Everything below is plain openpyxl plus Pillow, and every block builds its own tiny PNG inline, so you can paste and run without supplying an image of your own.",[37,38,46,47,46,51,46,55,46,62,46,72,46,78,46,81,46,84,46,88,46,90,46,93,46,98,46,102,46,106,46,110,46,119,46,125,46,130,46,135,46,143,46,149,46,154,46,158,46,161],"svg",{"viewBox":39,"role":40,"ariaLabelledBy":41,"xmlns":44,"style":45},"0 0 760 250","img",[42,43],"img-ov-t","img-ov-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  ",[48,49,50],"title",{"id":42},"An Excel image is a floating drawing, not a cell value",[52,53,54],"desc",{"id":43},"A logo embedded with openpyxl is anchored to cell A1 but sits in its own drawing layer above the worksheet grid, so the cell value stays empty and a pandas rewrite drops the image.",[56,57,61],"text",{"x":58,"y":59,"style":60},"250","34","font-size:13px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A floating layer over the grid",[63,64],"rect",{"x":65,"y":66,"width":67,"height":68,"rx":69,"fill":70,"stroke":71},"40","52","420","158","6","#ffffff","var(--line,#cdd5e6)",[73,74],"line",{"x1":65,"y1":75,"x2":76,"y2":75,"stroke":71,"style":77},"92","460","stroke-width:1px",[73,79],{"x1":65,"y1":80,"x2":76,"y2":80,"stroke":71,"style":77},"131",[73,82],{"x1":65,"y1":83,"x2":76,"y2":83,"stroke":71,"style":77},"170",[73,85],{"x1":86,"y1":66,"x2":86,"y2":87,"stroke":71,"style":77},"145","210",[73,89],{"x1":58,"y1":66,"x2":58,"y2":87,"stroke":71,"style":77},[73,91],{"x1":92,"y1":66,"x2":92,"y2":87,"stroke":71,"style":77},"355",[56,94,97],{"x":75,"y":95,"style":96},"46","font-size:11px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","A",[56,99,101],{"x":100,"y":95,"style":96},"197","B",[56,103,105],{"x":104,"y":95,"style":96},"302","C",[56,107,109],{"x":108,"y":95,"style":96},"407","D",[63,111],{"x":112,"y":113,"width":114,"height":115,"rx":116,"fill":117,"stroke":118},"64","78","168","60","10","var(--brand,#5b5cf0)","var(--brand-strong,#4338ca)",[56,120,124],{"x":121,"y":122,"style":123},"148","106","font-size:14px;font-weight:700;fill:#ffffff;text-anchor:middle","logo.png",[56,126,129],{"x":121,"y":127,"style":128},"125","font-size:11px;fill:rgba(255,255,255,0.88);text-anchor:middle","drawing layer",[56,131,134],{"x":58,"y":132,"style":133},"232","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","anchored at A1 — it floats above cells, it is not in them",[63,136],{"x":137,"y":115,"width":138,"height":139,"rx":140,"fill":141,"stroke":117,"style":142},"500","240","150","14","var(--surface-muted,#eef2ff)","stroke-width:2px",[56,144,148],{"x":145,"y":146,"style":147},"620","86","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","What that means",[56,150,153],{"x":145,"y":151,"style":152},"116","font-size:12px;fill:var(--text,#172033);text-anchor:middle","ws[\"A1\"].value → None",[56,155,157],{"x":145,"y":156,"style":152},"142","cells are not pushed aside",[56,159,160],{"x":145,"y":114,"style":152},"needs Pillow to embed",[56,162,165],{"x":145,"y":163,"style":164},"194","font-size:12px;fill:var(--accent,#f43f8f);text-anchor:middle","pandas rewrite drops it",[167,168,170],"h2",{"id":169},"install-openpyxl-and-pillow","Install openpyxl and Pillow",[10,172,173,174,176,177,181,182,185,186,189],{},"openpyxl writes the ",[14,175,16],{}," drawing XML itself, but it leans on ",[178,179,180],"strong",{},"Pillow"," to read the image you hand it — measure its dimensions, validate the format, and convert anything that is not already a clean PNG. Without Pillow installed, ",[14,183,184],{},"openpyxl.drawing.image.Image(\"logo.png\")"," raises ",[14,187,188],{},"ImportError"," the moment you construct it.",[191,192,197],"pre",{"className":193,"code":194,"language":195,"meta":196,"style":196},"language-bash shiki shiki-themes github-light github-dark","pip install openpyxl pillow\n","bash","",[14,198,199],{"__ignoreMap":196},[200,201,203,207,211,214],"span",{"class":73,"line":202},1,[200,204,206],{"class":205},"sScJk","pip",[200,208,210],{"class":209},"sZZnC"," install",[200,212,213],{"class":209}," openpyxl",[200,215,216],{"class":209}," pillow\n",[10,218,219],{},"That is the whole toolchain. Neither library needs Excel installed, so these scripts run on a headless CI runner or a Linux server exactly as they run on your laptop.",[167,221,223],{"id":222},"embed-an-image-anchored-to-a-cell","Embed an image anchored to a cell",[10,225,226,227,230,231,234,235,238,239,242],{},"The core API is small: build an ",[14,228,229],{},"openpyxl.drawing.image.Image",", then ",[14,232,233],{},"ws.add_image(img, \"A1\")"," to anchor its top-left corner at a cell. The image floats — anchoring to ",[14,236,237],{},"A1"," pins where it ",[19,240,241],{},"starts",", not which cells it occupies. It will happily overlap B1, C1, and the rows below if it is larger than that cell.",[191,244,248],{"className":245,"code":246,"language":247,"meta":196,"style":196},"language-python shiki shiki-themes github-light github-dark","from openpyxl import Workbook\nfrom openpyxl.drawing.image import Image as XLImage\nfrom PIL import Image as PILImage\n\n# Build a small placeholder logo so this runs end to end\nPILImage.new(\"RGB\", (160, 50), color=\"#4472C4\").save(\"logo.png\")\n\nwb = Workbook()\nws = wb.active\nws.title = \"Report\"\n\nlogo = XLImage(\"logo.png\")\nws.add_image(logo, \"A1\")          # top-left corner anchored to A1\n\nwb.save(\"with_logo.xlsx\")\nprint(\"Embedded logo.png anchored at A1\")\n","python",[14,249,250,266,285,304,311,318,361,366,377,388,399,404,419,434,439,450],{"__ignoreMap":196},[200,251,252,256,260,263],{"class":73,"line":202},[200,253,255],{"class":254},"szBVR","from",[200,257,259],{"class":258},"sVt8B"," openpyxl ",[200,261,262],{"class":254},"import",[200,264,265],{"class":258}," Workbook\n",[200,267,269,271,274,276,279,282],{"class":73,"line":268},2,[200,270,255],{"class":254},[200,272,273],{"class":258}," openpyxl.drawing.image ",[200,275,262],{"class":254},[200,277,278],{"class":258}," Image ",[200,280,281],{"class":254},"as",[200,283,284],{"class":258}," XLImage\n",[200,286,288,290,294,297,299,301],{"class":73,"line":287},3,[200,289,255],{"class":254},[200,291,293],{"class":292},"sj4cs"," PIL",[200,295,296],{"class":254}," import",[200,298,278],{"class":258},[200,300,281],{"class":254},[200,302,303],{"class":258}," PILImage\n",[200,305,307],{"class":73,"line":306},4,[200,308,310],{"emptyLinePlaceholder":309},true,"\n",[200,312,314],{"class":73,"line":313},5,[200,315,317],{"class":316},"sJ8bj","# Build a small placeholder logo so this runs end to end\n",[200,319,321,324,327,330,333,336,339,342,346,349,352,355,358],{"class":73,"line":320},6,[200,322,323],{"class":258},"PILImage.new(",[200,325,326],{"class":209},"\"RGB\"",[200,328,329],{"class":258},", (",[200,331,332],{"class":292},"160",[200,334,335],{"class":258},", ",[200,337,338],{"class":292},"50",[200,340,341],{"class":258},"), ",[200,343,345],{"class":344},"s4XuR","color",[200,347,348],{"class":254},"=",[200,350,351],{"class":209},"\"#4472C4\"",[200,353,354],{"class":258},").save(",[200,356,357],{"class":209},"\"logo.png\"",[200,359,360],{"class":258},")\n",[200,362,364],{"class":73,"line":363},7,[200,365,310],{"emptyLinePlaceholder":309},[200,367,369,372,374],{"class":73,"line":368},8,[200,370,371],{"class":258},"wb ",[200,373,348],{"class":254},[200,375,376],{"class":258}," Workbook()\n",[200,378,380,383,385],{"class":73,"line":379},9,[200,381,382],{"class":258},"ws ",[200,384,348],{"class":254},[200,386,387],{"class":258}," wb.active\n",[200,389,391,394,396],{"class":73,"line":390},10,[200,392,393],{"class":258},"ws.title ",[200,395,348],{"class":254},[200,397,398],{"class":209}," \"Report\"\n",[200,400,402],{"class":73,"line":401},11,[200,403,310],{"emptyLinePlaceholder":309},[200,405,407,410,412,415,417],{"class":73,"line":406},12,[200,408,409],{"class":258},"logo ",[200,411,348],{"class":254},[200,413,414],{"class":258}," XLImage(",[200,416,357],{"class":209},[200,418,360],{"class":258},[200,420,422,425,428,431],{"class":73,"line":421},13,[200,423,424],{"class":258},"ws.add_image(logo, ",[200,426,427],{"class":209},"\"A1\"",[200,429,430],{"class":258},")          ",[200,432,433],{"class":316},"# top-left corner anchored to A1\n",[200,435,437],{"class":73,"line":436},14,[200,438,310],{"emptyLinePlaceholder":309},[200,440,442,445,448],{"class":73,"line":441},15,[200,443,444],{"class":258},"wb.save(",[200,446,447],{"class":209},"\"with_logo.xlsx\"",[200,449,360],{"class":258},[200,451,453,456,459,462],{"class":73,"line":452},16,[200,454,455],{"class":292},"print",[200,457,458],{"class":258},"(",[200,460,461],{"class":209},"\"Embedded logo.png anchored at A1\"",[200,463,360],{"class":258},[10,465,466,467,470,471,474,475,477],{},"The image bytes are copied ",[19,468,469],{},"into"," the workbook, so ",[14,472,473],{},"with_logo.xlsx"," is self-contained — email it and the logo travels along. You do not keep a reference to ",[14,476,124],{}," inside the file; openpyxl has already absorbed it (with one timing caveat covered below).",[167,479,481],{"id":480},"resize-a-logo-in-pixels","Resize a logo in pixels",[10,483,484,485,488,489,492,493,496],{},"A raw logo is rarely the right size for a report header. Set ",[14,486,487],{},"img.width"," and ",[14,490,491],{},"img.height"," ",[178,494,495],{},"in pixels"," before adding the image. These are display dimensions on the sheet, independent of the source file's real resolution, so you control exactly how big the logo renders.",[191,498,500],{"className":245,"code":499,"language":247,"meta":196,"style":196},"from openpyxl import Workbook\nfrom openpyxl.drawing.image import Image as XLImage\nfrom PIL import Image as PILImage\n\nPILImage.new(\"RGB\", (400, 120), color=\"#4472C4\").save(\"big_logo.png\")\n\nwb = Workbook()\nws = wb.active\n\nlogo = XLImage(\"big_logo.png\")\nlogo.width = 200      # pixels on the sheet\nlogo.height = 60      # pixels on the sheet\nws.add_image(logo, \"A1\")\n\nwb.save(\"resized_logo.xlsx\")\nprint(f\"Logo rendered at {logo.width}x{logo.height}px\")\n",[14,501,502,512,526,540,544,575,579,587,595,599,611,624,636,644,648,657],{"__ignoreMap":196},[200,503,504,506,508,510],{"class":73,"line":202},[200,505,255],{"class":254},[200,507,259],{"class":258},[200,509,262],{"class":254},[200,511,265],{"class":258},[200,513,514,516,518,520,522,524],{"class":73,"line":268},[200,515,255],{"class":254},[200,517,273],{"class":258},[200,519,262],{"class":254},[200,521,278],{"class":258},[200,523,281],{"class":254},[200,525,284],{"class":258},[200,527,528,530,532,534,536,538],{"class":73,"line":287},[200,529,255],{"class":254},[200,531,293],{"class":292},[200,533,296],{"class":254},[200,535,278],{"class":258},[200,537,281],{"class":254},[200,539,303],{"class":258},[200,541,542],{"class":73,"line":306},[200,543,310],{"emptyLinePlaceholder":309},[200,545,546,548,550,552,555,557,560,562,564,566,568,570,573],{"class":73,"line":313},[200,547,323],{"class":258},[200,549,326],{"class":209},[200,551,329],{"class":258},[200,553,554],{"class":292},"400",[200,556,335],{"class":258},[200,558,559],{"class":292},"120",[200,561,341],{"class":258},[200,563,345],{"class":344},[200,565,348],{"class":254},[200,567,351],{"class":209},[200,569,354],{"class":258},[200,571,572],{"class":209},"\"big_logo.png\"",[200,574,360],{"class":258},[200,576,577],{"class":73,"line":320},[200,578,310],{"emptyLinePlaceholder":309},[200,580,581,583,585],{"class":73,"line":363},[200,582,371],{"class":258},[200,584,348],{"class":254},[200,586,376],{"class":258},[200,588,589,591,593],{"class":73,"line":368},[200,590,382],{"class":258},[200,592,348],{"class":254},[200,594,387],{"class":258},[200,596,597],{"class":73,"line":379},[200,598,310],{"emptyLinePlaceholder":309},[200,600,601,603,605,607,609],{"class":73,"line":390},[200,602,409],{"class":258},[200,604,348],{"class":254},[200,606,414],{"class":258},[200,608,572],{"class":209},[200,610,360],{"class":258},[200,612,613,616,618,621],{"class":73,"line":401},[200,614,615],{"class":258},"logo.width ",[200,617,348],{"class":254},[200,619,620],{"class":292}," 200",[200,622,623],{"class":316},"      # pixels on the sheet\n",[200,625,626,629,631,634],{"class":73,"line":406},[200,627,628],{"class":258},"logo.height ",[200,630,348],{"class":254},[200,632,633],{"class":292}," 60",[200,635,623],{"class":316},[200,637,638,640,642],{"class":73,"line":421},[200,639,424],{"class":258},[200,641,427],{"class":209},[200,643,360],{"class":258},[200,645,646],{"class":73,"line":436},[200,647,310],{"emptyLinePlaceholder":309},[200,649,650,652,655],{"class":73,"line":441},[200,651,444],{"class":258},[200,653,654],{"class":209},"\"resized_logo.xlsx\"",[200,656,360],{"class":258},[200,658,659,661,663,666,669,672,675,678,681,683,686,688,691],{"class":73,"line":452},[200,660,455],{"class":292},[200,662,458],{"class":258},[200,664,665],{"class":254},"f",[200,667,668],{"class":209},"\"Logo rendered at ",[200,670,671],{"class":292},"{",[200,673,674],{"class":258},"logo.width",[200,676,677],{"class":292},"}",[200,679,680],{"class":209},"x",[200,682,671],{"class":292},[200,684,685],{"class":258},"logo.height",[200,687,677],{"class":292},[200,689,690],{"class":209},"px\"",[200,692,360],{"class":258},[10,694,695,696,488,699,702,703,706,707,711],{},"There is no \"lock aspect ratio\" flag — setting ",[14,697,698],{},"width",[14,700,701],{},"height"," independently can stretch the image. To keep proportions, read the source size with Pillow and scale by a single factor: ",[14,704,705],{},"scale = 200 \u002F src_w; logo.width, logo.height = int(src_w * scale), int(src_h * scale)",". ",[28,708,710],{"href":709},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fadd-logo-image-to-excel-report-with-openpyxl\u002F","Add a Logo Image to an Excel Report with openpyxl"," walks through that aspect-safe sizing step by step.",[167,713,715],{"id":714},"build-a-header-band-with-a-logo-and-title","Build a header band with a logo and title",[10,717,718,719,722,723,726],{},"Because the logo floats and never pushes cells, you reserve space for it yourself with ",[178,720,721],{},"row height"," and place the title text in a ",[178,724,725],{},"merged cell"," beside it. Tall first row, merged title across a few columns, logo anchored at A1 — that is the standard report header.",[191,728,730],{"className":245,"code":729,"language":247,"meta":196,"style":196},"from openpyxl import Workbook\nfrom openpyxl.drawing.image import Image as XLImage\nfrom openpyxl.styles import Font, Alignment\nfrom PIL import Image as PILImage\n\nPILImage.new(\"RGB\", (150, 45), color=\"#4472C4\").save(\"brand.png\")\n\nwb = Workbook()\nws = wb.active\nws.title = \"Sales\"\n\n# Reserve a header band: one tall row for the logo to sit in\nws.row_dimensions[1].height = 48\n\n# Merged title to the right of the logo\nws.merge_cells(\"B1:E1\")\ntitle = ws[\"B1\"]\ntitle.value = \"Weekly Sales Report\"\ntitle.font = Font(bold=True, size=16, color=\"1F3864\")\ntitle.alignment = Alignment(vertical=\"center\")\n\nlogo = XLImage(\"brand.png\")\nlogo.width, logo.height = 150, 45\nws.add_image(logo, \"A1\")\n\n# Data starts cleanly below the band\nws.append([])  # spacer row 2\nws.append([\"Region\", \"Revenue\"])\nws.append([\"North\", 25640])\n\nwb.save(\"header_band.xlsx\")\nprint(\"Built a header band: logo at A1, title merged across B1:E1\")\n",[14,731,732,742,756,768,782,786,816,820,828,836,845,849,854,870,874,879,889,906,917,957,978,983,996,1012,1021,1026,1032,1041,1058,1073,1078,1088],{"__ignoreMap":196},[200,733,734,736,738,740],{"class":73,"line":202},[200,735,255],{"class":254},[200,737,259],{"class":258},[200,739,262],{"class":254},[200,741,265],{"class":258},[200,743,744,746,748,750,752,754],{"class":73,"line":268},[200,745,255],{"class":254},[200,747,273],{"class":258},[200,749,262],{"class":254},[200,751,278],{"class":258},[200,753,281],{"class":254},[200,755,284],{"class":258},[200,757,758,760,763,765],{"class":73,"line":287},[200,759,255],{"class":254},[200,761,762],{"class":258}," openpyxl.styles ",[200,764,262],{"class":254},[200,766,767],{"class":258}," Font, Alignment\n",[200,769,770,772,774,776,778,780],{"class":73,"line":306},[200,771,255],{"class":254},[200,773,293],{"class":292},[200,775,296],{"class":254},[200,777,278],{"class":258},[200,779,281],{"class":254},[200,781,303],{"class":258},[200,783,784],{"class":73,"line":313},[200,785,310],{"emptyLinePlaceholder":309},[200,787,788,790,792,794,796,798,801,803,805,807,809,811,814],{"class":73,"line":320},[200,789,323],{"class":258},[200,791,326],{"class":209},[200,793,329],{"class":258},[200,795,139],{"class":292},[200,797,335],{"class":258},[200,799,800],{"class":292},"45",[200,802,341],{"class":258},[200,804,345],{"class":344},[200,806,348],{"class":254},[200,808,351],{"class":209},[200,810,354],{"class":258},[200,812,813],{"class":209},"\"brand.png\"",[200,815,360],{"class":258},[200,817,818],{"class":73,"line":363},[200,819,310],{"emptyLinePlaceholder":309},[200,821,822,824,826],{"class":73,"line":368},[200,823,371],{"class":258},[200,825,348],{"class":254},[200,827,376],{"class":258},[200,829,830,832,834],{"class":73,"line":379},[200,831,382],{"class":258},[200,833,348],{"class":254},[200,835,387],{"class":258},[200,837,838,840,842],{"class":73,"line":390},[200,839,393],{"class":258},[200,841,348],{"class":254},[200,843,844],{"class":209}," \"Sales\"\n",[200,846,847],{"class":73,"line":401},[200,848,310],{"emptyLinePlaceholder":309},[200,850,851],{"class":73,"line":406},[200,852,853],{"class":316},"# Reserve a header band: one tall row for the logo to sit in\n",[200,855,856,859,862,865,867],{"class":73,"line":421},[200,857,858],{"class":258},"ws.row_dimensions[",[200,860,861],{"class":292},"1",[200,863,864],{"class":258},"].height ",[200,866,348],{"class":254},[200,868,869],{"class":292}," 48\n",[200,871,872],{"class":73,"line":436},[200,873,310],{"emptyLinePlaceholder":309},[200,875,876],{"class":73,"line":441},[200,877,878],{"class":316},"# Merged title to the right of the logo\n",[200,880,881,884,887],{"class":73,"line":452},[200,882,883],{"class":258},"ws.merge_cells(",[200,885,886],{"class":209},"\"B1:E1\"",[200,888,360],{"class":258},[200,890,892,895,897,900,903],{"class":73,"line":891},17,[200,893,894],{"class":258},"title ",[200,896,348],{"class":254},[200,898,899],{"class":258}," ws[",[200,901,902],{"class":209},"\"B1\"",[200,904,905],{"class":258},"]\n",[200,907,909,912,914],{"class":73,"line":908},18,[200,910,911],{"class":258},"title.value ",[200,913,348],{"class":254},[200,915,916],{"class":209}," \"Weekly Sales Report\"\n",[200,918,920,923,925,928,931,933,936,938,941,943,946,948,950,952,955],{"class":73,"line":919},19,[200,921,922],{"class":258},"title.font ",[200,924,348],{"class":254},[200,926,927],{"class":258}," Font(",[200,929,930],{"class":344},"bold",[200,932,348],{"class":254},[200,934,935],{"class":292},"True",[200,937,335],{"class":258},[200,939,940],{"class":344},"size",[200,942,348],{"class":254},[200,944,945],{"class":292},"16",[200,947,335],{"class":258},[200,949,345],{"class":344},[200,951,348],{"class":254},[200,953,954],{"class":209},"\"1F3864\"",[200,956,360],{"class":258},[200,958,960,963,965,968,971,973,976],{"class":73,"line":959},20,[200,961,962],{"class":258},"title.alignment ",[200,964,348],{"class":254},[200,966,967],{"class":258}," Alignment(",[200,969,970],{"class":344},"vertical",[200,972,348],{"class":254},[200,974,975],{"class":209},"\"center\"",[200,977,360],{"class":258},[200,979,981],{"class":73,"line":980},21,[200,982,310],{"emptyLinePlaceholder":309},[200,984,986,988,990,992,994],{"class":73,"line":985},22,[200,987,409],{"class":258},[200,989,348],{"class":254},[200,991,414],{"class":258},[200,993,813],{"class":209},[200,995,360],{"class":258},[200,997,999,1002,1004,1007,1009],{"class":73,"line":998},23,[200,1000,1001],{"class":258},"logo.width, logo.height ",[200,1003,348],{"class":254},[200,1005,1006],{"class":292}," 150",[200,1008,335],{"class":258},[200,1010,1011],{"class":292},"45\n",[200,1013,1015,1017,1019],{"class":73,"line":1014},24,[200,1016,424],{"class":258},[200,1018,427],{"class":209},[200,1020,360],{"class":258},[200,1022,1024],{"class":73,"line":1023},25,[200,1025,310],{"emptyLinePlaceholder":309},[200,1027,1029],{"class":73,"line":1028},26,[200,1030,1031],{"class":316},"# Data starts cleanly below the band\n",[200,1033,1035,1038],{"class":73,"line":1034},27,[200,1036,1037],{"class":258},"ws.append([])  ",[200,1039,1040],{"class":316},"# spacer row 2\n",[200,1042,1044,1047,1050,1052,1055],{"class":73,"line":1043},28,[200,1045,1046],{"class":258},"ws.append([",[200,1048,1049],{"class":209},"\"Region\"",[200,1051,335],{"class":258},[200,1053,1054],{"class":209},"\"Revenue\"",[200,1056,1057],{"class":258},"])\n",[200,1059,1061,1063,1066,1068,1071],{"class":73,"line":1060},29,[200,1062,1046],{"class":258},[200,1064,1065],{"class":209},"\"North\"",[200,1067,335],{"class":258},[200,1069,1070],{"class":292},"25640",[200,1072,1057],{"class":258},[200,1074,1076],{"class":73,"line":1075},30,[200,1077,310],{"emptyLinePlaceholder":309},[200,1079,1081,1083,1086],{"class":73,"line":1080},31,[200,1082,444],{"class":258},[200,1084,1085],{"class":209},"\"header_band.xlsx\"",[200,1087,360],{"class":258},[200,1089,1091,1093,1095,1098],{"class":73,"line":1090},32,[200,1092,455],{"class":292},[200,1094,458],{"class":258},[200,1096,1097],{"class":209},"\"Built a header band: logo at A1, title merged across B1:E1\"",[200,1099,360],{"class":258},[10,1101,1102,1103,1106,1107,1110],{},"The 48-pixel row height gives the 45-pixel logo room without overlapping the data, and the merged ",[14,1104,1105],{},"B1:E1"," keeps the title from colliding with the logo's float. Widen column A to the logo's pixel width (roughly ",[14,1108,1109],{},"width \u002F 7"," in Excel character units) if you want the logo fully contained rather than spilling toward B.",[167,1112,1114],{"id":1113},"add-a-logo-to-a-pandas-report","Add a logo to a pandas report",[10,1116,1117,1120,1121,1124,1125,32],{},[14,1118,1119],{},"pandas.to_excel"," writes data and nothing else — it has no API to embed an image, because it only knows about cell values. The pattern is therefore two steps: ",[178,1122,1123],{},"pandas writes the data, then you re-open the file with openpyxl and add the logo."," This is the same write-then-decorate flow the rest of this track uses, and it builds on ",[28,1126,1128],{"href":1127},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation",[191,1130,1132],{"className":245,"code":1131,"language":247,"meta":196,"style":196},"import pandas as pd\nfrom openpyxl import load_workbook\nfrom openpyxl.drawing.image import Image as XLImage\nfrom PIL import Image as PILImage\n\nPILImage.new(\"RGB\", (140, 44), color=\"#4472C4\").save(\"co_logo.png\")\n\n# 1. pandas writes the raw table, starting a few rows down to leave a band\ndf = pd.DataFrame({\"Region\": [\"North\", \"South\"], \"Revenue\": [25640, 18890]})\ndf.to_excel(\"pandas_report.xlsx\", sheet_name=\"Sales\",\n            index=False, startrow=3)\n\n# 2. re-open with openpyxl and drop the logo into the empty band\nwb = load_workbook(\"pandas_report.xlsx\")\nws = wb[\"Sales\"]\nws.row_dimensions[1].height = 40\n\nlogo = XLImage(\"co_logo.png\")\nlogo.width, logo.height = 140, 44\nws.add_image(logo, \"A1\")\n\nwb.save(\"pandas_report.xlsx\")\nprint(\"pandas wrote the data; openpyxl added the logo\")\n",[14,1133,1134,1146,1157,1171,1185,1189,1220,1224,1229,1268,1289,1311,1315,1320,1333,1346,1359,1363,1375,1389,1397,1401,1409],{"__ignoreMap":196},[200,1135,1136,1138,1141,1143],{"class":73,"line":202},[200,1137,262],{"class":254},[200,1139,1140],{"class":258}," pandas ",[200,1142,281],{"class":254},[200,1144,1145],{"class":258}," pd\n",[200,1147,1148,1150,1152,1154],{"class":73,"line":268},[200,1149,255],{"class":254},[200,1151,259],{"class":258},[200,1153,262],{"class":254},[200,1155,1156],{"class":258}," load_workbook\n",[200,1158,1159,1161,1163,1165,1167,1169],{"class":73,"line":287},[200,1160,255],{"class":254},[200,1162,273],{"class":258},[200,1164,262],{"class":254},[200,1166,278],{"class":258},[200,1168,281],{"class":254},[200,1170,284],{"class":258},[200,1172,1173,1175,1177,1179,1181,1183],{"class":73,"line":306},[200,1174,255],{"class":254},[200,1176,293],{"class":292},[200,1178,296],{"class":254},[200,1180,278],{"class":258},[200,1182,281],{"class":254},[200,1184,303],{"class":258},[200,1186,1187],{"class":73,"line":313},[200,1188,310],{"emptyLinePlaceholder":309},[200,1190,1191,1193,1195,1197,1200,1202,1205,1207,1209,1211,1213,1215,1218],{"class":73,"line":320},[200,1192,323],{"class":258},[200,1194,326],{"class":209},[200,1196,329],{"class":258},[200,1198,1199],{"class":292},"140",[200,1201,335],{"class":258},[200,1203,1204],{"class":292},"44",[200,1206,341],{"class":258},[200,1208,345],{"class":344},[200,1210,348],{"class":254},[200,1212,351],{"class":209},[200,1214,354],{"class":258},[200,1216,1217],{"class":209},"\"co_logo.png\"",[200,1219,360],{"class":258},[200,1221,1222],{"class":73,"line":363},[200,1223,310],{"emptyLinePlaceholder":309},[200,1225,1226],{"class":73,"line":368},[200,1227,1228],{"class":316},"# 1. pandas writes the raw table, starting a few rows down to leave a band\n",[200,1230,1231,1234,1236,1239,1241,1244,1246,1248,1251,1254,1256,1258,1260,1262,1265],{"class":73,"line":379},[200,1232,1233],{"class":258},"df ",[200,1235,348],{"class":254},[200,1237,1238],{"class":258}," pd.DataFrame({",[200,1240,1049],{"class":209},[200,1242,1243],{"class":258},": [",[200,1245,1065],{"class":209},[200,1247,335],{"class":258},[200,1249,1250],{"class":209},"\"South\"",[200,1252,1253],{"class":258},"], ",[200,1255,1054],{"class":209},[200,1257,1243],{"class":258},[200,1259,1070],{"class":292},[200,1261,335],{"class":258},[200,1263,1264],{"class":292},"18890",[200,1266,1267],{"class":258},"]})\n",[200,1269,1270,1273,1276,1278,1281,1283,1286],{"class":73,"line":390},[200,1271,1272],{"class":258},"df.to_excel(",[200,1274,1275],{"class":209},"\"pandas_report.xlsx\"",[200,1277,335],{"class":258},[200,1279,1280],{"class":344},"sheet_name",[200,1282,348],{"class":254},[200,1284,1285],{"class":209},"\"Sales\"",[200,1287,1288],{"class":258},",\n",[200,1290,1291,1294,1296,1299,1301,1304,1306,1309],{"class":73,"line":401},[200,1292,1293],{"class":344},"            index",[200,1295,348],{"class":254},[200,1297,1298],{"class":292},"False",[200,1300,335],{"class":258},[200,1302,1303],{"class":344},"startrow",[200,1305,348],{"class":254},[200,1307,1308],{"class":292},"3",[200,1310,360],{"class":258},[200,1312,1313],{"class":73,"line":406},[200,1314,310],{"emptyLinePlaceholder":309},[200,1316,1317],{"class":73,"line":421},[200,1318,1319],{"class":316},"# 2. re-open with openpyxl and drop the logo into the empty band\n",[200,1321,1322,1324,1326,1329,1331],{"class":73,"line":436},[200,1323,371],{"class":258},[200,1325,348],{"class":254},[200,1327,1328],{"class":258}," load_workbook(",[200,1330,1275],{"class":209},[200,1332,360],{"class":258},[200,1334,1335,1337,1339,1342,1344],{"class":73,"line":441},[200,1336,382],{"class":258},[200,1338,348],{"class":254},[200,1340,1341],{"class":258}," wb[",[200,1343,1285],{"class":209},[200,1345,905],{"class":258},[200,1347,1348,1350,1352,1354,1356],{"class":73,"line":452},[200,1349,858],{"class":258},[200,1351,861],{"class":292},[200,1353,864],{"class":258},[200,1355,348],{"class":254},[200,1357,1358],{"class":292}," 40\n",[200,1360,1361],{"class":73,"line":891},[200,1362,310],{"emptyLinePlaceholder":309},[200,1364,1365,1367,1369,1371,1373],{"class":73,"line":908},[200,1366,409],{"class":258},[200,1368,348],{"class":254},[200,1370,414],{"class":258},[200,1372,1217],{"class":209},[200,1374,360],{"class":258},[200,1376,1377,1379,1381,1384,1386],{"class":73,"line":919},[200,1378,1001],{"class":258},[200,1380,348],{"class":254},[200,1382,1383],{"class":292}," 140",[200,1385,335],{"class":258},[200,1387,1388],{"class":292},"44\n",[200,1390,1391,1393,1395],{"class":73,"line":959},[200,1392,424],{"class":258},[200,1394,427],{"class":209},[200,1396,360],{"class":258},[200,1398,1399],{"class":73,"line":980},[200,1400,310],{"emptyLinePlaceholder":309},[200,1402,1403,1405,1407],{"class":73,"line":985},[200,1404,444],{"class":258},[200,1406,1275],{"class":209},[200,1408,360],{"class":258},[200,1410,1411,1413,1415,1418],{"class":73,"line":998},[200,1412,455],{"class":292},[200,1414,458],{"class":258},[200,1416,1417],{"class":209},"\"pandas wrote the data; openpyxl added the logo\"",[200,1419,360],{"class":258},[10,1421,1422,1425,1426,32],{},[14,1423,1424],{},"startrow=3"," reserves rows 1-3 for the header band so the logo never overlaps the table. The crucial ordering rule: do the openpyxl step ",[178,1427,1428],{},"last",[167,1430,1432],{"id":1431},"why-pandas-erases-your-images","Why pandas erases your images",[10,1434,1435,1436,1439,1440,1442],{},"If you embed a logo and ",[19,1437,1438],{},"later"," write to that same file with ",[14,1441,1119],{},", the logo disappears. pandas does not read or preserve drawings — when it writes a sheet it regenerates the file's XML from the DataFrame alone, dropping the image, the charts, and most styling along with it. The fix is sequencing.",[1444,1445,1446,1462],"table",{},[1447,1448,1449],"thead",{},[1450,1451,1452,1456,1459],"tr",{},[1453,1454,1455],"th",{},"Symptom",[1453,1457,1458],{},"Cause",[1453,1460,1461],{},"Fix",[1463,1464,1465,1483,1501,1517],"tbody",{},[1450,1466,1467,1471,1477],{},[1468,1469,1470],"td",{},"Logo gone after a later pandas write",[1468,1472,1473,1476],{},[14,1474,1475],{},"to_excel"," rebuilds the sheet from the DataFrame and discards drawings",[1468,1478,1479,1480,1482],{},"Make the openpyxl image step the ",[178,1481,1428],{}," write to the file",[1450,1484,1485,1493,1496],{},[1468,1486,1487,1489,1490],{},[14,1488,188],{}," on ",[14,1491,1492],{},"Image(...)",[1468,1494,1495],{},"Pillow not installed",[1468,1497,1498],{},[14,1499,1500],{},"pip install pillow",[1450,1502,1503,1506,1509],{},[1468,1504,1505],{},"Logo overlaps the data table",[1468,1507,1508],{},"Images float; they never push cells down",[1468,1510,1511,1512,488,1515],{},"Reserve a band with ",[14,1513,1514],{},"row_dimensions[1].height",[14,1516,1303],{},[1450,1518,1519,1522,1528],{},[1468,1520,1521],{},"Image missing from saved file",[1468,1523,1524,1525],{},"Source PNG deleted before ",[14,1526,1527],{},"wb.save()",[1468,1529,1530],{},"Keep the file on disk until after the save call",[10,1532,1533,1534,1537,1538,1541],{},"The mental model: pandas owns ",[19,1535,1536],{},"data","; openpyxl owns the ",[19,1539,1540],{},"decorated artifact",". Once a workbook carries a logo, every subsequent edit must go through openpyxl, never back through pandas.",[167,1543,1545],{"id":1544},"frequently-asked-questions","Frequently asked questions",[10,1547,1548,1554,1555,1558,1559,1561,1562,1565],{},[178,1549,1550,1551,1553],{},"Why isn't my image in ",[14,1552,25],{},"?","\nBecause an Excel image is never a cell value. It is a floating drawing in a separate layer that is merely ",[19,1556,1557],{},"anchored"," to A1. ",[14,1560,25],{}," reads the cell's data; the image lives in ",[14,1563,1564],{},"ws._images",", and Excel renders it on top of the grid.",[10,1567,1568,1571,1572,1575,1576,1578],{},[178,1569,1570],{},"Do I really need Pillow?","\nYes. openpyxl uses Pillow to read the image's dimensions and validate its format. Constructing ",[14,1573,1574],{},"openpyxl.drawing.image.Image(...)"," without Pillow installed raises ",[14,1577,188],{}," immediately, even for a plain PNG.",[10,1580,1581,1584,1585,1588,1589,1592],{},[178,1582,1583],{},"My logo overlaps the data — how do I push the table down?","\nYou cannot push cells with an image; it floats. Instead reserve space: increase ",[14,1586,1587],{},"ws.row_dimensions[1].height"," and write your data starting a few rows lower (",[14,1590,1591],{},"startrow="," in pandas, or just append below row 1 in openpyxl).",[10,1594,1595,1598,488,1600,1602,1603,1606],{},[178,1596,1597],{},"Are the width and height in pixels or Excel units?",[14,1599,487],{},[14,1601,491],{}," are in ",[178,1604,1605],{},"pixels"," and control the on-sheet display size, independent of the source file's resolution. Column widths and row heights use Excel's own character\u002Fpoint units, which is why aligning them takes a conversion factor.",[10,1608,1609,1612,1613,1616,1617,1619,1620,1622],{},[178,1610,1611],{},"Will the logo survive if I reopen and resave with openpyxl?","\nYes. ",[14,1614,1615],{},"load_workbook"," reads existing drawings and ",[14,1618,1527],{}," writes them back. Only tools that rebuild the sheet from scratch — chiefly ",[14,1621,1119],{}," — drop the image.",[167,1624,1626],{"id":1625},"conclusion","Conclusion",[10,1628,1629,1630,1632,1633,1636],{},"An Excel image is a floating drawing anchored to a cell, not a cell value — internalize that and the rest follows. Build an ",[14,1631,229],{},", size it in pixels, anchor it with ",[14,1634,1635],{},"ws.add_image",", and reserve space with row height and a merged title since the float never moves cells. Because pandas cannot embed images and erases them on rewrite, always make openpyxl the last hand to touch the file.",[167,1638,1640],{"id":1639},"where-to-go-next","Where to go next",[10,1642,1643],{},"Up to the parent pillar:",[1645,1646,1647],"ul",{},[1648,1649,1650,1652],"li",{},[28,1651,31],{"href":30}," — the full polished-report track.",[10,1654,1655],{},"Go deeper here:",[1645,1657,1658],{},[1648,1659,1660,1662],{},[28,1661,710],{"href":709}," — the complete step-by-step header build with aspect-safe sizing.",[10,1664,1665],{},"Sibling clusters:",[1645,1667,1668,1675,1682],{},[1648,1669,1670,1674],{},[28,1671,1673],{"href":1672},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002F","Styling Excel Cells with openpyxl"," — fonts, fills, borders, and column widths to match your branded header.",[1648,1676,1677,1681],{},[28,1678,1680],{"href":1679},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002F","Applying Number and Date Formats in Excel"," — currency, percentage, and date display codes.",[1648,1683,1684,1688],{},[28,1685,1687],{"href":1686},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl"," — native charts that, like images, must survive the same pandas-last rule.",[1690,1691,1692],"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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":196,"searchDepth":268,"depth":268,"links":1694},[1695,1696,1697,1698,1699,1700,1701,1702,1703],{"id":169,"depth":268,"text":170},{"id":222,"depth":268,"text":223},{"id":480,"depth":268,"text":481},{"id":714,"depth":268,"text":715},{"id":1113,"depth":268,"text":1114},{"id":1431,"depth":268,"text":1432},{"id":1544,"depth":268,"text":1545},{"id":1625,"depth":268,"text":1626},{"id":1639,"depth":268,"text":1640},"2026-06-18","Embed logos and images into Excel with openpyxl: floating drawings anchored to cells, pixel resizing, header bands, and why pandas can't keep them.","md",[1708,1711,1713,1715,1717],{"q":1709,"a":1710},"Why isn't my image in ws[\"A1\"].value?","Because an Excel image is never a cell value. It is a floating drawing in a separate layer that is merely *anchored* to A1. ws[\"A1\"].value reads the cell's data; the image lives in ws._images, and Excel renders it on top of the grid.",{"q":1570,"a":1712},"Yes. openpyxl uses Pillow to read the image's dimensions and validate its format. Constructing openpyxl.drawing.image.Image(...) without Pillow installed raises ImportError immediately, even for a plain PNG.",{"q":1583,"a":1714},"You cannot push cells with an image; it floats. Instead reserve space: increase ws.row_dimensions[1].height and write your data starting a few rows lower (startrow= in pandas, or just append below row 1 in openpyxl).",{"q":1597,"a":1716},"img.width and img.height are in pixels and control the on-sheet display size, independent of the source file's resolution. Column widths and row heights use Excel's own character\u002Fpoint units, which is why aligning them takes a conversion factor.",{"q":1611,"a":1718},"Yes. load_workbook reads existing drawings and wb.save() writes them back. Only tools that rebuild the sheet from scratch — chiefly pandas.to_excel — drop the image.",{},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel",{"title":1722,"description":1723},"Insert Images & Logos into Excel in Python","Add logos and images to Excel reports with openpyxl: anchor a floating drawing to a cell, resize in pixels, build a header band, and survive pandas rewrites.","inserting-images-and-logos-into-excel","formatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Findex","cluster","EI2md-u4aQvjIs6ig71a2t6m0_eGfKtlfbE0A7S61RE",[1729,1733],{"title":1730,"path":1731,"stem":1732,"children":-1},"Create a Bar Chart in Excel with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl\u002Findex",{"title":710,"path":1734,"stem":1735,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fadd-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",1781795518492]