HarmonyOS鸿蒙Next中PDF合并有解决方法吗?

HarmonyOS鸿蒙Next中PDF合并有解决方法吗? 这边产品需要一个功能导入多分pdf能合并成一个,鸿蒙暂时有办法吗

6 回复

【背景知识】

insertPageFromDocument接口提供能力,将其他Document的Page添加到当前Document,Page中的批注不支持插入到当前Document。

addHeaderFooter接口提供能力,插入PDF文档页眉页脚。该方法属于耗时业务,需要遍历每一页去添加页眉页脚,添加页面较多时建议放到线程里去处理。

【解决方案】

  1. aboutToAppear回调中,确保rawfile目录下有pdf文件,拷贝到沙箱内。
  2. 使用insertPageFromDocument接口将input_add.pdf文档页插入到input_src.pdf末尾的位置,并另存文档。
  3. 给生成的testInsertPageFromDocument.pdf文档添加页码。

完整示例代码如下:

import { pdfService } from '@kit.PDFKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { Font } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct PdfPage {
  @State lastPage: number = 0;
  private pdfDocument: pdfService.PdfDocument = new pdfService.PdfDocument();
  private context = this.getUIContext().getHostContext() as Context;

  async aboutToAppear(): Promise<void> {
    try {
      //确保rawfile目录下有pdf文件
      await this.copyRawFileToSdcard(this.context, 'input_src.pdf');
      await this.copyRawFileToSdcard(this.context, 'input_add.pdf');
      promptAction.openToast({ message: '全部拷贝完成' });
    } catch (error) {
      promptAction.openToast({ message: '文件拷贝失败' });
    }
  }

  build() {
    Column() {
      // 将input_add.pdf文档页插入到input_src.pdf末尾的位置,并另存文档
      Button('insertPageFromDocument').onClick(async () => {
        let filePath = this.context.filesDir + '/input_src.pdf';
        let loadResult: pdfService.ParseResult = this.pdfDocument.loadDocument(filePath);
        if (pdfService.ParseResult.PARSE_SUCCESS === loadResult) {
          this.lastPage = this.pdfDocument.getPageCount();
        } else {
          promptAction.openToast({ message: '加载失败' });
        }
        let pdfDoc: pdfService.PdfDocument = new pdfService.PdfDocument();
        // 确保该沙箱目录下有input_add.pdf文档
        let res = pdfDoc.loadDocument(this.context.filesDir + '/input_add.pdf');
        if (res === pdfService.ParseResult.PARSE_SUCCESS) {
          this.pdfDocument.insertPageFromDocument(pdfDoc, 0, pdfDoc.getPageCount(), this.lastPage);
          let outPdfPath = this.context.filesDir + '/testInsertPageFromDocument.pdf';
          let result = this.pdfDocument.saveDocument(outPdfPath);
          this.lastPage = this.pdfDocument.getPageCount();
          hilog.info(0x0000, 'PdfPage', 'insertPageFromDocument %{public}s!', result ? 'success' : 'fail');
        } else {
          promptAction.openToast({ message: '加载失败' });
        }
        pdfDoc.releaseDocument();
      })

      Button('addHeaderFooter').onClick(async () => {
        // 确保沙箱目录有testInsertPageFromDocument.pdf文档
        let filePath = this.context.filesDir + '/testInsertPageFromDocument.pdf';
        let res = this.pdfDocument.loadDocument(filePath);
        if (res === pdfService.ParseResult.PARSE_SUCCESS) {
          let hfInfo: pdfService.HeaderFooterInfo = new pdfService.HeaderFooterInfo();
          hfInfo.fontInfo = new pdfService.FontInfo();
          // 确保字体路径存在
          let font: Font = new Font();
          hfInfo.fontInfo.fontPath = font.getFontByName('HarmonyOS Sans')?.path;
          // 如果不知道字体的具体名称,可以为空字符串
          hfInfo.fontInfo.fontName = '';
          hfInfo.textSize = 10;
          hfInfo.charset = pdfService.CharsetType.PDF_FONT_DEFAULT_CHARSET;
          hfInfo.underline = false;
          hfInfo.textColor = 0x00000000;
          hfInfo.leftMargin = 1.0;
          hfInfo.topMargin = 40.0;
          hfInfo.rightMargin = 1.0;
          hfInfo.bottomMargin = 40.0;
          let pdfPageCount = this.pdfDocument.getPageCount();
          for (let index = 0; index < pdfPageCount; index++) {
            hfInfo.footerCenterText = `${index + 1}`;
            this.pdfDocument.addHeaderFooter(hfInfo, index, index, true, true);
          }
          let outPdfPath = this.context.filesDir + '/testAddHeaderFooter.pdf';
          let result = this.pdfDocument.saveDocument(outPdfPath);
          hilog.info(0x0000, 'PdfPage', 'addHeaderFooter %{public}s!', result ? 'success' : 'fail');
        } else {
          promptAction.openToast({ message: '加载失败' });
        }
        this.pdfDocument.releaseDocument();
      })
    }
  }

  // 拷贝pdf文件到应用沙箱目录
  private copyRawFileToSdcard(context: common.Context, pdfName: string): Promise<void> {
    return new Promise((resolve, reject) => {
      let destRoot = context.filesDir;
      // rawfile下的文件名
      let srcFileName = pdfName;
      let destFilePath = `${destRoot}/${srcFileName}`;
      context.resourceManager.getRawFileContent(srcFileName, (error: BusinessError, data: Uint8Array) => {
        if (error) {
          promptAction.openToast({ message: '拷贝失败' });
          hilog.error(0x0000, 'PdfCopy', `Copy failed: ${error.code}`);
          return;
        }
        const fileStream = fs.createStreamSync(destFilePath, 'w+');
        fileStream.writeSync(data.buffer);
        fileStream.close();
        promptAction.openToast({ message: '拷贝成功' });
        resolve();
      });
    })
  }
}
  • 示例代码中input_src、input_add文件需要提前放在项目rawfile目录下,路径:项目-entry-src-main-resources-rawfile。
  • 先点击insertPageFromDocument按钮将pdf文件合并。
  • 再点击addHeaderFooter按钮给合并后的pdf文件添加页脚页码。

