如何 decompress/deflate PDF 流
How to decompress/deflate PDF Stream
使用 2016-W4 pdf
,它有 2 个大流 (第 1 和第 2 页),以及一堆其他对象和较小的流。我正在尝试缩小流,以处理源数据,但我很挣扎。我只能得到损坏的输入和无效的校验和错误。
我已经编写了一个测试脚本来帮助调试,并从文件中提取了较小的流来进行测试。
这里有来自原始 pdf 的 2 个流,以及它们的长度对象:
流 1:
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
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
之后和 [=17= 之前的回车 returns ]).
我都试过了:
我已将以下内容的流转换为 []byte
:
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 确实会在查看器中打开)
好了,表白时间...
我太专注于理解 deflate 以至于我完全忽略了 Vim 没有将流内容正确保存到新文件中这一事实。所以我花了很多时间阅读 RFC,并深入了解 Go compress/...
包的内部结构,假设问题出在我的代码上。
在我发布问题后不久,我尝试阅读整个 PDF,找到 stream
/endstream
位置,然后通过 deflate 推送它。当我看到内容在屏幕上滚动时,我意识到我犯了一个愚蠢的错误。
+1 @icza,这正是我的问题。
结果很好,因为我对整个过程有了更好的理解,而不是第一次绕圈就成功了。
二进制数据应该永远不会从文本编辑器中复制/保存。可能会有成功的情况,只是火上浇油
您最终从 PDF "mined out" 获得的数据很可能与 在 PDF 中的实际数据不同。您应该从十六进制编辑器中获取数据(例如尝试 hecate 获取新的东西),或者编写一个简单的应用程序来保存它(严格将文件作为二进制文件处理)。
提示 #1:
显示的二进制数据分布在多行中。二进制数据不包含回车returns,那是一个文本控件。如果是,那意味着编辑器 did 将其解释为文本,因此一些代码/字符 "consumed" 开始新行。多个序列可能被解释为相同的换行符(例如 \n
、\r\n
)。通过排除它们,您已经处于数据丢失状态,通过包括它们,您可能已经有了不同的序列。并且如果将数据解释并显示为文本,则可能会出现更多问题,因为控制字符较多,并且显示时可能不会显示某些字符。
提示#2:
当使用flateReaderFn
时,解码第二个例子成功(无错误完成)。这意味着 "you were barking up the right tree",但成功取决于实际数据是什么以及文本编辑器在多大程度上 "distorted"。
根据使用的过滤器,从 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的原始开发者。
使用 2016-W4 pdf
,它有 2 个大流 (第 1 和第 2 页),以及一堆其他对象和较小的流。我正在尝试缩小流,以处理源数据,但我很挣扎。我只能得到损坏的输入和无效的校验和错误。
我已经编写了一个测试脚本来帮助调试,并从文件中提取了较小的流来进行测试。
这里有来自原始 pdf 的 2 个流,以及它们的长度对象:
流 1:
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
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
之后和 [=17= 之前的回车 returns ]).
我都试过了:
我已将以下内容的流转换为 []byte
:
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 确实会在查看器中打开)
好了,表白时间...
我太专注于理解 deflate 以至于我完全忽略了 Vim 没有将流内容正确保存到新文件中这一事实。所以我花了很多时间阅读 RFC,并深入了解 Go compress/...
包的内部结构,假设问题出在我的代码上。
在我发布问题后不久,我尝试阅读整个 PDF,找到 stream
/endstream
位置,然后通过 deflate 推送它。当我看到内容在屏幕上滚动时,我意识到我犯了一个愚蠢的错误。
+1 @icza,这正是我的问题。
结果很好,因为我对整个过程有了更好的理解,而不是第一次绕圈就成功了。
二进制数据应该永远不会从文本编辑器中复制/保存。可能会有成功的情况,只是火上浇油
您最终从 PDF "mined out" 获得的数据很可能与 在 PDF 中的实际数据不同。您应该从十六进制编辑器中获取数据(例如尝试 hecate 获取新的东西),或者编写一个简单的应用程序来保存它(严格将文件作为二进制文件处理)。
提示 #1:
显示的二进制数据分布在多行中。二进制数据不包含回车returns,那是一个文本控件。如果是,那意味着编辑器 did 将其解释为文本,因此一些代码/字符 "consumed" 开始新行。多个序列可能被解释为相同的换行符(例如 \n
、\r\n
)。通过排除它们,您已经处于数据丢失状态,通过包括它们,您可能已经有了不同的序列。并且如果将数据解释并显示为文本,则可能会出现更多问题,因为控制字符较多,并且显示时可能不会显示某些字符。
提示#2:
当使用flateReaderFn
时,解码第二个例子成功(无错误完成)。这意味着 "you were barking up the right tree",但成功取决于实际数据是什么以及文本编辑器在多大程度上 "distorted"。
根据使用的过滤器,从 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的原始开发者。