在C#开发的实际项目中,经常需要将PDF文件转换为图片格式,同时给生成的图片添加文字或图片水印,比如文档预览、版权标识、内部流转标记等场景都需要用到这类功能。为了提升代码复用性,我们可以封装一个专门的水印处理类,统一处理PDF转图片和水印添加的逻辑。

准备工作
实现该功能需要引入两个常用的第三方NuGet包:iText7用于处理PDF文件读取,ImageMagick用于将PDF页面转换为图片,同时支持图片合成与水印添加。安装完成后即可开始类的封装。
水印类整体结构
我们封装的水印类主要包含三个核心方法:PDF转图片方法、文字水印添加方法、图片水印添加方法,同时提供参数配置属性,方便外部调用时调整水印效果。
using System;
using System.Collections.Generic;
using System.IO;
using ImageMagick;
using iText.Kernel.Pdf;
namespace PdfWatermarkHelper
{
/// <summary>
/// PDF生成带水印图片的处理类
/// </summary>
public class PdfWatermarkGenerator
{
/// <summary>
/// PDF文件路径
/// </summary>
public string PdfPath { get; set; }
/// <summary>
/// 输出图片目录
/// </summary>
public string OutputDir { get; set; }
/// <summary>
/// 文字水印内容
/// </summary>
public string TextWatermark { get; set; }
/// <summary>
/// 文字水印字体大小
/// </summary>
public int TextFontSize { get; set; } = 24;
/// <summary>
/// 文字水印颜色
/// </summary>
public MagickColor TextColor { get; set; } = new MagickColor("gray");
/// <summary>
/// 文字水印透明度 0-1之间
/// </summary>
public double TextOpacity { get; set; } = 0.5;
/// <summary>
/// 图片水印路径
/// </summary>
public string ImageWatermarkPath { get; set; }
/// <summary>
/// 图片水印缩放比例
/// </summary>
public double ImageScale { get; set; } = 0.3;
/// <summary>
/// 构造函数 初始化路径参数
/// </summary>
/// <param name="pdfPath">PDF文件路径</param>
/// <param name="outputDir">输出目录</param>
public PdfWatermarkGenerator(string pdfPath, string outputDir)
{
PdfPath = pdfPath;
OutputDir = outputDir;
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
}
/// <summary>
/// 将PDF所有页转换为带水印的图片
/// </summary>
/// <returns>生成的图片路径列表</returns>
public List<string> GenerateWatermarkImages()
{
List<string> resultPaths = new List<string>();
// 读取PDF文件
using (PdfReader reader = new PdfReader(PdfPath))
{
using (PdfDocument pdfDoc = new PdfDocument(reader))
{
int pageCount = pdfDoc.GetNumberOfPages();
for (int i = 1; i <= pageCount; i++)
{
string pageImagePath = ConvertPdfPageToImage(i);
// 添加文字水印
if (!string.IsNullOrEmpty(TextWatermark))
{
pageImagePath = AddTextWatermark(pageImagePath);
}
// 添加图片水印
if (!string.IsNullOrEmpty(ImageWatermarkPath) && File.Exists(ImageWatermarkPath))
{
pageImagePath = AddImageWatermark(pageImagePath);
}
resultPaths.Add(pageImagePath);
}
}
}
return resultPaths;
}
/// <summary>
/// 将PDF指定页转换为图片
/// </summary>
/// <param name="pageNumber">页码 从1开始</param>
/// <returns>转换后的图片路径</returns>
private string ConvertPdfPageToImage(int pageNumber)
{
string tempImagePath = Path.Combine(OutputDir, $"temp_page_{pageNumber}.png");
// 使用ImageMagick读取PDF指定页
using (MagickImageCollection images = new MagickImageCollection())
{
images.Read(PdfPath);
// 获取指定页
IMagickImage image = images[pageNumber - 1];
// 设置分辨率 提高图片清晰度
image.Density = new Density(300);
// 转换为PNG格式保存
image.Write(tempImagePath, MagickFormat.Png);
}
return tempImagePath;
}
/// <summary>
/// 给图片添加文字水印
/// </summary>
/// <param name="sourceImagePath">原图片路径</param>
/// <returns>添加水印后的图片路径</returns>
private string AddTextWatermark(string sourceImagePath)
{
string watermarkImagePath = Path.Combine(OutputDir, $"text_watermark_{Path.GetFileName(sourceImagePath)}");
using (MagickImage image = new MagickImage(sourceImagePath))
{
// 创建文字绘制设置
MagickReadSettings readSettings = new MagickReadSettings
{
Font = "Arial",
FontPointsize = TextFontSize,
BackgroundColor = MagickColors.Transparent
};
// 生成文字水印图片
using (MagickImage watermark = new MagickImage($"label:{TextWatermark}", readSettings))
{
watermark.Colorize(TextColor, new Percentage(100));
watermark.Opacity(new Percentage(TextOpacity * 100));
// 设置水印位置为右下角
int x = image.Width - watermark.Width - 20;
int y = image.Height - watermark.Height - 20;
// 合成水印
image.Composite(watermark, x, y, CompositeOperator.Over);
}
image.Write(watermarkImagePath);
}
// 删除临时原图
File.Delete(sourceImagePath);
return watermarkImagePath;
}
/// <summary>
/// 给图片添加图片水印
/// </summary>
/// <param name="sourceImagePath">原图片路径</param>
/// <returns>添加水印后的图片路径</returns>
private string AddImageWatermark(string sourceImagePath)
{
string watermarkImagePath = Path.Combine(OutputDir, $"image_watermark_{Path.GetFileName(sourceImagePath)}");
using (MagickImage image = new MagickImage(sourceImagePath))
{
using (MagickImage watermark = new MagickImage(ImageWatermarkPath))
{
// 缩放水印图片
int newWidth = (int)(image.Width * ImageScale);
int newHeight = (int)(watermark.Height * (double)newWidth / watermark.Width);
watermark.Resize(newWidth, newHeight);
// 设置水印位置为居中
int x = (image.Width - watermark.Width) / 2;
int y = (image.Height - watermark.Height) / 2;
// 设置水印透明度
watermark.Opacity(new Percentage(40));
// 合成水印
image.Composite(watermark, x, y, CompositeOperator.Over);
}
image.Write(watermarkImagePath);
}
File.Delete(sourceImagePath);
return watermarkImagePath;
}
}
}调用示例
封装好类之后,调用方式非常简单,只需要初始化类并配置对应参数,调用生成方法即可:
using System;
using System.Collections.Generic;
namespace PdfWatermarkTest
{
class Program
{
static void Main(string[] args)
{
// 初始化生成器
PdfWatermarkGenerator generator = new PdfWatermarkGenerator(
@"D:test.pdf",
@"D:output_images"
);
// 配置文字水印
generator.TextWatermark = "内部资料 禁止外传";
generator.TextFontSize = 30;
generator.TextColor = new ImageMagick.MagickColor("red");
generator.TextOpacity = 0.6;
// 配置图片水印
generator.ImageWatermarkPath = @"D:logo.png";
generator.ImageScale = 0.2;
// 生成带水印的图片
List<string> imagePaths = generator.GenerateWatermarkImages();
Console.WriteLine($"共生成{imagePaths.Count}张图片");
foreach (var path in imagePaths)
{
Console.WriteLine($"图片路径:{path}");
}
}
}
}注意事项
- 第三方库的使用需要提前安装对应的NuGet包,iText7需要注意商业使用授权问题,非商业场景可以使用开源版本。
- PDF转图片的分辨率可以通过
Density属性调整,数值越高图片越清晰,但生成速度会变慢,文件体积也会更大。 - 水印的位置、透明度、颜色等参数都可以根据业务需求修改,类中预留了对应的配置属性,无需修改核心逻辑。
- 如果PDF文件有密码保护,需要在
PdfReader初始化时传入密码参数,否则会读取失败。