summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/layouting.rs82
-rw-r--r--tests/layouts/shakespeare-right.tps88
-rw-r--r--tests/layouts/shakespeare.tps86
-rw-r--r--tests/layouts/styles.tps13
-rw-r--r--tests/render.py73
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()