从多页PDF创建单页PDF,无需外部库

时间:2019-12-17 04:19:35

标签: c# pdf binaryfiles pdf-scraping

围绕SO我已经看到了以下问题: Create Multi-Page PDF from other PDFs

但是它没有回答我的需要。 考虑我有一个20页的PDF。到目前为止一切顺利。

在同一地方,我只能拥有一页的PDF。这将用作我的模板PDF。 我想做的是替换模板PDF上的内容(FlateDecodeStream)(以及长度),并生成一个新的单页页面。

我可以使用PDF了;但是,不会显示小徽标,并且Adobe Reader表示正确显示PDF存在问题(谷歌浏览器chrome和edge只是不显示徽标,没有错误消息)。

我试图最后弄乱外部参照表(手动调整值),但是得到了相同的结果。

有没有对PDF有一定了解的人可以给我任何输入?

我正在上传template_pdf和其他我要提取数据并创建第三个pdf的文件(使用模板pdf,但包含另一个PDF的内容)。另外,我还将上传我手工制作的,显示错误的PDF(它显示数据但没有JPEG徽标)。

这里的所有内容:https://drive.google.com/drive/folders/1tsGIbtbfwuATPQ6a_VPjnxLT4ozzNt0s?usp=sharing

我一直在使用HxD进行所有操作(以查看十六进制内容并复制\粘贴数据)

预先感谢

编辑:我正在添加当前用于生成PDF的代码。即使外部参照表正常(具有正确的位置),其也是无效的PDF。该代码非常丑陋,但是现在我正在寻找使它起作用的方法(而不是编写一个不错的代码)

