Typst is a new markup-based typesetting system that is designed to be as powerful
@@ -39,7 +39,7 @@ A [gentle introduction][tutorial] to Typst is available in our documentation.
However, if you want to see the power of Typst encapsulated in one image, here
it is:
-
+
diff --git a/crates/typst-cli/src/args.rs b/crates/typst-cli/src/args.rs
index 83c4c8f9e..d6855d100 100644
--- a/crates/typst-cli/src/args.rs
+++ b/crates/typst-cli/src/args.rs
@@ -473,6 +473,9 @@ pub enum PdfStandard {
/// PDF/A-2b.
#[value(name = "a-2b")]
A_2b,
+ /// PDF/A-3b.
+ #[value(name = "a-3b")]
+ A_3b,
}
display_possible_values!(PdfStandard);
diff --git a/crates/typst-cli/src/compile.rs b/crates/typst-cli/src/compile.rs
index adeef0f2d..515a777a2 100644
--- a/crates/typst-cli/src/compile.rs
+++ b/crates/typst-cli/src/compile.rs
@@ -136,6 +136,7 @@ impl CompileConfig {
.map(|standard| match standard {
PdfStandard::V_1_7 => typst_pdf::PdfStandard::V_1_7,
PdfStandard::A_2b => typst_pdf::PdfStandard::A_2b,
+ PdfStandard::A_3b => typst_pdf::PdfStandard::A_3b,
})
.collect::>();
PdfStandards::new(&list)?
diff --git a/crates/typst-cli/src/world.rs b/crates/typst-cli/src/world.rs
index af6cf228f..12e80d273 100644
--- a/crates/typst-cli/src/world.rs
+++ b/crates/typst-cli/src/world.rs
@@ -305,7 +305,7 @@ impl FileSlot {
) -> FileResult {
self.file.get_or_init(
|| read(self.id, project_root, package_storage),
- |data, _| Ok(data.into()),
+ |data, _| Ok(Bytes::new(data)),
)
}
}
diff --git a/crates/typst-eval/src/call.rs b/crates/typst-eval/src/call.rs
index fc934cef5..f59235c78 100644
--- a/crates/typst-eval/src/call.rs
+++ b/crates/typst-eval/src/call.rs
@@ -7,12 +7,11 @@ use typst_library::diag::{
use typst_library::engine::{Engine, Sink, Traced};
use typst_library::foundations::{
Arg, Args, Bytes, Capturer, Closure, Content, Context, Func, IntoValue,
- NativeElement, Scope, Scopes, Value,
+ NativeElement, Scope, Scopes, SymbolElem, Value,
};
use typst_library::introspection::Introspector;
use typst_library::math::LrElem;
use typst_library::routines::Routines;
-use typst_library::text::TextElem;
use typst_library::World;
use typst_syntax::ast::{self, AstNode, Ident};
use typst_syntax::{Span, Spanned, SyntaxNode};
@@ -325,6 +324,13 @@ fn eval_field_call(
} else if let Some(callee) = target.ty().scope().get(&field) {
args.insert(0, target_expr.span(), target);
Ok(FieldCall::Normal(callee.clone(), args))
+ } else if let Value::Content(content) = &target {
+ if let Some(callee) = content.elem().scope().get(&field) {
+ args.insert(0, target_expr.span(), target);
+ Ok(FieldCall::Normal(callee.clone(), args))
+ } else {
+ bail!(missing_field_call_error(target, field))
+ }
} else if matches!(
target,
Value::Symbol(_) | Value::Func(_) | Value::Type(_) | Value::Module(_)
@@ -341,8 +347,20 @@ fn eval_field_call(
/// Produce an error when we cannot call the field.
fn missing_field_call_error(target: Value, field: Ident) -> SourceDiagnostic {
- let mut error =
- error!(field.span(), "type {} has no method `{}`", target.ty(), field.as_str());
+ let mut error = match &target {
+ Value::Content(content) => error!(
+ field.span(),
+ "element {} has no method `{}`",
+ content.elem().name(),
+ field.as_str(),
+ ),
+ _ => error!(
+ field.span(),
+ "type {} has no method `{}`",
+ target.ty(),
+ field.as_str()
+ ),
+ };
match target {
Value::Dict(ref dict) if matches!(dict.get(&field), Ok(Value::Func(_))) => {
@@ -360,6 +378,7 @@ fn missing_field_call_error(target: Value, field: Ident) -> SourceDiagnostic {
}
_ => {}
}
+
error
}
@@ -382,16 +401,16 @@ fn wrap_args_in_math(
let mut body = Content::empty();
for (i, arg) in args.all::()?.into_iter().enumerate() {
if i > 0 {
- body += TextElem::packed(',');
+ body += SymbolElem::packed(',');
}
body += arg;
}
if trailing_comma {
- body += TextElem::packed(',');
+ body += SymbolElem::packed(',');
}
Ok(Value::Content(
callee.display().spanned(callee_span)
- + LrElem::new(TextElem::packed('(') + body + TextElem::packed(')'))
+ + LrElem::new(SymbolElem::packed('(') + body + SymbolElem::packed(')'))
.pack()
.spanned(args.span),
))
@@ -685,8 +704,7 @@ mod tests {
// Named-params.
test(s, "$ foo(bar: y) $", &["foo"]);
- // This should be updated when we improve named-param parsing:
- test(s, "$ foo(x-y: 1, bar-z: 2) $", &["bar", "foo"]);
+ test(s, "$ foo(x-y: 1, bar-z: 2) $", &["foo"]);
// Field access in math.
test(s, "$ foo.bar $", &["foo"]);
diff --git a/crates/typst-eval/src/code.rs b/crates/typst-eval/src/code.rs
index 34373fd4a..2baf4ea9e 100644
--- a/crates/typst-eval/src/code.rs
+++ b/crates/typst-eval/src/code.rs
@@ -99,6 +99,7 @@ impl Eval for ast::Expr<'_> {
Self::Term(v) => v.eval(vm).map(Value::Content),
Self::Equation(v) => v.eval(vm).map(Value::Content),
Self::Math(v) => v.eval(vm).map(Value::Content),
+ Self::MathText(v) => v.eval(vm).map(Value::Content),
Self::MathIdent(v) => v.eval(vm),
Self::MathShorthand(v) => v.eval(vm),
Self::MathAlignPoint(v) => v.eval(vm).map(Value::Content),
diff --git a/crates/typst-eval/src/import.rs b/crates/typst-eval/src/import.rs
index 5b67c0608..2060d25f1 100644
--- a/crates/typst-eval/src/import.rs
+++ b/crates/typst-eval/src/import.rs
@@ -211,7 +211,7 @@ fn resolve_package(
// Evaluate the manifest.
let manifest_id = FileId::new(Some(spec.clone()), VirtualPath::new("typst.toml"));
let bytes = engine.world.file(manifest_id).at(span)?;
- let string = std::str::from_utf8(&bytes).map_err(FileError::from).at(span)?;
+ let string = bytes.as_str().map_err(FileError::from).at(span)?;
let manifest: PackageManifest = toml::from_str(string)
.map_err(|err| eco_format!("package manifest is malformed ({})", err.message()))
.at(span)?;
diff --git a/crates/typst-eval/src/math.rs b/crates/typst-eval/src/math.rs
index 51dc0a3d5..bfb54aa87 100644
--- a/crates/typst-eval/src/math.rs
+++ b/crates/typst-eval/src/math.rs
@@ -1,11 +1,11 @@
use ecow::eco_format;
use typst_library::diag::{At, SourceResult};
-use typst_library::foundations::{Content, NativeElement, Symbol, Value};
+use typst_library::foundations::{Content, NativeElement, Symbol, SymbolElem, Value};
use typst_library::math::{
AlignPointElem, AttachElem, FracElem, LrElem, PrimesElem, RootElem,
};
use typst_library::text::TextElem;
-use typst_syntax::ast::{self, AstNode};
+use typst_syntax::ast::{self, AstNode, MathTextKind};
use crate::{Eval, Vm};
@@ -20,6 +20,17 @@ impl Eval for ast::Math<'_> {
}
}
+impl Eval for ast::MathText<'_> {
+ type Output = Content;
+
+ fn eval(self, _: &mut Vm) -> SourceResult {
+ match self.get() {
+ MathTextKind::Character(c) => Ok(SymbolElem::packed(c)),
+ MathTextKind::Number(text) => Ok(TextElem::packed(text.clone())),
+ }
+ }
+}
+
impl Eval for ast::MathIdent<'_> {
type Output = Value;
@@ -102,6 +113,7 @@ impl Eval for ast::MathRoot<'_> {
type Output = Content;
fn eval(self, vm: &mut Vm) -> SourceResult {
+ // Use `TextElem` to match `MathTextKind::Number` above.
let index = self.index().map(|i| TextElem::packed(eco_format!("{i}")));
let radicand = self.radicand().eval_display(vm)?;
Ok(RootElem::new(radicand).with_index(index).pack())
diff --git a/crates/typst-html/src/encode.rs b/crates/typst-html/src/encode.rs
index b87b0e1d6..612f923fc 100644
--- a/crates/typst-html/src/encode.rs
+++ b/crates/typst-html/src/encode.rs
@@ -2,7 +2,7 @@ use std::fmt::Write;
use typst_library::diag::{bail, At, SourceResult, StrResult};
use typst_library::foundations::Repr;
-use typst_library::html::{charsets, tag, HtmlDocument, HtmlElement, HtmlNode};
+use typst_library::html::{charsets, tag, HtmlDocument, HtmlElement, HtmlNode, HtmlTag};
use typst_library::layout::Frame;
use typst_syntax::Span;
@@ -12,15 +12,19 @@ pub fn html(document: &HtmlDocument) -> SourceResult {
w.buf.push_str("");
write_indent(&mut w);
write_element(&mut w, &document.root)?;
+ if w.pretty {
+ w.buf.push('\n');
+ }
Ok(w.buf)
}
#[derive(Default)]
struct Writer {
+ /// The output buffer.
buf: String,
- /// current indentation level
+ /// The current indentation level
level: usize,
- /// pretty printing enabled?
+ /// Whether pretty printing is enabled.
pretty: bool,
}
@@ -85,26 +89,32 @@ fn write_element(w: &mut Writer, element: &HtmlElement) -> SourceResult<()> {
let pretty = w.pretty;
if !element.children.is_empty() {
- w.pretty &= is_pretty(element);
+ let pretty_inside = allows_pretty_inside(element.tag)
+ && element.children.iter().any(|node| match node {
+ HtmlNode::Element(child) => wants_pretty_around(child.tag),
+ _ => false,
+ });
+
+ w.pretty &= pretty_inside;
let mut indent = w.pretty;
w.level += 1;
for c in &element.children {
- let pretty_child = match c {
+ let pretty_around = match c {
HtmlNode::Tag(_) => continue,
- HtmlNode::Element(element) => is_pretty(element),
+ HtmlNode::Element(child) => w.pretty && wants_pretty_around(child.tag),
HtmlNode::Text(..) | HtmlNode::Frame(_) => false,
};
- if core::mem::take(&mut indent) || pretty_child {
+ if core::mem::take(&mut indent) || pretty_around {
write_indent(w);
}
write_node(w, c)?;
- indent = pretty_child;
+ indent = pretty_around;
}
w.level -= 1;
- write_indent(w)
+ write_indent(w);
}
w.pretty = pretty;
@@ -115,9 +125,27 @@ fn write_element(w: &mut Writer, element: &HtmlElement) -> SourceResult<()> {
Ok(())
}
-/// Whether the element should be pretty-printed.
-fn is_pretty(element: &HtmlElement) -> bool {
- tag::is_block_by_default(element.tag) || matches!(element.tag, tag::meta)
+/// Whether we are allowed to add an extra newline at the start and end of the
+/// element's contents.
+///
+/// Technically, users can change CSS `display` properties such that the
+/// insertion of whitespace may actually impact the visual output. For example,
+/// shows how adding CSS
+/// rules to `
` can make it sensitive to whitespace. For this reason, we
+/// should also respect the `style` tag in the future.
+fn allows_pretty_inside(tag: HtmlTag) -> bool {
+ (tag::is_block_by_default(tag) && tag != tag::pre)
+ || tag::is_tabular_by_default(tag)
+ || tag == tag::li
+}
+
+/// Whether newlines should be added before and after the element if the parent
+/// allows it.
+///
+/// In contrast to `allows_pretty_inside`, which is purely spec-driven, this is
+/// more subjective and depends on preference.
+fn wants_pretty_around(tag: HtmlTag) -> bool {
+ allows_pretty_inside(tag) || tag::is_metadata(tag) || tag == tag::pre
}
/// Escape a character.
diff --git a/crates/typst-html/src/lib.rs b/crates/typst-html/src/lib.rs
index ffd8e2505..25d0cd5d8 100644
--- a/crates/typst-html/src/lib.rs
+++ b/crates/typst-html/src/lib.rs
@@ -14,9 +14,9 @@ use typst_library::html::{
use typst_library::introspection::{
Introspector, Locator, LocatorLink, SplitLocator, TagElem,
};
-use typst_library::layout::{Abs, Axes, BoxElem, Region, Size};
+use typst_library::layout::{Abs, Axes, BlockBody, BlockElem, BoxElem, Region, Size};
use typst_library::model::{DocumentInfo, ParElem};
-use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
+use typst_library::routines::{Arenas, FragmentKind, Pair, RealizationKind, Routines};
use typst_library::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
use typst_library::World;
use typst_syntax::Span;
@@ -139,7 +139,9 @@ fn html_fragment_impl(
let arenas = Arenas::default();
let children = (engine.routines.realize)(
- RealizationKind::HtmlFragment,
+ // No need to know about the `FragmentKind` because we handle both
+ // uniformly.
+ RealizationKind::HtmlFragment(&mut FragmentKind::Block),
&mut engine,
&mut locator,
&arenas,
@@ -189,7 +191,8 @@ fn handle(
};
output.push(element.into());
} else if let Some(elem) = child.to_packed::() {
- let children = handle_list(engine, locator, elem.children.iter(&styles))?;
+ let children =
+ html_fragment(engine, &elem.body, locator.next(&elem.span()), styles)?;
output.push(
HtmlElement::new(tag::p)
.with_children(children)
@@ -197,13 +200,34 @@ fn handle(
.into(),
);
} else if let Some(elem) = child.to_packed::() {
- // FIXME: Very incomplete and hacky, but makes boxes kind fulfill their
- // purpose for now.
+ // TODO: This is rather incomplete.
if let Some(body) = elem.body(styles) {
let children =
html_fragment(engine, body, locator.next(&elem.span()), styles)?;
- output.extend(children);
+ output.push(
+ HtmlElement::new(tag::span)
+ .with_attr(attr::style, "display: inline-block;")
+ .with_children(children)
+ .spanned(elem.span())
+ .into(),
+ )
}
+ } else if let Some((elem, body)) =
+ child
+ .to_packed::()
+ .and_then(|elem| match elem.body(styles) {
+ Some(BlockBody::Content(body)) => Some((elem, body)),
+ _ => None,
+ })
+ {
+ // TODO: This is rather incomplete.
+ let children = html_fragment(engine, body, locator.next(&elem.span()), styles)?;
+ output.push(
+ HtmlElement::new(tag::div)
+ .with_children(children)
+ .spanned(elem.span())
+ .into(),
+ );
} else if child.is::() {
output.push(HtmlNode::text(' ', child.span()));
} else if let Some(elem) = child.to_packed::() {
diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs
index c22ea7e40..0f8abddb7 100644
--- a/crates/typst-ide/src/complete.rs
+++ b/crates/typst-ide/src/complete.rs
@@ -817,19 +817,8 @@ fn param_value_completions<'a>(
) {
if param.name == "font" {
ctx.font_completions();
- } else if param.name == "path" {
- ctx.file_completions_with_extensions(match func.name() {
- Some("image") => &["png", "jpg", "jpeg", "gif", "svg", "svgz"],
- Some("csv") => &["csv"],
- Some("plugin") => &["wasm"],
- Some("cbor") => &["cbor"],
- Some("json") => &["json"],
- Some("toml") => &["toml"],
- Some("xml") => &["xml"],
- Some("yaml") => &["yml", "yaml"],
- Some("bibliography") => &["bib", "yml", "yaml"],
- _ => &[],
- });
+ } else if let Some(extensions) = path_completion(func, param) {
+ ctx.file_completions_with_extensions(extensions);
} else if func.name() == Some("figure") && param.name == "body" {
ctx.snippet_completion("image", "image(\"${}\"),", "An image in a figure.");
ctx.snippet_completion("table", "table(\n ${}\n),", "A table in a figure.");
@@ -838,6 +827,28 @@ fn param_value_completions<'a>(
ctx.cast_completions(¶m.input);
}
+/// Returns which file extensions to complete for the given parameter if any.
+fn path_completion(func: &Func, param: &ParamInfo) -> Option<&'static [&'static str]> {
+ Some(match (func.name(), param.name) {
+ (Some("image"), "source") => &["png", "jpg", "jpeg", "gif", "svg", "svgz"],
+ (Some("csv"), "source") => &["csv"],
+ (Some("plugin"), "source") => &["wasm"],
+ (Some("cbor"), "source") => &["cbor"],
+ (Some("json"), "source") => &["json"],
+ (Some("toml"), "source") => &["toml"],
+ (Some("xml"), "source") => &["xml"],
+ (Some("yaml"), "source") => &["yml", "yaml"],
+ (Some("bibliography"), "sources") => &["bib", "yml", "yaml"],
+ (Some("bibliography"), "style") => &["csl"],
+ (Some("cite"), "style") => &["csl"],
+ (Some("raw"), "syntaxes") => &["sublime-syntax"],
+ (Some("raw"), "theme") => &["tmtheme"],
+ (Some("embed"), "path") => &[],
+ (None, "path") => &[],
+ _ => return None,
+ })
+}
+
/// Resolve a callee expression to a global function.
fn resolve_global_callee<'a>(
ctx: &CompletionContext<'a>,
diff --git a/crates/typst-ide/src/matchers.rs b/crates/typst-ide/src/matchers.rs
index d02eb2a95..b92cbf557 100644
--- a/crates/typst-ide/src/matchers.rs
+++ b/crates/typst-ide/src/matchers.rs
@@ -89,15 +89,21 @@ pub fn named_items(
// ```
Some(ast::Imports::Items(items)) => {
for item in items.iter() {
- let original = item.original_name();
let bound = item.bound_name();
- let scope = source.and_then(|(value, _)| value.scope());
- let span = scope
- .and_then(|s| s.get_span(&original))
- .unwrap_or(Span::detached())
- .or(bound.span());
- let value = scope.and_then(|s| s.get(&original));
+ let (span, value) = item.path().iter().fold(
+ (bound.span(), source.map(|(value, _)| value)),
+ |(span, value), path_ident| {
+ let scope = value.and_then(|v| v.scope());
+ let span = scope
+ .and_then(|s| s.get_span(&path_ident))
+ .unwrap_or(Span::detached())
+ .or(span);
+ let value = scope.and_then(|s| s.get(&path_ident));
+ (span, value)
+ },
+ );
+
if let Some(res) =
recv(NamedItem::Import(bound.get(), span, value))
{
@@ -269,16 +275,18 @@ mod tests {
use std::borrow::Borrow;
use ecow::EcoString;
+ use typst::foundations::Value;
use typst::syntax::{LinkedNode, Side};
use super::named_items;
- use crate::tests::{FilePos, WorldLike};
+ use crate::tests::{FilePos, TestWorld, WorldLike};
- type Response = Vec;
+ type Response = Vec<(EcoString, Option)>;
trait ResponseExt {
fn must_include<'a>(&self, includes: impl IntoIterator) -> &Self;
fn must_exclude<'a>(&self, excludes: impl IntoIterator) -> &Self;
+ fn must_include_value(&self, name_value: (&str, Option<&Value>)) -> &Self;
}
impl ResponseExt for Response {
@@ -286,7 +294,7 @@ mod tests {
fn must_include<'a>(&self, includes: impl IntoIterator) -> &Self {
for item in includes {
assert!(
- self.iter().any(|v| v == item),
+ self.iter().any(|v| v.0 == item),
"{item:?} was not contained in {self:?}",
);
}
@@ -297,12 +305,21 @@ mod tests {
fn must_exclude<'a>(&self, excludes: impl IntoIterator) -> &Self {
for item in excludes {
assert!(
- !self.iter().any(|v| v == item),
+ !self.iter().any(|v| v.0 == item),
"{item:?} was wrongly contained in {self:?}",
);
}
self
}
+
+ #[track_caller]
+ fn must_include_value(&self, name_value: (&str, Option<&Value>)) -> &Self {
+ assert!(
+ self.iter().any(|v| (v.0.as_str(), v.1.as_ref()) == name_value),
+ "{name_value:?} was not contained in {self:?}",
+ );
+ self
+ }
}
#[track_caller]
@@ -314,7 +331,7 @@ mod tests {
let leaf = node.leaf_at(cursor, Side::After).unwrap();
let mut items = vec![];
named_items(world, leaf, |s| {
- items.push(s.name().clone());
+ items.push((s.name().clone(), s.value().clone()));
None::<()>
});
items
@@ -340,5 +357,10 @@ mod tests {
#[test]
fn test_named_items_import() {
test("#import \"foo.typ\": a; #(a);", 2).must_include(["a"]);
+
+ let world = TestWorld::new("#import \"foo.typ\": a.b; #(b);")
+ .with_source("foo.typ", "#import \"a.typ\"")
+ .with_source("a.typ", "#let b = 1;");
+ test(&world, 2).must_include_value(("b", Some(&Value::Int(1))));
}
}
diff --git a/crates/typst-ide/src/tests.rs b/crates/typst-ide/src/tests.rs
index f41808dac..6678ab841 100644
--- a/crates/typst-ide/src/tests.rs
+++ b/crates/typst-ide/src/tests.rs
@@ -55,7 +55,7 @@ impl TestWorld {
pub fn with_asset_at(mut self, path: &str, filename: &str) -> Self {
let id = FileId::new(None, VirtualPath::new(path));
let data = typst_dev_assets::get_by_name(filename).unwrap();
- let bytes = Bytes::from_static(data);
+ let bytes = Bytes::new(data);
Arc::make_mut(&mut self.files).assets.insert(id, bytes);
self
}
@@ -152,7 +152,7 @@ impl Default for TestBase {
fn default() -> Self {
let fonts: Vec<_> = typst_assets::fonts()
.chain(typst_dev_assets::fonts())
- .flat_map(|data| Font::iter(Bytes::from_static(data)))
+ .flat_map(|data| Font::iter(Bytes::new(data)))
.collect();
Self {
diff --git a/crates/typst-kit/src/fonts.rs b/crates/typst-kit/src/fonts.rs
index 83e13fd8f..c15d739ec 100644
--- a/crates/typst-kit/src/fonts.rs
+++ b/crates/typst-kit/src/fonts.rs
@@ -13,6 +13,7 @@ use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use fontdb::{Database, Source};
+use typst_library::foundations::Bytes;
use typst_library::text::{Font, FontBook, FontInfo};
use typst_timing::TimingScope;
@@ -52,9 +53,8 @@ impl FontSlot {
.as_ref()
.expect("`path` is not `None` if `font` is uninitialized"),
)
- .ok()?
- .into();
- Font::new(data, self.index)
+ .ok()?;
+ Font::new(Bytes::new(data), self.index)
})
.clone()
}
@@ -196,7 +196,7 @@ impl FontSearcher {
#[cfg(feature = "embed-fonts")]
fn add_embedded(&mut self) {
for data in typst_assets::fonts() {
- let buffer = typst_library::foundations::Bytes::from_static(data);
+ let buffer = Bytes::new(data);
for (i, font) in Font::iter(buffer).enumerate() {
self.book.push(font.info().clone());
self.fonts.push(FontSlot {
diff --git a/crates/typst-layout/src/flow/collect.rs b/crates/typst-layout/src/flow/collect.rs
index 12cfa152e..34362a6c5 100644
--- a/crates/typst-layout/src/flow/collect.rs
+++ b/crates/typst-layout/src/flow/collect.rs
@@ -20,12 +20,16 @@ use typst_library::model::ParElem;
use typst_library::routines::{Pair, Routines};
use typst_library::text::TextElem;
use typst_library::World;
+use typst_utils::SliceExt;
-use super::{layout_multi_block, layout_single_block};
+use super::{layout_multi_block, layout_single_block, FlowMode};
+use crate::inline::ParSituation;
+use crate::modifiers::layout_and_modify;
/// Collects all elements of the flow into prepared children. These are much
/// simpler to handle than the raw elements.
#[typst_macros::time]
+#[allow(clippy::too_many_arguments)]
pub fn collect<'a>(
engine: &mut Engine,
bump: &'a Bump,
@@ -33,6 +37,7 @@ pub fn collect<'a>(
locator: Locator<'a>,
base: Size,
expand: bool,
+ mode: FlowMode,
) -> SourceResult>> {
Collector {
engine,
@@ -42,9 +47,9 @@ pub fn collect<'a>(
base,
expand,
output: Vec::with_capacity(children.len()),
- last_was_par: false,
+ par_situation: ParSituation::First,
}
- .run()
+ .run(mode)
}
/// State for collection.
@@ -56,12 +61,20 @@ struct Collector<'a, 'x, 'y> {
expand: bool,
locator: SplitLocator<'a>,
output: Vec>,
- last_was_par: bool,
+ par_situation: ParSituation,
}
impl<'a> Collector<'a, '_, '_> {
/// Perform the collection.
- fn run(mut self) -> SourceResult>> {
+ fn run(self, mode: FlowMode) -> SourceResult>> {
+ match mode {
+ FlowMode::Root | FlowMode::Block => self.run_block(),
+ FlowMode::Inline => self.run_inline(),
+ }
+ }
+
+ /// Perform collection for block-level children.
+ fn run_block(mut self) -> SourceResult>> {
for &(child, styles) in self.children {
if let Some(elem) = child.to_packed::() {
self.output.push(Child::Tag(&elem.tag));
@@ -94,6 +107,42 @@ impl<'a> Collector<'a, '_, '_> {
Ok(self.output)
}
+ /// Perform collection for inline-level children.
+ fn run_inline(mut self) -> SourceResult>> {
+ // Extract leading and trailing tags.
+ let (start, end) = self.children.split_prefix_suffix(|(c, _)| c.is::());
+ let inner = &self.children[start..end];
+
+ // Compute the shared styles, ignoring tags.
+ let styles = StyleChain::trunk(inner.iter().map(|&(_, s)| s)).unwrap_or_default();
+
+ // Layout the lines.
+ let lines = crate::inline::layout_inline(
+ self.engine,
+ inner,
+ &mut self.locator,
+ styles,
+ self.base,
+ self.expand,
+ None,
+ )?
+ .into_frames();
+
+ for (c, _) in &self.children[..start] {
+ let elem = c.to_packed::().unwrap();
+ self.output.push(Child::Tag(&elem.tag));
+ }
+
+ self.lines(lines, styles);
+
+ for (c, _) in &self.children[end..] {
+ let elem = c.to_packed::().unwrap();
+ self.output.push(Child::Tag(&elem.tag));
+ }
+
+ Ok(self.output)
+ }
+
/// Collect vertical spacing into a relative or fractional child.
fn v(&mut self, elem: &'a Packed, styles: StyleChain<'a>) {
self.output.push(match elem.amount {
@@ -109,24 +158,34 @@ impl<'a> Collector<'a, '_, '_> {
elem: &'a Packed,
styles: StyleChain<'a>,
) -> SourceResult<()> {
- let align = AlignElem::alignment_in(styles).resolve(styles);
- let leading = ParElem::leading_in(styles);
- let spacing = ParElem::spacing_in(styles);
- let costs = TextElem::costs_in(styles);
-
- let lines = crate::layout_inline(
+ let lines = crate::inline::layout_par(
+ elem,
self.engine,
- &elem.children,
self.locator.next(&elem.span()),
styles,
- self.last_was_par,
self.base,
self.expand,
+ self.par_situation,
)?
.into_frames();
+ let spacing = ParElem::spacing_in(styles);
self.output.push(Child::Rel(spacing.into(), 4));
+ self.lines(lines, styles);
+
+ self.output.push(Child::Rel(spacing.into(), 4));
+ self.par_situation = ParSituation::Consecutive;
+
+ Ok(())
+ }
+
+ /// Collect laid-out lines.
+ fn lines(&mut self, lines: Vec, styles: StyleChain<'a>) {
+ let align = AlignElem::alignment_in(styles).resolve(styles);
+ let leading = ParElem::leading_in(styles);
+ let costs = TextElem::costs_in(styles);
+
// Determine whether to prevent widow and orphans.
let len = lines.len();
let prevent_orphans =
@@ -165,11 +224,6 @@ impl<'a> Collector<'a, '_, '_> {
self.output
.push(Child::Line(self.boxed(LineChild { frame, align, need })));
}
-
- self.output.push(Child::Rel(spacing.into(), 4));
- self.last_was_par = true;
-
- Ok(())
}
/// Collect a block into a [`SingleChild`] or [`MultiChild`] depending on
@@ -218,7 +272,7 @@ impl<'a> Collector<'a, '_, '_> {
};
self.output.push(spacing(elem.below(styles)));
- self.last_was_par = false;
+ self.par_situation = ParSituation::Other;
}
/// Collects a placed element into a [`PlacedChild`].
@@ -377,8 +431,9 @@ fn layout_single_impl(
route: Route::extend(route),
};
- layout_single_block(elem, &mut engine, locator, styles, region)
- .map(|frame| frame.post_processed(styles))
+ layout_and_modify(styles, |styles| {
+ layout_single_block(elem, &mut engine, locator, styles, region)
+ })
}
/// A child that encapsulates a prepared breakable block.
@@ -473,11 +528,8 @@ fn layout_multi_impl(
route: Route::extend(route),
};
- layout_multi_block(elem, &mut engine, locator, styles, regions).map(|mut fragment| {
- for frame in &mut fragment {
- frame.post_process(styles);
- }
- fragment
+ layout_and_modify(styles, |styles| {
+ layout_multi_block(elem, &mut engine, locator, styles, regions)
})
}
@@ -579,20 +631,23 @@ impl PlacedChild<'_> {
self.cell.get_or_init(base, |base| {
let align = self.alignment.unwrap_or_else(|| Alignment::CENTER);
let aligned = AlignElem::set_alignment(align).wrap();
+ let styles = self.styles.chain(&aligned);
- let mut frame = crate::layout_frame(
- engine,
- &self.elem.body,
- self.locator.relayout(),
- self.styles.chain(&aligned),
- Region::new(base, Axes::splat(false)),
- )?;
+ let mut frame = layout_and_modify(styles, |styles| {
+ crate::layout_frame(
+ engine,
+ &self.elem.body,
+ self.locator.relayout(),
+ styles,
+ Region::new(base, Axes::splat(false)),
+ )
+ })?;
if self.float {
frame.set_parent(self.elem.location().unwrap());
}
- Ok(frame.post_processed(self.styles))
+ Ok(frame)
})
}
diff --git a/crates/typst-layout/src/flow/compose.rs b/crates/typst-layout/src/flow/compose.rs
index 3cf66f9e3..76af8f650 100644
--- a/crates/typst-layout/src/flow/compose.rs
+++ b/crates/typst-layout/src/flow/compose.rs
@@ -17,7 +17,9 @@ use typst_library::model::{
use typst_syntax::Span;
use typst_utils::{NonZeroExt, Numeric};
-use super::{distribute, Config, FlowResult, LineNumberConfig, PlacedChild, Stop, Work};
+use super::{
+ distribute, Config, FlowMode, FlowResult, LineNumberConfig, PlacedChild, Stop, Work,
+};
/// Composes the contents of a single page/region. A region can have multiple
/// columns/subregions.
@@ -356,7 +358,7 @@ impl<'a, 'b> Composer<'a, 'b, '_, '_> {
migratable: bool,
) -> FlowResult<()> {
// Footnotes are only supported at the root level.
- if !self.config.root {
+ if self.config.mode != FlowMode::Root {
return Ok(());
}
diff --git a/crates/typst-layout/src/flow/mod.rs b/crates/typst-layout/src/flow/mod.rs
index 2f0ec39a9..2acbbcef3 100644
--- a/crates/typst-layout/src/flow/mod.rs
+++ b/crates/typst-layout/src/flow/mod.rs
@@ -25,7 +25,7 @@ use typst_library::layout::{
Regions, Rel, Size,
};
use typst_library::model::{FootnoteElem, FootnoteEntry, LineNumberingScope, ParLine};
-use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
+use typst_library::routines::{Arenas, FragmentKind, Pair, RealizationKind, Routines};
use typst_library::text::TextElem;
use typst_library::World;
use typst_utils::{NonZeroExt, Numeric};
@@ -140,9 +140,10 @@ fn layout_fragment_impl(
engine.route.check_layout_depth().at(content.span())?;
+ let mut kind = FragmentKind::Block;
let arenas = Arenas::default();
let children = (engine.routines.realize)(
- RealizationKind::LayoutFragment,
+ RealizationKind::LayoutFragment(&mut kind),
&mut engine,
&mut locator,
&arenas,
@@ -158,25 +159,46 @@ fn layout_fragment_impl(
regions,
columns,
column_gutter,
- false,
+ kind.into(),
)
}
+/// The mode a flow can be laid out in.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum FlowMode {
+ /// A root flow with block-level elements. Like `FlowMode::Block`, but can
+ /// additionally host footnotes and line numbers.
+ Root,
+ /// A flow whose children are block-level elements.
+ Block,
+ /// A flow whose children are inline-level elements.
+ Inline,
+}
+
+impl From for FlowMode {
+ fn from(value: FragmentKind) -> Self {
+ match value {
+ FragmentKind::Inline => Self::Inline,
+ FragmentKind::Block => Self::Block,
+ }
+ }
+}
+
/// Lays out realized content into regions, potentially with columns.
#[allow(clippy::too_many_arguments)]
-pub(crate) fn layout_flow(
+pub fn layout_flow<'a>(
engine: &mut Engine,
- children: &[Pair],
- locator: &mut SplitLocator,
- shared: StyleChain,
+ children: &[Pair<'a>],
+ locator: &mut SplitLocator<'a>,
+ shared: StyleChain<'a>,
mut regions: Regions,
columns: NonZeroUsize,
column_gutter: Rel,
- root: bool,
+ mode: FlowMode,
) -> SourceResult {
// Prepare configuration that is shared across the whole flow.
let config = Config {
- root,
+ mode,
shared,
columns: {
let mut count = columns.get();
@@ -195,7 +217,7 @@ pub(crate) fn layout_flow(
gap: FootnoteEntry::gap_in(shared),
expand: regions.expand.x,
},
- line_numbers: root.then(|| LineNumberConfig {
+ line_numbers: (mode == FlowMode::Root).then(|| LineNumberConfig {
scope: ParLine::numbering_scope_in(shared),
default_clearance: {
let width = if PageElem::flipped_in(shared) {
@@ -225,6 +247,7 @@ pub(crate) fn layout_flow(
locator.next(&()),
Size::new(config.columns.width, regions.full),
regions.expand.x,
+ mode,
)?;
let mut work = Work::new(&children);
@@ -318,7 +341,7 @@ impl<'a, 'b> Work<'a, 'b> {
struct Config<'x> {
/// Whether this is the root flow, which can host footnotes and line
/// numbers.
- root: bool,
+ mode: FlowMode,
/// The styles shared by the whole flow. This is used for footnotes and line
/// numbers.
shared: StyleChain<'x>,
diff --git a/crates/typst-layout/src/grid/layouter.rs b/crates/typst-layout/src/grid/layouter.rs
index 7c94617dc..1f9cf6796 100644
--- a/crates/typst-layout/src/grid/layouter.rs
+++ b/crates/typst-layout/src/grid/layouter.rs
@@ -3,6 +3,7 @@ use std::fmt::Debug;
use typst_library::diag::{bail, SourceResult};
use typst_library::engine::Engine;
use typst_library::foundations::{Resolve, StyleChain};
+use typst_library::layout::grid::resolve::{Cell, CellGrid, LinePosition, Repeatable};
use typst_library::layout::{
Abs, Axes, Dir, Fr, Fragment, Frame, FrameItem, Length, Point, Region, Regions, Rel,
Size, Sizing,
@@ -13,8 +14,8 @@ use typst_syntax::Span;
use typst_utils::{MaybeReverseIter, Numeric};
use super::{
- generate_line_segments, hline_stroke_at_column, vline_stroke_at_row, Cell, CellGrid,
- LinePosition, LineSegment, Repeatable, Rowspan, UnbreakableRowGroup,
+ generate_line_segments, hline_stroke_at_column, layout_cell, vline_stroke_at_row,
+ LineSegment, Rowspan, UnbreakableRowGroup,
};
/// Performs grid layout.
@@ -843,7 +844,8 @@ impl<'a> GridLayouter<'a> {
let size = Size::new(available, height);
let pod = Region::new(size, Axes::splat(false));
- let frame = cell.layout(engine, 0, self.styles, pod.into())?.into_frame();
+ let frame =
+ layout_cell(cell, engine, 0, self.styles, pod.into())?.into_frame();
resolved.set_max(frame.width() - already_covered_width);
}
@@ -1086,7 +1088,7 @@ impl<'a> GridLayouter<'a> {
};
let frames =
- cell.layout(engine, disambiguator, self.styles, pod)?.into_frames();
+ layout_cell(cell, engine, disambiguator, self.styles, pod)?.into_frames();
// Skip the first region if one cell in it is empty. Then,
// remeasure.
@@ -1252,9 +1254,9 @@ impl<'a> GridLayouter<'a> {
// rows.
pod.full = self.regions.full;
}
- let frame = cell
- .layout(engine, disambiguator, self.styles, pod)?
- .into_frame();
+ let frame =
+ layout_cell(cell, engine, disambiguator, self.styles, pod)?
+ .into_frame();
let mut pos = pos;
if self.is_rtl {
// In the grid, cell colspans expand to the right,
@@ -1310,7 +1312,7 @@ impl<'a> GridLayouter<'a> {
// Push the layouted frames into the individual output frames.
let fragment =
- cell.layout(engine, disambiguator, self.styles, pod)?;
+ layout_cell(cell, engine, disambiguator, self.styles, pod)?;
for (output, frame) in outputs.iter_mut().zip(fragment) {
let mut pos = pos;
if self.is_rtl {
diff --git a/crates/typst-layout/src/grid/lines.rs b/crates/typst-layout/src/grid/lines.rs
index 3e89612a1..1227953d1 100644
--- a/crates/typst-layout/src/grid/lines.rs
+++ b/crates/typst-layout/src/grid/lines.rs
@@ -1,41 +1,11 @@
-use std::num::NonZeroUsize;
use std::sync::Arc;
use typst_library::foundations::{AlternativeFold, Fold};
+use typst_library::layout::grid::resolve::{CellGrid, Line, Repeatable};
use typst_library::layout::Abs;
use typst_library::visualize::Stroke;
-use super::{CellGrid, LinePosition, Repeatable, RowPiece};
-
-/// Represents an explicit grid line (horizontal or vertical) specified by the
-/// user.
-pub struct Line {
- /// The index of the track after this line. This will be the index of the
- /// row a horizontal line is above of, or of the column right after a
- /// vertical line.
- ///
- /// Must be within `0..=tracks.len()` (where `tracks` is either `grid.cols`
- /// or `grid.rows`, ignoring gutter tracks, as appropriate).
- pub index: usize,
- /// The index of the track at which this line starts being drawn.
- /// This is the first column a horizontal line appears in, or the first row
- /// a vertical line appears in.
- ///
- /// Must be within `0..tracks.len()` minus gutter tracks.
- pub start: usize,
- /// The index after the last track through which the line is drawn.
- /// Thus, the line is drawn through tracks `start..end` (note that `end` is
- /// exclusive).
- ///
- /// Must be within `1..=tracks.len()` minus gutter tracks.
- /// `None` indicates the line should go all the way to the end.
- pub end: Option,
- /// The line's stroke. This is `None` when the line is explicitly used to
- /// override a previously specified line.
- pub stroke: Option>>,
- /// The line's position in relation to the track with its index.
- pub position: LinePosition,
-}
+use super::RowPiece;
/// Indicates which priority a particular grid line segment should have, based
/// on the highest priority configuration that defined the segment's stroke.
@@ -588,13 +558,13 @@ pub fn hline_stroke_at_column(
#[cfg(test)]
mod test {
+ use std::num::NonZeroUsize;
use typst_library::foundations::Content;
use typst_library::introspection::Locator;
+ use typst_library::layout::grid::resolve::{Cell, Entry, LinePosition};
use typst_library::layout::{Axes, Sides, Sizing};
use typst_utils::NonZeroExt;
- use super::super::cells::Entry;
- use super::super::Cell;
use super::*;
fn sample_cell() -> Cell<'static> {
diff --git a/crates/typst-layout/src/grid/mod.rs b/crates/typst-layout/src/grid/mod.rs
index 769bef8c5..1b4380f0a 100644
--- a/crates/typst-layout/src/grid/mod.rs
+++ b/crates/typst-layout/src/grid/mod.rs
@@ -1,40 +1,44 @@
-mod cells;
mod layouter;
mod lines;
mod repeated;
mod rowspans;
-pub use self::cells::{Cell, CellGrid};
pub use self::layouter::GridLayouter;
-use std::num::NonZeroUsize;
-use std::sync::Arc;
-
-use ecow::eco_format;
-use typst_library::diag::{SourceResult, Trace, Tracepoint};
+use typst_library::diag::SourceResult;
use typst_library::engine::Engine;
-use typst_library::foundations::{Fold, Packed, Smart, StyleChain};
+use typst_library::foundations::{Packed, StyleChain};
use typst_library::introspection::Locator;
-use typst_library::layout::{
- Abs, Alignment, Axes, Dir, Fragment, GridCell, GridChild, GridElem, GridItem, Length,
- OuterHAlignment, OuterVAlignment, Regions, Rel, Sides,
-};
-use typst_library::model::{TableCell, TableChild, TableElem, TableItem};
-use typst_library::text::TextElem;
-use typst_library::visualize::{Paint, Stroke};
-use typst_syntax::Span;
+use typst_library::layout::grid::resolve::{grid_to_cellgrid, table_to_cellgrid, Cell};
+use typst_library::layout::{Fragment, GridElem, Regions};
+use typst_library::model::TableElem;
-use self::cells::{
- LinePosition, ResolvableCell, ResolvableGridChild, ResolvableGridItem,
-};
use self::layouter::RowPiece;
use self::lines::{
- generate_line_segments, hline_stroke_at_column, vline_stroke_at_row, Line,
- LineSegment,
+ generate_line_segments, hline_stroke_at_column, vline_stroke_at_row, LineSegment,
};
-use self::repeated::{Footer, Header, Repeatable};
use self::rowspans::{Rowspan, UnbreakableRowGroup};
+/// Layout the cell into the given regions.
+///
+/// The `disambiguator` indicates which instance of this cell this should be
+/// layouted as. For normal cells, it is always `0`, but for headers and
+/// footers, it indicates the index of the header/footer among all. See the
+/// [`Locator`] docs for more details on the concepts behind this.
+pub fn layout_cell(
+ cell: &Cell,
+ engine: &mut Engine,
+ disambiguator: usize,
+ styles: StyleChain,
+ regions: Regions,
+) -> SourceResult {
+ let mut locator = cell.locator.relayout();
+ if disambiguator > 0 {
+ locator = locator.split().next_inner(disambiguator as u128);
+ }
+ crate::layout_fragment(engine, &cell.body, locator, styles, regions)
+}
+
/// Layout the grid.
#[typst_macros::time(span = elem.span())]
pub fn layout_grid(
@@ -44,54 +48,8 @@ pub fn layout_grid(
styles: StyleChain,
regions: Regions,
) -> SourceResult {
- let inset = elem.inset(styles);
- let align = elem.align(styles);
- let columns = elem.columns(styles);
- let rows = elem.rows(styles);
- let column_gutter = elem.column_gutter(styles);
- let row_gutter = elem.row_gutter(styles);
- let fill = elem.fill(styles);
- let stroke = elem.stroke(styles);
-
- let tracks = Axes::new(columns.0.as_slice(), rows.0.as_slice());
- let gutter = Axes::new(column_gutter.0.as_slice(), row_gutter.0.as_slice());
- // Use trace to link back to the grid when a specific cell errors
- let tracepoint = || Tracepoint::Call(Some(eco_format!("grid")));
- let resolve_item = |item: &GridItem| grid_item_to_resolvable(item, styles);
- let children = elem.children().iter().map(|child| match child {
- GridChild::Header(header) => ResolvableGridChild::Header {
- repeat: header.repeat(styles),
- span: header.span(),
- items: header.children().iter().map(resolve_item),
- },
- GridChild::Footer(footer) => ResolvableGridChild::Footer {
- repeat: footer.repeat(styles),
- span: footer.span(),
- items: footer.children().iter().map(resolve_item),
- },
- GridChild::Item(item) => {
- ResolvableGridChild::Item(grid_item_to_resolvable(item, styles))
- }
- });
- let grid = CellGrid::resolve(
- tracks,
- gutter,
- locator,
- children,
- fill,
- align,
- &inset,
- &stroke,
- engine,
- styles,
- elem.span(),
- )
- .trace(engine.world, tracepoint, elem.span())?;
-
- let layouter = GridLayouter::new(&grid, regions, styles, elem.span());
-
- // Measure the columns and layout the grid row-by-row.
- layouter.layout(engine)
+ let grid = grid_to_cellgrid(elem, engine, locator, styles)?;
+ GridLayouter::new(&grid, regions, styles, elem.span()).layout(engine)
}
/// Layout the table.
@@ -103,314 +61,6 @@ pub fn layout_table(
styles: StyleChain,
regions: Regions,
) -> SourceResult {
- let inset = elem.inset(styles);
- let align = elem.align(styles);
- let columns = elem.columns(styles);
- let rows = elem.rows(styles);
- let column_gutter = elem.column_gutter(styles);
- let row_gutter = elem.row_gutter(styles);
- let fill = elem.fill(styles);
- let stroke = elem.stroke(styles);
-
- let tracks = Axes::new(columns.0.as_slice(), rows.0.as_slice());
- let gutter = Axes::new(column_gutter.0.as_slice(), row_gutter.0.as_slice());
- // Use trace to link back to the table when a specific cell errors
- let tracepoint = || Tracepoint::Call(Some(eco_format!("table")));
- let resolve_item = |item: &TableItem| table_item_to_resolvable(item, styles);
- let children = elem.children().iter().map(|child| match child {
- TableChild::Header(header) => ResolvableGridChild::Header {
- repeat: header.repeat(styles),
- span: header.span(),
- items: header.children().iter().map(resolve_item),
- },
- TableChild::Footer(footer) => ResolvableGridChild::Footer {
- repeat: footer.repeat(styles),
- span: footer.span(),
- items: footer.children().iter().map(resolve_item),
- },
- TableChild::Item(item) => {
- ResolvableGridChild::Item(table_item_to_resolvable(item, styles))
- }
- });
- let grid = CellGrid::resolve(
- tracks,
- gutter,
- locator,
- children,
- fill,
- align,
- &inset,
- &stroke,
- engine,
- styles,
- elem.span(),
- )
- .trace(engine.world, tracepoint, elem.span())?;
-
- let layouter = GridLayouter::new(&grid, regions, styles, elem.span());
- layouter.layout(engine)
-}
-
-fn grid_item_to_resolvable(
- item: &GridItem,
- styles: StyleChain,
-) -> ResolvableGridItem> {
- match item {
- GridItem::HLine(hline) => ResolvableGridItem::HLine {
- y: hline.y(styles),
- start: hline.start(styles),
- end: hline.end(styles),
- stroke: hline.stroke(styles),
- span: hline.span(),
- position: match hline.position(styles) {
- OuterVAlignment::Top => LinePosition::Before,
- OuterVAlignment::Bottom => LinePosition::After,
- },
- },
- GridItem::VLine(vline) => ResolvableGridItem::VLine {
- x: vline.x(styles),
- start: vline.start(styles),
- end: vline.end(styles),
- stroke: vline.stroke(styles),
- span: vline.span(),
- position: match vline.position(styles) {
- OuterHAlignment::Left if TextElem::dir_in(styles) == Dir::RTL => {
- LinePosition::After
- }
- OuterHAlignment::Right if TextElem::dir_in(styles) == Dir::RTL => {
- LinePosition::Before
- }
- OuterHAlignment::Start | OuterHAlignment::Left => LinePosition::Before,
- OuterHAlignment::End | OuterHAlignment::Right => LinePosition::After,
- },
- },
- GridItem::Cell(cell) => ResolvableGridItem::Cell(cell.clone()),
- }
-}
-
-fn table_item_to_resolvable(
- item: &TableItem,
- styles: StyleChain,
-) -> ResolvableGridItem> {
- match item {
- TableItem::HLine(hline) => ResolvableGridItem::HLine {
- y: hline.y(styles),
- start: hline.start(styles),
- end: hline.end(styles),
- stroke: hline.stroke(styles),
- span: hline.span(),
- position: match hline.position(styles) {
- OuterVAlignment::Top => LinePosition::Before,
- OuterVAlignment::Bottom => LinePosition::After,
- },
- },
- TableItem::VLine(vline) => ResolvableGridItem::VLine {
- x: vline.x(styles),
- start: vline.start(styles),
- end: vline.end(styles),
- stroke: vline.stroke(styles),
- span: vline.span(),
- position: match vline.position(styles) {
- OuterHAlignment::Left if TextElem::dir_in(styles) == Dir::RTL => {
- LinePosition::After
- }
- OuterHAlignment::Right if TextElem::dir_in(styles) == Dir::RTL => {
- LinePosition::Before
- }
- OuterHAlignment::Start | OuterHAlignment::Left => LinePosition::Before,
- OuterHAlignment::End | OuterHAlignment::Right => LinePosition::After,
- },
- },
- TableItem::Cell(cell) => ResolvableGridItem::Cell(cell.clone()),
- }
-}
-
-impl ResolvableCell for Packed {
- fn resolve_cell<'a>(
- mut self,
- x: usize,
- y: usize,
- fill: &Option,
- align: Smart,
- inset: Sides