mirror of
https://github.com/penpot/penpot.git
synced 2026-11-06 01:50:39 +00:00
♻️ 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
This commit is contained in:
parent
fc0f7d72a2
commit
177929fd1a
@ -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;
|
||||
|
||||
@ -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<Paragraph> {
|
||||
@ -2945,6 +2945,23 @@ pub struct TextLayoutData {
|
||||
pub paragraphs: Vec<ParagraphLayout>,
|
||||
}
|
||||
|
||||
// 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,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -46,7 +46,7 @@ fn table_data(font: &Font, tag: &[u8; 4]) -> Option<skia_safe::Data> {
|
||||
/// 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<u16>,
|
||||
@ -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<Self> {
|
||||
pub(crate) fn from_font(font: &Font) -> Option<Self> {
|
||||
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<Rc<VerticalMetrics>> {
|
||||
pub(crate) fn vertical_metrics(font: &Font) -> Option<Rc<VerticalMetrics>> {
|
||||
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<u16, VpalDelta>,
|
||||
}
|
||||
@ -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<Rc<VpalTable>> {
|
||||
pub(crate) fn vpal_table(font: &Font) -> Option<Rc<VpalTable>> {
|
||||
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]
|
||||
12
render-wasm/src/shapes/text_shaping/mod.rs
Normal file
12
render-wasm/src/shapes/text_shaping/mod.rs
Normal file
@ -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::*;
|
||||
@ -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<Segment> {
|
||||
pub(crate) fn segment_by_orientation(text: &str, orientation: TextOrientation) -> Vec<Segment> {
|
||||
let mut segments: Vec<Segment> = Vec::new();
|
||||
let mut utf16_offset = 0;
|
||||
for grapheme in text.graphemes(true) {
|
||||
@ -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<Item = (usize, usize)> + '_ {
|
||||
pub(crate) fn cluster_spans(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
|
||||
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<Vec<f32>> {
|
||||
pub(crate) fn scalar_flow_offsets(&self, extent: f32, chars: usize) -> Option<Vec<f32>> {
|
||||
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<skia::Rect> {
|
||||
pub(crate) fn cluster_ink_bounds(&self, glyph: usize, count: usize) -> Option<skia::Rect> {
|
||||
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<skia::Rect> {
|
||||
pub(crate) fn ink_bounds(&self) -> Option<skia::Rect> {
|
||||
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<TextBlob> {
|
||||
pub(crate) fn cluster_blob(&self, glyph: usize, count: usize) -> Option<TextBlob> {
|
||||
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<TextBlob> {
|
||||
pub(crate) fn blob(&self) -> Option<TextBlob> {
|
||||
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<TextBlob> {
|
||||
pub(crate) fn glyph_blob(&self, glyph: usize) -> Option<TextBlob> {
|
||||
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<Feature> {
|
||||
/// 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() {
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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::<Vec<_>>();
|
||||
}
|
||||
|
||||
@ -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<TextSpan>,
|
||||
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<TextSpan>, height: f32) -> TextContent {
|
||||
pub(crate) fn spans_content(spans: Vec<TextSpan>, 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<TextSpan>, 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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user