use lopdf::{dictionary, Document, Object, ObjectId, ObjectStream, Stream};
#[test]
fn test_object_stream_builder() {
let builder = ObjectStream::builder();
assert_eq!(builder.get_max_objects(), 100);
assert_eq!(builder.get_compression_level(), 6);
}
#[test]
fn test_object_stream_add_and_build() {
let mut obj_stream = ObjectStream::builder().build();
obj_stream.add_object((1, 0), Object::Integer(42)).unwrap();
obj_stream.add_object((2, 0), Object::Boolean(true)).unwrap();
obj_stream.add_object((3, 0), Object::Name(b"Test".to_vec())).unwrap();
let content = obj_stream.build_stream_content().unwrap();
assert!(!content.is_empty());
let stream_obj = obj_stream.to_stream_object().unwrap();
assert_eq!(stream_obj.dict.get(b"Type").unwrap(), &Object::Name(b"ObjStm".to_vec()));
assert_eq!(stream_obj.dict.get(b"N").unwrap(), &Object::Integer(3));
}
#[test]
fn test_save_with_object_streams() {
let mut doc = Document::with_version("1.5");
let pages_id = doc.new_object_id();
let font_id = doc.add_object(dictionary! {
"Type" => "Font",
"Subtype" => "Type1",
"BaseFont" => "Helvetica"
});
let resources_id = doc.add_object(dictionary! {
"Font" => dictionary! {
"F1" => font_id,
},
});
let content = lopdf::content::Content {
operations: vec![
lopdf::content::Operation::new("BT", vec![]),
lopdf::content::Operation::new("Tf", vec!["F1".into(), 12.into()]),
lopdf::content::Operation::new("Td", vec![50.into(), 700.into()]),
lopdf::content::Operation::new("Tj", vec![Object::string_literal("Test Document")]),
lopdf::content::Operation::new("ET", vec![]),
],
};
let content_id = doc.add_object(Stream::new(
dictionary! {},
content.encode().unwrap()
));
let page_id = doc.add_object(dictionary! {
"Type" => "Page",
"Parent" => pages_id,
"Contents" => content_id,
"Resources" => resources_id,
"MediaBox" => vec![0.into(), 0.into(), 612.into(), 792.into()],
});
doc.objects.insert(pages_id, Object::Dictionary(dictionary! {
"Type" => "Pages",
"Kids" => vec![page_id.into()],
"Count" => 1,
}));
let catalog_id = doc.add_object(dictionary! {
"Type" => "Catalog",
"Pages" => pages_id,
});
doc.trailer.set("Root", catalog_id);
let mut buffer = Vec::new();
let result = doc.save_modern(&mut buffer);
assert!(result.is_ok());
let content = String::from_utf8_lossy(&buffer);
assert!(content.starts_with("%PDF-1.5"));
assert!(content.contains("/ObjStm"), "Object streams should be created");
assert!(!content.contains("2 0 obj\n<</Type/Pages"), "Pages object should be compressed");
assert!(!content.contains("3 0 obj\n<</Type/Page"), "Page object should be compressed");
}
#[test]
fn test_object_stream_parses_objects_with_leading_whitespace() {
let obj10_bytes = b"\n<< /Type /Font /Subtype /TrueType /BaseFont /Calibri >>";
let obj11_bytes = b"\n<< /Type /FontDescriptor /FontName /Calibri >>";
let obj10_offset = 0usize;
let obj11_offset = obj10_bytes.len();
let index = format!("10 {} 11 {} ", obj10_offset, obj11_offset);
let first_offset = index.len();
let mut content = Vec::new();
content.extend_from_slice(index.as_bytes());
content.extend_from_slice(obj10_bytes);
content.extend_from_slice(obj11_bytes);
let dict = dictionary! {
"Type" => "ObjStm",
"N" => 2,
"First" => first_offset as i64,
};
let mut stream = Stream::new(dict, content);
let obj_stream = ObjectStream::new(&mut stream).expect("should parse object stream");
assert_eq!(
obj_stream.objects.len(), 2,
"expected 2 objects but got {}; leading whitespace was not skipped",
obj_stream.objects.len()
);
let obj10 = obj_stream.objects.get(&(10u32, 0u16) as &ObjectId)
.expect("object 10 missing");
if let Object::Dictionary(dict) = obj10 {
assert_eq!(dict.get(b"BaseFont").unwrap().as_name().unwrap(), b"Calibri");
} else {
panic!("object 10 should be a Dictionary, got {:?}", obj10);
}
let obj11 = obj_stream.objects.get(&(11u32, 0u16) as &ObjectId)
.expect("object 11 missing");
if let Object::Dictionary(dict) = obj11 {
assert_eq!(dict.get(b"FontName").unwrap().as_name().unwrap(), b"Calibri");
} else {
panic!("object 11 should be a Dictionary, got {:?}", obj11);
}
}