news 2026/9/16 17:29:59

InvenTree Label Sheet 插件实战:单张标签纸网格排版 PDF 打印指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
InvenTree Label Sheet 插件实战:单张标签纸网格排版 PDF 打印指南

InvenTree Label Sheet 插件实战:单张标签纸网格排版 PDF 打印指南

【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree

本篇指南围绕 InvenTree 内置的InvenTree Label Sheet PluginInvenTreeLabelSheet)展开,讲解如何将多枚标签按规则网格自动排列到单张标准标签纸上并生成 PDF。读完本文,你将掌握该插件的启用机制、五个打印选项(页面大小、跳过标签、边框、横向、页边距)的实际含义与取值约束、DEBUG 模式的使用场景,以及从标签模板到网格排版再到 PDF 渲染的完整实现链路。

插件概览:什么是 Label Sheet 插件

InvenTree Label Sheet Plugin是 InvenTree 提供的内置标签打印插件,核心功能是"把多枚标签合并到一张更大的纸张上,按规则网格(regular grid)排布,最终输出一个 PDF 文件"。这一能力在以下场景中非常实用:

  • 使用 A4、Letter 等标准整页标签纸(如预切 21×10 或 24×8 的空白标签纸)时,把一批零件、库存项(StockItem)的标签一次性铺满整张纸;
  • 需要按 3 列 × 8 行这类固定网格打印标签时,避免"一枚标签一页"造成的纸张浪费;
  • 需要跳过部分标签位置(例如标签纸首行已被手工占用)时进行偏移打印。

该插件通过 LabelPrintingMixin 标签打印混入类 提供自定义打印支持,是 InvenTree 标签打印插件体系中"合并多标签到单页"这一典型用法的内置实现。

从源码结构看,插件由 label_sheet.py 实现,位于plugin/builtin/labels/目录下,与默认 PDF 标签插件 inventree_label.py(InvenTreeLabel)、机器标签插件 inventree_machine.py 并列。

启用机制:强制启用的内置插件

Label Sheet 插件是一个强制(mandatory)插件,始终处于启用状态,无需手动安装或激活。在源码中这一特性体现为:

  • 类声明:class InvenTreeLabelSheetPlugin(LabelPrintingMixin, SettingsMixin, InvenTreePlugin)
  • 插件元数据:NAME = 'InvenTreeLabelSheet'TITLE = 'InvenTree Label Sheet Printer'DESCRIPTION = 'Arrays multiple labels onto a single sheet'VERSION = '1.0.1'
  • 关键行为标记:BLOCKING_PRINT = True,表示打印任务会同步阻塞前端服务器直至完成(不会卸载到后台 worker),保证 PDF 立即可下载。

因此你不需要任何安装步骤,只要 InvenTree 正常运行,该插件就会出现在可选的标签打印插件列表中。

插件设置:DEBUG 调试模式

插件通过SettingsMixin暴露了一个插件级设置项DEBUG

SETTINGS = { 'DEBUG': { 'name': _('Debug mode'), 'description': _('Enable debug mode - returns raw HTML instead of PDF'), 'validator': bool, 'default': False, } }
设置键名称默认值说明
DEBUGDebug modeFalse开启后插件返回原始 HTML(labels.html)而非 PDF

该模式仅供开发和测试使用,不应在生产环境中启用

  • 开启后,打印任务返回的是未经 PDF 渲染的原始 HTML 文档(见下节源码中str2bool(self.get_setting('DEBUG'))分支);
  • 由于跳过 WeasyPrint 渲染,可能无法生成有效的 PDF 文件,不能用于实际标签打印;
  • 典型用途:排查标签模板的渲染错误,例如检查模板变量未解析、CSS 未生效等模板层问题。

在 InvenTree 后台的插件设置界面中,该项显示为开关形式的 "Debug mode" 字段,其描述文字为 "Enable debug mode - returns raw HTML instead of PDF"。

使用方式:在打印对话框中选用插件

打印标签时,从插件列表中选择InvenTreeLabelSheet选项,插件便会将所选对象的标签排版到单张纸上,并生成包含全部标签的 PDF 文件供下载。

打印对话框为本次打印任务提供了一组额外的自定义选项,由插件内部的LabelPrintingOptionsSerializer定义。

五个打印选项详解

选项字段类型默认值取值范围/约束说明
page_sizeChoiceFieldA4A4/A3/Legal/Letter标签页的纸张规格
skipIntegerField0最小 0,最大 500打印标签页时跳过的标签数量(用于位置偏移)
borderBooleanFieldFalseTrue/False是否为每枚标签打印 1px 黑色边框(便于裁剪定位)
landscapeBooleanFieldFalseTrue/False是否以横向模式打印标签页(宽高互换)
marginIntegerField10最小 0(单位 mm)页面四周的页边距,单位为毫米

