iText - add content to existing PDF file

JavaPdfItext

Java Problem Overview


I want to do the following with iText:

(1) parse an existing PDF file

(2) add some data to it, on the existing single page of the document (such as a timestamp)

(3) write out the document

I just can't seem to figure out how to do this with iText. In pseudo code I would do this:

Document document = reader.read(input);
document.add(new Paragraph("my timestamp"));
writer.write(document, output);

But for some reason iText's API is so dauntingly complicated that I can't wrap my head around it. The PdfReader actually holds the document model or something (rather than spitting out a document), and you need a PdfWriter to read pages from it... eh?

Java Solutions


Solution 1 - Java

iText has more than one way of doing this. The PdfStamper class is one option. But I find the easiest method is to create a new PDF document then import individual pages from the existing document into the new PDF.

// Create output PDF
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte cb = writer.getDirectContent();

// Load existing PDF
PdfReader reader = new PdfReader(templateInputStream);
PdfImportedPage page = writer.getImportedPage(reader, 1); 

// Copy first page of existing PDF into output PDF
document.newPage();
cb.addTemplate(page, 0, 0);

// Add your new data / text here
// for example...
document.add(new Paragraph("my timestamp")); 

document.close();

This will read in a PDF from templateInputStream and write it out to outputStream. These might be file streams or memory streams or whatever suits your application.

Solution 2 - Java

Gutch's code is close, but it'll only work right if:

  • There are no annotations (links, fields, etc), no Document Structure/Marked Content, no bookmarks, no document-level script, etc, etc, etc...
  • The page size happens to be A.4 (decent odds, but it won't work on any ol' PDF you happen to come across)
  • You don't mind losing all the original document metadata (producer, creation date, possibly author/title/keywords), and maybe the document ID. You can't copy the creation date and doc ID unless you do some pretty deep hackery on iText itself).

The Approved Method is to do it the other way around. Open the existing document with a PdfStamper, and use the returned PdfContentByte from getOverContent() to write text (and whatever else you might need) directly to the page. No second document needed.

And you can use a ColumnText to handle layout and such for you... no need to get down and dirty with beginText(),setFontAndSize(),drawText(),drawText()...,endText().

Solution 3 - Java

This is the most complicated scenario I can imagine: I have a PDF file created with Ilustrator and modified with Acrobat to have AcroFields (AcroForm) that I'm going to fill with data with this Java code, the result of that PDF file with the data in the fields is modified adding a Document.

Actually in this case I'm dynamically generating a background that is added to a PDF that is also dynamically generated with a Document with an unknown amount of data or pages.

I'm using JBoss and this code is inside a JSP file (should work in any JSP webserver).

Note: if you are using IExplorer you must submit a HTTP form with POST method to be able to download the file. If not you are going to see the PDF code in the screen. This does not happen in Chrome or Firefox.

<%@ page import="java.io.*, com.lowagie.text.*, com.lowagie.text.pdf.*" %><%

response.setContentType("application/download");
response.setHeader("Content-disposition","attachment;filename=listaPrecios.pdf" ); 	

// -------- FIRST THE PDF WITH THE INFO ----------
String str = "";
// lots of words
for(int i = 0; i < 800; i++) str += "Hello" + i + " ";
// the document
Document doc = new Document( PageSize.A4, 25, 25, 200, 70 );
ByteArrayOutputStream streamDoc = new ByteArrayOutputStream();
PdfWriter.getInstance( doc, streamDoc );
// lets start filling with info
doc.open();
doc.add(new Paragraph(str));
doc.close();
// the beauty of this is the PDF will have all the pages it needs
PdfReader frente = new PdfReader(streamDoc.toByteArray());
PdfStamper stamperDoc = new PdfStamper( frente, response.getOutputStream());

// -------- THE BACKGROUND PDF FILE -------
// in JBoss the file has to be in webinf/classes to be readed this way
PdfReader fondo = new PdfReader("listaPrecios.pdf");
ByteArrayOutputStream streamFondo = new ByteArrayOutputStream();
PdfStamper stamperFondo = new PdfStamper( fondo, streamFondo);
// the acroform
AcroFields form = stamperFondo.getAcroFields();
// the fields 
form.setField("nombre","Avicultura");
form.setField("descripcion","Esto describe para que sirve la lista ");
stamperFondo.setFormFlattening(true);
stamperFondo.close();
// our background is ready
PdfReader fondoEstampado = new PdfReader( streamFondo.toByteArray() );