static void Main(string[] args)
    {

        Console.WriteLine("Hello World!");


        var jpegLogo = File.ReadAllBytes(@"C:\test\Ginfes-Reboot\jpegLogo.raw");
        var pdfStream = File.ReadAllBytes(@"C:\test\Ginfes-Reboot\pdfStream.raw");
        using (BinaryWriter b = new BinaryWriter(
        File.Open(@"C:\test\Ginfes-Reboot\newPdf_newmethod.pdf", FileMode.Create)))
        {
            WritePDFAgain(b,jpegLogo,pdfStream);

        }

    }
    private static void WritePDFAgain(BinaryWriter b, byte[] jpegLogo,byte[] pdfStream)
    {
        List<long> offSets = new List<long>();
        string str = "%PDF-1.4" + "\n";
        var byteArr = Encoding.ASCII.GetBytes(str);
        b.Write(byteArr);
        byteArr = StringToByteArray("25E2E3CFD30A");
        b.Write(byteArr);
        offSets.Add(b.BaseStream.Position);//0
        str = "3 0 obj" + "\n" + "<</Type/XObject/ColorSpace/DeviceRGB/Subtype/Image/BitsPerComponent 8/Width 60/Length 3857/Height 60/Filter/DCTDecode>>stream" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(jpegLogo);
        b.Write(Encoding.ASCII.GetBytes("\n"));
        b.Write(Encoding.ASCII.GetBytes("endstream" +"\n" + "endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//1
        str = "4 0 obj" + "\n" + "<</Length " + pdfStream.Length + "/Filter/FlateDecode>>stream" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(pdfStream);
        b.Write(Encoding.ASCII.GetBytes("\n"));
        b.Write(Encoding.ASCII.GetBytes("endstream" + "\n" + "endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//2
        str = "1 0 obj" + "\n" + "<</Group<</Type/Group/CS/DeviceRGB/S/Transparency>>/Parent 5 0 R/Contents 4 0 R/Type/Page/Resources<</XObject<</img0 3 0 R>>/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]/ColorSpace<</CS/DeviceRGB>>/Font<</F1 2 0 R>>>>/MediaBox[0 0 595 936]>>" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//3
        str = "6 0 obj" + "\n" + "[1 0 R/XYZ 0 814 0]" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//4
        str = "2 0 obj" + "\n" + "<</BaseFont/Helvetica/Type/Font/Encoding/WinAnsiEncoding/Subtype/Type1>>" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//5
        str = "5 0 obj" + "\n" + "<</ITXT(2.1.7)/Type/Pages/Count 1/Kids[1 0 R]>>" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//6
        str = "7 0 obj" + "\n" + "<</Names[(JR_PAGE_ANCHOR_0_1) 6 0 R]>>" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//7
        str = "8 0 obj" + "\n" + "<</Dests 7 0 R>>" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//8
        str = "9 0 obj" + "\n" + "<</Names 8 0 R/Type/Catalog/ViewerPreferences<</PrintScaling/AppDefault>>/Pages 5 0 R>>" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        offSets.Add(b.BaseStream.Position);//9
        str = "10 0 obj" + "\n" + @"<</Creator(JasperReports \(nfs_novo\))/Producer(iText 2.1.7 by 1T3XT)/ModDate(D:20191211152903-03'00')/CreationDate(D:20191211152903-03'00')>>" + "\n";
        b.Write(Encoding.ASCII.GetBytes(str));
        b.Write(Encoding.ASCII.GetBytes("endobj" + "\n"));
        b.Write(Encoding.ASCII.GetBytes("xref" + "\n" + "0 11" + "\n"));
        b.Write(Encoding.ASCII.GetBytes("0000000000 65535 f " + "\n"));            
        b.Write(Encoding.ASCII.GetBytes("000000" + offSets.ElementAt(2) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("000000" + offSets.ElementAt(4) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("00000000"+ offSets.ElementAt(0) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("000000" + offSets.ElementAt(1) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("000000" + offSets.ElementAt(5) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("000000" + offSets.ElementAt(3) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("00000" + offSets.ElementAt(6) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("00000" + offSets.ElementAt(7) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("00000" + offSets.ElementAt(8) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("00000" + offSets.ElementAt(9) + " 00000 f " + "\n"));
        b.Write(Encoding.ASCII.GetBytes("trailer" + "\n" + "<</Root 9 0 R/ID [<10a2f7fd162aa44a268ebb6f31cc98c4><c36ebb9dc93cd9a72f229f618092eeb0>]/Info 10 0 R/Size 11>>" + "\n"));
        b.Write(Encoding.ASCII.GetBytes("startxref" + "\n" + (b.BaseStream.Position + 6) + "%%EOF" + "\n"));
    }

使用的文件: https://drive.google.com/drive/folders/1i3J-yioFvcoiakyc_Wi8ddn9g6Pxy7zd?usp=sharing

1 个答案:

答案 0 :(得分:1)

大部分时间都在那儿;从示例中生成的PDF的唯一问题是pdfStream中引用的图像资源被命名为img10,而在创建资源字典时分配的名称是img0。 / p>

下面是一些代码,这些代码将标识正确的引用资源(在页面内容上使用正则表达式),然后您可以在构建字典时使用它。

您需要以下其他using指令:

using System.IO.Compression;
using System.Text.RegularExpressions;

此方法解压缩页面内容流并匹配图像资源名称:

private static string GetImageResourceName(byte[] pdfStream) {
    using (MemoryStream ms = new MemoryStream(pdfStream)) {                
        ms.Seek(2, SeekOrigin.Begin);   // skip first 2 bytes (zlib header)

        using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress)) {
            using (StreamReader sr = new StreamReader(ds)) {
                string contents = sr.ReadToEnd();

                // PostScript command referencing the image resource looks like: /img123 Do
                return Regex.Match(contents, @"\b(img\d+)\s+Do\b").Groups[1].Value;
            }
        }
    }
}

最后,您只需要在WritePDFAgain方法中更改此行:

str = String.Format(
    "1 0 obj\n<</Group<</Type/Group/CS/DeviceRGB/S/Transparency>>" 
    + "/Parent 5 0 R/Contents 4 0 R/Type/Page/Resources<</XObject" 
    + "<</{0} 3 0 R>>/ProcSet [/PDF /Text /ImageB /ImageC " 
    + "/ImageI]/ColorSpace<</CS/DeviceRGB>>/Font<</F1 2 0 R>>>>" 
    + "/MediaBox[0 0 595 936]>>\n", 
    GetImageResourceName(pdfStream)
);

根据我在评论中的免责声明,此代码仅适用于这种非常特殊的情况和输入数据。这绝不是通用解决方案,但我认为您会接受。

我要重申一点,如果您打算不为此使用任何外部库,那么您很可能最终会编写自己的库(尽管这是一个非常基本的库)。