C# 无法使用MemoryStream合并2个PDF

C# 无法使用MemoryStream合并2个PDF,c#,wkhtmltopdf,C#,Wkhtmltopdf,我有一个c#类,它接受HTML并使用wkhtmltopdf将其转换为PDF。 正如您将在下面看到的,我正在生成3个PDF—横向、纵向和两者的组合。 properties对象包含作为字符串的html以及横向/纵向的参数 System.IO.MemoryStream PDF = new WkHtmlToPdfConverter().GetPdfStream(properties); System.IO.FileStream file = new System.IO.FileStream("abc_l

我有一个c#类,它接受HTML并使用wkhtmltopdf将其转换为PDF。
正如您将在下面看到的,我正在生成3个PDF—横向、纵向和两者的组合。

properties
对象包含作为字符串的html以及横向/纵向的参数

System.IO.MemoryStream PDF = new WkHtmlToPdfConverter().GetPdfStream(properties);
System.IO.FileStream file = new System.IO.FileStream("abc_landscape.pdf", System.IO.FileMode.Create);
PDF.Position = 0;

properties.IsHorizontalOrientation = false;
System.IO.MemoryStream PDF_portrait = new WkHtmlToPdfConverter().GetPdfStream(properties);
System.IO.FileStream file_portrait = new System.IO.FileStream("abc_portrait.pdf", System.IO.FileMode.Create);
PDF_portrait.Position = 0;

System.IO.MemoryStream finalStream = new System.IO.MemoryStream();
PDF.CopyTo(finalStream);
PDF_portrait.CopyTo(finalStream);
System.IO.FileStream file_combined = new System.IO.FileStream("abc_combined.pdf", System.IO.FileMode.Create);

try
{
    PDF.WriteTo(file);
    PDF.Flush();

    PDF_portrait.WriteTo(file_portrait);
    PDF_portrait.Flush();

    finalStream.WriteTo(file_combined);
    finalStream.Flush();
}
catch (Exception)
{
    throw;
}
finally
{
    PDF.Close();
    file.Close();

    PDF_portrait.Close();
    file_portrait.Close();

    finalStream.Close();
    file_combined.Close();
}
pdf“abc_landscape.pdf”和“abc_ratio.pdf”按预期正确生成,但当我尝试将两者合并到第三个pdf(abc_combined.pdf)中时,操作失败。

我正在使用
MemoryStream
执行合并,在调试时,我可以看到
finalStream.length
等于前两个PDF的总和。但是当我试图打开PDF时,我只看到两个PDF中的一个的内容。
如下所示:

此外,当我试图关闭“abc_combined.pdf”时,系统会提示我保存它,这在其他两个pdf中不会发生。

