-
Notifications
You must be signed in to change notification settings - Fork 186
/
Copy pathxobject.rs
129 lines (111 loc) · 4.33 KB
/
xobject.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::*;
use crate::{Dictionary, Stream};
#[cfg(feature = "embed_image")]
use image::{self, ColorType, ImageFormat};
#[cfg(feature = "embed_image")]
use std::path::Path;
#[cfg(feature = "embed_image")]
use crate::Result;
#[derive(Debug, Clone)]
pub struct PdfImage<'a> {
pub id: ObjectId,
pub width: i64,
pub height: i64,
pub color_space: Option<String>,
pub filters: Option<Vec<String>>,
pub bits_per_component: Option<i64>,
/// Image Data
pub content: &'a [u8],
/// Origin Stream Dictionary
pub origin_dict: &'a Dictionary,
}
pub fn form(boundingbox: Vec<f32>, matrix: Vec<f32>, content: Vec<u8>) -> Stream {
let mut dict = Dictionary::new();
dict.set("Type", Object::Name(b"XObject".to_vec()));
dict.set("Subtype", Object::Name(b"Form".to_vec()));
dict.set(
"BBox",
Object::Array(boundingbox.into_iter().map(Object::Real).collect()),
);
dict.set("Matrix", Object::Array(matrix.into_iter().map(Object::Real).collect()));
let mut xobject = Stream::new(dict, content);
// Ignore any compression error.
let _ = xobject.compress();
xobject
}
#[cfg(feature = "embed_image")]
pub fn image<P: AsRef<Path>>(path: P) -> Result<Stream> {
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open(&path)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
image_from(buffer, path.as_ref())
}
#[cfg(feature = "embed_image")]
pub fn image_from(buffer: Vec<u8>, path: &Path) -> Result<Stream> {
let (width, height) = image::image_dimensions(path)?;
let format = image::guess_format(&buffer)?;
let is_jpeg = format == ImageFormat::Jpeg;
let (img, color_type) = if is_jpeg {
(None, ColorType::Rgb8) // JPEG do not need to be decoded
} else {
// Other formats need to be decoded
let img = image::load_from_memory(&buffer)?;
let color_type = img.color();
(Some(img), color_type)
};
// It looks like Adobe Illustrator uses a predictor offset of 2 bytes rather than 1 byte as
// the PNG specification suggests. This seems to come from the fact that the PNG specification
// doesn't allow 4-bit color images (only 8-bit and 16-bit color). With 1-bit, 2-bit and 4-bit
// mono images there isn't the same problem because there's only one component.
let bits = color_type.bits_per_pixel() / 3;
let color_space = match color_type {
ColorType::L8 => b"DeviceGray".to_vec(),
ColorType::La8 => b"DeviceGray".to_vec(),
ColorType::Rgb8 => b"DeviceRGB".to_vec(),
ColorType::Rgb16 => b"DeviceRGB".to_vec(),
ColorType::La16 => b"DeviceN".to_vec(),
ColorType::Rgba8 => b"DeviceN".to_vec(),
ColorType::Rgba16 => b"DeviceN".to_vec(),
_ => b"Indexed".to_vec(),
};
let mut dict = Dictionary::new();
dict.set("Type", Object::Name(b"XObject".to_vec()));
dict.set("Subtype", Object::Name(b"Image".to_vec()));
dict.set("Width", width);
dict.set("Height", height);
dict.set("ColorSpace", Object::Name(color_space));
dict.set("BitsPerComponent", bits);
if is_jpeg {
dict.set("Filter", Object::Name(b"DCTDecode".to_vec()));
Ok(Stream::new(dict, buffer))
} else {
let mut img_object = Stream::new(dict, img.unwrap().into_bytes());
// Ignore any compression error.
let _ = img_object.compress();
Ok(img_object)
}
}
#[cfg(all(feature = "embed_image", not(feature = "async")))]
#[test]
fn insert_image() {
use super::xobject;
let mut doc = Document::load("assets/example.pdf").unwrap();
let pages = doc.get_pages();
let page_id = *pages.get(&1).expect(&format!("Page {} not exist.", 1));
let img = xobject::image("assets/pdf_icon.jpg").unwrap();
doc.insert_image(page_id, img, (100.0, 210.0), (400.0, 225.0)).unwrap();
doc.save("test_5_image.pdf").unwrap();
}
#[cfg(all(feature = "embed_image", feature = "async"))]
#[tokio::test]
async fn insert_image() {
use super::xobject;
let mut doc = Document::load("assets/example.pdf").await.unwrap();
let pages = doc.get_pages();
let page_id = *pages.get(&1).expect(&format!("Page {} not exist.", 1));
let img = xobject::image("assets/pdf_icon.jpg").unwrap();
doc.insert_image(page_id, img, (100.0, 210.0), (400.0, 225.0)).unwrap();
doc.save("test_5_image.pdf").unwrap();
}