diff --git a/cli/src/args.rs b/cli/src/args.rs index 8c16e14e1..6b9f3df83 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -11,6 +11,10 @@ pub struct CliArguments { #[command(subcommand)] pub command: Command, + /// Configure the project root + #[clap(long = "root", env = "TYPST_ROOT", value_name = "DIR")] + pub root: Option, + /// Add additional directories to search for fonts #[clap( long = "font-path", diff --git a/cli/src/main.rs b/cli/src/main.rs index 52889a7fd..fe86caec8 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -93,6 +93,8 @@ fn typst_version() -> &'static str { /// A summary of the input arguments relevant to compilation. struct CompileSettings { + /// The project's root directory. + root: Option, /// The path to the input file. input: PathBuf, /// The path to the output file. @@ -115,8 +117,9 @@ impl CompileSettings { fn new( input: PathBuf, output: Option, - watch: bool, + root: Option, font_paths: Vec, + watch: bool, open: Option>, ppi: Option, diagnostic_format: DiagnosticFormat, @@ -126,6 +129,7 @@ impl CompileSettings { None => input.with_extension("pdf"), }; Self { + root, input, output, watch, @@ -149,7 +153,16 @@ impl CompileSettings { _ => unreachable!(), }; - Self::new(input, output, watch, args.font_paths, open, ppi, diagnostic_format) + Self::new( + input, + output, + args.root, + args.font_paths, + watch, + open, + ppi, + diagnostic_format, + ) } } @@ -179,22 +192,22 @@ impl FontsSettings { } /// Execute a compilation command. -fn compile(mut command: CompileSettings) -> StrResult<()> { +fn compile(mut settings: CompileSettings) -> StrResult<()> { // Create the world that serves sources, files, and fonts. - let mut world = SystemWorld::new(&command.input, &command.font_paths); + let mut world = SystemWorld::new(&settings)?; // Perform initial compilation. - let ok = compile_once(&mut world, &command)?; + let ok = compile_once(&mut world, &settings)?; // Open the file if requested, this must be done on the first **successful** // compilation. if ok { - if let Some(open) = command.open.take() { - open_file(open.as_deref(), &command.output)?; + if let Some(open) = settings.open.take() { + open_file(open.as_deref(), &settings.output)?; } } - if !command.watch { + if !settings.watch { return Ok(()); } @@ -219,7 +232,7 @@ fn compile(mut command: CompileSettings) -> StrResult<()> { if event .paths .iter() - .all(|path| is_same_file(path, &command.output).unwrap_or(false)) + .all(|path| is_same_file(path, &settings.output).unwrap_or(false)) { continue; } @@ -232,7 +245,7 @@ fn compile(mut command: CompileSettings) -> StrResult<()> { let dependencies = world.dependencies(); // Recompile. - let ok = compile_once(&mut world, &command)?; + let ok = compile_once(&mut world, &settings)?; comemo::evict(10); // Adjust the watching. @@ -241,8 +254,8 @@ fn compile(mut command: CompileSettings) -> StrResult<()> { // Open the file if requested, this must be done on the first // **successful** compilation if ok { - if let Some(open) = command.open.take() { - open_file(open.as_deref(), &command.output)?; + if let Some(open) = settings.open.take() { + open_file(open.as_deref(), &settings.output)?; } } } @@ -253,11 +266,11 @@ fn compile(mut command: CompileSettings) -> StrResult<()> { /// /// Returns whether it compiled without errors. #[tracing::instrument(skip_all)] -fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult { +fn compile_once(world: &mut SystemWorld, settings: &CompileSettings) -> StrResult { tracing::info!("Starting compilation"); let start = std::time::Instant::now(); - status(command, Status::Compiling).unwrap(); + status(settings, Status::Compiling).unwrap(); // Reset everything and ensure that the main file is still present. world.reset(); @@ -269,8 +282,8 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult match result { // Export the PDF / PNG. Ok(document) => { - export(&document, command)?; - status(command, Status::Success(duration)).unwrap(); + export(&document, settings)?; + status(settings, Status::Success(duration)).unwrap(); tracing::info!("Compilation succeeded in {duration:?}"); Ok(true) } @@ -278,8 +291,8 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult // Print diagnostics. Err(errors) => { set_failed(); - status(command, Status::Error).unwrap(); - print_diagnostics(world, *errors, command.diagnostic_format) + status(settings, Status::Error).unwrap(); + print_diagnostics(world, *errors, settings.diagnostic_format) .map_err(|_| "failed to print diagnostics")?; tracing::info!("Compilation failed after {duration:?}"); Ok(false) @@ -288,11 +301,11 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult } /// Export into the target format. -fn export(document: &Document, command: &CompileSettings) -> StrResult<()> { - match command.output.extension() { +fn export(document: &Document, settings: &CompileSettings) -> StrResult<()> { + match settings.output.extension() { Some(ext) if ext.eq_ignore_ascii_case("png") => { // Determine whether we have a `{n}` numbering. - let string = command.output.to_str().unwrap_or_default(); + let string = settings.output.to_str().unwrap_or_default(); let numbered = string.contains("{n}"); if !numbered && document.pages.len() > 1 { bail!("cannot export multiple PNGs without `{{n}}` in output path"); @@ -302,7 +315,7 @@ fn export(document: &Document, command: &CompileSettings) -> StrResult<()> { // first page should be numbered "001" if there are between 100 and // 999 pages. let width = 1 + document.pages.len().checked_ilog10().unwrap_or(0) as usize; - let ppi = command.ppi.unwrap_or(2.0); + let ppi = settings.ppi.unwrap_or(2.0); let mut storage; for (i, frame) in document.pages.iter().enumerate() { @@ -311,14 +324,15 @@ fn export(document: &Document, command: &CompileSettings) -> StrResult<()> { storage = string.replace("{n}", &format!("{:0width$}", i + 1)); Path::new(&storage) } else { - command.output.as_path() + settings.output.as_path() }; pixmap.save_png(path).map_err(|_| "failed to write PNG file")?; } } _ => { let buffer = typst::export::pdf(document); - fs::write(&command.output, buffer).map_err(|_| "failed to write PDF file")?; + fs::write(&settings.output, buffer) + .map_err(|_| "failed to write PDF file")?; } } Ok(()) @@ -326,14 +340,14 @@ fn export(document: &Document, command: &CompileSettings) -> StrResult<()> { /// Clear the terminal and render the status message. #[tracing::instrument(skip_all)] -fn status(command: &CompileSettings, status: Status) -> io::Result<()> { - if !command.watch { +fn status(settings: &CompileSettings, status: Status) -> io::Result<()> { + if !settings.watch { return Ok(()); } let esc = 27 as char; - let input = command.input.display(); - let output = command.output.display(); + let input = settings.input.display(); + let output = settings.output.display(); let time = chrono::offset::Local::now(); let timestamp = time.format("%H:%M:%S"); let message = status.message(); @@ -506,31 +520,43 @@ struct PathSlot { } impl SystemWorld { - fn new(input: &Path, font_paths: &[PathBuf]) -> Self { + fn new(settings: &CompileSettings) -> StrResult { let mut searcher = FontSearcher::new(); - searcher.search(font_paths); + searcher.search(&settings.font_paths); - let root = input - .canonicalize() - .ok() - .as_ref() - .and_then(|path| path.parent()) - .unwrap_or(Path::new(".")) - .to_owned(); + // Resolve the system-global input path. + let system_input = settings.input.canonicalize().map_err(|_| { + eco_format!("input file not found (searched at {})", settings.input.display()) + })?; - let file = input.file_name().unwrap_or(input.as_os_str()); - let main = FileId::new(None, Path::new(file)); + // Resolve the system-global root directory. + let root = { + let path = settings + .root + .as_deref() + .or_else(|| system_input.parent()) + .unwrap_or(Path::new(".")); + path.canonicalize().map_err(|_| { + eco_format!("root directory not found (searched at {})", path.display()) + })? + }; - Self { + // Resolve the input path within the project. + let project_input = system_input + .strip_prefix(&root) + .map(|path| Path::new("/").join(path)) + .map_err(|_| "input file must be contained in project root")?; + + Ok(Self { root, - main, + main: FileId::new(None, &project_input), library: Prehashed::new(typst_library::build()), book: Prehashed::new(searcher.book), fonts: searcher.fonts, hashes: RefCell::default(), paths: RefCell::default(), today: OnceCell::new(), - } + }) } } diff --git a/docs/src/html.rs b/docs/src/html.rs index a6e58b731..e1f294ee4 100644 --- a/docs/src/html.rs +++ b/docs/src/html.rs @@ -415,7 +415,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html { return Html::new(format!("
{}
", highlighted.as_str())); } - let id = FileId::new(None, Path::new("main.typ")); + let id = FileId::new(None, Path::new("/main.typ")); let source = Source::new(id, compile); let world = DocWorld(source); let mut frames = match typst::compile(&world) { @@ -486,7 +486,7 @@ impl World for DocWorld { fn file(&self, id: FileId) -> FileResult { assert!(id.package().is_none()); Ok(FILES - .get_file(id.path()) + .get_file(id.path().strip_prefix("/").unwrap()) .unwrap_or_else(|| panic!("failed to load {:?}", id.path().display())) .contents() .into()) diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 0805f9ccb..fe28e3f3b 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -1767,8 +1767,7 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult, PathBuf); /// Identifies a file. /// -/// This type is interned and thus cheap to clone, compare, and hash. +/// This type is globally interned and thus cheap to copy, compare, and hash. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct FileId(u16); impl FileId { /// Create a new interned file specification. /// - /// Normalizes the path before interning. + /// The path must start with a `/` or this function will panic. + /// Note that the path is normalized before interning. + #[track_caller] pub fn new(package: Option, path: &Path) -> Self { + assert_eq!( + path.components().next(), + Some(std::path::Component::RootDir), + "file path must be absolute within project or package: {}", + path.display(), + ); + + // Try to find an existing entry that we can reuse. let pair = (package, path.normalize()); + if let Some(&id) = INTERNER.read().unwrap().to_id.get(&pair) { + return id; + } + let mut interner = INTERNER.write().unwrap(); - interner.to_id.get(&pair).copied().unwrap_or_else(|| { - let leaked = Box::leak(Box::new(pair)); - let len = interner.from_id.len(); - if len >= usize::from(u16::MAX) { - panic!("too many file specifications"); - } - let id = FileId(len as u16); - interner.to_id.insert(leaked, id); - interner.from_id.push(leaked); - id - }) + let len = interner.from_id.len(); + if len >= usize::from(u16::MAX) { + panic!("too many file specifications"); + } + + // Create a new entry forever by leaking the pair. We can't leak more + // than 2^16 pair (and typically will leak a lot less), so its not a + // big deal. + let id = FileId(len as u16); + let leaked = Box::leak(Box::new(pair)); + interner.to_id.insert(leaked, id); + interner.from_id.push(leaked); + id } /// Get an id that does not identify any real file. @@ -72,11 +88,11 @@ impl FileId { } } - /// The normalized path to the file (within the package if there's a - /// package). + /// The absolute and normalized path to the file _within_ the project or + /// package. pub fn path(&self) -> &'static Path { if self.is_detached() { - Path::new("") + Path::new("/detached.typ") } else { &self.pair().1 } @@ -117,7 +133,7 @@ impl Display for FileId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let path = self.path().display(); match self.package() { - Some(package) => write!(f, "{package}/{path}"), + Some(package) => write!(f, "{package}{path}"), None => write!(f, "{path}"), } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 78c7bedf9..05914b042 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -140,7 +140,6 @@ impl PathExt for Path { match component { Component::CurDir => {} Component::ParentDir => match out.components().next_back() { - Some(Component::RootDir) => {} Some(Component::Normal(_)) => { out.pop(); } diff --git a/tests/src/tests.rs b/tests/src/tests.rs index a2b6e9852..b2a87a5cc 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -37,7 +37,7 @@ const REF_DIR: &str = "ref"; const PNG_DIR: &str = "png"; const PDF_DIR: &str = "pdf"; const FONT_DIR: &str = "../assets/fonts"; -const FILE_DIR: &str = "../assets/files"; +const ASSET_DIR: &str = "../assets"; #[derive(Debug, Clone, Parser)] #[clap(name = "typst-test", author)] @@ -281,7 +281,7 @@ impl World for TestWorld { impl TestWorld { fn set(&mut self, path: &Path, text: String) -> Source { - self.main = FileId::new(None, path); + self.main = FileId::new(None, &Path::new("/").join(path)); let mut slot = self.slot(self.main).unwrap(); let source = Source::new(self.main, text); slot.source = OnceCell::from(Ok(source.clone())); @@ -289,11 +289,9 @@ impl TestWorld { } fn slot(&self, id: FileId) -> FileResult> { - let path = id.path(); let root: PathBuf = match id.package() { Some(spec) => format!("packages/{}-{}", spec.name, spec.version).into(), - None if path.is_relative() => PathBuf::new(), - None => FILE_DIR.into(), + None => PathBuf::new(), }; let system_path = root.join_rooted(id.path()).ok_or(FileError::AccessDenied)?; @@ -310,16 +308,18 @@ impl TestWorld { /// Read as file. fn read(path: &Path) -> FileResult> { - let suffix = path - .strip_prefix(FILE_DIR) - .map(|suffix| Path::new("/").join(suffix)) - .unwrap_or_else(|_| path.into()); + // Basically symlinks `assets/files` to `tests/files` so that the assets + // are within the test project root. + let mut resolved = path.to_path_buf(); + if path.starts_with("files/") { + resolved = Path::new(ASSET_DIR).join(path); + } - let f = |e| FileError::from_io(e, &suffix); - if fs::metadata(path).map_err(f)?.is_dir() { + let f = |e| FileError::from_io(e, path); + if fs::metadata(&resolved).map_err(f)?.is_dir() { Err(FileError::IsDirectory) } else { - fs::read(path).map_err(f) + fs::read(&resolved).map_err(f) } } diff --git a/tests/typ/bugs/cite-locate.typ b/tests/typ/bugs/cite-locate.typ index c3786c5fc..4eb95b54e 100644 --- a/tests/typ/bugs/cite-locate.typ +++ b/tests/typ/bugs/cite-locate.typ @@ -20,4 +20,4 @@ #locate(loc => [Citation @distress on page #loc.page()]) #pagebreak() -#bibliography("/works.bib", style: "chicago-notes") +#bibliography("/files/works.bib", style: "chicago-notes") diff --git a/tests/typ/bugs/hide-meta.typ b/tests/typ/bugs/hide-meta.typ index 6dce81ad6..10c95e593 100644 --- a/tests/typ/bugs/hide-meta.typ +++ b/tests/typ/bugs/hide-meta.typ @@ -7,7 +7,7 @@ A pirate. @arrgh \ #set text(2pt) #hide[ A @arrgh pirate. - #bibliography("/works.bib") + #bibliography("/files/works.bib") ] --- diff --git a/tests/typ/coma.typ b/tests/typ/coma.typ index 7ec2c4105..32a683088 100644 --- a/tests/typ/coma.typ +++ b/tests/typ/coma.typ @@ -21,4 +21,4 @@ Die Tiefe eines Knotens _v_ ist die Länge des eindeutigen Weges von der Wurzel zu _v_, und die Höhe von _v_ ist die Länge eines längsten (absteigenden) Weges von _v_ zu einem Blatt. Die Höhe des Baumes ist die Höhe der Wurzel. -#align(center, image("/graph.png", width: 75%)) +#align(center, image("/files/graph.png", width: 75%)) diff --git a/tests/typ/compiler/import.typ b/tests/typ/compiler/import.typ index 0f1617668..7f6baee01 100644 --- a/tests/typ/compiler/import.typ +++ b/tests/typ/compiler/import.typ @@ -112,8 +112,8 @@ --- // Some non-text stuff. -// Error: 9-21 file is not valid utf-8 -#import "/rhino.png" +// Error: 9-27 file is not valid utf-8 +#import "/files/rhino.png" --- // Unresolved import. diff --git a/tests/typ/compiler/set.typ b/tests/typ/compiler/set.typ index 99bb90d0d..b317e60c8 100644 --- a/tests/typ/compiler/set.typ +++ b/tests/typ/compiler/set.typ @@ -40,7 +40,7 @@ Hello *#x* // Test relative path resolving in layout phase. #let choice = ("monkey.svg", "rhino.png", "tiger.jpg") #set enum(numbering: n => { - let path = "/" + choice.at(n - 1) + let path = "/files/" + choice.at(n - 1) move(dy: -0.15em, image(path, width: 1em, height: 1em)) }) diff --git a/tests/typ/compiler/show-text.typ b/tests/typ/compiler/show-text.typ index 0f978563a..e635574c0 100644 --- a/tests/typ/compiler/show-text.typ +++ b/tests/typ/compiler/show-text.typ @@ -54,6 +54,6 @@ World --- // Test absolute path in layout phase. -#show "GRAPH": image("/graph.png") +#show "GRAPH": image("/files/graph.png") The GRAPH has nodes. diff --git a/tests/typ/compute/data.typ b/tests/typ/compute/data.typ index d67b7aeff..f13f7da81 100644 --- a/tests/typ/compute/data.typ +++ b/tests/typ/compute/data.typ @@ -3,22 +3,22 @@ --- // Test reading plain text files -#let data = read("/hello.txt") +#let data = read("/files/hello.txt") #test(data, "Hello, world!") --- -// Error: 18-32 file not found (searched at /missing.txt) -#let data = read("/missing.txt") +// Error: 18-38 file not found (searched at files/missing.txt) +#let data = read("/files/missing.txt") --- -// Error: 18-28 file is not valid utf-8 -#let data = read("/bad.txt") +// Error: 18-34 file is not valid utf-8 +#let data = read("/files/bad.txt") --- // Test reading CSV data. // Ref: true #set page(width: auto) -#let data = csv("/zoo.csv") +#let data = csv("/files/zoo.csv") #let cells = data.at(0).map(strong) + data.slice(1).flatten() #table(columns: data.at(0).len(), ..cells) @@ -27,23 +27,23 @@ #csv("nope.csv") --- -// Error: 6-16 failed to parse csv file: found 3 instead of 2 fields in line 3 -#csv("/bad.csv") +// Error: 6-22 failed to parse csv file: found 3 instead of 2 fields in line 3 +#csv("/files/bad.csv") --- // Test reading JSON data. -#let data = json("/zoo.json") +#let data = json("/files/zoo.json") #test(data.len(), 3) #test(data.at(0).name, "Debby") #test(data.at(2).weight, 150) --- -// Error: 7-18 failed to parse json file: syntax error in line 3 -#json("/bad.json") +// Error: 7-24 failed to parse json file: syntax error in line 3 +#json("/files/bad.json") --- // Test reading TOML data. -#let data = toml("/toml-types.toml") +#let data = toml("/files/toml-types.toml") #test(data.string, "wonderful") #test(data.integer, 42) #test(data.float, 3.14) @@ -62,12 +62,12 @@ )) --- -// Error: 7-18 failed to parse toml file: expected `.`, `=`, index 15-15 -#toml("/bad.toml") +// Error: 7-24 failed to parse toml file: expected `.`, `=`, index 15-15 +#toml("/files/bad.toml") --- // Test reading YAML data -#let data = yaml("/yaml-types.yaml") +#let data = yaml("/files/yaml-types.yaml") #test(data.len(), 7) #test(data.null_key, (none, none)) #test(data.string, "text") @@ -80,12 +80,12 @@ --- --- -// Error: 7-18 failed to parse yaml file: while parsing a flow sequence, expected ',' or ']' at line 2 column 1 -#yaml("/bad.yaml") +// Error: 7-24 failed to parse yaml file: while parsing a flow sequence, expected ',' or ']' at line 2 column 1 +#yaml("/files/bad.yaml") --- // Test reading XML data. -#let data = xml("/data.xml") +#let data = xml("/files/data.xml") #test(data, (( tag: "data", attrs: (:), @@ -109,5 +109,5 @@ ),)) --- -// Error: 6-16 failed to parse xml file: found closing tag 'data' instead of 'hello' in line 3 -#xml("/bad.xml") +// Error: 6-22 failed to parse xml file: found closing tag 'data' instead of 'hello' in line 3 +#xml("/files/bad.xml") diff --git a/tests/typ/layout/grid-3.typ b/tests/typ/layout/grid-3.typ index ee79f2fde..89bcb90f1 100644 --- a/tests/typ/layout/grid-3.typ +++ b/tests/typ/layout/grid-3.typ @@ -23,7 +23,7 @@ columns: 4 * (1fr,), row-gutter: 10pt, column-gutter: (0pt, 10%), - align(top, image("/rhino.png")), + align(top, image("/files/rhino.png")), align(top, rect(inset: 0pt, fill: eastern, align(right)[LoL])), [rofl], [\ A] * 3, diff --git a/tests/typ/layout/pad.typ b/tests/typ/layout/pad.typ index 88e8510f9..f7b47142c 100644 --- a/tests/typ/layout/pad.typ +++ b/tests/typ/layout/pad.typ @@ -22,7 +22,7 @@ Hi #box(pad(left: 10pt)[A]) there // Test that the pad element doesn't consume the whole region. #set page(height: 6cm) #align(left)[Before] -#pad(10pt, image("/tiger.jpg")) +#pad(10pt, image("/files/tiger.jpg")) #align(right)[After] --- diff --git a/tests/typ/layout/par-bidi.typ b/tests/typ/layout/par-bidi.typ index 90c6a55ec..ea3711ab6 100644 --- a/tests/typ/layout/par-bidi.typ +++ b/tests/typ/layout/par-bidi.typ @@ -41,7 +41,7 @@ Lריווח #h(1cm) R --- // Test inline object. #set text(lang: "he") -קרנפיםRh#box(image("/rhino.png", height: 11pt))inoחיים +קרנפיםRh#box(image("/files/rhino.png", height: 11pt))inoחיים --- // Test whether L1 whitespace resetting destroys stuff. diff --git a/tests/typ/layout/par-indent.typ b/tests/typ/layout/par-indent.typ index 787891816..324ff44a4 100644 --- a/tests/typ/layout/par-indent.typ +++ b/tests/typ/layout/par-indent.typ @@ -9,10 +9,10 @@ The first paragraph has no indent. But the second one does. -#box(image("/tiger.jpg", height: 6pt)) +#box(image("/files/tiger.jpg", height: 6pt)) starts a paragraph, also with indent. -#align(center, image("/rhino.png", width: 1cm)) +#align(center, image("/files/rhino.png", width: 1cm)) = Headings - And lists. diff --git a/tests/typ/layout/place-background.typ b/tests/typ/layout/place-background.typ index 6f480b93a..3a7a06b92 100644 --- a/tests/typ/layout/place-background.typ +++ b/tests/typ/layout/place-background.typ @@ -7,7 +7,7 @@ dx: -10pt, dy: -10pt, image( - "/tiger.jpg", + "/files/tiger.jpg", fit: "cover", width: 100% + 20pt, height: 100% + 20pt, diff --git a/tests/typ/layout/place.typ b/tests/typ/layout/place.typ index f0ffaf576..6f79e3e13 100644 --- a/tests/typ/layout/place.typ +++ b/tests/typ/layout/place.typ @@ -5,7 +5,7 @@ #place(bottom + center)[© Typst] = Placement -#place(right, image("/tiger.jpg", width: 1.8cm)) +#place(right, image("/files/tiger.jpg", width: 1.8cm)) Hi there. This is \ a placed element. \ Unfortunately, \ diff --git a/tests/typ/layout/transform.typ b/tests/typ/layout/transform.typ index 22a945321..3a674282e 100644 --- a/tests/typ/layout/transform.typ +++ b/tests/typ/layout/transform.typ @@ -31,13 +31,13 @@ nor #xetex! // Test combination of scaling and rotation. #set page(height: 80pt) #align(center + horizon, - rotate(20deg, scale(70%, image("/tiger.jpg"))) + rotate(20deg, scale(70%, image("/files/tiger.jpg"))) ) --- // Test setting rotation origin. #rotate(10deg, origin: top + left, - image("/tiger.jpg", width: 50%) + image("/files/tiger.jpg", width: 50%) ) --- diff --git a/tests/typ/math/content.typ b/tests/typ/math/content.typ index 765e0ec32..5a51901a9 100644 --- a/tests/typ/math/content.typ +++ b/tests/typ/math/content.typ @@ -2,7 +2,7 @@ --- // Test images and font fallback. -#let monkey = move(dy: 0.2em, image("/monkey.svg", height: 1em)) +#let monkey = move(dy: 0.2em, image("/files/monkey.svg", height: 1em)) $ sum_(i=#emoji.apple)^#emoji.apple.red i + monkey/2 $ --- diff --git a/tests/typ/meta/bibliography-ordering.typ b/tests/typ/meta/bibliography-ordering.typ index c1c9880b1..5a00c51e1 100644 --- a/tests/typ/meta/bibliography-ordering.typ +++ b/tests/typ/meta/bibliography-ordering.typ @@ -4,4 +4,4 @@ @netwok, @issue201, @arrgh, @quark, @distress, @glacier-melt, @issue201, @tolkien54, @sharing, @restful -#bibliography("/works.bib") +#bibliography("/files/works.bib") diff --git a/tests/typ/meta/bibliography.typ b/tests/typ/meta/bibliography.typ index 94b443467..40d281fa2 100644 --- a/tests/typ/meta/bibliography.typ +++ b/tests/typ/meta/bibliography.typ @@ -5,19 +5,24 @@ = Introduction // Error: 1-7 label occurs in the document and its bibliography @arrgh -#bibliography("/works.bib") +#bibliography("/files/works.bib") --- #set page(width: 200pt) = Details See also #cite("arrgh", "distress", [p. 22]), @arrgh[p. 4], and @distress[p. 5]. -#bibliography("/works.bib") +#bibliography("/files/works.bib") --- // Test unconventional order. #set page(width: 200pt) -#bibliography("/works.bib", title: [Works to be cited], style: "chicago-author-date") +#bibliography( + "/files/works.bib", + title: [Works to be cited], + style: "chicago-author-date", +) #line(length: 100%) + #[#set cite(brackets: false) As described by @netwok], the net-work is a creature of its own. @@ -25,8 +30,8 @@ This is close to piratery! @arrgh And quark! @quark --- -// Error: 15-43 duplicate bibliography keys: arrgh, distress, glacier-melt, issue201, mcintosh_anxiety, netwok, psychology25, quark, restful, sharing, tolkien54 -#bibliography(("/works.bib", "/works.bib")) +// Error: 15-55 duplicate bibliography keys: arrgh, distress, glacier-melt, issue201, mcintosh_anxiety, netwok, psychology25, quark, restful, sharing, tolkien54 +#bibliography(("/files/works.bib", "/files/works.bib")) --- #set page(width: 200pt) @@ -35,4 +40,4 @@ And quark! @quark = Multiple Bibs Now we have multiple bibliographies containing #cite("glacier-melt", "keshav2007read") -#bibliography(("/works.bib", "/works_too.bib")) +#bibliography(("/files/works.bib", "/files/works_too.bib")) diff --git a/tests/typ/meta/cite-footnote.typ b/tests/typ/meta/cite-footnote.typ index 03984752e..071a68d7d 100644 --- a/tests/typ/meta/cite-footnote.typ +++ b/tests/typ/meta/cite-footnote.typ @@ -2,4 +2,4 @@ Hello @netwok And again: @netwok #pagebreak() -#bibliography("/works.bib", style: "chicago-notes") +#bibliography("/files/works.bib", style: "chicago-notes") diff --git a/tests/typ/meta/figure.typ b/tests/typ/meta/figure.typ index fcf927403..48dea0e84 100644 --- a/tests/typ/meta/figure.typ +++ b/tests/typ/meta/figure.typ @@ -13,7 +13,7 @@ We can clearly see that @fig-cylinder and ) #figure( - pad(y: -6pt, image("/cylinder.svg", height: 2cm)), + pad(y: -6pt, image("/files/cylinder.svg", height: 2cm)), caption: [The basic shapes.], numbering: "I", ) @@ -30,7 +30,7 @@ We can clearly see that @fig-cylinder and table( columns: 2, [Second cylinder], - image("/cylinder.svg"), + image("/files/cylinder.svg"), ), caption: "A table containing images." ) diff --git a/tests/typ/meta/footnote-container.typ b/tests/typ/meta/footnote-container.typ index fa2461622..562831a92 100644 --- a/tests/typ/meta/footnote-container.typ +++ b/tests/typ/meta/footnote-container.typ @@ -4,7 +4,7 @@ // Test footnote in caption. Read the docs #footnote[https://typst.app/docs]! #figure( - image("/graph.png", width: 70%), + image("/files/graph.png", width: 70%), caption: [ A graph #footnote[A _graph_ is a structure with nodes and edges.] ] diff --git a/tests/typ/meta/footnote-table.typ b/tests/typ/meta/footnote-table.typ index bfbc3779c..8256dc586 100644 --- a/tests/typ/meta/footnote-table.typ +++ b/tests/typ/meta/footnote-table.typ @@ -12,7 +12,7 @@ [This cell #footnote[This footnote is not on the same page] breaks over multiple pages.], - image("/tiger.jpg"), + image("/files/tiger.jpg"), ) #table( diff --git a/tests/typ/meta/link.typ b/tests/typ/meta/link.typ index 37eba6b7f..5dfe37e7c 100644 --- a/tests/typ/meta/link.typ +++ b/tests/typ/meta/link.typ @@ -54,7 +54,7 @@ My cool #box(move(dx: 0.7cm, dy: 0.7cm, rotate(10deg, scale(200%, mylink)))) // Link containing a block. #link("https://example.com/", block[ My cool rhino - #box(move(dx: 10pt, image("/rhino.png", width: 1cm))) + #box(move(dx: 10pt, image("/files/rhino.png", width: 1cm))) ]) --- diff --git a/tests/typ/meta/outline-entry.typ b/tests/typ/meta/outline-entry.typ index ac78ec2aa..501474429 100644 --- a/tests/typ/meta/outline-entry.typ +++ b/tests/typ/meta/outline-entry.typ @@ -57,4 +57,4 @@ // Error: 2-23 cannot outline cite #outline(target: cite) #cite("arrgh", "distress", [p. 22]) -#bibliography("/works.bib") +#bibliography("/files/works.bib") diff --git a/tests/typ/meta/query-figure.typ b/tests/typ/meta/query-figure.typ index 4b7307708..b1e59abee 100644 --- a/tests/typ/meta/query-figure.typ +++ b/tests/typ/meta/query-figure.typ @@ -24,7 +24,7 @@ }) #figure( - image("/glacier.jpg"), + image("/files/glacier.jpg"), caption: [Glacier melting], ) @@ -36,6 +36,6 @@ ) #figure( - image("/tiger.jpg"), + image("/files/tiger.jpg"), caption: [Tiger world], ) diff --git a/tests/typ/meta/ref.typ b/tests/typ/meta/ref.typ index a62e6ccec..630bad614 100644 --- a/tests/typ/meta/ref.typ +++ b/tests/typ/meta/ref.typ @@ -26,13 +26,13 @@ As seen in @intro, we proceed. = Intro #figure( - image("/cylinder.svg", height: 1cm), + image("/files/cylinder.svg", height: 1cm), caption: [A cylinder.], supplement: "Fig", ) #figure( - image("/tiger.jpg", height: 1cm), + image("/files/tiger.jpg", height: 1cm), caption: [A tiger.], supplement: "Tig", ) diff --git a/tests/typ/text/baseline.typ b/tests/typ/text/baseline.typ index c19e3c636..e00e9230b 100644 --- a/tests/typ/text/baseline.typ +++ b/tests/typ/text/baseline.typ @@ -1,12 +1,12 @@ -// Test baseline handling. - ---- -Hi #text(1.5em)[You], #text(0.75em)[how are you?] - -Our cockatoo was one of the -#text(baseline: -0.2em)[#box(circle(radius: 2pt)) first] -#text(baseline: 0.2em)[birds #box(circle(radius: 2pt))] -that ever learned to mimic a human voice. - ---- -Hey #box(baseline: 40%, image("/tiger.jpg", width: 1.5cm)) there! +// Test baseline handling. + +--- +Hi #text(1.5em)[You], #text(0.75em)[how are you?] + +Our cockatoo was one of the +#text(baseline: -0.2em)[#box(circle(radius: 2pt)) first] +#text(baseline: 0.2em)[birds #box(circle(radius: 2pt))] +that ever learned to mimic a human voice. + +--- +Hey #box(baseline: 40%, image("/files/tiger.jpg", width: 1.5cm)) there! diff --git a/tests/typ/text/linebreak-obj.typ b/tests/typ/text/linebreak-obj.typ index c0a74a1e4..50254df10 100644 --- a/tests/typ/text/linebreak-obj.typ +++ b/tests/typ/text/linebreak-obj.typ @@ -7,7 +7,7 @@ They can look for the details in @netwok, which is the authoritative source. -#bibliography("/works.bib") +#bibliography("/files/works.bib") --- // Test punctuation after math equations. diff --git a/tests/typ/visualize/image.typ b/tests/typ/visualize/image.typ index 7891e7e2d..e3bcc64fb 100644 --- a/tests/typ/visualize/image.typ +++ b/tests/typ/visualize/image.typ @@ -4,24 +4,24 @@ // Test loading different image formats. // Load an RGBA PNG image. -#image("/rhino.png") +#image("/files/rhino.png") // Load an RGB JPEG image. #set page(height: 60pt) -#image("/tiger.jpg") +#image("../../files/tiger.jpg") --- // Test configuring the size and fitting behaviour of images. // Set width and height explicitly. -#box(image("/rhino.png", width: 30pt)) -#box(image("/rhino.png", height: 30pt)) +#box(image("/files/rhino.png", width: 30pt)) +#box(image("/files/rhino.png", height: 30pt)) // Set width and height explicitly and force stretching. -#image("/monkey.svg", width: 100%, height: 20pt, fit: "stretch") +#image("/files/monkey.svg", width: 100%, height: 20pt, fit: "stretch") // Make sure the bounding-box of the image is correct. -#align(bottom + right, image("/tiger.jpg", width: 40pt, alt: "A tiger")) +#align(bottom + right, image("/files/tiger.jpg", width: 40pt, alt: "A tiger")) --- // Test all three fit modes. @@ -30,24 +30,24 @@ columns: (1fr, 1fr, 1fr), rows: 100%, gutter: 3pt, - image("/tiger.jpg", width: 100%, height: 100%, fit: "contain"), - image("/tiger.jpg", width: 100%, height: 100%, fit: "cover"), - image("/monkey.svg", width: 100%, height: 100%, fit: "stretch"), + image("/files/tiger.jpg", width: 100%, height: 100%, fit: "contain"), + image("/files/tiger.jpg", width: 100%, height: 100%, fit: "cover"), + image("/files/monkey.svg", width: 100%, height: 100%, fit: "stretch"), ) --- // Does not fit to remaining height of page. #set page(height: 60pt) Stuff -#image("/rhino.png") +#image("/files/rhino.png") --- // Test baseline. -A #box(image("/tiger.jpg", height: 1cm, width: 80%)) B +A #box(image("/files/tiger.jpg", height: 1cm, width: 80%)) B --- // Test advanced SVG features. -#image("/pattern.svg") +#image("/files/pattern.svg") --- // Error: 8-29 file not found (searched at typ/visualize/path/does/not/exist) @@ -58,5 +58,5 @@ A #box(image("/tiger.jpg", height: 1cm, width: 80%)) B #image("./image.typ") --- -// Error: 2-19 failed to parse svg: found closing tag 'g' instead of 'style' in line 4 -#image("/bad.svg") +// Error: 2-25 failed to parse svg: found closing tag 'g' instead of 'style' in line 4 +#image("/files/bad.svg") diff --git a/tests/typ/visualize/svg-text.typ b/tests/typ/visualize/svg-text.typ index 132905a88..838f73605 100644 --- a/tests/typ/visualize/svg-text.typ +++ b/tests/typ/visualize/svg-text.typ @@ -4,6 +4,6 @@ #set page(width: 250pt) #figure( - image("/diagram.svg"), + image("/files/diagram.svg"), caption: [A textful diagram], )