不依赖第三方库手动创建含表格与图片的PDF发票技术问询
手动构建含文本、表格、图片的PDF发票(原始对象实现)
核心思路
PDF的所有内容都是通过内容流(Content Stream) + 对应资源对象(字体、图片等)实现的:表格本质是画线条+单元格文本定位,图片是嵌入XObject资源后调用绘制。
1. 表格实现(原始对象版)
表格不需要特殊对象,用路径指令画边框,再把文本定位到单元格内即可。以下是简化的发票表格对象示例:
表格资源与内容流
先定义基础字体资源,再在内容流里完成线条绘制和文本写入:
% 字体资源对象(对象2) 2 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj % 页面内容流对象(对象3) 3 0 obj << /Length 400 >> stream % 设置字体和字号 BT /Helvetica 12 Tf % 画表格外框:左上角(50,700),右下角(550,500) 50 700 m 550 700 l 550 500 l 50 500 l 50 700 l S % 画列分隔线:3列,每列宽度166 216 700 m 216 500 l S 382 700 m 382 500 l S % 画行分隔线:3行,每行高度66 50 634 m 550 634 l S 50 568 m 550 568 l S % 写入表头文本 55 685 Td (商品名称) Tj 221 685 Td (数量) Tj 387 685 Td (金额) Tj % 写入第一行数据 55 619 Td (笔记本电脑) Tj 221 619 Td (1) Tj 387 619 Td (5999) Tj ET endstream endobj
关键逻辑说明
m= 移动画笔到目标坐标,l= 从当前点画直线到目标点,S= 描边路径(画出线条)BT/ET包裹文本操作块,Td移动文本光标到指定坐标,Tj输出文本内容- PDF坐标以页面左下角为原点,单位为PDF点(1点=1/72英寸)
2. 图片嵌入与绘制(原始对象版)
图片需要定义为XObject资源,再在内容流里调用绘制。以下是嵌入200x100尺寸RGB图片的示例:
图片对象与内容流
% 图片XObject对象(对象4) 4 0 obj << /Type /XObject /Subtype /Image /Width 200 /Height 100 /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode % 用Flate压缩,测试时可直接去掉用原始RGB数据 /Length 120000 % 需匹配实际压缩后的数据长度 >> stream % 此处放入压缩后的RGB图像二进制数据:每个像素3字节(R,G,B),共200*100*3=60000字节,压缩后长度需对应上面的/Length值 [实际压缩图像数据] endstream endobj % 在页面内容流(对象3的stream)中添加图片绘制指令 q % 保存当前画布状态 200 0 0 100 50 750 cm % 设置缩放与位置:宽200、高100,左下角坐标(50,750) /Img1 Do % 调用图片XObject资源 Q % 恢复画布状态
关键逻辑说明
- 需要在页面字典的
/Resources中添加图片引用:/XObject << /Img1 4 0 R >> cm是矩阵变换指令,格式为[X轴缩放 X倾斜 Y倾斜 Y轴缩放 X偏移 Y偏移],用于控制图片大小与位置- 测试小图时可去掉
/Filter /FlateDecode,直接放入原始60000字节RGB数据,更直观
3. 完整发票PDF结构框架
将所有对象组合,完整PDF文件结构如下(简化版):
%PDF-1.7 1 0 obj << /Type /Catalog /Pages 5 0 R >> endobj % 字体资源对象(对象2) 2 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj % 页面内容流对象(对象3) 3 0 obj << /Length 800 >> stream % 绘制发票logo图片 q 200 0 0 100 50 750 cm /Img1 Do Q % 写入发票标题 BT /Helvetica 16 Tf 300 720 Td (XX公司发票) Tj ET % 绘制表格与内容(前面表格的内容流代码) BT /Helvetica 12 Tf 50 700 m 550 700 l 550 500 l 50 500 l 50 700 l S % ... 剩余表格线条与文本指令 ET endstream endobj % 图片XObject对象(对象4) 4 0 obj << /Type /XObject /Subtype /Image /Width 200 /Height 100 /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode /Length [实际数据长度] >> stream [压缩图像数据] endstream endobj % 页面集合字典(对象5) 5 0 obj << /Type /Pages /Kids [6 0 R] /Count 1 >> endobj % 页面对象(对象6) 6 0 obj << /Type /Page /Parent 5 0 R /MediaBox [0 0 612 792] % A4页面尺寸 /Resources << /Font << /F1 2 0 R >> /XObject << /Img1 4 0 R >> >> /Contents 3 0 R >> endobj % 交叉引用表 xref 0 7 0000000000 65535 f 0000000009 00000 n 0000000079 00000 n 0000000173 00000 n 0000000500 00000 n 0000000800 00000 n 0000000900 00000 n % Trailer trailer << /Size 7 /Root 1 0 R >> startxref 1000 %%EOF
注意事项
- 所有对象编号需与交叉引用表一一对应,交叉引用中的字节偏移量需准确(可保存后用文本编辑器查看字节数调整)
- 文本坐标需自行计算,确保在单元格内对齐
- 若用FlateDecode压缩图片,可借助
gzip工具压缩原始RGB数据后再放入stream
内容的提问来源于stack exchange,提问作者Sandhya Yegireddi
相关产品推荐
相关产品推荐

