2026/4/5 16:21:46
网站建设
项目流程
标签式网站内容管理,做网站 负责 域名备案,wordpress轮播图,宁波建设行业招聘信息网站本文详细记录了将 pdf_image_renderer Flutter 插件从 Android/iOS 适配到鸿蒙 (HarmonyOS/OpenHarmony) 平台的完整过程#xff0c;包括技术方案选型、NAPI 原生模块开发、pdfium 库集成等核心内容。 一、项目背景
1.1 pdf_image_renderer 插件简介
pdf_image_renderer 是一…本文详细记录了将pdf_image_rendererFlutter 插件从 Android/iOS 适配到鸿蒙 (HarmonyOS/OpenHarmony) 平台的完整过程包括技术方案选型、NAPI 原生模块开发、pdfium 库集成等核心内容。一、项目背景1.1 pdf_image_renderer 插件简介pdf_image_renderer是一个 Flutter 插件用于将 PDF 文件渲染为位图图片。它使用各平台的原生渲染器来实现高质量的 PDF 渲染Android: 使用android.graphics.pdf.PdfRendereriOS: 使用CGPDFDocument/CGPDFPageHarmonyOS: 使用pdfium库通过 NAPI 实现本文重点1.2 为什么需要适配鸿蒙随着 HarmonyOS NEXT 的发布越来越多的开发者需要将现有的 Flutter 应用适配到鸿蒙平台。然而鸿蒙系统没有内置的 PDF 渲染 API不像 Android 的PdfRenderer或 iOS 的CGPDFDocument这给 PDF 相关功能的适配带来了挑战。二、技术方案设计2.1 方案调研经过调研鸿蒙平台渲染 PDF 的可选方案有方案优点缺点WebView 渲染实现简单性能差无法获取位图数据纯 Dart PDF 库跨平台渲染质量一般性能受限pdfium NAPI高性能渲染质量好需要 C 开发复杂度较高最终选择pdfium NAPI方案因为它能提供与 Android/iOS 原生渲染器同等质量的 PDF 渲染效果。2.2 整体架构┌─────────────────────────────────────────────────────────┐ │ Flutter (Dart) │ │ PdfImageRenderer API │ └─────────────────────┬───────────────────────────────────┘ │ MethodChannel ▼ ┌─────────────────────────────────────────────────────────┐ │ ArkTS Plugin Layer │ │ PdfImageRendererPlugin.ets │ │ - 处理 MethodChannel 调用 │ │ - 调用 NAPI 函数 │ │ - PNG 图片编码 (ohos.multimedia.image) │ └─────────────────────┬───────────────────────────────────┘ │ NAPI Import ▼ ┌─────────────────────────────────────────────────────────┐ │ C NAPI Layer │ │ libpdf_renderer.so │ │ - PDF 文档管理 │ │ - 页面渲染 │ │ - BGRA → RGBA 像素转换 │ └─────────────────────┬───────────────────────────────────┘ │ 动态链接 ▼ ┌─────────────────────────────────────────────────────────┐ │ libpdfium.so │ │ (预编译 PDF 渲染引擎) │ └─────────────────────────────────────────────────────────┘三、实现详解3.1 目录结构ohos/ ├── oh-package.json5 # 包配置 ├── build-profile.json5 # 构建配置含 CMake ├── Index.ets # 入口文件 ├── src/main/ │ ├── module.json5 # 模块配置 │ ├── ets/components/plugin/ │ │ └── PdfImageRendererPlugin.ets # ArkTS 插件主类 │ ├── cpp/ │ │ ├── CMakeLists.txt # C 构建配置 │ │ ├── napi_init.cpp # NAPI 模块注册 │ │ ├── pdf_renderer_napi.cpp # PDF 渲染实现 │ │ ├── pdf_renderer_napi.h │ │ ├── include/ │ │ │ └── fpdfview.h # pdfium 头文件 │ │ └── types/libpdf_renderer/ │ │ ├── index.d.ts # TypeScript 类型声明 │ │ └── oh-package.json5 │ └── libs/arm64-v8a/ │ └── libpdfium.so # 预编译 pdfium 库3.2 NAPI 模块实现3.2.1 模块注册 (napi_init.cpp)staticnapi_valueInit(napi_env env,napi_value exports){napi_property_descriptor desc[]{{openPdf,nullptr,NapiOpenPdf,nullptr,nullptr,nullptr,napi_default,nullptr},{closePdf,nullptr,NapiClosePdf,nullptr,nullptr,nullptr,napi_default,nullptr},{getPageCount,nullptr,NapiGetPageCount,nullptr,nullptr,nullptr,napi_default,nullptr},{openPage,nullptr,NapiOpenPage,nullptr,nullptr,nullptr,napi_default,nullptr},{closePage,nullptr,NapiClosePage,nullptr,nullptr,nullptr,napi_default,nullptr},{getPageWidth,nullptr,NapiGetPageWidth,nullptr,nullptr,nullptr,napi_default,nullptr},{getPageHeight,nullptr,NapiGetPageHeight,nullptr,nullptr,nullptr,napi_default,nullptr},{renderPage,nullptr,NapiRenderPage,nullptr,nullptr,nullptr,napi_default,nullptr},};napi_define_properties(env,exports,sizeof(desc)/sizeof(desc[0]),desc);// 初始化 pdfium 库PdfRendererNapi::GetInstance().InitLibrary();returnexports;}3.2.2 PDF 渲染核心逻辑uint8_t*PdfRendererNapi::RenderPage(intdocId,intpageIndex,intx,inty,intwidth,intheight,doublescale,uint32_tbackgroundColor,int*outDataSize){// 1. 创建位图FPDF_BITMAP bitmapFPDFBitmap_Create(scaledWidth,scaledHeight,1);// 2. 填充背景色FPDFBitmap_FillRect(bitmap,0,0,scaledWidth,scaledHeight,backgroundColor);// 3. 渲染页面到位图FPDF_RenderPageBitmap(bitmap,page,renderX,renderY,renderWidth,renderHeight,0,FPDF_ANNOT|FPDF_LCD_TEXT);// 4. BGRA → RGBA 转换pdfium 输出 BGRA但 HarmonyOS image API 需要 RGBAuint8_t*src(uint8_t*)FPDFBitmap_GetBuffer(bitmap);for(inti0;iscaledWidth*scaledHeight;i){intoffseti*4;outputData[offset0]src[offset2];// R - BoutputData[offset1]src[offset1];// G - GoutputData[offset2]src[offset0];// B - RoutputData[offset3]src[offset3];// A - A}returnoutputData;}3.3 ArkTS 插件层import{picker}fromkit.CoreFileKit;import{fileUri}fromkit.CoreFileKit;importimagefromohos.multimedia.image;importpdfRendererfromlibpdf_renderer.so;exportdefaultclassPdfImageRendererPluginimplementsFlutterPlugin,MethodCallHandler{// 文件选择器privateasynchandlePickPdfFile(call:MethodCall,result:MethodResult):Promisevoid{constdocumentSelectOptionsnewpicker.DocumentSelectOptions();documentSelectOptions.fileSuffixFilters[.pdf,.PDF];constdocumentPickernewpicker.DocumentViewPicker();consturisawaitdocumentPicker.select(documentSelectOptions);if(urisuris.length0){constpathnewfileUri.FileUri(uris[0]).path;result.success(path);}}// 渲染 PDF 页面privateasynchandleRenderPDFPage(call:MethodCall,result:MethodResult):Promisevoid{// 1. 调用 NAPI 渲染获取 RGBA 像素数据constrenderResultpdfRenderer.renderPage(docId,pageIndex,x,y,width,height,scale,bgColor);// 2. 创建 PixelMapconstpixelMapawaitimage.createPixelMap(renderResult.data.buffer,{size:{width:renderResult.width,height:renderResult.height},pixelFormat:image.PixelMapFormat.RGBA_8888,});// 3. 编码为 PNGconstpackerimage.createImagePacker();constpngBufferawaitpacker.packing(pixelMap,{format:image/png,quality:100});result.success(newUint8Array(pngBuffer));}}3.4 CMake 配置关键预编译库的正确引入方式# 错误方式库不会被打包到 HAP link_directories(${PDFIUM_LIB_PATH}) target_link_libraries(pdf_renderer pdfium) # 正确方式库会被自动打包到 HAP add_library(pdfium SHARED IMPORTED) set_target_properties(pdfium PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../libs/${OHOS_ARCH}/libpdfium.so) target_link_libraries(pdf_renderer PUBLIC pdfium)四、遇到的问题与解决方案4.1 pdfium 库获取问题鸿蒙平台没有预编译的 pdfium 库。解决从 bblanchon/pdfium-binaries 下载linux-musl-arm64版本该版本与 HarmonyOS 的 musl libc 兼容。curl-L -o pdfium.tgz https://github.com/bblanchon/pdfium-binaries/releases/latest/download/pdfium-linux-musl-arm64.tgz4.2 NAPI 模块加载失败问题运行时报错Cannot read property openPdf of undefined。原因libpdfium.so没有被打包到 HAP 中。解决使用 CMake 的IMPORTED库方式引入预编译库确保其被正确打包。4.3 file_picker 不支持 OHOS问题Flutter 的file_picker包不支持 OHOS 平台。解决在插件中实现原生文件选择器使用kit.CoreFileKit的pickerAPI。五、使用方法5.1 安装dependencies:pdf_image_renderer:^1.0.15.2 基本使用// 1. 创建渲染器finalpdfPdfImageRenderer(path:/path/to/document.pdf);// 2. 打开 PDFawaitpdf.open(password:optional_password);// 3. 获取页数finalpageCountawaitpdf.getPageCount();// 4. 渲染页面为图片finalimageDataawaitpdf.renderPage(pageIndex:0,scale:2.0,// 2倍分辨率background:Colors.white,);// 5. 显示图片Image(image:MemoryImage(imageData!))// 6. 关闭 PDFawaitpdf.close();5.3 OHOS 平台文件选择// 使用原生文件选择器仅 OHOSfinalpathawaitPdfImageRendererPlatform.instance.pickPdfFile();if(path!null){finalpdfPdfImageRenderer(path:path);awaitpdf.open();// ...}六、性能数据测试项设备结果打开 10 页 PDFHarmonyOS NEXT~50ms渲染单页 (A4, scale1)HarmonyOS NEXT~30ms渲染单页 (A4, scale3)HarmonyOS NEXT~150ms内存占用-约 20MB (含 pdfium)七、总结本次适配工作的主要成果完整实现PDF 渲染功能包括打开/关闭文档、获取页数、获取尺寸、渲染页面支持密码保护的 PDF 文件支持自定义渲染参数缩放比例、裁剪区域、背景颜色原生文件选择器集成高性能渲染使用 pdfium 引擎渲染质量与 Chrome 浏览器一致关键技术点OHOS NAPI (Node-API) 开发CMake 预编译库集成ArkTS 与 C 混合编程Flutter MethodChannel 通信HarmonyOS 多媒体 API (image 模块)源码仓库插件地址pdf_image_renderer