iText PdfCopy creates editable pdf document - java

I have a template pdf file which is used in a spring boot application. I need to update values in this template based on user input per request. Also in the request i will get multiple pdf files I need to merge those files along with updated file which is first page of final pdf.
I am using iText with Spring Boot. I am able to update the values in template and merge file content as well but final pdf is coming as editable with files are hidden. If i click on that filed i can able to see my values also can able to edit.
public void mergefiles(Map<String, String> tempData,MultipartFile[] userInfoFiles)
throws Exception{
FileOutputStream mergeOutStream = new FileOutputStream(new File("C:\\UpdateFile\\mergepath\\updatetem.pdf")); //To update user content to Template
PdfReader reader = new PdfReader(new FileInputStream(new File("C:\\UpdateFile\\template\\template.pdf"))); //Template File Stream
PdfStamper stamper = new PdfStamper(reader, mergeOutStream);
stamper.setFormFlattening(false);
AcroFields form = stamper.getAcroFields();
Map<String, Item> fieldMap = form.getFields();
for (String key : fieldMap.keySet()) {
String fieldValue = dataMap.get(key);
if (fieldValue != null) {
form.setField(key, fieldValue);
}
}
//Above part creates updated pdf with read only
//Below section creates merged file but first page is editable with
//filed values are hidden.
Document mergePdfDoc = new Document();
PdfCopy pdfCopy;
boolean smartCopy = false;
FileOutputStream newmergeOutStream = new FileOutputStream(new File("C:\\UpdateFile\\mergepath\\newmerged.pdf"));
if(smartCopy)
pdfCopy = new PdfSmartCopy(mergePdfDoc, newmergeOutStream);
else
pdfCopy = new PdfCopy(mergePdfDoc, newmergeOutStream);
mergePdfDoc.open();
pdfCopy.addDocument(stamper.getReader());
pdfCopy.freeReader(stamper.getReader());
PdfReader[] pdfReader = new PdfReader[userInfoFiles.length];
for(int i=0; i<=userInfoFiles.length-1;i++) {
pdfReader[i] = new PdfReader(userInfoFiles[i].getInputStream());
pdfCopy.addDocument(pdfReader[i]);
pdfCopy.freeReader(pdfReader[i]);
pdfReader[i].close();
}
stamper.close();
mergeOutStream.close();
mergePdfDoc.close();
}
Any input why final pdf is in editable form and filed values are hidden. I have to create a merged document and get ByteArray stream of the final document as its input to another function call.I am Using iText5.

The problem is that you add the PdfReader the PdfStamper is based on as input to your PdfCopy:
pdfCopy.addDocument(stamper.getReader());
The reader a stamper works on is dirty: some changes applied via the stamper are made to the objects the reader holds, some are only in the stamper or its output.
E.g. in your case the form fields are already defined in the original pdf. The field value is added to this field directly. Thus, it gets changed in the reader. But the appearance, the field visualization including a drawing of its current value, gets generated in a new indirect object which is added to the stamper output. Thus, there still is the original, empty visualization in the reader.
In a pdf viewer, therefore, the PdfCopy result at first has the looks of empty fields (as the appearances have been generated in the stamper only) but when editing a field, the changed value becomes visible (because the field editor is initialized with the field value).
To fix this, don't use the dirty reader but instead create a new, clean reader from the stamping result.
First create the stamped file:
FileOutputStream mergeOutStream = new FileOutputStream(new File("C:\\UpdateFile\\mergepath\\updatetem.pdf")); //To update user content to Template
PdfReader reader = new PdfReader(new FileInputStream(new File("C:\\UpdateFile\\template\\template.pdf"))); //Template File Stream
PdfStamper stamper = new PdfStamper(reader, mergeOutStream);
stamper.setFormFlattening(false);
AcroFields form = stamper.getAcroFields();
Map<String, Item> fieldMap = form.getFields();
for (String key : fieldMap.keySet()) {
String fieldValue = dataMap.get(key);
if (fieldValue != null) {
form.setField(key, fieldValue);
}
}
stamper.close();
And then merge:
Document mergePdfDoc = new Document();
PdfCopy pdfCopy;
boolean smartCopy = false;
FileOutputStream newmergeOutStream = new FileOutputStream(new File("C:\\UpdateFile\\mergepath\\newmerged.pdf"));
if(smartCopy)
pdfCopy = new PdfSmartCopy(mergePdfDoc, newmergeOutStream);
else
pdfCopy = new PdfCopy(mergePdfDoc, newmergeOutStream);
mergePdfDoc.open();
PdfReader reader = new PdfReader(new FileInputStream(new File("C:\\UpdateFile\\mergepath\\updatetem.pdf")));
pdfCopy.addDocument(reader);
pdfCopy.freeReader(reader);
PdfReader[] pdfReader = new PdfReader[userInfoFiles.length];
for(int i=0; i<=userInfoFiles.length-1;i++) {
pdfReader[i] = new PdfReader(userInfoFiles[i].getInputStream());
pdfCopy.addDocument(pdfReader[i]);
pdfCopy.freeReader(pdfReader[i]);
pdfReader[i].close();
}
mergeOutStream.close();
mergePdfDoc.close();
}

