Un roman entier en anglais

Un roman anglais du domaine public, lu chapitre par chapitre — douze en tout — et mis en page sur cinquante-neuf pages numérotées, chaque chapitre ouvrant sa propre page et le moteur choisissant chaque coupure de ligne.

Rust write_english_book.rs 100 lignes
  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
//! 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);
    }
}