鸿蒙学习实战之路-PDF水印添加与删除最佳实践

最近好多朋友问我:“西兰花啊,我想给PDF文档加水印,咋搞?有时候加了水印又想删掉,这能行吗?” 害,这问题我太熟了!今天我就手把手带你搞定PDF水印的添加和删除,从文本水印到图片水印,全部给你安排得明明白白~

一、水印,核心接口有哪些?

PDF Kit提供了两个核心接口来处理水印:

接口名 功能描述
addWatermark 插入水印到PDF文档
removeWatermark 删除PDF文档水印

这两个接口就是咱们今天要摆弄的"印章工具",用好了,PDF文档任你盖章~

二、添加文本水印,让文档更专业

1. 基本用法

import { pdfService } from '@kit.PDFKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { Font } from '@kit.ArkUI';

@Entry
@Component
struct PdfWatermarkExample {
  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 watermarkInfo: pdfService.TextWatermarkInfo = new pdfService.TextWatermarkInfo();
            
            // 设置水印类型
            watermarkInfo.watermarkType = pdfService.WatermarkType.WATERMARK_TEXT;
            
            // 设置水印内容
            watermarkInfo.content = '这是水印';
            
            // 设置字体信息
            watermarkInfo.fontInfo = new pdfService.FontInfo();
            let font: Font = new Font();
            watermarkInfo.fontInfo.fontPath = font.getFontByName('HarmonyOS Sans').path;
            
            // 设置水印样式
            watermarkInfo.textSize = 30;
            watermarkInfo.textColor = 200;
            watermarkInfo.opacity = 0.5;
            watermarkInfo.isOnTop = true;
            watermarkInfo.rotation = 45;
            watermarkInfo.scale = 1.5;
            
            // 设置水印位置
            watermarkInfo.verticalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_TOP;
            watermarkInfo.horizontalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_LEFT;
            watermarkInfo.horizontalSpace = 1.0;
            watermarkInfo.verticalSpace = 1.0;
            
            // 添加水印到第1-5页,包括奇数页和偶数页
            this.pdfDocument.addWatermark(watermarkInfo, 0, 5, true, true);
            
            // 保存文档
            let outPdfPath = this.context.filesDir + '/testTextWatermark.pdf';
            let saveResult = this.pdfDocument.saveDocument(outPdfPath);
            
            console.log('添加文本水印结果:', saveResult ? '成功' : '失败');
          }
          
          // 释放资源
          this.pdfDocument.releaseDocument();
        })
    }
  }
}

2. 实际应用场景

在实际开发中,我们通常会根据不同的需求设置不同的文本水印:

// 根据文档类型设置不同的文本水印
function setupTextWatermark(pdfDoc: pdfService.PdfDocument, docType: string) {
  let watermarkInfo: pdfService.TextWatermarkInfo = new pdfService.TextWatermarkInfo();
  
  // 基础设置
  watermarkInfo.watermarkType = pdfService.WatermarkType.WATERMARK_TEXT;
  watermarkInfo.fontInfo = new pdfService.FontInfo();
  let font: Font = new Font();
  watermarkInfo.fontInfo.fontPath = font.getFontByName('HarmonyOS Sans').path;
  watermarkInfo.textSize = 30;
  watermarkInfo.opacity = 0.5;
  watermarkInfo.rotation = 45;
  
  // 根据文档类型设置不同内容
  switch (docType) {
    case 'confidential':
      watermarkInfo.content = '机密文档';
      watermarkInfo.textColor = 0xFF0000; // 红色
      break;
    case 'draft':
      watermarkInfo.content = '草稿';
      watermarkInfo.textColor = 0x0000FF; // 蓝色
      break;
    case 'sample':
      watermarkInfo.content = '样本';
      watermarkInfo.textColor = 0x008000; // 绿色
      break;
  }
  
  // 添加到所有页面
  let pageCount = pdfDoc.getPageCount();
  pdfDoc.addWatermark(watermarkInfo, 0, pageCount, true, true);
}

三、添加图片水印,让文档更个性化

1. 基本用法