Related

Merging Documents iText7

I am adding text to an existing pdf.
My code so far will add the text to the file but it deletes the original content that was on the pdf before, does anyone know how to fix this? So that the added text is on a new page and the original content of the pdf is on another page.
String field1 = ("/Users/Desktop/") + selectedFile.getName();
System.out.println(field1);
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(field1));
PdfPage page = pdfDoc.addNewPage();
PdfCanvas pdfCanvas = new PdfCanvas(page);
Rectangle rectangle = new Rectangle(1,1, 600, 843);
pdfCanvas.rectangle(rectangle);
pdfCanvas.stroke();
Canvas canvas = new Canvas( pdfCanvas, pdfDoc, rectangle);
Scanner myObj = new Scanner(System.in); // Create a Scanner object
System.out.println("Enter text to add");
String addText = myObj.nextLine(); // Read user input
Paragraph p = new Paragraph(addText);
Scanner myObj1 = new Scanner(System.in); // Create a Scanner object
PdfFont font = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
p.setFont(font);
canvas.add(p);
pdfDoc.close();
canvas.close();
With PdfDocument pdfDoc = new PdfDocument(new PdfWriter(field1)) you will always create a new document with new content. You are now ignoring the original content. You must open the PDF in stamping mode.
Refer to the iText API: https://api.itextpdf.com/iText7/java/7.1.4/com/itextpdf/kernel/pdf/PdfDocument.html
Constructor and Description
PdfDocument(PdfReader reader)
Open PDF document in reading mode.
PdfDocument(PdfReader reader, DocumentProperties properties)
Open PDF document in reading mode.
PdfDocument(PdfReader reader, PdfWriter writer)
Opens PDF document in the stamping mode.
PdfDocument(PdfReader reader, PdfWriter writer, StampingProperties properties)
Open PDF document in stamping mode.
PdfDocument(PdfWriter writer)
Open PDF document in writing mode.
PdfDocument(PdfWriter writer, DocumentProperties properties)
Open PDF document in writing mode.

how to set attributes for existing pdf that contains only images using java itext?

