Merging PDFs with iText 7 makes printable layers always visible - java

Using iText7, I'm trying to merge various PDF documents into one single PDF but every printable layer present in the documents ends up being permanently visible.
Is it possible to copy or merge these documents while maintaining the layer properties? It seems as if iText triggers the printable flag, not unlike any "print to PDF" option in any document visualizer.
The first document always has a layer on each page marked as printable that was generated with:
PdfCanvas canvas = new PdfCanvas(page);
PdfLayer printLayer = new PdfLayer("print", pdfDocument);
Canvas canvasModel = new Canvas(canvas, pdfDocument, page.getPageSize());
layerImprimir.setOn(false);
layerImprimir.setOnPanel(false);
layerImprimir.setPrint("Print", true);
canvas.beginLayer(printLayer);
Paragraph paragraph = new Paragraph().setWidth(lineHeight).add(someText);
canvasModel.showTextAligned(paragraph, xPos, yPos, pageNumber, TextAlignment.CENTER, VerticalAlignment.MIDDLE, radAngle);
canvas.endLayer();
canvasModel.close();
Afterwards, that first document is merged with other PDFs that may or may not contain printable layers. I've used both PdfCopy and PdfMerger.
With PdfCopy:
Document finalDocument = new Document();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfCopy copy = new PdfCopy(finalDocument, baos);
PdfReader reader;
finalDocument.open();
reader = new PdfReader(firstDocument);
copy.addDocument(reader);
reader.close();
reader = new PdfReader(secondDocument);
copy.addDocument(reader);
reader.close();
finalDocument.close();
With PdfMerger (from https://stackoverflow.com/a/40594055/13114951):
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
PdfMerger merger = new PdfMerger(pdf);
//Add pages from the first document
PdfDocument firstSourcePdf = new PdfDocument(new PdfReader(SRC1));
merger.merge(firstSourcePdf, 1, firstSourcePdf.getNumberOfPages());
//Add pages from the second pdf document
PdfDocument secondSourcePdf = new PdfDocument(new PdfReader(SRC2));
merger.merge(secondSourcePdf, 1, secondSourcePdf.getNumberOfPages());
firstSourcePdf.close();
secondSourcePdf.close();
pdf.close();

Related

How to copy/move AcroForm fields from one document to new blank one using IText5 or IText7?

I need to copy whole AcroForm including field positions and values from template PDF to a new blank PDF file. How can I do that?
In short words - I need to get rid of "background" from the template and leave only filed forms.
The whole point of this is to create a PDF with content that would be printed on pre-printed templates.
I am using IText 5 but I can switch to 7 if usefull examples would be provided
After a lot of trial and error I have found the solution to "How to copy AcfroForm fields into another PDF". It is a iText v7 version. I hope it will help somebody someday.
private byte[] copyFormElements(byte[] sourceTemplate) throws IOException {
PdfReader completeReader = new PdfReader(new ByteArrayInputStream(sourceTemplate));
PdfDocument completeDoc = new PdfDocument(completeReader);
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfWriter offsetWriter = new PdfWriter(out);
PdfDocument offsetDoc = new PdfDocument(offsetWriter);
offsetDoc.initializeOutlines();
PdfPage blank = offsetDoc.addNewPage();
PdfAcroForm originalForm = PdfAcroForm.getAcroForm(completeDoc, false);
// originalForm.getPdfObject().copyTo(offsetDoc,false);
PdfAcroForm offsetForm = PdfAcroForm.getAcroForm(offsetDoc, true);
for (String name : originalForm.getFormFields().keySet()) {
PdfFormField field = originalForm.getField(name);
PdfDictionary copied = field.getPdfObject().copyTo(offsetDoc, false);
PdfFormField copiedField = PdfFormField.makeFormField(copied, offsetDoc);
offsetForm.addField(copiedField, blank);
}
offsetDoc.close();
completeDoc.close();
return out.toByteArray();
}
Did you check the PdfCopyForms object:
Allows you to add one (or more) existing PDF document(s) to create a new PDF and add the form of another PDF document to this new PDF.
I didn't find an example, but you could try something like this:
PdfReader reader1 = new PdfReader(src1); // a document with a form
PdfReader reader2 = new PdfReader(src2); // a document without a form
PdfCopyForms copy = new PdfCopyForms(new FileOutputStream(dest));
copy.AddDocument(reader1); // add the document without the form
copy.CopyDocumentFields(reader2); // add the fields of the document with the form
copy.close();
reader1.close();
reader2.close();
I see that the class is deprecated. I'm not sure of that's because iText 7 makes it much easier to do this, or if it's because there were technical problems with the class.

Create new pdf in loop with iText Java

I want to create a new pdf for each iteration of the loop so i wrote the below code in java:
PdfWriter writer;
PdfDocument pdf;
Document document;
int i=0;
while(condition){
writer = new PdfWriter("test_"+Integer.toString(i)+".pdf");
pdf = new PdfDocument(writer);
document = new Document(pdf);
//content code here
document.close()
i++;
}
i get the below exception on the line of the document.close() command:
Exception in thread "main" com.itextpdf.kernel.PdfException: Pdf
indirect object belongs to other PDF document. Copy object to current
pdf document. at
com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:184)
at
com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:174)
at
com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:104)
at
com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:176)
at
com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:104)
at
com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:176)
at
com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:104)
at com.itextpdf.kernel.pdf.PdfWriter.writeToBody(PdfWriter.java:335)
at com.itextpdf.kernel.pdf.PdfWriter.flushObject(PdfWriter.java:243)
at
com.itextpdf.kernel.pdf.PdfDocument.flushObject(PdfDocument.java:1446)
at com.itextpdf.kernel.pdf.PdfObject.flush(PdfObject.java:155) at
com.itextpdf.kernel.pdf.PdfObject.flush(PdfObject.java:128) at
com.itextpdf.kernel.pdf.PdfObjectWrapper.flush(PdfObjectWrapper.java:96)
at com.itextpdf.kernel.pdf.PdfPage.flush(PdfPage.java:489) at
com.itextpdf.kernel.pdf.PdfPage.flush(PdfPage.java:448) at
com.itextpdf.kernel.pdf.PdfDocument.close(PdfDocument.java:739) at
com.itextpdf.layout.Document.close(Document.java:120) at
gr.moh.Pdf.main(Pdf.java:224)
i have tried a lot but i cannot find why i get an error. If i do it without a loop it works. Any help?
P.S: i use itext 7
I had the below code out of the loop and it should be into the loop...
PdfFont normalFont = PdfFontFactory.createFont(fontDirectory.getAbsolutePath()+"\\arial.ttf", "Identity-H", true);
normal.setFont(normalFont).setFontSize(14);
Style red = new Style();
PdfFont redFont = PdfFontFactory.createFont(fontDirectory.getAbsolutePath()+"\\arial.ttf", "Identity-H", true);
red.setFont(redFont).setFontSize(14).setFontColor(Color.RED);
Style big = new Style();
PdfFont bigFont = PdfFontFactory.createFont(fontDirectory.getAbsolutePath()+"\\arial.ttf", "Identity-H", true);
big.setFont(bigFont).setFontSize(18).setBold();

