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

时间:2017-04-10 05:16:00

标签: java itext7

我想使用itext7生成类似这样的pdf文档:

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();

有人可以帮助我吗?

2 个答案:

答案 0 :(得分:2)

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

目前在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

答案 1 :(得分:1)

感谢您的回复@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,occupArea,LayoutResult这些类对我来说都不了解,我认为我需要看一下API来了解更多。