// ---- ADDING THE BACKGROUND TO EACH DATA PAGE ---------
PdfImportedPage pagina = stamperDoc.getImportedPage(fondoEstampado,1);
int n = frente.getNumberOfPages();
PdfContentByte background;
for (int i = 1; i <= n; i++) {
	background = stamperDoc.getUnderContent(i);
	background.addTemplate(pagina, 0, 0);
}
// after this everithing will be written in response.getOutputStream()
stamperDoc.close(); 
%>

There is another solution much simpler, and solves your problem. It depends the amount of text you want to add.

// read the file
PdfReader fondo = new PdfReader("listaPrecios.pdf");
PdfStamper stamper = new PdfStamper( fondo, response.getOutputStream());
PdfContentByte content = stamper.getOverContent(1);
// add text
ColumnText ct = new ColumnText( content );
// this are the coordinates where you want to add text
// if the text does not fit inside it will be cropped
ct.setSimpleColumn(50,500,500,50);
ct.setText(new Phrase(str, titulo1));
ct.go();

Solution 4 - Java

Document document = new Document();
	PdfWriter writer = PdfWriter.getInstance(document, 
		new FileOutputStream("E:/TextFieldForm.pdf"));
	document.open();
	
	PdfPTable table = new PdfPTable(2);
	table.getDefaultCell().setPadding(5f); // Code 1
	table.setHorizontalAlignment(Element.ALIGN_LEFT);
	PdfPCell cell;		
	
	// Code 2, add name TextField		
	table.addCell("Name"); 
	TextField nameField = new TextField(writer, 
		new Rectangle(0,0,200,10), "nameField");
	nameField.setBackgroundColor(Color.WHITE);
	nameField.setBorderColor(Color.BLACK);
	nameField.setBorderWidth(1);
	nameField.setBorderStyle(PdfBorderDictionary.STYLE_SOLID);
	nameField.setText("");
	nameField.setAlignment(Element.ALIGN_LEFT);
	nameField.setOptions(TextField.REQUIRED);				
	cell = new PdfPCell();
	cell.setMinimumHeight(10);
	cell.setCellEvent(new FieldCell(nameField.getTextField(), 
		200, writer));
	table.addCell(cell);
	
	// force upper case javascript
	writer.addJavaScript(
		"var nameField = this.getField('nameField');" +
		"nameField.setAction('Keystroke'," +
		"'forceUpperCase()');" +
		"" +
		"function forceUpperCase(){" +
		"if(!event.willCommit)event.change = " +
		"event.change.toUpperCase();" +
		"}");
	
	
	// Code 3, add empty row
	table.addCell("");
	table.addCell("");
	
	
	// Code 4, add age TextField
	table.addCell("Age");
	TextField ageComb = new TextField(writer, new Rectangle(0,
		 0, 30, 10), "ageField");
	ageComb.setBorderColor(Color.BLACK);
	ageComb.setBorderWidth(1);
	ageComb.setBorderStyle(PdfBorderDictionary.STYLE_SOLID);
	ageComb.setText("12");
	ageComb.setAlignment(Element.ALIGN_RIGHT);
	ageComb.setMaxCharacterLength(2);
	ageComb.setOptions(TextField.COMB | 
		TextField.DO_NOT_SCROLL);
	cell = new PdfPCell();
	cell.setMinimumHeight(10);
	cell.setCellEvent(new FieldCell(ageComb.getTextField(), 
		30, writer));
	table.addCell(cell);
	
	// validate age javascript
	writer.addJavaScript(
		"var ageField = this.getField('ageField');" +
		"ageField.setAction('Validate','checkAge()');" +
		"function checkAge(){" +
		"if(event.value < 12){" +
		"app.alert('Warning! Applicant\\'s age can not" +
		" be younger than 12.');" +
		"event.value = 12;" +
		"}}");		
	
	
	
	// add empty row
	table.addCell("");
	table.addCell("");
	
	
	// Code 5, add age TextField
	table.addCell("Comment");
	TextField comment = new TextField(writer, 
		new Rectangle(0, 0,200,	100), "commentField");
	comment.setBorderColor(Color.BLACK);
	comment.setBorderWidth(1);
	comment.setBorderStyle(PdfBorderDictionary.STYLE_SOLID);
	comment.setText("");
	comment.setOptions(TextField.MULTILINE | 
		TextField.DO_NOT_SCROLL);
	cell = new PdfPCell();
	cell.setMinimumHeight(100);
	cell.setCellEvent(new FieldCell(comment.getTextField(), 
		200, writer));
	table.addCell(cell);
	
	
	// check comment characters length javascript
	writer.addJavaScript(
		"var commentField = " +
		"this.getField('commentField');" +
		"commentField" +
		".setAction('Keystroke','checkLength()');" +
		"function checkLength(){" +
		"if(!event.willCommit && " +
		"event.value.length > 100){" +
		"app.alert('Warning! Comment can not " +
		"be more than 100 characters.');" +
		"event.change = '';" +
		"}}");			

	// add empty row
	table.addCell("");
	table.addCell("");
	
	
	// Code 6, add submit button	
	PushbuttonField submitBtn = new PushbuttonField(writer,
			new Rectangle(0, 0, 35, 15),"submitPOST");
	submitBtn.setBackgroundColor(Color.GRAY);
	submitBtn.
		setBorderStyle(PdfBorderDictionary.STYLE_BEVELED);
	submitBtn.setText("POST");
	submitBtn.setOptions(PushbuttonField.
		VISIBLE_BUT_DOES_NOT_PRINT);
	PdfFormField submitField = submitBtn.getField();
	submitField.setAction(PdfAction
	.createSubmitForm("",null, PdfAction.SUBMIT_HTML_FORMAT));
	
	cell = new PdfPCell();
	cell.setMinimumHeight(15);
	cell.setCellEvent(new FieldCell(submitField, 35, writer));
	table.addCell(cell);
	
	
	
	// Code 7, add reset button
	PushbuttonField resetBtn = new PushbuttonField(writer,
			new Rectangle(0, 0, 35, 15), "reset");
	resetBtn.setBackgroundColor(Color.GRAY);
	resetBtn.setBorderStyle(
		PdfBorderDictionary.STYLE_BEVELED);
	resetBtn.setText("RESET");
	resetBtn
	.setOptions(
		PushbuttonField.VISIBLE_BUT_DOES_NOT_PRINT);
	PdfFormField resetField = resetBtn.getField();
	resetField.setAction(PdfAction.createResetForm(null, 0));
	cell = new PdfPCell();
	cell.setMinimumHeight(15);
	cell.setCellEvent(new FieldCell(resetField, 35, writer));
	table.addCell(cell);		
			
	document.add(table);
	document.close();
}


