首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何解压/平减PDF流

如何解压/平减PDF流
EN

Stack Overflow用户
提问于 2017-02-20 22:37:52
回答 3查看 10K关注 0票数 4

使用2016-W4 pdf,它有两个大流(第1和第2页),以及一组其他对象和更小的流。为了处理源数据,我试图使流泄气,但我正在挣扎。我只能得到错误的输入和无效的校验和错误。

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

以下是来自原始pdf的两个流,以及它们的长度对象:

流1

代码语言:javascript
复制
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(T0�B ,JUWÈS0Ð37±402V(NFJS�þ¶
«
endstream
endobj
150 0 obj
42
endobj

流2

代码语言:javascript
复制
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

我只将stream内容复制到Vim中的新文件中(不包括stream之后和endstream__之前的回车返回)。

我都试过了:

我已经将流转换为[]byte,如下所示:

代码语言:javascript
复制
package main

import (
    "bytes"
    "compress/flate"
    "compress/gzip"
    "compress/zlib"
    "fmt"
    "io"
    "os"
)

var (
    flateReaderFn = func(r io.Reader) (io.ReadCloser, error) { return flate.NewReader(r), nil }
    zlibReaderFn  = func(r io.Reader) (io.ReadCloser, error) { return zlib.NewReader(r) }
)

func deflate(b []byte, skip, length int, newReader func(io.Reader) (io.ReadCloser, error)) {
    // rfc-1950
    // --------
    //   First 2 bytes
    //   [120, 1] - CMF, FLG
    //
    //   CMF: 120
    //     0111 1000
    //     ↑    ↑
    //     |    CM(8) = deflate compression method
    //     CINFO(7)   = 32k LZ77 window size
    //
    //   FLG: 1
    //     0001 ← FCHECK
    //            (CMF*256 + FLG) % 31 == 0
    //             120 * 256 + 1 = 30721
    //                             30721 % 31 == 0

    stream := bytes.NewReader(b[skip:length])
    r, err := newReader(stream)
    if err != nil {
        fmt.Println("\nfailed to create reader,", err)
        return
    }

    n, err := io.Copy(os.Stdout, r)
    if err != nil {
        if n > 0 {
            fmt.Print("\n")
        }
        fmt.Println("\nfailed to write contents from reader,", err)
        return
    }
    fmt.Printf("%d bytes written\n", n)
    r.Close()
}

func main() {
    //readerFn, skip := flateReaderFn, 2 // compress/flate RFC-1951, ignore first 2 bytes
    readerFn, skip := zlibReaderFn, 0 // compress/zlib RFC-1950, ignore nothing

    //                                                                                                ⤹ This is where the error occurs: `flate: corrupt input before offset 19`.
    stream1 := []byte{120, 1, 43, 84, 8, 84, 40, 84, 48, 0, 66, 11, 32, 44, 74, 85, 8, 87, 195, 136, 83, 48, 195, 144, 51, 55, 194, 177, 52, 48, 50, 86, 40, 78, 70, 194, 150, 74, 83, 8, 4, 0, 195, 190, 194, 182, 10, 194, 171, 10}
    stream2 := []byte{120, 1, 43, 84, 8, 4, 0, 1, 195, 167, 0, 195, 163, 10}

    fmt.Println("----------------------------------------\nStream 1:")
    deflate(stream1, skip, 42, readerFn) // flate: corrupt input before offset 19

    fmt.Println("----------------------------------------\nStream 2:")
    deflate(stream2, skip, 11, readerFn) // invalid checksum
}

我肯定我在哪里做错了什么,我只是看不出来。

( pdf可在观看者中开启)

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-02-21 20:23:49

二进制数据不应从文本编辑器中复制/保存。可能会出现这样的情况,它只会增加火焰中的油。

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

提示#1:

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

提示2:

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

票数 4
EN

Stack Overflow用户

发布于 2017-02-21 18:34:42

好吧,忏悔时间..。

我完全忽略了一个事实,即Vim没有将流内容正确地保存到新的文件中。因此,我花了相当长的时间阅读RFC,并深入研究Go compress/...包的内部结构,假设问题出在我的代码上。

在我贴出问题后不久,我试着把PDF作为一个整体来阅读,找到stream/endstream的位置,并推动它通过泄气。一看到内容在屏幕上滚动,我就意识到自己犯了一个愚蠢的错误。

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

这是一个很好的结果,因为我对整个过程的理解比第一次开始的时候要好得多。

票数 2
EN

Stack Overflow用户

发布于 2021-04-17 16:11:11

从PDF中提取对象可能很棘手,这取决于所使用的过滤器。过滤器还可以有需要正确处理的附加选项。

对于有兴趣提取对象而不考虑流程的低级细节的人来说。

要从PDF中获取单个对象并对其进行解码,可以这样做:

代码语言:javascript
复制
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
}

一个完整的例子:object.go

:我是UniPDF的最初开发人员。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42355485

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档