initial commit
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
package pdf
|
||||
|
||||
// ASCIIHexDecode and ASCII85Decode: the two filters that exist so a PDF can be
|
||||
// mailed through a 7-bit channel. Both are self-delimiting -- the data ends at
|
||||
// a marker, not at the end of the buffer -- so both are routinely handed more
|
||||
// bytes than they will consume, and both must ignore whitespace anywhere.
|
||||
//
|
||||
// Neither ever fails. A character that belongs to neither alphabet is dropped:
|
||||
// producers emit stray line-noise (a '\0' padding a stream, a lone '<' left
|
||||
// over from a hex string) often enough that refusing the stream would lose a
|
||||
// page over one byte. ISO 32000-2, 7.4.2 and 7.4.3 both make an unexpected
|
||||
// character an error; treating it as a repair instead is the difference
|
||||
// between a reader and a validator.
|
||||
|
||||
// ascii_hex_decode decodes ASCIIHexDecode data.
|
||||
//
|
||||
// Whitespace is ignored, '>' ends the data, and an odd final digit is padded
|
||||
// with '0' as the spec requires. Any other character is skipped.
|
||||
//
|
||||
// The returned slice is owned by the caller and allocated with `allocator`.
|
||||
ascii_hex_decode :: proc(data: []byte, allocator := context.allocator) -> (out: []byte, err: Error) {
|
||||
n := ascii_hex_scan(data, nil)
|
||||
if n == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
buf, alloc_err := make([]byte, n, allocator)
|
||||
if alloc_err != nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
ascii_hex_scan(data, buf)
|
||||
return buf, .None
|
||||
}
|
||||
|
||||
// ascii85_decode decodes ASCII85Decode data.
|
||||
//
|
||||
// "~>" ends the data, 'z' at the start of a group stands for four zero bytes,
|
||||
// whitespace is ignored anywhere, and a truncated final group of n characters
|
||||
// yields n-1 bytes. A leading "<~" -- which the filter does not define but
|
||||
// which producers copying Adobe's own encoder emit -- is skipped.
|
||||
//
|
||||
// The returned slice is owned by the caller and allocated with `allocator`.
|
||||
ascii85_decode :: proc(data: []byte, allocator := context.allocator) -> (out: []byte, err: Error) {
|
||||
n := ascii85_scan(data, nil)
|
||||
if n == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
buf, alloc_err := make([]byte, n, allocator)
|
||||
if alloc_err != nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
ascii85_scan(data, buf)
|
||||
return buf, .None
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scanners
|
||||
//
|
||||
// Each decoder is one procedure run twice: once with `out == nil` to measure
|
||||
// the result, once to fill it. Measuring first means the allocation is exactly
|
||||
// the size of the output rather than an upper bound derived from an
|
||||
// attacker-supplied length, and means the two passes cannot disagree about
|
||||
// what the data says.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// ISO 32000-2, Table 1: the six white-space characters. NUL counts, which
|
||||
// matters here because it is what stream padding is usually made of.
|
||||
@(private)
|
||||
ascii_is_whitespace :: proc "contextless" (c: byte) -> bool {
|
||||
switch c {
|
||||
case 0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(private)
|
||||
ascii_hex_scan :: proc(data: []byte, out: []byte) -> int {
|
||||
n := 0
|
||||
hi: byte
|
||||
have_hi := false
|
||||
for c in data {
|
||||
if c == '>' {
|
||||
break
|
||||
}
|
||||
// hex_value rejects whitespace and junk alike, and both are skipped,
|
||||
// so there is nothing to tell apart here.
|
||||
v, ok := hex_value(c)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !have_hi {
|
||||
hi, have_hi = v, true
|
||||
continue
|
||||
}
|
||||
if n < len(out) {
|
||||
out[n] = hi << 4 | v
|
||||
}
|
||||
n += 1
|
||||
have_hi = false
|
||||
}
|
||||
// A final odd digit is padded with '0', i.e. shifted into the high nibble.
|
||||
if have_hi {
|
||||
if n < len(out) {
|
||||
out[n] = hi << 4
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@(private)
|
||||
ascii85_scan :: proc(data: []byte, out: []byte) -> int {
|
||||
i := 0
|
||||
for i < len(data) && ascii_is_whitespace(data[i]) {
|
||||
i += 1
|
||||
}
|
||||
if i + 1 < len(data) && data[i] == '<' && data[i + 1] == '~' {
|
||||
i += 2
|
||||
}
|
||||
|
||||
n := 0
|
||||
group: [5]byte
|
||||
ngroup := 0
|
||||
|
||||
for ; i < len(data); i += 1 {
|
||||
c := data[i]
|
||||
if ascii_is_whitespace(c) {
|
||||
continue
|
||||
}
|
||||
// The terminator is "~>", but a '~' that is not followed by '>' is
|
||||
// still the end of anything decodable, so do not look at the next byte.
|
||||
if c == '~' {
|
||||
break
|
||||
}
|
||||
if c == 'z' && ngroup == 0 {
|
||||
n = ascii85_emit(out, n, 0, 4)
|
||||
continue
|
||||
}
|
||||
// Everything outside the alphabet is dropped -- including a 'z' in the
|
||||
// middle of a group, which has no defined meaning ('z' is above 'u').
|
||||
if c < '!' || c > 'u' {
|
||||
continue
|
||||
}
|
||||
group[ngroup] = c - '!'
|
||||
ngroup += 1
|
||||
if ngroup == 5 {
|
||||
n = ascii85_emit(out, n, ascii85_value(group[:], 5), 4)
|
||||
ngroup = 0
|
||||
}
|
||||
}
|
||||
|
||||
// A partial group of n characters encodes n-1 bytes. A single leftover
|
||||
// character encodes nothing at all and is dropped.
|
||||
if ngroup >= 2 {
|
||||
n = ascii85_emit(out, n, ascii85_value(group[:], ngroup), ngroup - 1)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ascii85_value folds a group of 1..5 digits, padding a short group with 'u'
|
||||
// as ISO 32000-2, 7.4.3 requires. Five digits can encode up to 85^5-1, which
|
||||
// is larger than a u32 can hold; such a group is out of spec, and clamping it
|
||||
// keeps the bytes that were probably meant rather than dropping the group.
|
||||
@(private)
|
||||
ascii85_value :: proc(digits: []byte, count: int) -> u32 {
|
||||
v: u64 = 0
|
||||
for k in 0 ..< 5 {
|
||||
d: u64 = 84 // 'u' - '!'
|
||||
if k < count && k < len(digits) {
|
||||
d = u64(digits[k])
|
||||
}
|
||||
v = v * 85 + d
|
||||
}
|
||||
if v > 0xFFFF_FFFF {
|
||||
return 0xFFFF_FFFF
|
||||
}
|
||||
return u32(v)
|
||||
}
|
||||
|
||||
@(private)
|
||||
ascii85_emit :: proc(out: []byte, n: int, v: u32, count: int) -> int {
|
||||
b := [4]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}
|
||||
m := n
|
||||
for k in 0 ..< count {
|
||||
if k >= len(b) {
|
||||
break
|
||||
}
|
||||
if m < len(out) {
|
||||
out[m] = b[k]
|
||||
}
|
||||
m += 1
|
||||
}
|
||||
return m
|
||||
}
|
||||
Reference in New Issue
Block a user