///|
fn is_encryption_error(err : Error) -> Bool {
match err {
@pdf.PdfError::Msg(message) =>
message.contains("Encryption") || message.contains("encryption")
_ => false
}
}
///|
fn should_fallback_on_error(
ctx : PdfRead,
err : Error,
revision : Int?,
) -> Bool {
if ctx.error_on_malformed {
return false
}
if revision is Some(-1) {
return false
}
match err {
ReadPdfError::Revisions(_) | ReadPdfError::BadRevision => false
_ => !is_encryption_error(err)
}
}
///|
fn advance_to_integer(input : @pdfio.Input) -> Bool {
let mut found = false
while !found {
let b = (input.input_byte)()
if b == @pdfio.no_more {
return false
}
let c = b.to_byte().to_char()
if is_digit(c) {
input.rewind()
found = true
}
}
true
}
///|
fn read_malformed_pdf_objects(
input : @pdfio.Input,
read_stream_data : Bool,
) -> (@pdf.PdfObjMap, Int) raise {
let objects : @pdf.PdfObjMap = Map::new()
let mut max_obj = 0
(input.seek_in)(0)
while true {
if !advance_to_integer(input) {
break
}
let start = (input.pos_in)()
let objnum_str = @pdfsyntax.PdfSyntax::new().getuntil_white_or_delimiter_string(
input,
)
let objnum = match parse_uint_string(objnum_str) {
Some(v) => v
None => {
(input.seek_in)(start + 1)
continue
}
}
@pdfsyntax.PdfSyntax::new().dropwhite(input)
let gen_str = @pdfsyntax.PdfSyntax::new().getuntil_white_or_delimiter_string(
input,
)
let gen = match parse_uint_string(gen_str) {
Some(v) => v
None => {
(input.seek_in)(start + 1)
continue
}
}
@pdfsyntax.PdfSyntax::new().dropwhite(input)
let kw = @pdfsyntax.PdfSyntax::new().getuntil_white_or_delimiter_string(
input,
)
if kw != "obj" {
(input.seek_in)(start + 1)
continue
}
let parsed = try {
(input.seek_in)(start)
let lexemes = @pdfsyntax.PdfSyntax::new().lex_object_at(
false,
input,
read_stream_data,
_ => [],
)
let (_, obj) = @pdfsyntax.PdfSyntax::new().parse(
lexemes,
failure_is_ok=true,
)
Some(obj)
} catch {
_ => None
}
match parsed {
Some(obj) => {
objects.set(objnum, ({ val: @pdf.ObjectData::Parsed(obj) }, gen))
if objnum > max_obj {
max_obj = objnum
}
}
None => (input.seek_in)(start + 1)
}
}
(objects, max_obj)
}
///|
fn trailer_boundary_ok(input : @pdfio.Input, pos : Int, len : Int) -> Bool {
let before_ok = if pos == 0 {
true
} else {
(input.seek_in)(pos - 1)
let c = (input.input_char)()
(input.seek_in)(pos)
match c {
None => true
Some(ch) => @pdfsyntax.PdfSyntax::new().is_whitespace_or_delimiter(ch)
}
}
let after_ok = if pos + len >= input.in_channel_length {
true
} else {
(input.seek_in)(pos + len)
let c = (input.input_char)()
(input.seek_in)(pos)
match c {
None => true
Some(ch) => @pdfsyntax.PdfSyntax::new().is_whitespace_or_delimiter(ch)
}
}
before_ok && after_ok
}
///|
fn read_malformed_trailerdicts(
input : @pdfio.Input,
) -> Array[@pdf.PdfObject] raise {
let dicts = Array::new()
let target = "trailer"
let len = target.length()
let mut pos = 0
while pos + len <= input.in_channel_length {
(input.seek_in)(pos)
if @pdfsyntax.PdfSyntax::new().read_chunk(len, input) == target &&
trailer_boundary_ok(input, pos, len) {
(input.seek_in)(pos + len)
try {
@pdfsyntax.PdfSyntax::new().dropwhite(input)
let lexemes = @pdfsyntax.PdfSyntax::new().lex_dictionary(false, input)
let (_, parsed) = @pdfsyntax.PdfSyntax::new().parse(
lexemes,
failure_is_ok=true,
)
let dict = extract_trailer_dict(parsed)
dicts.push(dict)
} catch {
_ => ()
}
let next_pos = (input.pos_in)()
if next_pos <= pos {
pos = pos + 1
} else {
pos = next_pos
}
} else {
pos = pos + 1
}
}
dicts
}
///|
fn find_catalog_object(objects : @pdf.PdfObjMap) -> Int? {
for entry in objects {
let objnum = entry.0
let (obj_ref, _gen) = entry.1
match obj_ref.val {
@pdf.ObjectData::Parsed(obj)
| @pdf.ObjectData::ParsedAlreadyDecrypted(obj) =>
match obj.lookup_immediate("/Type") {
Some(@pdf.PdfObject::Name(name)) if name.equal_string_bytes(
"/Catalog",
) => return Some(objnum)
_ => ()
}
_ => ()
}
}
None
}
///|
fn read_malformed_pdf(
ctx : PdfRead,
revision : Int?,
user_password : String?,
owner_password : String?,
input : @pdfio.Input,
read_stream_data : Bool,
) -> @pdf.Pdf raise {
let _ = revision
let _ = user_password
let _ = owner_password
(input.seek_in)(0)
let (major_header, minor_header) = read_header(ctx, input)
let (objects, max_obj) = read_malformed_pdf_objects(input, read_stream_data)
let trailers = read_malformed_trailerdicts(input)
let mut trailerdict = if trailers.length() > 0 {
trailers[trailers.length() - 1]
} else {
@pdf.PdfObject::Dictionary([])
}
let root = match trailerdict.lookup_immediate("/Root") {
Some(@pdf.PdfObject::Indirect(i)) => i
Some(_) => {
let from_catalog = find_catalog_object(objects)
match from_catalog {
Some(i) => i
None => raise @pdf.PdfError::Msg("Malformed /Root entry")
}
}
None => {
let from_catalog = find_catalog_object(objects)
match from_catalog {
Some(i) => i
None => raise @pdf.PdfError::Msg("No /Root entry")
}
}
}
if trailerdict.lookup_immediate("/Root") is None {
trailerdict = trailerdict.add_entry("/Root", @pdf.PdfObject::Indirect(root))
}
let sanitized_trailer = sanitize_trailerdict(objects.length(), trailerdict)
let pdf_objects : @pdf.PdfObjects = {
max_obj_num: max_obj,
parse: None,
objects,
object_stream_ids: Map::new(),
}
let pdf_tmp = @pdf.Pdf::{
major: major_header,
minor: minor_header,
root,
objects: pdf_objects,
trailerdict: sanitized_trailer,
was_linearized: false,
saved_encryption: None,
}
let (major, minor) = match version_from_catalog(ctx, pdf_tmp) {
Some(pair) => pair
None => (major_header, minor_header)
}
@pdf.Pdf::{
major,
minor,
root,
objects: pdf_objects,
trailerdict: sanitized_trailer,
was_linearized: false,
saved_encryption: None,
}
}