如何用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
相关产品推荐
相关产品推荐

