Some pdf file watermark does not show using iText - pdf

Our company using iText to stamp some watermark text (not image) on some pdf forms. I noticed 95% forms shows watermark correctly, about 5% does not. I tested, copy 2 original pdf files, one was marked ok, other one does not ok, then tested in via a small program, same result: one got marked, the other does not. I then tried the latest version of iText jar file (version 5.0.6), same thing. I checked pdf file properties, security settings etc, seems nothing shows any hint. The result file does changed size and markd "changed by iText version...." after executed program.
Here is the sample watermark code (using itext jar version 2.1.7), note topText, mainText, bottonText parameters passed in, make 3 lines of watermarks show in the pdf as watermark.
Any help appreciated !!
public class WatermarkGenerator {
private static int TEXT_TILT_ANGLE = 25;
private static Color MEDIUM_GRAY = new Color(160, 160, 160);
private static int SUPPORT_FONT_SIZE = 42;
private static int PRIMARY_FONT_SIZE = 54;
public static void addWaterMark(InputStream pdfInputStream,
OutputStream outputStream, String topText,
String mainText, String bottomText) throws Exception {
PdfReader reader = new PdfReader(pdfInputStream);
int numPages = reader.getNumberOfPages();
// Create a stamper that will copy the document to the output
// stream.
PdfStamper stamp = new PdfStamper(reader, outputStream);
int page=1;
BaseFont baseFont =
BaseFont.createFont(BaseFont.HELVETICA_BOLDOBLIQUE,
BaseFont.WINANSI, BaseFont.EMBEDDED);
float width;
float height;
while (page <= numPages) {
PdfContentByte cb = stamp.getOverContent(page);
height = reader.getPageSizeWithRotation(page).getHeight() / 2;
width = reader.getPageSizeWithRotation(page).getWidth() / 2;
cb = stamp.getUnderContent(page);
cb.saveState();
cb.setColorFill(MEDIUM_GRAY);
// Top Text
cb.beginText();
cb.setFontAndSize(baseFont, SUPPORT_FONT_SIZE);
cb.showTextAligned(Element.ALIGN_CENTER, topText, width,
height+PRIMARY_FONT_SIZE+16, TEXT_TILT_ANGLE);
cb.endText();
// Primary Text
cb.beginText();
cb.setFontAndSize(baseFont, PRIMARY_FONT_SIZE);
cb.showTextAligned(Element.ALIGN_CENTER, mainText, width,
height, TEXT_TILT_ANGLE);
cb.endText();
// Bottom Text
cb.beginText();
cb.setFontAndSize(baseFont, SUPPORT_FONT_SIZE);
cb.showTextAligned(Element.ALIGN_CENTER, bottomText, width,
height-PRIMARY_FONT_SIZE-6, TEXT_TILT_ANGLE);
cb.endText();
cb.restoreState();
page++;
}
stamp.close();
}
}

We solved problem by change Adobe LifecycleSave file option. File->Save->properties->Save as, then look at Save as type, default is Acrobat 7.0.5 Dynamic PDF Form File, we changed to use 7.0.5 Static PDF Form File (actually any static one will work). File saved in static one do not have this watermark disappear problem. Thanks Mark for pointing to the right direction.

You're using the underContent rather than the overContent. Don't do that. It leaves you at the mercy of big, white-filled rectangles that some folks insist on drawing first thing. It's a hold over from less-than-good PostScript interpreters and hasn't been necessary for Many Years.
Okay, having viewed your PDF, I can see the problem is that this is an XFA-based form (from LiveCycle Designer). Acrobat can (and often does) rebuild the entire file based on the XFA (a type of xml) it contains. That's how your changes are lost. When Acrobat rebuilds the PDF from the XFA, all the existing PDF information is pitched, including your watermark.
The only way to get this to work would be to define the watermark as part of the XFA file contained in the PDF.
Detecting these forms isn't all that hard:
PdfReader reader = new PdfReader(...);
AcroFields acFields = reader.getAcroFields();
XfaForm xfaForm = acFields.getXfaForm();
if (xfaForm != null && xfaForm.isXfaPresent()) {
// Ohs nose.
throw new ItsATrapException("We can't repel XML of that magnitude!");
}
Modifying them on the other hand could be Quite Challenging, but here's the specs.
Once you've figured out what needs to be changed, it's a simple matter of XML manipulation... but that "figure it out" part could be interesting.
Good hunting.

