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 | //! Places images on a page, and writes the result to disk.
//!
//! Each image named on the command line gets a page of its own, exactly as big
//! as the image is: at 72 dots to the inch one pixel is one point, so the page
//! that comes back out of a renderer is the image that went in. That is what
//! makes the output checkable against its own source, pixel for pixel.
//!
//! Named nothing, the example places the two committed fixtures — a JPEG, whose
//! bytes go into the file undecoded, and a PNG whose transparency becomes a
//! soft mask — and adds a page that lays them out the way a document would.
//!
//! Usage: `cargo run --example write_image -- tmp/image.pdf [image...]`
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use hqf_pdf::content::Content;
use hqf_pdf::cos::Name;
use hqf_pdf::{Document, Image, Page, Rgb};
#[path = "shared/out.rs"]
mod out;
#[path = "shared/licence.rs"]
mod licence;
/// The images the example places when the caller names none: the ones committed
/// for the tests, so that it runs on any machine.
fn default_images() -> Vec<PathBuf> {
let images = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("images");
vec![images.join("photo.jpg"), images.join("logo.png")]
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = env::args().skip(1);
let out = args.next().unwrap_or_else(|| out::default_path("image"));
let paths: Vec<PathBuf> = args.map(PathBuf::from).collect();
let showcase = paths.is_empty();
let paths = if showcase { default_images() } else { paths };
let mut doc = Document::new();
doc.set_license(licence::licensed());
doc.set_info(Name::new("Title"), "hqf-pdf image sample");
let mut handles = Vec::new();
for path in &paths {
let image = Image::parse(fs::read(path)?)?;
println!(
"{}: {}x{} {:?}{}, {}, {}",
path.display(),
image.width(),
image.height(),
image.color_space(),
if image.has_alpha() {
", with a soft mask"
} else {
""
},
image.resolution().map_or_else(
|| "stating no resolution of its own".to_owned(),
|resolution| { format!("{} by {} dots to the inch", resolution.x, resolution.y) },
),
image.orientation().map_or_else(
|| "and no way up".to_owned(),
|orientation| format!("seen {orientation:?}"),
)
);
let handle = doc.add_image(image);
// A page the size of the image, with the image filling it. The size is
// the one the file itself asks for; a file asking for none is laid down
// one pixel to the point.
let (width, height) = handle.size();
let mut c = Content::new();
c.draw_image(&handle, 0.0, 0.0, width, height)?;
let mut page = Page::new(width, height);
page.content = c.into_bytes();
doc.add_page(page)?;
handles.push(handle);
}
if showcase {
// One more page, laid out the way a document would lay it out: the
// photo fitted into a box without being stretched, and the logo, whose
// transparency lets what is under it show through, over a rule.
let mut c = Content::new();
c.set_fill(Rgb::new(0.93, 0.94, 0.96))?;
c.rect(0.0, 700.0, 595.276, 142.0)?;
c.fill();
let photo = &handles[0];
let (width, height) = photo.fit_within(240.0, 160.0)?;
c.draw_image(photo, 60.0, 500.0, width, height)?;
let logo = &handles[1];
let (width, height) = logo.fit_within(90.0, 90.0)?;
c.draw_image(logo, 380.0, 730.0, width, height)?;
let mut page = Page::a4();
page.content = c.into_bytes();
doc.add_page(page)?;
}
let bytes = doc.to_bytes()?;
if let Some(parent) = Path::new(&out).parent() {
fs::create_dir_all(parent)?;
}
fs::write(&out, &bytes)?;
println!(
"wrote {out}: {} bytes, {} page(s)",
bytes.len(),
doc.page_count()
);
Ok(())
}
|