fix: ignore any tags inside of tilings

This commit is contained in:
Tobias Schmitz 2025-08-04 18:51:02 +02:00
parent b312b45b19
commit 7ea16f25b5
No known key found for this signature in database
6 changed files with 109 additions and 37 deletions

View File

@ -16,6 +16,7 @@ use typst_library::visualize::{
use typst_utils::Numeric;
use crate::convert::{FrameContext, GlobalContext, State, handle_frame};
use crate::tags::{self, Disable};
use crate::util::{AbsExt, FillRuleExt, LineCapExt, LineJoinExt, TransformExt};
pub(crate) fn convert_fill(
@ -127,8 +128,12 @@ fn convert_pattern(
let mut stream_builder = surface.stream_builder();
let mut surface = stream_builder.surface();
let mut fc = FrameContext::new(None, pattern.frame().size());
handle_frame(&mut fc, pattern.frame(), None, &mut surface, gc)?;
{
let mut handle = tags::disable(gc, &mut surface, Disable::Tiling);
let (gc, surface) = handle.reborrow();
let mut fc = FrameContext::new(None, pattern.frame().size());
handle_frame(&mut fc, pattern.frame(), None, surface, gc)?;
}
surface.finish();
let stream = stream_builder.finish();
let pattern = Pattern {

View File

@ -38,7 +38,7 @@ pub struct Tags {
/// before the reference in the text, so we only resolve them once tags
/// for the whole document are generated.
pub footnotes: HashMap<Location, FootnoteCtx>,
pub in_artifact: Option<(Location, ArtifactKind)>,
pub disable: Option<Disable>,
/// Used to group multiple link annotations using quad points.
link_id: LinkId,
/// Used to generate IDs referenced in table `Headers` attributes.
@ -57,7 +57,7 @@ impl Tags {
stack: TagStack::new(),
placeholders: Placeholders(Vec::new()),
footnotes: HashMap::new(),
in_artifact: None,
disable: None,
link_id: LinkId(0),
table_id: TableId(0),
@ -187,6 +187,12 @@ impl Tags {
}
}
#[derive(Clone, Copy, Debug)]
pub enum Disable {
ArtifactTag(Location, ArtifactKind),
Tiling,
}
#[derive(Clone, Debug)]
pub struct TextAttrs {
lineheight: Option<LineHeight>,

View File

@ -100,17 +100,17 @@ pub fn handle_start(
return Ok(());
}
if gc.tags.in_artifact.is_some() {
if gc.tags.disable.is_some() {
// Don't nest artifacts
return Ok(());
}
if let Some(artifact) = elem.to_packed::<ArtifactElem>() {
let kind = artifact.kind.val();
push_artifact(gc, surface, elem, kind);
push_artifact_tag(gc, surface, elem, kind);
return Ok(());
} else if let Some(_) = elem.to_packed::<RepeatElem>() {
push_artifact(gc, surface, elem, ArtifactKind::Other);
push_artifact_tag(gc, surface, elem, ArtifactKind::Other);
return Ok(());
}
@ -207,7 +207,7 @@ pub fn handle_start(
// first page. Maybe it should be the cell on the last page, but that
// would require more changes in the layouting code, or a pre-pass
// on the frames to figure out if there are other footers following.
push_artifact(gc, surface, elem, ArtifactKind::Other);
push_artifact_tag(gc, surface, elem, ArtifactKind::Other);
} else {
push_stack(gc, elem, StackEntryKind::TableCell(cell.clone()))?;
}
@ -312,7 +312,7 @@ fn push_stack(
Ok(())
}
fn push_artifact(
fn push_artifact_tag(
gc: &mut GlobalContext,
surface: &mut Surface,
elem: &Content,
@ -321,7 +321,7 @@ fn push_artifact(
let loc = elem.location().expect("elem to be locatable");
let ty = artifact_type(kind);
surface.start_tagged(ContentTag::Artifact(ty));
gc.tags.in_artifact = Some((loc, kind));
gc.tags.disable = Some(Disable::ArtifactTag(loc, kind));
}
pub fn handle_end(
@ -333,10 +333,11 @@ pub fn handle_end(
return Ok(());
}
if let Some((l, _)) = gc.tags.in_artifact
if let Some(Disable::ArtifactTag(l, _)) = gc.tags.disable
&& l == loc
{
pop_artifact(gc, surface);
surface.end_tagged();
gc.tags.disable = None;
return Ok(());
}
@ -533,17 +534,16 @@ fn pop_stack(gc: &mut GlobalContext, entry: StackEntry) {
gc.tags.push(node);
}
fn pop_artifact(gc: &mut GlobalContext, surface: &mut Surface) {
surface.end_tagged();
gc.tags.in_artifact = None;
}
pub fn page_start(gc: &mut GlobalContext, surface: &mut Surface) {
if gc.options.disable_tags {
return;
}
if let Some((_, kind)) = gc.tags.in_artifact {
if let Some(disable) = gc.tags.disable {
let kind = match disable {
Disable::ArtifactTag(_, kind) => kind,
Disable::Tiling => ArtifactKind::Other,
};
let ty = artifact_type(kind);
surface.start_tagged(ContentTag::Artifact(ty));
}
@ -554,7 +554,7 @@ pub fn page_end(gc: &mut GlobalContext, surface: &mut Surface) {
return;
}
if gc.tags.in_artifact.is_some() {
if gc.tags.disable.is_some() {
surface.end_tagged();
}
}
@ -584,6 +584,43 @@ pub fn add_link_annotations(
}
}
pub struct DisableHandle<'a, 'b, 'c, 'd> {
gc: &'b mut GlobalContext<'a>,
surface: &'d mut Surface<'c>,
/// Whether this handle started the disabled range.
started: bool,
}
impl Drop for DisableHandle<'_, '_, '_, '_> {
fn drop(&mut self) {
if self.started {
self.gc.tags.disable = None;
self.surface.end_tagged();
}
}
}
impl<'a, 'c> DisableHandle<'a, '_, 'c, '_> {
pub fn reborrow<'s>(
&'s mut self,
) -> (&'s mut GlobalContext<'a>, &'s mut Surface<'c>) {
(self.gc, self.surface)
}
}
pub fn disable<'a, 'b, 'c, 'd>(
gc: &'b mut GlobalContext<'a>,
surface: &'d mut Surface<'c>,
kind: Disable,
) -> DisableHandle<'a, 'b, 'c, 'd> {
let started = gc.tags.disable.is_none();
if started {
gc.tags.disable = Some(kind);
surface.start_tagged(ContentTag::Artifact(ArtifactType::Other));
}
DisableHandle { gc, surface, started }
}
pub fn text<'a, 'b>(
gc: &mut GlobalContext,
fc: &FrameContext,
@ -596,7 +633,7 @@ pub fn text<'a, 'b>(
update_bbox(gc, fc, || text.bbox());
if gc.tags.in_artifact.is_some() {
if gc.tags.disable.is_some() {
return TagHandle { surface, started: false };
}
@ -682,25 +719,16 @@ fn start_content<'a, 'b>(
surface: &'b mut Surface<'a>,
content: ContentTag,
) -> TagHandle<'a, 'b> {
if gc.tags.in_artifact.is_some() {
if gc.tags.disable.is_some() {
return TagHandle { surface, started: false };
} else if let Some(StackEntryKind::Table(_)) = gc.tags.stack.last().map(|e| &e.kind) {
// TODO: handle this more like other artifacts
// Mark any direct child of a table as an aritfact. Any real content
// will be wrapped inside a `TableCell`.
// Don't store artifact content ids, they will be omitted anyway when
// serializing the tag tree.
surface.start_tagged(ContentTag::Artifact(ArtifactType::Other));
TagHandle { surface, started: true }
} else {
let artifact = matches!(content, ContentTag::Artifact(_));
let id = surface.start_tagged(content);
if !artifact {
gc.tags.push(TagNode::Leaf(id));
}
TagHandle { surface, started: true }
}
let artifact = matches!(content, ContentTag::Artifact(_));
let id = surface.start_tagged(content);
if !artifact {
gc.tags.push(TagNode::Leaf(id));
}
TagHandle { surface, started: true }
}
fn artifact_type(kind: ArtifactKind) -> ArtifactType {

View File

@ -0,0 +1,8 @@
- Tag: H1
/T: "Heading 1"
/K:
- Content: page=0 mcid=0
- Tag: H1
/T: "Heading 2"
/K:
- Content: page=0 mcid=1

View File

@ -0,0 +1,4 @@
- Tag: H1
/T: "Rectangle"
/K:
- Content: page=0 mcid=0

View File

@ -0,0 +1,21 @@
--- disable-tags-artifact pdftags ---
= Heading 1
#pdf.artifact[
#table(
columns: 2,
[a], [b],
[c], [d],
)
]
= Heading 2
--- disable-tags-tiling pdftags ---
= Rectangle
#let pat = tiling(size: (20pt, 20pt))[
- a
- b
- c
]
#rect(fill: pat)