Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,20 @@ func (r *renderer) decodeImage(dict reader.Dict, raw []byte, resources reader.Di
return nil
}
if mask, ok := reader.ToBool(resolve(r.doc, dict.Get("ImageMask"))); ok && mask {
// A stencil is one bit a pixel, so the bytes have to be samples. When
// the filter chain stopped at an image format nothing here decodes,
// they are not: they are still compressed, and drawing them paints
// noise through the shape of nothing.
//
// 273 of the image masks in the 1 633 real forms carry an encoded
// filter. 236 of them were faxes, which the reader now decodes; the
// nine that remain are JBIG2, and until something decodes those the
// honest answer is not to draw them. That is the rule the rest of this
// function already follows: "the image is not drawn rather than drawn
// wrong".
if imageFilter != "" {
return nil
}
return r.stencil(dict, data, w, h)
}
var out *sampled
Expand Down
59 changes: 59 additions & 0 deletions image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,62 @@ func TestAnImageUnderAClip(t *testing.T) {
wantBlack(t, img, 5, 10)
wantWhite(t, img, 15, 10)
}

func TestAMaskWhoseBytesAreStillEncodedIsNotDrawn(t *testing.T) {
// A stencil is one bit a pixel, so the bytes have to be samples. A mask
// whose filter chain stopped at an image format nothing here decodes is
// still compressed, and painting it puts noise on the page in the shape of
// nothing at all.
//
// 273 of the image masks in the 1 633 real forms carry an encoded filter.
// 236 were faxes, which the reader decodes as of go-pdfkit/reader#15; the
// nine that remain are JBIG2. Fifty-one first pages of real forms were
// showing this noise before either change.
for _, tc := range []struct {
name string
filter reader.Name
drawn bool
}{
{"a format nothing here decodes", "JBIG2Decode", false},
{"no filter at all", "", true},
} {
t.Run(tc.name, func(t *testing.T) {
w := reader.NewWriter("1.7")
pagesRef := w.Reserve()
dict := reader.Dict{"Type": reader.Name("XObject"),
"Subtype": reader.Name("Image"), "Width": reader.Integer(8),
"Height": reader.Integer(8), "ImageMask": reader.Bool(true),
"BitsPerComponent": reader.Integer(1)}
if tc.filter != "" {
dict["Filter"] = tc.filter
}
// Eight rows of a byte each, every bit zero: as samples that is a
// solid black stencil, so anything drawn at all is visible.
img := w.Add(&reader.Stream{Dict: dict, Raw: make([]byte, 8)})
page := reader.Dict{"Type": reader.Name("Page"), "Parent": pagesRef,
"MediaBox": nums(0, 0, 20, 20),
"Resources": reader.Dict{"XObject": reader.Dict{"M": img}},
"Contents": w.Add(&reader.Stream{Dict: reader.Dict{},
Raw: []byte("0 g q 20 0 0 20 0 0 cm /M Do Q")})}
pageRef := w.Add(page)
w.Put(pagesRef, reader.Dict{"Type": reader.Name("Pages"),
"Kids": reader.Array{pageRef}, "Count": reader.Integer(1)})
out, err := w.Finish(reader.Dict{"Root": w.Add(reader.Dict{
"Type": reader.Name("Catalog"), "Pages": pagesRef})})
if err != nil {
t.Fatal(err)
}
doc, err := reader.Open(out)
if err != nil {
t.Fatal(err)
}
pic, err := Page(doc, 1, Options{Scale: 1})
if err != nil {
t.Fatal(err)
}
if drawn := inked(pic) > 0; drawn != tc.drawn {
t.Errorf("drawn = %v, want %v", drawn, tc.drawn)
}
})
}
}
Loading