I would like to set attributes to pdf before uploading it into a server.
Document document = new Document();
try
{
OutputStream file = new FileOutputStream({Localpath});
PdfWriter.getInstance(document, file);
document.open();
//Set attributes here
document.addTitle("TITLE");
document.close();
file.close();
} catch (Exception e)
{
e.printStackTrace();
}
But its not working. The file is getting corrupted
In a comment to another answer the OP clarified:
I want to set attributes to an existing pdf(not to create new pdf)
Obviously, though, his code creates a new document from scratch (as is obvious from the fact that a mere FileOutputStream is used to access the file, no reading, only writing).
To manipulate an existing PDF, one has to use a PdfReader / PdfWriter couple. Bruno Lowagie provided an example for that in his answer to the stack overflow question "iText setting Creation Date & Modified Date in sandbox.stamper.SuperImpose.java":
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Map info = reader.getInfo();
info.put("Title", "New title");
info.put("CreationDate", new PdfDate().toString());
stamper.setMoreInfo(info);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XmpWriter xmp = new XmpWriter(baos, info);
xmp.close();
stamper.setXmpMetadata(baos.toByteArray());
stamper.close();
reader.close();
}
(ChangeMetadata.java)
As you see the code sets the metadata both in the ol'fashioned PDF info dictionary (stamper.setMoreInfo) and in the XMP metadata (stamper.setXmpMetadata).
Obviously src and dest should not be the same here.
Without a second file
In yet another comment the OP clarified that he had already tried a similar solution but that he wants to prevent the
Temporary existence of second file
This can easily be prevented by first reading the original PDF into a byte[] and then stamping to it as the target file. E.g. if File singleFile references the original file which is also to be the target file, you can implement:
byte[] original = Files.readAllBytes(singleFile.toPath());
PdfReader reader = new PdfReader(original);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(singleFile));
Map<String, String> info = reader.getInfo();
info.put("Title", "New title");
info.put("CreationDate", new PdfDate().toString());
stamper.setMoreInfo(info);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XmpWriter xmp = new XmpWriter(baos, info);
xmp.close();
stamper.setXmpMetadata(baos.toByteArray());
stamper.close();
reader.close();
(UpdateMetaData test testChangeTitleWithoutTempFile)

I can't value fields iText 2.1.7

We have created PDF files via a template and we fill the fields with values from the database. "Name field" = "value".
Now we want to read the value of one of the fields in the PDF file we created earlier.
CODE
reader = new PdfReader("C:\\temp\\letter.pdf");
baos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, baos);
form = stamper.getAcroFields();
System.out.println(form.getField("CUSTOMER-NAME"));
But when I do that returns "null" and it does not recognize any field. It is as if the fields does not exist.
Any help would appreciated. Thanks.
Hi,
I have 2 file types. One has been flattened :-( and another one no.
For the last file, i am using this code and it work:
PdfReader reader = new PdfReader(FILE);
PdfDictionary root = reader.getCatalog();
PdfDictionary form1 = root.getAsDict(PdfName.ACROFORM);
PdfArray fields = form1.getAsArray(PdfName.FIELDS);
PdfDictionary page;
PdfArray annots;
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
page = reader.getPageN(i);
annots = page.getAsArray(PdfName.ANNOTS);
for (int j = 0; j < annots.size(); j++) {
fields.add(annots.getAsIndirectObject(j));
}
}
AcroFields form2 = reader.getAcroFields();
Thanks a lot!!!
Regards,
Muni
As bruno suggested - if you only want to read, only use the PdfReader...
Try the following code and see whether something is printed out:
PdfReader reader = new PdfReader("C:\\temp\\letter.pdf");
AcroFields acroFields = reader.getAcroFields();
Map<String, AcroFields.Item> fields = acroFields.getFields();
Iterator<Entry<String, Item>> it = fields.entrySet().iterator();
while (it.hasNext()) {
Entry<String, Item> entry = it.next();
String value = acroFields.getField(entry.getKey());
System.out.println(entry.getKey()+":"+value);
}
There could be many reasons why your field is not found:
the PDF doesn't contain PDF form fields at all (pdf has been flattened)
the field name is a different one (e.g. Customer-Name)
you field has a parent (so the name is parent.CUSTOMER-NAME)
the form fields were not correctly inserted
...
Can you see the fields in e.g. adobe reader?

Convert a PDF with forms to a PDF with text only (preserve data) using iText

