iTextSharp生成损坏的PDF文件

本文关键字:PDF 文件 损坏 iTextSharp | 更新日期: 2023-09-27 18:19:14

我正在尝试从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吗?

iTextSharp生成损坏的PDF文件

尝试一下

我将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头丢失了。

根据Chris Haas的建议,我删除了假的 行。
            output.Position = 0;

确实,现在输出的PDF是有效的,特别是它有它的标题。

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序言,即标题行和"二进制"注释;output现在包含%PDF-1.4'n%âãÏÓ'n,当前流在末尾的位置。

            document.Add(new Paragraph("Test"));

一个新的段落被添加到当前(第一)页,但只在内存中,构成当前页内容的对象只会在新页开始或文档完成时写入。output仍然包含%PDF-1.4'n%âãÏÓ'n,当前流位置仍然在末尾。

            output.Position = 0;

流位置被重置。output仍然包含%PDF-1.4'n%âãÏÓ'n,但是当前流位置现在在开始!

        }

这是using (document)代码块的结尾。这样,就调用了document的Dispose方法。其中document告诉writer文档创建完成。因此,writer现在写入仍在内存中的所有文档对象,然后添加PDF文件尾声(交叉引用,预告片,…)。

由于流的位置现在是流的开始,现有的内容被覆盖!output现在包含 20 obj…%%EOF,即只缺少PDF序言的完整PDF。

多亏了mkl的提示,我能够解决这个问题,但是,它似乎不正确,它必须这样做。一定有更好的办法。但解决方案是将输出刷新到一个数组以获得前15个字节,然后关闭文档并刷新到另一个数组以获得前15个字节之后的所有内容(据我所见,输出流从未包含所有字节),然后创建第三个数组并将前2个复制到其中。下面是完整的代码:

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)));            
}