Related

iText5.x Setting pushbutton appearance without breaking Seal

Here is the context:
We add two empty pages to an existing pdf, each containing an empty pushbutton field
We apply a PAdES B-B seal with all modification rights on the document
We modify a pushbutton to insert an image in it
When we try to modify the pushbutton appearance to set an image, the seal validity breaks with "unauthorized modification" no matter what we try.
Here is a code sample:
PdfReader pdfReader = new PdfReader("test.pdf");
PdfStamper pdfStamper = new PdfStamper(pdfReader, output, pdfReader.getPdfVersion(), true);
AcroFields acroFields = pdfStamper.getAcroFields();
String imageFieldId = "imageField1";
acroFields.setField(imageFieldId, Base64.encodeBytes(consentImage));
pdfStamper.close();
pdfReader.close();
We also tried with the recommanded way in documentation without success:
PushbuttonField pbField = acroFields.getNewPushbuttonFromField(imageFieldId);
pbField.setImage(Image.getInstance("image1.jpg"));
acroFields.replacePushbuttonField(imageFieldId, pbField.getField());
Problem is: i don't know if that type of modification is supported by iText or if it's our way of modifying the button which is wrong?
Update:
If the certification is replaced by a simple signature, we can set the pushbutton appearance without breaking it.
Why the certification signature is broken
You say
We apply a PAdES B-B seal with all modification rights on the document
which does not mean that all imaginable modifications of the document are allowed but instead that all allowable modifications are allowed. According to the PDF specification the choices are:
No changes to the document shall be permitted; any change to the document shall invalidate the signature.
Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature.
Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
Thus, in case of your document the allowed changes include form fill-ins and arbitrary annotation manipulation.
Unfortunately iText 5, when setting "the value" of an AcroForm push button, does not merely set the button appearance to the button but instead
PushbuttonField pb = getNewPushbuttonFromField(name);
pb.setImage(img);
replacePushbuttonField(name, pb.getField());
I.e. it essentially replaces the former push button with a similar one. This as such is not allowed.
Why a mere approval signature is not broken
The PDF specification does not restrict the changes allowed to a document signed by a mere approval signature (unless restrictions explicitly are given in a FieldMDP transform).
Adobe once claimed that they do restrict changes allowed to signed but not certified documents like those to a certified document with restriction value 3 plus "Adding signature fields", cf. this answer, but apparently they are a bit laxer in other respects, too. In particular current Adobe Reader versions only warn about "Form Fields with Property Changes" in the case at hand.
An additional complication
The PDF in question actually does not have only the AcroForm form definition, instead it has a similar XFA form definition, it is a hybrid form document. Thus, to change the image in both form definitions, one has to consider the filling of the XFA form, too.
Fortunately, the way iText 5 fills in the image into the XFA form does not make Adobe Reader assume the seal broken.
How to set the button image instead to not break the seal
To not break the seal, we have to set the button image without changing the underlying form, merely the widget. Thus, the following code attempts to only change the appearance of the button:
PdfReader pdfReader = new PdfReader(SOURCE);
PdfStamper pdfStamper = new PdfStamper(pdfReader, TARGET, pdfReader.getPdfVersion(), true);
byte[] bytes = IMAGE_BYTES;
AcroFields acroFields = pdfStamper.getAcroFields();
String name = "mainform[0].subform_0[0].image_0_0[0]";
String value = Base64.getEncoder().encodeToString(bytes);
Image image = Image.getInstance(bytes);
XfaForm xfa = acroFields.getXfa();
if (xfa.isXfaPresent()) {
name = xfa.findFieldName(name, acroFields);
if (name != null) {
String shortName = XfaForm.Xml2Som.getShortName(name);
Node xn = xfa.findDatasetsNode(shortName);
if (xn == null) {
xn = xfa.getDatasetsSom().insertNode(xfa.getDatasetsNode(), shortName);
}
xfa.setNodeText(xn, value);
}
}
PdfDictionary widget = acroFields.getFieldItem(name).getWidget(0);
PdfArray boxArray = widget.getAsArray(PdfName.RECT);
Rectangle box = new Rectangle(boxArray.getAsNumber(0).floatValue(), boxArray.getAsNumber(1).floatValue(), boxArray.getAsNumber(2).floatValue(), boxArray.getAsNumber(3).floatValue());
float ratioImage = image.getWidth() / image.getHeight();
float ratioBox = box.getWidth() / box.getHeight();
boolean fillHorizontally = ratioImage > ratioBox;
float width = fillHorizontally ? 1 : ratioBox / ratioImage;
float height = fillHorizontally ? ratioImage / ratioBox : 1;
float xOffset = 0; // centered: (width - 1) / 2;
float yOffset = height - 1; // centered: (height - 1) / 2;
PdfAppearance app = PdfAppearance.createAppearance(pdfStamper.getWriter(), width, height);
app.addImage(image, 1, 0, 0, 1, xOffset, yOffset);
PdfDictionary dic = (PdfDictionary)widget.get(PdfName.AP);
if (dic == null)
dic = new PdfDictionary();
dic.put(PdfAnnotation.APPEARANCE_NORMAL, app.getIndirectReference());
widget.put(PdfName.AP, dic);
pdfStamper.markUsed(widget);
pdfStamper.close();
pdfReader.close();
(SetImageInSignedPdf test testSetInXfaAndAppearanceSampleCert)
In my tests this results in the image being visible both in viewers that support XFA forms and those that don't, and the seal not being considered broken by Adobe Reader.
Beware, though, I only developed and tested this with your sample document; chances are that some border conditions might not be considered.

