diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/layouting.rs | 82 | ||||
| -rw-r--r-- | tests/layouts/shakespeare-right.tps | 88 | ||||
| -rw-r--r-- | tests/layouts/shakespeare.tps | 86 | ||||
| -rw-r--r-- | tests/layouts/styles.tps | 13 | ||||
| -rw-r--r-- | tests/render.py | 73 |
5 files changed, 342 insertions, 0 deletions
diff --git a/tests/layouting.rs b/tests/layouting.rs new file mode 100644 index 00000000..b748748f --- /dev/null +++ b/tests/layouting.rs @@ -0,0 +1,82 @@ +use std::fs::{self, File}; +use std::io::{Write, Read, BufWriter}; +use std::process::Command; + +use typst::Typesetter; +use typst::toddle::query::FileSystemFontProvider; +use typst::export::pdf::PdfExporter; +use typst::doc::LayoutAction; + +const CACHE_DIR: &str = "test-cache"; + + +#[test] +fn layouting() { + fs::create_dir_all(format!("{}/serialized", CACHE_DIR)).unwrap(); + fs::create_dir_all(format!("{}/rendered", CACHE_DIR)).unwrap(); + fs::create_dir_all(format!("{}/pdf", CACHE_DIR)).unwrap(); + + for entry in fs::read_dir("tests/layouts/").unwrap() { + let path = entry.unwrap().path(); + + let mut file = File::open(&path).unwrap(); + let mut src = String::new(); + file.read_to_string(&mut src).unwrap(); + + let name = path + .file_stem().unwrap() + .to_str().unwrap(); + + test(name, &src); + } +} + +/// Create a _PDF_ with a name from the source code. +fn test(name: &str, src: &str) { + let mut typesetter = Typesetter::new(); + let provider = FileSystemFontProvider::from_listing("fonts/fonts.toml").unwrap(); + typesetter.add_font_provider(provider.clone()); + + // Layout into box layout. + let tree = typesetter.parse(src).unwrap(); + let layout = typesetter.layout(&tree).unwrap(); + + // Write the serialed layout file. + let path = format!("{}/serialized/{}.box", CACHE_DIR, name); + let mut file = File::create(path).unwrap(); + + // Find all used fonts and their filenames. + let mut map = Vec::new(); + let mut loader = typesetter.loader().borrow_mut(); + for action in &layout.actions { + if let LayoutAction::SetFont(index, _) = action { + if map.iter().find(|(i, _)| i == index).is_none() { + let (_, provider_index) = loader.get_provider_and_index(*index); + let filename = provider.get_path(provider_index).to_str().unwrap(); + map.push((*index, filename)); + } + } + } + drop(loader); + + // Write the font mapping into the serialization file. + writeln!(file, "{}", map.len()).unwrap(); + for (index, path) in map { + writeln!(file, "{} {}", index, path).unwrap(); + } + layout.serialize(&mut file).unwrap(); + + // Render the layout into a PNG. + Command::new("python") + .arg("tests/render.py") + .arg(name) + .spawn() + .expect("failed to run python-based renderer"); + + // Write the PDF file. + let path = format!("{}/pdf/{}.pdf", CACHE_DIR, name); + let file = BufWriter::new(File::create(path).unwrap()); + let document = layout.into_doc(); + let exporter = PdfExporter::new(); + exporter.export(&document, typesetter.loader(), file).unwrap(); +} diff --git a/tests/layouts/shakespeare-right.tps b/tests/layouts/shakespeare-right.tps new file mode 100644 index 00000000..db670fdf --- /dev/null +++ b/tests/layouts/shakespeare-right.tps @@ -0,0 +1,88 @@ +[align: right][ + [bold][Scene 5: _The Tower of London_] + + [italic][Enter Mortimer, brought in a chair, and Gaolers.] + + *Mortimer.* Kind keepers of my weak decaying age, + Let dying Mortimer here rest himself. + Even like a man new haled from the rack, + So fare my limbs with long imprisonment; + And these grey locks, the pursuivants of death, + Nestor-like aged in an age of care, + Argue the end of Edmund Mortimer. + These eyes, like lamps whose wasting oil is spent, + Wax dim, as drawing to their exigent; + Weak shoulders, overborne with burdening grief, + And pithless arms, like to a withered vine + That droops his sapless branches to the ground. + Yet are these feet, whose strengthless stay is numb, + Unable to support this lump of clay, + Swift-winged with desire to get a grave, + As witting I no other comfort have. + But tell me, keeper, will my nephew come? + + *First Keeper.* Richard Plantagenet, my lord, will come. + We sent unto the Temple, unto his chamber; + And answer was return'd that he will come. + + *Mortimer.* Enough; my soul shall then be satisfied. + Poor gentleman! his wrong doth equal mine. + Since Henry Monmouth first began to reign, + Before whose glory I was great in arms, + This loathsome sequestration have I had; + And even since then hath Richard been obscur'd, + Depriv'd of honour and inheritance. + But now the arbitrator of despairs, + Just Death, kind umpire of men's miseries, + With sweet enlargement doth dismiss me hence. + I would his troubles likewise were expir'd, + That so he might recover what was lost. + + + [italic][Enter Richard Plantagenet] + + *First Keeper.* My lord, your loving nephew now is come. + + *Mortimer.* Richard Plantagenet, my friend, is he come? + + *Plantagenet.* Ay, noble uncle, thus ignobly us'd, + Your nephew, late despised Richard, comes. + + *Mortimer.* Direct mine arms I may embrace his neck + And in his bosom spend my latter gasp. + O, tell me when my lips do touch his cheeks, + That I may kindly give one fainting kiss. + And now declare, sweet stem from York's great stock, + Why didst thou say of late thou wert despis'd? + + *Plantagenet.* First, lean thine aged back against mine arm; + And, in that ease, I'll tell thee my disease. + This day, in argument upon a case, + Some words there grew 'twixt Somerset and me; + Among which terms he us'd his lavish tongue + And did upbraid me with my father's death; + Which obloquy set bars before my tongue, + Else with the like I had requited him. + Therefore, good uncle, for my father's sake, + In honour of a true Plantagenet, + And for alliance sake, declare the cause + My father, Earl of Cambridge, lost his head. + + *Mortimer.* That cause, fair nephew, that imprison'd me + And hath detain'd me all my flow'ring youth + Within a loathsome dungeon, there to pine, + Was cursed instrument of his decease. + + *Plantagenet.* Discover more at large what cause that was, + For I am ignorant and cannot guess. + + *Mortimer.* I will, if that my fading breath permit + And death approach not ere my tale be done. + Henry the Fourth, grandfather to this king, + Depos'd his nephew Richard, Edward's son, + The first-begotten and the lawful heir + Of Edward king, the third of that descent; + During whose reign the Percies of the north, + Finding his usurpation most unjust, + Endeavour'd my advancement to the throne ... +] diff --git a/tests/layouts/shakespeare.tps b/tests/layouts/shakespeare.tps new file mode 100644 index 00000000..e0839302 --- /dev/null +++ b/tests/layouts/shakespeare.tps @@ -0,0 +1,86 @@ +[bold][Scene 5: _The Tower of London_] + +[italic][Enter Mortimer, brought in a chair, and Gaolers.] + +*Mortimer.* Kind keepers of my weak decaying age, + Let dying Mortimer here rest himself. + Even like a man new haled from the rack, + So fare my limbs with long imprisonment; + And these grey locks, the pursuivants of death, + Nestor-like aged in an age of care, + Argue the end of Edmund Mortimer. + These eyes, like lamps whose wasting oil is spent, + Wax dim, as drawing to their exigent; + Weak shoulders, overborne with burdening grief, + And pithless arms, like to a withered vine + That droops his sapless branches to the ground. + Yet are these feet, whose strengthless stay is numb, + Unable to support this lump of clay, + Swift-winged with desire to get a grave, + As witting I no other comfort have. + But tell me, keeper, will my nephew come? + +*First Keeper.* Richard Plantagenet, my lord, will come. + We sent unto the Temple, unto his chamber; + And answer was return'd that he will come. + +*Mortimer.* Enough; my soul shall then be satisfied. + Poor gentleman! his wrong doth equal mine. + Since Henry Monmouth first began to reign, + Before whose glory I was great in arms, + This loathsome sequestration have I had; + And even since then hath Richard been obscur'd, + Depriv'd of honour and inheritance. + But now the arbitrator of despairs, + Just Death, kind umpire of men's miseries, + With sweet enlargement doth dismiss me hence. + I would his troubles likewise were expir'd, + That so he might recover what was lost. + + +[italic][Enter Richard Plantagenet] + +*First Keeper.* My lord, your loving nephew now is come. + +*Mortimer.* Richard Plantagenet, my friend, is he come? + +*Plantagenet.* Ay, noble uncle, thus ignobly us'd, + Your nephew, late despised Richard, comes. + +*Mortimer.* Direct mine arms I may embrace his neck + And in his bosom spend my latter gasp. + O, tell me when my lips do touch his cheeks, + That I may kindly give one fainting kiss. + And now declare, sweet stem from York's great stock, + Why didst thou say of late thou wert despis'd? + +*Plantagenet.* First, lean thine aged back against mine arm; + And, in that ease, I'll tell thee my disease. + This day, in argument upon a case, + Some words there grew 'twixt Somerset and me; + Among which terms he us'd his lavish tongue + And did upbraid me with my father's death; + Which obloquy set bars before my tongue, + Else with the like I had requited him. + Therefore, good uncle, for my father's sake, + In honour of a true Plantagenet, + And for alliance sake, declare the cause + My father, Earl of Cambridge, lost his head. + +*Mortimer.* That cause, fair nephew, that imprison'd me + And hath detain'd me all my flow'ring youth + Within a loathsome dungeon, there to pine, + Was cursed instrument of his decease. + +*Plantagenet.* Discover more at large what cause that was, + For I am ignorant and cannot guess. + +*Mortimer.* I will, if that my fading breath permit + And death approach not ere my tale be done. + Henry the Fourth, grandfather to this king, + Depos'd his nephew Richard, Edward's son, + The first-begotten and the lawful heir + Of Edward king, the third of that descent; + During whose reign the Percies of the north, + Finding his usurpation most unjust, + Endeavour'd my advancement to the throne ... diff --git a/tests/layouts/styles.tps b/tests/layouts/styles.tps new file mode 100644 index 00000000..790d2b38 --- /dev/null +++ b/tests/layouts/styles.tps @@ -0,0 +1,13 @@ +_Multiline:_ +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy +eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam +voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet +clita kasd gubergren, no sea takimata sanctus est. + +_Emoji:_ Hello World! 🌍 + +_Styles:_ This is made *bold*, that _italic_ and this one `monospace` using the +built-in syntax! + +_Styles with functions:_ This [bold][word] is made bold and [italic][that] is italic +using the standard library functions [mono][bold] and `italic`! diff --git a/tests/render.py b/tests/render.py new file mode 100644 index 00000000..02c2693f --- /dev/null +++ b/tests/render.py @@ -0,0 +1,73 @@ +import sys +import os +import pathlib +from PIL import Image, ImageDraw, ImageFont + + +BASE = os.path.dirname(__file__) +CACHE_DIR = os.path.join(BASE, "../test-cache/"); + + +def main(): + assert len(sys.argv) == 2, "usage: python render.py <name>" + name = sys.argv[1] + + filename = os.path.join(CACHE_DIR, f"serialized/{name}.box") + with open(filename, encoding="utf-8") as file: + lines = [line[:-1] for line in file.readlines()] + + fonts = {} + font_count = int(lines[0]) + for i in range(font_count): + parts = lines[1 + i].split(' ', 1) + index = int(parts[0]) + path = parts[1] + fonts[index] = os.path.join(BASE, "../fonts", path) + + width, height = (float(s) for s in lines[font_count + 1].split()) + + renderer = Renderer(fonts, width, height) + for command in lines[font_count + 2:]: + renderer.execute(command) + + pathlib.Path(os.path.join(CACHE_DIR, "rendered")).mkdir(parents=True, exist_ok=True) + renderer.export(name) + + +class Renderer: + def __init__(self, fonts, width, height): + self.fonts = fonts + self.img = Image.new("RGBA", (pix(width), pix(height)), (255, 255, 255)) + self.draw = ImageDraw.Draw(self.img) + self.cursor = (0, 0) + + def execute(self, command): + cmd = command[0] + parts = command.split()[1:] + + if cmd == 'm': + x, y = (pix(float(s)) for s in parts) + self.cursor = (x, y) + + elif cmd == 'f': + index = int(parts[0]) + size = pix(float(parts[1])) + self.font = ImageFont.truetype(self.fonts[index], size) + + elif cmd == 'w': + text = command[2:] + self.draw.text(self.cursor, text, (0, 0, 0), font=self.font) + + else: + raise Exception("invalid command") + + def export(self, name): + self.img.save(CACHE_DIR + "rendered/" + name + ".png") + + +def pix(points): + return int(2 * points) + + +if __name__ == "__main__": + main() |
