Skip to content

Commit

Permalink
fix(exporter::svg): broken clip on adjacent paths (#386)
Browse files Browse the repository at this point in the history
* fix(exporter): save gradient back from refactor

* fix(exporter::svg): broken clip on adjacent paths
  • Loading branch information
Myriad-Dreamin authored Oct 31, 2023
1 parent b0c6104 commit 4aa33b8
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 32 deletions.
4 changes: 2 additions & 2 deletions exporter/canvas/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ impl CanvasElem for CanvasPathElem {

if fill {
// todo: canvas gradient
if fill_color.starts_with("url") {
if fill_color.starts_with('@') {
fill_color = "black".into()
}
canvas.set_fill_style(&fill_color.as_ref().into());
Expand All @@ -187,7 +187,7 @@ impl CanvasElem for CanvasPathElem {

if stroke && stroke_width.abs() > 1e-5 {
// todo: canvas gradient
if stroke_color.starts_with("url") {
if stroke_color.starts_with('@') {
stroke_color = "black".into()
}

Expand Down
4 changes: 2 additions & 2 deletions exporter/svg/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ fn render_path(path: &ir::PathItem, state: RenderState, abs_ref: &Fingerprint) -
for style in &path.styles {
match style {
PathStyle::Fill(color) => {
fill_color = if color.starts_with("url") {
fill_color = if color.starts_with('@') {
ft = format!(r#"url(#{})"#, state.at(abs_ref).as_svg_id("pf"));
&ft
} else {
Expand All @@ -640,7 +640,7 @@ fn render_path(path: &ir::PathItem, state: RenderState, abs_ref: &Fingerprint) -
}
PathStyle::Stroke(color) => {
// compress the stroke color
p.push(if color.starts_with("url") {
p.push(if color.starts_with('@') {
let ps = state.at(abs_ref).as_svg_id("ps");
format!(r##"stroke="url(#{})" "##, &ps)
} else {
Expand Down
68 changes: 40 additions & 28 deletions exporter/svg/src/frontend/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,10 @@ impl<'m, 't, Feat: ExportFeature> RenderVm for RenderContext<'m, 't, Feat> {

let mut group_ctx = text.shape.add_transform(self, group_ctx, upem);

if let Some(fill) = &group_ctx.text_fill {
let width = if let Some(fill) = &group_ctx.text_fill {
// clip path rect
let clip_id = fill.as_svg_id("tc");
group_ctx.content.push(SvgText::Plain(format!(
r#"<clipPath id="{}" clipPathUnits="userSpaceOnUse">"#,
clip_id
)));
}
let width = text.render_glyphs(upem, |x, g| {
group_ctx.render_glyph(self, x, g);
});
if let Some(fill) = &group_ctx.text_fill {
let fill_id = fill.as_svg_id("tf");
let clip_id = fill.as_svg_id("tc");

// because the text is already scaled by the font size,
// we need to scale it back to the original size.
Expand All @@ -256,16 +246,26 @@ impl<'m, 't, Feat: ExportFeature> RenderVm for RenderContext<'m, 't, Feat> {
.descender
.at(TypstAbs::raw(upem.0 as f64))
.to_pt() as f32;
let width = width.0 * upem.0 / text.shape.size.0;

group_ctx.content.push(SvgText::Plain(format!(
r#"<clipPath id="{}" clipPathUnits="userSpaceOnUse">"#,
clip_id
)));

let width = text.render_glyphs(upem, |x, g| {
group_ctx.render_glyph(self, x, g);
group_ctx.content.push(SvgText::Plain("<path/>".into()));
});

group_ctx
.content
.push(SvgText::Plain(r#"</clipPath>"#.to_owned()));

// clip path rect
let scaled_width = width.0 * upem.0 / text.shape.size.0;
group_ctx.content.push(SvgText::Plain(format!(
r##"<rect fill="url(#{})" width="{:.1}" height="{:.1}" y="{:.1}" clip-path="url(#{})"/>"##,
fill_id, width, upem.0, descender, clip_id
fill_id, scaled_width, upem.0, descender, clip_id
)));

// image glyphs
Expand All @@ -276,7 +276,13 @@ impl<'m, 't, Feat: ExportFeature> RenderVm for RenderContext<'m, 't, Feat> {
}
group_ctx.render_glyph(self, x, g);
});
}

width
} else {
text.render_glyphs(upem, |x, g| {
group_ctx.render_glyph(self, x, g);
})
};

if self.should_render_text_element() {
group_ctx.render_text_semantics_inner(
Expand Down Expand Up @@ -346,35 +352,35 @@ impl<'m, 't, Feat: ExportFeature> FlatRenderVm<'m> for RenderContext<'m, 't, Fea

group_ctx = text.shape.add_transform(self, group_ctx, upem);

if let Some(fill) = &group_ctx.text_fill {
let width = if let Some(fill) = &group_ctx.text_fill {
// clip path rect
let clip_id = fill.as_svg_id("tc");
group_ctx.content.push(SvgText::Plain(format!(
r#"<clipPath id="{}" clipPathUnits="userSpaceOnUse">"#,
clip_id
)));
}
let width = text.render_glyphs(upem, |x, g| {
group_ctx.render_glyph_ref(self, x, g);
});
if let Some(fill) = &group_ctx.text_fill {
let fill_id = fill.as_svg_id("tf");
let clip_id = fill.as_svg_id("tc");

// because the text is already scaled by the font size,
// we need to scale it back to the original size.
// todo: infinite multiplication
let descender = font.descender.0 * upem.0;
let width = width.0 * upem.0 / text.shape.size.0;

group_ctx.content.push(SvgText::Plain(format!(
r#"<clipPath id="{}" clipPathUnits="userSpaceOnUse">"#,
clip_id
)));

let width = text.render_glyphs(upem, |x, g| {
group_ctx.render_glyph_ref(self, x, g);
group_ctx.content.push(SvgText::Plain("<path/>".into()));
});

group_ctx
.content
.push(SvgText::Plain(r#"</clipPath>"#.to_owned()));

// clip path rect
let scaled_width = width.0 * upem.0 / text.shape.size.0;
group_ctx.content.push(SvgText::Plain(format!(
r##"<rect fill="url(#{})" width="{:.1}" height="{:.1}" y="{:.1}" clip-path="url(#{})"/>"##,
fill_id, width, upem.0, descender, clip_id
fill_id, scaled_width, upem.0, descender, clip_id
)));

// image glyphs
Expand All @@ -388,7 +394,13 @@ impl<'m, 't, Feat: ExportFeature> FlatRenderVm<'m> for RenderContext<'m, 't, Fea
}
group_ctx.render_glyph_ref(self, x, g);
});
}

width
} else {
text.render_glyphs(upem, |x, g| {
group_ctx.render_glyph_ref(self, x, g);
})
};

if self.should_render_text_element() {
group_ctx.render_text_semantics_inner(
Expand Down
10 changes: 10 additions & 0 deletions fuzzers/corpora/visualize/gradient-official_00.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@


#set text(fill: gradient.linear(red, blue), font: "Open Sans")
#let rainbow(content) = {
set text(fill: gradient.linear(..color.map.rainbow))
box(content)
}

This is a gradient on text, but with a #rainbow[twist]!

0 comments on commit 4aa33b8

Please sign in to comment.