如何解压缩/缩小PDF流

时间:2017-02-20 22:37:53

标签: pdf go gzip zlib

使用<div class="parent"> <div class="sibling-1">Sibling 1</div> <div class="sibling-2">Sibling 2</div> <div class="sibling-3">Sibling 3</div> </div>,其中包含2个大流(第1页和第2页),以及一堆其他对象和较小的流。我正试图缩小流,以使用源数据,但我正在努力。我只能得到损坏的输入和无效的校验和错误。

我编写了一个测试脚本来帮助调试,并从文件中抽出较小的流来测试。

以下是来自原始pdf的2个流及其长度对象:

流1

2016-W4 pdf

流2

149 0 obj
<< /Length 150 0 R /Filter /FlateDecode /Type /XObject /Subtype /Form /FormType
1 /BBox [0 0 8 8] /Resources 151 0 R >>
stream
x+TT(T0B ,JUWÈS0Ð37±402V(NFJSþ¶
«
endstream
endobj
150 0 obj
42
endobj

我只将142 0 obj << /Length 143 0 R /Filter /FlateDecode /Type /XObject /Subtype /Form /FormType 1 /BBox [0 0 0 0] /Resources 144 0 R >> stream x+Tçã endstream endobj 143 0 obj 11 endobj 内容复制到Vim 中的新文件中(不包括stream之后和stream之前的回车)

我试过了两个:

  • endstream rfc-1951) - (删除前2个字节(compress/flateCMF))
  • FLG rfc-1950

我已将流转换为compress/zlib以获取以下内容:

[]byte

我确定我在某处做错了什么,我只是看不到它。

(pdf在观众中打开)

3 个答案:

答案 0 :(得分:2)

二进制数据永远不会从文本编辑器中复制出来/保存。可能会出现这样的情况,并且它只是为火焰添加油。

您最终从PDF中“挖掘出来”的数据很可能与PDF中 的实际数据不同。您应该从十六进制编辑器中获取数据(例如,尝试hecate获取新的内容),或编写一个保存它的简单应用程序(严格将文件作为二进制文件处理)。

提示#1:

显示的二进制数据分布在多行中。二进制数据不包含回车符,这是一个文本控件。如果是这样,那意味着编辑器 将其解释为文本,因此一些代码/字符“消耗”以开始新行。多个序列可以被解释为相同的换行符(例如\n\r\n)。通过排除它们,您已经处于数据丢失状态,通过包含它们,您可能已经有了不同的序列。如果数据被解释并显示为文本,则可能会出现更多问题,因为控制字符越多,显示时可能不会显示某些字符。

提示#2:

使用flateReaderFn时,解码第二个示例成功(完成没有错误)。这意味着“你正在吠叫正确的树”,但成功取决于实际数据是什么以及文本编辑器“扭曲”到什么程度。

答案 1 :(得分:1)

好的,忏悔时间......

我非常想要了解deflate,我完全忽略了Vim没有正确地将流内容保存到新文件中的事实。所以我花了很多时间阅读RFC,并深入研究Go compress/...软件包的内部结构,假设问题与我的代码有关。

在我发布我的问题后不久,我尝试整体阅读PDF,找到stream / endstream位置,并通过缩小推送。当我看到内容滚动屏幕时,我意识到我的愚蠢错误。

+1 @icza,这正是我的问题。

从那时起它很好,因为我对整个过程有了更好的理解,而不是第一次使用它的时候。

答案 2 :(得分:0)

根据所使用的过滤器,从 PDF 中提取对象可能会很棘手。过滤器还可以有其他需要正确处理的选项。

对于有兴趣提取对象而不关心过程的低级细节的人。

要从 PDF 中获取单个对象并对其进行解码,可以按如下方式进行:

package main

import (
    "fmt"
    "os"
    "strconv"

    "github.com/unidoc/unipdf/v3/core"
    "github.com/unidoc/unipdf/v3/model"
)


func main() {
    objNum := 149 // Get object 149
    err := inspectPdfObject("input.pdf", objNum)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
}

func inspectPdfObject(inputPath string, objNum int) error {
    f, err := os.Open(inputPath)
    if err != nil {
        return err
    }

    defer f.Close()

    pdfReader, err := model.NewPdfReader(f)
    if err != nil {
        return err
    }

    isEncrypted, err := pdfReader.IsEncrypted()
    if err != nil {
        return err
    }

    if isEncrypted {
        // If encrypted, try decrypting with an empty one.
        // Can also specify a user/owner password here by modifying the line below.
        auth, err := pdfReader.Decrypt([]byte(""))
        if err != nil {
            fmt.Printf("Decryption error: %v\n", err)
            return err
        }
        if !auth {
            fmt.Println(" This file is encrypted with opening password. Modify the code to specify the password.")
            return nil
        }
    }

    obj, err := pdfReader.GetIndirectObjectByNumber(objNum)
    if err != nil {
        return err
    }

    fmt.Printf("Object %d: %s\n", objNum, obj.String())

    if stream, is := obj.(*core.PdfObjectStream); is {
        decoded, err := core.DecodeStream(stream)
        if err != nil {
            return err
        }
        fmt.Printf("Decoded:\n%s", decoded)
    } else if indObj, is := obj.(*core.PdfIndirectObject); is {
        fmt.Printf("%T\n", indObj.PdfObject)
        fmt.Printf("%s\n", indObj.PdfObject.String())
    }

    return nil
}

完整示例:pdf_get_object.go

披露:我是 UniPDF 的原始开发者。

相关问题