下面是一些我已经尝试过但没有效果的东西:

  • 将CopyTo()更改为WriteTo()
  • 将相同的PDF(横向或纵向)与其自身合并

    如果需要,下面是
    GetPdfStream()
    方法的详细说明
  • var htmlStream=newmemoryStream();
    var writer=新的StreamWriter(htmlStream);
    writer.Write(htmlString);
    writer.Flush();
    htmlStream.Position=0;
    返回htmlStream;
    过程=过程启动(psi);
    process.EnableRaisingEvents=true;
    尝试
    {
    process.Start();
    process.BeginErrorReadLine();
    var inputTask=Task.Run(()=>
    {
    htmlStream.CopyTo(process.StandardInput.BaseStream);
    process.StandardInput.Close();
    });
    //将输出复制到memorystream
    MemoryStream pdf=新的MemoryStream();
    var outputTask=Task.Run(()=>
    {
    process.StandardOutput.BaseStream.CopyTo(pdf);
    });
    Task.WaitAll(输入任务、输出任务);
    process.WaitForExit();
    //重置存储器流读取位置
    pdf.Position=0;
    返回pdf;
    }
    捕获(例外情况除外)
    {
    掷骰子;
    }
    最后
    {
    process.Dispose();
    }
    
    这个来自Stack Overflow()的答案对我有用:

            using (PdfDocument one = PdfReader.Open("pdf 1.pdf", PdfDocumentOpenMode.Import))
            using (PdfDocument two = PdfReader.Open("pdf 2.pdf", PdfDocumentOpenMode.Import))
            using (PdfDocument outPdf = new PdfDocument())
            {
                CopyPages(one, outPdf);
                CopyPages(two, outPdf);
    
                outPdf.Save("file1and2.pdf");
            }
    
            void CopyPages(PdfDocument from, PdfDocument to)
            {
                for (int i = 0; i < from.PageCount; i++)
                {
                    to.AddPage(from.Pages[i]);
                }
            }
    
    使用(pdfdocumentone=PdfReader.Open(“pdf 1.pdf”,PdfDocumentOpenMode.Import))
    使用(PdfDocument two=PdfReader.Open(“pdf 2.pdf”,PdfDocumentOpenMode.Import))
    使用(PdfDocument outPdf=new PdfDocument())
    {
    复印页(一页,输出页);
    复印页(两页,输出DF);
    outPdf.Save(“file1和file2.pdf”);
    }
    作废副本页(PDF文档发件人、PDF文档收件人)
    {
    for(int i=0;i
    PDF的工作原理并非如此。PDF是特定格式的结构化文件。 您不能只将一个字节附加到另一个字节,然后期望结果是有效的文档

    您将拥有一个能够理解格式并能为您执行操作的用户,或者开发您自己的解决方案。

    在不使用第三方库的情况下,将pdf合并到C#或任何其他语言中是不直接的

    我假设您不使用库的要求是,大多数免费库、nuget包都有商业使用的限制或/和成本

    我做了研究,发现了一个名为with package的开源库,它也可用于Java。它是免费的,不受限制(如果你愿意捐赠)。这个图书馆有很多书。一个这样的文档可以将2个或多个文档合并到一个文档中

    我举了一个例子,将一个包含多个pdf文件的文件夹合并并保存到同一个或另一个文件夹中。也可以使用MemoryStream,但我觉得在这种情况下没有必要

    代码是自我解释的,这里的关键点是使用
    序列化模式enum.Incremental

    public static void MergePdf(string srcPath, string destFile)
    {
        var list = Directory.GetFiles(Path.GetFullPath(srcPath));
        if (string.IsNullOrWhiteSpace(srcPath) || string.IsNullOrWhiteSpace(destFile) || list.Length <= 1)
            return;
        var files = list.Select(File.ReadAllBytes).ToList();
        using (var dest = new org.pdfclown.files.File(new org.pdfclown.bytes.Buffer(files[0])))
        {
            var document = dest.Document;
            var builder = new org.pdfclown.tools.PageManager(document);
            foreach (var file in files.Skip(1))
            {
                using (var src = new org.pdfclown.files.File(new org.pdfclown.bytes.Buffer(file)))
                { builder.Add(src.Document); }
            }
    
            dest.Save(destFile, SerializationModeEnum.Incremental);
        }
    }
    
    输入示例
    PDF文档A和PDF文档B

    输出示例

    我的研究链接:

    免责声明:此答案的一部分来自我的个人网站,源代码为github


    PDF文件不仅仅是文本和图像。在幕后有一个严格的文件格式,它描述了PDF版本、文件中包含的对象以及在哪里可以找到它们

    为了合并2个PDF,您需要操纵流

    首先,您只需要保存其中一个文件的头。这很简单,因为这只是第一行

    然后你可以写第一页的正文,然后写第二页

    现在最困难的部分,也可能是说服您使用库的部分,是您必须重新构建外部参照表。外部参照表是一个交叉引用表,它描述文档的内容,更重要的是描述在何处查找每个元素。您必须计算第二页的字节偏移量,将其外部参照表中的所有元素移动那么多,然后将其外部参照表添加到第一页。您还需要确保在外部参照表中为分页符创建对象

    完成后,您需要重新构建文档拖车,它告诉应用程序文档的各个部分在哪里


    这不是一件小事,您最终将重新编写大量已经存在的代码。

    我正在寻找一些没有PdfSharp@Sanketh.K.JainMemoryStream独占还是允许使用其他技术?()只需C#。没有其他技术。到目前为止,我在一个流中有我的PDF,它是作为wkhtmltopdf的输出生成的。我不明白你为什么不能在已经使用wkhtmltopdf的情况下使用另一个nuget包,但这只是我的意见。我理解。但这就是我得到的要求:PPdf是一种结构化文件格式,这意味着它由许多小部分组成,可以构建一个完整的文档。格式见本文件第7.5节
    var srcPath = @"C:\temp\pdf\input";
    var destFile = @"c:\temp\pdf\output\merged.pdf";
    MergePdf(srcPath, destFile);