使用Rust结合pdfium与WinAPI打印PDF无输出问题排查
Rust + Pdfium + WinAPI 打印PDF问题排查思路
问题描述
用Rust结合pdfium和WinAPI打印PDF,代码无报错、调试输出显示正常,但打印机队列任务仅短暂闪过无实际打印内容;用Microsoft Print to PDF测试时生成0字节且无法打开的文件,怀疑问题出在pdfium的FPDF_RenderPage方法,需排查思路。
核心排查方向
1. HDC与打印句柄未关联
当前代码分开使用CreateDCA创建HDC、OpenPrinter获取打印机句柄,但两者没有绑定,WinAPI打印流程中需要让打印文档与渲染用的HDC关联:
- 修正方案:移除单独的
OpenPrinter调用,直接用CreateDC获取的HDC调用StartDocA/StartPage(而非StartDocPrinterA/StartPagePrinter),让渲染内容直接输出到HDC对应的打印任务。
2. FPDF_RenderPage参数单位不匹配
PDF页面尺寸单位是点(1点=1/72英寸),而WinAPI HDC的单位依赖打印机分辨率(默认可能是缇或像素),直接将浮点尺寸转i32会导致渲染尺寸错误:
- 修正步骤:
- 用
GetDeviceCaps(hdc, LOGPIXELSX)和GetDeviceCaps(hdc, LOGPIXELSY)获取打印机DPI; - 将PDF点尺寸转换为打印机像素:
let dpi_x = GetDeviceCaps(hdc, LOGPIXELSX); let dpi_y = GetDeviceCaps(hdc, LOGPIXELSY); let render_width = (resp_page_width * dpi_x as f32 / 72.0) as i32; let render_height = (resp_page_height * dpi_y as f32 / 72.0) as i32; - 打印场景建议给
FPDF_RenderPage的flags参数加上0x1000(FPDF_PRINTING)标志,替换或补充当前的0x800。
- 用
3. 打印流程顺序不合理
当前在启动打印页面后才初始化pdfium、加载文档,虽不报错但可能引发资源竞争:
- 调整顺序:将
FPDF_InitLibraryWithConfig放在打印流程(StartDocPrinterA)之前,确保pdfium资源加载完成后再处理打印任务。
4. 打印数据类型不匹配
设置的data_type为XPS_PASS,但GDI渲染的内容对应RAW或系统自动判断类型:
- 测试方案:将
DOC_INFO_1A的pDatatype设为PSTR::null(),或改为"RAW\0",避免数据类型不兼容导致内容丢失。
5. 补充错误检查
现有调试仅打印基础返回值,需补充关键节点的有效性检查:
- 检查
FPDF_LoadMemDocument和FPDF_LoadPage的返回值是否为空指针; - WinAPI调用失败时用
GetLastError()获取具体错误码,比如StartDocPrinterA返回0时排查错误原因。
原代码及调试输出
FFI 代码
[repr(C)] #[derive(Clone, Copy)] struct FPDF_LIBRARY_CONFIG { version: i32, m_pUserFontPaths: *const c_char, m_pIsolate: *const c_void, m_v8EmbedderSlot: u32, } #[cfg(target_os = "windows")] #[link(name = "pdfium")] extern "C" { fn FPDF_InitLibraryWithConfig(config: *const FPDF_LIBRARY_CONFIG) -> c_void; fn FPDF_DestroyLibrary() -> c_void; fn FPDF_LoadMemDocument( file_content: *const c_void, content_size: i32, password: *const c_char, ) -> *const c_void; fn FPDF_LoadPage(document: *const c_void, page_index: i32) -> *const c_void; fn FPDF_GetPageWidthF(page: *const c_void) -> c_float; fn FPDF_GetPageHeightF(page: *const c_void) -> c_float; fn FPDF_RenderPage( hdc: HDC, page: *const c_void, start_x: i32, start_y: i32, size_x: i32, size_y: i32, rotate: i32, flags: i32, ) -> c_void; }
打印实现代码
pub fn print(file: &[u8]) -> anyhow::Result<()> { use std::ptr::null; use windows::{ core::{s, PCSTR, PSTR}, Win32::{ Foundation::HANDLE, Graphics::{ Gdi::{CreateDCA, DeleteDC}, Printing::{ ClosePrinter, EndDocPrinter, EndPagePrinter, OpenPrinterA, StartDocPrinterA, StartPagePrinter, DOC_INFO_1A }, }, }, }; unsafe { let hdc = CreateDCA(PCSTR::null(), s!("Microsoft Print to PDF"), None, None); println!("create dca: {} {:?}", hdc.is_invalid(), hdc); let mut printer_handle = HANDLE::default(); let data_type = "XPS_PASS\0"; // also tried "RAW" and "Text" let resp_open_printer = OpenPrinterA(s!("Microsoft Print to PDF"), &mut printer_handle, None); println!("open printer: {} {:?}", resp_open_printer.is_ok(), printer_handle); let doc_name = "Test Document\0"; let doc_Info = DOC_INFO_1A { pDocName: PSTR::from_raw(doc_name.as_ptr().cast_mut()), pOutputFile: PSTR::null(), pDatatype: PSTR::from_raw(data_type.as_ptr().cast_mut()), }; let resp_start_doc_printer = StartDocPrinterA(printer_handle, 1, &doc_Info); println!("start doc printer: {}", resp_start_doc_printer); let resp_start_page_printer = StartPagePrinter(printer_handle); println!("start page printer: {}", resp_start_page_printer.as_bool()); let config = FPDF_LIBRARY_CONFIG { version: 2, m_pUserFontPaths: null(), m_pIsolate: null(), m_v8EmbedderSlot: 0, }; FPDF_InitLibraryWithConfig(&config); let doc = FPDF_LoadMemDocument(file.as_ptr() as *const c_void, file.bytes().count() as i32, null()); println!("load mem doc: {:?} {}", doc, file.bytes().count() as i32); let page = FPDF_LoadPage(doc, 0); println!("load page: {:?}", page); let resp_page_width = FPDF_GetPageWidthF(page); println!("page width: {}", resp_page_width); let resp_page_height = FPDF_GetPageHeightF(page); println!("page height: {}", resp_page_height); FPDF_RenderPage(hdc, page, 0, 0, resp_page_width as i32, resp_page_height as i32, 0, 0x800); let resp_end_page_printer = EndPagePrinter(printer_handle); println!("end page printer: {}", resp_end_page_printer.as_bool()); let resp_end_doc_printer = EndDocPrinter(printer_handle); println!("end doc printer: {}", resp_end_doc_printer.as_bool()); let resp_close_printer = ClosePrinter(printer_handle); println!("close printer: {}", resp_close_printer.is_ok()); let resp_delete_dc = DeleteDC(hdc); println!("delete dc: {}", resp_delete_dc.as_bool()); FPDF_DestroyLibrary(); } ... }
调试输出
create dca: false HDC(-551480207) // false is good open printer: true HANDLE(1372067071760) start doc printer: 73 start page printer: true load mem doc: 0x13f780cc190 4953 load page: 0x13f758f9730 page width: 297.64 // correct width page height: 575.43005 // correct height end page printer: true end doc printer: true close printer: true delete dc: true
内容的提问来源于stack exchange,提问作者e-Ra
相关产品推荐
相关产品推荐

