initial commit
This commit is contained in:
+803
@@ -0,0 +1,803 @@
|
||||
package pdf
|
||||
|
||||
import "core:mem"
|
||||
|
||||
// Stream decoding: the /Filter chain, the /DecodeParms that go with it, and
|
||||
// the two predictors (ISO 32000-2, 7.4).
|
||||
//
|
||||
// The codecs themselves live in filter_*.odin. This file is only the plumbing:
|
||||
// which filters a stream asks for, in what order, with which parameters, and
|
||||
// what to do when one of them cannot deliver.
|
||||
//
|
||||
// The governing rule is that a stream which will not decode is an *empty*
|
||||
// stream, not a failed document. A page whose one broken image will not
|
||||
// inflate should still draw its text, so `stream_data` records a repair and
|
||||
// hands back nothing rather than returning an error every caller would have to
|
||||
// ignore. `Stream.state` is where the difference between "decoded to nothing"
|
||||
// and "would not decode" is kept.
|
||||
|
||||
// A chain longer than this is not a document, it is an attempt to make a
|
||||
// reader do work. Real files use one filter; two (an ASCII armour over a
|
||||
// compressor) is the most that occurs in practice.
|
||||
@(private)
|
||||
MAX_FILTER_CHAIN :: 16
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Filter names
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@(private)
|
||||
Filter_Kind :: enum u8 {
|
||||
Unknown = 0,
|
||||
Ascii_Hex,
|
||||
Ascii85,
|
||||
Lzw,
|
||||
Flate,
|
||||
Run_Length,
|
||||
Ccitt,
|
||||
Dct,
|
||||
Jpx,
|
||||
Jbig2,
|
||||
// /Crypt is a marker, not a transformation: decryption has already
|
||||
// happened by the time a stream reaches this file.
|
||||
Crypt,
|
||||
}
|
||||
|
||||
// filter_kind classifies a filter name.
|
||||
//
|
||||
// The abbreviations are the ones ISO 32000-2 table 93 defines for inline
|
||||
// images. They are accepted on ordinary streams too: a producer that emits
|
||||
// /Fl outside an inline image is wrong, but the intent is not in doubt and no
|
||||
// other filter name could be meant.
|
||||
@(private)
|
||||
filter_kind :: proc(name: string) -> Filter_Kind {
|
||||
switch name {
|
||||
case "ASCIIHexDecode", "AHx": return .Ascii_Hex
|
||||
case "ASCII85Decode", "A85": return .Ascii85
|
||||
case "LZWDecode", "LZW": return .Lzw
|
||||
case "FlateDecode", "Fl": return .Flate
|
||||
case "RunLengthDecode", "RL": return .Run_Length
|
||||
case "CCITTFaxDecode", "CCF": return .Ccitt
|
||||
case "DCTDecode", "DCT": return .Dct
|
||||
case "JPXDecode": return .Jpx
|
||||
case "JBIG2Decode": return .Jbig2
|
||||
case "Crypt": return .Crypt
|
||||
}
|
||||
return .Unknown
|
||||
}
|
||||
|
||||
// filter_name returns the unabbreviated spelling, so callers downstream of
|
||||
// `stream_image_data` can compare against one name instead of two.
|
||||
@(private)
|
||||
filter_name :: proc(kind: Filter_Kind, fallback: string) -> string {
|
||||
switch kind {
|
||||
case .Ascii_Hex: return "ASCIIHexDecode"
|
||||
case .Ascii85: return "ASCII85Decode"
|
||||
case .Lzw: return "LZWDecode"
|
||||
case .Flate: return "FlateDecode"
|
||||
case .Run_Length: return "RunLengthDecode"
|
||||
case .Ccitt: return "CCITTFaxDecode"
|
||||
case .Dct: return "DCTDecode"
|
||||
case .Jpx: return "JPXDecode"
|
||||
case .Jbig2: return "JBIG2Decode"
|
||||
case .Crypt: return "Crypt"
|
||||
case .Unknown: return fallback
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
// filter_is_image reports whether a filter produces an image rather than a
|
||||
// byte stream. These four are the ones `stream_image_data` stops at: decoding
|
||||
// them needs the image dictionary (dimensions, colour space, /Decode), and
|
||||
// their output is samples, not bytes a content parser could read.
|
||||
@(private)
|
||||
filter_is_image :: proc(kind: Filter_Kind) -> bool {
|
||||
#partial switch kind {
|
||||
case .Dct, .Jpx, .Jbig2, .Ccitt: return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// filter_takes_parms reports whether a filter reads /DecodeParms at all. Used
|
||||
// to place a parameter dictionary that a producer wrote where an array was
|
||||
// required; see `filter_attach_parms`.
|
||||
@(private)
|
||||
filter_takes_parms :: proc(kind: Filter_Kind) -> bool {
|
||||
#partial switch kind {
|
||||
case .Lzw, .Flate, .Ccitt, .Jbig2, .Crypt: return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The chain
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@(private)
|
||||
Filter_Step :: struct {
|
||||
name: string,
|
||||
kind: Filter_Kind,
|
||||
parms: ^Dict,
|
||||
// Position in the /Filter array, which is what /DecodeParms is indexed by.
|
||||
// Not the same as the step's own index once a malformed entry is dropped.
|
||||
slot: int,
|
||||
}
|
||||
|
||||
// filter_chain fills `steps` with the stream's filters in application order.
|
||||
//
|
||||
// `ok` is false only when the file asks for more filters than
|
||||
// MAX_FILTER_CHAIN; every other malformation is absorbed. A missing /Filter
|
||||
// leaves n == 0 and ok == true.
|
||||
@(private)
|
||||
filter_chain :: proc(doc: ^Document, d: ^Dict, steps: []Filter_Step) -> (n: int, ok: bool) {
|
||||
if d == nil || len(steps) == 0 {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
// Inline images abbreviate both keys (ISO 32000-2, table 91). On an
|
||||
// ordinary stream dictionary /F is an external file specification instead,
|
||||
// but that is a string or a dictionary and neither survives the name test
|
||||
// below, so looking is free.
|
||||
filters := dict_get_alt(doc, d, "Filter", "F")
|
||||
parms := dict_get_alt(doc, d, "DecodeParms", "DP")
|
||||
|
||||
#partial switch f in filters {
|
||||
case Name:
|
||||
steps[0] = Filter_Step{name = string(f), kind = filter_kind(string(f)), slot = 0}
|
||||
n = 1
|
||||
case ^Array:
|
||||
count := array_len(f)
|
||||
if count > len(steps) {
|
||||
return 0, false
|
||||
}
|
||||
for i in 0 ..< count {
|
||||
name, is_name := to_name(array_get(doc, f, i))
|
||||
if !is_name {
|
||||
// A null or a stray number inside /Filter. Dropping the entry
|
||||
// keeps the rest of the chain usable; treating it as a filter
|
||||
// would lose the stream over one bad element.
|
||||
continue
|
||||
}
|
||||
steps[n] = Filter_Step{name = name, kind = filter_kind(name), slot = i}
|
||||
n += 1
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
#partial switch p in parms {
|
||||
case ^Array:
|
||||
// Positional, and a short or over-long array is not fatal: a missing
|
||||
// or null entry simply means "no parameters for that filter".
|
||||
for i in 0 ..< n {
|
||||
steps[i].parms = array_dict(doc, p, steps[i].slot)
|
||||
}
|
||||
case ^Dict:
|
||||
filter_attach_parms(steps[:n], p)
|
||||
case ^Stream:
|
||||
// Never legal, but `to_dict` accepts a stream where a dictionary is
|
||||
// wanted and a file that points /DecodeParms at a stream object still
|
||||
// means its dictionary.
|
||||
filter_attach_parms(steps[:n], p.dict)
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
@(private)
|
||||
filter_attach_parms :: proc(steps: []Filter_Step, p: ^Dict) {
|
||||
if len(steps) == 1 {
|
||||
steps[0].parms = p
|
||||
return
|
||||
}
|
||||
// ISO 32000-2, 7.4.1 requires an array of dictionaries when /Filter is an
|
||||
// array, but producers write a bare dictionary for chains such as
|
||||
// [/ASCII85Decode /FlateDecode]. It can only have been meant for whichever
|
||||
// filter reads parameters at all, so give it to the first one that does.
|
||||
for i in 0 ..< len(steps) {
|
||||
if filter_takes_parms(steps[i].kind) {
|
||||
steps[i].parms = p
|
||||
return
|
||||
}
|
||||
}
|
||||
steps[0].parms = p
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Decode parameters
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@(private)
|
||||
Predictor_Parms :: struct {
|
||||
predictor: int,
|
||||
colors: int,
|
||||
bpc: int,
|
||||
columns: int,
|
||||
}
|
||||
|
||||
// filter_predictor_parms reads the four predictor entries of ISO 32000-2
|
||||
// table 8. Their defaults are what an absent /DecodeParms means, so a nil
|
||||
// dictionary is the ordinary case rather than an error.
|
||||
@(private)
|
||||
filter_predictor_parms :: proc(doc: ^Document, parms: ^Dict) -> Predictor_Parms {
|
||||
return Predictor_Parms{
|
||||
predictor = int(dict_int(doc, parms, "Predictor", 1)),
|
||||
colors = int(dict_int(doc, parms, "Colors", 1)),
|
||||
bpc = int(dict_int(doc, parms, "BitsPerComponent", 8)),
|
||||
columns = int(dict_int(doc, parms, "Columns", 1)),
|
||||
}
|
||||
}
|
||||
|
||||
// filter_ccitt_params reads the CCITTFaxDecode parameters of ISO 32000-2
|
||||
// table 12. Exposed to the image layer, which gets the raw parameter
|
||||
// dictionary back from `stream_image_data` and has to build the same struct.
|
||||
@(private)
|
||||
filter_ccitt_params :: proc(doc: ^Document, parms: ^Dict) -> Ccitt_Params {
|
||||
p := ccitt_default_params()
|
||||
p.k = int(dict_int(doc, parms, "K", 0))
|
||||
p.columns = int(dict_int(doc, parms, "Columns", 1728))
|
||||
p.rows = int(dict_int(doc, parms, "Rows", 0))
|
||||
p.black_is_1 = dict_bool(doc, parms, "BlackIs1", false)
|
||||
p.byte_align = dict_bool(doc, parms, "EncodedByteAlign", false)
|
||||
p.end_of_line = dict_bool(doc, parms, "EndOfLine", false)
|
||||
p.end_of_block = dict_bool(doc, parms, "EndOfBlock", true)
|
||||
return p
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// One filter
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// apply_filter runs a single filter over `data`.
|
||||
//
|
||||
// `name` may be the full name or the inline-image abbreviation. `parms` is the
|
||||
// matching /DecodeParms entry and may be nil, which means "all defaults".
|
||||
//
|
||||
// Ownership: when `err` is .None the result is allocated with `allocator` and
|
||||
// belongs to the caller. When it is not, the result is either nil or `data`
|
||||
// itself and must not be freed -- the pass-through errors (.Unknown_Filter,
|
||||
// .Unsupported_Image, .Unsupported_Jpx) hand the bytes back untouched so a
|
||||
// caller that knows better can still do something with them.
|
||||
//
|
||||
// A codec that fails part-way through returns .None with a short result, which
|
||||
// is deliberate: half a content stream draws half a page, and that beats a
|
||||
// blank one. Compare the length against what you expected if you need to know.
|
||||
//
|
||||
// This is also the allocator boundary, and that is the part worth reading
|
||||
// twice. Every codec here grows a buffer it cannot size in advance, and the
|
||||
// tolerant ones -- flate above all -- decode the same bytes several ways and
|
||||
// keep the best attempt, dropping the losers with `delete`. Both habits are
|
||||
// harmless against the heap and ruinous against `doc.allocator`: a
|
||||
// `core:mem/virtual` arena answers .Free with .Mode_Not_Implemented, so
|
||||
// `delete` on arena memory is a silent no-op rather than an error, and every
|
||||
// rejected attempt and every superseded growth buffer stays until `close`.
|
||||
// A damaged 256 KiB flate stream can strand megabytes that way, and the frees
|
||||
// that would appear to clean it up are the ones ARCHITECTURE.md forbids.
|
||||
//
|
||||
// So a codec never sees the caller's allocator. It speculates in scratch,
|
||||
// where its own `delete` calls do what they say, and exactly one buffer -- the
|
||||
// winner, at its final length -- is copied into `allocator` afterwards. The
|
||||
// caller's arena grows by the size of the output, not by the number of
|
||||
// attempts it took to find it, and a codec added later inherits that without
|
||||
// having to know about it.
|
||||
apply_filter :: proc(doc: ^Document, name: string, data: []byte, parms: ^Dict,
|
||||
allocator := context.allocator) -> (out: []byte, err: Error) {
|
||||
scratch := context.allocator
|
||||
if allocator == scratch {
|
||||
// The caller already asked for the allocator the codec would have
|
||||
// speculated in, so copying the result to itself would only double the
|
||||
// peak for nothing.
|
||||
return apply_filter_decode(doc, name, data, parms, scratch)
|
||||
}
|
||||
|
||||
tmp, terr := apply_filter_decode(doc, name, data, parms, scratch)
|
||||
if terr != .None {
|
||||
// Per the ownership rule above, a failed filter hands back nil or
|
||||
// `data` itself. Neither is ours to copy or to free.
|
||||
return tmp, terr
|
||||
}
|
||||
defer delete(tmp, scratch)
|
||||
return filter_copy(tmp, allocator)
|
||||
}
|
||||
|
||||
// apply_filter_decode is the dispatch proper. `scratch` is throw-away memory
|
||||
// that `apply_filter` frees on the way out, so a codec reached from here may
|
||||
// allocate and free as freely as it likes; nothing it leaves behind outlives
|
||||
// the call. Do not call this with a Document arena -- that is the whole point
|
||||
// of it being separate.
|
||||
@(private)
|
||||
apply_filter_decode :: proc(doc: ^Document, name: string, data: []byte, parms: ^Dict,
|
||||
scratch: mem.Allocator) -> (out: []byte, err: Error) {
|
||||
switch filter_kind(name) {
|
||||
case .Ascii_Hex:
|
||||
return ascii_hex_decode(data, scratch)
|
||||
|
||||
case .Ascii85:
|
||||
return ascii85_decode(data, scratch)
|
||||
|
||||
case .Run_Length:
|
||||
return run_length_decode(data, scratch)
|
||||
|
||||
case .Flate:
|
||||
pp := filter_predictor_parms(doc, parms)
|
||||
if pp.predictor <= 1 {
|
||||
return flate_decode(data, scratch)
|
||||
}
|
||||
// The un-predicted bytes are an intermediate: freed here rather than
|
||||
// carried out, so a predicted stream costs one buffer and not two.
|
||||
raw, ferr := flate_decode(data, scratch)
|
||||
defer delete(raw, scratch)
|
||||
if ferr != .None {
|
||||
return nil, ferr
|
||||
}
|
||||
return apply_predictor(raw, pp.predictor, pp.colors, pp.bpc, pp.columns, scratch)
|
||||
|
||||
case .Lzw:
|
||||
// /EarlyChange defaults to 1 (ISO 32000-2, table 8).
|
||||
early := dict_int(doc, parms, "EarlyChange", 1) != 0
|
||||
pp := filter_predictor_parms(doc, parms)
|
||||
if pp.predictor <= 1 {
|
||||
return lzw_decode(data, early, scratch)
|
||||
}
|
||||
raw, ferr := lzw_decode(data, early, scratch)
|
||||
defer delete(raw, scratch)
|
||||
if ferr != .None {
|
||||
return nil, ferr
|
||||
}
|
||||
return apply_predictor(raw, pp.predictor, pp.colors, pp.bpc, pp.columns, scratch)
|
||||
|
||||
case .Ccitt:
|
||||
return ccitt_decode(data, filter_ccitt_params(doc, parms), scratch)
|
||||
|
||||
case .Crypt:
|
||||
// Decryption is done at parse time, by the time any stream reaches
|
||||
// here, so /Crypt is the identity. ISO 32000-2, 7.4.10.
|
||||
return filter_copy(data, scratch)
|
||||
|
||||
case .Dct, .Jbig2:
|
||||
// Decoded by the image layer, which needs the image dictionary to make
|
||||
// sense of the samples. `stream_image_data` is the way in.
|
||||
return data, .Unsupported_Image
|
||||
|
||||
case .Jpx:
|
||||
return data, .Unsupported_Jpx
|
||||
|
||||
case .Unknown:
|
||||
return data, .Unknown_Filter
|
||||
}
|
||||
return data, .Unknown_Filter
|
||||
}
|
||||
|
||||
@(private)
|
||||
filter_copy :: proc(data: []byte, allocator: mem.Allocator) -> (out: []byte, err: Error) {
|
||||
if len(data) == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
buf, aerr := make([]byte, len(data), allocator)
|
||||
if aerr != nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
copy(buf, data)
|
||||
return buf, .None
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Whole streams
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// stream_data decodes a stream's whole filter chain and caches the result.
|
||||
//
|
||||
// The bytes belong to the Document (they are allocated in its arena, or are a
|
||||
// sub-slice of the file when the stream has no filters) and must not be freed.
|
||||
// Calling this twice returns the same slice without decoding again.
|
||||
//
|
||||
// A stream that will not decode is reported as empty, with a .Bad_Filter_Data
|
||||
// repair on the Document and `Stream.state` left at .Failed; the error return
|
||||
// is reserved for a caller mistake. This is the difference between a reader
|
||||
// and a validator: one broken image must not cost a page.
|
||||
//
|
||||
// With a nil Document there is no arena to allocate from, so the cached bytes
|
||||
// come from the context allocator and belong to the caller -- except for a
|
||||
// stream with no filters, which still borrows the file. `Stream.raw` says
|
||||
// which of the two you have.
|
||||
stream_data :: proc(doc: ^Document, s: ^Stream) -> (data: []byte, err: Error) {
|
||||
if s == nil {
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
switch s.state {
|
||||
case .Decoded:
|
||||
return s.decoded, .None
|
||||
case .Failed:
|
||||
return nil, .None
|
||||
case .Pending:
|
||||
}
|
||||
|
||||
allocator := context.allocator
|
||||
if doc != nil {
|
||||
allocator = doc.allocator
|
||||
}
|
||||
|
||||
// An empty payload decodes to nothing whatever the chain says. Running
|
||||
// flate over it would only manufacture a repair for a stream that is
|
||||
// legitimately empty -- an empty content stream is written this way.
|
||||
if len(s.raw) == 0 {
|
||||
s.decoded = nil
|
||||
s.state = .Decoded
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
steps: [MAX_FILTER_CHAIN]Filter_Step
|
||||
n, chain_ok := filter_chain(doc, s.dict, steps[:])
|
||||
if !chain_ok {
|
||||
return stream_failed(doc, s)
|
||||
}
|
||||
|
||||
// Only the last stage's output is worth keeping, so the ones before it are
|
||||
// scratch: each is freed as soon as the next stage has read it, and the
|
||||
// survivor is copied into `allocator` at the end. Giving `allocator` every
|
||||
// stage instead would strand the intermediates of an /ASCII85Decode over a
|
||||
// /FlateDecode in the Document arena for the life of the document -- the
|
||||
// arena cannot free them and ARCHITECTURE.md forbids asking it to -- and
|
||||
// with no Document at all it would leak them outright.
|
||||
scratch := context.allocator
|
||||
cur := s.raw
|
||||
tmp: []byte
|
||||
filtered := false
|
||||
defer delete(tmp, scratch)
|
||||
|
||||
for i in 0 ..< n {
|
||||
if steps[i].kind == .Crypt {
|
||||
// Identity here, and skipping it avoids copying the payload for
|
||||
// nothing.
|
||||
continue
|
||||
}
|
||||
next, ferr := apply_filter(doc, steps[i].name, cur, steps[i].parms, scratch)
|
||||
if ferr != .None {
|
||||
return stream_failed(doc, s)
|
||||
}
|
||||
delete(tmp, scratch)
|
||||
tmp = next
|
||||
cur = next
|
||||
filtered = true
|
||||
}
|
||||
|
||||
if !filtered {
|
||||
// No /Filter at all, or a chain of nothing but /Crypt, which was
|
||||
// already applied at parse time. The payload is still a sub-slice of
|
||||
// the file, so there is nothing to copy and nothing to own -- and a
|
||||
// caller with a nil Document must not free what it gets back here.
|
||||
s.decoded = s.raw
|
||||
s.state = .Decoded
|
||||
return s.decoded, .None
|
||||
}
|
||||
|
||||
out := cur
|
||||
if allocator == scratch {
|
||||
// Where the two are the same allocator -- a nil Document -- the last
|
||||
// stage already produced the buffer the caller keeps. Hand ownership
|
||||
// over instead of copying it and freeing the original.
|
||||
tmp = nil
|
||||
} else {
|
||||
cerr: Error
|
||||
out, cerr = filter_copy(cur, allocator)
|
||||
if cerr != .None {
|
||||
return stream_failed(doc, s)
|
||||
}
|
||||
}
|
||||
|
||||
s.decoded = out
|
||||
s.state = .Decoded
|
||||
return out, .None
|
||||
}
|
||||
|
||||
@(private)
|
||||
stream_failed :: proc(doc: ^Document, s: ^Stream) -> (data: []byte, err: Error) {
|
||||
add_repair(doc, .Bad_Filter_Data, 0, s.ref)
|
||||
s.decoded = nil
|
||||
s.state = .Failed
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
// stream_data_raw returns the stream's bytes exactly as stored -- still
|
||||
// filtered, but already decrypted. Borrowed from the Document; do not free.
|
||||
stream_data_raw :: proc(doc: ^Document, s: ^Stream) -> []byte {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.raw
|
||||
}
|
||||
|
||||
// stream_image_data decodes everything up to the stream's image filter.
|
||||
//
|
||||
// An image whose final filter is DCTDecode, JPXDecode, JBIG2Decode or
|
||||
// CCITTFaxDecode is decoded straight to pixels by the raster path, which needs
|
||||
// the image dictionary to do it. So this walks the chain applying only the
|
||||
// byte filters, stops at the first image filter, and returns the bytes that
|
||||
// filter would have been given, its unabbreviated name and its parameters.
|
||||
// `image_filter` is "" when the chain held no image filter, in which case
|
||||
// `data` is the finished sample bytes.
|
||||
//
|
||||
// The result is allocated with `allocator` and belongs to the caller. A JPX or
|
||||
// JBIG2 codestream comes back intact and without an error: deciding what to do
|
||||
// with a format this layer does not decode is the image layer's business.
|
||||
stream_image_data :: proc(doc: ^Document, s: ^Stream, allocator := context.allocator) ->
|
||||
(data: []byte, image_filter: string, parms: ^Dict, err: Error) {
|
||||
if s == nil {
|
||||
return nil, "", nil, .Invalid_Argument
|
||||
}
|
||||
|
||||
steps: [MAX_FILTER_CHAIN]Filter_Step
|
||||
n, chain_ok := filter_chain(doc, s.dict, steps[:])
|
||||
if !chain_ok {
|
||||
add_repair(doc, .Bad_Filter_Data, 0, s.ref)
|
||||
return nil, "", nil, .Unknown_Filter
|
||||
}
|
||||
|
||||
// Intermediates are scratch and are freed as the chain advances; only the
|
||||
// last result is copied into `allocator`. Keeping them out of `allocator`
|
||||
// means this is leak-free even when the caller hands us an arena it never
|
||||
// frees individually.
|
||||
cur := s.raw
|
||||
tmp: []byte
|
||||
defer delete(tmp, context.allocator)
|
||||
|
||||
for i in 0 ..< n {
|
||||
if filter_is_image(steps[i].kind) {
|
||||
out, cerr := filter_copy(cur, allocator)
|
||||
if cerr != .None {
|
||||
return nil, "", nil, cerr
|
||||
}
|
||||
return out, filter_name(steps[i].kind, steps[i].name), steps[i].parms, .None
|
||||
}
|
||||
if steps[i].kind == .Crypt {
|
||||
continue
|
||||
}
|
||||
next, ferr := apply_filter(doc, steps[i].name, cur, steps[i].parms, context.allocator)
|
||||
if ferr != .None {
|
||||
add_repair(doc, .Bad_Filter_Data, 0, s.ref)
|
||||
return nil, "", nil, ferr
|
||||
}
|
||||
delete(tmp, context.allocator)
|
||||
tmp = next
|
||||
cur = next
|
||||
}
|
||||
|
||||
out, cerr := filter_copy(cur, allocator)
|
||||
if cerr != .None {
|
||||
return nil, "", nil, cerr
|
||||
}
|
||||
return out, "", nil, .None
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Predictors
|
||||
//
|
||||
// A predictor is not a filter: it is a reversible transform the encoder ran
|
||||
// over the samples first, so that the compressor behind it sees mostly zeroes.
|
||||
// ISO 32000-2, 7.4.4.4 defines two, both borrowed wholesale -- predictor 2 is
|
||||
// TIFF 6.0's horizontal differencing, and 10..15 are the five PNG line filters
|
||||
// (RFC 2083, 6). The PNG value is the *maximum* filter the encoder used;
|
||||
// the actual filter is the first byte of each row, so all six PNG values
|
||||
// behave identically on decode.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// DeviceN tops out at 32 colorants, and no predictor has ever been applied to
|
||||
// anything wider.
|
||||
@(private)
|
||||
MAX_PREDICTOR_COLORS :: 32
|
||||
@(private)
|
||||
MAX_PREDICTOR_COLUMNS :: 1 << 20
|
||||
// 16 MiB for a single row. An xref stream's rows are a handful of bytes and an
|
||||
// image's are tens of kilobytes; this only exists so a /Columns out of a fuzzer
|
||||
// cannot ask for an allocation the machine will not survive.
|
||||
@(private)
|
||||
MAX_PREDICTOR_ROW_BYTES :: 1 << 24
|
||||
@(private)
|
||||
MAX_PREDICTOR_OUTPUT :: 1 << 30
|
||||
|
||||
// apply_predictor undoes a /Predictor transform.
|
||||
//
|
||||
// `predictor` is the /Predictor value: 0 or 1 for none, 2 for TIFF horizontal
|
||||
// differencing, 10..15 for the PNG line filters. `colors`, `bpc` and `columns`
|
||||
// are /Colors, /BitsPerComponent and /Columns.
|
||||
//
|
||||
// Parameters that no image could have are rejected with .Bad_Predictor rather
|
||||
// than clamped: unlike a damaged payload, a nonsensical /Columns means the
|
||||
// output would be misaligned garbage, and garbage that looks like data is
|
||||
// worse than nothing.
|
||||
//
|
||||
// A final row cut short by a truncated payload is zero-padded and kept.
|
||||
// Damaged xref streams rely on this -- the last entry of a truncated xref
|
||||
// stream is still worth having.
|
||||
//
|
||||
// The result is allocated with `allocator` and belongs to the caller.
|
||||
apply_predictor :: proc(data: []byte, predictor, colors, bpc, columns: int,
|
||||
allocator := context.allocator) -> (out: []byte, err: Error) {
|
||||
png := false
|
||||
switch {
|
||||
case predictor == 0, predictor == 1:
|
||||
// /Predictor 0 is not defined; files that write it mean "none".
|
||||
return filter_copy(data, allocator)
|
||||
case predictor == 2:
|
||||
case predictor >= 10 && predictor <= 15:
|
||||
png = true
|
||||
case:
|
||||
return nil, .Bad_Predictor
|
||||
}
|
||||
|
||||
if colors < 1 || colors > MAX_PREDICTOR_COLORS {
|
||||
return nil, .Bad_Predictor
|
||||
}
|
||||
switch bpc {
|
||||
case 1, 2, 4, 8, 16:
|
||||
case:
|
||||
return nil, .Bad_Predictor
|
||||
}
|
||||
if columns < 1 || columns > MAX_PREDICTOR_COLUMNS {
|
||||
return nil, .Bad_Predictor
|
||||
}
|
||||
|
||||
row_bits := i64(columns) * i64(colors) * i64(bpc)
|
||||
if row_bits > i64(MAX_PREDICTOR_ROW_BYTES) * 8 {
|
||||
return nil, .Bad_Predictor
|
||||
}
|
||||
row_bytes := int((row_bits + 7) / 8)
|
||||
if row_bytes < 1 {
|
||||
return nil, .Bad_Predictor
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
// A PNG row carries a leading filter tag; a TIFF row does not.
|
||||
stride := row_bytes + 1 if png else row_bytes
|
||||
rows := (len(data) + stride - 1) / stride
|
||||
total := rows * row_bytes
|
||||
if total < 1 || total > MAX_PREDICTOR_OUTPUT {
|
||||
return nil, .Bad_Predictor
|
||||
}
|
||||
|
||||
buf, aerr := make([]byte, total, allocator)
|
||||
if aerr != nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
for r in 0 ..< rows {
|
||||
row := buf[r * row_bytes:][:row_bytes]
|
||||
src := r * stride + (1 if png else 0)
|
||||
// `buf` came from make and is zeroed, so a short final row is padded
|
||||
// by simply not copying over it.
|
||||
if src < len(data) {
|
||||
copy(row, data[src:min(src + row_bytes, len(data))])
|
||||
}
|
||||
if png {
|
||||
tag := 0
|
||||
if r * stride < len(data) {
|
||||
tag = int(data[r * stride])
|
||||
}
|
||||
prev: []byte
|
||||
if r > 0 {
|
||||
prev = buf[(r - 1) * row_bytes:][:row_bytes]
|
||||
}
|
||||
predictor_png_row(row, prev, tag, max(1, (colors * bpc + 7) / 8))
|
||||
} else {
|
||||
predictor_tiff_row(row, colors, bpc, columns)
|
||||
}
|
||||
}
|
||||
return buf, .None
|
||||
}
|
||||
|
||||
// predictor_png_row reverses one PNG line filter in place. `prev` is the
|
||||
// already reconstructed line above, or nil on the first line, where the
|
||||
// filters are defined to read zeroes. `bpp` is the byte distance to the pixel
|
||||
// on the left, at least 1 (RFC 2083, 6.1).
|
||||
@(private)
|
||||
predictor_png_row :: proc(row, prev: []byte, tag, bpp: int) {
|
||||
n := len(row)
|
||||
switch tag {
|
||||
case 0: // None
|
||||
case 1: // Sub
|
||||
for i in bpp ..< n {
|
||||
row[i] += row[i - bpp]
|
||||
}
|
||||
case 2: // Up
|
||||
if prev == nil {
|
||||
return
|
||||
}
|
||||
for i in 0 ..< n {
|
||||
row[i] += prev[i]
|
||||
}
|
||||
case 3: // Average
|
||||
for i in 0 ..< n {
|
||||
left := i >= bpp ? int(row[i - bpp]) : 0
|
||||
up := prev != nil ? int(prev[i]) : 0
|
||||
row[i] += byte((left + up) / 2)
|
||||
}
|
||||
case 4: // Paeth
|
||||
for i in 0 ..< n {
|
||||
a := i >= bpp ? int(row[i - bpp]) : 0
|
||||
b := prev != nil ? int(prev[i]) : 0
|
||||
c := prev != nil && i >= bpp ? int(prev[i - bpp]) : 0
|
||||
row[i] += byte(predictor_paeth(a, b, c))
|
||||
}
|
||||
case:
|
||||
// An undefined tag. Encoders do not emit one, but a byte lost from the
|
||||
// front of the payload shifts every tag, and reading the row as
|
||||
// unfiltered keeps the rest of the stream in step instead of failing
|
||||
// the whole stream on one byte.
|
||||
}
|
||||
}
|
||||
|
||||
// predictor_paeth is the predictor of RFC 2083, 6.6, verbatim: of the three
|
||||
// neighbours it picks whichever the linear estimate a+b-c is nearest to,
|
||||
// resolving ties towards a then b.
|
||||
@(private)
|
||||
predictor_paeth :: proc(a, b, c: int) -> int {
|
||||
p := a + b - c
|
||||
pa := abs(p - a)
|
||||
pb := abs(p - b)
|
||||
pc := abs(p - c)
|
||||
if pa <= pb && pa <= pc {
|
||||
return a
|
||||
}
|
||||
if pb <= pc {
|
||||
return b
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// predictor_tiff_row reverses TIFF horizontal differencing in place: every
|
||||
// component is stored as its difference from the component `colors` positions
|
||||
// earlier in the same row, modulo the component's own width.
|
||||
@(private)
|
||||
predictor_tiff_row :: proc(row: []byte, colors, bpc, columns: int) {
|
||||
n := columns * colors
|
||||
switch bpc {
|
||||
case 8:
|
||||
limit := min(n, len(row))
|
||||
for i in colors ..< limit {
|
||||
row[i] += row[i - colors]
|
||||
}
|
||||
case 16:
|
||||
limit := min(n, len(row) / 2)
|
||||
for i in colors ..< limit {
|
||||
prev := u16(row[(i - colors) * 2]) << 8 | u16(row[(i - colors) * 2 + 1])
|
||||
cur := u16(row[i * 2]) << 8 | u16(row[i * 2 + 1])
|
||||
v := cur + prev
|
||||
row[i * 2] = byte(v >> 8)
|
||||
row[i * 2 + 1] = byte(v)
|
||||
}
|
||||
case 1, 2, 4:
|
||||
// Sub-byte components are packed high bit first and never straddle a
|
||||
// byte, since 1, 2 and 4 all divide 8.
|
||||
mask := (1 << uint(bpc)) - 1
|
||||
limit := min(n, len(row) * 8 / bpc)
|
||||
for i in colors ..< limit {
|
||||
v := predictor_bits_get(row, i - colors, bpc) + predictor_bits_get(row, i, bpc)
|
||||
predictor_bits_set(row, i, bpc, v & mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
predictor_bits_get :: proc(row: []byte, index, bpc: int) -> int {
|
||||
bit := index * bpc
|
||||
at := bit >> 3
|
||||
if at < 0 || at >= len(row) {
|
||||
return 0
|
||||
}
|
||||
shift := uint(8 - bpc - (bit & 7))
|
||||
return int(row[at] >> shift) & ((1 << uint(bpc)) - 1)
|
||||
}
|
||||
|
||||
@(private)
|
||||
predictor_bits_set :: proc(row: []byte, index, bpc, value: int) {
|
||||
bit := index * bpc
|
||||
at := bit >> 3
|
||||
if at < 0 || at >= len(row) {
|
||||
return
|
||||
}
|
||||
shift := uint(8 - bpc - (bit & 7))
|
||||
mask := byte(((1 << uint(bpc)) - 1) << shift)
|
||||
row[at] = (row[at] & ~mask) | (byte(value) << shift) & mask
|
||||
}
|
||||
Reference in New Issue
Block a user