Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: render labels on content #638

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions crates/conversion/typst2vec/src/pass/typst2vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ impl Typst2VecPass {
self.intern(m, &t.1);
}
}
VecItem::Labelled(t) => {
if !self.items.contains_key(&t.1) {
self.intern(m, &t.1);
}
}
VecItem::Group(g) => {
for (_, id) in g.0.iter() {
if !self.items.contains_key(id) {
Expand Down Expand Up @@ -330,6 +335,11 @@ impl<const ENABLE_REF_CNT: bool> Typst2VecPassImpl<ENABLE_REF_CNT> {
)));
}

if let Some(label) = group.label.as_ref() {
let label = label.as_str().into();
inner = self.store(VecItem::Labelled(LabelledRef(label, inner)));
}

inner
}
FrameItem::Text(text) => {
Expand Down Expand Up @@ -427,6 +437,11 @@ impl<const ENABLE_REF_CNT: bool> Typst2VecPassImpl<ENABLE_REF_CNT> {
}
}

// pub label: Option<Label>,
// if let Some(label) = group.label {
// self.xml.write_attribute("data-typst-label", label.as_str());
// }

let g = self.store(VecItem::Group(GroupRef(
items.into_iter().map(|(x, _, y)| (x, y)).collect(),
)));
Expand Down
7 changes: 6 additions & 1 deletion crates/conversion/vec2bbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ impl Vec2BBoxPass {
fn bbox_of_(&mut self, module: &Module, v: Fingerprint, ts: Transform) -> Option<Rect> {
let item = module.get_item(&v).unwrap();
match item {
VecItem::Item(item) => self.bbox_of(module, item.1, item.0.clone().into()),
VecItem::Item(item) => {
let ts_group: Transform = item.0.clone().into();
let ts = ts.pre_concat(ts_group);
self.bbox_of(module, item.1, ts)
}
VecItem::Labelled(item) => self.bbox_of(module, item.1, ts),
VecItem::Group(g) => {
let mut r = Rect::default();
for (p, f) in g.0.iter() {
Expand Down
3 changes: 3 additions & 0 deletions crates/conversion/vec2dom/src/canvas_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ impl TypstElem {

ch.child.attach_canvas(c.inner[0].1.clone());
}
Label(ch) => {
ch.child.attach_canvas(g);
}
Html(..) | Link(..) | Image(..) | Text(..) | Path(..) | ContentHint(..) => {}
};
}
Expand Down
8 changes: 7 additions & 1 deletion crates/conversion/vec2dom/src/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use std::{
sync::{Arc, Mutex},
};

use reflexo::error::prelude::*;
use reflexo::hash::Fingerprint;
use reflexo::vector::ir::{self, Module, Page, Point, Scalar, Size, TextItem, TransformItem};
use reflexo::{error::prelude::*, ImmutStr};
use reflexo_vec2canvas::{CanvasElem, CanvasNode, CanvasOp, CanvasStateGuard};
use web_sys::{
js_sys::Reflect,
Expand Down Expand Up @@ -669,6 +669,7 @@ impl TypstPageElem {
pub enum TypstDomExtra {
Group(GroupElem),
Item(TransformElem),
Label(LabelElem),
Image(ImageElem),
Text(TextElem),
Path(PathElem),
Expand Down Expand Up @@ -719,6 +720,11 @@ pub struct TransformElem {
pub child: Box<TypstElem>,
}

#[derive(Debug)]
pub struct LabelElem {
pub label: ImmutStr,
pub child: Box<TypstElem>,
}
#[derive(Debug)]
pub struct GroupElem {
pub children: Vec<(Point, TypstElem)>,
Expand Down
47 changes: 47 additions & 0 deletions crates/conversion/vec2dom/src/svg_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use reflexo::hash::Fingerprint;
use reflexo::vector::ir::{self, Module, Page, TransformedRef, VecItem};
use reflexo::vector::{incr::IncrDocClient, vm::RenderVm};
use reflexo_vec2canvas::BBoxAt;
use reflexo_vec2svg::ir::LabelledRef;
use reflexo_vec2svg::{SvgExporter, SvgTask, SvgText};
use web_sys::{wasm_bindgen::JsCast, Element, SvgGraphicsElement};

Expand Down Expand Up @@ -176,6 +177,52 @@ impl TypstPageElem {
}
TypstDomExtra::Group(GroupElem { children })
}
VecItem::Labelled(LabelledRef(label, fg)) => {
let ch = g
.last_element_child()
.ok_or_else(|| {
web_sys::console::log_2(
&g,
&format!("Invalid item reference: {item:?}").into(),
);
panic!("Invalid item reference: {}", fg.as_svg_id("g"));
})
.unwrap();

#[cfg(feature = "debug_attach")]
web_sys::console::log_3(
&format!(
"attach {a:?} -> {b:?} {c:?}",
a = data.as_svg_id("g"),
b = fg.as_svg_id("g"),
c = label
)
.into(),
&ch,
&g,
);

let child = Self::attach_svg(
ctx,
ch.first_element_child()
.ok_or_else(|| {
web_sys::console::log_2(
&g,
&format!("Invalid item translate: {item:?}").into(),
);
panic!("Invalid item translate: {}", fg.as_svg_id("g"));
})
.unwrap()
.dyn_into()
.unwrap(),
*fg,
);

TypstDomExtra::Label(LabelElem {
label: label.clone(),
child: Box::new(child),
})
}
VecItem::Item(TransformedRef(trans, fg)) => {
let ch = g
.last_element_child()
Expand Down
11 changes: 10 additions & 1 deletion crates/conversion/vec2sema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
};

use reflexo::{
escape::{self, AttributeEscapes, PcDataEscapes},
escape::{self, escape_str, AttributeEscapes, PcDataEscapes},
hash::Fingerprint,
vector::ir::{self, Module, Point, Rect, Scalar, VecItem},
};
Expand Down Expand Up @@ -323,6 +323,15 @@ impl SemaTask {
self.render_semantics_walk(ctx, ts, t.1, fallbacks, output);
output.push(Cow::Borrowed("</span>"));
}
Labelled(t) => {
output.push(Cow::Borrowed(r#""#));
output.push(Cow::Owned(format!(
r#"<span class="typst-content-group" data-typst-label="{}" >"#,
escape_str::<AttributeEscapes>(&t.0)
)));
self.render_semantics_walk(ctx, ts, t.1, fallbacks, output);
output.push(Cow::Borrowed("</span>"));
}
Text(t) => {
let text_id = self.dfn_count;
self.dfn_count += 1;
Expand Down
5 changes: 5 additions & 0 deletions crates/conversion/vec2svg/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,11 @@ impl<
self.attributes.push(("data-reuse-from", v.as_svg_id("g")));
self
}

fn with_label(mut self, _ctx: &mut C, label: &str) -> Self {
self.attributes.push(("data-typst-label", label.into()));
self
}
}

/// See [`FlatGroupContext`].
Expand Down
1 change: 1 addition & 0 deletions crates/reflexo/src/vector/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub enum VecItem {
ContentHint(char),
ColorTransform(Arc<ColorTransform>),
Html(HtmlItem),
Labelled(LabelledRef),
}

/// Module with page references, corresponding to a `typst::model::Document`.
Expand Down
7 changes: 7 additions & 0 deletions crates/reflexo/src/vector/ir/compose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ impl fmt::Debug for Page {
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
pub struct TransformedRef(pub TransformItem, pub Fingerprint);

/// References to a vec item with transform.
/// Item representing an `<g/>` element applied with a [`TransformItem`].
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
pub struct LabelledRef(pub ImmutStr, pub Fingerprint);

/// References to a group of items with translates.
/// Absolute positioning items at their corresponding points.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
Expand Down
44 changes: 44 additions & 0 deletions crates/reflexo/src/vector/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ pub trait GroupContext<C>: Sized {
fn with_text(self, _ctx: &mut C, _text: &ir::TextItem, _fill_key: &Fingerprint) -> Self {
self
}
fn with_label(self, _ctx: &mut C, _label: &str) -> Self {
self
}

fn with_reuse(self, _ctx: &mut C, _v: &Fingerprint) -> Self {
self
Expand Down Expand Up @@ -120,6 +123,7 @@ pub trait RenderVm<'m>: Sized + FontIndice<'m> {
match &item {
ir::VecItem::Group(group) => self.render_group(abs_ref, group),
ir::VecItem::Item(transformed) => self.render_transformed_item(abs_ref, transformed),
ir::VecItem::Labelled(labelled) => self.render_labelled_item(abs_ref, labelled),
ir::VecItem::Text(text) => {
let mut g = self.start_text(abs_ref, text);
g = self.render_text(g, abs_ref, text);
Expand Down Expand Up @@ -192,6 +196,20 @@ pub trait RenderVm<'m>: Sized + FontIndice<'m> {
ts.into()
}

/// Render a labelled frame into underlying context.
fn render_labelled_item(
&mut self,
abs_ref: &Fingerprint,
labelled: &ir::LabelledRef,
) -> Self::Resultant {
let mut ts = self.start_group(abs_ref).with_label(self, &labelled.0);

let item_ref = &labelled.1;
// let item = self.get_item(&item_ref).unwrap();
ts.render_item(self, item_ref);
ts.into()
}

/// Render a text into the underlying context.
fn render_text(
&mut self,
Expand Down Expand Up @@ -248,6 +266,13 @@ where
self.render_diff_transformed_item(&mut group_ctx, prev_item, transformed);
group_ctx
}
ir::VecItem::Labelled(labelled) => {
let mut group_ctx = group_ctx
.with_reuse(self, prev_abs_ref)
.with_label(self, &labelled.0);
self.render_diff_labelled_item(&mut group_ctx, prev_item, labelled);
group_ctx
}
ir::VecItem::Text(text) => {
let group_ctx = group_ctx.with_text(self, text, next_abs_ref);
self.render_diff_text(group_ctx, next_abs_ref, prev_abs_ref, text)
Expand Down Expand Up @@ -350,6 +375,25 @@ where
// failed to reuse
}

/// Render a labelled frame into underlying context.
fn render_diff_labelled_item(
&mut self,
ts: &mut Self::Group,
prev_item_: Option<&ir::VecItem>,
labelled: &ir::LabelledRef,
) {
let child_ref = &labelled.1;
match prev_item_ {
// if both items are labelled, we can reuse the internal item with transforming it a
// bit.
Some(ir::VecItem::Labelled(ir::LabelledRef(_item, prev_ref))) => {
ts.render_diff_item_at(self, Point::default(), child_ref, prev_ref);
}
_ => ts.render_item(self, child_ref),
}
// failed to reuse
}

/// Render a diff text into the underlying context.
fn render_diff_text(
&mut self,
Expand Down
21 changes: 21 additions & 0 deletions fuzzers/corpora/meta/tag_00.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

#[] <group>

#[
#path(fill: rgb("#000"))
] <path>

#[
#path(fill: rgb("#001"))
#path(fill: rgb("#002"))
] <group2>

#box[
#path(fill: rgb("#003"))
#path(fill: rgb("#004"))
] <box-group>

#block[
#path(fill: rgb("#005"))
#path(fill: rgb("#006"))
] <block-group-group>