I want to write some text in a PDF file.
Approach 1: Using AcroForms.
To me, this the simplest approach. But we have some performance doubts. Using this approach, I'll be embedding a PDF template that contains in my resources folder and populating already existing fields that I've already created in Acrobat like so:
PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
form.setGenerateAppearance(true);
form.getField("name")
.setVisibility(PdfFormField.VISIBLE)
.setReadOnly(true)
.setValue("Hamza Belmellouki");
// other fields being set
Approach 2: Generate the PDF from Scratch.
In this approach, I write the whole content to the file(Static content as well as dynamic one) and generate it.
In terms of performance, I am wondering which approach should we go with?
Related
I am watermarking pdf document using itext7 library. it is preserving layers and shows one of its signature invalid.
I want to flatten the created document.
When i tried saving the document manually using Adobe print option, it flattens all signature and makes the document as valid document. Same functionality i want with java program.
Is there any way using java program, we can flatten pdf document?
According to your tag selection you appear to be using iText 7 for Java.
How to flatten a PDF AcroForm form using iText 7 is explained in the iText 7 knowledge base example Flattening a form. The pivotal code is:
PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
// If no fields have been explicitly included, then all fields are flattened.
// Otherwise only the included fields are flattened.
form.flattenFields();
pdfDoc.close();
(https://kb.itextpdf.com/home/it7kb/examples/flattening-a-form visited 2021-05-24)
In iText v5 there was the option to make 'smart' concatenation of PDF documents:
public PdfConcatenate(OutputStream os, boolean smart) throws DocumentException
Creates an instance of the concatenation class.
Parameters:
os - the OutputStream for the PDF document
smart - do we want PdfCopy to detect redundant content?
The initialization I was doing would be something like:
PdfConcatenate concatenatedPdf = new PdfConcatenate(outputStream, true);
In iText 7 I read we should use the copyPages function. Something like:
[...]
PdfDocument concatenatedPdf = new PdfDocument(writer);
PdfDocument docToAdd = new PdfDocument(pdfReader);
docToAdd.copyPagesTo(1, docToAdd.getNumberOfPages(), concatenatedPdf);
I'm migrating a logic to merge documents from iText v5 to v7. For a sample test in v5 with PdfConcatenate and the flag 'smart' the result PDF is 177 KB, in v7 is 763 KB. Is there a way to detect this redundant content in iText v7?
First of all, iText7 provides a convenient class called PdfMerger for merging PDFs.
Here is a sample how to use it:
PdfDocument sourceDocument = new PdfDocument(new PdfReader(filename));
PdfMerger resultDocument = new PdfMerger(new PdfDocument(new PdfWriter(resultFile)));
resultDocument.merge(sourceDocument, fromPage, toPage);
resultDocument.close();
sourceDocument.close();
Of course in the example only one set of pages from source document is added to the resultant document but you can call merge function as many times as you like.
Now when you want the resultant file to be as small in terms of file size as possible, you need to specify some settings for the destination PdfDocument that you feed to the PdfMerger.
First of all, you can tweak the compression level for streams to use more CPU and time but compress better:
PdfMerger resultDocument = new PdfMerger(new PdfDocument(
new PdfWriter(resultFile, new WriterProperties().setCompressionLevel(CompressionConstants.BEST_COMPRESSION))));
To compress even better you can use full compression. That would not only better compress streams (content of the page, images, fonts), but also would compress PDF objects that usually consume many bits in the out file size. This can be done like this:
PdfMerger resultDocument = new PdfMerger(new PdfDocument(
new PdfWriter(resultFile, new WriterProperties().setFullCompressionMode(true))));
In case source documents have same objects by default you might have some duplication. So-called "Smart Mode" provides a possibility to avoid such duplication and optimize the file size for cases when there are many duplicating objects. This would be the closes analogue to the "smart" flag you refer to in your iText 5 code. You can enable smart mode in iText 7 in the following way:
PdfMerger resultDocument = new PdfMerger(new PdfDocument(
new PdfWriter(resultFile, new WriterProperties().useSmartMode())));
I am using iText7 for pdf signature workflow, i followed the samples provided with i7js-signatures. However my requirement is to take an input pdf file, add sequential signatures to it, and further pass it for signature.
i tried splitting up the process in two steps.
Take input pdf and add sequential signature panel, in the intermediate_output file.
public void createForm() throws IOException {
PdfDocument pdfDoc = new PdfDocument(new PdfReader(FORM),new PdfWriter(TMP));
//PdfDocument pdfDoc = new PdfDocument(new PdfWriter(FORM));
pdfDoc.addNewPage();
Document doc = new Document(pdfDoc);
Table table = new Table(1);
table.addCell("Signer 1: Alice");
table.addCell(createSignatureFieldCell("sig1"));
table.addCell("Signer 2: Bob");
table.addCell(createSignatureFieldCell("sig2"));
table.addCell("Signer 3: Carol");
table.addCell(createSignatureFieldCell("sig3"));
doc.add(table);
doc.close();
}
Take intermediate_output file and sign it.
While running the step two with output of step 1, i am getting com.itextpdf.kernel.PdfException: error.reading.objstm
Exception in thread "main" com.itextpdf.kernel.PdfException: error.reading.objstm
at com.itextpdf.kernel.pdf.PdfReader.readObjectStream(PdfReader.java:508)
at com.itextpdf.kernel.pdf.PdfReader.readObject(PdfReader.java:1014)
at com.itextpdf.kernel.pdf.PdfReader.readObject(PdfReader.java:533)
at com.itextpdf.kernel.pdf.PdfIndirectReference.getRefersTo(PdfIndirectReference.java:128)
at com.itextpdf.kernel.pdf.PdfIndirectReference.getRefersTo(PdfIndirectReference.java:132)
at com.itextpdf.kernel.pdf.PdfArray.get(PdfArray.java:376)
at com.itextpdf.kernel.pdf.PdfArray.get(PdfArray.java:237)
Input pdf:
Intermediate pdf page 1:
Intermediate pdf page 2:
Please guide me in case, i am doing something wrong here.
The underlying iText 7 bug/peculiarity is the same as described in this answer where the table is built across four pages but all the fields turns up on the last page.
As you clarified, though, in a comment, you want the table and the fields on the last page anyways. Thus, all we need to do is move the table to the last page, too.
This actually is quite simple, merely add an appropriate AreaBreak before adding the table:
doc.add(new AreaBreak(AreaBreakType.LAST_PAGE));
doc.add(table);
(AddSignatureField test testAddSignaturesInTable)
You updated your iText version in the context of this question. Meanwhile there have been considerable changes in the table creation code. Thus, you will probably want to also set a width of the signature cell, e.g.
Cell cell = new Cell();
cell.setHeight(50);
cell.setWidth(200);
cell.setNextRenderer(new SignatureFieldCellRenderer(cell, name));
return cell;
(AddSignatureField method createSignatureFieldCell)
In my project, some data sets are needed to be exported in PDF format.
I learned that iText is helpful, and PdfpTable can do the work, but it needs much code to deal with styles. While using PDF template can save time and code for adjusting style, but I can only set certain fields left in the template.
Can you give me some suggestions to show the data sets using commands like foreach? Thanks in advance!
Here are my code using pdfpTable, which has done the work, but the code is a little ugly:
PdfPTable pdfTable = createNewPDFTable();
for (int i = 0; i < dataSet.size(); i++) {
MetaObject metaObject = SystemMetaObject.forObject(dataSet.get(i));
for (String field : fields) {
Phrase phrase = new Phrase(String.valueOf(metaObject.getValue(field) != null ? metaObject.getValue(field) : "")
, PDFUtil.createChineseSong(DEFAULT_CELL_FONT_SIZE));
PdfPCell fieldCell = new PdfPCell(phrase);
fieldCell.setBorder(Rectangle.NO_BORDER);
fieldCell.setFixedHeight(DEFAULT_COLUMN_HEIGHT);
fieldCell.setHorizontalAlignment(Element.ALIGN_CENTER);
fieldCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
pdfTable.addCell(fieldCell);
}
}
Here are some code using pdfp template,which is copied from itext examples, the work is unfinished yet because i haven't find a proper way to show the data set.
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
form.setField("text_1", "Bruno Lowagie");
form.setFieldProperty("text_1", "setfflags", PdfFormField.FF_READ_ONLY, null);
There is an inconsistency in your question. You write: PdfpTable can do the work, but it needs much code to deal with styles. However, in your first code snippet, you don't really create your PDFs the way one would expect. Instead of producing a high volume of finished PDFs, you create use PdfPTable to create a template. I assume you then use that template to create a high volume of finished PDFs.
If you want to use a template and populate it afterwards, you shouldn't create your form using iText. Create it manually, for instance using Open Office or Libre Office. See for instance the example in chapter 6 of my book (section 6.3.5). Create the template with a tool that has a GUI, then fill out that template many times using iText.
This approach has some down-sides: all the content has to fit the fields you define. All fields have a fixed position on a fixed page.
If "applying styles through code" is a problem, you may want to follow the approach described in the ZUGFeRD book. In that book, we create HTML first: Creating HTML invoices.
Once you have the HTML, then convert the HTML to PDF, and use CSS to apply styles: Creating PDF invoices.
This is how we create a ZUGFeRDDocument:
ZugferdDocument pdfDocument = new ZugferdDocument(
new PdfWriter(fos), ZugferdConformanceLevel.ZUGFeRDComfort,
new PdfOutputIntent("Custom", "", "http://www.color.org",
"sRGB IEC61966-2.1", new FileInputStream(INTENT)));
pdfDocument.addFileAttachment(
"ZUGFeRD invoice", dom.toXML(), "ZUGFeRD-invoice.xml",
PdfName.ApplicationXml, new PdfDictionary(), PdfName.Alternative);
pdfDocument.setTagged();
HtmlConverter.convertToPdf(
new ByteArrayInputStream(html), pdfDocument, getProperties());
The getProperties() method looks like this:
public ConverterProperties getProperties() {
if (properties == null) {
properties = new ConverterProperties()
.setBaseUri("resources/zugferd/");
}
return properties;
}
You can find other examples on how to use HTML to PDF here: pdfHTML add-on (read the introduction).
Note that you are using an old version of iText. The examples I shared are using iText 7. There's a huge difference between iText 5 and iText 7.
I am using iText 2.1.7 to generate a document from a database. One of the fields I need to add is in XHTML format. I can use the HTMLWorker class to generate the HTML but this is a bit limited.
I convert this to XHTML using the following code:
String url = chapterDesc.getString("description").toString(); // get the HTML string from the database
org.w3c.dom.Document doc = XMLResource.load(new ByteArrayInputStream(url.getBytes())).getDocument();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(doc, null);
ByteArrayOutputStream os = new ByteArrayOutputStream();
renderer.layout();
renderer.createPDF(os);
I want to add this information to the document in memory. Is this possible?
Do I need to use PdfStamper? I believe that this requires the document to be closed? If it is possible I would like to avoid using multiple passes to add these descriptions.
Flying saucer does not work correctly with any version of iText other than 2.0.8. Also since you meantioned creating the pdf in memory are you using JSF, JSP, or servlets? If you are than you can just send your ByteArrayOutputStream as a response on one of these pages using something along the lines of
response.setContentType("application/pdf");
response.setContentLength(os.size());
os.writeTo(response.getOutputStream());
response.flushBuffer();
I know it's been more than two years since you've asked, but I'm facing the same problem. I googled for a solution and apparently there is none anywhere to be found. So I had to develop my own and I thought I might as well share it. Hope it'll be useful to someone.
I tried to use flying saucer as you did, but it didn't work for me. My piece of HTML was just a simple table so I could use iText HTMLWorker to do the parsing.
So first I get a PdfStamper as you suggested.
PdfReader template = new PdfReader(templateFileName);
PdfStamper editablePage = new PdfStamper(template, reportOutStream);
Then I work with the document (fill the fields, insert some images) and after that I need to insert an HTML snippet.
//getting a 'canvas' to add parsed elements
final ColumnText page = new ColumnText(editablePage.getOverContent(pageNumber));
//finding out the page sizefinal
Rectangle pagesize = editablePage.getReader().getPageSize(pageNumber);
//you can define any size here, that will be where your parsed elements will be added
page.setSimpleColumn(0, 0, pagesize.getWidth(), pagesize.getHeight());
If you need simple styling, HTMLWorker can do some
StyleSheet styles = new StyleSheet();
styles.loadStyle("h1", "color", "#008080");
//parsing
List<Element> parsedTags = HTMLWorker.parseToList(new StringReader(htmlSnippet), styles);
for (Element tag : parsedTags)
{
page.addElement(tag);
page.go();
}
These are just some basic ideas of how to do that, hope it helps.