mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-27 11:46:51 -06:00
T: Type on a Path — tool, options, screen and PDF
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
bfe4220840
commit
eb4fa9c64c
@@ -303,7 +303,37 @@ pub fn frame_specs(doc: &Document, sid: StoryId) -> Vec<FrameSpec> {
|
||||
// Grid lines are at spread y = start + n·inc (page tops are y = 0); map into inner space (translation only).
|
||||
let ty = xf.translation().y;
|
||||
let grid = (inc > 0.0).then_some((start - ty, inc));
|
||||
out.push(FrameSpec { id: fid, area: item.text_area(), opts: tf.options.clone(), exclusions, page_name, page, grid, left_page });
|
||||
// Type on a path: one line as long as the path (from the start offset).
|
||||
let (area, opts) = match &tf.options.path {
|
||||
Some(pt) => {
|
||||
let len = designcraft_geom::warp::PathWarp::new(&item.path.to_bezpath(), false).length();
|
||||
let size = st
|
||||
.paras
|
||||
.first()
|
||||
.map(|p| doc.styles.resolve_para(p).1)
|
||||
.map(|base| st.runs().map(|(_, f)| doc.styles.resolve_char(&base, f).size).fold(base.size, f64::max))
|
||||
.unwrap_or(12.0);
|
||||
let opts = TextFrameOptions {
|
||||
columns: 1,
|
||||
inset: [0.0; 4],
|
||||
first_baseline: designcraft_doc::FirstBaseline::Ascent,
|
||||
path: None,
|
||||
..tf.options.clone()
|
||||
};
|
||||
(Rect::new(0.0, 0.0, (len - pt.start).max(1.0), size * 1.6), opts)
|
||||
}
|
||||
None => (item.text_area(), tf.options.clone()),
|
||||
};
|
||||
out.push(FrameSpec {
|
||||
id: fid,
|
||||
area,
|
||||
opts,
|
||||
exclusions: if tf.options.path.is_some() { vec![] } else { exclusions },
|
||||
page_name,
|
||||
page,
|
||||
grid,
|
||||
left_page,
|
||||
});
|
||||
}
|
||||
out
|
||||
}
|
||||
@@ -1593,3 +1623,53 @@ fn line_dist(l: &Line, y: f64) -> f64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Each visible glyph of a type-on-a-path frame placed on the path: its outline in frame space
|
||||
/// (grouped by run style).
|
||||
pub fn path_glyphs(
|
||||
cs: &ComposedStory,
|
||||
ft: &FrameText,
|
||||
path: &designcraft_geom::BezPath,
|
||||
pt: &designcraft_doc::PathType,
|
||||
) -> Vec<(u32, designcraft_geom::BezPath)> {
|
||||
let db = designcraft_fonts::FontDb::global();
|
||||
let warp = designcraft_geom::warp::PathWarp::new(path, pt.flip);
|
||||
let mut runs: Vec<(u32, designcraft_geom::BezPath)> = Vec::new();
|
||||
for l in &ft.lines {
|
||||
// The part of the type that sits on the path.
|
||||
let on = match pt.align {
|
||||
designcraft_doc::PathAlign::Baseline => l.baseline,
|
||||
designcraft_doc::PathAlign::Ascender => l.baseline - l.ascent,
|
||||
designcraft_doc::PathAlign::Descender => l.baseline + l.descent,
|
||||
designcraft_doc::PathAlign::Center => l.baseline - (l.ascent - l.descent) / 2.0,
|
||||
};
|
||||
for g in l.glyphs.iter().filter(|g| g.visible) {
|
||||
let outline = db.outline(&g.face, g.gid);
|
||||
if outline.elements().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let style = &cs.styles[g.style as usize];
|
||||
let mid = g.x + g.adv / 2.0;
|
||||
let Some((p, a)) = warp.at(pt.start + mid) else { continue };
|
||||
let place = designcraft_geom::Affine::translate(p.to_vec2())
|
||||
* designcraft_geom::Affine::rotate(a)
|
||||
* designcraft_geom::Affine::translate((-mid, -on));
|
||||
let skew = if style.skew != 0.0 {
|
||||
designcraft_geom::Affine::new([1.0, 0.0, -style.skew.to_radians().tan(), 1.0, 0.0, 0.0])
|
||||
} else {
|
||||
designcraft_geom::Affine::IDENTITY
|
||||
};
|
||||
let m =
|
||||
place * designcraft_geom::Affine::translate((g.x, l.baseline + g.y)) * skew * designcraft_geom::Affine::scale_non_uniform(g.sx, g.sy);
|
||||
let bp = match runs.iter_mut().find(|r| r.0 == g.style) {
|
||||
Some(r) => &mut r.1,
|
||||
None => {
|
||||
runs.push((g.style, designcraft_geom::BezPath::new()));
|
||||
&mut runs.last_mut().expect("pushed").1
|
||||
}
|
||||
};
|
||||
bp.extend((m * outline.as_ref().clone()).elements().iter().copied());
|
||||
}
|
||||
}
|
||||
runs
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user