xPdf API 接口规范
版本: Unified API / V2 Engine 更新: 2026-03-10
说明: 本文描述当前正式公开 schema。唯一 canonical 路由为
POST /api/v1/label,prod/test仅通过环境与 token 区分。
1. 接口概览
| 路由 | 鉴权方式 | 用途 |
|---|---|---|
POST /api/v1/label | Authorization: Bearer <token> | 唯一公开请求入口 |
Content-Type:application/json- 响应:
application/pdf(二进制流) - 默认输出模式:
binary - 当
settings.output.mode = "file"时,响应会带Content-Disposition
2. 请求结构
{
"settings": {
"defaults": {
"text": {
"font_family": "NotoSans-Regular",
"font_size": 11,
"color": "#111111"
},
"stroke": {
"color": "#000000",
"width": 0.4
},
"fill": {
"color": "#FFFFFF",
"opacity": 1.0
},
"shape": {
"corner_radius": 0
}
},
"metadata": {
"title": "xPdf Document",
"author": "xPdf"
},
"output": {
"mode": "file",
"file_name": "invoice-20260310.pdf"
},
"profile": "pdfa-2b"
},
"header": {
"height": 12,
"elements": []
},
"footer": {
"height": 10,
"elements": []
},
"pages": [
{
"size": "label_100_150",
"elements": []
}
]
}3. 顶层对象
3.1 DocumentRequest
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
settings | Settings | 否 | 全局设置 |
pages | Page[] | 是 | 页面数组 |
header | Section | 否 | 全局页眉 |
footer | Section | 否 | 全局页脚 |
3.1.1 Page 尺寸
每个页面必须显式指定尺寸,支持两种写法,二选一:
sizewidth+height
规则:
size和width/height不能同时出现- 不写
size时,必须同时提供width与height size不区分大小写
支持的 size 预设:
a4=210 x 297 mma6=105 x 148 mmletter=215.9 x 279.4 mmlegal=215.9 x 355.6 mmlabel_100_100=100 x 100 mmlabel_100_150=100 x 150 mmlabel_4_6_in=101.6 x 152.4 mm
示例:
{
"pages": [
{ "size": "A4", "elements": [] },
{ "width": 100, "height": 150, "elements": [] }
]
}3.1.2 Page Margin / Content Box
可选配置:
settings.page_marginpages[].margin
示例:
{
"settings": {
"page_margin": { "top": 10, "right": 12, "bottom": 10, "left": 12 }
},
"pages": [
{
"size": "letter",
"margin": { "top": 8, "right": 10, "bottom": 12, "left": 10 },
"elements": []
}
]
}规则:
- 未配置
page_margin时,正文元素维持当前页面绝对坐标语义。 - 一旦配置
page_margin,pages[].elements的x/y改为相对content box左上角。 pages[].elements超出content box会直接校验报错,不做自动裁剪或纠偏。header/footer仍按页面坐标工作,不受page_margin约束。- 正文自动分页到后续页时,会从下一页
content box顶部继续排版。
3.2 Section(header / footer)
header 和 footer 共用同一个结构:
{
"height": 12,
"elements": [
{ "type": "text", "x": 5, "y": 8, "content": "Page Header" }
]
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
height | number | 是 | 区域高度,单位 mm |
elements | Element[] | 否 | 该区域内渲染的元素列表 |
语义说明:
header是全局页眉,会应用到每一页。footer是全局页脚,会应用到每一页。elements为空时,该区域本身仍然存在;不要依赖"空数组自动忽略区域"。
坐标与定位:
header.elements按页面绝对坐标渲染,通常约定写在y = 0 .. header.height区间内。footer.elements会自动整体下移到页面底部,偏移量为page.height - footer.height。 也就是说,footer内部元素通常写成相对页脚区域的坐标,例如y = 0 .. footer.height。
与正文的关系:
header不会自动把正文元素整体下移。正文元素仍按页面绝对坐标渲染。- 因此如果你使用页眉,建议正文元素自己避开页眉区域,例如从
y >= header.height开始排布。 footer.height会参与正文可用高度计算,正文分页/溢出时会避开页脚底部区域。- 配置
page_margin后,正文自动分页到后续页会从下一页content box顶部开始。
实践建议:
header.height/footer.height应与内部元素实际占用高度大体一致,不要随意写过大。- 如果只是普通固定区域装饰,推荐把页眉内容放在
header,页脚内容放在footer,不要手工复制到每页。 - 如果需要每页不同的顶部/底部内容,仍应放在
pages[].elements中单独控制,而不是使用全局header/footer。
3.3 Settings
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
defaults | Defaults | 否 | 全局默认样式 |
metadata | Metadata | 否 | PDF 元数据 |
output | OutputSettings | 否 | 响应输出模式与文件名 |
profile | string | 否 | PDF/A 配置 |
page_margin | PageMargin | 否 | 正文内容区页边距 |
profile 支持: pdfa-1b, pdfa-2b, pdfa-3b, pdfa-4, pdfa-2u, pdfa-3u, pdfa-ua1 等。
3.3.1 OutputSettings
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mode | binary | file | 否 | 输出方式,默认 binary |
file_name | string | 否 | 自定义文件名,仅 mode = "file" 时生效 |
规则:
binary或不传:只返回 PDF 二进制。file:返回Content-Disposition,用于按文件下载。file_name不传:回退到默认文件名gPdf-MMDDHHmmssSSS.pdf。file_name会经过安全清洗,并自动补.pdf后缀。
3.4 Defaults
| 字段 | 类型 | 说明 |
|---|---|---|
text | TextStyle | 文本默认样式 |
stroke | StrokeStyle | 线条/描边默认样式 |
fill | FillStyle | 填充默认样式 |
shape | ShapeDefaults | 形状默认样式 |
ShapeDefaults:
| 字段 | 类型 | 说明 |
|---|---|---|
corner_radius | number | 矩形默认圆角半径(mm) |
说明:
settings.defaults只接受结构化写法:text / stroke / fill / shape
3.5 Metadata
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 标题 |
author | string | 作者 |
subject | string | 主题 |
creator | string | 创建工具 |
producer | string | 生成器 |
language | string | 语言代码 |
4. 元素类型
elements[] 中每项都必须带 type:
textbarcodelinerectcircleellipsepolygonlinkimagetablestack
通用字段:
- 大多数元素支持
z_index(默认0) - 大多数元素支持
comment(仅备注,不渲染) - 支持旋转的元素:
text,barcode,rect,ellipse,image(0/90/180/270) - 超链接支持两种模式:
- 元素挂载
link字段(text/barcode/line/rect/circle/ellipse/polygon/image) - 独立热点元素
type: "link"
- 元素挂载
4.1 x_anchor(横向锚定定位)
x_anchor 用于按参考边界自动计算元素的最终 x。
当前支持的元素:
textbarcoderectimagelink
不支持:
line/circle/ellipse/polygon/table/stack/block
规则:
x与x_anchor互斥,同时传入会直接报错- 不使用
x_anchor时,仍按原有x绝对定位 text使用x_anchor时,style.width必填barcode / rect / image / link使用x_anchor时,使用元素自身widthtable_left / table_right只允许在stack -> block内使用
参考值:
page_leftpage_rightcontent_leftcontent_righttable_lefttable_right
计算规则:
- 左侧参考:
resolved_x = reference + offset - 右侧参考:
resolved_x = reference - offset - width
示例:
{
"type": "text",
"x_anchor": { "reference": "content_right", "offset": 8 },
"y": 12,
"content": "$1,235.85",
"style": {
"width": 24,
"text_align": "right"
}
}5. 样式对象
5.1 StrokeStyle
{
"color": "#111111",
"width": 0.5,
"opacity": 1.0,
"cap": "butt",
"join": "miter",
"miter_limit": 10,
"dash": {
"preset": "dashed",
"pattern": [3, 2],
"phase": 0
},
"compound": {
"kind": "double",
"gap": 0.3
}
}字段说明:
cap:butt/round/squarejoin:miter/round/beveldash.preset:solid/dashed/dotted/customdash.pattern仅在preset=custom时建议提供compound.kind: 当前仅支持doublecompound.gap: 双线两根线之间的净距(mm)
说明:
- 未设置
compound时按单线处理。 compound供line、table.grid、table.cell.borders统一复用。double + dash为非法组合。double + marker为非法组合。rect/circle/ellipse/polygon若传入compound会报不支持。
5.2 FillStyle
{
"color": "#F5F5F5",
"opacity": 1.0,
"rule": "nonzero"
}rule: nonzero / even_odd
5.3 MarkerStyle (Line)
{
"start": "none",
"end": "arrow",
"size": 2.5
}start/end: none / arrow / open_arrow / circle / bar
5.4 LinkSpec (超链接)
{
"target": { "type": "url", "url": "https://example.com" },
"alt": "open official site",
"padding": 1.0,
"border": { "color": "#1A202C", "width": 0.3 }
}LinkTarget:
- URL:
{ "type": "url", "url": "https://..." } - 页内跳转:
{ "type": "page", "page": 2, "x": 10, "y": 20 }
LinkBorderStyle:
color: hex 颜色width: 线宽(mm),0视为不绘制
约束:
- URL 仅支持
http://、https://、mailto:、tel: - URL 前后空白会被裁剪(trim)后再写入
page从1开始,且不能超过请求中显式pages数padding必须为有限数且>= 0border.width必须为有限数且>= 0border.color(若提供)必须是合法 hex 颜色- 任一
link字段不合法会直接返回 ValidationError(不会静默跳过)
6. Shape 元素
line/rect/circle/ellipse/polygon 均可选挂载 link: LinkSpec。
6.1 Line
{
"type": "line",
"x1": 4,
"y1": 99,
"x2": 96,
"y2": 99,
"stroke": {
"color": "#000000",
"width": 0.4,
"dash": { "preset": "solid" }
},
"link": {
"target": { "type": "url", "url": "https://example.com/spec" }
}
}stroke 省略时会走默认链路(见第 9 节)。
6.2 Rect
{
"type": "rect",
"x_anchor": { "reference": "content_right", "offset": 6 },
"y": 20,
"width": 60,
"height": 20,
"fill": { "color": "#FFFFFF" },
"stroke": { "color": "#222222", "width": 0.6 },
"corner_radius": 2
}6.3 Circle
{
"type": "circle",
"cx": 40,
"cy": 40,
"r": 12,
"fill": { "color": "#E6F4FF" },
"stroke": { "color": "#2B6CB0", "width": 0.5 }
}6.4 Ellipse
{
"type": "ellipse",
"cx": 70,
"cy": 40,
"rx": 16,
"ry": 10,
"rotation": 0,
"fill": { "color": "#FFF7E6" },
"stroke": { "color": "#C05621", "width": 0.5 }
}6.5 Polygon
{
"type": "polygon",
"points": [
{ "x": 20, "y": 80 },
{ "x": 35, "y": 60 },
{ "x": 50, "y": 80 },
{ "x": 40, "y": 95 }
],
"fill": { "color": "#F0FFF4" },
"stroke": { "color": "#2F855A", "width": 0.5 }
}6.6 Link(独立热点)
{
"type": "link",
"x_anchor": { "reference": "content_left", "offset": 10 },
"y": 10,
"width": 40,
"height": 8,
"target": { "type": "url", "url": "https://example.com" },
"alt": "Open website"
}7. 其他元素
7.1 Text
必填字段: y, content,并且必须二选一提供:
xx_anchor
style 复用 TextStyle:
font_family,font_size,font_weight,colortext_align,vertical_align,line_heightwidth,height,text_overflowshrink_to_fit,min_font_size- 可选
link: LinkSpec
支持变量: {page}, {total_pages}。
7.2 Barcode
必填字段: y, format, content, width, height,并且必须二选一提供:
xx_anchor
可选: options, hrt, rotation, z_index, comment, link
7.3 Image
必填字段: y, width, height, data,并且必须二选一提供:
xx_anchor
可选: format, rotation, z_index, comment, link
data 支持:
- 文件名(从资源层读取)
- Base64 数据
7.4 Table
说明:
- 当前公开
tableschema 仅支持本节定义的结构。
顶层结构:
{
"type": "table",
"x": 12,
"y": 24,
"width": 180,
"columns": [],
"rows": [],
"cell": {},
"header": {},
"row_header": {},
"body": {},
"grid": {},
"pagination": {}
}顶层字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
x | number | 是 | 左上角 X(mm) |
y | number | 是 | 左上角 Y(mm) |
z_index | integer | 否 | 层级 |
comment | string | 否 | 备注 |
width | number | 否 | 表格总宽度 |
columns | TableColumn[] | 是 | 列定义 |
rows | TableRow[] | 是 | 行数据 |
cell | TableCellStyle | 否 | 全表默认单元格样式 |
header | TableHeaderConfig | 否 | 列表头配置 |
row_header | TableZoneConfig | 否 | 行表头区配置 |
body | TableBodyConfig | 否 | 正文区配置 |
grid | TableGridConfig | 否 | 网格线配置 |
pagination | TablePaginationConfig | 否 | 分页配置 |
7.4.1 Column
{
"key": "amount",
"header": "Amount",
"width": { "mode": "fixed", "value": 30 },
"role": "data",
"cell": {
"text": { "text_align": "right" }
},
"header_cell": {
"text": { "font_weight": "bold" }
}
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
key | string | 是 | 列 key,必须唯一 |
header | string | 否 | 叶子列表头文本,默认空字符串 |
width | TableColumnWidth | 是 | 列宽模型 |
role | string | 否 | data / row_header,默认 data |
cell | TableCellStyle | 否 | 该列正文单元格样式 |
header_cell | TableCellStyle | 否 | 该列列表头单元格样式 |
规则:
columns[].key必须唯一。role = "row_header"的列必须连续放在最左侧。- 支持多个行表头列。
TableColumnWidth:
{ "mode": "fixed", "value": 30 }
{ "mode": "percent", "value": 25 }
{ "mode": "auto" }7.4.2 Row / Cell
rows 为对象数组,key 对应 columns[].key。
简写单元格:
{ "name": "Apple", "qty": 2, "enabled": true, "note": null }复杂单元格:
{
"group": {
"value": "Fruit",
"row_span": 2,
"col_span": 1,
"style": {
"text": { "font_weight": "bold" }
},
"link": {
"target": { "type": "url", "url": "https://example.com" }
}
}
}复杂单元格字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
value | string | number | boolean | null | 否 | 单元格值 |
row_span | integer | 否 | 向下合并行数 |
col_span | integer | 否 | 向右合并列数 |
style | TableCellStyle | 否 | 单元格样式覆盖 |
link | LinkSpec | 否 | 单元格超链接 |
规则:
row_span >= 1col_span >= 1- span 不能越界,也不能和其他 span 重叠
null渲染为空字符串boolean渲染为"true"/"false"link仅能出现在复杂单元格对象中
7.4.3 TableCellStyle
{
"padding": { "x": 1, "y": 1 },
"text": { "font_size": 9, "color": "#111111" },
"fill": { "color": "#FFFFFF" },
"content_offset_x": 1.5,
"content_offset_y": 0.5,
"borders": {
"top": false,
"right": { "color": "#111111", "width": 0.2 },
"bottom": { "color": "#111111", "width": 0.2 },
"left": false
}
}| 字段 | 类型 | 说明 |
|---|---|---|
padding | TablePadding | 单元格内边距 |
text | TextStyle | 文本样式 |
fill | FillStyle | 填充样式 |
content_offset_x | number | 单元格内容横向微调(mm) |
content_offset_y | number | 单元格内容纵向微调(mm) |
borders | TableBorders | 单元格单边边框 |
TablePadding:
| 字段 | 类型 | 说明 |
|---|---|---|
x | number | 水平内边距(mm) |
y | number | 垂直内边距(mm) |
TableBorders:
top?: false | StrokeStyleright?: false | StrokeStylebottom?: false | StrokeStyleleft?: false | StrokeStyle
7.4.4 Header / Row Header / Body
header:
{
"show": true,
"repeat_on_page_break": true,
"rows": [
{
"cells": [
{ "content": "Product", "col_span": 2 },
{ "content": "Stock", "row_span": 2 }
]
}
],
"cell": {
"fill": { "color": "#F3F4F6" },
"text": { "font_weight": "bold" }
}
}字段:
show?: boolean,默认truerepeat_on_page_break?: boolean,默认truerows?: TableHeaderRow[],分组表头行;叶子表头仍来自columns[].headercell?: TableCellStyle
row_header:
{
"cell": {
"fill": { "color": "#F8F8F8" },
"text": { "font_weight": "bold" }
}
}字段:
cell?: TableCellStyle
body:
{
"cell": {},
"alternate_fill": { "color": "#FAFAFA" }
}字段:
cell?: TableCellStylealternate_fill?: FillStyle
表达规则:
- 分组表头使用
header.rows - 叶子列表头使用
columns[].header - 行表头使用
columns[].role = "row_header"
7.4.5 Grid
{
"frame": {
"color": "#111111",
"width": 0.3,
"compound": { "kind": "double", "gap": 0.3 }
},
"horizontal": { "color": "#D1D5DB", "width": 0.2 },
"vertical": false
}字段:
frame?: false | StrokeStylehorizontal?: false | StrokeStylevertical?: false | StrokeStyle
说明:
grid.frame、grid.horizontal、grid.vertical统一使用StrokeStyle
7.4.6 Pagination
{
"keep_spans_together": true,
"row_min_height": 10,
"header_min_height": 12
}字段:
keep_spans_together?: boolean,默认truerow_min_height?: numberheader_min_height?: number
说明:
- 当存在
row_span时,要求keep_spans_together = true
7.4.7 宽度与样式规则
宽度规则:
columns.length >= 1columns[].width统一使用fixed / percent / auto- 若提供
table.width:fixed先按 mm 占用percent按table.width百分比分配auto吃剩余空间,并基于表头/正文内容测量分配- 若不存在
auto,列宽求解结果必须精确填满table.width
- 若未提供
table.width:- 所有列都必须使用
fixed
- 所有列都必须使用
percent总和不得超过100rows中出现未在columns[].key定义的字段会报错header.show = false时,header.rows、columns[].header与header_cell视为忽略- 当使用
row_span时,不支持keep_spans_together = false
样式优先级:
settings.defaultstable.cellheader.cell / row_header.cell / body.cellcolumns[].cell / columns[].header_cellcell.style
边框优先级:
gridtable.cell.borders- 区域级
cell.borders - 列级
cell/header_cell.borders cell.style.borders
7.5 Stack / Block
用途:
stack用于"表格结束后再跟随一组内容"的发票/对账单场景。- 它不接管 table 的原有定位;
table.x/y/width仍按当前语义工作。 stack只解决table与后续内容块之间的顺序排版和分页问题。
结构:
{
"type": "stack",
"gap": 6,
"children": [
{
"type": "table",
"x": 18,
"y": 123,
"width": 180,
"columns": [],
"rows": []
},
{
"type": "block",
"elements": [
{ "type": "text", "x": 128, "y": 0, "content": "Subtotal" },
{ "type": "text", "x": 168, "y": 0, "content": "$1,343.65" },
{ "type": "line", "x1": 128, "y1": 7, "x2": 178, "y2": 7 }
]
}
]
}顶层字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
gap | number | 否 | 前后 child 之间的垂直间距(mm),默认 0 |
children | StackChild[] | 是 | 顺序排版的子项 |
规则:
stack仅允许出现在pages[].elementsstack.children[0]必须是tablestack.children[1..]只能是blockchildren.length >= 2
block:
{
"type": "block",
"elements": [
{ "type": "text", "x": 128, "y": 0, "content": "Subtotal" },
{ "type": "text", "x": 168, "y": 0, "content": "$1,343.65" }
]
}规则:
block不提供自己的x/y/width/heightblock.elements[].x继续使用现有 body 元素语义block.elements[].y为相对block起点的偏移block内不允许再嵌套table/stack/blockblock高度由系统根据内部元素自动测量
分页语义:
table先按现有逻辑分页- 只有在
table真正结束后,后续block才开始排版 - 若某个
block当前页放不下,则整块移到下一页 - 若某个
block自身高度已超过一页可用高度,则直接报错 gap表示前一个 child 最终结束位置到下一个 child 起始位置的垂直距离- 当
block被整体移到下一页时,不保留上一页的gap
8. 坐标与单位
- 坐标单位: mm
- 原点: 左上角
(0, 0) - X 轴向右, Y 轴向下
9. 默认值优先级
9.1 样式优先级
- 元素本身字段(例如
line.stroke.width) settings.defaults对应字段(如defaults.stroke.width)- 系统默认配置
table 的优先级为:
settings.defaultstable.cellheader.cell / row_header.cell / body.cellcolumns[].cell / columns[].header_cellcell.style
9.2 线条/形状默认行为
line.stroke全省略时:color默认#000000width默认0.4
rect/circle/ellipse/polygon.stroke全省略时:color默认#000000width默认1.0
fill全省略时默认不填充(透明)rect.corner_radius默认链路:element.corner_radiussettings.defaults.shape.corner_radius- 系统默认值
0
10. 错误码
| 错误码 | 触发条件 |
|---|---|
| API-001 | JSON 非法 |
| API-002 | 请求校验失败 |
| API-101 | Authorization 缺失或格式错误 |
| API-102 | 鉴权失败 |
| API-500 | 系统内部错误 |
| API-501 | PDF 渲染失败 |
常见 ValidationError 触发:
link非法(不支持的 URL scheme、页码越界、非法padding/border)table非法(未知列 key、table.width无法为未声明列分配正宽度、非法 span)profile非法x与x_anchor同时出现