summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst/src/layout/inline/shaping.rs35
1 files changed, 33 insertions, 2 deletions
diff --git a/crates/typst/src/layout/inline/shaping.rs b/crates/typst/src/layout/inline/shaping.rs
index b91d0b0b..b715f664 100644
--- a/crates/typst/src/layout/inline/shaping.rs
+++ b/crates/typst/src/layout/inline/shaping.rs
@@ -2,10 +2,11 @@ use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};
use std::ops::Range;
use std::str::FromStr;
+use std::sync::Arc;
use az::SaturatingAs;
use ecow::EcoString;
-use rustybuzz::{Tag, UnicodeBuffer};
+use rustybuzz::{ShapePlan, Tag, UnicodeBuffer};
use unicode_script::{Script, UnicodeScript};
use super::SpanMapper;
@@ -690,9 +691,21 @@ fn shape_segment<'a>(
Dir::RTL => rustybuzz::Direction::RightToLeft,
_ => unimplemented!("vertical text layout"),
});
+ buffer.guess_segment_properties();
+
+ // Prepare the shape plan. This plan depends on direction, script, language,
+ // and features, but is independent from the text and can thus be
+ // memoized.
+ let plan = create_shape_plan(
+ &font,
+ buffer.direction(),
+ buffer.script(),
+ buffer.language().as_ref(),
+ &ctx.features,
+ );
// Shape!
- let buffer = rustybuzz::shape(font.rusty(), &ctx.features, buffer);
+ let buffer = rustybuzz::shape_with_plan(font.rusty(), &plan, buffer);
let infos = buffer.glyph_infos();
let pos = buffer.glyph_positions();
let ltr = ctx.dir.is_positive();
@@ -783,6 +796,24 @@ fn shape_segment<'a>(
ctx.used.pop();
}
+/// Create a shape plan.
+#[comemo::memoize]
+fn create_shape_plan(
+ font: &Font,
+ direction: rustybuzz::Direction,
+ script: rustybuzz::Script,
+ language: Option<&rustybuzz::Language>,
+ features: &[rustybuzz::Feature],
+) -> Arc<ShapePlan> {
+ Arc::new(rustybuzz::ShapePlan::new(
+ font.rusty(),
+ direction,
+ Some(script),
+ language,
+ features,
+ ))
+}
+
/// Shape the text with tofus from the given font.
fn shape_tofus(ctx: &mut ShapingContext, base: usize, text: &str, font: Font) {
let x_advance = font.advance(0).unwrap_or_default();