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
130
131
132
133 | //! 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;
#[path = "shared/failure.rs"]
mod failure;
/// 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() -> std::process::ExitCode {
failure::reported(run())
}
fn run() -> 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(())
}
|