From 97958d6ec19ce2059cff7de74039c70682708a6a Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:27:55 +0800 Subject: [PATCH] fix: calculate bbox of text elements which have zero advance (#608) --- crates/conversion/vec2canvas/src/lib.rs | 16 ++++++++++++---- crates/conversion/vec2dom/src/svg_backend.rs | 14 +++++++++----- crates/reflexo/src/vector/ir/geom.rs | 8 ++++++++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/crates/conversion/vec2canvas/src/lib.rs b/crates/conversion/vec2canvas/src/lib.rs index 18f099e3..7d8d29d2 100644 --- a/crates/conversion/vec2canvas/src/lib.rs +++ b/crates/conversion/vec2canvas/src/lib.rs @@ -219,12 +219,20 @@ impl<'m, 't, Feat: ExportFeature> RenderVm<'m> for CanvasRenderTask<'m, 't, Feat let upem = Scalar(font.units_per_em.0); let accender = Scalar(font.ascender.0) * upem; + // todo: glyphs like macron has zero width... why? let w = text.width(); - CanvasBBox::Static(Box::new(Rect { - lo: Point::new(Scalar(0.), accender - upem), - hi: Point::new(w * upem / text.shape.size, accender), - })) + if text.shape.size.0 == 0. { + CanvasBBox::Static(Box::new(Rect { + lo: Point::new(Scalar(0.), accender - upem), + hi: Point::new(Scalar(0.), accender), + })) + } else { + CanvasBBox::Static(Box::new(Rect { + lo: Point::new(Scalar(0.), accender - upem), + hi: Point::new(w * upem / text.shape.size, accender), + })) + } }; for style in &text.shape.styles { if let ir::PathStyle::Fill(fill) = style { diff --git a/crates/conversion/vec2dom/src/svg_backend.rs b/crates/conversion/vec2dom/src/svg_backend.rs index 65bd78a7..e0057b64 100644 --- a/crates/conversion/vec2dom/src/svg_backend.rs +++ b/crates/conversion/vec2dom/src/svg_backend.rs @@ -282,13 +282,17 @@ impl TypstElem { } let bbox = self.canvas.as_ref().unwrap().bbox_at(ts); - // web_sys::console::log_2( - // &"bbox".into(), - // &format!("{:?} -> {:?} & {:?}", self.f.as_svg_id("g"), bbox, - // viewport).into(), ); let should_visible = bbox - .map(|new_rect| !new_rect.intersect(&viewport).is_empty()) + .map(|new_rect| new_rect.intersect(&viewport).is_intersected()) .unwrap_or(true); + // web_sys::console::log_2( + // &"bbox".into(), + // &format!( + // "{:?} -> ({bbox:?} & {viewport:?}) = {should_visible}", + // self.f.as_svg_id("g") + // ) + // .into(), + // ); if should_visible != self.is_svg_visible { let (x, y) = (&self.stub, &self.g); diff --git a/crates/reflexo/src/vector/ir/geom.rs b/crates/reflexo/src/vector/ir/geom.rs index abc073ec..4ce22864 100644 --- a/crates/reflexo/src/vector/ir/geom.rs +++ b/crates/reflexo/src/vector/ir/geom.rs @@ -362,10 +362,18 @@ impl Rect { } } + /// Returns whether the rectangle has no area. pub fn is_empty(&self) -> bool { self.lo.x >= self.hi.x || self.lo.y >= self.hi.y } + /// Returns whether the rectangle is not well constructed. + /// + /// Note: This is not the same as `is_empty`. + pub fn is_intersected(&self) -> bool { + self.lo.x <= self.hi.x || self.lo.y <= self.hi.y + } + pub fn intersect(&self, other: &Self) -> Self { Self { lo: self.lo.max(&other.lo),