更多关于HarmonyOS鸿蒙Next中PDF合并有解决方法吗?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


我试了一下报错,好像虚拟器运行不了得要真机。,

开发者您好,x86版本模拟器不支持PDF Kit,建议使用真机或者云调试测试https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-emulator-specification#section38231424133213

一、当前能力现状

  • 原生接口限制

鸿蒙PDF Kit(版本5.0.0及以上)目前未直接提供PDF文件合并的API,但支持PDF文档的加载、编辑及保存操作。

二、替代实现方案

// 示例:基于现有能力的合并思路(需自行实现核心逻辑)
import { pdfService, pdfViewManager, PdfView } from '@kit.PDFKit';
import { fileIo } from '@kit.CoreFileKit';

// 步骤1:加载多个PDF文件
async function mergePDFs(filePaths: string[], outputPath: string) {
  const pdfDoc = pdfService.createDocument();
  
  // 遍历所有文件
  for (const path of filePaths) {
    try {
      const controller = new pdfViewManager.PdfController();
      await controller.loadDocument(path);
      const pageCount = controller.getPageCount();
      
      // 逐页提取内容(需自定义页面处理逻辑)
      for (let i = 0; i < pageCount; i++) {
        const page = controller.getPage(i);
        pdfDoc.addPage(page); // 伪代码,实际需转存页面内容
      }
    } catch (error) {
      console.error(`加载文件失败:${path}`, error);
    }
  }
  
  // 步骤2:保存合并后文件
  const content = pdfDoc.save();
  const fd = fileIo.openSync(outputPath, fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE);
  fileIo.writeSync(fd, content);
  fileIo.closeSync(fd);
}

三、关键注意事项

  • 版本兼容性

需确认SDK版本不低于HarmonyOS 6.0.0 Release,且DevEco Studio版本为6.0.0及以上

  • 文件权限管理

合并操作需申请文件访问权限:

"requestPermissions": [
  "ohos.permission.READ_MEDIA",
  "ohos.permission.WRITE_MEDIA"
]
  • 替代方案建议
    • 若需快速实现,可考虑:
      • 将多份PDF转为图片格式,通过image组件合并后生成新PDF
      • 调用服务端接口处理合并逻辑
    • 参考文档扫描组件(DocumentScanner)的批量处理机制,如结果1中提到的多页扫描生成单个PDF功能

HarmonyOS Next中PDF合并可通过ArkUI组件和系统文件管理API实现。开发者需使用@ohos.file.fs@ohos.file.picker模块访问文件,通过PDF解析库(如第三方适配库)提取内容后,使用Canvas或PDF生成工具重新绘制合并。注意需申请ohos.permission.READ_MEDIAohos.permission.WRITE_MEDIA权限。目前官方未提供直接合并的API,需自行实现解析与合成逻辑。

目前HarmonyOS Next的ArkUI框架及API暂未提供原生的PDF文档合并功能。若需实现此功能,可以考虑以下技术路径:

  1. 集成第三方C/C++库:寻找支持PDF处理的成熟开源库(如PDFium、PoDoFo等),通过HarmonyOS的Native API(NDK)进行封装,以动态链接库形式调用核心合并功能。

  2. 使用纯JavaScript/TypeScript方案:评估是否可采用Web组件承载PDF.js等前端库,在渲染层实现客户端合并逻辑。需注意性能与大文件兼容性。

  3. 服务端处理:将PDF文件上传至后端服务进行合并,客户端通过网络接口获取结果。此方案可规避客户端性能限制,但依赖网络环境。

建议优先评估业务场景对离线处理、性能及文件大小的要求,再选择对应方案。若采用本地库集成,需重点关注Native内存管理及ArkTS/CPP数据交互的稳定性。

回到顶部