2017-04-10 113 views
2

我想用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

回答

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

+0

您可能想嘗試在itext代碼庫中包含這些類。 – mkl

+0

但是,我想知道,總是在默認的用戶座標系中給出'occupallyrea'座標嗎?例如。如果書寫方向旋轉... – mkl

+0

嗨@mkl。你的意思是RTL文本嗎?在RTL文本中,佔用區域相同,但字形重新排序發生,因此它只涉及要打印的字形的正確順序,但佔用區域座標遵循相同的邏輯和LTR情況。或者你是指轉換矩陣之類的東西? –

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,occupiedArea,LayoutResult這些類對我來說都不理解,我認爲我需要看看API來了解更多。