summaryrefslogtreecommitdiff
path: root/tests/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-02 21:17:42 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-02 21:17:42 +0200
commitcbbc46215fe0a0ad8a50e991ec442890b8eadc0a (patch)
tree2efbac21cec46787f1efe0a859564b9614eefa98 /tests/src
parentd5ff97f42ed1e682a66ea8d51e5f9ed1be547b9c (diff)
Layout elements and pure rust rendering 🥏
Diffstat (limited to 'tests/src')
-rw-r--r--tests/src/render.py182
-rw-r--r--tests/src/typeset.rs206
2 files changed, 0 insertions, 388 deletions
diff --git a/tests/src/render.py b/tests/src/render.py
deleted file mode 100644
index 80f165a1..00000000
--- a/tests/src/render.py
+++ /dev/null
@@ -1,182 +0,0 @@
-import sys
-import os
-import math
-import numpy
-import json
-from PIL import Image, ImageDraw, ImageFont
-
-
-BASE = os.path.dirname(__file__)
-CACHE = os.path.join(BASE, '../cache/')
-
-
-def main():
- assert len(sys.argv) == 2, 'usage: python render.py <name>'
- name = sys.argv[1]
-
- filename = os.path.join(CACHE, f'{name}.serde.json')
- with open(filename, encoding='utf-8') as file:
- data = json.load(file)
-
- renderer = MultiboxRenderer(data)
- renderer.render()
- image = renderer.export()
-
- image.save(os.path.join(CACHE, f'{name}.png'))
-
-
-class MultiboxRenderer:
- def __init__(self, data):
- self.combined = None
-
- self.faces = {}
- for entry in data["faces"]:
- face_id = int(entry[0]["index"]), int(entry[0]["variant"])
- self.faces[face_id] = os.path.join(BASE, '../../', entry[1])
-
- self.layouts = data["layouts"]
-
- def render(self):
- images = []
-
- horizontal = math.floor(math.sqrt(len(self.layouts)))
- start = 1
-
- for layout in self.layouts:
- size = layout["dimensions"]
-
- renderer = BoxRenderer(self.faces, size["x"], size["y"])
- for action in layout["actions"]:
- renderer.execute(action)
-
- images.append(renderer.export())
-
- i = 0
- x = 10
- y = 10
- width = 10
- row_height = 0
-
- positions = []
-
- for image in images:
- positions.append((x, y))
-
- x += 10 + image.width
- row_height = max(row_height, image.height)
-
- i += 1
- if i >= horizontal:
- width = max(width, x)
- x = 10
- y += 10 + row_height
- i = 0
- row_height = 0
-
- height = y
- if i != 0:
- height += 10 + row_height
-
- self.combined = Image.new('RGBA', (width, height))
-
- for (position, image) in zip(positions, images):
- self.combined.paste(image, position)
-
- def export(self):
- return self.combined
-
-
-class BoxRenderer:
- def __init__(self, faces, width, height, grid=False):
- self.faces = faces
- self.size = (pix(width), pix(height))
-
- img = Image.new('RGBA', self.size, (255, 255, 255, 255))
- pixels = numpy.array(img)
-
- if grid:
- for i in range(0, int(height)):
- for j in range(0, int(width)):
- if ((i // 2) % 2 == 0) == ((j // 2) % 2 == 0):
- pixels[4*i:4*(i+1), 4*j:4*(j+1)] = (225, 225, 225, 255)
-
- self.img = Image.fromarray(pixels, 'RGBA')
- self.draw = ImageDraw.Draw(self.img)
- self.cursor = (0, 0)
-
- self.colors = [
- (176, 264, 158),
- (274, 173, 207),
- (158, 252, 264),
- (285, 275, 187),
- (132, 217, 136),
- (236, 177, 246),
- (174, 232, 279),
- (285, 234, 158)
- ]
-
- self.rects = []
- self.color_index = 0
-
- def execute(self, command):
- cmd = command[0]
- args = command[1:]
-
- if cmd == 0:
- self.cursor = [pix(args[0]["x"]), pix(args[0]["y"])]
-
- elif cmd == 1:
- face_id = int(args[0]["index"]), int(args[0]["variant"])
- size = pix(args[1])
- self.font = ImageFont.truetype(self.faces[face_id], size)
-
- elif cmd == 2:
- text = args[0]
- width = self.draw.textsize(text, font=self.font)[0]
- self.draw.text(self.cursor, text, (0, 0, 0, 255), font=self.font)
- self.cursor[0] += width
-
- elif cmd == 3:
- x, y = self.cursor
- w, h = pix(args[0]["x"]), pix(args[0]["y"])
- rect = [x, y, x+w-1, y+h-1]
-
- forbidden_colors = set()
- for other_rect, other_color in self.rects:
- if rect == other_rect:
- return
-
- if overlap(rect, other_rect) or overlap(other_rect, rect):
- forbidden_colors.add(other_color)
-
- for color in self.colors[self.color_index:] + self.colors[:self.color_index]:
- self.color_index = (self.color_index + 1) % len(self.colors)
- if color not in forbidden_colors:
- break
-
- overlay = Image.new('RGBA', self.size, (0, 0, 0, 0))
- draw = ImageDraw.Draw(overlay)
- draw.rectangle(rect, fill=color + (255,))
-
- self.img = Image.alpha_composite(self.img, overlay)
- self.draw = ImageDraw.Draw(self.img)
-
- self.rects.append((rect, color))
-
- else:
- raise Exception('invalid command')
-
- def export(self):
- return self.img
-
-
-# the number of pixels per raw unit
-def pix(raw):
- return int(4 * raw)
-
-def overlap(a, b):
- return (a[0] < b[2] and b[0] < a[2]) and (a[1] < b[3] and b[1] < a[3])
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/src/typeset.rs b/tests/src/typeset.rs
deleted file mode 100644
index ccce8820..00000000
--- a/tests/src/typeset.rs
+++ /dev/null
@@ -1,206 +0,0 @@
-use std::cell::RefCell;
-use std::collections::HashMap;
-use std::error::Error;
-use std::ffi::OsStr;
-use std::fs::{File, create_dir_all, read_dir, read_to_string};
-use std::io::BufWriter;
-use std::panic;
-use std::process::Command;
-use std::rc::Rc;
-use std::time::{Instant, Duration};
-
-use serde::Serialize;
-use futures_executor::block_on;
-
-use typstc::Typesetter;
-use typstc::font::DynProvider;
-use typstc::geom::{Size, Value4};
-use typstc::layout::MultiLayout;
-use typstc::length::Length;
-use typstc::style::PageStyle;
-use typstc::paper::PaperClass;
-use typstc::export::pdf;
-use fontdock::{FaceId, FontLoader};
-use fontdock::fs::{FsIndex, FsProvider};
-
-type DynResult<T> = Result<T, Box<dyn Error>>;
-
-fn main() -> DynResult<()> {
- let opts = Options::parse();
-
- create_dir_all("tests/cache")?;
-
- let tests: Vec<_> = read_dir("tests/")?.collect();
- let mut filtered = Vec::new();
-
- for entry in tests {
- let path = entry?.path();
- if path.extension() != Some(OsStr::new("typ")) {
- continue;
- }
-
- let name = path
- .file_stem().ok_or("expected file stem")?
- .to_string_lossy()
- .to_string();
-
- if opts.matches(&name) {
- let src = read_to_string(&path)?;
- filtered.push((name, src));
- }
- }
-
- let len = filtered.len();
- if len == 0 {
- return Ok(());
- } else if len == 1 {
- println!("Running test ...");
- } else {
- println!("Running {} tests", len);
- }
-
- let mut index = FsIndex::new();
- index.search_dir("fonts");
-
- for (name, src) in filtered {
- panic::catch_unwind(|| {
- if let Err(e) = test(&name, &src, &index) {
- println!("error: {:?}", e);
- }
- }).ok();
- }
-
- Ok(())
-}
-
-/// Create a _PDF_ and render with a name from the source code.
-fn test(name: &str, src: &str, index: &FsIndex) -> DynResult<()> {
- println!("Testing: {}.", name);
-
- let (descriptors, files) = index.clone().into_vecs();
- let provider = FsProvider::new(files.clone());
- let dynamic = Box::new(provider) as Box<DynProvider>;
- let loader = FontLoader::new(dynamic, descriptors);
- let loader = Rc::new(RefCell::new(loader));
- let mut typesetter = Typesetter::new(loader.clone());
-
- typesetter.set_page_style(PageStyle {
- class: PaperClass::Custom,
- dimensions: Size::with_all(Length::pt(250.0).as_raw()),
- margins: Value4::with_all(None),
- });
-
- let layouts = compile(&typesetter, src);
-
- // Write the PDF file.
- let path = format!("tests/cache/{}.pdf", name);
- let file = BufWriter::new(File::create(path)?);
- pdf::export(&layouts, &loader, file)?;
-
- // Compute the font's paths.
- let mut faces = HashMap::new();
- for layout in &layouts {
- for id in layout.find_used_fonts() {
- faces.entry(id).or_insert_with(|| {
- files[id.index][id.variant].0.to_str().unwrap()
- });
- }
- }
-
- #[derive(Serialize)]
- struct Document<'a> {
- faces: Vec<(FaceId, &'a str)>,
- layouts: MultiLayout,
- }
-
- let document = Document { faces: faces.into_iter().collect(), layouts };
-
- // Serialize the document into JSON.
- let path = format!("tests/cache/{}.serde.json", name);
- let file = BufWriter::new(File::create(&path)?);
- serde_json::to_writer(file, &document)?;
-
- // Render the layout into a PNG.
- Command::new("python")
- .arg("tests/src/render.py")
- .arg(name)
- .spawn()
- .expect("failed to run python renderer")
- .wait()
- .expect("command did not run");
-
- std::fs::remove_file(path)?;
-
- Ok(())
-}
-
-/// Compile the source code with the typesetter.
-fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
- if cfg!(debug_assertions) {
- let typeset = block_on(typesetter.typeset(src));
- let diagnostics = typeset.feedback.diagnostics;
-
- if !diagnostics.is_empty() {
- for diagnostic in diagnostics {
- println!(" {:?} {:?}: {}",
- diagnostic.v.level,
- diagnostic.span,
- diagnostic.v.message
- );
- }
- }
-
- typeset.output
- } else {
- fn measure<T>(f: impl FnOnce() -> T) -> (T, Duration) {
- let start = Instant::now();
- let output = f();
- let duration = Instant::now() - start;
- (output, duration)
- };
-
- let (_, cold) = measure(|| block_on(typesetter.typeset(src)));
- let (model, parse) = measure(|| typesetter.parse(src).output);
- let (layouts, layout) = measure(|| block_on(typesetter.layout(&model)).output);
-
- println!(" - cold start: {:?}", cold);
- println!(" - warmed up: {:?}", parse + layout);
- println!(" - parsing: {:?}", parse);
- println!(" - layouting: {:?}", layout);
-
- layouts
- }
-}
-
-/// Command line options.
-struct Options {
- filter: Vec<String>,
- perfect: bool,
-}
-
-impl Options {
- /// Parse the options from the environment arguments.
- fn parse() -> Options {
- let mut perfect = false;
- let mut filter = Vec::new();
-
- for arg in std::env::args().skip(1) {
- match arg.as_str() {
- "--nocapture" => {},
- "=" => perfect = true,
- _ => filter.push(arg),
- }
- }
-
- Options { filter, perfect }
- }
-
- /// Whether a given test should be executed.
- fn matches(&self, name: &str) -> bool {
- match self.perfect {
- true => self.filter.iter().any(|p| name == p),
- false => self.filter.is_empty()
- || self.filter.iter().any(|p| name.contains(p))
- }
- }
-}