itext on tomcat print different fonts

I'm executing this code from Eclipse and on Tomcat into a webapp
FileInputStream is = new FileInputStream("C:/Users/admin/Desktop/dummy.txt");
try {
FontFactory.register("C:/Workspace/Osmosit/ReportManager/testSvn/ReportManagerCommon/src/main/java/com/osmosit/reportmanager/common/itext/fonts/ARIALUNI.TTF");
} catch (Exception e) {
e.printStackTrace();
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
Document document = new Document(PageSize.A4);
PdfWriter writer;
writer = PdfWriter.getInstance(document, byteArrayOutputStream);
document.open();
XMLWorkerHelper.getInstance().parseXHtml(writer, document, is);
document.close();
byteArrayOutputStream.close();
FileOutputStream fos = new FileOutputStream("C:/Users/admin/Desktop/prova-web.pdf");
fos.write(byteArrayOutputStream.toByteArray());
fos.close();
the dummy.txt is a simple html with aranic and latin characters
<div style="font-family: Arial Unicode MS;" ><p>كما. أي مدن العدّ وقام test latin</p><br /></div>
When I run under eclipse I obtain a correct pd, when it runs on Tomcat I get this:
كما. أي مدن العدّ وقام test latin
PS: I'm using itextpdf ver 5.5.8
You have an encoding problem. Either you saved dummy.txt using the wrong encoding (e.g. as Latin-1 instead of as UTF-8), or you are reading dummy.txt using the wrong encoding.
See html to pdf convert, cyrillic characters not displayed properly and adapt the line in which you use parseHtml():
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
is, null, Charset.forName("UTF-8"), fontImp);
Take a look at the ParseHtml11 example to find out what fontImp is about.
You are also making another mistake: Arabic is read from right to left, and in your code, you aren't defining the run direction. See Arabic characters from html content to pdf using iText
In your case, I would put the Arabic text in a table and I would follow the ParseHtml7 example from the official documentation:
public void createPdf(String file) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
// Styles
CSSResolver cssResolver = new StyleAttrCSSResolver();
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("resources/fonts/NotoNaskhArabic-Regular.ttf");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
// HTML
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines
ElementList elements = new ElementList();
ElementHandlerPipeline pdf = new ElementHandlerPipeline(elements, null);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new FileInputStream(HTML), Charset.forName("UTF-8"));
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell();
cell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
for (Element e : elements) {
cell.addElement(e);
}
table.addCell(cell);
document.add(table);
// step 5
document.close();
}

