You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PdfSharp创建带自定义图片的RubberStamp注释外观流?

在PdfSharp中为RubberStamp注释添加自定义图片

问题描述

在C#项目中使用PdfSharp库生成带注释的PDF时,需要创建带有自定义图片的RubberStamp注释,但PdfSharp自带的RubberStamp仅支持TopSecret等预定义图标,无法直接指定自定义图片。

尝试手动模仿带自定义图章的PDF结构:创建XObjects、将图片加入资源、构建外观流,但未成功——图片已写入文件,PDF阅读器能看到注释矩形,但显示空白。希望找到方法给RubberStamp的外观流添加自定义图片,或者触发PdfSharp自动重建外观流。

尝试的代码片段

public void SavePdf()
{
    System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

    // Create a new PDF document
    PdfDocument pdfDocument = new PdfDocument();
    pdfDocument.Version = 15;

    // Create an empty page
    PdfPage pdfPage;
    pdfPage = pdfDocument.AddPage();
    pdfPage.Width = XUnit.FromInch(8.5);
    pdfPage.Height = XUnit.FromInch(11.00);

    // Get an XGraphics object for drawing
    XGraphics gfx = XGraphics.FromPdfPage(pdfPage);

    // Creating the Rubber Stamp with some default settings
    PdfRubberStampAnnotation cstamp = new PdfRubberStampAnnotation() { Icon = PdfRubberStampAnnotationIcon.TopSecret };
    XRect rect = gfx.Transformer.WorldToDefaultPage(new XRect(new XPoint(10, 10), new XSize(200, 50)));
    cstamp.Rectangle = new PdfRectangle(rect);
    cstamp.Color = XColor.FromArgb(255, 0, 0);

    // Load the custom image
    var image = XImage.FromFile(@"c:\temp\test.bmp");
    var pdfImage = new PdfImage(pdfDocument, image);
    string r = pdfPage.Resources.AddImage(pdfImage); // adding the image to the Page References

    // Now let's create the XForm object, and reference the image
    var matrix = new PdfArray();
    matrix.Elements.Add(new PdfReal(1));
    matrix.Elements.Add(new PdfReal(0));
    matrix.Elements.Add(new PdfReal(0));
    matrix.Elements.Add(new PdfReal(1));
    matrix.Elements.Add(new PdfReal(0));
    matrix.Elements.Add(new PdfReal(0));

    var xform = new PdfDictionary(pdfDocument);
    xform.Elements.Add("/Type", new PdfName("/XObject"));
    xform.Elements.Add("/Subtype", new PdfName("/Form"));
    xform.Elements.Add("/FormType", new PdfInteger(1));
    xform.Elements.Add("/BBox", new PdfRectangle(new XRect(10, 10, image.PointWidth, image.PointHeight)));
    xform.Elements.Add("/Matrix", matrix);
    xform.Elements.Add("/Resources", pdfImage.Reference);
    xform.Elements.Add("/Length", new PdfInteger(0));

    // Now let's create the appearance stream, and add the XForm object
    var appearanceStream = new PdfDictionary(pdfDocument);
    appearanceStream.Elements.Add("/N", xform);
    cstamp.Elements.Add("/AP", appearanceStream);

    // Add to the page
    gfx.PdfPage.Annotations.Add(cstamp);

    pdfDocument.Save(@"c:\temp\1.pdf");
}

补充尝试代码

byte[] buffer = new byte[10000];
appearanceStream.Stream = xform.CreateStream(buffer);
appearanceStream.Elements["/N"] = new PdfString(string.Format("q\n200 0 0 200 0 0 cm\n{0} Do\n Q", r));

解决方法

要实现自定义图片的RubberStamp注释,需要正确构建注释的外观流(AP),并确保图片资源、表单XObject和绘制指令正确关联。以下是修正后的实现代码:

public void SaveCustomStampPdf()
{
    System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

    // 创建PDF文档
    PdfDocument pdfDocument = new PdfDocument();
    pdfDocument.Version = 15;

    // 添加页面
    PdfPage pdfPage = pdfDocument.AddPage();
    pdfPage.Width = XUnit.FromInch(8.5);
    pdfPage.Height = XUnit.FromInch(11.0);

    // 创建RubberStamp注释
    PdfRubberStampAnnotation stamp = new PdfRubberStampAnnotation();
    // 设置注释位置和大小(PDF坐标:左下角为原点)
    XRect stampRect = new XRect(100, 100, 200, 100);
    stamp.Rectangle = new PdfRectangle(stampRect);
    stamp.Color = XColor.FromArgb(255, 0, 0);

    // 加载自定义图片
    using (XImage image = XImage.FromFile(@"c:\temp\test.bmp"))
    {
        // 将图片添加到文档资源,获取资源名称
        PdfImage pdfImage = new PdfImage(pdfDocument, image);
        string imageResourceName = pdfPage.Resources.AddImage(pdfImage);

        // 创建外观流的表单XObject
        PdfDictionary formXObject = new PdfDictionary(pdfDocument);
        formXObject.Elements.Add("/Type", new PdfName("/XObject"));
        formXObject.Elements.Add("/Subtype", new PdfName("/Form"));
        formXObject.Elements.Add("/FormType", new PdfInteger(1));
        // 设置表单的边界框,与注释大小一致
        formXObject.Elements.Add("/BBox", new PdfRectangle(stampRect));
        // 设置资源字典,关联图片资源
        PdfDictionary resources = new PdfDictionary(pdfDocument);
        PdfDictionary xObjects = new PdfDictionary(pdfDocument);
        xObjects.Elements.Add(imageResourceName, pdfImage.Reference);
        resources.Elements.Add("/XObject", xObjects);
        formXObject.Elements.Add("/Resources", resources);

        // 构建绘制图片的PDF指令流
        // 指令含义:保存状态 -> 设置缩放矩阵 -> 绘制图片 -> 恢复状态
        string content = $"q\n{stampRect.Width} 0 0 {stampRect.Height} {stampRect.X} {stampRect.Y} cm\n/{imageResourceName} Do\nQ";
        byte[] contentBytes = System.Text.Encoding.ASCII.GetBytes(content);
        // 为表单XObject创建流并写入指令
        formXObject.CreateStream(contentBytes);
        formXObject.Elements.SetInteger("/Length", contentBytes.Length);

        // 创建注释的外观字典,指定正常状态下的外观为表单XObject
        PdfDictionary appearanceDict = new PdfDictionary(pdfDocument);
        appearanceDict.Elements.Add("/N", formXObject.Reference);
        stamp.Elements.Add("/AP", appearanceDict);
    }

    // 将注释添加到页面
    pdfPage.Annotations.Add(stamp);

    // 保存文档
    pdfDocument.Save(@"c:\temp\custom_stamp.pdf");
}

关键修正点说明

  • 资源字典正确关联:原代码直接将图片引用赋值给表单的/Resources,但PDF规范要求/Resources必须是包含/XObject子字典的字典,需先创建资源字典和XObject子字典,再关联图片资源。
  • 正确构建绘制指令:外观流的内容需为有效的PDF绘制指令,通过CreateStream将指令写入XObject的流中,并设置正确的/Length值,确保阅读器能正确解析内容。
  • 表单边界框与注释匹配:表单XObject的/BBox需与注释的Rectangle保持一致,保证图片在注释区域内正确显示。
  • 引用XObject而非直接赋值:外观字典的/N需引用表单XObject的Reference对象,而非直接赋值字典本身,符合PDF的引用机制。

内容的提问来源于stack exchange,提问作者Zsolt J

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:49:58