class FieldCell implements PdfPCellEvent{
	
	PdfFormField formField;
	PdfWriter writer;
	int width;
	
	public FieldCell(PdfFormField formField, int width, 
		PdfWriter writer){
		this.formField = formField;
		this.width = width;
		this.writer = writer;
	}
	
	public void cellLayout(PdfPCell cell, Rectangle rect, 
		PdfContentByte[] canvas){
		try{
			// delete cell border
			PdfContentByte cb = canvas[PdfPTable
				.LINECANVAS];
			cb.reset();
			
			formField.setWidget(
				new Rectangle(rect.left(), 
					rect.bottom(), 
					rect.left()+width, 
					rect.top()), 
					PdfAnnotation
					.HIGHLIGHT_NONE);
			
			writer.addAnnotation(formField);
		}catch(Exception e){
			System.out.println(e);
		}
	}
}

Solution 5 - Java

how-to-update-a-pdf-without-creating-a-new-pdf

iText 7, please pay attention to version

PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
//manipulate pdf…
pdfDoc.close();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWouter LievensView Question on Stackoverflow
Solution 1 - JavagutchView Answer on Stackoverflow
Solution 2 - JavaMark StorerView Answer on Stackoverflow
Solution 3 - JavaAlfredView Answer on Stackoverflow
Solution 4 - JavaRamesh. VeguruView Answer on Stackoverflow
Solution 5 - JavaSteven JuView Answer on Stackoverflow