快速构建一个功能齐全的 PDF 阅读器
福昕 PDF SDK 鸿蒙版将所有的 UI 实现(包括应用程序的基本 UI 和即用型 UI 功能模块)封装在 UI Extensions 组件中,因此开发人员可以轻松快速通过几行代码构建一个功能丰富的 PDF 阅读器。本章将提供详细的教程来帮助您快速开始使用福昕 PDF SDK 鸿蒙版在 HarmonyOS Next 平台创建 PDF 阅读器。
如果您正在基于 OpenHarmony 操作系统开发应用,以下示例工程的步骤同样适用。
提示:
由于 OpenHarmony 平台暂未提供 UIEXTENSIONS 组件,您需要忽略示例工程中所有与 UIExtensions 组件相关的步骤。
创建一个新的示例工程
以下将创建一个名为 PDFReader
的示例工程。构建的示例工程环境,使用 DevEco Studio NEXT Developer Beta3
,以及 OpenHarmony SDK API Version 12 Beta3
.
打开 DevEco Studio,创建新工程:
- 选择
File
->New
->Create Project…
。 - 在
Choose Your Ability Template
向导中,选择Empty Ability
。 - 点击
Next
。
- 选择
配置工程信息:
- 在
Configure Your Project
对话框中,填写工程相关信息,例如工程名称、存储路径等。 - 点击
Finish
。
- 在
集成 SDK 到示例工程
我们将集成 SDK 默认的内置 UI 到示例工程。 为了简单和方便,本示例工程将直接使用 UI Extensions
组件,而非源代码工程。 我们只需要添加以下的库文件到 PDFReader
示例工程中。
- FoxitRDK.har
- FoxitRDKUIExtensions.har
操作步骤:
拷贝库文件夹
- 将下载包中的
libs
文件夹拷贝到工程根目录,即PDFReader
文件夹下。
- 将下载包中的
安装
FoxitRDK.har
- 打开 Terminal,在工程根目录下,运行以下命令来安装
FoxitRDK.har
:
tsohpm install libs/FoxitRDK.har
安装成功后,
- 在
PDFReader/oh_modules
目录下会生成foxit\_rdk 文件夹
。 - 在
PDFReader/oh-package.json5
文件中会自动生成引用,如下图所示。
- 打开 Terminal,在工程根目录下,运行以下命令来安装
安装
FoxitRDKUIExtensions.har
库。- 由于 FoxitRDKUIExtensions.har 依赖 FoxitRDK.har,因此当在本地环境安装时,您需要在
PDFReader/oh-package.json5
文件中添加如下的命令:
ts"overrides": { "foxit_rdk": "file:libs/FoxitRDK.har" },
- 打开 Terminal,在工程根目录下,运行以下命令来安装
FoxitRDKUIExtensions.har
:
tsohpm install libs/FoxitRDKUIExtensions.har
安装成功后,
- 在
PDFReader/oh_modules
目录下会生成uiextensions
文件夹。 - 在
PDFReader/oh-package.json5
文件中会自动生成引用,如下图所示。
- 由于 FoxitRDKUIExtensions.har 依赖 FoxitRDK.har,因此当在本地环境安装时,您需要在
初始化 SDK
在调用任何 APIs 之前,应用程序必须使用 license 授权码初始化 SDK 库。静态初始化函数 Library.initialize(sn, key)
用于 SDK 库的初始化。试用 license 文件在 下载包的lib
文件夹下。当试用期结束后,您需要购买正式 license 以继续使用该 SDK。以下是初始化 SDK 库的步骤,您需要根据系统加载正确的模块。
引入
foxit_rdk
模块和uiextensions
模块:- 在
"PDFReader/entry/src/main/ets/pages/Index.ets"
文件中,添加如下的代码:
- 在
ts
// 引入 foxit_rdk 模块
import {FoxitRDKNative, PDFViewCtrl, PDFViewCtrlModel,} from 'foxit_rdk';
// 引入 uiextensions模块
import {UIExtensionsManager, UIExtensionsComponent} from 'uiextensions';
提示
- OpenHarmony 的 SDK 库仅支持 PDFViewCtrl 模块。 如果您正使用该库,则不需要引入 uiextensions 模块。
- 初始化 SDK 库:
ts
let sn: string = 'sn'
let key: string = 'key'
FoxitRDKNative.common.Library.Initialize(sn, key);
提示
- 参数 "sn" 的值在
**_sn.txt
文件中 ("SN=" 后面的字符串),"key" 的值在**_key.txt
文件中 ("Sign=" 后面的字符串)。
添加 PDF 文件到沙盒目录
PDF 文件的管理由应用层自己实现。应用层可以创建一个文件列表来管理文件,详细信息可参考鸿蒙系统的接口手册。本示例为了简单起见,将 PDF 文件拷贝到沙盒目录下。 操作步骤:
- 将 PDF 文件,比如 "Sample.pdf" 拷贝到
"PDFReader/entry/src/main/resources/rawfile"
文件夹下。 - 在
"PDFReader/entry/src/main/ets/pages/Index.ets"
文件中添加如下的代码:
ts
import fs from '@ohos.file.fs';
let context = getContext(this)
let filesDir = context.filesDir;
let fileName = "Sample.pdf";
let openDocPath = filesDir + "/" + fileName;
export async function copy_rawfile__to_sandbox(cb: (error: Error) => void) {
try {
let sss = fs.createStreamSync(openDocPath, "w");
sss.closeSync();
// 获取 rawfile 目录下的 Sample.pdf
context.resourceManager.getRawFd('rawfile/' + fileName, async (error, value) => {
if (error != null) { // getRawFileDescriptor 运行失败
console.log("[rawfile_copy_to_sandbox] 未能成功将rawfile目录下的Sample.pdf文件拷贝到应用沙盒目录下");
} else { // getRawFileDescriptor 运行成功
let fd = value.fd;
console.log("LXG fd:" + fd)
console.log("LXG fileDir:" + filesDir)
// 打开文件流
let inputStream = fs.fdopenStreamSync(fd, "r+");
console.log("LXG inputStream:")
let outputStream = fs.createStreamSync(openDocPath, "w+");
// 以流的形式读取源文件内容并写入目标文件
let bufSize = value.length;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
class Option {
public offset: number = 0;
public length: number = bufSize;
}
let option = new Option();
option.offset = value.offset;
let readLen = await inputStream.read(buf, option);
readSize += readLen;
while (readLen > 0) {
await outputStream.write(buf);
option.offset = readSize + value.offset;
option.length = value.length - readSize >= bufSize ? bufSize : value.length - readSize;
readLen = await inputStream.read(buf, option);
readSize += readLen;
}
// 关闭文件流
inputStream.closeSync();
outputStream.closeSync();
}
return cb(error);
});
} catch (error) {
console.log("[rawfile_copy_to_sandbox] 未能成功将 rawfile 目录下的 Sample.pdf 文件拷贝到应用沙盒目录下");
return cb(error);
}
return cb(new Error());
}
使用 PDFViewCtrl 组件显示 PDF 文档
到目前为止,我们已经在 PDFReader
示例工程中集成和初始化了 SDK,并且添加了 PDF 文档。现在,我们将使用 PDFViewCtrl 组件,通过几行代码来显示一个 PDF 文档。
操作步骤:
- 在
"PDFReader/entry/src/main/ets/pages/Index.ets"
文件中添加如下的代码:
ts
@Entry
@Component
struct
Index
{
@State
message
:
string = 'Open Doc';
@State
clickID
:
number = 0;
@State
pdfViewCtrlModel
:
PDFViewCtrlModel = new PDFViewCtrlModel(getContext(this))
public
openDoc()
{
this.pdfViewCtrlModel.openDoc(openDocPath, '', (error) => {
if (error == FoxitRDKNative.common.ErrorCode.e_ErrSuccess) {
this.clickID = 1;
}
})
}
build()
{
Row()
{
Column()
{
if (this.clickID == 0) {
Button(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let res = fs.accessSync(openDocPath);
if (!res) {
copy_rawfile__to_sandbox((error) => {
if (error == null) {
try {
this.openDoc()
} catch (err) {
console.info(` fail callback, code: ${err.code}, msg: ${err.msg}`)
}
}
})
} else {
this.openDoc()
}
})
} else {
PDFViewCtrl({model: this.pdfViewCtrlModel})
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
}
}
.
width('100%')
}
.
height('100%')
}
}
当前,"PDFReader/entry/src/main/ets/pages/Index.ets"
的全部代码如下所示:
ts
import {FoxitRDKNative, PDFViewCtrl, PDFViewCtrlModel,} from 'foxit_rdk';
import fs from '@ohos.file.fs';
// 初始化SDK库
let sn: string = ''
let key: string = ''
FoxitRDKNative.common.Library.Initialize(sn, key);
// 将PDF文档拷贝到应用沙盒目录下
let context = getContext(this)
let filesDir = context.filesDir;
let fileName = "sample.pdf";
let openDocPath = filesDir + "/" + fileName;
export async function copy_rawfile__to_sandbox(cb: (error: Error) => void) {
try {
let sss = fs.createStreamSync(openDocPath, "w");
sss.closeSync();
// 获取rawfile目录下的 "sample.pdf"
context.resourceManager.getRawFd('rawfile/' + fileName, async (error, value) => {
if (error != null) { // getRawFileDescriptor运行失败
console.log("[rawfile_copy_to_sandbox] 未能成功将rawfile目录下的 sample.pdf文件拷贝到应用沙盒目录下");
} else { // getRawFileDescriptor运行成功
let fd = value.fd;
console.log("LXG fd:" + fd)
console.log("LXG fileDir:" + filesDir)
// 打开文件流
let inputStream = fs.fdopenStreamSync(fd, "r+");
console.log("LXG inputStream:")
let outputStream = fs.createStreamSync(openDocPath, "w+");
// 以流的形式读取源文件内容并写入目标文件
let bufSize = value.length;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
class Option {
public offset: number = 0;
public length: number = bufSize;
}
let option = new Option();
option.offset = value.offset;
let readLen = await inputStream.read(buf, option);
readSize += readLen;
while (readLen > 0) {
await outputStream.write(buf);
option.offset = readSize + value.offset;
option.length = value.length - readSize >= bufSize ? bufSize : value.length - readSize;
readLen = await inputStream.read(buf, option);
readSize += readLen;
}
// 关闭文件流
inputStream.closeSync();
outputStream.closeSync();
}
return cb(error);
});
} catch (error) {
console.log("[rawfile_copy_to_sandbox] 未能成功将rawfile目录下的 sample.pdf文件拷贝到应用沙盒目录下");
return cb(error);
}
return cb(new Error());
}
// 使用PDFViewCtrl打开一个PDF文档
@Entry
@Component
struct
Index
{
@State
message
:
string = 'Open Doc';
@State
clickID
:
number = 0;
@State
pdfViewCtrlModel
:
PDFViewCtrlModel = new PDFViewCtrlModel(getContext(this))
public
openDoc()
{
this.pdfViewCtrlModel.openDoc(openDocPath, '', (error) => {
if (error == FoxitRDKNative.common.ErrorCode.e_ErrSuccess) {
this.clickID = 1;
}
})
}
build()
{
Row()
{
Column()
{
if (this.clickID == 0) {
Button(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let res = fs.accessSync(openDocPath);
if (!res) {
copy_rawfile__to_sandbox((error) => {
if (error == null) {
try {
this.openDoc()
} catch (err) {
console.info(` fail callback, code: ${err.code}, msg: ${err.msg}`)
}
}
})
} else {
this.openDoc()
}
})
} else {
PDFViewCtrl({model: this.pdfViewCtrlModel})
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
}
}
.
width('100%')
}
.
height('100%')
}
}
如果您使用华为模拟器来运行该示例工程。运行成功后,点击 OpenDoc
按钮,即可看到 Sample.pdf
文档显示如下图所示 。
提示
- PDFViewCtrl 是视图控制组件,仅提供一些基本功能, 比如,显示 PDF 文档、放大、缩小,及翻页等。
使用 UI Extensions 组件构建一个功能丰富的 PDF 阅读器
福昕 PDF SDK 鸿蒙版提供内置的 UI 设计,包含应用程序基础 UI 和功能模块 UI。这些内置 UI 通过 SDK 鸿蒙版实现,并封装在 UI Extensions
组件中。因此,构建功能完善的 PDF 阅读器变得非常简单,您只需实例化一个 UIExtensionsManager
对象。
操作步骤:
- 在
"PDFReader/entry/src/main/ets/pages/Index.ets"
文件中添加如下的代码:
ts
@Entry
@Component
struct
Index
{
@State
message
:
string = 'Open Doc';
@State
clickID
:
number = 0;
@State
uiextensionsManager
:
UIExtensionsManager = new UIExtensionsManager(getContext(this));
public
openDoc()
{
this.uiextensionsManager.openDocument(openDocPath, '', (error: number) => {
if (error == FoxitRDKNative.common.ErrorCode.e_ErrSuccess) {
this.clickID = 1;
}
})
}
build()
{
Row()
{
Column()
{
if (this.clickID == 0) {
Button(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let res = fs.accessSync(openDocPath);
if (!res) {
copy_rawfile__to_sandbox((error) => {
if (error == null) {
try {
this.openDoc()
} catch (err) {
console.info(` fail callback, code: ${err.code}, msg: ${err.msg}`)
}
}
})
} else {
this.openDoc()
}
})
} else {
UIExtensionsComponent({uiExtMgr: this.uiextensionsManager})
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
}
}
.
width('100%')
}
.
height('100%')
}
}
当前,"PDFReader/entry/src/main/ets/pages/Index.ets"
的全部代码如下所示:
ts
import {FoxitRDKNative, PDFViewCtrl, PDFViewCtrlModel,} from 'foxit_rdk';
import {UIExtensionsManager, UIExtensionsComponent} from 'uiextensions';
import fs from '@ohos.file.fs';
// 初始化SDK库
let sn: string = ''
let key: string = ''
FoxitRDKNative.common.Library.Initialize(sn, key);
// 将PDF文档拷贝到应用沙盒目录下
let context = getContext(this)
let filesDir = context.filesDir;
let fileName = "sample.pdf";
let openDocPath = filesDir + "/" + fileName;
export async function copy_rawfile__to_sandbox(cb: (error: Error) => void) {
try {
let sss = fs.createStreamSync(openDocPath, "w");
sss.closeSync();
// 获取rawfile目录下的 "sample.pdf"
context.resourceManager.getRawFd('rawfile/' + fileName, async (error, value) => {
if (error != null) { // getRawFileDescriptor运行失败
console.log("[rawfile_copy_to_sandbox] 未能成功将rawfile目录下的 sample.pdf文件拷贝到应用沙盒目录下");
} else { // getRawFileDescriptor运行成功
let fd = value.fd;
console.log("LXG fd:" + fd)
console.log("LXG fileDir:" + filesDir)
// 打开文件流
let inputStream = fs.fdopenStreamSync(fd, "r+");
console.log("LXG inputStream:")
let outputStream = fs.createStreamSync(openDocPath, "w+");
// 以流的形式读取源文件内容并写入目标文件
let bufSize = value.length;
let readSize = 0;
let buf = new ArrayBuffer(bufSize);
class Option {
public offset: number = 0;
public length: number = bufSize;
}
let option = new Option();
option.offset = value.offset;
let readLen = await inputStream.read(buf, option);
readSize += readLen;
while (readLen > 0) {
await outputStream.write(buf);
option.offset = readSize + value.offset;
option.length = value.length - readSize >= bufSize ? bufSize : value.length - readSize;
readLen = await inputStream.read(buf, option);
readSize += readLen;
}
// 关闭文件流
inputStream.closeSync();
outputStream.closeSync();
}
return cb(error);
});
} catch (error) {
console.log("[rawfile_copy_to_sandbox] 未能成功将rawfile目录下的 sample.pdf文件拷贝到应用沙盒目录下");
return cb(error);
}
return cb(new Error());
}
// 使用UI Extensions组件构建一个功能丰富的PDF阅读器
@Entry
@Component
struct
Index
{
@State
message
:
string = 'Open Doc';
@State
clickID
:
number = 0;
@State
uiextensionsManager
:
UIExtensionsManager = new UIExtensionsManager(getContext(this));
public
openDoc()
{
this.uiextensionsManager.openDocument(openDocPath, '', (error: number) => {
if (error == FoxitRDKNative.common.ErrorCode.e_ErrSuccess) {
this.clickID = 1;
}
})
}
build()
{
Row()
{
Column()
{
if (this.clickID == 0) {
Button(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let res = fs.accessSync(openDocPath);
if (!res) {
copy_rawfile__to_sandbox((error) => {
if (error == null) {
try {
this.openDoc()
} catch (err) {
console.info(` fail callback, code: ${err.code}, msg: ${err.msg}`)
}
}
})
} else {
this.openDoc()
}
})
} else {
UIExtensionsComponent({uiExtMgr: this.uiextensionsManager})
.width('100%')
.height('100%')
.backgroundColor(Color.Black)
}
}
.
width('100%')
}
.
height('100%')
}
}
如果您使用华为模拟器来运行该示例工程。运行成功后,点击 打开文档
按钮,即可看到 Sample.pdf
文档显示如下图所示 。至此,您已经成功构建了一个功能齐全的 PDF 阅读器。 功能支持详情,请参阅 福昕 PDF SDK 移动版主要功能列表。