[{"data":1,"prerenderedAt":2210},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python":2176,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python":2185,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python":2190,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python":2191},{"id":4,"title":5,"body":6,"dateModified":2143,"datePublished":2143,"description":2144,"extension":2145,"faq":2146,"keyword":2155,"kind":2156,"meta":2157,"navigation":312,"path":2165,"seo":2166,"slug":2169,"stem":2170,"topics":2171,"type":2174,"__hash__":2175},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python\u002Findex.md","Generate QR Codes and Barcodes in Excel with Python",{"type":7,"value":8,"toc":2131},"minimark",[9,19,24,65,83,267,271,274,518,530,534,542,864,871,1002,1006,1009,1439,1463,1467,1470,1552,1781,1784,1998,2002,2011,2015,2050,2054,2066,2070,2076,2082,2088,2094,2098,2127],[10,11,12,13,18],"p",{},"Asset registers, stock lists, event badges and equipment labels often need a code people can scan: a QR code that opens the asset's page, or a barcode a handheld scanner reads into the stock system. Excel has no built-in barcode generator, and barcode fonts are fiddly — they need the right font installed on every machine and check characters calculated by hand. Generating the codes as images in Python and placing one in each row avoids both problems. The part that is easy to skip, and should not be, is checking that the codes still scan after Excel scales them and a printer prints them. This page is part of ",[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",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas qrcode pillow python-barcode zxing-cpp\n","bash","",[32,33,34],"code",{"__ignoreMap":30},[35,36,39,43,47,50,53,56,59,62],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," openpyxl",[35,51,52],{"class":45}," pandas",[35,54,55],{"class":45}," qrcode",[35,57,58],{"class":45}," pillow",[35,60,61],{"class":45}," python-barcode",[35,63,64],{"class":45}," zxing-cpp\n",[10,66,67,70,71,74,75,78,79,82],{},[32,68,69],{},"qrcode"," makes QR codes, ",[32,72,73],{},"python-barcode"," makes linear barcodes such as Code 128, and Pillow renders both as images. ",[32,76,77],{},"zxing-cpp"," is only used for checking: it reads codes back from an image. ",[32,80,81],{},"assets.xlsx"," lists six assets with an ID, description and location.",[84,85,93,94,93,98,93,102,93,110,93,120,93,131,93,138,93,143,93,149,93,154,93,158,93,165,93,171,93,175,93,178,93,182,93,185,93,188,93,194,93,198,93,202,93,205,93,209,93,212,93,217,93,224,93,229,93,232,93,236,93,239,93,244,93,251,93,257,93,260,93,263],"svg",{"viewBox":86,"role":87,"ariaLabelledBy":88,"xmlns":91,"style":92},"4 0 752 160","img",[89,90],"qr-flow-t","qr-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[95,96,97],"title",{"id":89},"From a list of IDs to scannable labels",[99,100,101],"desc",{"id":90},"Each asset ID is turned into a QR code image and a Code 128 image in memory, both are anchored in the asset's row, rows are resized to fit, and the rendered PDF is decoded to prove every code scans.",[103,104],"rect",{"x":105,"y":106,"width":107,"height":108,"fill":109},"4","0","752","160","#ffffff",[103,111],{"x":112,"y":113,"width":114,"height":115,"rx":116,"fill":117,"stroke":118,"style":119},"20.0","16.0","115.2","128.0","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[121,122,124],"g",{"transform":123},"translate(65.6,46.5) scale(1.000)",[125,126],"path",{"d":127,"fill":128,"stroke":129,"style":130},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[132,133,137],"text",{"x":134,"y":135,"style":136},"77.6","93.0","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Asset list",[132,139,142],{"x":134,"y":140,"style":141},"112.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","one ID per row",[37,144],{"x1":145,"y1":146,"x2":147,"y2":146,"stroke":148,"style":119},"140.2","80.0","162.2","var(--brand,#5b5cf0)",[150,151],"polygon",{"points":152,"fill":153},"166.2,80.0 157.2,85.0 157.2,75.0","#5b5cf0",[103,155],{"x":156,"y":113,"width":114,"height":115,"rx":116,"fill":157,"stroke":148,"style":119},"171.2","#f0f4ff",[121,159,161],{"transform":160},"translate(216.8,37.8) scale(1.000)",[125,162],{"d":163,"fill":128,"stroke":164,"style":130},"M4 5h16v14H4z M4 16l5-5 4 4 3-3 4 4 M15 9h.01","var(--brand-strong,#4338ca)",[132,166,170],{"x":167,"y":168,"style":169},"228.8","84.2","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Make images",[132,172,174],{"x":167,"y":173,"style":141},"103.8","qrcode,",[132,176,73],{"x":167,"y":177,"style":141},"121.2",[37,179],{"x1":180,"y1":146,"x2":181,"y2":146,"stroke":148,"style":119},"291.4","313.4",[150,183],{"points":184,"fill":153},"317.4,80.0 308.4,85.0 308.4,75.0",[103,186],{"x":187,"y":113,"width":114,"height":115,"rx":116,"fill":157,"stroke":148,"style":119},"322.4",[121,189,191],{"transform":190},"translate(368.0,37.8) scale(1.000)",[125,192],{"d":193,"fill":128,"stroke":164,"style":130},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16",[132,195,197],{"x":196,"y":168,"style":169},"380.0","Anchor in",[132,199,201],{"x":196,"y":200,"style":169},"101.8","cells",[132,203,204],{"x":196,"y":177,"style":141},"openpyxl Image",[37,206],{"x1":207,"y1":146,"x2":208,"y2":146,"stroke":148,"style":119},"442.6","464.6",[150,210],{"points":211,"fill":153},"468.6,80.0 459.6,85.0 459.6,75.0",[103,213],{"x":214,"y":113,"width":114,"height":115,"rx":116,"fill":215,"stroke":216,"style":119},"473.6","#d9f4f1","var(--teal,#0f9488)",[121,218,220],{"transform":219},"translate(519.2,46.5) scale(1.000)",[125,221],{"d":222,"fill":128,"stroke":223,"style":130},"M12 9a3 3 0 1 0 .01 0z M12 2.5v3 M12 18.5v3 M2.5 12h3 M18.5 12h3 M5.3 5.3l2.1 2.1 M16.6 16.6l2.1 2.1 M5.3 18.7l2.1-2.1 M16.6 7.4l2.1-2.1","var(--teal-ink,#0b6157)",[132,225,228],{"x":226,"y":135,"style":227},"531.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Size rows",[132,230,231],{"x":226,"y":140,"style":141},"fit the images",[37,233],{"x1":234,"y1":146,"x2":235,"y2":146,"stroke":148,"style":119},"593.8","615.8",[150,237],{"points":238,"fill":153},"619.8,80.0 610.8,85.0 610.8,75.0",[103,240],{"x":241,"y":113,"width":114,"height":115,"rx":116,"fill":242,"stroke":243,"style":119},"624.8","#fdefd8","var(--gold,#b4740a)",[121,245,247],{"transform":246},"translate(670.4,29.0) scale(1.000)",[125,248],{"d":249,"fill":128,"stroke":250,"style":130},"M5 12.5l4.5 4.5L19 7","var(--gold-ink,#7a4e06)",[132,252,256],{"x":253,"y":254,"style":255},"682.4","75.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Decode",[132,258,259],{"x":253,"y":135,"style":255},"render",[132,261,262],{"x":253,"y":140,"style":141},"zxing-cpp at",[132,264,266],{"x":253,"y":265,"style":141},"130.0","300 dpi",[20,268,270],{"id":269},"step-1-make-a-qr-code-in-memory","Step 1 — Make a QR code in memory",[10,272,273],{},"Build the QR code with an explicit error-correction level and a small module size, and keep it in memory — there is no reason to write hundreds of PNG files to disk:",[25,275,279],{"className":276,"code":277,"language":278,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","import io\nimport qrcode\nimport zxingcpp\n\ndef qr_png(text, box=4):\n    q = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=box, border=2)\n    q.add_data(text)\n    q.make(fit=True)\n    img = q.make_image(fill_color=\"black\", back_color=\"white\").convert(\"RGB\")\n    buf = io.BytesIO()\n    img.save(buf, format=\"PNG\")\n    return buf, img\n\nbuf, img = qr_png(\"https:\u002F\u002Fassets.example.com\u002FAS-10442\")\nprint(img.size, [r.text for r in zxingcpp.read_barcodes(img)])\n","python",[32,280,281,291,299,307,314,336,381,387,403,440,451,467,476,481,497],{"__ignoreMap":30},[35,282,283,287],{"class":37,"line":38},[35,284,286],{"class":285},"s-kum","import",[35,288,290],{"class":289},"skGVy"," io\n",[35,292,294,296],{"class":37,"line":293},2,[35,295,286],{"class":285},[35,297,298],{"class":289}," qrcode\n",[35,300,302,304],{"class":37,"line":301},3,[35,303,286],{"class":285},[35,305,306],{"class":289}," zxingcpp\n",[35,308,310],{"class":37,"line":309},4,[35,311,313],{"emptyLinePlaceholder":312},true,"\n",[35,315,317,320,324,327,330,333],{"class":37,"line":316},5,[35,318,319],{"class":285},"def",[35,321,323],{"class":322},"s_Opv"," qr_png",[35,325,326],{"class":289},"(text, box",[35,328,329],{"class":285},"=",[35,331,105],{"class":332},"sP0c6",[35,334,335],{"class":289},"):\n",[35,337,339,342,344,347,351,353,356,359,362,365,367,370,373,375,378],{"class":37,"line":338},6,[35,340,341],{"class":289},"    q ",[35,343,329],{"class":285},[35,345,346],{"class":289}," qrcode.QRCode(",[35,348,350],{"class":349},"sa561","error_correction",[35,352,329],{"class":285},[35,354,355],{"class":289},"qrcode.constants.",[35,357,358],{"class":332},"ERROR_CORRECT_M",[35,360,361],{"class":289},", ",[35,363,364],{"class":349},"box_size",[35,366,329],{"class":285},[35,368,369],{"class":289},"box, ",[35,371,372],{"class":349},"border",[35,374,329],{"class":285},[35,376,377],{"class":332},"2",[35,379,380],{"class":289},")\n",[35,382,384],{"class":37,"line":383},7,[35,385,386],{"class":289},"    q.add_data(text)\n",[35,388,390,393,396,398,401],{"class":37,"line":389},8,[35,391,392],{"class":289},"    q.make(",[35,394,395],{"class":349},"fit",[35,397,329],{"class":285},[35,399,400],{"class":332},"True",[35,402,380],{"class":289},[35,404,406,409,411,414,417,419,422,424,427,429,432,435,438],{"class":37,"line":405},9,[35,407,408],{"class":289},"    img ",[35,410,329],{"class":285},[35,412,413],{"class":289}," q.make_image(",[35,415,416],{"class":349},"fill_color",[35,418,329],{"class":285},[35,420,421],{"class":45},"\"black\"",[35,423,361],{"class":289},[35,425,426],{"class":349},"back_color",[35,428,329],{"class":285},[35,430,431],{"class":45},"\"white\"",[35,433,434],{"class":289},").convert(",[35,436,437],{"class":45},"\"RGB\"",[35,439,380],{"class":289},[35,441,443,446,448],{"class":37,"line":442},10,[35,444,445],{"class":289},"    buf ",[35,447,329],{"class":285},[35,449,450],{"class":289}," io.BytesIO()\n",[35,452,454,457,460,462,465],{"class":37,"line":453},11,[35,455,456],{"class":289},"    img.save(buf, ",[35,458,459],{"class":349},"format",[35,461,329],{"class":285},[35,463,464],{"class":45},"\"PNG\"",[35,466,380],{"class":289},[35,468,470,473],{"class":37,"line":469},12,[35,471,472],{"class":285},"    return",[35,474,475],{"class":289}," buf, img\n",[35,477,479],{"class":37,"line":478},13,[35,480,313],{"emptyLinePlaceholder":312},[35,482,484,487,489,492,495],{"class":37,"line":483},14,[35,485,486],{"class":289},"buf, img ",[35,488,329],{"class":285},[35,490,491],{"class":289}," qr_png(",[35,493,494],{"class":45},"\"https:\u002F\u002Fassets.example.com\u002FAS-10442\"",[35,496,380],{"class":289},[35,498,500,503,506,509,512,515],{"class":37,"line":499},15,[35,501,502],{"class":332},"print",[35,504,505],{"class":289},"(img.size, [r.text ",[35,507,508],{"class":285},"for",[35,510,511],{"class":289}," r ",[35,513,514],{"class":285},"in",[35,516,517],{"class":289}," zxingcpp.read_barcodes(img)])\n",[10,519,520,522,523,525,526,529],{},[32,521,364],{}," is the number of pixels per QR module and ",[32,524,372],{}," the quiet zone in modules; the specification asks for four, but two is enough when the code sits in a white cell with space around it. Error-correction level M recovers from about 15% damage, a good default for printed labels. Decoding the image straight away with ",[32,527,528],{},"zxingcpp.read_barcodes"," confirms the content before anything else happens — a URL with a typo scans perfectly and goes nowhere, so read the text back, not just the fact that something decoded.",[20,531,533],{"id":532},"step-2-make-a-code-128-barcode","Step 2 — Make a Code 128 barcode",[10,535,536,537,541],{},"Linear barcodes are less forgiving than QR codes. The two settings that decide whether a scanner reads one are the width of the thinnest bar and the blank ",[538,539,540],"em",{},"quiet zone"," at each end:",[25,543,545],{"className":276,"code":544,"language":278,"meta":30,"style":30},"import barcode\nfrom barcode.writer import ImageWriter\nfrom PIL import Image\n\ndef code128_png(text, dpi=300, module_mm=0.254, quiet_mm=6.5):\n    buf = io.BytesIO()\n    barcode.get(\"code128\", text, writer=ImageWriter()).write(\n        buf, options={\"module_width\": module_mm, \"module_height\": 9.0, \"quiet_zone\": quiet_mm,\n                      \"font_size\": 7, \"text_distance\": 3.0, \"dpi\": dpi})\n    return buf, Image.open(io.BytesIO(buf.getvalue()))\n\nfor dpi, module in [(200, 0.2), (300, 0.254)]:\n    _, bc = code128_png(\"AS-10442\", dpi=dpi, module_mm=module)\n    px = module \u002F 25.4 * dpi\n    print(f\"{dpi} dpi, {module} mm bars = {px:.2f} px:\", bc.size, [r.text for r in zxingcpp.read_barcodes(bc)])\n",[32,546,547,554,567,580,584,617,625,644,680,708,715,719,751,782,804],{"__ignoreMap":30},[35,548,549,551],{"class":37,"line":38},[35,550,286],{"class":285},[35,552,553],{"class":289}," barcode\n",[35,555,556,559,562,564],{"class":37,"line":293},[35,557,558],{"class":285},"from",[35,560,561],{"class":289}," barcode.writer ",[35,563,286],{"class":285},[35,565,566],{"class":289}," ImageWriter\n",[35,568,569,571,574,577],{"class":37,"line":301},[35,570,558],{"class":285},[35,572,573],{"class":332}," PIL",[35,575,576],{"class":285}," import",[35,578,579],{"class":289}," Image\n",[35,581,582],{"class":37,"line":309},[35,583,313],{"emptyLinePlaceholder":312},[35,585,586,588,591,594,596,599,602,604,607,610,612,615],{"class":37,"line":316},[35,587,319],{"class":285},[35,589,590],{"class":322}," code128_png",[35,592,593],{"class":289},"(text, dpi",[35,595,329],{"class":285},[35,597,598],{"class":332},"300",[35,600,601],{"class":289},", module_mm",[35,603,329],{"class":285},[35,605,606],{"class":332},"0.254",[35,608,609],{"class":289},", quiet_mm",[35,611,329],{"class":285},[35,613,614],{"class":332},"6.5",[35,616,335],{"class":289},[35,618,619,621,623],{"class":37,"line":338},[35,620,445],{"class":289},[35,622,329],{"class":285},[35,624,450],{"class":289},[35,626,627,630,633,636,639,641],{"class":37,"line":383},[35,628,629],{"class":289},"    barcode.get(",[35,631,632],{"class":45},"\"code128\"",[35,634,635],{"class":289},", text, ",[35,637,638],{"class":349},"writer",[35,640,329],{"class":285},[35,642,643],{"class":289},"ImageWriter()).write(\n",[35,645,646,649,652,654,657,660,663,666,669,672,674,677],{"class":37,"line":389},[35,647,648],{"class":289},"        buf, ",[35,650,651],{"class":349},"options",[35,653,329],{"class":285},[35,655,656],{"class":289},"{",[35,658,659],{"class":45},"\"module_width\"",[35,661,662],{"class":289},": module_mm, ",[35,664,665],{"class":45},"\"module_height\"",[35,667,668],{"class":289},": ",[35,670,671],{"class":332},"9.0",[35,673,361],{"class":289},[35,675,676],{"class":45},"\"quiet_zone\"",[35,678,679],{"class":289},": quiet_mm,\n",[35,681,682,685,687,690,692,695,697,700,702,705],{"class":37,"line":405},[35,683,684],{"class":45},"                      \"font_size\"",[35,686,668],{"class":289},[35,688,689],{"class":332},"7",[35,691,361],{"class":289},[35,693,694],{"class":45},"\"text_distance\"",[35,696,668],{"class":289},[35,698,699],{"class":332},"3.0",[35,701,361],{"class":289},[35,703,704],{"class":45},"\"dpi\"",[35,706,707],{"class":289},": dpi})\n",[35,709,710,712],{"class":37,"line":442},[35,711,472],{"class":285},[35,713,714],{"class":289}," buf, Image.open(io.BytesIO(buf.getvalue()))\n",[35,716,717],{"class":37,"line":453},[35,718,313],{"emptyLinePlaceholder":312},[35,720,721,723,726,728,731,734,736,739,742,744,746,748],{"class":37,"line":469},[35,722,508],{"class":285},[35,724,725],{"class":289}," dpi, module ",[35,727,514],{"class":285},[35,729,730],{"class":289}," [(",[35,732,733],{"class":332},"200",[35,735,361],{"class":289},[35,737,738],{"class":332},"0.2",[35,740,741],{"class":289},"), (",[35,743,598],{"class":332},[35,745,361],{"class":289},[35,747,606],{"class":332},[35,749,750],{"class":289},")]:\n",[35,752,753,756,758,761,764,766,769,771,774,777,779],{"class":37,"line":478},[35,754,755],{"class":289},"    _, bc ",[35,757,329],{"class":285},[35,759,760],{"class":289}," code128_png(",[35,762,763],{"class":45},"\"AS-10442\"",[35,765,361],{"class":289},[35,767,768],{"class":349},"dpi",[35,770,329],{"class":285},[35,772,773],{"class":289},"dpi, ",[35,775,776],{"class":349},"module_mm",[35,778,329],{"class":285},[35,780,781],{"class":289},"module)\n",[35,783,784,787,789,792,795,798,801],{"class":37,"line":483},[35,785,786],{"class":289},"    px ",[35,788,329],{"class":285},[35,790,791],{"class":289}," module ",[35,793,794],{"class":285},"\u002F",[35,796,797],{"class":332}," 25.4",[35,799,800],{"class":285}," *",[35,802,803],{"class":289}," dpi\n",[35,805,806,809,812,815,818,821,823,826,829,831,834,836,839,841,844,847,849,852,855,857,859,861],{"class":37,"line":499},[35,807,808],{"class":332},"    print",[35,810,811],{"class":289},"(",[35,813,814],{"class":285},"f",[35,816,817],{"class":45},"\"",[35,819,656],{"class":820},"sSjpA",[35,822,768],{"class":289},[35,824,825],{"class":820},"}",[35,827,828],{"class":45}," dpi, ",[35,830,656],{"class":820},[35,832,833],{"class":289},"module",[35,835,825],{"class":820},[35,837,838],{"class":45}," mm bars = ",[35,840,656],{"class":820},[35,842,843],{"class":289},"px",[35,845,846],{"class":285},":.2f",[35,848,825],{"class":820},[35,850,851],{"class":45}," px:\"",[35,853,854],{"class":289},", bc.size, [r.text ",[35,856,508],{"class":285},[35,858,511],{"class":289},[35,860,514],{"class":285},[35,862,863],{"class":289}," zxingcpp.read_barcodes(bc)])\n",[10,865,866,867,870],{},"The library's defaults — 0.2 mm bars — at 200 dpi make each bar 1.57 pixels wide, so bar edges fall between pixels, get smeared into grey, and the reader finds nothing at all. A ",[32,868,869],{},"module_width"," of 0.254 mm at 300 dpi is exactly three pixels per bar, every edge lands on a pixel boundary, and the same text decodes. In tests with a clean image, the reader tolerated a trimmed quiet zone — the blank margin at each end — but printed labels sit next to borders, text and other codes, which is exactly what the quiet zone protects against, so keep the default 6.5 mm and make the column wider instead. Code 128 is the usual choice for internal IDs because it encodes letters, digits and punctuation compactly; use EAN-13 only for retail product numbers.",[84,872,93,877,93,880,93,883,93,886,93,891,93,898,93,904,93,910,93,916,93,922,93,926,93,931,93,935,93,940,93,944,93,949,93,952,93,958,93,963,93,967,93,971,93,976,93,979,93,984,93,987,93,992,93,995],{"viewBox":873,"role":87,"ariaLabelledBy":874,"xmlns":91,"style":92},"4 0 752 233",[875,876],"qr-rules-t","qr-rules-d",[95,878,879],{"id":875},"What makes a printed code scan",[99,881,882],{"id":876},"Two panels. For QR codes the rules are an error-correction level, whole-pixel modules and a quiet border. For Code 128 the rules are bars that are whole pixels wide, the default quiet zone at each end, and enough height.",[103,884],{"x":105,"y":106,"width":107,"height":885,"fill":109},"233",[103,887],{"x":112,"y":113,"width":888,"height":889,"rx":890,"fill":157,"stroke":148,"style":119},"351.0","201.0","14",[121,892,894],{"transform":893},"translate(182.5,29.0) scale(1.083)",[125,895],{"d":896,"fill":128,"stroke":164,"style":897},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[132,899,903],{"x":900,"y":901,"style":902},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","QR code",[37,905],{"x1":906,"y1":907,"x2":908,"y2":907,"stroke":148,"style":909},"36.0","91.0","355.0","stroke-width:1px",[132,911,915],{"x":912,"y":913,"style":914},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","error correction M or Q",[121,917,919],{"transform":918},"translate(38.5,99.5) scale(0.625)",[125,920],{"d":249,"fill":128,"stroke":216,"style":921},"stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[132,923,925],{"x":912,"y":924,"style":914},"137.0","whole-pixel box_size",[121,927,929],{"transform":928},"translate(38.5,125.5) scale(0.625)",[125,930],{"d":249,"fill":128,"stroke":216,"style":921},[132,932,934],{"x":912,"y":933,"style":914},"163.0","border of 2–4 modules",[121,936,938],{"transform":937},"translate(38.5,151.5) scale(0.625)",[125,939],{"d":249,"fill":128,"stroke":216,"style":921},[132,941,943],{"x":912,"y":942,"style":914},"189.0","read the text back",[121,945,947],{"transform":946},"translate(38.5,177.5) scale(0.625)",[125,948],{"d":249,"fill":128,"stroke":216,"style":921},[103,950],{"x":951,"y":113,"width":888,"height":889,"rx":890,"fill":215,"stroke":216,"style":119},"389.0",[121,953,955],{"transform":954},"translate(551.5,29.0) scale(1.083)",[125,956],{"d":957,"fill":128,"stroke":223,"style":897},"M3 5h18l-7 8v6l-4 2v-8z",[132,959,962],{"x":960,"y":901,"style":961},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Code 128",[37,964],{"x1":965,"y1":907,"x2":966,"y2":907,"stroke":216,"style":909},"405.0","724.0",[132,968,970],{"x":969,"y":913,"style":914},"429.0","bars a whole number of pixels",[121,972,974],{"transform":973},"translate(407.5,99.5) scale(0.625)",[125,975],{"d":249,"fill":128,"stroke":216,"style":921},[132,977,978],{"x":969,"y":924,"style":914},"keep the 6.5 mm quiet zone",[121,980,982],{"transform":981},"translate(407.5,125.5) scale(0.625)",[125,983],{"d":249,"fill":128,"stroke":216,"style":921},[132,985,986],{"x":969,"y":933,"style":914},"at least 8–9 mm tall",[121,988,990],{"transform":989},"translate(407.5,151.5) scale(0.625)",[125,991],{"d":249,"fill":128,"stroke":216,"style":921},[132,993,994],{"x":969,"y":942,"style":914},"trimming the margins breaks it",[121,996,998],{"transform":997},"translate(407.5,177.5) scale(0.625)",[125,999],{"d":1000,"fill":128,"stroke":1001,"style":921},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)",[20,1003,1005],{"id":1004},"step-3-anchor-one-of-each-in-every-row","Step 3 — Anchor one of each in every row",[10,1007,1008],{},"Load the asset list, add two columns, and place the images with openpyxl, each anchored to its row's cell:",[25,1010,1012],{"className":276,"code":1011,"language":278,"meta":30,"style":30},"import pandas as pd\nfrom openpyxl import load_workbook\nfrom openpyxl.drawing.image import Image as XLImage\nfrom openpyxl.styles import Alignment\n\nassets = pd.read_excel(\"assets.xlsx\")\nwb = load_workbook(\"assets.xlsx\")\nws = wb.active\nws[\"D1\"], ws[\"E1\"] = \"QR\", \"Barcode\"\nfor r, asset_id in enumerate(assets[\"AssetID\"], start=2):\n    qbuf, _ = qr_png(f\"https:\u002F\u002Fassets.example.com\u002F{asset_id}\")\n    q = XLImage(qbuf)\n    q.width = q.height = 90\n    ws.add_image(q, f\"D{r}\")\n    bbuf, bimg = code128_png(asset_id)\n    b = XLImage(bbuf)\n    b.width, b.height = 260, round(260 * bimg.height \u002F bimg.width)\n    ws.add_image(b, f\"E{r}\")\n    ws.row_dimensions[r].height = 72\n    for c in ws[r][:3]:\n        c.alignment = Alignment(vertical=\"center\")\nws.column_dimensions[\"B\"].width = 18\nws.column_dimensions[\"D\"].width = 14\nws.column_dimensions[\"E\"].width = 38\nwb.save(\"assets_labels.xlsx\")\nprint(len(ws._images), \"images placed\")\n",[32,1013,1014,1027,1039,1056,1068,1072,1087,1101,1111,1138,1168,1193,1202,1217,1238,1248,1259,1290,1311,1322,1342,1363,1380,1395,1410,1421],{"__ignoreMap":30},[35,1015,1016,1018,1021,1024],{"class":37,"line":38},[35,1017,286],{"class":285},[35,1019,1020],{"class":289}," pandas ",[35,1022,1023],{"class":285},"as",[35,1025,1026],{"class":289}," pd\n",[35,1028,1029,1031,1034,1036],{"class":37,"line":293},[35,1030,558],{"class":285},[35,1032,1033],{"class":289}," openpyxl ",[35,1035,286],{"class":285},[35,1037,1038],{"class":289}," load_workbook\n",[35,1040,1041,1043,1046,1048,1051,1053],{"class":37,"line":301},[35,1042,558],{"class":285},[35,1044,1045],{"class":289}," openpyxl.drawing.image ",[35,1047,286],{"class":285},[35,1049,1050],{"class":289}," Image ",[35,1052,1023],{"class":285},[35,1054,1055],{"class":289}," XLImage\n",[35,1057,1058,1060,1063,1065],{"class":37,"line":309},[35,1059,558],{"class":285},[35,1061,1062],{"class":289}," openpyxl.styles ",[35,1064,286],{"class":285},[35,1066,1067],{"class":289}," Alignment\n",[35,1069,1070],{"class":37,"line":316},[35,1071,313],{"emptyLinePlaceholder":312},[35,1073,1074,1077,1079,1082,1085],{"class":37,"line":338},[35,1075,1076],{"class":289},"assets ",[35,1078,329],{"class":285},[35,1080,1081],{"class":289}," pd.read_excel(",[35,1083,1084],{"class":45},"\"assets.xlsx\"",[35,1086,380],{"class":289},[35,1088,1089,1092,1094,1097,1099],{"class":37,"line":383},[35,1090,1091],{"class":289},"wb ",[35,1093,329],{"class":285},[35,1095,1096],{"class":289}," load_workbook(",[35,1098,1084],{"class":45},[35,1100,380],{"class":289},[35,1102,1103,1106,1108],{"class":37,"line":389},[35,1104,1105],{"class":289},"ws ",[35,1107,329],{"class":285},[35,1109,1110],{"class":289}," wb.active\n",[35,1112,1113,1116,1119,1122,1125,1128,1130,1133,1135],{"class":37,"line":405},[35,1114,1115],{"class":289},"ws[",[35,1117,1118],{"class":45},"\"D1\"",[35,1120,1121],{"class":289},"], ws[",[35,1123,1124],{"class":45},"\"E1\"",[35,1126,1127],{"class":289},"] ",[35,1129,329],{"class":285},[35,1131,1132],{"class":45}," \"QR\"",[35,1134,361],{"class":289},[35,1136,1137],{"class":45},"\"Barcode\"\n",[35,1139,1140,1142,1145,1147,1150,1153,1156,1159,1162,1164,1166],{"class":37,"line":442},[35,1141,508],{"class":285},[35,1143,1144],{"class":289}," r, asset_id ",[35,1146,514],{"class":285},[35,1148,1149],{"class":332}," enumerate",[35,1151,1152],{"class":289},"(assets[",[35,1154,1155],{"class":45},"\"AssetID\"",[35,1157,1158],{"class":289},"], ",[35,1160,1161],{"class":349},"start",[35,1163,329],{"class":285},[35,1165,377],{"class":332},[35,1167,335],{"class":289},[35,1169,1170,1173,1175,1177,1179,1182,1184,1187,1189,1191],{"class":37,"line":453},[35,1171,1172],{"class":289},"    qbuf, _ ",[35,1174,329],{"class":285},[35,1176,491],{"class":289},[35,1178,814],{"class":285},[35,1180,1181],{"class":45},"\"https:\u002F\u002Fassets.example.com\u002F",[35,1183,656],{"class":820},[35,1185,1186],{"class":289},"asset_id",[35,1188,825],{"class":820},[35,1190,817],{"class":45},[35,1192,380],{"class":289},[35,1194,1195,1197,1199],{"class":37,"line":469},[35,1196,341],{"class":289},[35,1198,329],{"class":285},[35,1200,1201],{"class":289}," XLImage(qbuf)\n",[35,1203,1204,1207,1209,1212,1214],{"class":37,"line":478},[35,1205,1206],{"class":289},"    q.width ",[35,1208,329],{"class":285},[35,1210,1211],{"class":289}," q.height ",[35,1213,329],{"class":285},[35,1215,1216],{"class":332}," 90\n",[35,1218,1219,1222,1224,1227,1229,1232,1234,1236],{"class":37,"line":483},[35,1220,1221],{"class":289},"    ws.add_image(q, ",[35,1223,814],{"class":285},[35,1225,1226],{"class":45},"\"D",[35,1228,656],{"class":820},[35,1230,1231],{"class":289},"r",[35,1233,825],{"class":820},[35,1235,817],{"class":45},[35,1237,380],{"class":289},[35,1239,1240,1243,1245],{"class":37,"line":499},[35,1241,1242],{"class":289},"    bbuf, bimg ",[35,1244,329],{"class":285},[35,1246,1247],{"class":289}," code128_png(asset_id)\n",[35,1249,1251,1254,1256],{"class":37,"line":1250},16,[35,1252,1253],{"class":289},"    b ",[35,1255,329],{"class":285},[35,1257,1258],{"class":289}," XLImage(bbuf)\n",[35,1260,1262,1265,1267,1270,1272,1275,1277,1280,1282,1285,1287],{"class":37,"line":1261},17,[35,1263,1264],{"class":289},"    b.width, b.height ",[35,1266,329],{"class":285},[35,1268,1269],{"class":332}," 260",[35,1271,361],{"class":289},[35,1273,1274],{"class":332},"round",[35,1276,811],{"class":289},[35,1278,1279],{"class":332},"260",[35,1281,800],{"class":285},[35,1283,1284],{"class":289}," bimg.height ",[35,1286,794],{"class":285},[35,1288,1289],{"class":289}," bimg.width)\n",[35,1291,1293,1296,1298,1301,1303,1305,1307,1309],{"class":37,"line":1292},18,[35,1294,1295],{"class":289},"    ws.add_image(b, ",[35,1297,814],{"class":285},[35,1299,1300],{"class":45},"\"E",[35,1302,656],{"class":820},[35,1304,1231],{"class":289},[35,1306,825],{"class":820},[35,1308,817],{"class":45},[35,1310,380],{"class":289},[35,1312,1314,1317,1319],{"class":37,"line":1313},19,[35,1315,1316],{"class":289},"    ws.row_dimensions[r].height ",[35,1318,329],{"class":285},[35,1320,1321],{"class":332}," 72\n",[35,1323,1325,1328,1331,1333,1336,1339],{"class":37,"line":1324},20,[35,1326,1327],{"class":285},"    for",[35,1329,1330],{"class":289}," c ",[35,1332,514],{"class":285},[35,1334,1335],{"class":289}," ws[r][:",[35,1337,1338],{"class":332},"3",[35,1340,1341],{"class":289},"]:\n",[35,1343,1345,1348,1350,1353,1356,1358,1361],{"class":37,"line":1344},21,[35,1346,1347],{"class":289},"        c.alignment ",[35,1349,329],{"class":285},[35,1351,1352],{"class":289}," Alignment(",[35,1354,1355],{"class":349},"vertical",[35,1357,329],{"class":285},[35,1359,1360],{"class":45},"\"center\"",[35,1362,380],{"class":289},[35,1364,1366,1369,1372,1375,1377],{"class":37,"line":1365},22,[35,1367,1368],{"class":289},"ws.column_dimensions[",[35,1370,1371],{"class":45},"\"B\"",[35,1373,1374],{"class":289},"].width ",[35,1376,329],{"class":285},[35,1378,1379],{"class":332}," 18\n",[35,1381,1383,1385,1388,1390,1392],{"class":37,"line":1382},23,[35,1384,1368],{"class":289},[35,1386,1387],{"class":45},"\"D\"",[35,1389,1374],{"class":289},[35,1391,329],{"class":285},[35,1393,1394],{"class":332}," 14\n",[35,1396,1398,1400,1403,1405,1407],{"class":37,"line":1397},24,[35,1399,1368],{"class":289},[35,1401,1402],{"class":45},"\"E\"",[35,1404,1374],{"class":289},[35,1406,329],{"class":285},[35,1408,1409],{"class":332}," 38\n",[35,1411,1413,1416,1419],{"class":37,"line":1412},25,[35,1414,1415],{"class":289},"wb.save(",[35,1417,1418],{"class":45},"\"assets_labels.xlsx\"",[35,1420,380],{"class":289},[35,1422,1424,1426,1428,1431,1434,1437],{"class":37,"line":1423},26,[35,1425,502],{"class":332},[35,1427,811],{"class":289},[35,1429,1430],{"class":332},"len",[35,1432,1433],{"class":289},"(ws._images), ",[35,1435,1436],{"class":45},"\"images placed\"",[35,1438,380],{"class":289},[10,1440,1441,1442,1445,1446,1449,1450,1453,1454,1457,1458,1462],{},"openpyxl's ",[32,1443,1444],{},"Image"," accepts a file-like object, so the ",[32,1447,1448],{},"BytesIO"," buffer goes straight in. ",[32,1451,1452],{},"width"," and ",[32,1455,1456],{},"height"," are the displayed size in pixels at 96 per inch; the barcode keeps its aspect ratio so the bars are scaled evenly. A row height of 72 points (96 pixels) gives the 90-pixel QR code a little space. Each image is anchored at its cell's top-left corner, so sorting or filtering the rows in Excel afterwards does not move the images with them — generate the file in its final order. ",[14,1459,1461],{"href":1460},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fresize-and-position-images-in-excel-with-openpyxl\u002F","Resize and Position Images in Excel with openpyxl"," covers anchoring in more detail.",[20,1464,1466],{"id":1465},"step-4-prove-the-codes-scan-after-rendering","Step 4 — Prove the codes scan after rendering",[10,1468,1469],{},"The codes decoded as standalone images, but what matters is the printed page: Excel scales the images to the displayed size, and printing resamples them again. Render the sheet to PDF, rasterise it at 300 dpi — printer resolution — and decode everything on the page:",[25,1471,1473],{"className":27,"code":1472,"language":29,"meta":30,"style":30},"soffice -env:UserInstallation=file:\u002F\u002F$PWD\u002Flo_profile --headless --convert-to pdf assets_labels.xlsx > \u002Fdev\u002Fnull 2>&1\npdftoppm -png -r 300 -f 1 -l 1 assets_labels.pdf page && ls page*\n",[32,1474,1475,1510],{"__ignoreMap":30},[35,1476,1477,1480,1483,1486,1489,1492,1495,1498,1501,1504,1507],{"class":37,"line":38},[35,1478,1479],{"class":41},"soffice",[35,1481,1482],{"class":332}," -env:UserInstallation=file:\u002F\u002F",[35,1484,1485],{"class":289},"$PWD",[35,1487,1488],{"class":332},"\u002Flo_profile",[35,1490,1491],{"class":332}," --headless",[35,1493,1494],{"class":332}," --convert-to",[35,1496,1497],{"class":45}," pdf",[35,1499,1500],{"class":45}," assets_labels.xlsx",[35,1502,1503],{"class":285}," >",[35,1505,1506],{"class":45}," \u002Fdev\u002Fnull",[35,1508,1509],{"class":285}," 2>&1\n",[35,1511,1512,1515,1518,1521,1524,1527,1530,1533,1535,1538,1541,1544,1547,1549],{"class":37,"line":293},[35,1513,1514],{"class":41},"pdftoppm",[35,1516,1517],{"class":332}," -png",[35,1519,1520],{"class":332}," -r",[35,1522,1523],{"class":332}," 300",[35,1525,1526],{"class":332}," -f",[35,1528,1529],{"class":332}," 1",[35,1531,1532],{"class":332}," -l",[35,1534,1529],{"class":332},[35,1536,1537],{"class":45}," assets_labels.pdf",[35,1539,1540],{"class":45}," page",[35,1542,1543],{"class":289}," && ",[35,1545,1546],{"class":41},"ls",[35,1548,1540],{"class":45},[35,1550,1551],{"class":332},"*\n",[25,1553,1555],{"className":276,"code":1554,"language":278,"meta":30,"style":30},"page = Image.open(\"page-1.png\")\nfound = sorted({(r.format.name, r.text) for r in zxingcpp.read_barcodes(page)})\nqr = {t.rsplit(\"\u002F\", 1)[-1] for f, t in found if f == \"QRCode\"}\nbars = {t for f, t in found if f == \"Code128\"}\nexpected = set(assets[\"AssetID\"])\nprint(f\"QR codes read: {len(qr)}\u002F{len(expected)}, barcodes read: {len(bars)}\u002F{len(expected)}\")\nprint(\"missing:\", sorted(expected - qr), sorted(expected - bars))\n",[32,1556,1557,1572,1594,1647,1676,1693,1750],{"__ignoreMap":30},[35,1558,1559,1562,1564,1567,1570],{"class":37,"line":38},[35,1560,1561],{"class":289},"page ",[35,1563,329],{"class":285},[35,1565,1566],{"class":289}," Image.open(",[35,1568,1569],{"class":45},"\"page-1.png\"",[35,1571,380],{"class":289},[35,1573,1574,1577,1579,1582,1585,1587,1589,1591],{"class":37,"line":293},[35,1575,1576],{"class":289},"found ",[35,1578,329],{"class":285},[35,1580,1581],{"class":332}," sorted",[35,1583,1584],{"class":289},"({(r.format.name, r.text) ",[35,1586,508],{"class":285},[35,1588,511],{"class":289},[35,1590,514],{"class":285},[35,1592,1593],{"class":289}," zxingcpp.read_barcodes(page)})\n",[35,1595,1596,1599,1601,1604,1607,1609,1612,1615,1618,1620,1622,1624,1627,1629,1632,1635,1638,1641,1644],{"class":37,"line":301},[35,1597,1598],{"class":289},"qr ",[35,1600,329],{"class":285},[35,1602,1603],{"class":289}," {t.rsplit(",[35,1605,1606],{"class":45},"\"\u002F\"",[35,1608,361],{"class":289},[35,1610,1611],{"class":332},"1",[35,1613,1614],{"class":289},")[",[35,1616,1617],{"class":285},"-",[35,1619,1611],{"class":332},[35,1621,1127],{"class":289},[35,1623,508],{"class":285},[35,1625,1626],{"class":289}," f, t ",[35,1628,514],{"class":285},[35,1630,1631],{"class":289}," found ",[35,1633,1634],{"class":285},"if",[35,1636,1637],{"class":289}," f ",[35,1639,1640],{"class":285},"==",[35,1642,1643],{"class":45}," \"QRCode\"",[35,1645,1646],{"class":289},"}\n",[35,1648,1649,1652,1654,1657,1659,1661,1663,1665,1667,1669,1671,1674],{"class":37,"line":309},[35,1650,1651],{"class":289},"bars ",[35,1653,329],{"class":285},[35,1655,1656],{"class":289}," {t ",[35,1658,508],{"class":285},[35,1660,1626],{"class":289},[35,1662,514],{"class":285},[35,1664,1631],{"class":289},[35,1666,1634],{"class":285},[35,1668,1637],{"class":289},[35,1670,1640],{"class":285},[35,1672,1673],{"class":45}," \"Code128\"",[35,1675,1646],{"class":289},[35,1677,1678,1681,1683,1686,1688,1690],{"class":37,"line":316},[35,1679,1680],{"class":289},"expected ",[35,1682,329],{"class":285},[35,1684,1685],{"class":332}," set",[35,1687,1152],{"class":289},[35,1689,1155],{"class":45},[35,1691,1692],{"class":289},"])\n",[35,1694,1695,1697,1699,1701,1704,1706,1708,1711,1713,1715,1717,1719,1722,1724,1727,1729,1731,1734,1736,1738,1740,1742,1744,1746,1748],{"class":37,"line":338},[35,1696,502],{"class":332},[35,1698,811],{"class":289},[35,1700,814],{"class":285},[35,1702,1703],{"class":45},"\"QR codes read: ",[35,1705,656],{"class":820},[35,1707,1430],{"class":332},[35,1709,1710],{"class":289},"(qr)",[35,1712,825],{"class":820},[35,1714,794],{"class":45},[35,1716,656],{"class":820},[35,1718,1430],{"class":332},[35,1720,1721],{"class":289},"(expected)",[35,1723,825],{"class":820},[35,1725,1726],{"class":45},", barcodes read: ",[35,1728,656],{"class":820},[35,1730,1430],{"class":332},[35,1732,1733],{"class":289},"(bars)",[35,1735,825],{"class":820},[35,1737,794],{"class":45},[35,1739,656],{"class":820},[35,1741,1430],{"class":332},[35,1743,1721],{"class":289},[35,1745,825],{"class":820},[35,1747,817],{"class":45},[35,1749,380],{"class":289},[35,1751,1752,1754,1756,1759,1761,1764,1767,1769,1772,1774,1776,1778],{"class":37,"line":383},[35,1753,502],{"class":332},[35,1755,811],{"class":289},[35,1757,1758],{"class":45},"\"missing:\"",[35,1760,361],{"class":289},[35,1762,1763],{"class":332},"sorted",[35,1765,1766],{"class":289},"(expected ",[35,1768,1617],{"class":285},[35,1770,1771],{"class":289}," qr), ",[35,1773,1763],{"class":332},[35,1775,1766],{"class":289},[35,1777,1617],{"class":285},[35,1779,1780],{"class":289}," bars))\n",[10,1782,1783],{},"Every QR code and every barcode on the rendered page decodes to the right ID. Running this check in the script that generates the labels turns \"the barcodes looked fine\" into a tested fact, and it catches the real failures — a column made narrower so the barcode is squeezed, a smaller image size that loses the bars' pixel alignment, a changed URL prefix.",[84,1785,93,1791,93,1794,93,1797,93,1802,93,1809,93,1814,93,1821,93,1826,93,1831,93,1834,93,1840,93,1844,93,1848,93,1852,93,1856,93,1860,93,1864,93,1868,93,1872,93,1876,93,1880,93,1882,93,1886,93,1888,93,1891,93,1893,93,1896,93,1898,93,1901,93,1903,93,1906,93,1909,93,1912,93,1914,93,1918,93,1920,93,1923,93,1925,93,1928,93,1930,93,1934,93,1936,93,1939,93,1942,93,1945,93,1947,93,1950,93,1952,93,1955,93,1957,93,1959,93,1961,93,1963,93,1965,93,1968,93,1971,93,1974,93,1976,93,1979,93,1981,93,1984,93,1986,93,1989,93,1991,93,1993,93,1995],{"viewBox":1786,"role":87,"ariaLabelledBy":1787,"xmlns":91,"style":1790},"111 0 538 198",[1788,1789],"qr-sheet-t","qr-sheet-d","width:100%;max-width:538px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[95,1792,1793],{"id":1788},"The labelled asset list",[99,1795,1796],{"id":1789},"A worksheet with asset ID, description and location, followed by a QR code column and a barcode column with one code per row.",[103,1798],{"x":1799,"y":106,"width":1800,"height":1801,"fill":109},"111","538","198",[103,1803],{"x":1804,"y":113,"width":1805,"height":1806,"rx":1807,"fill":117,"stroke":118,"style":1808},"127.4","505.2","30.0","8","stroke-width:1.5px",[103,1810],{"x":1804,"y":1811,"width":1805,"height":1812,"rx":106,"fill":117,"stroke":118,"style":1813},"38.0","8.0","stroke-width:0px",[121,1815,1817],{"transform":1816},"translate(137.4,23.0) scale(0.667)",[125,1818],{"d":1819,"fill":128,"stroke":223,"style":1820},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[132,1822,1825],{"x":1823,"y":906,"style":1824},"159.4","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start","assets_labels.xlsx",[103,1827],{"x":1804,"y":1828,"width":1829,"height":1830,"rx":106,"fill":117,"stroke":118,"style":909},"46.0","32.0","24.0",[103,1832],{"x":1823,"y":1828,"width":1833,"height":1830,"rx":106,"fill":117,"stroke":118,"style":909},"86.7",[132,1835,1839],{"x":1836,"y":1837,"style":1838},"202.7","63.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[103,1841],{"x":1842,"y":1828,"width":1843,"height":1830,"rx":106,"fill":117,"stroke":118,"style":909},"246.1","120.3",[132,1845,1847],{"x":1846,"y":1837,"style":1838},"306.2","B",[103,1849],{"x":1850,"y":1828,"width":1851,"height":1830,"rx":106,"fill":117,"stroke":118,"style":909},"366.4","79.7",[132,1853,1855],{"x":1854,"y":1837,"style":1838},"406.2","C",[103,1857],{"x":1858,"y":1828,"width":1859,"height":1830,"rx":106,"fill":117,"stroke":118,"style":909},"446.1","44.0",[132,1861,1863],{"x":1862,"y":1837,"style":1838},"468.1","D",[103,1865],{"x":1866,"y":1828,"width":1867,"height":1830,"rx":106,"fill":117,"stroke":118,"style":909},"490.1","142.5",[132,1869,1871],{"x":1870,"y":1837,"style":1838},"561.3","E",[103,1873],{"x":1804,"y":1874,"width":1829,"height":1875,"rx":106,"fill":117,"stroke":118,"style":909},"70.0","28.0",[132,1877,1611],{"x":1878,"y":1879,"style":1838},"143.4","89.0",[103,1881],{"x":1823,"y":1874,"width":1833,"height":1875,"rx":106,"fill":157,"stroke":148,"style":1808},[132,1883,1885],{"x":1836,"y":1879,"style":1884},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","AssetID",[103,1887],{"x":1842,"y":1874,"width":1843,"height":1875,"rx":106,"fill":157,"stroke":148,"style":1808},[132,1889,1890],{"x":1846,"y":1879,"style":1884},"Description",[103,1892],{"x":1850,"y":1874,"width":1851,"height":1875,"rx":106,"fill":157,"stroke":148,"style":1808},[132,1894,1895],{"x":1854,"y":1879,"style":1884},"Location",[103,1897],{"x":1858,"y":1874,"width":1859,"height":1875,"rx":106,"fill":157,"stroke":148,"style":1808},[132,1899,1900],{"x":1862,"y":1879,"style":1884},"QR",[103,1902],{"x":1866,"y":1874,"width":1867,"height":1875,"rx":106,"fill":157,"stroke":148,"style":1808},[132,1904,1905],{"x":1870,"y":1879,"style":1884},"Barcode",[103,1907],{"x":1804,"y":1908,"width":1829,"height":1875,"rx":106,"fill":117,"stroke":118,"style":909},"98.0",[132,1910,377],{"x":1878,"y":1911,"style":1838},"117.0",[103,1913],{"x":1823,"y":1908,"width":1833,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1915,1917],{"x":1836,"y":1911,"style":1916},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","AS-10442",[103,1919],{"x":1842,"y":1908,"width":1843,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1921,1922],{"x":1846,"y":1911,"style":1916},"Laptop, 14in",[103,1924],{"x":1850,"y":1908,"width":1851,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1926,1927],{"x":1854,"y":1911,"style":1916},"Leeds 2F",[103,1929],{"x":1858,"y":1908,"width":1859,"height":1875,"rx":106,"fill":117,"stroke":118,"style":1808},[132,1931,1933],{"x":1862,"y":1911,"style":1932},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","▣",[103,1935],{"x":1866,"y":1908,"width":1867,"height":1875,"rx":106,"fill":117,"stroke":118,"style":1808},[132,1937,1938],{"x":1870,"y":1911,"style":1932},"║▌║║▌║▌ AS-10442",[103,1940],{"x":1804,"y":1941,"width":1829,"height":1875,"rx":106,"fill":117,"stroke":118,"style":909},"126.0",[132,1943,1338],{"x":1878,"y":1944,"style":1838},"145.0",[103,1946],{"x":1823,"y":1941,"width":1833,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1948,1949],{"x":1836,"y":1944,"style":1916},"AS-10443",[103,1951],{"x":1842,"y":1941,"width":1843,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1953,1954],{"x":1846,"y":1944,"style":1916},"Monitor, 27in",[103,1956],{"x":1850,"y":1941,"width":1851,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1958,1927],{"x":1854,"y":1944,"style":1916},[103,1960],{"x":1858,"y":1941,"width":1859,"height":1875,"rx":106,"fill":117,"stroke":118,"style":1808},[132,1962,1933],{"x":1862,"y":1944,"style":1932},[103,1964],{"x":1866,"y":1941,"width":1867,"height":1875,"rx":106,"fill":117,"stroke":118,"style":1808},[132,1966,1967],{"x":1870,"y":1944,"style":1932},"║▌║▌║║▌ AS-10443",[103,1969],{"x":1804,"y":1970,"width":1829,"height":1875,"rx":106,"fill":117,"stroke":118,"style":909},"154.0",[132,1972,105],{"x":1878,"y":1973,"style":1838},"173.0",[103,1975],{"x":1823,"y":1970,"width":1833,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1977,1978],{"x":1836,"y":1973,"style":1916},"AS-10457",[103,1980],{"x":1842,"y":1970,"width":1843,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1982,1983],{"x":1846,"y":1973,"style":1916},"Docking station",[103,1985],{"x":1850,"y":1970,"width":1851,"height":1875,"rx":106,"fill":109,"stroke":118,"style":909},[132,1987,1988],{"x":1854,"y":1973,"style":1916},"Leeds 3F",[103,1990],{"x":1858,"y":1970,"width":1859,"height":1875,"rx":106,"fill":117,"stroke":118,"style":1808},[132,1992,1933],{"x":1862,"y":1973,"style":1932},[103,1994],{"x":1866,"y":1970,"width":1867,"height":1875,"rx":106,"fill":117,"stroke":118,"style":1808},[132,1996,1997],{"x":1870,"y":1973,"style":1932},"║║▌║▌║▌ AS-10457",[20,1999,2001],{"id":2000},"performance-notes","Performance notes",[10,2003,2004,2005,2007,2008,2010],{},"Generating a QR code and a barcode takes a few milliseconds each, and openpyxl stores each image once in the workbook's media folder, so a sheet of a thousand assets is a few seconds' work and a few megabytes of file. The decode check is the slow step — reading a 300 dpi page takes a noticeable fraction of a second — so for long lists render and check a sample of pages, or check every page in a nightly run rather than on every generation. If the file grows too large to email, lower the QR ",[32,2006,364],{}," or the barcode ",[32,2009,768],{}," together with the display size, keeping the bars on whole pixels.",[20,2012,2014],{"id":2013},"common-pitfalls","Common pitfalls",[2016,2017,2018,2026,2032,2038,2044],"ul",{},[2019,2020,2021,2025],"li",{},[2022,2023,2024],"strong",{},"Fractional bar widths."," Bars 1.57 pixels wide did not decode at all; choose module widths that are a whole number of pixels at the output resolution.",[2019,2027,2028,2031],{},[2022,2029,2030],{},"Trimming the quiet zone."," The blank margins are part of the barcode; on a printed label they keep neighbouring lines and text from being read as bars.",[2019,2033,2034,2037],{},[2022,2035,2036],{},"Squashing the image."," Scale barcodes with their aspect ratio, never width alone.",[2019,2039,2040,2043],{},[2022,2041,2042],{},"Sorting after inserting."," Images are anchored to positions, not to rows' data; sort before generating.",[2019,2045,2046,2049],{},[2022,2047,2048],{},"Trusting that it decodes."," Check the decoded text, not just that something was found.",[20,2051,2053],{"id":2052},"conclusion","Conclusion",[10,2055,2056,2057,2059,2060,2062,2063,2065],{},"Generate each code in memory — ",[32,2058,69],{}," with error correction M and a whole-pixel module size, ",[32,2061,73],{},"'s Code 128 with whole-pixel bars and the default quiet zone — and anchor it in its row with openpyxl's ",[32,2064,1444],{},", scaled with its aspect ratio into a row tall enough to hold it. Then render the sheet at printer resolution and decode every code, comparing the text with the expected IDs, so the labels are known to scan before anyone prints them.",[20,2067,2069],{"id":2068},"frequently-asked-questions","Frequently asked questions",[10,2071,2072,2075],{},[2022,2073,2074],{},"How do I add a QR code to an Excel cell with Python?","\nGenerate the QR code with the qrcode library, save it to a BytesIO buffer as PNG, create an openpyxl Image from the buffer, set its width and height, and add it with ws.add_image(img, \"D2\"). Make the row tall enough to hold it.",[10,2077,2078,2081],{},[2022,2079,2080],{},"How do I generate a barcode in Excel with Python?","\nUse python-barcode with the ImageWriter to create a Code 128 PNG in memory and insert it with openpyxl. Barcode fonts are an alternative, but they depend on the font being installed and on check characters being calculated correctly.",[10,2083,2084,2087],{},[2022,2085,2086],{},"Why won't my barcode scan?","\nThe usual causes are bar widths that are not a whole number of pixels, bars squeezed by scaling the image unevenly, or a trimmed quiet zone. Choose module widths that match the output resolution, scale with the aspect ratio and keep the default quiet zone.",[10,2089,2090,2093],{},[2022,2091,2092],{},"How can I test that the codes are readable?","\nRender the workbook to PDF, rasterise it at 300 dpi with pdftoppm, and decode the image with zxing-cpp. Compare the decoded text with the expected IDs.",[20,2095,2097],{"id":2096},"related","Related",[2016,2099,2100,2106,2113,2120],{},[2019,2101,2102,2103,2105],{},"Up one level: ",[14,2104,17],{"href":16}," — pictures in and out of workbooks.",[2019,2107,2108,2112],{},[14,2109,2111],{"href":2110},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Finsert-an-image-from-memory-into-excel\u002F","Insert an Image from Memory into Excel"," — the BytesIO technique in detail.",[2019,2114,2115,2119],{},[14,2116,2118],{"href":2117},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fbuild-a-product-catalog-with-images-in-excel\u002F","Build a Product Catalog with Images in Excel"," — one picture per row at larger scale.",[2019,2121,2122,2126],{},[14,2123,2125],{"href":2124},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-one-pdf-per-excel-row\u002F","Generate One PDF per Excel Row"," — turning rows into printable labels.",[2128,2129,2130],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":30,"searchDepth":293,"depth":293,"links":2132},[2133,2134,2135,2136,2137,2138,2139,2140,2141,2142],{"id":22,"depth":293,"text":23},{"id":269,"depth":293,"text":270},{"id":532,"depth":293,"text":533},{"id":1004,"depth":293,"text":1005},{"id":1465,"depth":293,"text":1466},{"id":2000,"depth":293,"text":2001},{"id":2013,"depth":293,"text":2014},{"id":2052,"depth":293,"text":2053},{"id":2068,"depth":293,"text":2069},{"id":2096,"depth":293,"text":2097},"2026-09-29","Put a scannable QR code and a Code 128 barcode next to every row of an asset or stock list: generate the images in memory with qrcode and python-barcode, anchor them in cells with openpyxl, size rows and columns to fit, and prove every code still scans after printing by decoding the rendered PDF.","md",[2147,2149,2151,2153],{"q":2074,"a":2148},"Generate the QR code with the qrcode library, save it to a BytesIO buffer as PNG, create an openpyxl Image from the buffer, set its width and height, and add it with ws.add_image(img, \"D2\"). Make the row tall enough to hold it.",{"q":2080,"a":2150},"Use python-barcode with the ImageWriter to create a Code 128 PNG in memory and insert it with openpyxl. Barcode fonts are an alternative, but they depend on the font being installed and on check characters being calculated correctly.",{"q":2086,"a":2152},"The usual causes are bar widths that are not a whole number of pixels, bars squeezed by scaling the image unevenly, or a trimmed quiet zone. Choose module widths that match the output resolution, scale with the aspect ratio and keep the default quiet zone.",{"q":2092,"a":2154},"Render the workbook to PDF, rasterise it at 300 dpi with pdftoppm, and decode the image with zxing-cpp. Compare the decoded text with the expected IDs.","generate qr codes and barcodes in excel with python",null,{"breadcrumb":2158},[2159,2161,2164],{"name":2160,"item":794},"Home",{"name":2162,"item":2163},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":17,"item":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python",{"title":2167,"description":2168},"Add QR Codes and Barcodes to Excel Rows with Python","Generate QR codes (qrcode) and Code 128 barcodes (python-barcode) for each Excel row, insert them with openpyxl Image from BytesIO, fit row heights, keep quiet zones and whole-pixel bars, and verify by decoding a 300 dpi render with zxing-cpp.","generate-qr-codes-and-barcodes-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fgenerate-qr-codes-and-barcodes-in-excel-with-python\u002Findex",[2172,2173],"charts-and-images","report-layouts","how-to","fJel0j1kbBUAleScxqWByJ21-3eoi-QqEa4Obl7c-GY",[2177,2181],{"title":2178,"path":2179,"stem":2180,"children":-1},"Extract Images from an Excel File with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fextract-images-from-an-excel-file-with-python","formatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fextract-images-from-an-excel-file-with-python\u002Findex",{"title":2182,"path":2183,"stem":2184,"children":-1},"Insert an Image from Memory into Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Finsert-an-image-from-memory-into-excel","formatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Finsert-an-image-from-memory-into-excel\u002Findex",[2186,2188],{"path":2187,"title":2162},"\u002Fformatting-and-charting-excel-reports-with-python",{"path":2189,"title":17},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel",[],[2192,2195,2198,2201,2204,2207],{"path":2193,"title":2194,"kind":2174},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-dashboard-sheet-with-charts-from-multiple-tabs","Build a Dashboard Sheet with Charts from Multiple Tabs",{"path":2196,"title":2197,"kind":2174},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-rolling-12-month-chart-on-an-excel-dashboard","Build a Rolling 12-Month Chart on an Excel Dashboard",{"path":2199,"title":2200,"kind":2174},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fkeep-charts-and-images-when-filling-an-excel-template","Keep Charts and Images When Filling an Excel Template",{"path":2202,"title":2203,"kind":2174},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter","Add a Chart to an Excel File with xlsxwriter",{"path":2205,"title":2206,"kind":2174},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-sparklines-to-an-excel-report-with-xlsxwriter","Add Sparklines to an Excel Report with xlsxwriter",{"path":2208,"title":2209,"kind":2174},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fbuild-a-waterfall-chart-in-excel-with-python","Build a Waterfall Chart in Excel with Python",1790972116765]