如何合并多个PDF以及给PDF添加页码
===================

#### 问题现象

HarmonyOS Next有没有提供将多个pdf合并成一个pdf以及给pdf添加页码的API。

#### 背景知识

[insertPageFromDocument](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/pdf-arkts-pdfservice#section7656515132015)接口提供能力，将其他Document的Page添加到当前Document，Page中的批注不支持插入到当前Document。

参数说明：

| 
参数名

 | 

类型

 | 

必填

 | 

说明

 |
| :-- | :-- | :-- | :-- |
| 

document

 | 

[PdfDocument](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/pdf-arkts-pdfservice#pdfdocument)

 | 

是

 | 

PdfDocument对象。

 |
| 

fromIndex

 | 

number

 | 

是

 | 

从其他文档第几页开始添加，大于等于0，0为起始页。

 |
| 

pageCount

 | 

number

 | 

是

 | 

添加页数量，大于0，小于等于总页数。

 |
| 

index

 | 

number

 | 

是

 | 

从当前文档第几页开始添加，大于等于0，小于总页数，0为起始页。

 |

[addHeaderFooter](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/pdf-arkts-pdfservice#section4211341401)接口提供能力，插入pdf文档页眉页脚。该方法属于耗时业务，需要遍历每一页去添加页眉页脚，添加页面较多时建议放到线程里去处理。

参数说明：

| 
参数名

 | 

类型

 | 

必填

 | 

说明

 |
| :-- | :-- | :-- | :-- |
| 

info

 | 

[HeaderFooterInfo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/pdf-arkts-pdfservice#headerfooterinfo)

 | 

是

 | 

页眉页脚的信息。

 |
| 

startIndex

 | 

number

 | 

是

 | 

起始页，必须大于等于0，0为起始页。

 |
| 

endIndex

 | 

number

 | 

是

 | 

结束页，小于总页数。

 |
| 

oddPages

 | 

boolean

 | 

是

 | 

奇数页是否添加，true表示是，false表示否。

 |
| 

evenPages

 | 

boolean

 | 

是

 | 

偶数页是否添加，true表示是，false表示否。

 |

#### 解决方案

1.  aboutToAppear回调中，确保rawfile目录下有pdf文件，拷贝到沙箱内。
2.  使用[insertPageFromDocument](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/pdf-arkts-pdfservice#section7656515132015)接口将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) => {
      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();
      });
    });
  }
}

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/9a/v3/WJZtoiFFSZWrV6wSX8bBRg/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T134305Z&HW-CC-Expire=86400&HW-CC-Sign=6EA948017A9683C991E7BBD95FD5A4EB99F7ADE340F147B8AA71180FDAB8F1A3)

*   示例代码中input\_src、input\_add文件需要提前放在项目rawfile目录下，路径：项目/entry/src/main/resources/rawfile。
*   先点击insertPageFromDocument按钮将pdf文件合并。
*   再点击addHeaderFooter按钮给合并后的pdf文件添加页脚页码。
*   IDE打开Device File Browser，查看保存的pdf文件，路径如下：应用包名/data/storage/el2/base/haps/entry/files，具体参考[访问设备文件](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-device-file-explorer)。