I have multiple PDFs that get populated with multiple records (a.pdf,b.pdf,c[0-9].pdf,d[0-9].pdf,ez.pdf) using acroforms and pdfbox.
The resulting files (aflat.pdf,bflat.pdf,c[0-9]flat.pdf,d[0-9]flat.pdf,ezflat.pdf) should have their forms(dictionaries and whatever adobe uses) removed but the fields filled as raw text saved on the pdf (setReadOnly is not what I want!).
PdfStamper can only remove fields without saving their content but I've found some references to PdfContentByte as a way to save the content. Alas, the documentation is too brief to understand how I should do this.
As a last resort I could use FieldPosition to write directly on the PDF. Has anyone ever encountered such problem? How do I solve it?
UPDATE: Saving a single page of b.pdf yields a valid bfilled.pdf but a blank bflattened.pdf. Saving the whole document solved the issue.
populateB();
try (PDDocument doc = new PDDocument(); FileOutputStream stream = new FileOutputStream("bfilled.pdf")) {
//importing the page will corrupt the fields
/*wrong approach*/doc.importPage((PDPage)pdfDocuments.get(0).getDocumentCatalog().getAllPages().get(0));
/*wrong approach*/doc.save(stream);
//save the whole document instead
pdfDocuments.get(0).save(stream);//<---right approach
}
try (FileOutputStream stream = new FileOutputStream("bflattened.pdf")) {
PdfStamper stamper = new PdfStamper(new PdfReader("bfilled.pdf"), stream);
stamper.setFormFlattening(true);
stamper.close();
}
Use PdfStamper.setFormFlattening(true) to get rid of the fields and write them as content.
Always use the whole page when working with acroforms
populateB();
try (PDDocument doc = new PDDocument(); FileOutputStream stream = new FileOutputStream("bfilled.pdf")) {
//importing the page will corrupt the fields
doc.importPage((PDPage) pdfDocuments.get(0).getDocumentCatalog().getAllPages().get(0));
doc.save(stream);
//save the whole document instead
pdfDocuments.get(0).save(stream);
}
try (FileOutputStream stream = new FileOutputStream("bflattened.pdf")) {
PdfStamper stamper = new PdfStamper(new PdfReader("bfilled.pdf"), stream);
stamper.setFormFlattening(true);
stamper.close();
}

How to add an image in the last page of pdf using iText?

How do i add an image on the last page of existing PDF document. Please help me.
The following example adds an image to the second page of an existing pdf using Itext 5.
String src = "c:/in.pdf;
String dest = "c:/out.pdf";
String IMG = "C:/image.jpg";
try {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
com.itextpdf.text.Image image = com.itextpdf.text.Image.getInstance(IMG);
image.setAbsolutePosition(36, 400);
PdfContentByte over = stamper.getOverContent(2);
over.addImage(image);
stamper.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
You can read the text from the PDF using the same ITEXT library.Try this
PdfReader reader = new PdfReader(INPUTFILE);
int n = reader.getNumberOfPages();
PdfTextExtractor parser =new PdfTextExtractor(new PdfReader("C:/Text.pdf"));
parser.getTextFromPage(3); // Extracting the content from a particular page.
After you have add your data ,You can load images either from file or from a URL, like this:
Image image1 = Image.getInstance("watermark.png");
document.add(image1);
String imageUrl = "http://applause-voice.com/wp-content/uploads/2011/04/1hello.jpg";
Image image2 = Image.getInstance(new URL(imageUrl));
document.add(image2);
If you will add this code at the end of your Java Program , then the image will automatically comes at the end of your page.
The best solution for me was to create a new in-memory PDF document with the image I want to add, then copy this page to the original document.
// Create a separate doc for image
var pdfDocWithImageOutStream = new ByteArrayOutputStream();
var pdfDocWithImage = new PdfDocument(new PdfWriter(pdfDocWithImageOutStream).setSmartMode(true));
var docWithImage = new Document(pdfDocWithImage, destinationPdf.getDefaultPageSize());
// Add image to the doc
docWithImage.add(image);
// Close the doc to save data
docWithImage.close();
pdfDocWithImage.close();
// Open the same doc for reading
pdfDocWithImage = new PdfDocument(new PdfReader(new ByteArrayInputStream(pdfDocWithImageOutStream.toByteArray())));
docWithImage = new Document(pdfDocWithImage, destinationPdf.getDefaultPageSize());
// Copy page to original (destinationPdf)
pdfDocWithImage.copyPagesTo(1, pdfDocWithImage.getNumberOfPages(), destinationPdf);
docWithImage.close();
pdfDocWithImage.close();

Categories