///|
/// Add or replace a dictionary entry while preserving historical insertion order.
pub fn dict_add_entries(
entries : ArrayView[(@core.PdfName, PdfObject)],
key : @core.PdfName,
value : PdfObject,
) -> Array[(@core.PdfName, PdfObject)] {
let output : Array[(@core.PdfName, PdfObject)] = Array(
capacity=entries.length() + 1,
)
for i in 0.. @core.PdfName {
let prefix_bytes = @ascii.encode(prefix)
let index_bytes = @ascii.encode(index.to_string())
let output = Array::make(
1 + prefix_bytes.length() + index_bytes.length(),
b'\x00',
)
output[0] = 47
let mut position = 1
for byte in prefix_bytes {
output[position] = byte
position += 1
}
for byte in index_bytes {
output[position] = byte
position += 1
}
@core.pdf_name_of_bytes(Bytes::from_array(output))
}
///|
fn pdf_object_dictionary_has_key(
entries : ArrayView[(@core.PdfName, PdfObject)],
key : @core.PdfName,
) -> Bool {
for entry in entries {
if entry.0 == key {
break true
}
} nobreak {
false
}
}
///|
/// Generate a dictionary key with the form `/` that is not in use.
///
/// The scan starts at `n = 0` and increments until the name is absent from the
/// dictionary or stream dictionary. Non-dictionary-like objects raise
/// `@core.PdfError::DictionaryExpected`.
pub fn PdfObject::unique_key(
self : PdfObject,
prefix : String,
) -> @core.PdfName raise @core.PdfError {
match self.dictionary_view() {
Some(entries) => {
let mut index = 0
let mut candidate = pdf_object_unique_key_candidate(prefix, index)
while pdf_object_dictionary_has_key(entries, candidate) {
index += 1
candidate = pdf_object_unique_key_candidate(prefix, index)
}
candidate
}
None => raise DictionaryExpected
}
}
///|
fn dict_replace_entries(
entries : ArrayView[(@core.PdfName, PdfObject)],
key : @core.PdfName,
value : PdfObject,
) -> Array[(@core.PdfName, PdfObject)] raise @core.PdfError {
let output : Array[(@core.PdfName, PdfObject)] = Array(
capacity=entries.length(),
)
for i in 0.. Array[(@core.PdfName, PdfObject)] {
let output : Array[(@core.PdfName, PdfObject)] = Array(
capacity=entries.length(),
)
for entry in entries {
let (name, value) = entry
if name != key {
output.push((name, value))
}
}
output
}
///|
/// Add or replace a dictionary entry.
///
/// `PdfNull` is promoted to a one-entry dictionary. Existing keys are replaced
/// in place. New keys are inserted using the historical CamlPDF order, which
/// places the new entry before existing entries. Stream objects mutate their
/// stream dictionary and return the same stream object.
pub fn PdfObject::add_dict_entry(
self : PdfObject,
key : @core.PdfName,
value : PdfObject,
) -> PdfObject raise @core.PdfError {
match self {
PdfNull => PdfDictionary([(key, value)])
PdfDictionary(entries) =>
PdfDictionary(dict_add_entries(entries, key, value))
PdfStreamObject(stream) => {
stream.dictionary = stream.dictionary.add_dict_entry(key, value)
self
}
_ => raise DictionaryExpected
}
}
///|
/// Replace an existing dictionary entry.
///
/// The key must already exist in a plain dictionary or stream dictionary.
/// Missing keys and non-dictionary inputs raise `@core.PdfError::DictionaryKeyNotFound`.
pub fn PdfObject::replace_dict_entry(
self : PdfObject,
key : @core.PdfName,
value : PdfObject,
) -> PdfObject raise @core.PdfError {
match self {
PdfDictionary(entries) =>
PdfDictionary(dict_replace_entries(entries, key, value))
PdfStreamObject(stream) => {
stream.dictionary = stream.dictionary.replace_dict_entry(key, value)
self
}
_ => raise DictionaryKeyNotFound(key)
}
}
///|
/// Remove a dictionary entry if present.
///
/// Missing keys are ignored for dictionaries. Stream objects mutate their stream
/// dictionary and return the same stream object. Non-dictionary inputs raise
/// `@core.PdfError::DictionaryExpected`.
pub fn PdfObject::remove_dict_entry(
self : PdfObject,
key : @core.PdfName,
) -> PdfObject raise @core.PdfError {
match self {
PdfDictionary(entries) => PdfDictionary(dict_remove_entries(entries, key))
PdfStreamObject(stream) => {
stream.dictionary = stream.dictionary.remove_dict_entry(key)
self
}
_ => raise DictionaryExpected
}
}