We have been trying to achieve this since a week now, still no luck. I wonder if this is possible. So need a confirmation from other experts.
We are trying to create PDF table with attachments embedded inside PDF per row like this in screenshot.
We are able to write a code which can print above PDF table correctly and it works fine.
The problem here is – inside PDF the embedded-file is same. But this file is actually copied 9 times instead of only once. Here is the screenshot:
Now because — our system has few files which are just evidence for each row. We just need to embed one attachment only once and then refer it as icon into multiple rows. This will help us save the size of PDF drastically.
One probable solution we have is — we will need to change the design, where for each evidence show all related rows. But our client would never approve this. Hence looking for a solution.
Please help us with this.
Here is the code that we are using:
@ExtendWith(MockitoExtension.class)
public class DeleteMeTest {
public static final int ICON_SIZE = 11;
public static final String PATH = "src/test/resources";
public static final File FILE = new File(PATH);
public static final String LOCATION = FILE.getAbsolutePath() + "/pdf-embed/";
public static final String FILE_ICON_NAME = LOCATION + "file.png"; // Icon file
public static final String ATTACHMENT_FILE_NAME = "source.pdf"; // source file
public static final String TARGET_PDF = "target.pdf"; // target pdf
@Test
void testPdfWithEmbeddedDocuments() throws IOException {
try (OutputStream target = new FileOutputStream(LOCATION + TARGET_PDF);
InputStream source1 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source2 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source3 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source4 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source5 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source6 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source7 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source8 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME);
InputStream source9 = new FileInputStream(LOCATION + ATTACHMENT_FILE_NAME)) {
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new BufferedOutputStream(target)));
Document document = new com.itextpdf.layout.Document(pdfDocument, PageSize.A4);
Table table = new Table(UnitValue.createPercentArray(new float[]{90, 10})).useAllAvailableWidth();
table.setFontSize(7);
// Add Table body
addTableRow(table, pdfDocument, source1);
addTableRow(table, pdfDocument, source2);
addTableRow(table, pdfDocument, source3);
addTableRow(table, pdfDocument, source4);
addTableRow(table, pdfDocument, source5);
addTableRow(table, pdfDocument, source6);
addTableRow(table, pdfDocument, source7);
addTableRow(table, pdfDocument, source8);
addTableRow(table, pdfDocument, source9);
// finalize PDF
document.add(table);
pdfDocument.close();
}
}
private void addTableRow(Table table, PdfDocument pdfDocument, InputStream source) {
table.startNewRow();
table.addCell(getTextCell("Some File Name: "));
table.addCell(getAttachmentCell(pdfDocument, new Attachment(source, TARGET_PDF)));
}
private Cell getTextCell(String text) {
Paragraph paragraph = new Paragraph();
paragraph.add(text);
Cell cell = new Cell();
cell.add(paragraph);
return cell;
}
private Cell getAttachmentCell(PdfDocument pdfDoc, Attachment attachment) {
try (InputStream iconFileStream = new FileInputStream(FILE_ICON_NAME)) {
ImageData img = ImageDataFactory.create(iconFileStream.readAllBytes());
Image i = new Image(img);
i.setNextRenderer(new LinkImageRenderer(i, pdfDoc, attachment));
Cell fileCell = new Cell();
fileCell.add(i);
return fileCell;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static class LinkImageRenderer extends ImageRenderer {
private final Attachment attachment;
private final PdfDocument pdfDoc;
private final Image image;
public LinkImageRenderer(Image image, PdfDocument pdfDoc, Attachment attachment) {
super((image.setHeight(ICON_SIZE).setWidth(ICON_SIZE)));
this.pdfDoc = pdfDoc;
this.image = image;
this.attachment = attachment;
}
@Override
public IRenderer getNextRenderer() {
return new LinkImageRenderer(image, pdfDoc, attachment);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(pdfDoc,
attachment.inputStream,
attachment.fileName,
attachment.fileName,
null,
null);
Rectangle rect = getOccupiedAreaBBox();
PdfAnnotation pdfAnnotation = new PdfFileAttachmentAnnotation(rect, fileSpec);
pdfAnnotation.setContents(attachment.fileName);
PdfFormXObject xObject = new PdfFormXObject(rect);
pdfAnnotation.setNormalAppearance(xObject.getPdfObject());
drawContext.getDocument().getLastPage().addAnnotation(pdfAnnotation);
}
}
public static class Attachment {
public String fileName;
public InputStream inputStream;
public Attachment(InputStream stream, String file) {
inputStream = stream;
fileName = file;
}
}
}
Here is the itext dependency
<!-- https://kb.itextpdf.com/itext/release-itext-core-8-0-0 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>8.0.2</version>
</dependency>