鸿蒙学习实战之路-PDF页眉页脚添加与删除最佳实践
最近好多朋友问我:“西兰花啊,我想给PDF文档加页眉页脚,咋搞?有时候加错了想删除,又不知道咋弄?” 害,这问题我太熟了!今天我就手把手带你搞定PDF页眉页脚的添加和删除,这可是PDF文档格式化的重要操作,学会了这个,你的文档瞬间变得专业起来~
·
鸿蒙学习实战之路-PDF页眉页脚添加与删除最佳实践
最近好多朋友问我:“西兰花啊,我想给PDF文档加页眉页脚,咋搞?有时候加错了想删除,又不知道咋弄?” 害,这问题我太熟了!今天我就手把手带你搞定PDF页眉页脚的添加和删除,这可是PDF文档格式化的重要操作,学会了这个,你的文档瞬间变得专业起来~
一、页眉页脚,核心接口有哪些?
PDF Kit提供了两个核心接口来处理页眉页脚:
| 接口名 | 功能描述 |
|---|---|
| addHeaderFooter | 插入PDF文档页眉页脚 |
| removeHeaderFooter | 删除PDF文档页眉页脚 |
这两个接口就是咱们今天要摆弄的"装饰工具",用好了,PDF文档任你美化~
二、添加页眉页脚,让文档更专业
1. 基本用法
import { pdfService } from '@kit.PDFKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { Font } from '@kit.ArkUI';
@Entry
@Component
struct PdfPage {
private pdfDocument: pdfService.PdfDocument = new pdfService.PdfDocument();
private context = this.getUIContext().getHostContext() as Context;
build() {
Column() {
// 添加页眉页脚
Button('添加页眉页脚')
.onClick(async () => {
// 确保沙箱目录有input.pdf文档
let filePath = this.context.filesDir + '/input.pdf';
let loadResult = this.pdfDocument.loadDocument(filePath);
if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
// 创建页眉页脚信息对象
let headerFooterInfo: pdfService.HeaderFooterInfo = new pdfService.HeaderFooterInfo();
// 设置字体信息
headerFooterInfo.fontInfo = new pdfService.FontInfo();
let font: Font = new Font();
headerFooterInfo.fontInfo.fontPath = font.getFontByName('HarmonyOS Sans')?.path;
headerFooterInfo.fontInfo.fontName = ''; // 如果不知道字体名称,设为空
// 设置字体大小和颜色
headerFooterInfo.textSize = 10;
headerFooterInfo.charset = pdfService.CharsetType.PDF_FONT_DEFAULT_CHARSET;
headerFooterInfo.underline = false;
headerFooterInfo.textColor = 0x00000000; // 黑色
// 设置边距
headerFooterInfo.leftMargin = 1.0;
headerFooterInfo.topMargin = 40.0;
headerFooterInfo.rightMargin = 1.0;
headerFooterInfo.bottomMargin = 40.0;
// 设置页眉页脚内容
// <<dd.mm.yyyy>> 是日期占位符,<<1/n>> 是页码占位符
headerFooterInfo.headerLeftText = '左侧页眉 <<dd.mm.yyyy>> <<1/n>>';
headerFooterInfo.headerCenterText = '居中页眉 <<m/d/yyyy>> <<1/n>>';
headerFooterInfo.headerRightText = '右侧页眉 <<m/d>><<1>>';
headerFooterInfo.footerLeftText = '左侧页脚 <<m/d>><<1>>';
headerFooterInfo.footerCenterText = '居中页脚 <<m/d>><<1>>';
headerFooterInfo.footerRightText = '右侧页脚 <<dd.mm.yyyy>><<1>>';
// 添加页眉页脚到第1-5页,包括奇数页和偶数页
this.pdfDocument.addHeaderFooter(headerFooterInfo, 1, 5, true, true);
// 保存文档
let outPdfPath = this.context.filesDir + '/testAddHeaderFooter.pdf';
let saveResult = this.pdfDocument.saveDocument(outPdfPath);
console.log('添加页眉页脚结果:', saveResult ? '成功' : '失败');
}
// 释放资源
this.pdfDocument.releaseDocument();
})
}
}
}
2. 实际应用场景
在实际开发中,我们通常会根据不同的需求设置不同的页眉页脚:
// 根据文档类型设置不同的页眉页脚
function setupHeaderFooter(pdfDoc: pdfService.PdfDocument, docType: string) {
let headerFooterInfo: pdfService.HeaderFooterInfo = new pdfService.HeaderFooterInfo();
// 基础设置
headerFooterInfo.fontInfo = new pdfService.FontInfo();
let font: Font = new Font();
headerFooterInfo.fontInfo.fontPath = font.getFontByName('HarmonyOS Sans')?.path;
headerFooterInfo.textSize = 10;
headerFooterInfo.textColor = 0x00000000;
// 根据文档类型设置不同内容
switch (docType) {
case 'report':
headerFooterInfo.headerCenterText = '工作报告';
headerFooterInfo.footerRightText = '<<dd.mm.yyyy>> 第 <<1>> 页';
break;
case 'contract':
headerFooterInfo.headerCenterText = '合同文档';
headerFooterInfo.footerRightText = '机密文档 <<1/n>>';
break;
case 'manual':
headerFooterInfo.headerCenterText = '操作手册';
headerFooterInfo.footerRightText = '版本 1.0 <<1/n>>';
break;
}
// 添加到所有页面
let pageCount = pdfDoc.getPageCount();
pdfDoc.addHeaderFooter(headerFooterInfo, 1, pageCount, true, true);
}
三、删除页眉页脚,一键搞定
1. 基本用法
// 删除页眉页脚
Button('删除页眉页脚')
.onClick(async () => {
let filePath = this.context.filesDir + '/testAddHeaderFooter.pdf';
let loadResult = this.pdfDocument.loadDocument(filePath);
if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
// 检查是否有页眉页脚
if (this.pdfDocument.hasHeaderFooter()) {
// 删除页眉页脚
let removeResult = this.pdfDocument.removeHeaderFooter();
if (removeResult) {
// 保存文档
let outPdfPath = this.context.filesDir + '/removeHeaderFooter.pdf';
let saveResult = this.pdfDocument.saveDocument(outPdfPath);
console.log('删除页眉页脚结果:', saveResult ? '成功' : '失败');
} else {
console.log('删除页眉页脚失败');
}
} else {
console.log('文档没有页眉页脚');
}
}
// 释放资源
this.pdfDocument.releaseDocument();
})
🥦 西兰花警告
- 性能问题:addHeaderFooter方法属于耗时操作,需要遍历每一页添加页眉页脚,页面较多时一定要放到线程里处理,不然会阻塞UI线程!
- 字体路径:字体路径一定要正确,不然会导致页眉页脚显示异常或者添加失败!
- 边距设置:边距设置要合理,太小会导致页眉页脚与正文重叠,太大会浪费页面空间!
- 资源释放:操作完文档一定要记得调用releaseDocument(),不然会内存泄漏!
- 占位符格式:日期和页码占位符要使用正确的格式,比如<<dd.mm.yyyy>>表示日期,<<1/n>>表示页码!
🥦 西兰花小贴士
- 批量处理:添加页眉页脚时,可以指定起始页和结束页,实现部分页面添加
- 奇偶页不同:通过设置oddPages和evenPages参数,可以为奇数页和偶数页设置不同的页眉页脚
- 线程处理:处理大量页面时,建议使用Worker线程,避免阻塞UI
- 预览效果:添加页眉页脚前,最好先预览一下效果,避免反复修改
- 样式统一:保持页眉页脚的样式与文档整体风格一致,增强专业性
四、常见问题与解决方案
1. 添加页眉页脚时应用卡死
问题:调用addHeaderFooter后,应用卡死不动
解决方案:这是因为添加页眉页脚是耗时操作,需要放到线程里处理:
// 正确写法
import { worker } from '@kit.WorkerKit';
// 创建Worker来处理耗时操作
async function addHeaderFooterInWorker(filePath: string) {
return new Promise((resolve, reject) => {
const headerFooterWorker = worker.createWorker('headerFooterWorker.ts');
headerFooterWorker.onmessage = (message) => {
resolve(message);
headerFooterWorker.terminate();
};
headerFooterWorker.onerror = (error) => {
reject(error);
headerFooterWorker.terminate();
};
headerFooterWorker.postMessage({ filePath });
});
}
2. 页眉页脚与正文重叠
问题:添加的页眉页脚与文档正文重叠了
解决方案:增大边距设置:
// 正确写法
headerFooterInfo.topMargin = 50.0; // 增大页眉边距
headerFooterInfo.bottomMargin = 50.0; // 增大页脚边距
3. 页码占位符不显示
问题:设置了<<1/n>>占位符,但生成的PDF中显示的还是<<1/n>>,不是实际页码
解决方案:确保占位符格式正确,不要有多余的空格:
// 正确写法
headerFooterInfo.footerRightText = '第 <<1>> 页,共 <<n>> 页';
// 或者
headerFooterInfo.footerRightText = '<<1/n>>';
五、完整代码示例
import { pdfService } from '@kit.PDFKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { Font } from '@kit.ArkUI';
@Entry
@Component
struct PdfHeaderFooterExample {
private pdfDocument: pdfService.PdfDocument = new pdfService.PdfDocument();
private context = this.getUIContext().getHostContext() as Context;
// 添加页眉页脚
async addHeaderFooter() {
try {
let filePath = this.context.filesDir + '/input.pdf';
let loadResult = this.pdfDocument.loadDocument(filePath);
if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
// 创建页眉页脚信息
let headerFooterInfo: pdfService.HeaderFooterInfo = new pdfService.HeaderFooterInfo();
// 设置字体
headerFooterInfo.fontInfo = new pdfService.FontInfo();
let font: Font = new Font();
headerFooterInfo.fontInfo.fontPath = font.getFontByName('HarmonyOS Sans')?.path;
headerFooterInfo.fontInfo.fontName = '';
// 设置样式
headerFooterInfo.textSize = 10;
headerFooterInfo.charset = pdfService.CharsetType.PDF_FONT_DEFAULT_CHARSET;
headerFooterInfo.underline = false;
headerFooterInfo.textColor = 0x00000000;
// 设置边距
headerFooterInfo.leftMargin = 1.0;
headerFooterInfo.topMargin = 40.0;
headerFooterInfo.rightMargin = 1.0;
headerFooterInfo.bottomMargin = 40.0;
// 设置内容
headerFooterInfo.headerLeftText = '公司名称';
headerFooterInfo.headerCenterText = '文档标题';
headerFooterInfo.headerRightText = '<<dd.mm.yyyy>>';
headerFooterInfo.footerLeftText = '版权所有';
headerFooterInfo.footerCenterText = 'CONFIDENTIAL';
headerFooterInfo.footerRightText = '第 <<1>> 页,共 <<n>> 页';
// 获取页面总数
let pageCount = this.pdfDocument.getPageCount();
// 添加页眉页脚到所有页面
this.pdfDocument.addHeaderFooter(headerFooterInfo, 1, pageCount, true, true);
// 保存文档
let outPdfPath = this.context.filesDir + '/withHeaderFooter.pdf';
let saveResult = this.pdfDocument.saveDocument(outPdfPath);
console.log('添加页眉页脚结果:', saveResult ? '成功' : '失败');
return saveResult;
} else {
console.log('加载PDF失败');
return false;
}
} catch (error) {
console.error('添加页眉页脚时出错:', error);
return false;
} finally {
this.pdfDocument.releaseDocument();
}
}
// 删除页眉页脚
async removeHeaderFooter() {
try {
let filePath = this.context.filesDir + '/withHeaderFooter.pdf';
let loadResult = this.pdfDocument.loadDocument(filePath);
if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
// 检查是否有页眉页脚
if (this.pdfDocument.hasHeaderFooter()) {
// 删除页眉页脚
let removeResult = this.pdfDocument.removeHeaderFooter();
if (removeResult) {
// 保存文档
let outPdfPath = this.context.filesDir + '/withoutHeaderFooter.pdf';
let saveResult = this.pdfDocument.saveDocument(outPdfPath);
console.log('删除页眉页脚结果:', saveResult ? '成功' : '失败');
return saveResult;
} else {
console.log('删除页眉页脚失败');
return false;
}
} else {
console.log('文档没有页眉页脚');
return true;
}
} else {
console.log('加载PDF失败');
return false;
}
} catch (error) {
console.error('删除页眉页脚时出错:', error);
return false;
} finally {
this.pdfDocument.releaseDocument();
}
}
build() {
Column() {
Button('添加页眉页脚')
.margin(10)
.onClick(async () => {
let result = await this.addHeaderFooter();
if (result) {
console.log('操作成功');
} else {
console.log('操作失败');
}
})
Button('删除页眉页脚')
.margin(10)
.onClick(async () => {
let result = await this.removeHeaderFooter();
if (result) {
console.log('操作成功');
} else {
console.log('操作失败');
}
})
}
.padding(20)
}
}
五、实际应用案例
案例1:批量文档处理工具
在批量文档处理工具中,我们可以为多个PDF文档添加统一的页眉页脚:
import { pdfService } from '@kit.PDFKit';
import { worker } from '@kit.WorkerKit';
@Entry
@Component
struct BatchPdfProcessor {
private context = this.getUIContext().getHostContext() as Context;
// 批量添加页眉页脚
async batchAddHeaderFooter(filePaths: string[]) {
for (let filePath of filePaths) {
console.log(`正在处理: ${filePath}`);
// 使用Worker处理耗时操作
await this.addHeaderFooterInWorker(filePath);
}
console.log('批量处理完成');
}
// 在Worker中处理
async addHeaderFooterInWorker(filePath: string) {
// 实现Worker处理逻辑
// ...
}
build() {
Column() {
Button('批量添加页眉页脚')
.onClick(async () => {
// 假设这里获取了多个PDF文件路径
let filePaths = [
this.context.filesDir + '/doc1.pdf',
this.context.filesDir + '/doc2.pdf',
this.context.filesDir + '/doc3.pdf'
];
await this.batchAddHeaderFooter(filePaths);
})
}
}
}
案例2:文档模板生成器
在文档模板生成器中,我们可以根据用户选择的模板添加不同的页眉页脚:
import { pdfService } from '@kit.PDFKit';
@Entry
@Component
struct DocumentTemplateGenerator {
private pdfDocument: pdfService.PdfDocument = new pdfService.PdfDocument();
private context = this.getUIContext().getHostContext() as Context;
@State selectedTemplate: string = 'report';
// 根据模板设置页眉页脚
async generateDocumentWithTemplate(template: string) {
try {
let filePath = this.context.filesDir + '/template.pdf';
let loadResult = this.pdfDocument.loadDocument(filePath);
if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
let headerFooterInfo: pdfService.HeaderFooterInfo = new pdfService.HeaderFooterInfo();
// 设置基础样式
headerFooterInfo.textSize = 10;
headerFooterInfo.textColor = 0x00000000;
// 根据模板设置内容
switch (template) {
case 'report':
headerFooterInfo.headerCenterText = '工作报告';
headerFooterInfo.footerRightText = '<<dd.mm.yyyy>> <<1/n>>';
break;
case 'contract':
headerFooterInfo.headerCenterText = '合同文档';
headerFooterInfo.footerRightText = '机密 <<1/n>>';
break;
case 'invoice':
headerFooterInfo.headerCenterText = '发票';
headerFooterInfo.footerRightText = '编号: INV-<<1>>';
break;
}
// 添加到所有页面
let pageCount = this.pdfDocument.getPageCount();
this.pdfDocument.addHeaderFooter(headerFooterInfo, 1, pageCount, true, true);
// 保存文档
let outPdfPath = this.context.filesDir + `/generated_${template}.pdf`;
let saveResult = this.pdfDocument.saveDocument(outPdfPath);
console.log('生成文档结果:', saveResult ? '成功' : '失败');
}
} catch (error) {
console.error('生成文档时出错:', error);
} finally {
this.pdfDocument.releaseDocument();
}
}
build() {
Column() {
Text('选择文档模板')
.fontSize(16)
.margin(10)
RadioGroup({
value: this.selectedTemplate
}) {
Radio({ value: 'report' })
.group('template')
.onChange((isChecked: boolean) => {
if (isChecked) {
this.selectedTemplate = 'report';
}
})
Text('工作报告')
.marginLeft(10)
Radio({ value: 'contract' })
.group('template')
.onChange((isChecked: boolean) => {
if (isChecked) {
this.selectedTemplate = 'contract';
}
})
Text('合同文档')
.marginLeft(10)
Radio({ value: 'invoice' })
.group('template')
.onChange((isChecked: boolean) => {
if (isChecked) {
this.selectedTemplate = 'invoice';
}
})
Text('发票')
.marginLeft(10)
}
.margin(10)
Button('生成文档')
.margin(10)
.onClick(async () => {
await this.generateDocumentWithTemplate(this.selectedTemplate);
})
}
.padding(20)
}
}
六、实用资源推荐
📚 推荐资料:
我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦
更多推荐

所有评论(0)