马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
本帖最后由 pbai 于 2026-8-28 10:42 编辑
PBIDEA:用 uo_pdfwriter/uo_pdfview/uo_pdfmaker 生成与预览 PDF
阅读说明
1. 适用版本:PB 12.5 及以上(含 PBIDEA 1.x);以下 API 名称以 PBIDEA 运行库 PbIdea.dll 的导出为准
2. 支持数据库:本文不涉及数据库,无需任何 DBMS(纯文件/报表生成)
3. 操作系统与环境要求:Windows 7+,已安装 PBIDEA 运行库(PbIdea.dll),且 uo_pdfwriter/uo_pdfview/uo_pdfmaker 所在 pbl 已加入工程
4. 难度系数:★★★☆☆
5. 其它阅读说明:示例运行前须确保 PbIdea.dll 已注册(构造 uo_pdfwriter 时会自动调用 pdfwCreate);预览示例需在窗口上放置 uo_pdfview 控件;若用不可视方式调用 uo_pdfview,用后务必 Release()
一、三个对象各管一摊:先分清"谁来生成、谁来预览"
PBIDEA 在 PbIdea.dll 里封装了一组 PDF 相关对象,日常最常用的是三个,职责非常清晰:
- uo_pdfwriter:从零"画"出一份 PDF。你控制坐标、字体、颜色、线条、矩形、图片,逐页绘制,最终落盘成文件或内存 blob。适合生成固定版式的单据、标签、证书、报表封面等。
- uo_pdfmaker:把已有的 DataWindow / DataStore 通过"虚拟打印"直接导出成 PDF(也能导出 bmp/jpg/png,由扩展名决定)。适合"我已经有报表 DW,不想重画"的场景。
- uo_pdfview:一个窗口预览控件(继承自 userobject),负责"看" PDF——翻页、缩放、打印、另存;它也顺带提供合并(MeargePDF)、拆分(SplitPDF)、提取文本(GetPageText)等"加工"能力,但不负责从零生成。
一句话记忆:writer 负责画,maker 负责把 DW 转,view 负责看和加工。
二、uo_pdfwriter:从零绘制 PDF 的全貌
uo_pdfwriter 继承自 nonvisualobject,构造时自动调用 pdfwCreate()、析构时自动调用 pdfwDestroy(),所以你 create 之后直接开画即可,无需手写初始化/释放底层。
2.1 打开文档:数据去哪由 Open 决定
Open 有多个重载,关键点在于是否带文件名:
| 写法 | 数据去向 | | Open(int widthMM, int heightMM) | 不写文件,生成的内容留在 data 成员(blob),最后用 Save(文件名) 落盘 | | Open(string filename, int widthMM, int heightMM) | 直接指定输出文件,最后 close() 即写出该文件 | | Open(datawindow dw) / Open(datastore ds) | 直接把某个 DW/DS 的当前内容渲染进 PDF | | 以上四种均另有带 uo_json metadata 的重载 | metadata 可写入 PDF 元信息(标题/作者等),配合 DrawWaterMark() 出水印 |
尺寸单位是毫米,A4 约为 210×297。坐标 (0,0) 在页面左上角,向右为 x 正、向下为 y 正——和屏幕坐标一致,比"纸张坐标系"好理解。
如果只想改单位(像素/英寸),可先调 SetUnit(int unit),常量:PDF_PB=0、PDF_PEXEL=1、PDF_INCH=2、PDF_MM=3(默认)、PDF_POINT=4。
2.2 页面与坐标控制
- BeginPage() / EndPage():开始/结束一页,可多次调用生成多页。
- SetMargin(left, top, right, bottom):设置页边距。
- SetOffset(x, y)、Translate(x, y)、Rotate(angle):平移与旋转,做复杂排版时用。
- SaveStatus() / RestoreStatus():保存/恢复当前绘制状态(颜色、字体、坐标变换),类似画笔的"快照"。
2.3 文本绘制
SetFont(fontName, fontSize) 或 SetFont(fontName, fontSize, bold, italic) 设定字体;SetColor(ulong clr)(clr 用 PB 的 RGB(r,g,b) 生成)或 SetColor(clr, alpha) 设定颜色。然后 DrawString 有 6 个重载,常用的是:
- DrawString(text):在当前位置写文本;
- DrawString(x, y, text):在指定坐标写文本;
- DrawString(x, y, width, height, text, align):在指定矩形框内写文本,align 用对齐常量(见 2.5)。
2.4 图形与图片
- Rectangle(left, top, width, height, stroke, fill):画矩形,stroke=是否描边、fill=是否填充;
- LineTo(x1, y1, x2, y2, stroke):画线;
- Stroke() / Fill() / FillAndStrok():对未立即描边/填充的图形后续补操作;
- DrawPicture(x, y, width, height, imageFile) 或 DrawPicture(x, y, width, height, imageData):插图,支持 bmp/jpeg/png,可传文件名也可传图片 blob;另有带 alpha(透明度 0-255)和 angle(旋转 0-360)的重载。
2.5 常用常量(类型变量)
- 单位:PDF_MM=3 等(见 2.1);
- 文本对齐:ALIGN_LEFT=0、ALIGN_CENTER=1、ALIGN_RIGHT=2、ALIGN_TOP=0、ALIGN_VCENTER=4、ALIGN_BOTTOM=8;
- 线型:LINE_SOLID=0、LINE_DASH=1、LINE_DOT=2、LINE_DASHDOT=3、LINE_DASHDOTDOT=4。
三、典型用法示例(最小可运行)
示例 1:用 uo_pdfwriter 生成一份带标题、线条和图片的 PDF
前置:新建窗口 w_pdf_demo,放一个按钮 cb_make。工程已加入含 uo_pdfwriter 的 pbl,且 PbIdea.dll 已注册。运行后会在 C:\temp 下生成 demo.pdf。 - // 示例输入:输出 PDF 文件名(含路径)
- string ls_file
- ls_file = 'C:\temp\demo.pdf'
- // 示例输入:要插入的图片(支持 bmp/jpeg/png),不存在则跳过图片
- string ls_img
- ls_img = 'C:\temp\logo.png'
- // 示例输入:页面尺寸(毫米),A4 约 210 x 297
- int li_w, li_h
- li_w = 210
- li_h = 297
- uo_pdfwriter lw
- lw = create uo_pdfwriter
- // 打开文档:直接带文件名 + 尺寸(毫米),Close 时自动落盘
- lw.Open(ls_file, li_w, li_h)
- // 新页:坐标 (0,0) 在左上角,单位默认毫米
- lw.BeginPage()
- // 标题:宋体 16 号,黑色
- lw.SetFont('宋体', 16)
- lw.SetColor(RGB(0, 0, 0))
- lw.DrawString(20, 20, 'PowerBuilder 生成的 PDF 示例')
- // 正文:12 号字
- lw.SetFont('宋体', 12)
- lw.DrawString(20, 40, '这是用 uo_pdfwriter 从零绘制的一段正文。')
- // 画一条横线(stroke=true 立即描边)
- lw.LineTo(20, 60, 190, 60, true)
- // 画一个蓝色填充矩形(stroke=false 不描边,fill=true 填充)
- lw.SetColor(RGB(0, 0, 255))
- lw.Rectangle(20, 70, 60, 8, false, true)
- // 插入图片(文件存在才画)
- if FileExists(ls_img) then
- lw.DrawPicture(20, 90, 80, 40, ls_img)
- end if
- lw.EndPage()
- // 关闭并落盘(Open 已带文件名,Close 直接写出)
- lw.close()
- MessageBox('完成', 'PDF 已生成:' + ls_file)
复制代码
示例 2:把 DataWindow 直接导出为 PDF(uo_pdfmaker)
前置:窗口上已放置 DataWindow 控件 dw_1,DataObject 设为报表 d_report,且已 Retrieve 取数。 - // 前置:窗口上已放置 DataWindow 控件 dw_1(DataObject = d_report),且已 Retrieve 取数
- // 示例输入:输出 PDF 文件名
- string ls_pdf
- ls_pdf = 'C:\temp\report.pdf'
- uo_pdfmaker lm
- lm = create uo_pdfmaker
- // 虚拟打印 DW 内容为 PDF;扩展名 pdf 决定输出类型(也可 bmp/jpg/png)
- boolean lb_ok
- lb_ok = lm.PrintDatawindow(dw_1, ls_pdf)
- if lb_ok then
- MessageBox('成功', '报表已导出:' + ls_pdf)
- else
- MessageBox('失败', '导出失败,请检查 PbIdea.dll 与虚拟打印机配置')
- end if
复制代码
PrintDatawindow 还有不带文件名的版本 PrintDatawindow(dw) 直接返回 blob,方便上传或入库,不必落盘。
示例 3:在窗口里预览 PDF(uo_pdfview 控件)
前置:窗口 w_pdfview 上放置 uo_pdfview 控件 uo_pdfview_1(从 userobject 拖入即可)。 - // 前置:窗口 w_pdfview 上已放置 uo_pdfview 控件 uo_pdfview_1
- // 示例输入:要预览的 PDF 文件路径
- string ls_file
- ls_file = 'C:\temp\demo.pdf'
- boolean lb_ok
- lb_ok = uo_pdfview_1.Load(ls_file)
- if lb_ok then
- MessageBox('提示', 'PDF 已加载,可滚动/翻页/缩放浏览')
- else
- MessageBox('失败', '加载失败,文件可能不存在或已损坏')
- end if
复制代码
预览控件常用事件:onready(integer pagecount) 在加载完成时触发,可在里面显示总页数;onpagechanged(integer page) 在翻页时触发;onclose() 在关闭时触发;oncommand(long id) 返回 false 走工具栏默认动作、返回 true 则由你自己接管对应按钮。
示例 4:合并多个 PDF(注意函数名是 MeargePDF)
前置:准备若干 PDF 文件,准备把它们的内容按顺序合并成一个新文件。 - // 示例输入:待合并的 PDF 文件数组
- string ls_files[]
- ls_files[1] = 'C:\temp\a.pdf'
- ls_files[2] = 'C:\temp\b.pdf'
- ls_files[3] = 'C:\temp\c.pdf'
- // 示例输入:合并后的输出文件
- string ls_out
- ls_out = 'C:\temp\merged.pdf'
- uo_pdfview lv
- lv = create uo_pdfview
- // 注意:源码里函数名拼写为 MeargePDF(不是 MergePDF),调用必须照写
- boolean lb_ok
- lb_ok = lv.MeargePDF(ls_files, ls_out)
- // 不可视方式使用时必须 Release(),否则会内存泄漏
- lv.Release()
- if lb_ok then
- MessageBox('完成', '已合并为:' + ls_out)
- else
- MessageBox('失败', '合并失败')
- end if
复制代码
四、边界情况与常见坑(务必留意)
- MeargePDF 不是 MergePDF:源码导出名就是 MeargePDF(注意是 Merge 的 M-e 之后多写了一个 a,g、e 数量都一致),调用时照搬,别自作主张改成"正确"的 Merge,否则编译找不到函数。
- Open 带不带文件名,决定数据去向:
- Open(file, w, h) → 最后 close() 直接写出该文件;
- Open(w, h) 不带文件名 → 内容留在 lw.data(blob 成员),必须再调 lw.Save(文件名) 才能落盘,close() 不会写文件。两者不要混用。
- 坐标与单位:默认单位是毫米,(0,0) 在左上角。需要像素/英寸先 SetUnit。绘制前务必 BeginPage,否则内容不进任何页。
- 中文与字体:SetFont 用系统已安装的字体名,例如 '宋体' 或 'SimSun'。字体名写错、或目标机器没装该字体,中文会不显示或变方块。
- uo_pdfview 两种用法:放在窗口上做预览,生命周期随窗口自动管理;用 create uo_pdfview 以不可视方式做合并/拆分/提取文本时,用后必须 Release(),否则内存泄漏。
- 图片格式:DrawPicture 支持 bmp/jpeg/png。传 blob 时要传图片的原始字节(可用 uo_file.BlobFromFile 之类读入),不是文件路径字符串。
- 依赖 PbIdea.dll:uo_pdfwriter 一构造就 pdfwCreate(),若 dll 未注册、不在 PATH、或位数不匹配(32/64 位 PB 与 dll 要一致),构造即失败。部署机器必须随包带上该 dll。
- 判返回值:Open/Save/PrintDatawindow/MeargePDF 等都返回 boolean,生成前养成判返回值的习惯,失败优先结合 PbIdea.dll 状态排查,而不是盲目重试。
五、与相近方案对比
| 方案 | 适用场景 | 优点 | 代价 | | uo_pdfwriter 手工绘制 | 固定版式单据/标签/封面 | 体积小、像素级可控、不依赖已有 DW | 版式要自己算坐标,复杂报表难写 | | uo_pdfmaker DW 虚拟打印 | 已有报表 DW 想导出 | 零重画,直接复用现有报表 | 样式受 DW 限制,难做 DW 之外的自由排版 | | uo_pdfview 浏览/加工 | 看 PDF、合并/拆分/取文本 | 控件即开即用,加工 API 全 | 只"看和改",不能从零生成 | | 第三方(GhostScript/商业库) | 需要表单域、加密、数字签名等高级特性 | 能力最全 | 需额外安装/授权 |
实际项目常组合使用:用 maker 把报表 DW 导出,用 view 合并多份并加水印,用 writer 补一张定制封面,最后再合并成一个成品 PDF。
六、进阶扩展点
- 水印:uo_pdfmaker.SetWaterMaker(text, color, angle[, alpha]) 给导出的 PDF 加文字水印;uo_pdfwriter 则在 Open 时传入带水印信息的 uo_json metadata,再调 DrawWaterMark() 绘制。
- PDF 元信息:uo_pdfwriter.Open(文件名, 宽, 高, metadata) 的 metadata 可写标题/作者,最终体现在 PDF 属性里。
- 提取文本做发票识别:uo_pdfview.GetPageText(pageIndex, uo_json result, offsetX, offsetY) 按字间距/行间距把散文本聚合成句,适合做结构化抽取;GetPageText(page, ref sText[], ref strPosition[]) 还能拿到每个文本块的坐标。
- 打印与另存:uo_pdfview.Print(显示对话框?) / Print(打印机名) / Print(uo_json 选项),SaveAs(新文件) / SaveAsBlob()。
- 拆分:uo_pdfview.SplitPDF(源文件, 前缀, ref 输出数组[]),还支持按书签(byBookmark)或按关键字坐标拆分。
- 后台 DW 转 blob:uo_pdfmaker.PrintDatawindow(dw) 返回 blob,可直接入库或网络上传,不必落盘。
掌握这三件套,PowerBuilder 项目里"生成报表 PDF / 预览单据 / 合并归档"这类需求基本都能一站式搞定,无需再外接复杂的第三方库。 |