hoping that someone can see the flaw in my code to merge to PDF-a documents using ITextSharp. Currently it complains about missing metadata which PDF-a requires.
Document document = new Document();
MemoryStream ms = new MemoryStream();
using (PdfACopy pdfaCopy = new PdfACopy(document, ms, PdfAConformanceLevel.PDF_A_1A))
{
document.Open();
using (PdfReader reader = new PdfReader("Doc1.pdf"))
{
pdfaCopy.AddDocument(reader);
}
using (PdfReader reader = new PdfReader("doc2.pdf"))
{
pdfaCopy.AddDocument(reader);
}
}
The exact error received is
Unhandled Exception: iTextSharp.text.pdf.PdfAConformanceException: The document catalog dictionary of a PDF/A conforming file shall contain
the Metadata key
I was hoping that the 'document catalog dictionary' would be copied as well, but I guess the 'new Document()' creates an empty non-conforming document or something.
Thanks! Hope you can help
Wouter
You need to add this line:
copy.CreateXmpMetadata();
This will create some default XMP metadata. Of course: if you want to create your own XMP file containing info about the documents you're about to merge, you can also use:
copy.XmpMetadata = myMetaData;
where myMetaData is a byte array containing a correct XMP stream.
I hope you understand that iText can't automatically create the correct metadata. Providing metadata is something that needs human attention.
Related
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 am trying to convert a word document(.docx) into PDF. I am trying to pass Stream object into load document method of RichEditDocumentServer,but my bad ,I am getting a blank pdf. I tried passing the file path in load document method ,which worked fine. But my requirement has to meet with stream object. Can anyone help me to fix the issue. A sample code has been added below.
private Stream ConvertToPdf(Stream fileStream)
{
RichEditDocumentServer server = new RichEditDocumentServer();
fileStream.Seek(0, SeekOrigin.Begin);
server.LoadDocument(fileStream, DocumentFormat.Doc);
Stream convertStream = new MemoryStream();
server.ExportToPdf(convertStream);
convertStream.Seek(0, SeekOrigin.Begin);
return convertStream;
}
it worked with the below link
https://www.devexpress.com/support/center/Question/Details/T340655#answer-9c3224dd-383a-4faa-9672-9b34e36c1c7a
server.LoadDocument(fileStream, DocumentFormat.OpenXml);
I'm fairly new too and struggling with OpenXML. We basically have a Word template and depending on the number of records the user selects in a website we have, we pull the records out of the database and use the template to create a word document. Then we assemble all these documents into one master Word document which is served up to the user. We use OpenXML and altchunks to do this:
string altChunkId = "AltChunkId" + itemNo.ToString();
MainDocumentPart mainPart = myDoc.MainDocumentPart;
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart( AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (FileStream fileStream = File.Open(createdFileName, FileMode.Open))
{
chunk.FeedData(fileStream);
fileStream.Close();
}
AltChunk altChunk = new AltChunk();
AltChunkProperties altChunkProperties = new AltChunkProperties();
MatchSource matchSrc = new MatchSource();
matchSrc.Val = true;
altChunkProperties.Append(matchSrc);
altChunk.AppendChild(altChunkProperties);
altChunk.Id = altChunkId;
mainPart.Document.Body.Append(altChunk);
mainPart.Document.Save();
When I open one of the template documents in the Productivity tool I can see all the elements which I'd expect:
However, when I look at the 'master' document, all I can get down to is the MatchSource:
What I don't understand is why I can't see the paragraph tags etc in the master document that's produced. Can anybody help me understand how I would see this information? Is there something wrong with the document structure?
That's because of the way altchunks are merged to the master document. altchunks won't change the master document's openxml markup but it does add the file as embedded resources. Think of it as if you are attaching something to an email but not adding it to the actual email body text. There are some third party solutions like Document Builder that will merge the document by modifying the master documents markup. Then you can see what you are expecting in the productivity tool.
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 OpenXML SDK 2.0 to do the following:
Open A.docx
Modify the document
Save the modified document as B.docx
A & B would be parameters to a method and they could be the same. Assuming they are not the same, A should not be modified at all.
I cannot see a "SaveAs" method, in fact `WordprocessingDocument" class doesn't really seem to support concept of file location.
How should I do this?
I use a memory stream and pass it to the WordprocessingDocument.Open method. After I'm done changing the document, I just write the bytes to the destination:
var source = File.ReadAllBytes(filename);
using (var ms = new MemoryStream()) {
ms.Write(source, 0, source.Length);
/* settings defined elsewhere */
using (var doc = WordprocessingDocument.Open(ms, true, settings)) {
/* do something to the doc */
}
/* used in File.WriteAllBytes elsewhere */
return ms.ToArray();
}
+1 on the answer already given...
Here is an MSDN article that discusses working with in-memory Open XML documents. I think that you will find it relevant.
http://msdn.microsoft.com/en-us/library/office/ee945362.aspx