各选项在打印对话框中的含义与界面截图一致:顶部为模板(Template)与打印插件(Printing Plugin)选择;下方依次是Page Size(页面大小,默认 A4)Skip Labels(跳过标签,默认 0)Border(边框,默认关闭)Landscape(横向,默认关闭),底部为 Cancel / Print 按钮。

page_size的可选值来自 report.helpers.report_page_size_options(),与报表系统共用同一套页面规格定义;其实际尺寸(单位 mm)由 report.helpers.page_sizes() 给出:

页面代码尺寸(mm)
A4210 × 297
A3297 × 420
Legal215.9 × 355.6
Letter215.9 × 279.4

若传入未知的页面代码,page_size()会记录警告并默认回退到 A4(见 report/helpers.py)。

打印选项在任务中的传递

这些选项在print_labels方法中通过kwargs['printing_options']读取(序列化器被赋给类属性PrintingOptionsSerializer = LabelPrintingOptionsSerializer,由 LabelPrintingMixin 机制注入打印界面并随任务下发):

printing_options = kwargs['printing_options'] page_size_code = printing_options.get('page_size', 'A4') landscape = printing_options.get('landscape', False) border = printing_options.get('border', False) skip = int(printing_options.get('skip', 0)) margin = printing_options.get('margin', 10)

实现原理:从网格计算到 PDF 渲染

InvenTreeLabelSheetPlugin对 LabelPrintingMixin 的入口方法print_labels进行了整体重写(源码注释明确说明 "we override the entire print_labels method"),以完成网格排版。整个过程可分为四个阶段。

阶段一:计算可用空间与网格行列数

# 获取页面尺寸(mm) page_size = report.helpers.page_size(page_size_code) page_width, page_height = page_size # 横向模式交换宽高 if landscape: page_width, page_height = page_height, page_width # 扣除页边距后的可用空间 available_width = page_width - (2 * margin) available_height = page_height - (2 * margin) # 按标签实际尺寸计算行列数(向下取整) n_cols = math.floor(available_width / label.width) n_rows = math.floor(available_height / label.height) n_cells = n_cols * n_rows if n_cells == 0: raise ValidationError(_('Label is too large for page size'))

要点:

  • 网格行列数完全由"标签模板的width/height(mm)"与"扣除边距后的页面尺寸"决定,因此不必手动配置每页多少枚标签
  • 当标签尺寸大于页面可用空间时(n_cells == 0),插件会抛出ValidationError,提示 "Label is too large for page size";
  • 从源码结构可以推断,调整margin或切换landscape会直接改变可用空间,进而影响单页标签数量。

阶段二:跳过标签与分页切片

# 在标签列表前插入 skip 个空占位 items = [None] * skip + list(items) n_labels = len(items) # 文档级数据 document_data = { 'border': border, 'landscape': landscape, 'page_width': page_width, 'page_height': page_height, 'label_width': label.width, 'label_height': label.height, 'n_labels': n_labels, 'n_pages': math.ceil(n_labels / n_cells), 'n_cols': n_cols, 'n_rows': n_rows, 'margin': margin, }

随后按每页n_cells枚进行切片并逐页渲染,同时更新打印进度:

while idx < n_labels: if page := self.print_page(label, items[idx : idx + n_cells], request, **document_data): pages.append(page) idx += n_cells output.progress += 1 output.save()

若最终没有任何页面生成(例如所有标签渲染失败),则抛出ValidationError('No labels were generated')

阶段三:单页网格渲染

