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