Reintroduce --root

This commit is contained in:
Laurenz 2023-06-28 10:35:44 +02:00
parent 7b92bd7c34
commit 04bffc4f12
36 changed files with 208 additions and 159 deletions

View File

@ -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<PathBuf>,
/// Add additional directories to search for fonts
#[clap(
long = "font-path",

View File

@ -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<PathBuf>,
/// 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<PathBuf>,
watch: bool,
root: Option<PathBuf>,
font_paths: Vec<PathBuf>,
watch: bool,
open: Option<Option<String>>,
ppi: Option<f32>,
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<bool> {
fn compile_once(world: &mut SystemWorld, settings: &CompileSettings) -> StrResult<bool> {
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<Self> {
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(),
}
})
}
}

View File

@ -415,7 +415,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html {
return Html::new(format!("<pre>{}</pre>", 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<Bytes> {
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())

View File

@ -1767,8 +1767,7 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Mo
manifest.validate(&spec).at(span)?;
// Evaluate the entry point.
let entrypoint = Path::new("/").join(manifest.package.entrypoint.as_str());
let entrypoint_id = FileId::new(Some(spec), &entrypoint);
let entrypoint_id = manifest_id.join(&manifest.package.entrypoint).at(span)?;
let source = vm.world().source(entrypoint_id).at(span)?;
let point = || Tracepoint::Import;
Ok(eval(vm.world(), vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), &source)

View File

@ -29,28 +29,44 @@ type Pair = &'static (Option<PackageSpec>, 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<PackageSpec>, 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("<detached>")
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}"),
}
}

View File

@ -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();
}

View File

@ -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<RefMut<PathSlot>> {
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<Vec<u8>> {
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)
}
}

View File

@ -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")

View File

@ -7,7 +7,7 @@ A pirate. @arrgh \
#set text(2pt)
#hide[
A @arrgh pirate.
#bibliography("/works.bib")
#bibliography("/files/works.bib")
]
---

View File

@ -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%))

View File

@ -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.

View File

@ -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))
})

View File

@ -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.

View File

@ -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")

View File

@ -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,

View File

@ -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]
---

View File

@ -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.

View File

@ -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.

View File

@ -7,7 +7,7 @@
dx: -10pt,
dy: -10pt,
image(
"/tiger.jpg",
"/files/tiger.jpg",
fit: "cover",
width: 100% + 20pt,
height: 100% + 20pt,

View File

@ -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, \

View File

@ -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%)
)
---

View File

@ -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 $
---

View File

@ -4,4 +4,4 @@
@netwok, @issue201, @arrgh, @quark, @distress,
@glacier-melt, @issue201, @tolkien54, @sharing, @restful
#bibliography("/works.bib")
#bibliography("/files/works.bib")

View File

@ -5,19 +5,24 @@
= Introduction <arrgh>
// 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"))

View File

@ -2,4 +2,4 @@ Hello @netwok
And again: @netwok
#pagebreak()
#bibliography("/works.bib", style: "chicago-notes")
#bibliography("/files/works.bib", style: "chicago-notes")

View File

@ -13,7 +13,7 @@ We can clearly see that @fig-cylinder and
) <tab-basic>
#figure(
pad(y: -6pt, image("/cylinder.svg", height: 2cm)),
pad(y: -6pt, image("/files/cylinder.svg", height: 2cm)),
caption: [The basic shapes.],
numbering: "I",
) <fig-cylinder>
@ -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."
) <fig-image-in-table>

View File

@ -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.]
]

View File

@ -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(

View File

@ -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)))
])
---

View File

@ -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")

View File

@ -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],
)

View File

@ -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",
) <fig1>
#figure(
image("/tiger.jpg", height: 1cm),
image("/files/tiger.jpg", height: 1cm),
caption: [A tiger.],
supplement: "Tig",
) <fig2>

View File

@ -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!

View File

@ -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.

View File

@ -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")

View File

@ -4,6 +4,6 @@
#set page(width: 250pt)
#figure(
image("/diagram.svg"),
image("/files/diagram.svg"),
caption: [A textful diagram],
)