//! Lays a whole English novel out and writes it as one PDF.
//!
//! It is what a document's weight really looks like. A novel draws the whole
//! alphabet in both cases, the digits and the punctuation, so the embedded font
//! carries far more outlines than an invoice does — and its metrics, which used
//! to be stated for every glyph of the face, are stated only for the glyphs the
//! book draws.
//!
//! Its twin, `write_french_book`, lays another novel out through the same code.
//! Neither is a translation of the other: each is a book of its own, and each
//! says how its own file marks a chapter.
//!
//! Usage: `cargo run --example write_english_book -- tmp/english_book.pdf
//! [font.ttf]`
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use hqf_pdf::{Document, Font};
#[path = "shared/out.rs"]
mod out;
#[path = "shared/licence.rs"]
mod licence;
#[path = "shared/book.rs"]
mod book;
/// The font the example draws with when none is given on the command line.
fn default_font() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fonts")
.join("DejaVuSans.ttf")
}
/// The novel this lays out, carried in full. Its file opens a chapter on a line
/// that reads `CHAPTER I.`, and it holds twelve of them.
const BOOK: book::Book = book::Book {
title: "Alice's Adventures in Wonderland",
author: "Lewis Carroll",
file: "wonderland.txt",
mark_before: "CHAPTER ",
mark_after: ".",
chapters: 12,
};
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("english_book"));
let font_path = args.next().map_or_else(default_font, PathBuf::from);
let mut doc = Document::new();
doc.set_license(licence::licensed());
let handle = doc.add_font(Font::parse(fs::read(&font_path)?)?);
let text = fs::read_to_string(BOOK.path())?;
book::lay_out(&mut doc, &handle, &BOOK, &text)?;
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", bytes.len());
Ok(())
}
#[cfg(test)]
mod tests {
use hqf_pdf::{Document, Font, FontHandle};
use super::{BOOK, book, default_font};
/// The font every measurement here is taken in, which is the one the
/// example draws with when the command line says nothing.
fn font(document: &mut Document) -> FontHandle {
document.add_font(
Font::parse(std::fs::read(default_font()).expect("the committed font is there"))
.expect("the committed font parses"),
)
}
#[test]
fn every_chapter_of_the_novel_is_headed_and_carries_prose() {
book::check_every_chapter_is_headed(&BOOK);
}
#[test]
fn the_running_heads_stay_on_one_line() {
let mut document = Document::new();
let handle = font(&mut document);
book::check_the_running_heads_stay_on_one_line(&BOOK, &handle);
}
}