I'm trying to write Hebrew characters to a PDF.
I created HTML in the servlet by appending to a string buffer.
Then passing the same to xmlworkerhelper.
Also registered the arial.ttf, arialuni.ttf and arialbold.ttf fonts using XMLWorkerFontProvider and using the registered fonts in xmlworkerhelper.
Used the below fonts for registering arial.ttf, ArialBold.ttf, arialuni.ttf;
Any solutions for this issue will be very helpful. Let me know if more details required.
Below is the snippet.
XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider();
fontImp.register(arial);
fontImp.register(arialbold);
fontImp.register(arialuni);
byte[] byte6=PDFtext.toString().getBytes("ISO-8859-1");
String bufferfinalPDF=new String(byte6, "UTF-8");
XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(bufferfinalPDF.getBytes(Charset.forName(baseFont))),null,Charset.forName(baseFont),fontImp);
Solution or a suggestion on this is very helpful.
Related
I have searched and searched and cannot find the answer to my problem. I've tried many different approaches in my code, but I've hit a wall and I'm not sure where to go from here. I seem to be wanting to do the same thing as these two threads:
Trying to insert an image into a pdf in c#
Add image in an existing PDF with itextsharp
They are very similar and the answer is the same. However, when I use that exact code, the result is a PDF without an image. Here is my code:
using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
using (var newFileStream = new FileStream(fileNameNew, FileMode.Create))
{
var pdfReader = new PdfReader(existingFileStream);
var stamper = new PdfStamper(pdfReader, newFileStream, '\0', true);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
foreach (var field in form.Fields)
{
if (field.Key == "form1[0].ec_Bldg_Photo_1[0].ImageField2[0]")
{
PushbuttonField imageField = form.GetNewPushbuttonFromField(field.Key);
imageField.Layout = PushbuttonField.LAYOUT_ICON_ONLY;
imageField.IconReference = null;
imageField.ProportionalIcon = true;
imageField.Image = Image.GetInstance(#"PATH_TO_IMAGE\front.jpg");
form.ReplacePushbuttonField(field.Key, imageField.Field);
}
}
stamper.FormFlattening = false;
stamper.Close();
pdfReader.Close();
}
I have tried to rule out all of the obvious things. My path to the image is correct, the field is indeed a PushbuttonField when I read the existing PDF field and get the field type. If I open the PDF in Adobe Reader and click on the placeholder for the image, it allows me to pick a file from my PC. When I place an image in the file, save, and then read in that PDF, I can then change my code to this:
imageField.ProportionalIcon = false;
And now all of sudden the image is stretched on the saved copy. So I see that it is changing this part but this is when I enter the image manually in Adobe Reader. When I read in the field after I set that image in Adobe Reader and it shows correctly, I see a couple interesting things. The field.Image property IS NULL and the field.IconReference is NOT NULL. When I use the original code to try and insert the image, it is reversed, where Image is NOT NULL but IconReference IS NULL
Any help would be greatly appreciated, thank you!!
EDIT 1: Ok so I didn't see it the first time, but I went back and checked more thoroughly and I did find that key. Here it is:
Several things are at play here.
Usage Rights:
The PDF is digitally signed with a private key owned by Adobe.
You can see this using RUPS here (in your screen shot you didn't go deep enough):
This has two implications:
The signature unlocks special permissions in Adobe Reader, such as the permission to save a filled out form locally.
Making any changes to the original PDF breaks the signature and removes the special permissions leading to an ugly error message in Adobe Reader.
This functionality is deprecated in (and even removed from) PDF 2.0. It's old technology that became obsolete with the emergence of PDF viewers other than Adobe Reader.
My suggestion: remove the usage rights to avoid breaking the signature. See the FAQ entry "Why do I get an error saying that "use of extended features is no longer available"?" iText 7 / iText 5
This is the iText 7 code:
public void removeUsageRights(PdfDocument pdfDoc) {
PdfDictionary perms = pdfDoc.getCatalog().getPdfObject().getAsDictionary(PdfName.Perms);
if (perms == null) {
return;
}
perms.remove(new PdfName("UR"));
perms.remove(PdfName.UR3);
if (perms.size() == 0) {
pdfDoc.getCatalog().remove(PdfName.Perms);
}
}
This is the iText 5 code:
PdfReader reader = new PdfReader(old_file);
if (reader.hasUsageRights()) {
reader.removeUsageRights();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(new_file));
stamper.close();
}
reader.close();
This is the iText 5 answer.
Hybrid Form:
If you click on the /AcroForm entry, you see this:
There is a /Fields array with references to field dictionaries that are also widget annotations. That means that the document has an AcroForm form inside. However, there is also an /XFA entry with a series of XML snippets. That means that the document has an XFA form inside.
In other words: the same form description is added twice inside. You are changing a button in one form (the AcroForm part), but not in the other (the XFA form) and that leads to inconsistencies.
XFA has been deprecated in PDF 2.0 because there weren't many vendors supporting that technology. It's kind of frustrating to be confronted with forms that use deprecated technology.
My suggestion: I would remove the XFA part. See the FAQ entry "Is it safe to remove XFA?" iText 5 / iText 7
In iText 5, removing XFA is done like this:
AcroFields form = stamper.getAcroFields();
form.removeXfa();
Important: my suggestion is to remove all the deprecated functionality from the PDF, but if the government expects that functionality to be present, then you're out of luck. In that case, you will need to use Adobe software to process the form. If that's the case, you could complain to the government that their requirements lead to a de facto vendor lock-in. By the way: iText Software is also a vendor. It's an open source company that offers open source software under the AGPL license. The AGPL license allows free use under certain circumstances (see How do I make sure my software complies with AGPL: How can I use iText for free?) If you don't meet those requirements, you will have to purchase a commercial license for your use of iText.
I want to use iText to convert a series of html file to PDF.
For instance: if have these files:
page1.html
page2.html
page3.html
...
Now I want to create a single PDF file, where page1.html is the first page, page2.html is the second page, and so on...
I know how to convert a single HTML file to a PDF, but I don't know how to combine these different PDFs resulting from this operation into a single PDF.
Before we start: I am not a C# developer, so I can not give you an example in C#. All the iText examples I write, are written in Java. Fortunately, iText and iTextSharp are always kept in sync. In the context of this question, you can rest assure that whatever works for iText will also work for iTextSharp, but you'll have to make small adaptations that are specific to C#. From what I hear from C# developers, this is usually not hard to achieve.
Regarding the answer: there are two answers and answer #2 is generally better than answer #1, but I'm giving both options because there may be specific cases where answer #1 is better.
Test data: I have created 3 simple HTML files, each containing some info about a State in the US:
page1.html: California
page2.html: New York
page3.html: Massachusetts
We are going to use XML Worker to parse these three files and we want a single PDF file as a result.
Answer #1: see ParseMultipleHtmlFiles1 for the full code sample and multiple_html_pages1.pdf for the resulting PDF.
You say that you already succeeded in converting one HTML file into one PDF files. It is assumed that you did it like this:
public byte[] parseHtml(String html) throws DocumentException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, baos);
// step 3
document.open();
// step 4
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new FileInputStream(html));
// step 5
document.close();
// return the bytes of the PDF
return baos.toByteArray();
}
This is not the most efficient way to parse an HTML file (there are other examples on the web site), but it's the simplest way.
As you can see, this method parse an HTML into a PDF file and returns that PDF file in the form of a byte[]. As we want to create a single PDF, we can feed this byte array to a PdfCopy instance, so that we can concatenate multiple documents.
Suppose that we have three documents:
public static final String[] HTML = {
"resources/xml/page1.html",
"resources/xml/page2.html",
"resources/xml/page3.html"
};
We can loop over these three documents, parse them one by one to a byte[], create a PdfReader instance with the PDF bytes, and add the document to the PdfCopy instance using the addDocument() method:
public void createPdf(String file) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(file));
document.open();
PdfReader reader;
for (String html : HTML) {
reader = new PdfReader(parseHtml(html));
copy.addDocument(reader);
reader.close();
}
document.close();
}
This solves your problem, but why do I think it's not the optimal solution?
Suppose that you need to use a special font that needs to be embedded. In that case, every separate PDF file will contain a subset of that font. Different files will require different font subsets, and PdfCopy (nor PdfSmartCopy for that matter) can merge font subsets. This could result in a bloated PDF file with way too many font subsets of the same font.
How do we solve this? That's explained in answer #2.
Answer #2: See ParseMultipleHtmlFiles2 for the full code sample and multiple_html_pages2.pdf for the resulting PDF. You already see the difference in file size: 4.61 KB versus 5.05 KB (and we didn't even introduce embedded fonts).
In this case, we don't parse the HTML to a PDF file the way we did in the parseHtml() method from answer #1. Instead, we parse the HTML to an iText ElementList using the parseToElementList() method. This method requires two Strings. One containing the HTML code, the other one containing CSS values.
We use a utility method to read the HTML file into a String. As for the CSS value, we could pass null to parseToElementList(), but in that case, default styles will be ignored. You'll notice that the <h1> tag we introduced in our HTML will look completely different if you don't pass the default.css that is shipped with XML Worker.
Long story short, this is the code:
public void createPdf(String file) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(file));
document.open();
String css = readCSS();
for (String htmlfile : HTML) {
String html = Utilities.readFileToString(htmlfile);
ElementList list = XMLWorkerHelper.parseToElementList(html, css);
for (Element e : list) {
document.add(e);
}
document.newPage();
}
document.close();
}
We create a single Document and a single PdfWriter instance. We parse the different HTML files into ElementLists one by one, and we add all the elements to the Document.
As you want a new page, each time a new HTML file is parsed, I introduced a document.newPage(). If you remove this line, you can add the three HTML pages on a single page (which wouldn't be possible if you would opt for answer #1).
I want to use iText to convert a series of html file to PDF.
For instance: if have these files:
page1.html
page2.html
page3.html
...
Now I want to create a single PDF file, where page1.html is the first page, page2.html is the second page, and so on...
I know how to convert a single HTML file to a PDF, but I don't know how to combine these different PDFs resulting from this operation into a single PDF.
Before we start: I am not a C# developer, so I can not give you an example in C#. All the iText examples I write, are written in Java. Fortunately, iText and iTextSharp are always kept in sync. In the context of this question, you can rest assure that whatever works for iText will also work for iTextSharp, but you'll have to make small adaptations that are specific to C#. From what I hear from C# developers, this is usually not hard to achieve.
Regarding the answer: there are two answers and answer #2 is generally better than answer #1, but I'm giving both options because there may be specific cases where answer #1 is better.
Test data: I have created 3 simple HTML files, each containing some info about a State in the US:
page1.html: California
page2.html: New York
page3.html: Massachusetts
We are going to use XML Worker to parse these three files and we want a single PDF file as a result.
Answer #1: see ParseMultipleHtmlFiles1 for the full code sample and multiple_html_pages1.pdf for the resulting PDF.
You say that you already succeeded in converting one HTML file into one PDF files. It is assumed that you did it like this:
public byte[] parseHtml(String html) throws DocumentException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, baos);
// step 3
document.open();
// step 4
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new FileInputStream(html));
// step 5
document.close();
// return the bytes of the PDF
return baos.toByteArray();
}
This is not the most efficient way to parse an HTML file (there are other examples on the web site), but it's the simplest way.
As you can see, this method parse an HTML into a PDF file and returns that PDF file in the form of a byte[]. As we want to create a single PDF, we can feed this byte array to a PdfCopy instance, so that we can concatenate multiple documents.
Suppose that we have three documents:
public static final String[] HTML = {
"resources/xml/page1.html",
"resources/xml/page2.html",
"resources/xml/page3.html"
};
We can loop over these three documents, parse them one by one to a byte[], create a PdfReader instance with the PDF bytes, and add the document to the PdfCopy instance using the addDocument() method:
public void createPdf(String file) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(file));
document.open();
PdfReader reader;
for (String html : HTML) {
reader = new PdfReader(parseHtml(html));
copy.addDocument(reader);
reader.close();
}
document.close();
}
This solves your problem, but why do I think it's not the optimal solution?
Suppose that you need to use a special font that needs to be embedded. In that case, every separate PDF file will contain a subset of that font. Different files will require different font subsets, and PdfCopy (nor PdfSmartCopy for that matter) can merge font subsets. This could result in a bloated PDF file with way too many font subsets of the same font.
How do we solve this? That's explained in answer #2.
Answer #2: See ParseMultipleHtmlFiles2 for the full code sample and multiple_html_pages2.pdf for the resulting PDF. You already see the difference in file size: 4.61 KB versus 5.05 KB (and we didn't even introduce embedded fonts).
In this case, we don't parse the HTML to a PDF file the way we did in the parseHtml() method from answer #1. Instead, we parse the HTML to an iText ElementList using the parseToElementList() method. This method requires two Strings. One containing the HTML code, the other one containing CSS values.
We use a utility method to read the HTML file into a String. As for the CSS value, we could pass null to parseToElementList(), but in that case, default styles will be ignored. You'll notice that the <h1> tag we introduced in our HTML will look completely different if you don't pass the default.css that is shipped with XML Worker.
Long story short, this is the code:
public void createPdf(String file) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(file));
document.open();
String css = readCSS();
for (String htmlfile : HTML) {
String html = Utilities.readFileToString(htmlfile);
ElementList list = XMLWorkerHelper.parseToElementList(html, css);
for (Element e : list) {
document.add(e);
}
document.newPage();
}
document.close();
}
We create a single Document and a single PdfWriter instance. We parse the different HTML files into ElementLists one by one, and we add all the elements to the Document.
As you want a new page, each time a new HTML file is parsed, I introduced a document.newPage(). If you remove this line, you can add the three HTML pages on a single page (which wouldn't be possible if you would opt for answer #1).
Using SSJS I've successfully created a PDF using the simple HTML parser that comes with iText but the simple HTML parser doesn't respect CSS and is very limited. I downloaded the XMLWorker class from the iText site and have tried to use that instead but my knowledge of working out how to call Java packages is too limited. All the examples I can find use Java and refer to the classes directly, eg.
Document newPDF = new Document();
But in SSJS we have to use dot notation, eg.
var newPDF:com.itextpdf.text.Document = new com.itextpdf.text.Document();
This - I think - is where I stumble. My code looks like this:
function createLPO2(pReqDoc:NotesDocument) {
importPackage(com.itextpdf);
//importPackage(com.itextpdf.tool.xml.XMLWorkerHelper);
importPackage(java.io);
var con = facesContext.getExternalContext();
var response:com.ibm.xsp.webapp.XspHttpServletResponse = con.getResponse();
response.setContentType("application/pdf");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Content-Disposition","attachment; filename=\"LPO_" + pReqDoc.getItemValueString("RequisitionNo") + ".pdf\"");
var newPDF:com.itextpdf.text.Document = new com.itextpdf.text.Document();
var writer = com.itextpdf.text.pdf.PdfWriter.getInstance(newPDF,response.getOutputStream());
var xmlWorkerHelper = com.itextpdf.tool.xml.XMLWorkerHelper.getInstance();
var strHTML = getTestHTML(); //this is the HTML used in the examples on the iText site
xmlWorkerHelper.parseXHtml(writer, newPDF, new java.io.StringReader(strHTML));
newPDF.close();
writer.close();
facesContext.responseComplete();
}
If I run this script as it is I get a script error on the Domino console. If I remove the comment on the line importPackage(com.itextpdf.tool.xml.XMLWorkerHelper); it gives a completely different error. I suspect I have to import the XMLWorkerHelper package and not just the com.itextpdf package. I thought if I opened the jar file using a tool like 7-zip I could work out the path, which is how I arrived at com.itextpdf.tool.xml.XMLWorkerHelper
Is this right? If so, why does my script fail?
Rob,
seriously, don't try to do that in SSJS. iText is all Java, if you try to mangle with it in a different language it will stress you out. Create a wrapper class that has a method that takes an OutputStream and whatever data (Document, View etc) you need. Obtain the OutputStream in your SSJS and call the function. Look for the XAgent XSnippet on OpenNTF and my blog series (the last two are missing - bear with me) on PDF creation.
One word of caution: iText is GPL, so you either GPL your software too, buy a commercial iText license or look for alternatives like Apache PDFBox or Apache FOP. Ah the second caution: HTML to PDF is a Pita. You could look at a commercial tool like e.g. from Swing software (or change your approach)
I am trying to set field values programatically by modifying XFA Forms PDF containing 2D Barcode.I am having troble opening rersulting PDF with Regular Adobe Reader. Here is an Error "This document enabled extended features in adobe reader.The document has been changed since it was created and use of extended feature is no longer available.Please contact the author of original version of this document" (Note:-File opens fine with Adobe acrobat)
Following is an example c# code which does this.
var reader = new PdfReader(#"c:\abc.pdf");
// System.IO.FileStream fs = new FileStream(reader, System.IO.FileMode.CreateNew, FileAccess.ReadWrite);
var output = new MemoryStream();
var stamper = new PdfStamper(reader, output, '\0', true);
stamper.ViewerPreferences = PdfWriter.AllowModifyContents;
stamper.AcroFields.SetField("form1[0].#subform[0].Line1a_FamilyName[0]", "Family Name");
stamper.FormFlattening = false;
stamper.Close();
reader.Close();
Response.AddHeader("Content-Disposition", "attachment; filename=YourPDF.pdf");
Response.ContentType = "application/pdf";
Response.BinaryWrite(output.ToArray());
Response.End();
See this post by iText's author:
Submitted by Bruno Lowagie on Fri, 12/31/2010 - 16:37
After filling out my form, my PDF shows the following message: This document enabled extended features in Adobe Reader. The document has been changed since it was created and use of extended features is no longer available. Please contact the author for the original version of this document. How do I avoid this message?
The creator of the form made the document Reader enabled. Reader enabling can only be done using Adobe software. You can avoid this message in two ways:
1. Remove the usage rights. This will result in a form that is no longer Reader enabled. For instance: if the creator of the document allowed that the filled out form could be saved locally, this will no longer be possible after removing the usage rights.
2. Fill out the form in append mode. This will result in a bigger file size, but Reader enabling will be preserved.
For more info, read section 8.7.1 of iText in Action.