Python中用Barcode库生成条码后添加商品名称的字体适配问题
解决Python Barcode库生成条码后添加商品名称的字体/尺寸问题
我用Python的Barcode库生成条码,核心代码如下:
barcodes = CompanyBarcodes.objects.filter(asn_id__asn_number=asn_id, company=company, product__asset_category__in=cat_ids ).prefetch_related('company', 'product', 'grn_id', 'asn_id', 'itemGroup') items = [] for i in barcodes: items.append({'bar': i.barcode, 'name': str(i.product.name) + " - " + str(i.product.sku)}) alist = [] for item in items: print("item", item) EAN = barcode.get_barcode_class('code128') d = {'module_width': 0.15, 'module_height': 2.0, 'quiet_zone': 0.5, 'text_distance': 1.5, 'font_size': 3, 'text_line_distance': 3, 'dpi': 900} w = ImageWriter(d) ean = EAN(item['bar'], writer=ImageWriter()) a = ean.save(item['bar'], options=d)
现在需要把items里的商品名称(name字段)添加到生成的条码图片中,我尝试了以下代码,但添加的文字尺寸过小,且和条码自带的字体不一致:
with open(a, 'rb') as image: img = Image.open(image) # Create a drawing context on the image draw = ImageDraw.Draw(img) # Use the same font as the original barcode image font = ImageFont.load_default() # Set the font size font_size = 10 # You can adjust the font size as needed # Calculate the position to center the text text = item['name'] text_width, text_height = draw.textsize(text, font=font) image_width, image_height = img.size text_x = (image_width - text_width) / 2 text_y = image_height - text_height - 5 # Adjust the vertical position as needed # Draw the text on the image draw.text((text_x, text_y), text, fill=(0, 0, 0), font=font) # You can adjust the fill color # Save the modified image img.save(a) # Overwrite the original image with the changes encoded_image = base64.b64encode(open(a, 'rb').read()).decode('utf-8') alist.append({'image': encoded_image, **item})
解决方案
1. 匹配条码使用的字体
Barcode库默认使用FreeMono等宽字体(和条码下方数字的字体一致),不要用ImageFont.load_default()加载默认字体,直接指定同类型等宽字体即可。如果系统没有FreeMono,可替换为Courier New等系统自带的等宽字体。
2. 按DPI换算字体大小
因为生成条码时设置了dpi=900,字体大小需要按这个DPI换算(条码的font_size=3对应900DPI下的3*900/72≈38号字体),避免文字过小。
修改后的完整代码
将添加文字的代码替换为以下内容:
from PIL import Image, ImageDraw, ImageFont # 获取条码生成时的DPI配置 dpi = d['dpi'] # 按DPI换算字体大小,和条码文字尺寸匹配 font_size = int(d['font_size'] * dpi / 72) # 加载和条码一致的等宽字体 try: font = ImageFont.truetype("FreeMono.ttf", font_size) except IOError: # 兜底用Courier New等宽字体 font = ImageFont.truetype("Courier New", font_size) except: # 极端情况用默认字体,但按DPI放大 font = ImageFont.load_default(size=font_size) with open(a, 'rb') as image: img = Image.open(image) draw = ImageDraw.Draw(img) text = item['name'] # 计算文字位置:水平居中,垂直在条码下方预留足够间距 text_width, text_height = draw.textsize(text, font=font) image_width, image_height = img.size text_x = (image_width - text_width) // 2 # 按DPI换算偏移量,避免在不同分辨率下位置偏移 text_y = image_height - text_height - int(10 * dpi / 72) # 绘制文字,颜色和条码文字保持一致(黑色) draw.text((text_x, text_y), text, fill=(0, 0, 0), font=font) # 保存时指定DPI,避免图片缩放变形 img.save(a, dpi=(dpi, dpi)) encoded_image = base64.b64encode(open(a, 'rb').read()).decode('utf-8') alist.append({'image': encoded_image, **item})
额外优化点
- 生成条码时复用已定义的
ImageWriter实例,避免参数不一致:ean = EAN(item['bar'], writer=w) # 用之前定义的w,而非重新创建ImageWriter() a = ean.save(item['bar'], options=d) - 若商品名称过长,可添加自动换行逻辑:
# 按图片宽度自动拆分文字 def wrap_text(text, font, max_width): lines = [] current_line = "" for word in text.split(): test_line = current_line + word + " " test_width, _ = draw.textsize(test_line, font=font) if test_width <= max_width: current_line = test_line else: lines.append(current_line.strip()) current_line = word + " " lines.append(current_line.strip()) return lines # 应用换行逻辑,左右预留边距 wrapped_lines = wrap_text(text, font, image_width - int(20 * dpi / 72)) total_text_height = text_height * len(wrapped_lines) # 调整垂直位置,让所有换行文字居中 text_y = image_height - total_text_height - int(10 * dpi / 72) # 逐行绘制文字 for line in wrapped_lines: line_width, _ = draw.textsize(line, font=font) line_x = (image_width - line_width) // 2 draw.text((line_x, text_y), line, fill=(0,0,0), font=font) text_y += text_height
内容的提问来源于stack exchange,提问作者Rahul Sharma
相关产品推荐
相关产品推荐

