From 177929fd1afe7d3fe794086468e7f6412d880930 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 Nov 2026 16:02:51 +0100 Subject: [PATCH] :recycle: Move shared glyph shaping out of the vertical text module The horizontal ruby, Zhuyin and emphasis painters and the text content code shape glyphs through helpers that lived under text_vertical. Move shaping.rs, font_tables.rs and orientation.rs to a new shapes/text_shaping module, which both engines import. The position-data direction values were split between the vertical positions module and the Zhuyin module. They now sit next to PositionData in shapes/text.rs, with their values unchanged. AI-assisted-by: claude-opus-5-5 --- render-wasm/src/shapes.rs | 1 + render-wasm/src/shapes/text.rs | 21 ++++++++++-- render-wasm/src/shapes/text_horizontal/mod.rs | 4 +-- .../src/shapes/text_horizontal/ruby.rs | 2 +- .../src/shapes/text_horizontal/tests.rs | 2 +- .../font_tables.rs | 20 ++++++------ render-wasm/src/shapes/text_shaping/mod.rs | 12 +++++++ .../orientation.rs | 6 ++-- .../shaping.rs | 32 +++++++++---------- .../src/shapes/text_vertical/annotations.rs | 12 ++++--- render-wasm/src/shapes/text_vertical/cells.rs | 10 +++--- .../src/shapes/text_vertical/layout.rs | 2 +- render-wasm/src/shapes/text_vertical/mod.rs | 18 +++-------- render-wasm/src/shapes/text_vertical/paint.rs | 4 +-- .../src/shapes/text_vertical/positions.rs | 16 +++------- .../src/shapes/text_vertical/test_support.rs | 26 +++++++-------- render-wasm/src/shapes/text_zhuyin.rs | 3 +- 17 files changed, 105 insertions(+), 86 deletions(-) rename render-wasm/src/shapes/{text_vertical => text_shaping}/font_tables.rs (93%) create mode 100644 render-wasm/src/shapes/text_shaping/mod.rs rename render-wasm/src/shapes/{text_vertical => text_shaping}/orientation.rs (97%) rename render-wasm/src/shapes/{text_vertical => text_shaping}/shaping.rs (96%) diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index c814b86f23..ae37d8afdb 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -38,6 +38,7 @@ pub mod text_paragraph; pub mod text_paths; pub mod text_policy; mod text_ruby; +mod text_shaping; pub mod text_vertical; mod text_warichu; mod text_zhuyin; diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index b77712f9c4..d183a55418 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -645,7 +645,7 @@ impl TextContent { .iter() .flat_map(|p| p.children()) .flat_map(|span| span.text.chars()) - .any(|c| super::text_vertical::is_emoji_char(c) || matches!(c, '\u{FE0F}' | '\u{20E3}')) + .any(|c| super::text_shaping::is_emoji_char(c) || matches!(c, '\u{FE0F}' | '\u{20E3}')) } pub fn paragraphs_mut(&mut self) -> &mut Vec { @@ -2945,6 +2945,23 @@ pub struct TextLayoutData { pub paragraphs: Vec, } +// Position-data `direction` values. 0 and 1 are the horizontal rtl and ltr +// runs (`direction_to_int`); the others mark vertical strips and annotation +// boxes the frontend reads differently. + +/// A vertical-rl strip. +pub const DIRECTION_VERTICAL_RL: u32 = 2; + +/// A vertical ruby strip; offsets index the *ruby* string. +pub const DIRECTION_VERTICAL_RUBY: u32 = 3; + +/// One emphasis mark (圏点) box, in any mode. +pub const DIRECTION_EMPHASIS_MARK: u32 = 4; + +/// A single upright Zhuyin glyph, in any mode; offsets index the *ruby* +/// string and the box carries its drawing baseline. +pub const DIRECTION_ZHUYIN: u32 = 5; + pub(crate) fn direction_to_int(direction: TextDirection) -> u32 { match direction { TextDirection::RTL => 0, @@ -3097,7 +3114,7 @@ impl HorizontalPositionEntries<'_> { placement.span, placement.range.clone(), horizontal_emphasis_mark_box(span, &placement), - super::text_vertical::DIRECTION_EMPHASIS_MARK, + DIRECTION_EMPHASIS_MARK, )); } } diff --git a/render-wasm/src/shapes/text_horizontal/mod.rs b/render-wasm/src/shapes/text_horizontal/mod.rs index ca852abd62..735ac66d1c 100644 --- a/render-wasm/src/shapes/text_horizontal/mod.rs +++ b/render-wasm/src/shapes/text_horizontal/mod.rs @@ -24,7 +24,7 @@ pub(crate) use warichu::*; use super::text::{add_text_with_tabs, Paragraph, TextContent, TextSpan}; use super::text_attrs::*; use super::text_ruby::*; -use super::text_vertical::{ +use super::text_shaping::{ is_upright_char, shape_segment_with_fallbacks, single_glyph_blob, span_font_families, ShapedRun, }; use super::text_warichu::*; @@ -433,6 +433,6 @@ pub(crate) fn horizontal_base_em_top( return rect.top.max(rect.bottom - font_size); }; let font = laid_out.font_at_utf16(base_start.max(line.start_index)); - let (ascent, descent) = super::text_vertical::upright_centre_metrics(&font); + let (ascent, descent) = super::text_shaping::upright_centre_metrics(&font); line.baseline as f32 + (ascent + descent - font_size) / 2.0 } diff --git a/render-wasm/src/shapes/text_horizontal/ruby.rs b/render-wasm/src/shapes/text_horizontal/ruby.rs index 3dec4a87fb..f6a4a8b376 100644 --- a/render-wasm/src/shapes/text_horizontal/ruby.rs +++ b/render-wasm/src/shapes/text_horizontal/ruby.rs @@ -78,7 +78,7 @@ pub(crate) fn horizontal_zhuyin_entries( y: origin.y + glyph.origin.y - size, width: size, height: size, - direction: super::super::text_zhuyin::DIRECTION_ZHUYIN, + direction: crate::shapes::DIRECTION_ZHUYIN, }); } } diff --git a/render-wasm/src/shapes/text_horizontal/tests.rs b/render-wasm/src/shapes/text_horizontal/tests.rs index 5910074e7d..5a9f0a83bf 100644 --- a/render-wasm/src/shapes/text_horizontal/tests.rs +++ b/render-wasm/src/shapes/text_horizontal/tests.rs @@ -1181,7 +1181,7 @@ fn horizontal_position_data_carries_warichu_lines_and_emphasis_marks() { assert_eq!(warichu_lines, vec![(0, 2), (2, 4)]); let marks: Vec<(u32, u32)> = data .iter() - .filter(|entry| entry.direction == super::super::text_vertical::DIRECTION_EMPHASIS_MARK) + .filter(|entry| entry.direction == crate::shapes::DIRECTION_EMPHASIS_MARK) .map(|entry| (entry.start_pos, entry.end_pos)) .collect(); assert_eq!(marks, vec![(0, 1), (2, 3)], "A and B are marked; 、 is not"); diff --git a/render-wasm/src/shapes/text_vertical/font_tables.rs b/render-wasm/src/shapes/text_shaping/font_tables.rs similarity index 93% rename from render-wasm/src/shapes/text_vertical/font_tables.rs rename to render-wasm/src/shapes/text_shaping/font_tables.rs index c9f7498d06..065a07e875 100644 --- a/render-wasm/src/shapes/text_vertical/font_tables.rs +++ b/render-wasm/src/shapes/text_shaping/font_tables.rs @@ -46,7 +46,7 @@ fn table_data(font: &Font, tag: &[u8; 4]) -> Option { /// advance by the glyph's vertical advance, not its shaped horizontal one; /// the two differ for vertical alternates and proportional glyphs (e.g. the /// vertical kana repeat marks). -pub(super) struct VerticalMetrics { +pub(crate) struct VerticalMetrics { units_per_em: f32, /// Advance heights of the first `advances.len()` glyph ids. advances: Vec, @@ -57,7 +57,7 @@ pub(super) struct VerticalMetrics { impl VerticalMetrics { /// Parses `vhea`/`vmtx`. `None` when the font has no vertical metrics /// (callers keep horizontal advances). - pub(super) fn from_font(font: &Font) -> Option { + pub(crate) fn from_font(font: &Font) -> Option { let units_per_em = units_per_em(font)?; let vhea = table_data(font, b"vhea")?; let vmtx = table_data(font, b"vmtx")?; @@ -87,7 +87,7 @@ impl VerticalMetrics { /// Vertical advance of `glyph` at `font_size`, in pixels (same units as /// the horizontal advances). - pub(super) fn advance(&self, glyph: GlyphId, font_size: f32) -> f32 { + pub(crate) fn advance(&self, glyph: GlyphId, font_size: f32) -> f32 { let raw = self .advances .get(glyph as usize) @@ -97,12 +97,12 @@ impl VerticalMetrics { } } -pub(super) fn vertical_metrics(font: &Font) -> Option> { +pub(crate) fn vertical_metrics(font: &Font) -> Option> { cached_table(&VERTICAL_METRICS, font, VerticalMetrics::from_font) } /// GPOS `vpal` deltas for one typeface, in font units. -pub(super) struct VpalTable { +pub(crate) struct VpalTable { units_per_em: f32, deltas: HashMap, } @@ -122,7 +122,7 @@ impl VpalTable { /// (negative when the cell tightens) and flow-axis ink shift (positive /// down the column; GPOS `yPlacement` is y-up, so its sign flips). /// `None` when no glyph of the cluster is covered. - pub(super) fn cluster_delta(&self, glyphs: &[GlyphId], font_size: f32) -> Option<(f32, f32)> { + pub(crate) fn cluster_delta(&self, glyphs: &[GlyphId], font_size: f32) -> Option<(f32, f32)> { let scale = font_size / self.units_per_em; let mut advance = 0.0f32; let mut flow_shift = None; @@ -136,7 +136,7 @@ impl VpalTable { } } -pub(super) fn vpal_table(font: &Font) -> Option> { +pub(crate) fn vpal_table(font: &Font) -> Option> { cached_table(&VPAL_TABLES, font, VpalTable::from_font) } @@ -183,20 +183,20 @@ pub(crate) fn upright_centre_metrics(font: &Font) -> (f32, f32) { /// glyph's line box in the em cell, as Tate-chu-yoko does. CJK faces have an /// ascent larger than the em, so hanging glyphs from the ascent would push /// them below their cells and overflow the column. -pub(super) fn upright_baseline_offset(ascent: f32, descent: f32, font_size: f32) -> f32 { +pub(crate) fn upright_baseline_offset(ascent: f32, descent: f32, font_size: f32) -> f32 { font_size / 2.0 - (ascent + descent) / 2.0 } /// Baseline offset for an upright glyph of `font` in an em cell of `font_size`. -pub(super) fn upright_baseline(font: &Font, font_size: f32) -> f32 { +pub(crate) fn upright_baseline(font: &Font, font_size: f32) -> f32 { let (ascent, descent) = upright_centre_metrics(font); upright_baseline_offset(ascent, descent, font_size) } #[cfg(test)] mod tests { - use super::super::test_support::{TEST_FONT, VMTX_TEST_FONT}; use super::*; + use crate::shapes::text_vertical::test_support::{TEST_FONT, VMTX_TEST_FONT}; use skia_safe::FontMgr; #[test] diff --git a/render-wasm/src/shapes/text_shaping/mod.rs b/render-wasm/src/shapes/text_shaping/mod.rs new file mode 100644 index 0000000000..13dc95813a --- /dev/null +++ b/render-wasm/src/shapes/text_shaping/mod.rs @@ -0,0 +1,12 @@ +//! Glyph shaping shared by the vertical engine and the horizontal annotation +//! painters (ruby, Zhuyin, emphasis): SkShaper runs with per-span font +//! fallback, upright/sideways orientation and the font tables (vmtx, vpal, +//! OS/2 typo metrics) the upright em box comes from. + +mod font_tables; +mod orientation; +mod shaping; + +pub(crate) use font_tables::*; +pub(crate) use orientation::*; +pub(crate) use shaping::*; diff --git a/render-wasm/src/shapes/text_vertical/orientation.rs b/render-wasm/src/shapes/text_shaping/orientation.rs similarity index 97% rename from render-wasm/src/shapes/text_vertical/orientation.rs rename to render-wasm/src/shapes/text_shaping/orientation.rs index 941e2a5c83..36dc56baf2 100644 --- a/render-wasm/src/shapes/text_vertical/orientation.rs +++ b/render-wasm/src/shapes/text_shaping/orientation.rs @@ -46,7 +46,7 @@ pub(crate) fn is_upright_char(c: char) -> bool { /// horizontal glyph rotates clockwise as a fallback. Covers UAX #50 `Tr`, /// plus comma/full-stop punctuation whose plain glyph sits in the wrong /// half of the vertical em box. -pub(super) fn uses_rotated_vertical_fallback(c: char) -> bool { +pub(crate) fn uses_rotated_vertical_fallback(c: char) -> bool { matches!(u32::from(c), 0x2018..=0x2019 // single quotation marks | 0x201C..=0x201D // double quotation marks @@ -73,7 +73,7 @@ pub(super) fn uses_rotated_vertical_fallback(c: char) -> bool { } #[derive(Debug, PartialEq)] -pub(super) struct Segment { +pub(crate) struct Segment { pub text: String, /// UTF-16 offset of the segment start within its span text. pub utf16_start: usize, @@ -82,7 +82,7 @@ pub(super) struct Segment { /// Split text into maximal runs of same orientation. Under /// `TextOrientation::Upright` every character is upright. -pub(super) fn segment_by_orientation(text: &str, orientation: TextOrientation) -> Vec { +pub(crate) fn segment_by_orientation(text: &str, orientation: TextOrientation) -> Vec { let mut segments: Vec = Vec::new(); let mut utf16_offset = 0; for grapheme in text.graphemes(true) { diff --git a/render-wasm/src/shapes/text_vertical/shaping.rs b/render-wasm/src/shapes/text_shaping/shaping.rs similarity index 96% rename from render-wasm/src/shapes/text_vertical/shaping.rs rename to render-wasm/src/shapes/text_shaping/shaping.rs index 6cbe091e23..fd83a19bd6 100644 --- a/render-wasm/src/shapes/text_vertical/shaping.rs +++ b/render-wasm/src/shapes/text_shaping/shaping.rs @@ -17,7 +17,7 @@ use crate::shapes::{FontFeatures, TextSpan}; use super::font_tables::upright_centre_metrics; /// JLREQ preferred width of a word space inside sideways Western text. -pub(super) const WESTERN_WORD_SPACING_EM: f32 = 1.0 / 3.0; +pub(crate) const WESTERN_WORD_SPACING_EM: f32 = 1.0 / 3.0; /// Candidate families for a span: its own font first, then emoji and the /// registered fallback fonts (Noto etc.) in the span language's order. @@ -52,7 +52,7 @@ pub struct ShapedRun { impl ShapedRun { /// Glyph clusters as (first glyph, glyph count). A combining sequence /// shapes to one cluster and must stay in one cell. - pub(super) fn cluster_spans(&self) -> impl Iterator + '_ { + pub(crate) fn cluster_spans(&self) -> impl Iterator + '_ { let mut glyph = 0usize; std::iter::from_fn(move || { let cluster = *self.clusters.get(glyph)?; @@ -69,7 +69,7 @@ impl ShapedRun { /// Flow offset from the run start of each scalar boundary (0..=`chars`) /// of a sideways run, from its glyph positions; the last boundary is /// `extent`. A scalar inside a ligature takes the next glyph's position. - pub(super) fn scalar_flow_offsets(&self, extent: f32, chars: usize) -> Option> { + pub(crate) fn scalar_flow_offsets(&self, extent: f32, chars: usize) -> Option> { let origin = self.positions.first()?.x; let mut offsets = Vec::with_capacity(chars + 1); let mut utf8 = self.utf8_range.start; @@ -87,7 +87,7 @@ impl ShapedRun { } /// Summed horizontal advance of a cluster. - pub(super) fn cluster_advance(&self, glyph: usize, count: usize) -> f32 { + pub(crate) fn cluster_advance(&self, glyph: usize, count: usize) -> f32 { self.advances[glyph..glyph + count].iter().sum() } @@ -101,7 +101,7 @@ impl ShapedRun { } /// Ink bounds of a cluster, relative to its first glyph's pen position. - pub(super) fn cluster_ink_bounds(&self, glyph: usize, count: usize) -> Option { + pub(crate) fn cluster_ink_bounds(&self, glyph: usize, count: usize) -> Option { glyph_run_ink_bounds( &self.font, &self.glyphs[glyph..glyph + count], @@ -110,7 +110,7 @@ impl ShapedRun { } /// Ink bounds of the whole run at its shaped positions. - pub(super) fn ink_bounds(&self) -> Option { + pub(crate) fn ink_bounds(&self) -> Option { glyph_run_ink_bounds(&self.font, &self.glyphs, &self.positions) } @@ -165,18 +165,18 @@ impl ShapedRun { } /// Text blob of one cluster, positioned from its first glyph's pen x. - pub(super) fn cluster_blob(&self, glyph: usize, count: usize) -> Option { + pub(crate) fn cluster_blob(&self, glyph: usize, count: usize) -> Option { self.text_blob(glyph, count, &self.cluster_positions(glyph, count)) } /// Text blob of the whole run at its shaped positions. - pub(super) fn blob(&self) -> Option { + pub(crate) fn blob(&self) -> Option { self.text_blob(0, self.glyphs.len(), &self.positions) } /// Text blob of one glyph at its pen origin. The first glyph of a /// cluster carries the cluster's source text; the rest are glyph-only. - pub(super) fn glyph_blob(&self, glyph: usize) -> Option { + pub(crate) fn glyph_blob(&self, glyph: usize) -> Option { self.glyphs.get(glyph)?; let starts_cluster = glyph == 0 || self.clusters.get(glyph) != self.clusters.get(glyph - 1); if starts_cluster { @@ -187,7 +187,7 @@ impl ShapedRun { } /// Local-y shift that centres a cluster's ink after a 90° rotation. - pub(super) fn cluster_rotated_baseline_shift(&self, glyph: usize, count: usize) -> f32 { + pub(crate) fn cluster_rotated_baseline_shift(&self, glyph: usize, count: usize) -> f32 { rotated_run_baseline_shift( &self.font, &self.glyphs[glyph..glyph + count], @@ -197,7 +197,7 @@ impl ShapedRun { /// Spreads the glyphs by `letter_spacing` each, like horizontal /// per-glyph spacing. Returns the spaced run advance. - pub(super) fn spread_glyphs(&mut self, letter_spacing: f32) -> f32 { + pub(crate) fn spread_glyphs(&mut self, letter_spacing: f32) -> f32 { if letter_spacing != 0.0 { for (i, position) in self.positions.iter_mut().enumerate() { position.x += letter_spacing * i as f32; @@ -209,7 +209,7 @@ impl ShapedRun { /// Sets ASCII word spaces in a sideways Western run to JLREQ's one-third /// em. The space keeps its scalar and break opportunity; only its advance /// and the following glyph positions change. - pub(super) fn normalize_word_spaces(&mut self, segment_text: &str, font_size: f32) { + pub(crate) fn normalize_word_spaces(&mut self, segment_text: &str, font_size: f32) { let clusters: Vec<(usize, usize)> = self.cluster_spans().collect(); let mut accumulated_shift = 0.0f32; for (glyph, count) in clusters { @@ -324,7 +324,7 @@ fn font_feature(font_features: FontFeatures) -> Option { /// Shape one orientation segment on a single unbounded line. Upright /// segments get the OpenType vertical substitution features so CJK /// punctuation and brackets take their vertical forms. -pub(super) fn shape_segment( +pub(crate) fn shape_segment( text: &str, font: &Font, upright: bool, @@ -516,7 +516,7 @@ fn glyph_run_ink_bounds( /// Offset that centres a local-y band on the column axis after the run is /// rotated 90°. -pub(super) fn rotated_baseline_shift(top: f32, bottom: f32) -> f32 { +pub(crate) fn rotated_baseline_shift(top: f32, bottom: f32) -> f32 { -(top + bottom) / 2.0 } @@ -547,9 +547,9 @@ fn rotated_run_baseline_shift(font: &Font, glyphs: &[GlyphId], positions: &[SkPo #[cfg(test)] mod tests { - use super::super::layout::CellKind; - use super::super::test_support::*; use super::*; + use crate::shapes::text_vertical::test_support::*; + use crate::shapes::text_vertical::CellKind; #[test] fn selected_language_reaches_horizontal_and_vertical_localized_glyphs() { diff --git a/render-wasm/src/shapes/text_vertical/annotations.rs b/render-wasm/src/shapes/text_vertical/annotations.rs index dbe3df7be3..94ab790913 100644 --- a/render-wasm/src/shapes/text_vertical/annotations.rs +++ b/render-wasm/src/shapes/text_vertical/annotations.rs @@ -21,8 +21,10 @@ use super::flow::FlowCell; use super::layout::{ column_base_center, CellKind, ParagraphTextMap, VerticalCell, VerticalColumn, VerticalLayout, }; -use super::orientation::segment_by_orientation; -use super::shaping::{shape_segment, shape_segment_with_fallbacks, span_font_families, ShapedRun}; +use crate::shapes::text_shaping::segment_by_orientation; +use crate::shapes::text_shaping::{ + shape_segment, shape_segment_with_fallbacks, span_font_families, ShapedRun, +}; /// One shaped ruby glyph, retaining its fallback-font run and source range. #[derive(Debug, Clone, Copy)] @@ -739,9 +741,10 @@ pub(super) fn emphasis_mark_center(layout: &VerticalLayout, mark: &EmphasisMark) #[cfg(test)] mod tests { use super::super::layout::VerticalLayout; - use super::super::positions::{position_data, DIRECTION_VERTICAL_RUBY}; + use super::super::positions::position_data; use super::super::test_support::*; use super::*; + use crate::shapes::DIRECTION_VERTICAL_RUBY; use crate::shapes::{AnnotationClearance, GrowType, RubySize}; use crate::shapes::{ TextAlign, TextEmphasis, TextOrientation, TextSpan, TextTransform, VerticalAlign, @@ -772,7 +775,6 @@ mod tests { #[test] fn zhuyin_is_upright_per_base_and_tones_have_exact_source_ranges() { - use crate::shapes::text_zhuyin; let content = spans_content( vec![ TextSpan { @@ -814,7 +816,7 @@ mod tests { ); let readings: Vec<_> = entries .iter() - .filter(|p| p.direction == text_zhuyin::DIRECTION_ZHUYIN) + .filter(|p| p.direction == crate::shapes::DIRECTION_ZHUYIN) .collect(); assert_eq!(readings.len(), 6); assert!(readings diff --git a/render-wasm/src/shapes/text_vertical/cells.rs b/render-wasm/src/shapes/text_vertical/cells.rs index dd890557fc..5286f2a907 100644 --- a/render-wasm/src/shapes/text_vertical/cells.rs +++ b/render-wasm/src/shapes/text_vertical/cells.rs @@ -14,12 +14,14 @@ use crate::shapes::text_warichu::{warichu_split_chars, WARICHU_FONT_SCALE}; use crate::shapes::{FontFeatures, TextCombineUpright, TextSpan}; use super::flow::{min_squeezed_extent, FlowCell, FlowScript, FIT_TOLERANCE}; -use super::font_tables::{upright_baseline, vertical_metrics, vpal_table}; use super::layout::{CellKind, VerticalCell}; -use super::orientation::{ +use crate::shapes::text_shaping::{ is_upright_char, segment_by_orientation, uses_rotated_vertical_fallback, Segment, }; -use super::shaping::{resolve_typefaces, shape_with_typefaces, span_font_families, ShapedRun}; +use crate::shapes::text_shaping::{ + resolve_typefaces, shape_with_typefaces, span_font_families, ShapedRun, +}; +use crate::shapes::text_shaping::{upright_baseline, vertical_metrics, vpal_table}; /// Smallest horizontal scale for `all` tate-chu-yoko, except short Western /// runs; digit modes use 1 / run length. @@ -757,9 +759,9 @@ impl<'a> WarichuNote<'a> { #[cfg(test)] mod tests { use super::super::layout::VerticalCell; - use super::super::shaping::WESTERN_WORD_SPACING_EM; use super::super::test_support::*; use super::*; + use crate::shapes::text_shaping::WESTERN_WORD_SPACING_EM; use crate::shapes::{Fill, SolidColor, TextDecoration, TextOrientation}; use skia_safe as skia; diff --git a/render-wasm/src/shapes/text_vertical/layout.rs b/render-wasm/src/shapes/text_vertical/layout.rs index be3021f6bc..737ba2e5c8 100644 --- a/render-wasm/src/shapes/text_vertical/layout.rs +++ b/render-wasm/src/shapes/text_vertical/layout.rs @@ -30,7 +30,7 @@ use super::flow::{ plan_with_edge_trimming, preferred_pair_spacing, shed_punctuation_aki, FlowCell, FIT_TOLERANCE, }; use super::paint::LayoutDraws; -use super::shaping::ShapedRun; +use crate::shapes::text_shaping::ShapedRun; #[derive(Debug, Clone, Copy)] pub enum CellKind { diff --git a/render-wasm/src/shapes/text_vertical/mod.rs b/render-wasm/src/shapes/text_vertical/mod.rs index 2fc3de0c73..e8b5cab249 100644 --- a/render-wasm/src/shapes/text_vertical/mod.rs +++ b/render-wasm/src/shapes/text_vertical/mod.rs @@ -2,8 +2,8 @@ // right->left. Skia's skparagraph has no vertical writing mode, so this // module owns the whole vertical pipeline: // -// segment spans by glyph orientation (`orientation`) -> shape each segment -// with SkShaper, OpenType `vert`/`vrt2` for upright runs (`shaping`) -> +// segment spans by glyph orientation and shape each segment with +// SkShaper, OpenType `vert`/`vrt2` for upright runs (`text_shaping`) -> // derive cells: one per upright glyph cluster, one per rotated run, one per // composite (`cells`) -> JLREQ spacing and kinsoku-aware column breaks // (`flow`) -> place columns and annotations (`layout`, `annotations`) -> @@ -31,26 +31,18 @@ mod annotations; mod cells; mod flow; -mod font_tables; mod layout; -mod orientation; mod paint; mod positions; -mod shaping; #[cfg(test)] pub(crate) mod test_support; -pub(crate) use font_tables::upright_centre_metrics; +#[cfg(test)] +pub(crate) use layout::CellKind; pub use layout::{block_axis_offset, layout_for_rect, VerticalLayout}; -pub(crate) use orientation::{is_emoji_char, is_upright_char}; pub use paint::{ paint_drop_shadow, paint_grid, paint_layout, paint_stroke, paint_text_vertical, paint_text_vertical_with, vertical_text_paths, GlyphOutput, }; -pub use positions::{ - caret_from_point, caret_rect, intersects, position_data, range_rects, DIRECTION_EMPHASIS_MARK, -}; -pub(crate) use shaping::{ - shape_segment_with_fallbacks, single_glyph_blob, span_font_families, ShapedRun, -}; +pub use positions::{caret_from_point, caret_rect, intersects, position_data, range_rects}; diff --git a/render-wasm/src/shapes/text_vertical/paint.rs b/render-wasm/src/shapes/text_vertical/paint.rs index 39357c8259..40df7f4c1c 100644 --- a/render-wasm/src/shapes/text_vertical/paint.rs +++ b/render-wasm/src/shapes/text_vertical/paint.rs @@ -8,9 +8,9 @@ use crate::math::Rect; use crate::shapes::{Stroke, StrokeKind, TextContent, VerticalAlign}; use super::annotations::{emphasis_mark_center, EmphasisMark, RubyCell}; -use super::font_tables::{upright_baseline, upright_centre_metrics}; use super::layout::{column_base_center, CellKind, VerticalCell, VerticalLayout}; -use super::shaping::{single_glyph_blob, ShapedRun}; +use crate::shapes::text_shaping::{single_glyph_blob, ShapedRun}; +use crate::shapes::text_shaping::{upright_baseline, upright_centre_metrics}; /// How glyphs reach the canvas. #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/render-wasm/src/shapes/text_vertical/positions.rs b/render-wasm/src/shapes/text_vertical/positions.rs index e1dda69925..3e9fceb282 100644 --- a/render-wasm/src/shapes/text_vertical/positions.rs +++ b/render-wasm/src/shapes/text_vertical/positions.rs @@ -3,20 +3,14 @@ use std::ops::Range; use skia_safe::{Contains, Point as SkPoint}; use crate::math::Rect; -use crate::shapes::{PositionData, VerticalAlign}; +use crate::shapes::{ + PositionData, VerticalAlign, DIRECTION_EMPHASIS_MARK, DIRECTION_VERTICAL_RL, + DIRECTION_VERTICAL_RUBY, DIRECTION_ZHUYIN, +}; use super::annotations::emphasis_mark_center; use super::layout::{column_base_center, CellKind, VerticalCell, VerticalLayout}; -/// Position-data `direction` of a vertical-rl strip; 0/1 are rtl/ltr. -pub const DIRECTION_VERTICAL_RL: u32 = 2; - -/// Position-data `direction` of a ruby strip; offsets index the *ruby* string. -pub const DIRECTION_VERTICAL_RUBY: u32 = 3; - -/// Position-data `direction` of one emphasis mark (圏点) box, in any mode. -pub const DIRECTION_EMPHASIS_MARK: u32 = 4; - /// Paragraph source UTF-16 range of a `transformed` span-text range. fn source_utf16_range( layout: &VerticalLayout, @@ -214,7 +208,7 @@ fn ruby_entries(layout: &VerticalLayout, (origin_x, origin_y): (f32, f32)) -> Ve y: origin_y + origin.y - ruby.font_size, width: ruby.font_size, height: ruby.font_size, - direction: super::super::text_zhuyin::DIRECTION_ZHUYIN, + direction: DIRECTION_ZHUYIN, }) .collect::>(); } diff --git a/render-wasm/src/shapes/text_vertical/test_support.rs b/render-wasm/src/shapes/text_vertical/test_support.rs index 105290f9d4..93aec7b896 100644 --- a/render-wasm/src/shapes/text_vertical/test_support.rs +++ b/render-wasm/src/shapes/text_vertical/test_support.rs @@ -41,7 +41,7 @@ pub(crate) fn provider(font: &[u8]) -> TypefaceFontProvider { } /// `provider(font)` plus `fallback` registered as the "fallback" family. -pub(super) fn provider_with_fallback(font: &[u8], fallback: &[u8]) -> TypefaceFontProvider { +pub(crate) fn provider_with_fallback(font: &[u8], fallback: &[u8]) -> TypefaceFontProvider { let mut provider = provider(font); let typeface = FontMgr::new() .new_from_data(skia_safe::Data::new_copy(fallback), None) @@ -50,7 +50,7 @@ pub(super) fn provider_with_fallback(font: &[u8], fallback: &[u8]) -> TypefaceFo provider } -pub(super) fn make_span(text: &str) -> TextSpan { +pub(crate) fn make_span(text: &str) -> TextSpan { TextSpan { text: text.to_string(), font_size: EM, @@ -59,7 +59,7 @@ pub(super) fn make_span(text: &str) -> TextSpan { } } -pub(super) fn vertical_paragraph( +pub(crate) fn vertical_paragraph( spans: Vec, align: TextAlign, line_height: f32, @@ -93,7 +93,7 @@ pub(crate) fn content_of( } /// One left-aligned paragraph holding `spans`. -pub(super) fn spans_content(spans: Vec, height: f32) -> TextContent { +pub(crate) fn spans_content(spans: Vec, height: f32) -> TextContent { content_of( vec![vertical_paragraph(spans, TextAlign::Left, 1.0)], height, @@ -102,7 +102,7 @@ pub(super) fn spans_content(spans: Vec, height: f32) -> TextContent { } /// One paragraph per text, each a single span. -pub(super) fn make_content(texts: &[&str], height: f32) -> TextContent { +pub(crate) fn make_content(texts: &[&str], height: f32) -> TextContent { let paragraphs = texts .iter() .map(|text| vertical_paragraph(vec![make_span(text)], TextAlign::Left, 1.0)) @@ -111,11 +111,11 @@ pub(super) fn make_content(texts: &[&str], height: f32) -> TextContent { } /// One paragraph with a span per text. -pub(super) fn make_content_with_spans(texts: &[&str], height: f32) -> TextContent { +pub(crate) fn make_content_with_spans(texts: &[&str], height: f32) -> TextContent { spans_content(texts.iter().map(|text| make_span(text)).collect(), height) } -pub(super) fn ruby_content(base: &str, ruby: &str, height: f32) -> TextContent { +pub(crate) fn ruby_content(base: &str, ruby: &str, height: f32) -> TextContent { spans_content( vec![TextSpan { ruby: ruby.to_string(), @@ -125,7 +125,7 @@ pub(super) fn ruby_content(base: &str, ruby: &str, height: f32) -> TextContent { ) } -pub(super) fn warichu_content(base: &str, height: f32) -> TextContent { +pub(crate) fn warichu_content(base: &str, height: f32) -> TextContent { spans_content( vec![TextSpan { warichu: true, @@ -136,7 +136,7 @@ pub(super) fn warichu_content(base: &str, height: f32) -> TextContent { ) } -pub(super) fn spaced_content(text: &str, letter_spacing: f32) -> TextContent { +pub(crate) fn spaced_content(text: &str, letter_spacing: f32) -> TextContent { spans_content( vec![TextSpan { letter_spacing, @@ -146,7 +146,7 @@ pub(super) fn spaced_content(text: &str, letter_spacing: f32) -> TextContent { ) } -pub(super) fn decorated_content(text: &str, decoration: TextDecoration) -> TextContent { +pub(crate) fn decorated_content(text: &str, decoration: TextDecoration) -> TextContent { spans_content( vec![TextSpan { text_decoration: Some(decoration), @@ -156,7 +156,7 @@ pub(super) fn decorated_content(text: &str, decoration: TextDecoration) -> TextC ) } -pub(super) fn layout_with_fallback( +pub(crate) fn layout_with_fallback( provider: &TypefaceFontProvider, content: &TextContent, max_height: f32, @@ -181,7 +181,7 @@ pub(crate) fn layout_with_height( } /// Layout with `provider` and a 1000px wrap budget. -pub(super) fn layout_with( +pub(crate) fn layout_with( provider: &TypefaceFontProvider, content: &TextContent, ) -> VerticalLayout { @@ -189,6 +189,6 @@ pub(super) fn layout_with( } /// Layout with the Latin test face. -pub(super) fn layout_content(content: &TextContent, max_height: f32) -> VerticalLayout { +pub(crate) fn layout_content(content: &TextContent, max_height: f32) -> VerticalLayout { layout_with_height(&provider(TEST_FONT), content, max_height) } diff --git a/render-wasm/src/shapes/text_zhuyin.rs b/render-wasm/src/shapes/text_zhuyin.rs index 826bb4d07d..da056b1758 100644 --- a/render-wasm/src/shapes/text_zhuyin.rs +++ b/render-wasm/src/shapes/text_zhuyin.rs @@ -5,13 +5,12 @@ use std::ops::Range; use skia_safe::{self as skia, textlayout::TypefaceFontProvider, FontMgr}; use super::cjk_chars::is_zhuyin_base; -use super::text_vertical::{shape_segment_with_fallbacks, span_font_families, ShapedRun}; +use super::text_shaping::{shape_segment_with_fallbacks, span_font_families, ShapedRun}; use super::TextSpan; use crate::render::FallbackFonts; pub(crate) const FONT_SCALE: f32 = 0.3; pub(crate) const STRIP_WIDTH: f32 = 0.6; -pub(crate) const DIRECTION_ZHUYIN: u32 = 5; pub(crate) struct Slot { pub ch: char,