function that can use iText to concatenate / merge pdfs together - causing some issues

I'm using the following code to merge PDFs together using iText:
public static void concatenatePdfs(List<File> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
Document document = new Document();
FileOutputStream outputStream = new FileOutputStream(outputFile);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte cb = writer.getDirectContent();
for (File inFile : listOfPdfFiles) {
PdfReader reader = new PdfReader(inFile.getAbsolutePath());
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
cb.addTemplate(page, 0, 0);
}
}
outputStream.flush();
document.close();
outputStream.close();
}
This usually works great! But once and a while, it's rotating some of the pages by 90 degrees? Anyone ever have this happen?
I am looking into the PDFs themselves to see what is special about the ones that are being flipped.
There are errors once in a while because you are using the wrong method to concatenate documents. Please read chapter 6 of my book and you'll notice that using PdfWriter to concatenate (or merge) PDF documents is wrong:
You completely ignore the page size of the pages in the original document (you assume they are all of size A4),
You ignore page boundaries such as the crop box (if present),
You ignore the rotation value stored in the page dictionary,
You throw away all interactivity that is present in the original document, and so on.
Concatenating PDFs is done using PdfCopy, see for instance the FillFlattenMerge2 example:
Document document = new Document();
PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
document.open();
PdfReader reader;
String line = br.readLine();
// loop over readers
// add the PDF to PdfCopy
reader = new PdfReader(baos.toByteArray());
copy.addDocument(reader);
reader.close();
// end loop
document.close();
There are other examples in the book.
In case anyone is looking for it, using Bruno Lowagie's correct answer above, here is the version of the function that does not seem to have the page flipping issue i described above:
public static void concatenatePdfs(List<File> listOfPdfFiles, File outputFile) throws DocumentException, IOException {
Document document = new Document();
FileOutputStream outputStream = new FileOutputStream(outputFile);
PdfCopy copy = new PdfSmartCopy(document, outputStream);
document.open();
for (File inFile : listOfPdfFiles) {
PdfReader reader = new PdfReader(inFile.getAbsolutePath());
copy.addDocument(reader);
reader.close();
}
document.close();
}

How to show a limited number of pages of PDF file in JSP using iText?

I have to display a PDF document in a JSP page. The PDF document has 25 pages, but I want to display only 10 pages of the PDF file. How can I achieve this with help of iText?
Assuming you have the PDF file already.
You can use PdfStamper and PdfCopy to slice the PDF up:
PdfReader reader = new PdfReader("THE PDF SOURCE");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Document document = new Document();
PdfCopy copy = new PdfCopy(document, outputStream);
document.open();
PdfStamper stamper = new PdfStamper(reader, outputStream);
for (int i = 1; i < reader.getNumberOfPages(); i++) {
// Select what pages you need here
PdfImportedPage importedPage = stamper.getImportedPage(reader, i);
copy.addPage(importedPage);
}
copy.freeReader(reader);
outputStream.flush();
document.close();
// Now you can send the byte array to your user
// set content type to application/pdf
As for sending the pdf to display, it depends on the way you display it. The outputstream will at the end of the supplied code contain the pages you copy in the loop, in the example it is all of the pages.
This essentially is a new PDF file, but in memory. If it is the same 10 pages of the same file every time, you may consider saving it as a file.

Categories

Resources