///|
/// Lookup an object in a document, parsing it if required.
pub fn Pdf::lookup_obj(self : Pdf, objnum : Int) -> PdfObject {
match self.objects.objects.get(objnum) {
None => Null
Some((obj_ref, _gen)) =>
match obj_ref.val {
Parsed(obj) | ParsedAlreadyDecrypted(obj) => obj
ToParse =>
match self.objects.parse {
None => Null
Some(parser) => {
let obj = parser(objnum)
obj_ref.val = Parsed(obj)
obj
}
}
ToParseFromObjectStream(themap, streamobjnum, _index, objstreamparser) =>
self.parse_delayed_object_stream(
themap, objnum, streamobjnum, objstreamparser,
)
}
}
}
///|
fn Pdf::parse_delayed_object_stream(
self : Pdf,
themap : Map[Int, Array[Int]],
objnum : Int,
streamobjnum : Int,
parser : (Int, Array[Int]) -> Array[(Int, (Ref[ObjectData], Int))],
) -> PdfObject {
let indexes = match themap.get(objnum) {
Some(value) => value
None => return Null
}
let objects_from_stream = parser(streamobjnum, indexes)
let mut found = false
for pair in objects_from_stream {
if pair.0 == objnum {
found = true
}
self.objects.objects.set(pair.0, pair.1)
}
self.removeobj(streamobjnum)
if found {
self.lookup_obj(objnum)
} else {
Null
}
}
///|
/// Make a PDF object direct -- that is, follow any indirect links.
pub fn Pdf::direct(self : Pdf, obj : PdfObject) -> PdfObject {
match obj {
Indirect(i) => self.direct(self.lookup_obj(i))
_ => obj
}
}
///|
/// Lookup the key, resolving indirections at source and destination.
pub fn Pdf::lookup_direct(
self : Pdf,
key : String,
dict : PdfObject,
) -> PdfObject? {
let dict_obj = self.direct(dict)
let entry = match dict_obj {
Dictionary(entries) =>
match entries.search_by(pair => pair.0 == key) {
None => None
Some(i) => Some(entries[i].1)
}
Stream(r) => {
let (inner, _) = r.val
match inner {
Dictionary(entries) =>
match entries.search_by(pair => pair.0 == key) {
None => None
Some(i) => Some(entries[i].1)
}
_ => None
}
}
_ => None
}
match entry {
None => None
Some(value) => Some(self.direct(value))
}
}
///|
/// Parse array index keys of the form "/[]".
fn array_index_from_key(key : String) -> Int? {
if !key.has_prefix("/[") {
return None
}
let rest = try key[2:] catch {
_ => return None
} noraise {
view => view
}
let mut value = 0
let mut seen = false
for ch in rest {
if ch >= '0' && ch <= '9' {
value = value * 10 + (ch.to_int() - '0'.to_int())
seen = true
} else {
break
}
}
if seen {
Some(value)
} else {
None
}
}
///|
/// Lookup a key, allowing array index chains.
fn Pdf::lookup_direct_or_array(
self : Pdf,
key : String,
obj : PdfObject,
) -> PdfObject? {
match array_index_from_key(key) {
Some(index) =>
match obj {
Array(items) => Some(self.direct(items[index]))
_ => None
}
None => self.lookup_direct(key, obj)
}
}
///|
/// Lookup a key in a nested dictionary chain.
pub fn Pdf::lookup_chain(
self : Pdf,
start : PdfObject,
keys : ArrayView[String],
) -> PdfObject? {
let mut current = start
for key in keys {
match self.lookup_direct_or_array(key, current) {
None => return None
Some(next) => current = next
}
}
Some(current)
}
///|
/// Replace or insert a chain from the trailer dictionary.
pub fn Pdf::replace_chain(
self : Pdf,
chain : ArrayView[String],
obj : PdfObject,
) -> Unit raise {
fn replace_in_obj(
pdf : Pdf,
current : PdfObject,
chain : ArrayView[String],
obj : PdfObject,
) -> PdfObject raise {
if chain.length() == 0 {
return obj
}
let key = chain[0]
let rest = chain.sub(start=1)
match array_index_from_key(key) {
Some(index) =>
match current {
Array(items) => {
if index < 0 || index >= items.length() {
raise PdfError::Msg("replace_chain: array index out of range")
}
let updated = if rest.length() == 0 {
obj
} else {
replace_in_obj(pdf, items[index], rest, obj)
}
let out = items.copy()
out[index] = updated
Array(out)
}
_ => raise PdfError::Msg("replace_chain: bad array chain")
}
None =>
match current {
Null => {
let child = replace_in_obj(pdf, Dictionary([]), rest, obj)
Dictionary([(key, child)])
}
Dictionary(entries) => {
let mut found = false
let out = Array::new(capacity=entries.length() + 1)
for entry in entries {
let (k, v) = entry
if k == key {
let child = replace_in_obj(pdf, v, rest, obj)
out.push((k, child))
found = true
} else {
out.push((k, v))
}
}
if !found {
let child = replace_in_obj(pdf, Dictionary([]), rest, obj)
out.push((key, child))
}
Dictionary(out)
}
Stream(r) => {
let (inner, stream) = r.val
let updated = replace_in_obj(pdf, inner, chain, obj)
r.val = (updated, stream)
Stream(r)
}
_ => raise PdfError::Msg("replace_chain: not a dictionary")
}
}
}
self.trailerdict = replace_in_obj(self, self.trailerdict, chain, obj)
}
///|
/// Return the object number of an indirect dictionary object, if it is indirect.
pub fn Pdf::indirect_number(self : Pdf, key : String, dict : PdfObject) -> Int? {
match self.direct(dict) {
Dictionary(entries) =>
match entries.search_by(pair => pair.0 == key) {
None => None
Some(i) =>
match entries[i].1 {
Indirect(objnum) => Some(objnum)
_ => None
}
}
Stream(r) => {
let (inner, _) = r.val
self.indirect_number(key, inner)
}
_ => None
}
}
///|
/// Same as lookup_direct, but allow a second alternative key.
pub fn Pdf::lookup_direct_orelse(
self : Pdf,
key : String,
alt : String,
dict : PdfObject,
) -> PdfObject? {
match self.lookup_direct(key, dict) {
Some(obj) => Some(obj)
None => self.lookup_direct(alt, dict)
}
}
///|
/// Lookup an object, failing if not found.
pub fn Pdf::lookup_fail(
self : Pdf,
errtext : String,
key : String,
dict : PdfObject,
) -> PdfObject raise {
match self.lookup_direct(key, dict) {
Some(obj) => obj
None => raise PdfError::Msg(errtext)
}
}