print_page方法把一页内的标签渲染成一张 HTML 表格(class='label-sheet-table'):

  • n_rows×n_cols双重循环生成<tr>/<td>单元格;
  • 每个单元格的 CSS 类带有行号与列号(label-sheet-row-{row}label-sheet-col-{col}),用于后续精确绝对定位;
  • 被跳过的标签(items[idx] is None)渲染为空单元格label-sheet-cell-skip),不打印任何内容,从而实现偏移打印;
  • 正常标签通过label.render_as_string(items[idx], request, insert_page_style=False)渲染为 HTML 片段——注意这里禁用了模板的@page样式,避免与整页的页面样式冲突;
  • 单枚标签渲染抛出的异常会被捕获并记录日志,同时该单元格渲染为红色错误块(label-sheet-cell-errorbackground-color: #F00),便于在 DEBUG 阶段定位问题模板。

阶段四:整页 CSS 定位与 PDF 生成

wrap_pages方法将各页 HTML 拼接为单一文档,并在<head>中注入整页样式:

  • 每个单元格的绝对定位偏移按"行号 × 标签高度"、"列号 × 标签宽度"逐行逐列生成(单位 mm):
    • .label-sheet-row-{row} { top: {row * label_height}mm; }
    • .label-sheet-col-{col} { left: {col * label_width}mm; }
  • @page规则按page_width×page_height(mm)设定纸张大小,margin按选项设定;
  • 表格page-break-after: always实现每页强制分页,border-spacing: 0mmpadding: 0mm保证网格对齐;
  • 开启border选项时,单元格获得1px solid #000边框(关闭时0mm无边框)。

最后按 DEBUG 设置分派输出:

if str2bool(self.get_setting('DEBUG')): generated_file = ContentFile(html_data, 'labels.html') # 原始 HTML else: html = weasyprint.HTML(string=html_data, url_fetcher=InvenTreeURLFetcher()) document = html.render().write_pdf() generated_file = ContentFile(document, 'labels.pdf') # 渲染 PDF output.mark_complete(progress=n_labels, output=generated_file)

正常模式下,HTML 通过WeasyPrint渲染为 PDF(使用InvenTreeURLFetcher作为 URL 抓取器,支持标签模板中引用的资源),产物以labels.pdf命名保存并标记任务完成。

与默认标签插件的对比

为了更清晰地理解 Label Sheet 插件的定位,这里将其与内置的默认 PDF 标签插件InvenTreeLabel进行对比:

维度InvenTreeLabel(默认)InvenTreeLabelSheet(本文主题)
实现文件inventree_label.pylabel_sheet.py
输出形态每枚标签渲染后,通过PdfWriter拼接为纵向串联的单页序列PDF多枚标签按网格排列在单张纸上,生成整页 PDF
实现方式沿用混入类默认的print_label逐枚渲染流程,before_printing/get_generated_file拼接结果整体重写print_labels,自行计算网格、分页与定位
打印选项仅 DEBUG 设置,无额外打印选项DEBUG 设置 + 五个打印选项(页面大小/跳过/边框/横向/页边距)
适用场景单标签独立成页、标签尺寸较大标准整页标签纸批量铺排

两者都实现LabelPrintingMixinSettingsMixin,均提供 DEBUG 原始 HTML 输出模式,且都设置BLOCKING_PRINT = True同步返回打印结果。

总结

InvenTree Label Sheet 插件以"整页网格排版"的方式扩展了默认标签打印能力:用户只需选择模板与插件、按需调整五个打印选项,即可在 A4/Letter 等标准纸上批量打印标签。其核心价值在于免手动配置网格——行列数完全由标签尺寸、页面尺寸与页边距自动推导,同时通过skip选项解决标签纸偏移问题,通过 DEBUG 模式辅助模板排错。如需深入了解插件混入类的通用机制(如自定义打印选项序列化器、print_labelprint_labels的分工、BLOCKING_PRINT的异步打印行为),可继续阅读 标签打印混入类文档,或直接阅读 label_sheet.py 源码。

【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 17:29:48

北京屈光参差就诊指南 这些权威医生可以参考

屈光参差指双眼屈光度差异超过250度的特殊屈光状态&#xff0c;它带来的不只是"两只眼睛度数不一样"&#xff0c;更会直接破坏双眼影像融合机制&#xff0c;导致视物重影、深度感丧失、3D视觉无法建立&#xff0c;长期还可能诱发单眼抑制和弱视。普通眼镜店常规验光往…

作者头像 李华
网站建设 2026/9/16 17:27:54

LLM系统提示词泄露风险与六层防御实战指南

1. 这不是“泄露”&#xff0c;而是模型训练与部署中被长期忽视的提示词暴露风险最近在几个技术社区里&#xff0c;频繁看到有人发帖问&#xff1a;“为什么我微调后的模型一上线&#xff0c;别人就能猜出我的system prompt&#xff1f;”、“API返回里怎么带出了内部指令&…

作者头像 李华
网站建设 2026/9/16 17:26:17

用Vue构建心理咨询系统:路由、状态管理与部署实战

简介&#xff1a;这是一份基于Vue的大学生心理咨询系统毕业设计项目&#xff0c;面向高校软件技术、计算机等专业学生&#xff0c;适用于毕业设计、课程设计或前端综合实训。压缩包共一百六十九个文件&#xff0c;以四十二个Vue页面组件和七十一个JavaScript逻辑文件为主&#…

作者头像 李华