// 添加图片水印
Button('添加图片水印')
  .onClick(async () => {
    let filePath = this.context.filesDir + '/input.pdf';
    let loadResult = this.pdfDocument.loadDocument(filePath);
    
    if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
      // 创建图片水印信息对象
      let watermarkInfo: pdfService.ImageWatermarkInfo = new pdfService.ImageWatermarkInfo();
      
      // 设置水印类型
      watermarkInfo.watermarkType = pdfService.WatermarkType.WATERMARK_IMAGE;
      
      // 设置图片路径
      watermarkInfo.imagePath = this.context.filesDir + '/img.jpg';
      
      // 设置水印样式
      watermarkInfo.opacity = 0.5;
      watermarkInfo.isOnTop = true;
      watermarkInfo.rotation = 45;
      watermarkInfo.scale = 0.5;
      
      // 设置水印位置
      watermarkInfo.verticalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_TOP;
      watermarkInfo.horizontalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_LEFT;
      watermarkInfo.horizontalSpace = 1.0;
      watermarkInfo.verticalSpace = 1.0;
      
      // 添加水印到第1-5页
      this.pdfDocument.addWatermark(watermarkInfo, 0, 5, true, true);
      
      // 保存文档
      let outPdfPath = this.context.filesDir + '/testImageWatermark.pdf';
      let saveResult = this.pdfDocument.saveDocument(outPdfPath);
      
      console.log('添加图片水印结果:', saveResult ? '成功' : '失败');
    }
    
    // 释放资源
    this.pdfDocument.releaseDocument();
  })

四、删除水印,一键搞定

1. 基本用法

// 删除水印
Button('删除水印')
  .onClick(async () => {
    let filePath = this.context.filesDir + '/testTextWatermark.pdf';
    let loadResult = this.pdfDocument.loadDocument(filePath);
    
    if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
      // 检查是否有水印
      if (this.pdfDocument.hasWatermark()) {
        // 删除水印
        let removeResult = this.pdfDocument.removeWatermark();
        
        if (removeResult) {
          // 保存文档
          let outPdfPath = this.context.filesDir + '/removeWatermark.pdf';
          let saveResult = this.pdfDocument.saveDocument(outPdfPath);
          console.log('删除水印结果:', saveResult ? '成功' : '失败');
        } else {
          console.log('删除水印失败');
        }
      } else {
        console.log('文档没有水印');
      }
    }
    
    // 释放资源
    this.pdfDocument.releaseDocument();
  })

🥦 西兰花警告

  • 性能问题:addWatermark方法属于耗时操作,需要遍历每一页添加水印,页面较多时一定要放到线程里处理,不然会阻塞UI线程!
  • 字体路径:添加文本水印时,字体路径一定要正确,不然会导致水印显示异常或者添加失败!
  • 图片格式:添加图片水印时,要确保图片格式受支持,不然会添加失败!
  • 资源释放:操作完文档一定要记得调用releaseDocument(),不然会内存泄漏!
  • 页面范围:添加水印时,要注意设置正确的页面范围,避免水印添加到不需要的页面!

🥦 西兰花小贴士

  • 批量处理:添加水印时,可以指定起始页和结束页,实现部分页面添加
  • 奇偶页不同:通过设置oddPages和evenPages参数,可以为奇数页和偶数页设置不同的水印
  • 线程处理:处理大量页面时,建议使用Worker线程,避免阻塞UI
  • 预览效果:添加水印前,最好先预览一下效果,避免反复修改
  • 样式统一:保持水印的样式与文档整体风格一致,增强专业性

五、常见问题与解决方案

1. 添加水印时应用卡死

问题:调用addWatermark后,应用卡死不动

解决方案:这是因为添加水印是耗时操作,需要放到线程里处理:

// 正确写法
import { worker } from '@kit.WorkerKit';

// 创建Worker来处理耗时操作
async function addWatermarkInWorker(filePath: string) {
  return new Promise((resolve, reject) => {
    const watermarkWorker = worker.createWorker('watermarkWorker.ts');
    
    watermarkWorker.onmessage = (message) => {
      resolve(message);
      watermarkWorker.terminate();
    };
    
    watermarkWorker.onerror = (error) => {
      reject(error);
      watermarkWorker.terminate();
    };
    
    watermarkWorker.postMessage({ filePath });
  });
}

