Skip to content

常见问题

从指定的 PDF 文件路径打开一个 PDF 文档

如何从指定的 PDF 文件路径打开一个 PDF 文档?

在构造 PDFDoc 时,只能使用绝对路径。由于 HarmonyOS Next 的沙盒机制,您无法访问应用外文件的真实路径。因此,您需要先将文件拷贝到应用内部,然后再打开。请参考如下的代码:

js
@Entry
@Component
struct Index {
  @State isShowDoc: boolean = false;
  @State pdfViewModel: PDFViewCtrlModel = new PDFViewCtrlModel(getContext());
  private appFilesDir = getContext().filesDir;

  build() {
    Column() {
      if (!this.isShowDoc) {
        Button('Open Doc')
          .onClick(async ()=>{
            const filePath = await this.selectFile();
            if(filePath != null) {
              this.isShowDoc = true;
              this.pdfViewModel.openDoc(filePath);
            }
          })
      } else {
        PDFViewCtrl({ model: this.pdfViewModel })
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  async selectFile(): Promise<string | null> {
    try {
      let documentSelectOptions = new picker.DocumentSelectOptions();
      // documentSelectOptions.fileSuffixFilters = suffix;
      documentSelectOptions.maxSelectNumber = 10;
      if (canIUse('SystemCapability.FileManagement.UserFileService.FolderSelection')) {
        documentSelectOptions.selectMode = picker.DocumentSelectMode.FILE;
      }

      const documentViewPicker = new picker.DocumentViewPicker(getContext());

      const documentSelectResult: Array<string> = await documentViewPicker.select(documentSelectOptions);
      if (documentSelectResult.length === 0) {
        return null;
      }

      const importFiles: Array<string> = new Array();
      for (let i = 0; i < documentSelectResult.length; i++) {
        let uri = documentSelectResult[i];

        let src_file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
        src_file.path

        const dest_filepath = this.appFilesDir + '/' + src_file.name;
        fs.copyFileSync(src_file.fd, dest_filepath);
        fs.closeSync(src_file.fd);

        importFiles.push(dest_filepath);
      }
      return importFiles[0];
    } catch (error) {
      console.error(error);
    }
    return null;
  }
}

打开 PDF 文档时显示指定的页面

如何在打开 PDF 文档时,显示指定的页面?

为了在打开 PDF 文档时显示指定的页面,您需要使用接口 pdfViewModel.gotoPage。Foxit PDF SDK 鸿蒙版使用多线程来提高渲染速度,因此您需要确保在使用 pdfViewModel.gotoPage 接口之前,文档已经被成功加载。

请在 IDocEventListener 中实现回调接口,然后在 onDocOpened 事件中调用 gotoPage 接口。以下是示例代码:

js
import { FoxitRDKNative, IDocEventListener, PDFViewCtrl, PDFViewCtrlModel } from 'foxit_rdk';
import picker from '@ohos.file.picker';
import fs, { Filter, Options } from '@ohos.file.fs';

@Entry
@Component
struct Index {
  @State isShowDoc: boolean = false;
  @State pdfViewModel: PDFViewCtrlModel = new PDFViewCtrlModel(getContext());
  private appFilesDir = getContext().filesDir;

  build() {
    Column() {
      if (!this.isShowDoc) {
        Button('Open Doc')
          .onClick(async () => {
            const filePath = await this.selectFile();
            if (filePath != null) {
              this.isShowDoc = true;
              this.pdfViewModel.openDoc(filePath);
            }
          })
      } else {
        RelativeContainer() {
          PDFViewCtrl({ model: this.pdfViewModel })
            .id('pdfview')
            .alignRules({
              top: { anchor: "__container__", align: VerticalAlign.Top },
              left: { anchor: "__container__", align: HorizontalAlign.Start }
            })

          Column() {
            Button('Goto Last Page')
              .onClick(() => {
                const pageCount = this.pdfViewModel.getPageCount();
                this.pdfViewModel.gotoPage(pageCount -1);
              })
          }
          .width('100%')
          .margin({ top: 100 })
          .id('ToolAction')
        }
        .width('100%')
        .height('100%')
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  aboutToAppear(): void {
    this.pdfViewModel.registerDocEventListener(this.docEventListener);
  }

  aboutToDisappear(): void {
    this.pdfViewModel.unregisterDocEventListener(this.docEventListener);
  }

  async selectFile(): Promise<string | null> {
    try {
      let documentSelectOptions = new picker.DocumentSelectOptions();
      // documentSelectOptions.fileSuffixFilters = suffix;
      documentSelectOptions.maxSelectNumber = 10;
      if (canIUse('SystemCapability.FileManagement.UserFileService.FolderSelection')) {
        documentSelectOptions.selectMode = picker.DocumentSelectMode.FILE;
      }

      const documentViewPicker = new picker.DocumentViewPicker(getContext());
      // await select complete
      const documentSelectResult: Array<string> = await documentViewPicker.select(documentSelectOptions);
      if (documentSelectResult.length === 0) {
        return null;
      }

      const importFiles: Array<string> = new Array();
      for (let i = 0; i < documentSelectResult.length; i++) {
        let uri = documentSelectResult[i];

        let src_file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
        src_file.path

        const dest_filepath = this.appFilesDir + '/' + src_file.name;
        fs.copyFileSync(src_file.fd, dest_filepath);
        fs.closeSync(src_file.fd);

        importFiles.push(dest_filepath);
      }
      return importFiles[0];
    } catch (error) {
      console.error(error);
    }
    return null;
  }

  private docEventListener: IDocEventListener = {
    onDocOpened: (document: FoxitRDKNative.pdf.PDFDoc, errCode: number): void => {
      if (errCode == FoxitRDKNative.common.ErrorCode.e_ErrSuccess) {
        const pageCount = this.pdfViewModel.getPageCount();
        this.pdfViewModel.gotoPage(pageCount - 2);
      }
    }
  }
}

License key 和序列号无法正常工作

从网站下载的 SDK 包,未进行任何更改,为什么 license key 和序列号无法正常工作?

通常,上传到网站的包,里面的 license key 和序列号是可以正常工作的。在上传到网站之前是经过测试的。因此,如果您发现 license key 和序列号无法使用,则可能是由设备的日期引起的。如果您设备的时间在下载包 "libs" 文件夹下 hosnsdk_key.txt 文件中的 StartDate 之前,则 "librdk.so" 库将无法解锁。请检查您设备的日期。

如何在 PDF 文档中添加 link 注释?

为了将 link 注释添加到 PDF 文档,首先需要调用 PDFPage.AddAnnot 将一个 link 注释添加到指定页面,然后调用 Action.Create 创建一个 action,并将该 action 设置给刚添加的 link 注释。以下是在 PDF 首页添加一个 URI link 注释的示例代码:

js
import { FoxitRDKNative } from 'foxit_rdk';

class LinkUnit {
  private addLinkAnnot() {
    try {
      const path = 'xxx/Sample.pdf'
      // 初始化一个 PDFDoc 对象
      const document = new FoxitRDKNative.pdf.PDFDoc(path);
      // 加载未加密的文档内容
      document.Load();
      // 获取 PDF文件的第一页
      const page = document.GetPage(0);

      // 在第一页添加一个 link annotation
      const linkAnnot = new FoxitRDKNative.pdf.annots.Link(page.AddAnnot(FoxitRDKNative.pdf.annots.Annot.e_Link,
        new FoxitRDKNative.common.fxcrt.RectF(250, 650, 400, 750)));

      // 创建一个 URI action 并设置 URI
      const uriAction =
        new FoxitRDKNative.pdf.actions.URIAction(FoxitRDKNative.pdf.actions.Action.Create(document,
          FoxitRDKNative.pdf.actions.Action.e_TypeURI));
      uriAction.SetURI("www.foxitsoftware.com");

      // 将 action 设置给 link annotation
      linkAnnot.SetAction(uriAction);
      linkAnnot.ResetAppearanceStream();

      // 保存已添加 link annotation 的文档
      document.SaveAs("xxx/sample_link.pdf", FoxitRDKNative.pdf.PDFDoc.e_SaveFlagNormal);
    } catch (e) {
      console.error(e);
    }
  }
}

向 PDF 文档中插入图片

如何向 PDF 文档中插入图片?

有两种方法可以帮助您将图片插入到 PDF 文档中。第一钟是调用 PDFPage.AddImageFromFilePath 接口。您可以参考如下示例代码,该代码将图片插入到 PDF 文档的首页:

备注:在调用 PDFPage.AddImageFromFilePath 接口之前,您需要获取并解析将要添加图片的页面。

js
import { FoxitRDKNative } from 'foxit_rdk';

class ImageUnit {
  //1
  private addImage(): void {
    try {
      const path = "xxx/Sample.pdf";
      // 初始化一个 PDFDoc 对象
      const document = new FoxitRDKNative.pdf.PDFDoc(path);
      // 加载未加密的文档内容
      document.Load();
      // 获取PDF文件的第一页
      const page = document.GetPage(0);
      // 解析页面
      if (!page.IsParsed()) {
        const parse = page.StartParse(FoxitRDKNative.pdf.PDFPage.e_ParsePageNormal, null, false);
        let state = FoxitRDKNative.common.Progressive.e_ToBeContinued;
        while (state == FoxitRDKNative.common.Progressive.e_ToBeContinued) {
          state = parse.Continue();
        }
      }
      // 在第一页添加一张图片
      page.AddImageFromFilePath("XXX/2.png", new FoxitRDKNative.common.fxcrt.PointF(20, 30), 60, 50, true)
      // 保存已添加图片的文档
      document.SaveAs("XXX/sample_image.pdf", FoxitRDKNative.pdf.PDFDoc.e_SaveFlagNormal);
    } catch (e) {
      console.error(e);
    }
  }
}

第二种是使用 PDFPage.AddAnnot 接口在指定的页面添加一个 screen 注释,然后将图片设置给刚添加的 screen 注释。您可以参考以下的示例代码,该代码将图片作为 screen 注释插入到 PDF 文件的首页:

js
import { FoxitRDKNative } from 'foxit_rdk';

class ImageUnit {
  private addScreenAnnot(): void {
    try {
      const path = "xxx/Sample.pdf";
      // 初始化一个 PDFDoc 对象
      const document = new FoxitRDKNative.pdf.PDFDoc(path);
      // 加载未加密的文档内容
      document.Load();
      // 获取 PDF 文件的第一页
      const page = document.GetPage(0);

      // 在第一页添加一个 screen annotation
      const screen = new FoxitRDKNative.pdf.annots.Screen(page.AddAnnot(FoxitRDKNative.pdf.annots.Annot.e_Screen,
        new FoxitRDKNative.common.fxcrt.RectF(100, 350, 250, 150)));

      // 加载一个本地图片
      const image = new FoxitRDKNative.common.Image('xxx/test.png');
      screen.SetImage(image, 0, 0);
      screen.ResetAppearanceStream();

      // 保存已添加 screen annotation 的文档
      document.SaveAs("XXX/sample_image.pdf", FoxitRDKNative.pdf.PDFDoc.e_SaveFlagNormal);
    } catch (e) {
      console.error(e);
    }
  }
}

高亮 PDF 文档中的表单域和设置高亮颜色

如何设置是否高亮 PDF 文档中的表单域? 以及如何设置高亮的颜色?

默认情况下,高亮 PDF 文档中的表单域功能是启用的。如果您想要禁用它或者设置高亮颜色,可以通过JSON 文件或调用 API 进行设置。

备注:如果您需要设置高亮的颜色,请确保表单域高亮的功能是启用的。

通过 JSON 文件

设置 "highlightForm": false, 在 PDF 文档中禁用表单域高亮功能。 设置 "highlightFormColor": "#2000ffcc", 设置高亮颜色 (输入您需要的颜色值)。

通过调用 API

UIExtensionsManager.enableFormHighlight 接口用来设置是否在 PDF 文档中启用表单域高亮的功能。如果您不需要启用该功能,请将其参数设置为 "false",如下所示:

js
// 假设已经初始化一个UIExtensionsManager对象: uiextensionsManager
this.uiextensionsManager.enableFormHighlight (false);

UIExtensionsManager.setFormHighlightColor 接口用来设置高亮的颜色。以下是调用此 API 的示例代码:

js
// 假设已经初始化一个UIExtensionsManager对象: uiextensionsManager
this.uiextensionsManager.setFormHighlightColor(0x4b0000ff);

支持全文索引搜索

Foxit PDF SDK 鸿蒙版是否支持全文索引搜索?如果支持,如何搜索在我的移动设备上离线存储的 PDF 文件?

是的。Foxit PDF SDK 鸿蒙版支持全文索引搜索。

要使用此功能,请按照如下的步骤:

a) 根据目录来创建一个文档来源,该目录为文档的搜索目录。

js
DocumentsSource#constructor(directory: string);

b) 创建一个全文文本搜索对象,以及设置用于存储索引数据的数据库路径。

js
FullTextSearch#constructor();
FullTextSearch#SetDataBasePath(path_of_data_base: string): void;

c) 开始索引文档来源中的 PDF 文档。

js
FullTextSearch#StartUpdateIndex(source: DocumentsSource, pause: PauseCallback, reupdate: boolean): Progressive;

备注:您可以索引指定的PDF文件。例如,如果某个PDF文档的内容发生了更改,您可以使用以下的API对其重新进行索引:

js
FullTextSearch#UpdateIndexWithFilePath(file_path: string): boolean

d) 从索引数据源中搜索指定的内容。搜索的结果将通过指定的回调函数来返回给外部,每找到一个匹配结果,则调用一次回调函数。

js
FullTextSearch#SearchOf(match_string: string, rank_mode: number, callback: SearchCallback): boolean

如下是使用全文索引搜索的示例代码:

js
import { FoxitRDKNative } from 'foxit_rdk';
import { systemDateTime } from '@kit.BasicServicesKit';

class DocumentsSourceUnit {
  private testSearch(): void {
    try {
      const directory = "A search directory...";
      const search = new FoxitRDKNative.fts.FullTextSearch();
      const dbPath = "The path of data base to store the indexed data...";
      search.SetDataBasePath(dbPath);
      // 获取文档源信息
      const source = new FoxitRDKNative.fts.DocumentsSource(directory);

      // 创建一个由用户实现的暂停回调对象,以暂停更新过程
      const pauseCallback = new PauseCallbackImpl(30);

      // 开始索引文档来源中的PDF文档
      const progressive = search.StartUpdateIndex(source, pauseCallback, false);
      let state = FoxitRDKNative.common.Progressive.e_ToBeContinued;
      while (state == FoxitRDKNative.common.Progressive.e_ToBeContinued) {
        state = progressive.Continue();
      }

      // 创建一个回调对象,当找到匹配项时将被调用
      const searchCallback = new MySearchCallback();

      // 从索引的数据源中搜索指定的关键词
      search.SearchOf("looking for this text", FoxitRDKNative.fts.FullTextSearch.e_RankHitCountASC, searchCallback);
    } catch (e) {
      console.error(e);
    }
  }
}

PauseCallbackImpl 回调的示例代码如下所示:

js
class PauseCallbackImpl extends FoxitRDKNative.common.PauseCallback {
  private m_milliseconds = 0;
  private m_startTime = 0;

  public constructor(milliSeconds: number) {
    super()
    this.m_milliseconds = milliSeconds;
    this.m_startTime = systemDateTime.getTime();
  }

  NeedToPauseNow(): boolean {
    if (this.m_milliseconds < 1) {
      return false;
    }

    const diff = systemDateTime.getTime() - this.m_startTime;
    if (diff > this.m_milliseconds) {
      this.m_startTime = systemDateTime.getTime();
      return true;
    } else {
      return false;
    }
  }
}

MySearchCallback 回调的示例如下所示:

js
class MySearchCallback extends FoxitRDKNative.fts.SearchCallback {
  public release(): void {
  }

  public retrieveSearchResult(filePath: string, pageIndex: number, matchResult: string, matchStartTextIndex: number,
    matchEndTextIndex: number): number {
    const s =
      `Found file is :${filePath} \n Page index is :${pageIndex} Start text index :${matchStartTextIndex} End text index :${matchEndTextIndex} \n Match is :${matchResult} \n\n`;
    console.info('MySearchCallback', 'retrieveSearchResult:  ' + s);
    return 0;
  }
}

备注

  • Foxit PDF SDK 鸿蒙版提供的全文索引搜索将以递归的方式遍历整个目录,以便搜索目录下的文件和文件夹都会被索引。

  • 如果需要中止索引进程,可以将 pause 回调参数传递给 StartUpdateIndex 接口。其回调函数 NeedToPauseNow 都会被调用,一旦完成一个 PDF 文档的索引。因此,调用者可以在回调函数 NeedToPauseNow 返回 "true" 时中止索引进程。

  • 索引数据库的位置由 SetDataBasePath 接口设置。如果要清除索引数据库,请手动清除。目前,不支持从索引函数中删除指定文件相关的索引内容。

  • SearchOf 接口的每个搜索结果都由指定的回调返回到外部。一旦 SearchOf 接口返回 "true"或 "false",则表示搜索已完成。

夜间模式颜色设置

如何设置夜间模式颜色?

设置夜间模式颜色,需要首先调用 PDFViewCtrlModel#setMappingModeBackgroundColor(mappingModeBackgroundColor: number)PDFViewCtrlModel#setMappingModeForegroundColor(mappingModeForegroundColor: number) 接口根据您的需要设置颜色,然后使用 PDFViewCtrlModel#setColorMode(colorMode: number) 设置颜色模式。

备注:如果颜色模式已经设置为 Renderer.e_ColorModeMapping/Renderer.e_ColorModeMappingGray,在调用 PDFViewCtrlModel#setMappingModeBackgroundColor(mappingModeBackgroundColor: number)PDFViewCtrlModel#setMappingModeForegroundColor(mappingModeForegroundColor: number) 接口之后,您仍然需要再次设置。否则,颜色设置可能不会起作用。

上述接口需要在 UI Extensions 组件的源代码中调用,请将 "libs" 文件夹下的 "uiextensions" 工程添加到您的工程中。然后在 "uiextensions/src/main/ets/frame/impl/MainFrame.ets" 文件中定位到 onValueChanged 函数,根据您的需要设置颜色。

使用 UIExtensions 设置只读模式

如何使用 UIExtensions 设置只读模式?

1:接口定义

在 UIExtensionsManager 提供了以下接口:

js
/**
 * whether the pdf document can be modified
 *
 * @return whether the pdf document can be modified
 */
public isEnableModification(): boolean

/**
 * Set whether the pdf document can be modified. The default setting allows modification
 *
 * @param isEnabled whether the pdf document can be modified
 */
public enableModification(isEnabled: boolean): void

2: 接口使用

默认情况下没有权限控制的文档是可以编辑的。如果需要禁用编辑相关的交互功能,可以通过以下方法设置:

js
import { UIExtensionsManager } from 'uiextensions';

class EnableModificationUnit{
  private uiextensionsManager:UIExtensionsManager;
  constructor(uiextMgr:UIExtensionsManager) {
    this.uiextensionsManager = uiextMgr;
  }

  private test():void{
    this.uiextensionsManager.enableModification(false);
  }
}

更新页面绑定 (page binding) 支持 RTL (Right-to-Left)

如何自动更新页面绑定来支持 RTL (Right-to-Left) ?

对于大多数语言,我们使用的阅读习惯都是从左往右,这需要在左边缘进行页面绑定。但是,也有一些语言的阅读习惯是从右往左,比如阿拉伯语和希伯来语以及几种东亚文字。在这种情况下,用户最好在右边缘进行页面绑定,页面将从右到左排列(第一页在最右边)。为此,我们做了从右到左的页面布局的适配。

页面绑定与水平滚动一起使用。对于垂直滚动,它仅在启用双页模式时才有效。

以编程方式更新页面绑定

Foxit PDF SDK 鸿蒙版在 PDF_PAGE_BINDING_EDGE 枚举类中定义了常量 LEFTRIGHT:

  • LEFT: 表示文档顺序从左往右排列
  • RIGHT: 表示文档顺序从右往左排列

然后,通过如下的方法来更新页面绑定来切换页面布局:

js
import { PDFViewCtrlModel, PDF_PAGE_BINDING_EDGE } from 'foxit_rdk';
import { UIExtensionsManager } from 'uiextensions';

class PageBindingUnit {
  private uiExtensionsManager: UIExtensionsManager;
  private pdfviewCtrl: PDFViewCtrlModel;

  constructor(uiExtMgr: UIExtensionsManager) {
    this.uiExtensionsManager = uiExtMgr;
    this.pdfviewCtrl = this.uiExtensionsManager.getPDFViewCtrlModel();
  }

  private test(): void {
    this.pdfviewCtrl.setPageBinding(PDF_PAGE_BINDING_EDGE.RIGHT);
  }
}

打开网页 PDF 文件的问题

如何解决使用 PDFViewCtrlModel#openDocFromUrl(url: string, password?: string, cacheOption?: FoxitRDKNative.custom.CacheOption, requestOptions?: Record<string, string>, cb?: (errorCode: FoxitRDKNative.Common.ErrorCode) => void) 接口打开网页PDF文件时,一些文档可能无法正确显示或根本无法显示的问题?

这个问题发生在一些带有大量对象的文档在加载完成之前就被关闭,导致缓存的数据不完整。在尝试加载数据时,当前没有方法来判定数据的有效性,只能判定该数据是否已经被缓存了。

为了解决这个问题,建议用户清除缓存数据,并使用以下方法重新加载文档。这种方法不会影响文档的打开速度,因为 SDK 内部是根据页码来加载网页的。

js
/**
 * Clear the cache file by the specified url.
 *
 * @param url The url of the document that used in {@link PDFViewCtrlModel.openDocFromUrl}
 * @see openDocFromUrl(string, string, CacheOption, Record<string, string>, (errorCode: FoxitRDKNative.common.ErrorCode) => void)
 */
public clearCacheFile(url: string): void 

/**
 * Clear all the cache files.
 *
 * @see openDocFromUrl(string, string, CacheOption, Record<string, string>, (errorCode: FoxitRDKNative.common.ErrorCode) => void)
 */
public clearAllCacheFiles(): void