iTextSharp 生成损坏的 PDF 文件
iTextSharp generates corrupted PDF file
我正在尝试从 HTML 字符串和外部 css 文件生成 PDF 文件并将 PDF 保存到磁盘。从这个例子可以看出,我使用的是非常简单的 html。我知道 css 文件正在通过查看智能感知读入 ccsResolver。
这是我使用的代码:
internal string Create(PdfDocumentDefinition documentDefinition)
{
MemoryStream output = new MemoryStream();
MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes("<html><head></head><body>Hello, World!</body></html>"));
string pathName = @WebConfigurationManager.AppSettings["StagingPath"] + documentDefinition.DocumentName + ".pdf";
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, output);
using (output)
{
using (document)
{
document.Open();
CssResolverPipeline pipeline = SetCssResolver(documentDefinition.CssFiles, document, writer);
XMLWorker worker = new XMLWorker(pipeline, true);
XMLParser parser = new XMLParser(worker);
parser.Parse(input);
output.Position = 0;
}
Byte[] data = output.ToArray();
File.WriteAllBytes(pathName, data);
}
return pathName;
}
private CssResolverPipeline SetCssResolver(List<String> cssFiles, Document document, PdfWriter writer)
{
var htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
ICSSResolver cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
if (cssFiles != null)
{
foreach (String cssFile in cssFiles)
{
//cssResolver.AddCssFile(cssFile, true);
}
}
return new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));
}
这是在 NotePad++ 中查看的输出:
2 0 obj
<</Length 117/Filter/FlateDecode>>stream
xœ+ä*ä2гP€á¢t.c 256U0·0R(JåJã
ĪÊÜÒXÏÔHÁÌBÏÌBÁÐPÏ¢Ø@!¨¤Å)¤ÌÂÐH!$(¬khbè»*€„Ò¸4<RsròuÂó‹rR5C²€Š@J\C€ú¼i!*
endstream
endobj
4 0 obj
<</Type/Page/MediaBox[0 0 595 842]/Resources<</Font<</F1 1 0 R>>>>/Contents 2 0 R/Parent 3 0 R>>
endobj
1 0 obj
<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding/WinAnsiEncoding>>
endobj
3 0 obj
<</Type/Pages/Count 1/Kids[4 0 R]>>
endobj
5 0 obj
<</Type/Catalog/Pages 3 0 R>>
endobj
6 0 obj
<</Producer(iTextSharp’ 5.5.7 ©2000-2015 iText Group NV \(AGPL-version\))/CreationDate(D:20151026102026-05'00')/ModDate(D:20151026102026-05'00')>>
endobj
xref
0 7
0000000000 65535 f
0000000311 00000 n
0000000015 00000 n
0000000399 00000 n
0000000199 00000 n
0000000450 00000 n
0000000495 00000 n
trailer
<</Size 7/Root 5 0 R/Info 6 0 R/ID [<055082e8139638e35ce08dedae069690><055082e8139638e35ce08dedae069690>]>>
%iText-5.5.7
startxref
657
%%EOF
我已经为此工作了大约 4 个小时。谁能看出为什么它没有生成有效的 PDF?
感谢 mkl 的提示,我能够解决这个问题,但是,必须以这种方式完成似乎并不正确。一定会有更好的办法。但解决方案是将输出刷新到一个数组以获取前 15 个字节,然后关闭文档并刷新到另一个数组以获取前 15 个字节之后的所有内容(据我所知,输出流从不包含所有字节),然后创建第三个数组并将前两个数组复制到其中。完整代码如下:
internal string Create(PdfDocumentDefinition documentDefinition)
{
string pathName = @WebConfigurationManager.AppSettings["StagingPath"] + documentDefinition.DocumentName + ".pdf";
MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(documentDefinition.Source));
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
MemoryStream output = new MemoryStream();
using (output)
{
PdfWriter writer = PdfWriter.GetInstance(document, output);
document.Open();
CssResolverPipeline pipeline = SetCssResolver(documentDefinition.CssFiles, document, writer);
XMLWorker worker = new XMLWorker(pipeline, true);
XMLParser parser = new XMLParser(worker);
parser.Parse(input);
output.Position = 0;
Byte[] firstBytes = output.ToArray();
document.Close();
Byte[] lastBytes = output.ToArray();
Byte[] allBytes = new Byte[firstBytes.Length + lastBytes.Length];
firstBytes.CopyTo(allBytes, 0);
lastBytes.CopyTo(allBytes, firstBytes.Length);
File.WriteAllBytes(pathName, allBytes);
}
return pathName;
}
private CssResolverPipeline SetCssResolver(List<String> cssFiles, Document document, PdfWriter writer)
{
var htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
ICSSResolver cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
if (cssFiles != null)
{
foreach (String cssFile in cssFiles)
{
cssResolver.AddCssFile(cssFile, true);
}
}
return new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));
}
正在尝试
我将 OP 的原始代码简化为
[Test]
public void ResetStreamPositionAtEndOfUsing()
{
string outputFilePath = @"test-results\misc\resetStreamPosition.pdf";
Directory.CreateDirectory(@"test-results\misc\");
MemoryStream output = new MemoryStream();
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, output);
using (output)
{
using (document)
{
document.Open();
document.Add(new Paragraph("Test"));
output.Position = 0;
}
Byte[] data = output.ToArray();
File.WriteAllBytes(outputFilePath, data);
}
}
运行 它生成了一个无效的 PDF 文件,几乎与 OP 粘贴到问题中的文件相同。特别是缺少 PDF header。
按照Chris Haas的建议,我删除了伪线
output.Position = 0;
事实上,现在输出的 PDF 是有效的,特别是它有它的 header。
分析
MemoryStream output
发生了什么?
MemoryStream output = new MemoryStream();
output
创建为空。
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, output);
新的PdfWriter
只是实例化,什么都没写,output
还是空的
using (output)
{
using (document)
{
document.Open();
document
通知 writer
文档构建开始,因此 writer
首先编写 PDF 序言,即 header 行和 "binary" 注释; output
现在包含 %PDF-1。4\n%âãÏÓ\n,当前流位置在末尾。
document.Add(new Paragraph("Test"));
在当前(第一)页添加了一个新段落,但只在内存中,构成当前页内容的objects只有在开始新页或文档被写入时才会被写入完成的。 output
仍然包含%PDF-1。4\n%âãÏÓ\n,当前流位置还在最后。
output.Position = 0;
流位置已重置。 output
仍然包含 %PDF-1.4\n%âãÏÓ\n,但 当前流位置现在位于开始处!
}
这是using (document)
代码块的结尾。于是调用了document的Dispose
方法。其中 document
告诉 writer
文档创建完成。 writer
,因此,现在写入仍在内存中的所有文档 objects,然后添加 PDF 文件结语(交叉引用、预告片等)。
由于流位置现在在流的开头,现有内容被覆盖! output
现在包含 2 0 obj...%%EOF,即完整的 PDF 仅缺少 PDF 序言。
我正在尝试从 HTML 字符串和外部 css 文件生成 PDF 文件并将 PDF 保存到磁盘。从这个例子可以看出,我使用的是非常简单的 html。我知道 css 文件正在通过查看智能感知读入 ccsResolver。
这是我使用的代码:
internal string Create(PdfDocumentDefinition documentDefinition)
{
MemoryStream output = new MemoryStream();
MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes("<html><head></head><body>Hello, World!</body></html>"));
string pathName = @WebConfigurationManager.AppSettings["StagingPath"] + documentDefinition.DocumentName + ".pdf";
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, output);
using (output)
{
using (document)
{
document.Open();
CssResolverPipeline pipeline = SetCssResolver(documentDefinition.CssFiles, document, writer);
XMLWorker worker = new XMLWorker(pipeline, true);
XMLParser parser = new XMLParser(worker);
parser.Parse(input);
output.Position = 0;
}
Byte[] data = output.ToArray();
File.WriteAllBytes(pathName, data);
}
return pathName;
}
private CssResolverPipeline SetCssResolver(List<String> cssFiles, Document document, PdfWriter writer)
{
var htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
ICSSResolver cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
if (cssFiles != null)
{
foreach (String cssFile in cssFiles)
{
//cssResolver.AddCssFile(cssFile, true);
}
}
return new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));
}
这是在 NotePad++ 中查看的输出:
2 0 obj
<</Length 117/Filter/FlateDecode>>stream
xœ+ä*ä2гP€á¢t.c 256U0·0R(JåJã
ĪÊÜÒXÏÔHÁÌBÏÌBÁÐPÏ¢Ø@!¨¤Å)¤ÌÂÐH!$(¬khbè»*€„Ò¸4<RsròuÂó‹rR5C²€Š@J\C€ú¼i!*
endstream
endobj
4 0 obj
<</Type/Page/MediaBox[0 0 595 842]/Resources<</Font<</F1 1 0 R>>>>/Contents 2 0 R/Parent 3 0 R>>
endobj
1 0 obj
<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding/WinAnsiEncoding>>
endobj
3 0 obj
<</Type/Pages/Count 1/Kids[4 0 R]>>
endobj
5 0 obj
<</Type/Catalog/Pages 3 0 R>>
endobj
6 0 obj
<</Producer(iTextSharp’ 5.5.7 ©2000-2015 iText Group NV \(AGPL-version\))/CreationDate(D:20151026102026-05'00')/ModDate(D:20151026102026-05'00')>>
endobj
xref
0 7
0000000000 65535 f
0000000311 00000 n
0000000015 00000 n
0000000399 00000 n
0000000199 00000 n
0000000450 00000 n
0000000495 00000 n
trailer
<</Size 7/Root 5 0 R/Info 6 0 R/ID [<055082e8139638e35ce08dedae069690><055082e8139638e35ce08dedae069690>]>>
%iText-5.5.7
startxref
657
%%EOF
我已经为此工作了大约 4 个小时。谁能看出为什么它没有生成有效的 PDF?
感谢 mkl 的提示,我能够解决这个问题,但是,必须以这种方式完成似乎并不正确。一定会有更好的办法。但解决方案是将输出刷新到一个数组以获取前 15 个字节,然后关闭文档并刷新到另一个数组以获取前 15 个字节之后的所有内容(据我所知,输出流从不包含所有字节),然后创建第三个数组并将前两个数组复制到其中。完整代码如下:
internal string Create(PdfDocumentDefinition documentDefinition)
{
string pathName = @WebConfigurationManager.AppSettings["StagingPath"] + documentDefinition.DocumentName + ".pdf";
MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(documentDefinition.Source));
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
MemoryStream output = new MemoryStream();
using (output)
{
PdfWriter writer = PdfWriter.GetInstance(document, output);
document.Open();
CssResolverPipeline pipeline = SetCssResolver(documentDefinition.CssFiles, document, writer);
XMLWorker worker = new XMLWorker(pipeline, true);
XMLParser parser = new XMLParser(worker);
parser.Parse(input);
output.Position = 0;
Byte[] firstBytes = output.ToArray();
document.Close();
Byte[] lastBytes = output.ToArray();
Byte[] allBytes = new Byte[firstBytes.Length + lastBytes.Length];
firstBytes.CopyTo(allBytes, 0);
lastBytes.CopyTo(allBytes, firstBytes.Length);
File.WriteAllBytes(pathName, allBytes);
}
return pathName;
}
private CssResolverPipeline SetCssResolver(List<String> cssFiles, Document document, PdfWriter writer)
{
var htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
ICSSResolver cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
if (cssFiles != null)
{
foreach (String cssFile in cssFiles)
{
cssResolver.AddCssFile(cssFile, true);
}
}
return new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));
}
正在尝试
我将 OP 的原始代码简化为
[Test]
public void ResetStreamPositionAtEndOfUsing()
{
string outputFilePath = @"test-results\misc\resetStreamPosition.pdf";
Directory.CreateDirectory(@"test-results\misc\");
MemoryStream output = new MemoryStream();
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, output);
using (output)
{
using (document)
{
document.Open();
document.Add(new Paragraph("Test"));
output.Position = 0;
}
Byte[] data = output.ToArray();
File.WriteAllBytes(outputFilePath, data);
}
}
运行 它生成了一个无效的 PDF 文件,几乎与 OP 粘贴到问题中的文件相同。特别是缺少 PDF header。
按照Chris Haas的建议,我删除了伪线
output.Position = 0;
事实上,现在输出的 PDF 是有效的,特别是它有它的 header。
分析
MemoryStream output
发生了什么?
MemoryStream output = new MemoryStream();
output
创建为空。
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, output);
新的PdfWriter
只是实例化,什么都没写,output
还是空的
using (output)
{
using (document)
{
document.Open();
document
通知 writer
文档构建开始,因此 writer
首先编写 PDF 序言,即 header 行和 "binary" 注释; output
现在包含 %PDF-1。4\n%âãÏÓ\n,当前流位置在末尾。
document.Add(new Paragraph("Test"));
在当前(第一)页添加了一个新段落,但只在内存中,构成当前页内容的objects只有在开始新页或文档被写入时才会被写入完成的。 output
仍然包含%PDF-1。4\n%âãÏÓ\n,当前流位置还在最后。
output.Position = 0;
流位置已重置。 output
仍然包含 %PDF-1.4\n%âãÏÓ\n,但 当前流位置现在位于开始处!
}
这是using (document)
代码块的结尾。于是调用了document的Dispose
方法。其中 document
告诉 writer
文档创建完成。 writer
,因此,现在写入仍在内存中的所有文档 objects,然后添加 PDF 文件结语(交叉引用、预告片等)。
由于流位置现在在流的开头,现有内容被覆盖! output
现在包含 2 0 obj...%%EOF,即完整的 PDF 仅缺少 PDF 序言。