2. 添加图片水印时失败

问题:调用addWatermark添加图片水印时,返回失败

解决方案:检查图片路径是否正确,图片格式是否受支持:

// 正确写法
let watermarkInfo: pdfService.ImageWatermarkInfo = new pdfService.ImageWatermarkInfo();
watermarkInfo.imagePath = this.context.filesDir + '/img.jpg'; // 确保图片存在

3. 删除水印时返回false

问题:调用removeWatermark时,返回false

解决方案:先检查文档是否有水印,再删除:

// 正确写法
if (this.pdfDocument.hasWatermark()) {
  let removeResult = this.pdfDocument.removeWatermark();
  if (removeResult) {
    // 保存文档
  }
}

六、完整代码示例

import { pdfService } from '@kit.PDFKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { Font } from '@kit.ArkUI';

@Entry
@Component
struct PdfWatermarkCompleteExample {
  private pdfDocument: pdfService.PdfDocument = new pdfService.PdfDocument();
  private context = this.getUIContext().getHostContext() as Context;

  // 添加文本水印
  async addTextWatermark() {
    try {
      let filePath = this.context.filesDir + '/input.pdf';
      let loadResult = this.pdfDocument.loadDocument(filePath);
      
      if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
        let watermarkInfo: pdfService.TextWatermarkInfo = new pdfService.TextWatermarkInfo();
        watermarkInfo.watermarkType = pdfService.WatermarkType.WATERMARK_TEXT;
        watermarkInfo.content = '这是水印';
        
        // 设置字体
        watermarkInfo.fontInfo = new pdfService.FontInfo();
        let font: Font = new Font();
        watermarkInfo.fontInfo.fontPath = font.getFontByName('HarmonyOS Sans').path;
        
        // 设置样式
        watermarkInfo.textSize = 30;
        watermarkInfo.textColor = 200;
        watermarkInfo.opacity = 0.5;
        watermarkInfo.isOnTop = true;
        watermarkInfo.rotation = 45;
        watermarkInfo.scale = 1.5;
        
        // 设置位置
        watermarkInfo.verticalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_TOP;
        watermarkInfo.horizontalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_LEFT;
        watermarkInfo.horizontalSpace = 1.0;
        watermarkInfo.verticalSpace = 1.0;
        
        // 添加到所有页面
        let pageCount = this.pdfDocument.getPageCount();
        this.pdfDocument.addWatermark(watermarkInfo, 0, pageCount, true, true);
        
        // 保存文档
        let outPdfPath = this.context.filesDir + '/withTextWatermark.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 addImageWatermark() {
    try {
      let filePath = this.context.filesDir + '/input.pdf';
      let loadResult = this.pdfDocument.loadDocument(filePath);
      
      if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
        let watermarkInfo: pdfService.ImageWatermarkInfo = new pdfService.ImageWatermarkInfo();
        watermarkInfo.watermarkType = pdfService.WatermarkType.WATERMARK_IMAGE;
        watermarkInfo.imagePath = this.context.filesDir + '/img.jpg';
        
        // 设置样式
        watermarkInfo.opacity = 0.5;
        watermarkInfo.isOnTop = true;
        watermarkInfo.rotation = 45;
        watermarkInfo.scale = 0.5;
        
        // 设置位置
        watermarkInfo.verticalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_TOP;
        watermarkInfo.horizontalAlignment = pdfService.WatermarkAlignment.WATERMARK_ALIGNMENT_LEFT;
        watermarkInfo.horizontalSpace = 1.0;
        watermarkInfo.verticalSpace = 1.0;
        
        // 添加到所有页面
        let pageCount = this.pdfDocument.getPageCount();
        this.pdfDocument.addWatermark(watermarkInfo, 0, pageCount, true, true);
        
        // 保存文档
        let outPdfPath = this.context.filesDir + '/withImageWatermark.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 removeWatermark() {
    try {
      let filePath = this.context.filesDir + '/withTextWatermark.pdf';
      let loadResult = this.pdfDocument.loadDocument(filePath);
      
      if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
        // 检查是否有水印
        if (this.pdfDocument.hasWatermark()) {
          // 删除水印
          let removeResult = this.pdfDocument.removeWatermark();
          
          if (removeResult) {
            // 保存文档
            let outPdfPath = this.context.filesDir + '/withoutWatermark.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.addTextWatermark();
          if (result) {
            console.log('添加文本水印成功');
          } else {
            console.log('添加文本水印失败');
          }
        })

      Button('添加图片水印')
        .margin(10)
        .onClick(async () => {
          let result = await this.addImageWatermark();
          if (result) {
            console.log('添加图片水印成功');
          } else {
            console.log('添加图片水印失败');
          }
        })

      Button('删除水印')
        .margin(10)
        .onClick(async () => {
          let result = await this.removeWatermark();
          if (result) {
            console.log('删除水印成功');
          } else {
            console.log('删除水印失败');
          }
        })
    }
    .padding(20)
  }
}

七、实际应用案例

案例1:文档保护工具

在文档保护工具中,我们可以为敏感文档添加水印:

import { pdfService } from '@kit.PDFKit';
import { worker } from '@kit.WorkerKit';

@Entry
@Component
struct DocumentProtector {
  private context = this.getUIContext().getHostContext() as Context;

  // 批量添加水印
  async batchAddWatermark(filePaths: string[]) {
    for (let filePath of filePaths) {
      console.log(`正在处理: ${filePath}`);
      
      // 使用Worker处理耗时操作
      await this.addWatermarkInWorker(filePath);
    }
    
    console.log('批量处理完成');
  }

  // 在Worker中处理
  async addWatermarkInWorker(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.batchAddWatermark(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 = 'confidential';

  // 根据模板设置水印
  async generateDocumentWithWatermark(template: string) {
    try {
      let filePath = this.context.filesDir + '/template.pdf';
      let loadResult = this.pdfDocument.loadDocument(filePath);
      
      if (loadResult === pdfService.ParseResult.PARSE_SUCCESS) {
        let watermarkInfo: pdfService.TextWatermarkInfo = new pdfService.TextWatermarkInfo();
        watermarkInfo.watermarkType = pdfService.WatermarkType.WATERMARK_TEXT;
        
        // 根据模板设置水印内容
        switch (template) {
          case 'confidential':
            watermarkInfo.content = '机密文档';
            watermarkInfo.textColor = 0xFF0000; // 红色
            break;
          case 'draft':
            watermarkInfo.content = '草稿';
            watermarkInfo.textColor = 0x0000FF; // 蓝色
            break;
          case 'sample':
            watermarkInfo.content = '样本';
            watermarkInfo.textColor = 0x008000; // 绿色
            break;
        }
        
        // 设置其他属性
        watermarkInfo.opacity = 0.5;
        watermarkInfo.rotation = 45;
        
        // 添加到所有页面
        let pageCount = this.pdfDocument.getPageCount();
        this.pdfDocument.addWatermark(watermarkInfo, 0, 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: 'confidential' })
          .group('template')
          .onChange((isChecked: boolean) => {
            if (isChecked) {
              this.selectedTemplate = 'confidential';
            }
          })
        Text('机密文档')
          .marginLeft(10)
        
        Radio({ value: 'draft' })
          .group('template')
          .onChange((isChecked: boolean) => {
            if (isChecked) {
              this.selectedTemplate = 'draft';
            }
          })
        Text('草稿')
          .marginLeft(10)
        
        Radio({ value: 'sample' })
          .group('template')
          .onChange((isChecked: boolean) => {
            if (isChecked) {
              this.selectedTemplate = 'sample';
            }
          })
        Text('样本')
          .marginLeft(10)
      }
      .margin(10)
      
      Button('生成文档')
        .margin(10)
        .onClick(async () => {
          await this.generateDocumentWithWatermark(this.selectedTemplate);
        })
    }
  }
}

八、实用资源推荐

📚 推荐资料


下一步行动

👉 预告:《鸿蒙学习实战之路-PDF文档书签添加与删除最佳实践》


我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