IText 7:如何创建段落的文本和附件组合?

问题描述:

我想用itext7产生这样的PDF文档:IText 7:如何创建段落的文本和附件组合?

the effect that I need

但我无法找到任何方法来实现这一目标。 我在教程iText - clickable image should open ms word attachment中看到的内容不能将附件和文本放在一起。您只能将附件放在单独的页面上。 如果是itext5,我这样做:

PdfAnnotation anno = PdfAnnotation.createFileAttachment(writer, null, fileDescribe, pdfFileSpecification); 
chunk.setAnnotation(anno); 
paragrah.add(chunk); 

所以我可以用一个段落添加文本,注释,但itext7教程的话:

PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST)); 
    Rectangle rect = new Rectangle(36, 700, 100, 100); 
    PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(pdfDoc, PATH, null, "test.docx", null, null, false); 
    PdfAnnotation attachment = new PdfFileAttachmentAnnotation(rect, fs) 
      .setContents("Click me"); 

    PdfFormXObject xObject = new PdfFormXObject(rect); 
    ImageData imageData = ImageDataFactory.create(IMG); 
    PdfCanvas canvas = new PdfCanvas(xObject, pdfDoc); 
    canvas.addImage(imageData, rect, true); 
    attachment.setNormalAppearance(xObject.getPdfObject()); 

    pdfDoc.addNewPage().addAnnotation(attachment); 
    pdfDoc.close(); 

有人能帮助我吗?

+0

谢谢! @mkl –

+0

请不要将您的解决方案添加到问题主体,而是将其作为答案发布。 – mkl

如果我理解正确,您希望在其他布局元素中添加注释到您的文档流。

目前在iText7有没有快速的方法来实现它(如setAnnotation方法在iText5)。但是,iText7足够灵活,可以让您创建自定义元素,而不是深入挖掘代码。

初始部分与当前示例中的相同。这里的注释本身正在建立:

PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST)); 
Rectangle rect = new Rectangle(36, 700, 50, 50); 
PdfFileSpec fs = PdfFileSpec.createEmbeddedFileSpec(pdfDoc, PATH, null, "test.docx", null, null, false); 
PdfAnnotation attachment = new PdfFileAttachmentAnnotation(rect, fs) 
     .setContents("Click me"); 

PdfFormXObject xObject = new PdfFormXObject(rect); 
ImageData imageData = ImageDataFactory.create(IMG); 
PdfCanvas canvas = new PdfCanvas(xObject, pdfDoc); 
canvas.addImage(imageData, rect, true); 
attachment.setNormalAppearance(xObject.getPdfObject()); 

然后,我们要实现的是能够自定义注解的元素添加到布局Document流。 在最好的情况下,代码应该是这样的:

Document document = new Document(pdfDoc); 
Paragraph p = new Paragraph("There are two").add(new AnnotationElement(attachment)).add(new Text("elements")); 
document.add(p); 
document.close(); 

现在我们只剩下定义AnnotationElement和相应的渲染器。元素本身不具有任何特定的逻辑除了创建自定义渲染和传递一个注解吧:

private static class AnnotationElement extends AbstractElement<AnnotationElement> implements ILeafElement { 
    private PdfAnnotation annotation; 

    public AnnotationElement(PdfAnnotation annotation) { 
     this.annotation = annotation; 
    } 

    @Override 
    protected IRenderer makeNewRenderer() { 
     return new AnnotationRenderer(annotation); 
    } 
} 

渲染器实现了更多的代码,但它是简单的定义上layout占用面积,并增加了注释到draw上的页面。请注意,它并没有涵盖所有情况(例如没有足够的空间来容纳注释),但这对于简单情况和演示目的来说已经足够了。

private static class AnnotationRenderer extends AbstractRenderer implements ILeafElementRenderer { 
    private PdfAnnotation annotation; 

    public AnnotationRenderer(PdfAnnotation annotat) { 
     this.annotation = annotat; 
    } 

    @Override 
    public float getAscent() { 
     return annotation.getRectangle().toRectangle().getHeight(); 
    } 

    @Override 
    public float getDescent() { 
     return 0; 
    } 

    @Override 
    public LayoutResult layout(LayoutContext layoutContext) { 
     occupiedArea = layoutContext.getArea().clone(); 

     float myHeight = annotation.getRectangle().toRectangle().getHeight(); 
     float myWidth = annotation.getRectangle().toRectangle().getWidth(); 
     occupiedArea.getBBox().moveUp(occupiedArea.getBBox().getHeight() - myHeight).setHeight(myHeight); 
     occupiedArea.getBBox().setWidth(myWidth); 

     return new LayoutResult(LayoutResult.FULL, occupiedArea, null, null); 
    } 

    @Override 
    public void draw(DrawContext drawContext) { 
     super.draw(drawContext); 
     annotation.setRectangle(new PdfArray(occupiedArea.getBBox())); 
     drawContext.getDocument().getPage(occupiedArea.getPageNumber()).addAnnotation(annotation); 
    } 

    @Override 
    public IRenderer getNextRenderer() { 
     return new AnnotationRenderer(annotation); 
    } 
} 

请注意,该示例适用于当前的7.0.3-SNAPSHOT版本。可能不适用于版本7.0.2版本,因为ILeafElementRenderer接口稍后添加,但如果确实需要,仍然可以将代码调整为7.0.2

+0

您可能想尝试在itext代码库中包含这些类。 – mkl

+0

但是,我想知道,总是在默认的用户坐标系中给出'occupallyrea'坐标吗?例如。如果书写方向旋转... – mkl

+0

嗨@mkl。你的意思是RTL文本吗?在RTL文本中,占用区域相同,但字形重新排序发生,因此它只涉及要打印的字形的正确顺序,但占用区域坐标遵循相同的逻辑和LTR情况。或者你是指转换矩阵之类的东西? –

谢谢您的回复@Alexey Subach,通过使用您的方法,我解决了我自己的问题。因为我使用7.0.2-SNAPSHOT版本。所以在AnnotationRenderer类做了一个小的变化:

public class AnnotationRenderer extends AbstractRenderer implements IRenderer { 

    private PdfAnnotation annotation; 

    public AnnotationRenderer(PdfAnnotation annotation) { 
    this.annotation = annotation; 
    } 

    public float getAscent(){ 
    return annotation.getRectangle().toRectangle().getHeight(); 
    } 

    public float getDescent(){ 
    return 0; 
    } 

    @Override 
    public LayoutResult layout(LayoutContext layoutContext) { 
    occupiedArea = layoutContext.getArea().clone(); 

    float myHeight = annotation.getRectangle().toRectangle().getHeight(); 
    float myWidth = annotation.getRectangle().toRectangle().getWidth(); 
    occupiedArea.getBBox().moveUp(occupiedArea.getBBox().getHeight() - myHeight).setHeight(myHeight); 
    occupiedArea.getBBox().setWidth(myWidth); 

    return new LayoutResult(LayoutResult.FULL, occupiedArea, null, null); 
    } 

    @Override 
    public IRenderer getNextRenderer() { 
    return new AnnotationRenderer(annotation); 
    } 

    @Override 
    public void draw(DrawContext drawContext) { 
     super.draw(drawContext); 
    annotation.setRectangle(new PdfArray(occupiedArea.getBBox())); 
    drawContext.getDocument().getPage(occupiedArea.getPageNumber()).addAnnotation(annotation); 
} 

}

我刚刚接触iText的很快,当我觉得自己未必能解决只是遇到了这个问题,但幸运的得到你的帮助。实际上,LayoutContext,occupiedArea,LayoutResult这些类对我来说都不理解,我认为我需要看看API来了解更多。