如何为PdfFormXObject添加可复用的Annotation
问题
我正在使用iText 7(.NET版本,F#语言)构建可在文档多页复用的PDF组件。我知道如何为Page添加Annotation,但当使用PdfFormXObject渲染PDF片段时,对应的Canvas没有关联Page,无法通过Page.AddAnnotation()添加Annotation。
相关代码示例:
let template = new PdfFormXObject(rect) let templateCanvas = PdfCanvas(template, pageContext.Canvas.GetPdfDocument()) let newCanvas = new Canvas(templateCanvas, rect)
尝试添加Annotation时,因newCanvas.GetPage()返回null报错:
// 创建目标和Annotation(destPage为页码) let dest = PdfExplicitDestination.CreateFitB(destPage) let action = PdfAction.CreateGoTo(dest) let annotation = PdfLinkAnnotation(rect) let border = iText.Kernel.Pdf.PdfAnnotationBorder(0f, 0f, 0f) // 配置Annotation的动作和显示信息 annotation .SetHighlightMode(PdfAnnotation.HIGHLIGHT_PUSH) .SetAction(action) .SetBorder(border) |> ignore // 尝试添加到页面报错:newCanvas无关联Page newCanvas.GetPage().AddAnnotation(annotation) |> ignore // 求解决方法
我想知道:
- 是否可以将Annotation作为原始PDF指令直接添加到Canvas?
- 有没有其他可复用PDF片段的机制,能同时复用GoTo Annotation?
- 了解到通过PdfStringDestination可复用链接(示例代码如下),但不确定是否适用于当前场景,也想知道如何将Annotation转为PdfDictionary并添加到PdfFormXObject中。
C#示例代码:
private static void Main(string[] args) { try { PdfDocument pdfDocument = new PdfDocument(new PdfWriter("TestMultiLink.pdf")); Document document = new Document(pdfDocument); string destinationName = "MyForwardDestination"; // 创建可多次使用的PdfStringDestination var stringDestination = new PdfStringDestination(destinationName); for (int page = 1; page <= 50; page++) { document.Add(new Paragraph().SetFontSize(100).Add($"{page}")); switch (page) { case 1: // 首次使用PdfStringDestination document.Add(new Paragraph(new Link("Click here for a forward jump", stringDestination).SetFontSize(20))); break; case 3: // 复用stringDestination document.Add(new Paragraph(new Link("Click here for a forward jump", stringDestination).SetFontSize(10))); break; case 42: pdfDocument.AddNamedDestination(destinationName, PdfExplicitDestination.CreateFit(pdfDocument.GetLastPage()).GetPdfObject()); break; } if (page < 50) document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE)); } document.Close(); } catch (Exception e) { Console.WriteLine($"Ouch: {e.Message}"); } }
解决方案
1. 无法将Annotation作为原始指令添加到Canvas
PDF规范中,注释属于页面级对象,不属于内容流(Canvas对应的就是内容流),因此无法通过原始PDF指令将注释嵌入到PdfFormXObject的内容中。PdfFormXObject本质是可复用的内容片段,本身不承载注释,注释必须关联到具体页面。
2. 复用带注释PDF片段的可行方案
方案一:复用注释逻辑,同步渲染与添加注释
既然PdfFormXObject无法携带注释,可单独封装创建注释的逻辑,在每次渲染PdfFormXObject到目标页面时,同步创建并添加对应的注释到该页面:
// 封装创建链接注释的函数 let createLinkAnnotation (page: PdfPage) (rect: Rectangle) (dest: PdfExplicitDestination) = let action = PdfAction.CreateGoTo(dest) let annotation = PdfLinkAnnotation(rect) let border = iText.Kernel.Pdf.PdfAnnotationBorder(0f, 0f, 0f) annotation .SetHighlightMode(PdfAnnotation.HIGHLIGHT_PUSH) .SetAction(action) .SetBorder(border) |> ignore page.AddAnnotation(annotation) // 渲染模板到页面时同步添加注释 let renderTemplateToPage (page: PdfPage) (template: PdfFormXObject) (templateRect: Rectangle) (destPage: PdfPage) = let canvas = PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), page.GetDocument()) canvas.AddXObject(template, templateRect.GetLeft(), templateRect.GetBottom(), templateRect.GetWidth()) // 同步创建对应位置的注释并添加到当前页面 createLinkAnnotation page templateRect (PdfExplicitDestination.CreateFitB(destPage))
每次将PdfFormXObject绘制到页面时调用该渲染函数,即可实现内容片段与注释的同步复用。
3. PdfStringDestination的适用性及注释转字典说明
PdfStringDestination完全适配你的场景
如果链接目标固定(比如跳转到某一页),使用命名目的地(PdfStringDestination)是更简洁的复用方式:
- 先定义命名目的地,在目标页面创建时绑定到该页面
- 每次创建链接注释时直接引用该命名目的地,无需重复创建
PdfExplicitDestination - 将创建注释的逻辑与PdfFormXObject的渲染逻辑绑定,即可实现复用。
适配F#的示例代码:
// 定义全局命名目的地名称 let destinationName = "TargetPage" // 在目标页面创建时绑定命名目的地 let setupNamedDestination (pdfDoc: PdfDocument) (targetPage: PdfPage) = pdfDoc.AddNamedDestination(destinationName, PdfExplicitDestination.CreateFitB(targetPage).GetPdfObject()) // 封装创建基于命名目的地的链接注释 let createNamedLinkAnnotation (page: PdfPage) (rect: Rectangle) = let dest = PdfStringDestination(destinationName) let action = PdfAction.CreateGoTo(dest.GetPdfObject()) let annotation = PdfLinkAnnotation(rect) let border = iText.Kernel.Pdf.PdfAnnotationBorder(0f, 0f, 0f) annotation .SetHighlightMode(PdfAnnotation.HIGHLIGHT_PUSH) .SetAction(action) .SetBorder(border) |> ignore page.AddAnnotation(annotation) // 渲染模板并添加注释 let renderTemplateWithNamedLink (page: PdfPage) (template: PdfFormXObject) (templateRect: Rectangle) = let canvas = PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), page.GetDocument()) canvas.AddXObject(template, templateRect.GetLeft(), templateRect.GetBottom(), templateRect.GetWidth()) createNamedLinkAnnotation page templateRect
关于将Annotation转为PdfDictionary添加到PdfFormXObject
PDF规范不允许将注释对象嵌入到PdfFormXObject中,注释必须属于页面的Annots数组。即使手动将注释的PdfDictionary添加到PdfFormXObject的字典里,PDF阅读器也不会识别为可点击的注释,因此这种方法不可行。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

