-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfilter_test.go
More file actions
179 lines (164 loc) · 5.46 KB
/
Copy pathfilter_test.go
File metadata and controls
179 lines (164 loc) · 5.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package pdfdisassembler
import (
"bytes"
"compress/lzw"
"compress/zlib"
"encoding/ascii85"
"fmt"
"testing"
)
// buildStreamObjectPDF wraps stream as indirect object 3 with the given stream
// dict entries (e.g. "/Filter /LZWDecode ..."), reachable via a classical xref.
func buildStreamObjectPDF(t *testing.T, dictEntries string, stream []byte) []byte {
t.Helper()
var buf bytes.Buffer
off := func() int { return buf.Len() }
buf.WriteString("%PDF-1.7\n%\xe2\xe3\xcf\xd3\n")
offsets := make([]int, 4)
offsets[1] = off()
buf.WriteString("1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n")
offsets[2] = off()
buf.WriteString("2 0 obj\n<< /Type /Pages /Count 0 /Kids [] >>\nendobj\n")
offsets[3] = off()
fmt.Fprintf(&buf, "3 0 obj\n<< %s /Length %d >>\nstream\n", dictEntries, len(stream))
buf.Write(stream)
buf.WriteString("\nendstream\nendobj\n")
xrefOff := off()
buf.WriteString("xref\n0 4\n")
fmt.Fprintf(&buf, "%010d %05d f \n", 0, 65535)
for i := 1; i <= 3; i++ {
fmt.Fprintf(&buf, "%010d %05d n \n", offsets[i], 0)
}
buf.WriteString("trailer\n<< /Size 4 /Root 1 0 R >>\n")
fmt.Fprintf(&buf, "startxref\n%d\n%%%%EOF\n", xrefOff)
return buf.Bytes()
}
func streamObject3(t *testing.T, data []byte) []byte {
t.Helper()
r, err := Open(bytes.NewReader(data))
if err != nil {
t.Fatalf("Open: %v", err)
}
t.Cleanup(func() { r.Close() })
v, err := r.Resolve(Reference{Number: 3, Generation: 0})
if err != nil {
t.Fatalf("Resolve: %v", err)
}
stm, ok := v.(*Stream)
if !ok {
t.Fatalf("object 3 is %T, want *Stream", v)
}
got, err := stm.Content()
if err != nil {
t.Fatalf("Content: %v", err)
}
return got
}
// A stream declaring /DecodeParms << /EarlyChange 0 >> must decode with early
// change off. The stdlib LZW writer emits the non-early convention, so honouring
// the parameter reproduces the input; ignoring it garbles a stream past the
// first code-width boundary.
func TestLZWStreamEarlyChangeZero(t *testing.T) {
orig := make([]byte, 4096)
x := uint32(99)
for i := range orig {
x = x*1664525 + 1013904223
orig[i] = byte(x >> 24)
}
var enc bytes.Buffer
w := lzw.NewWriter(&enc, lzw.MSB, 8)
w.Write(orig)
w.Close()
got := streamObject3(t, buildStreamObjectPDF(t,
"/Filter /LZWDecode /DecodeParms << /EarlyChange 0 >>", enc.Bytes()))
if !bytes.Equal(got, orig) {
t.Fatal("LZW stream with /EarlyChange 0 decoded incorrectly")
}
}
// A /Filter array applies filters in order: the raw bytes are ASCII85 wrapping a
// FlateDecode stream, so the chain must un-ASCII85 then inflate.
func TestStreamFilterChainArray(t *testing.T) {
orig := []byte("chained filters: ASCII85 over Flate over the original bytes")
var fl bytes.Buffer
zw := zlib.NewWriter(&fl)
zw.Write(orig)
zw.Close()
a85 := make([]byte, ascii85.MaxEncodedLen(fl.Len()))
n := ascii85.Encode(a85, fl.Bytes())
stream := append(a85[:n:n], '~', '>')
got := streamObject3(t, buildStreamObjectPDF(t,
"/Filter [ /ASCII85Decode /FlateDecode ]", stream))
if !bytes.Equal(got, orig) {
t.Fatalf("chained decode = %q, want %q", got, orig)
}
}
func TestParamsFromDict(t *testing.T) {
d := newDict(nil)
d.set("Predictor", Integer(12))
d.set("Columns", Integer(5))
d.set("Colors", Integer(3))
d.set("BitsPerComponent", Integer(16))
d.set("EarlyChange", Integer(0))
p := paramsFromDict(d)
if p.Predictor != 12 || p.Columns != 5 || p.Colors != 3 || p.BitsPerComponent != 16 {
t.Errorf("predictor params = %+v, want Predictor=12 Columns=5 Colors=3 BitsPerComponent=16", p)
}
if !p.NoEarlyChange {
t.Error("/EarlyChange 0 must set NoEarlyChange")
}
// 1 is the LZW default, so it must NOT set NoEarlyChange.
d1 := newDict(nil)
d1.set("EarlyChange", Integer(1))
if paramsFromDict(d1).NoEarlyChange {
t.Error("/EarlyChange 1 must not set NoEarlyChange")
}
empty := paramsFromDict(newDict(nil))
if empty.Predictor != 0 || empty.Columns != 0 || empty.Colors != 0 ||
empty.BitsPerComponent != 0 || empty.NoEarlyChange {
t.Errorf("empty dict = %+v, want zero Params", empty)
}
}
// /F and /DP are the /Filter and /DecodeParms abbreviations; a null /DP entry
// leaves that filter with default params.
func TestStreamFilterChainAbbreviations(t *testing.T) {
orig := []byte("abbreviated filter keys decode the same")
var fl bytes.Buffer
zw := zlib.NewWriter(&fl)
zw.Write(orig)
zw.Close()
a85 := make([]byte, ascii85.MaxEncodedLen(fl.Len()))
n := ascii85.Encode(a85, fl.Bytes())
stream := append(a85[:n:n], '~', '>')
got := streamObject3(t, buildStreamObjectPDF(t,
"/F [ /ASCII85Decode /FlateDecode ] /DP [ null null ]", stream))
if !bytes.Equal(got, orig) {
t.Fatalf("abbreviated /F+/DP decode = %q, want %q", got, orig)
}
}
// A malformed or unsupported filter chain must surface an error from Content(),
// not panic.
func TestStreamFilterChainErrors(t *testing.T) {
cases := map[string]string{
"filter_wrong_type": "/Filter 42",
"filter_array_bad_entry": "/Filter [ /FlateDecode 42 ]",
"image_only_filter": "/Filter /DCTDecode",
"undecodable_data": "/Filter /FlateDecode",
}
for name, dictEntries := range cases {
t.Run(name, func(t *testing.T) {
data := buildStreamObjectPDF(t, dictEntries, []byte("not valid filtered data"))
r, err := Open(bytes.NewReader(data))
if err != nil {
t.Fatalf("Open: %v", err)
}
defer r.Close()
v, err := r.Resolve(Reference{Number: 3, Generation: 0})
if err != nil {
t.Fatalf("Resolve: %v", err)
}
if _, err := v.(*Stream).Content(); err == nil {
t.Fatal("expected a Content() error, got nil")
}
})
}
}