Why is flying saucer always printing PDF on A4 paper?

I'm trying to save an html document to PDF using flyingsaucer but the generated document always ends up having an A4 dimension when I look at the Document Properties from Adobe Reader (Page Size: 8.26 x 11.69 in).
I did read the documentation and I'm passing the css #page {size: letter;} style. And while it does have an effect on the output, the page size always remains 8.26 x 11.69 in Adobe Reader. For example, if I set the page size to legal, my PDF is still the size of a A4 but the top of the document is missing as if it had fell off the "paper".
I'm not sure if the problem falls on the itext side or the flying saucer side. I was using a fairly old version so my first step was to upgrade to the latest 9.1.6 version of flying saucer. I also moved from itext 2.0.8 to openPDF 1.0.1 but I'm still getting the same behavior.
I also traced in the debugger up to the com.lowagie.text.Document creation in ITextRenderer and at this point the document size passed is correct. That makes me think that the issue might be in openPDF / iText but I can't find what I'm doing wrong.
It turns out the PDF generation was correctly using the #page size declaration and the problem was occurring later in our software. What I had not noticed is that after the generation of the PDF another method was called to merge multiple PDFs into one. This method should probably not have been called, but that's another story.
The bottom line is this method created a new com.lowagie.text.Document(), which by default creates an A4 sized document, and then was iterating over all pages of the pdf, adding the pages to the new document using pdfWriter.getImportedPage(pdfReader, currentPage++). These imported pages did not retain their original size.
I fixed it by passing the page size of the fist page when creating the merged document object:
document = new Document(pdfReader.getPageSize(1));
The real problem is that you're (unwittingly) using software that is no longer supported. Anything that still has the namespace lowagie (the founder and CTO of iText) is really outdated.
If you simply want to convert HTML to pdf, why not use iText directly and cut out the middle-man?
We have multiple options for you.
XMLWorker (iText5 based code that converts HTML to pdf)
pdfHTML (iText7 based add-on that converts HTML5/CSS3 to pdf)
This is a rather extensive code-sample for using pdfHTML:
public void createPdf(String src, String dest, String resources) throws IOException {
try {
FileOutputStream outputStream = new FileOutputStream(dest);
WriterProperties writerProperties = new WriterProperties();
//Add metadata
writerProperties.addXmpMetadata();
PdfWriter pdfWriter = new PdfWriter(outputStream, writerProperties);
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
pdfDoc.getCatalog().setLang(new PdfString("en-US"));
//Set the document to be tagged
pdfDoc.setTagged();
pdfDoc.getCatalog().setViewerPreferences(new PdfViewerPreferences().setDisplayDocTitle(true));
//Set meta tags
PdfDocumentInfo pdfMetaData = pdfDoc.getDocumentInfo();
pdfMetaData.setAuthor("Joris Schellekens");
pdfMetaData.addCreationDate();
pdfMetaData.getProducer();
pdfMetaData.setCreator("iText Software");
pdfMetaData.setKeywords("example, accessibility");
pdfMetaData.setSubject("PDF accessibility");
//Title is derived from html
// pdf conversion
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.addStandardPdfFonts();
fp.addDirectory(resources);//The noto-nashk font file (.ttf extension) is placed in the resources
props.setFontProvider(fp);
props.setBaseUri(resources);
//Setup custom tagworker factory for better tagging of headers
DefaultTagWorkerFactory tagWorkerFactory = new AccessibilityTagWorkerFactory();
props.setTagWorkerFactory(tagWorkerFactory);
HtmlConverter.convertToPdf(new FileInputStream(src), pdfDoc, props);
pdfDoc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
You can find more information at http://itextpdf.com/itext7/pdfHTML

Increase left margin of an existing pdf using iTextSharp [duplicate]

My web application signs PDF documents. I would like to let users download the original PDF document (not signed) but adding an image and the signers in the left margin of the pdf document.
I've seen this idea in another web application, and I would like to do the same. Of course I would like to do it using itext library.
I have attached two images, the original PDF document (not signed) and the modified PDF document.
First this: it is important to change the document before you digitally sign it. Once digitally signed, these changes will break the signature.
I will break up the question in two parts and I'll skip the part about the actual watermarking as this is already explained here: How to watermark PDFs using text or images?
This question is not a duplicate of that question, because of the extra requirement to add an extra margin to the right.
Take a look at the primes.pdf document. This is the source file we are going to use in the AddExtraMargin example with the following result: primes_extra_margin.pdf. As you can see, a half an inch margin was added to the left of each page.
This is how it's done:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
// properties
PdfContentByte over;
PdfDictionary pageDict;
PdfArray mediabox;
float llx, lly, ury;
// loop over every page
for (int i = 1; i <= n; i++) {
pageDict = reader.getPageN(i);
mediabox = pageDict.getAsArray(PdfName.MEDIABOX);
llx = mediabox.getAsNumber(0).floatValue();
lly = mediabox.getAsNumber(1).floatValue();
ury = mediabox.getAsNumber(3).floatValue();
mediabox.set(0, new PdfNumber(llx - 36));
over = stamper.getOverContent(i);
over.saveState();
over.setColorFill(new GrayColor(0.5f));
over.rectangle(llx - 36, lly, 36, ury - llx);
over.fill();
over.restoreState();
}
stamper.close();
reader.close();
}
The PdfDictionary we get with the getPageN() method is called the page dictionary. It has plenty of information about a specific page in the PDF. We are only looking at one entry: the /MediaBox. This is only a proof of concept. If you want to write a more robust application, you should also look at the /CropBox and the /Rotate entry. Incidentally, I know that these entries don't exist in primes.pdf, so I am omitting them here.
The media box of a page is an array with four values that represent a rectangle defined by the coordinates of its lower-left and upper-right corner (usually, I refer to them as llx, lly, urx and ury).
In my code sample, I change the value of llx by subtracting 36 user units. If you compare the page size of both PDFs, you'll see that we've added half an inch.
We also use these coordinates to draw a rectangle that covers the extra half inch. Now switch to the other watermark examples to find out how to add text or other content to each page.
Update:
if you need to scale down the existing pages, please read Fix the orientation of a PDF in order to scale it

iTextSharp rotated PDF page reverts orientation when file is rasterized at print house

Using iTextSharp I am creating a PDF composed of a collection of existing PDFs, some of the included PDFs are landscape orientation and need to be rotated. So, I do the following:
private static void AdjustRotationIfNeeded(PdfImportedPage pdfImportedPage, PdfReader reader, int documentPage)
{
float width = pdfImportedPage.Width;
float height = pdfImportedPage.Height;
if (pdfImportedPage.Rotation != 0)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(0));
}
if (width > height)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(270));
}
}
This works great. The included PDFs rotated to portrait orientation if needed. The PDF prints correctly on my local printer.
This file is sent to a fulfillment house, and unfortunately, the landscape included files do not print properly when going through their printer and rasterization process. They use Kodak (Creo) NexRip 11.01 or Kodak (Creo) Prinergy 6.1. machines. The fulfillment house's suggestion is to: "generate a new PDF file after we rotate pages or make any changes to a PDF. It is as easy as exporting out to a PostScript and distilling back to a PDF."
I know iTextSharp doesn't support PostScript. Is there another way iTextSharp can rotate included PDFs to hold the orientation when rasterized?
First let me assure you that changing the rotation in the page dictionary is the correct procedure to achieve what you want. As far as I can see your code, there's nothing wrong with it. You are doing the right thing.
Unfortunately, you are faced with a third party product over which you have no control that is not doing the right thing. How to solve this?
I have written an example called IncorrectExample. I have named it that way because I don't want it to be used in a context that is different from yours. You can safely ignore all the warnings I added: they are not meant for you. This example is very specific to your problem.
Please try the following code:
public void manipulatePdf(String src, String dest)
throws IOException, DocumentException {
// Creating a reader
PdfReader reader = new PdfReader(src);
// step 1
Rectangle pagesize = getPageSize(reader, 1);
Document document = new Document(pagesize);
// step 2
PdfWriter writer
= PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte cb = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
pagesize = getPageSize(reader, i);
document.setPageSize(pagesize);
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
if (isPortrait(reader, i)) {
cb.addTemplate(page, 0, 0);
}
else {
cb.addTemplate(page, 0, 1, -1, 0, pagesize.getWidth(), 0);
}
}
// step 4
document.close();
reader.close();
}
public Rectangle getPageSize(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSizeWithRotation(pagenumber);
return new Rectangle(
Math.min(pagesize.getWidth(), pagesize.getHeight()),
Math.max(pagesize.getWidth(), pagesize.getHeight()));
}
public boolean isPortrait(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSize(pagenumber);
return pagesize.getHeight() > pagesize.getWidth();
}
I have taken the pages.pdf file as an example. This file is special in the sense that it has two pages in landscape that are created in a different way:
one page is a page of which the width is smaller than the height (sounds like it's a page in portrait), but as there's a /Rotate value of 90 added to the page dictionary, it is shown in landscape.
the other page isn't rotated, but it has a height that is smaller than the width.
In my example, I am using the classes Document and PdfWriter to create a copy of the original document. This is wrong in general because it throws away all interaction. I should use PdfStamper or PdfCopy instead, but it is right in your specific case because you don't need the interactivity: the final purpose of the PDF is to be printed.
With Document, I create new pages using a new Rectangle that uses the lowest value of the dimensions of the existing page as the width and the highest value as the height. This way, the page will always be in portrait. Note that I use the method getPageSizeWithRotation() to make sure I get the correct width and height, taking into account any possible rotation.
I then add a PdfImportedPage to the direct content of the writer. I use the isPortrait() method to find out if I need to rotate the page or not. Observe that the isPortrait() method looks at the page size without taking into account the rotation. If we did take into account the rotation, we'd rotate pages that don't need rotating.
The resulting PDF can be found here: pages_changed.pdf
As you can see, some information got lost: there was an annotation on the final page: it's gone. There were specific viewer preferences defined for the original document: they're gone. But that shouldn't matter in your specific case, because all that matters for you is that the pages are printed correctly.

How to merge PDFs to a single file without multiple copies of the same font?

I create PDFs and concatenate them into a single PDF.
My resulting PDF is a lot bigger than I had expected in file size.
I realised that my output PDF has a ton of duplicate font, and it is the reason of unexpectedly big file size.
Here, my question is:
I would like to create PDFs which only embed font information, so let they use Windows System Font.
When I merge them into a single PDF, I insert actual font which PDF needs.
If possible, please let me know how to do it.
I've created the MergeAndAddFont example to explain the different options.
We'll create PDFs using this code snippet:
public void createPdf(String filename, String text, boolean embedded, boolean subset) throws DocumentException, IOException {
// step 1
Document document = new Document();
// step 2
PdfWriter.getInstance(document, new FileOutputStream(filename));
// step 3
document.open();
// step 4
BaseFont bf = BaseFont.createFont(FONT, BaseFont.WINANSI, embedded);
bf.setSubset(subset);
Font font = new Font(bf, 12);
document.add(new Paragraph(text, font));
// step 5
document.close();
}
We use this code to create 3 test files, 1, 2, 3 and we'll do this 3 times: A, B, C.
The first time, we use the parameters embedded = true and subset = true, resulting in the files testA1.pdf with text "abcdefgh" (3.71 KB), testA2.pdf with text "ijklmnopq" (3.49 KB) and testA3.pdf with text "rstuvwxyz" (3.55 KB). The font is embedded and the file size is relatively low because we only embed a subset of the font.
Now we merge these files using the following code, using the smart parameter to indicate whether we want to use PdfCopy or PdfSmartCopy:
public void mergeFiles(String[] files, String result, boolean smart) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy;
if (smart)
copy = new PdfSmartCopy(document, new FileOutputStream(result));
else
copy = new PdfCopy(document, new FileOutputStream(result));
document.open();
PdfReader[] reader = new PdfReader[3];
for (int i = 0; i < files.length; i++) {
reader[i] = new PdfReader(files[i]);
copy.addDocument(reader[i]);
}
document.close();
for (int i = 0; i < reader.length; i++) {
reader[i].close();
}
}
When we merge the document, be it with PdfCopy or PdfSmartCopy, the different subsets of the same font will be copied as separate objects in the resulting PDF testA_merged1.pdf / testA_merged2.pdf (both 9.75 KB).
This is the problem you are experiencing: PdfSmartCopy can detect and reuse identical objects, but the different subsets of the same font aren't identical and iText can't merge different subsets of the same font into one font.
The second time, we use the parameters embedded = true and subset = false, resulting in the files testB1.pdf (21.38 KB), testB2.pdf (21.38 KB) and testA3.pdf (21.38 KB). The font is fully embedded and the file size of a single file is a lot bigger than before because the full font is embedded.
If we merge the files using PdfCopy, the font will be present in the merged document redundantly, resulting in the bloated file testB_merged1.pdf (63.16 KB). This is definitely not what you want!
However, if we use PdfSmartCopy, iText detects an identical font stream and reuses it, resulting in testB_merged2.pdf (21.95 KB) which is much smaller than we had with PdfCopy. It's still bigger than the document with the subsetted fonts, but if you're concatenating a huge amount of files, the result will be better if you embed the complete font.
The third time, we use the parameters embedded = false and subset = false, resulting in the files testC1.pdf (2.04 KB), testC2.pdf (2.04 KB) and testC3.pdf (2.04 KB). The font isn't embedded, resulting in an excellent file size, but if you compare with one of the previous results, you'll see that the font looks completely different.
We merge the files using PdfSmartCopy, resulting in testC_merged1.pdf (2.6 KB). Again, we have an excellent file size, but again we have the problem that the font isn't visualized correctly.
To fix this, we need to embed the font:
private void embedFont(String merged, String fontfile, String result) throws IOException, DocumentException {
// the font file
RandomAccessFile raf = new RandomAccessFile(fontfile, "r");
byte fontbytes[] = new byte[(int)raf.length()];
raf.readFully(fontbytes);
raf.close();
// create a new stream for the font file
PdfStream stream = new PdfStream(fontbytes);
stream.flateCompress();
stream.put(PdfName.LENGTH1, new PdfNumber(fontbytes.length));
// create a reader object
PdfReader reader = new PdfReader(merged);
int n = reader.getXrefSize();
PdfObject object;
PdfDictionary font;
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(result));
PdfName fontname = new PdfName(BaseFont.createFont(fontfile, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED).getPostscriptFontName());
for (int i = 0; i < n; i++) {
object = reader.getPdfObject(i);
if (object == null || !object.isDictionary())
continue;
font = (PdfDictionary)object;
if (PdfName.FONTDESCRIPTOR.equals(font.get(PdfName.TYPE))
&& fontname.equals(font.get(PdfName.FONTNAME))) {
PdfIndirectObject objref = stamper.getWriter().addToBody(stream);
font.put(PdfName.FONTFILE2, objref.getIndirectReference());
}
}
stamper.close();
reader.close();
}
Now, we have the file testC_merged2.pdf (22.03 KB) and that's actually the answer to your question. As you can see, the second option is better than this third option.
Caveats: This example uses the Gravitas One font as a simple font. As soon as you use the font as a composite font (you tell iText to use it as a composite font by choosing the encoding IDENTITY-H or IDENTITY-V), you can no longer choose whether or not to embed the font, whether or not to subset the font. As defined in ISO-32000-1, iText will always embed composite fonts and will always subset them.
This means that you can't use the above solutions when you need special fonts (Chinese, Japanese, Korean). In that case, you shouldn't embed the fonts, but use so-called CJK fonts. They CJK fonts will use font packs that can be downloaded by Adobe Reader.