I used the link given below for getting metadata in itext 5 using: Get and set metadata for itext pdf document
Currently, i can get metadata in itext7 too using the snippet:
PdfDocument pdfDoc = new PdfDocument(new PdfReader(src));
PdfDocumentInfo info = pdfDoc.getDocumentInfo();
info.getAuthor();
info.getCreator();
info.getProducer();
I don't know how to get custom properties using the same.
I can set custom metadata using:
pdfDoc.getDocumentInfo().setMoreInfo("Test", "test");
How to get this value programatically without hard coding the key name?
Also, is there a way to get these metadata values (including custom metadata) without actually writing:
getAuthor, getCreator, etc?
No worries, got the answer.
Didn't realize earlier that:
PdfDictionary map = info.getPdfObject();
returns map type object. Parsed the map to get all the key-value pairs including the custom properties.
Below is the full code snippet:
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfDocumentInfo;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.PdfReader;
import java.io.File;
import java.io.IOException;
import java.util.Map.Entry;
public class GetInfo {
public static final String SRC = "hello.pdf";
public static void main(String[] args) throws IOException {
File file = new File(SRC);
file.getParentFile().mkdirs();
new GetInfo().manipulatePdf(SRC);
}
public void manipulatePdf(String src) throws IOException {
PdfDocument pdfDoc = new PdfDocument(new PdfReader(src));
PdfDocumentInfo info = pdfDoc.getDocumentInfo();
PdfDictionary map = info.getPdfObject();
for(Entry<PdfName, PdfObject> entry : map.entrySet()){
System.out.println(entry.getKey().getValue() + " - " + entry.getValue() );
}
pdfDoc.close();
}
}
In iText 7.0.8+ you can get a pdf metadata map this way.
PdfDocument pdfDoc = new PdfDocument(new PdfReader(src));
//get metadata map
PdfDictionary catalog = pdfDoc.getTrailer();
PdfDictionary map = catalog.getAsDictionary(PdfName.Info);
for (Map.Entry<PdfName, PdfObject> entry : map.entrySet()) {
System.out.println(entry.getKey().getValue() + " - " + entry.getValue());
}
pdfDoc.close();
You can set custom metadata using:
map.put(new PdfName("test"), new PdfString("test"));
Related
Details : When i hit a URL it gives me a jsp page which needs to be converted in to a PDF. for now am using ITextPDF in java component for reading the jsp and writing it as PDF. Is there any alternative process within mule without using ITextPDF.
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.mule.api.MuleEventContext;
import org.mule.api.lifecycle.Callable;
import org.mule.api.transport.PropertyScope;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
public class PDFConversion implements Callable {
private final String USER_AGENT = "Mozilla/5.0";
StringBuffer response = new StringBuffer();
ByteArrayOutputStream bos;
byte[] result;
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
try {
String productid="";
String vertical="";
String postcode="";
String metertype="";
String includeSolar="";
productid=eventContext.getMessage().getInvocationProperty("productId");
vertical=eventContext.getMessage().getInvocationProperty("vertical");
postcode=eventContext.getMessage().getInvocationProperty("postcode");
metertype=eventContext.getMessage().getInvocationProperty("metertype");
includeSolar=eventContext.getMessage().getInvocationProperty("includeSolar");
String url = "http://191.111.0.111:****/PDFService/electricitypdf?productid="+productid+"&vertical="+vertical+"&postcode="+postcode+"&metertype="+"&includeSolar="+includeSolar;
System.out.println(" URL -Request-----"+url);
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// optional default is GET
con.setRequestMethod("GET");
// add request header
con.setRequestProperty("User-Agent", USER_AGENT);
int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + url);
System.out.println("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
bos = new ByteArrayOutputStream();
int next = in.read();
while (next > -1) {
bos.write(next);
next = in.read();
}
bos.flush();
/ ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] bytes = IOUtils.toByteArray(in); while ((inputLine = in.readLine()) != null) { response.append(inputLine); }/ in.close();
} catch (Exception e) {
e.printStackTrace();
}
Document document = new Document(PageSize.LETTER, 0.75F, 0.75F, 0.75F, 0.75F);
document.setPageSize(PageSize.LETTER.rotate());
// PdfWriter.setPageEvent(new HeaderFooter(mmPDFDocument.right() -
// mmPDFDocument.left(), footer, path, headerType, unicodeFont));
PdfWriter.getInstance(document, bos);
document.open();
int numberpages=10;
for (int i = 1; i <= numberpages; i++)
{
document.add(new Chunk("Hello PDF Service - Page "+i));
document.newPage();
}
FileOutputStream fos= new FileOutputStream("energy.pdf");
fos.write(bos.toByteArray());
fos.close();
document.close();
return bos;
}
}
Despite writing this the browser is unable to display the content as PDF i could see that the PDF file is being written and when the URL is hit it generates a file of no type(not a pdf). any help appreciated !!
Your solution is basically a standard Java Application running on Mule ESB. Mule is for message based communication and transformations. If you wanted to turn your solution into a full "Mule Solution" I would recommend....
Use an http:request directive to call the jsp page to return back html.
Example here: Mule ESB : Read HTML
Write a transformer to transform the text/html message to a byte array (or some other type) this will have to be done using ITextPDF.
Output the byte array to a file.
You basically are doing the correct steps but not using any advantages of the Mule platform. When you separate the solution out this way you will be able to write the pdf file easily and not get any strange results.
I have used the code suggested in:
PDFBox Overlay fails
to add a watermark to an existing pdf.
Unfortunately, the pdf produced is corrupted. The pdf reader complains when I open the document: "An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF document to correct the problem".
The document is opened but it does not show the images.
It seems to happen with all the pdfs. It could be worth saying that it happens also with a different implementation that simply uses the Overlay class.
The following url points to a pdf that I used for my testing:
A pdf with an image
The code to test this transformation is:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.PDExtendedGraphicsState;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
import org.apache.pdfbox.util.MapUtil;
/**
* This test is about overlaying with special effect.
*
* #author mkl
*/
public class OverlayWithEffect
{
final static File RESULT_FOLDER = new File("target/test-outputs", "assembly");
public static void overlayWithDarkenBlendMode(PDDocument document, PDDocument overlay) throws IOException
{
PDXObjectForm xobject = importAsXObject(document, (PDPage) overlay.getDocumentCatalog().getAllPages().get(0));
PDExtendedGraphicsState darken = new PDExtendedGraphicsState();
darken.getCOSDictionary().setName("BM", "Darken");
List<PDPage> pages = document.getDocumentCatalog().getAllPages();
for (PDPage page: pages)
{
if (page.getResources() == null) {
page.setResources(page.findResources());
}
if (page.getResources() != null) {
Map<String, PDExtendedGraphicsState> states = page.getResources().getGraphicsStates();
if (states == null) {
states = new HashMap<String, PDExtendedGraphicsState>();
}
String darkenKey = MapUtil.getNextUniqueKey(states, "Dkn");
states.put(darkenKey, darken);
page.getResources().setGraphicsStates(states);
PDPageContentStream stream = new PDPageContentStream(document, page, true, false, true);
stream.appendRawCommands(String.format("/%s gs ", darkenKey));
stream.drawXObject(xobject, 0, 0, 1, 1);
stream.close();
}
}
}
public static PDXObjectForm importAsXObject(PDDocument target, PDPage page) throws IOException
{
final PDStream xobjectStream = new PDStream(target, page.getContents().createInputStream(), false);
final PDXObjectForm xobject = new PDXObjectForm(xobjectStream);
xobject.setResources(page.findResources());
xobject.setBBox(page.findCropBox());
COSDictionary group = new COSDictionary();
group.setName("S", "Transparency");
group.setBoolean(COSName.getPDFName("K"), true);
xobject.getCOSStream().setItem(COSName.getPDFName("Group"), group);
return xobject;
}
public static void main(String[] args) throws COSVisitorException, IOException
{
InputStream sourceStream = new FileInputStream("x:/pdf-test.pdf");
InputStream overlayStream = new FileInputStream("x:/draft.pdf");
try {
final PDDocument document = PDDocument.load(sourceStream);
final PDDocument overlay = PDDocument.load(overlayStream);
overlayWithDarkenBlendMode(document, overlay);
document.save("x:/da-draft-5.pdf");
document.close();
}
finally {
sourceStream.close();
overlayStream.close();
}
}
}
I am using version 1.7 of pdfbox.
Thanks
As suggested by mkl, it is probably an issue with the version of pdfbox that I am using.
I'm currently developing a method that will accept HTML input and convert it into a valid PDF/A file. I know how to programmatically construct a valid PDF/A file using iText (reference: http://itextsupport.com/download/pdfa3.html) but I'm unable to generate a valid PDF/A file using HTML as input and using XMLWorker to transform this input into a PDF file. The problem that I have right now is due to the embedded fonts requirement of the PDF/A format. I always get this exception:
Exception in thread "main" com.itextpdf.text.pdf.PdfAConformanceException: All the fonts must be embedded. This one isn't: Helvetica
I try to force which fonts will the HTML input use via a CSS file and I register the fonts I want to use in the output PDF file via the XMLWorkerFontProvider class, but it seems I'm doing something wrong because the exception commented above is always thrown.
What else do I need in order to XMLWorker uses the fonts registered via XMLWorkerFontProvider class? I want to avoid the use of the default font Helvetica in every HTML element present in the input.
Below is the code I'm using for testing:
style.css (just 1 line):
* { font: normal 100% Arial, sans-serif !important; }
Main.java:
package com.itextpdf;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.ICC_Profile;
import com.itextpdf.text.pdf.PdfAConformanceLevel;
import com.itextpdf.text.pdf.PdfAWriter;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFile;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliers;
import com.itextpdf.tool.xml.html.CssAppliersImpl;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CSSResolver;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
public class Main {
/**
* #param args
*/
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
buf.append("<!DOCTYPE html>");
buf.append("<html>");
buf.append("<head>");
buf.append("<title>Test</title>");
buf.append("</head>");
buf.append("<body>");
buf.append("<p>This is a test</p>");
buf.append("</body>");
buf.append("</html>");
OutputStream file = null;
Document document = null;
PdfAWriter writer = null;
try {
file = new FileOutputStream(new File("C:\\Users\\amartin\\Desktop\\Test.pdf"));
document = new Document();
writer = PdfAWriter.getInstance(document, file, PdfAConformanceLevel.PDF_A_1B);
// Create XMP metadata. It's a PDF/A requirement.
writer.createXmpMetadata();
document.open();
// Set output intent. PDF/A requirement.
ICC_Profile icc = ICC_Profile.getInstance(new FileInputStream("./src/main/resources/com/itextpdf/sRGB Color Space Profile.icm"));
writer.setOutputIntents("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", icc);
// CSS
CSSResolver cssResolver = new StyleAttrCSSResolver();
CssFile cssFile = XMLWorkerHelper.getCSS(new FileInputStream("./css/style.css"));
cssResolver.addCss(cssFile);
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider();
fontProvider.register("./fonts/arial.ttf");
fontProvider.register("./fonts/sans-serif.ttf");
fontProvider.addFontSubstitute("lowagie", "garamond");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
Reader reader = new StringReader(buf.toString());
p.parse(reader);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document != null && document.isOpen())
document.close();
try {
if (file != null)
file.close();
} catch (IOException e) {}
if (writer != null && !writer.isCloseStream())
writer.close();
}
}
}
edit:
Answering to Bruno, I have extended the FontFactoryImp class overriding the getFont() method (the one that has all the arguments). It calls the the System.out.println function like this:
System.out.println("=fontname: " + fontname + " =encoding: " + encoding + " =embedded : " + embedded + " =size: " + size + " =style: " + style + " =BaseColor: " + color)
and then calls parent.getFont() method with the same arguments. The only output I see is this:
=fontname: null =encoding: Cp1252 =embedded : true =size: -1.0 =style: -1 =BaseColor: null
=fontname: null =encoding: Cp1252 =embedded : true =size: -1.0 =style: -1 =BaseColor: null
and the exception thrown, pasted before this code.
Based on the feedback you're sending to the System.out, it seems that XML Worker doesn't pick up the font family you want to use.
Please specify the font family like this:
font-family: "Arial"
Using 'font' in CSS may work, but it's tricky. I think iText sees normal and interprets it as Use the default font.
The complete code that makes this example work is the following:
style.css:
* {
font-family: "Arial";
font-style: normal;
}
Main.java:
package com.itextpdf;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.ICC_Profile;
import com.itextpdf.text.pdf.PdfAConformanceLevel;
import com.itextpdf.text.pdf.PdfAWriter;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFile;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliers;
import com.itextpdf.tool.xml.html.CssAppliersImpl;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CSSResolver;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
public class Main {
public static void main(String[] args) {
StringBuffer buf = new StringBuffer();
String title = "Test";
// Sample HTML content.
buf.append("<!DOCTYPE html>");
buf.append("<html>");
buf.append("<head>");
buf.append("<title>" + title + "</title>");
buf.append("</head>");
buf.append("<body>");
buf.append("<p>This is a test</p>");
buf.append("</body>");
buf.append("</html>");
OutputStream file = null;
Document document = null;
PdfAWriter writer = null;
try {
file = new FileOutputStream(new File("C:\\Users\\amartin\\Desktop\\Test.pdf"));
document = new Document();
writer = PdfAWriter.getInstance(document, file, PdfAConformanceLevel.PDF_A_1B);
// Avoid discrepances between document title and XMP metadata information.
document.addTitle(title);
// Create XMP metadata. It's a PDF/A requirement.
writer.createXmpMetadata();
document.open();
// Set output intent. PDF/A requirement.
ICC_Profile icc = ICC_Profile.getInstance(new FileInputStream("./src/main/resources/com/itextpdf/sRGB Color Space Profile.icm"));
writer.setOutputIntents("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", icc);
// CSS stylesheet.
CSSResolver cssResolver = new StyleAttrCSSResolver();
CssFile cssFile = XMLWorkerHelper.getCSS(new FileInputStream("./css/style.css"));
cssResolver.addCss(cssFile);
MyFontProvider fontProvider = new MyFontProvider();
fontProvider.register("./fonts/arial.ttf");
/* DEBUG
System.out.println("Fonts present in " + fontProvider.getClass().getName());
Set<String> registeredFonts = fontProvider.getRegisteredFonts();
for (String font : registeredFonts)
System.out.println(font);
*/
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines.
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
Reader reader = new StringReader(buf.toString());
p.parse(reader);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document != null && document.isOpen())
document.close();
try {
if (file != null)
file.close();
} catch (IOException e) {}
if (writer != null && !writer.isCloseStream())
writer.close();
}
}
}
MyFontProvider.java:
package com.itextpdf;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactoryImp;
public class MyFontProvider extends FontFactoryImp {
#Override
public Font getFont(String fontname, String encoding, boolean embedded,
float size, int style, BaseColor color) {
System.out.println("=fontname: " + fontname + " =encoding: " + encoding + " =embedded : " + embedded + " =size: " + size + " =style: " + style + " =BaseColor: " + color);
return super.getFont(fontname, encoding, embedded, size, style, color);
}
}
Again, thank you, Bruno. I'm really glad to get your help here :)
I am surfing the web-users pdf files from Domino Server. I have a template.pdf and a font file on my Java package to generate these pdf files without SAVING them on the server. However the PdfStamper requires me to use OutputStream which needs a path.
Does Domino Server have a temp space? Is there a different way to implement this? Is there a fake path to set?
As a test for now I'm saving it on my local machine.
package de.vogella.itext.write;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
public class FillUpPDF {
// Get the files from jar/package
InputStream streamTemplate = getClass().getResourceAsStream("Template1.pdf");
InputStream streamFont = getClass().getResourceAsStream("Ruritania.ttf");
// Files
public static final String sTemplate = "Template1.pdf";
public static final String sResultPDF = "C:/Test/Bob Info.pdf";
public static final String sResultFont = "Ruritania.ttf";
// Manipulates a PDF file source with the file destination as result
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
// Read the Template
PdfReader reader = new PdfReader(src);
// Copy the template and output it to the destination
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest)); //Where to fileoutputstream on Domino Server temporarily
// Gets the fields on the form
AcroFields form = stamper.getAcroFields();
// Create and set the font
BaseFont newFont = BaseFont.createFont(sResultFont, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
form.addSubstitutionFont(newFont);
/* Filling up the fields on the form */
form.setFieldProperty("text_1", "textfont", newFont, null);
form.setFieldProperty("text_1", "textsize", new Float(9), null);
form.setField("text_1", "Bob");
form.setFieldProperty("text_2", "textfont", newFont, null);
form.setFieldProperty("text_2", "textsize", new Float(9), null);
form.setField("text_2", "Gates");
form.setFieldProperty("text_3", "textfont", newFont, null);
form.setFieldProperty("text_3", "textsize", new Float(9), null);
form.setField("text_3", "Oct 17, 2013 at 11:00am");
form.setFieldProperty("text_4", "textfont", newFont, null);
form.setFieldProperty("text_4", "textsize", new Float(9), null);
form.setField("text_4", "Cloud and Smarter Infrastructure");
// Fixed the field
stamper.setFormFlattening(true);
stamper.setFreeTextFlattening(true);
// Close
stamper.close();
reader.close();
// Next Step: Send out the created pdf file to the user.
}
// Main method
public void main(String[] args) throws Exception {
FillUpPDF certification = new FillUpPDF();
//certification.manipulatePdf(sTemplate, sResultPDF);
certification.manipulatePdf(sTemplate, sResultPDF);
}
}
You do not need a temp file path. You can use the outputstream from the response and thereby send the PDF directly to the browser.
Example:
XspHttpServletResponse response = (XspHttpServletResponse) JSFUtil.getFacesContext().getExternalContext().getResponse();
ServletOutputStream out = response.getOutputStream();
PdfStamper stamper = new PdfStamper(reader, out);
I currently have the following class that I'm trying to add a Hashtable of metadata properties to a PDF. The problem is, even though it appears to assign the hashtable to the stamper.MoreInfo property it doesn't appear to save the MoreInfo property once the stamper is closed.
public class PdfEnricher
{
readonly IFileSystem fileSystem;
public PdfEnricher(IFileSystem fileSystem)
{
this.fileSystem = fileSystem;
}
public void Enrich(string pdfFile, Hashtable fields)
{
if (!fileSystem.FileExists(pdfFile)) return;
var newFile = GetNewFileName(pdfFile);
var stamper = GetStamper(pdfFile, newFile);
SetFieldsAndClose(stamper, fields);
}
string GetNewFileName(string pdfFile)
{
return fileSystem.GetDirectoryName(pdfFile) + #"\NewFileName.pdf";
}
static void SetFieldsAndClose(PdfStamper stamper, Hashtable fields)
{
stamper.MoreInfo = fields;
stamper.FormFlattening = true;
stamper.Close();
}
static PdfStamper GetStamper(string pdfFile, string newFile)
{
var reader = new PdfReader(pdfFile);
return new PdfStamper(reader, new FileStream(newFile, FileMode.Create));
}
}
Any ideas?
As always, Use The Source.
In this case, I saw a possibility fairly quickly (Java source btw):
public void close() throws DocumentException, IOException {
if (!hasSignature) {
stamper.close( moreInfo );
return;
}
Does this form already have signatures of some sort? Lets see when hasSignatures would be true.
That can't be the case with your source. hasSignatures is only set when you sign a PDF via PdfStamper.createSignature(...), so that's clearly not it.
Err... how are you checking that your MoreInfo was added? It won't be in the XMP metadata. MoreInfo is added directly to the Doc Info dictionary. You see them in the "Custom" tab of Acrobat (and most likely Reader, though I don't have it handy at the moment).
Are you absolutely sure MoreInfo isn't null, and all its values aren't null?
The Dictionary is just passed around by reference, so any changes (in another thread) would be reflected in the PDF as it was written.
The correct way to iterate through a document's "Doc info dictionary":
PdfReader reader = new PdfReader(somePath);
Map<String, String> info = reader.getInfo();
for (String key : info.keySet()) {
System.out.println( key + ": " + info.get(key) );
}
Note that this will go through all the fields in the document info dictionary, not just the custom ones. Also be aware that changes made the the Map from getInfo() will not carry over to the PDF. The map is new'ed, populated, and returned.