195 lines
6.0 KiB
Odin
195 lines
6.0 KiB
Odin
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
|
||
|
|
}
|