mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-02 22:20:50 -06:00
Fix #4978: Area Type Options › Text Flow by rows or by columns
This commit is contained in:
@@ -18,6 +18,7 @@ pub fn layout(db: &FontDb, t: &TextObject) -> TextLayout {
|
||||
first_baseline: a.first_baseline,
|
||||
first_baseline_min: a.first_baseline_min,
|
||||
vertical_align: a.vertical_align,
|
||||
flow: a.flow,
|
||||
fit: a.fit,
|
||||
..LayoutOptions::default()
|
||||
};
|
||||
|
||||
@@ -142,9 +142,13 @@ impl Region {
|
||||
let cw = ((bbox.width() - gutter * (cols - 1) as f64) / cols as f64).max(0.0);
|
||||
let rh = ((bbox.height() - gutter * (rows - 1) as f64) / rows as f64).max(0.0);
|
||||
let mut out = Vec::with_capacity(rows * cols);
|
||||
// Text flows down each column, then across (Illustrator's default "by columns").
|
||||
for c in 0..cols {
|
||||
for r in 0..rows {
|
||||
// Text Flow: down each column, then across (by columns, the default), or across each row,
|
||||
// then down (by rows).
|
||||
let by_rows = opts.flow == vectorcraft_doc::TextFlow::Rows;
|
||||
let (outer, inner) = if by_rows { (rows, cols) } else { (cols, rows) };
|
||||
for o in 0..outer {
|
||||
for i in 0..inner {
|
||||
let (r, c) = if by_rows { (o, i) } else { (i, o) };
|
||||
let x0 = bbox.x0 + c as f64 * (cw + gutter);
|
||||
let y0 = bbox.y0 + r as f64 * (rh + gutter);
|
||||
let cell = if rows * cols == 1 { bbox } else { Rect::new(x0, y0, x0 + cw, y0 + rh) };
|
||||
|
||||
@@ -47,7 +47,7 @@ use kurbo::{Affine, BezPath, Point, Rect, Shape, Vec2};
|
||||
pub use layout::{decorations, fit_first_line, layout, layout_with, scaled};
|
||||
pub use vectorcraft_doc::TextObject;
|
||||
|
||||
pub use vectorcraft_doc::{AreaFit, FirstBaseline, VerticalAlign};
|
||||
pub use vectorcraft_doc::{AreaFit, FirstBaseline, TextFlow, VerticalAlign};
|
||||
|
||||
/// Paragraph composer; stored per text object in [`vectorcraft_doc::ParaStyle::composer`].
|
||||
pub use vectorcraft_doc::Composer;
|
||||
@@ -56,7 +56,7 @@ pub use vectorcraft_doc::Composer;
|
||||
/// composer, OpenType features). [`layout`] takes rows/columns/inset/first baseline from the object.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LayoutOptions {
|
||||
/// Area type rows and columns (text flows down each column, then across). Layout clamps each
|
||||
/// Area type rows and columns (text fills them in [`LayoutOptions::flow`] order). Layout clamps each
|
||||
/// count to 1..=[`vectorcraft_doc::AreaOptions::MAX_ROWS_COLUMNS`].
|
||||
pub rows: usize,
|
||||
pub columns: usize,
|
||||
@@ -69,6 +69,8 @@ pub struct LayoutOptions {
|
||||
pub first_baseline_min: f64,
|
||||
/// Vertical alignment of the lines in each row/column (Area Type Options "Align").
|
||||
pub vertical_align: VerticalAlign,
|
||||
/// Area Type Options "Text Flow": by columns (down each column, then across) or by rows.
|
||||
pub flow: TextFlow,
|
||||
/// Area type only: Shrink Text to Fit scales overflowing text down at layout time (see
|
||||
/// [`TextLayout::fit_scale`]); the other fits are the engine's business and lay out as `None`.
|
||||
pub fit: AreaFit,
|
||||
@@ -87,6 +89,7 @@ impl Default for LayoutOptions {
|
||||
first_baseline: FirstBaseline::Ascent,
|
||||
first_baseline_min: 0.0,
|
||||
vertical_align: VerticalAlign::Top,
|
||||
flow: TextFlow::Columns,
|
||||
fit: AreaFit::None,
|
||||
composer: None,
|
||||
features: OtFeatures::default(),
|
||||
|
||||
@@ -457,6 +457,26 @@ fn hit_testing_picks_the_right_column() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Area Type Options › Text Flow (#4978): by columns (the default) the second cell is the one
|
||||
/// below the first; by rows it is the one beside it.
|
||||
#[test]
|
||||
fn text_flow_fills_the_cells_by_columns_or_by_rows() {
|
||||
let t = area(©.repeat(8), style(12.0), Rect::new(0.0, 0.0, 420.0, 300.0), Justify::Left);
|
||||
let grid = LayoutOptions { rows: 2, columns: 2, gutter: 20.0, ..Default::default() };
|
||||
for (flow, second) in [(TextFlow::Columns, (0.0, 160.0)), (TextFlow::Rows, (220.0, 0.0))] {
|
||||
let l = layout_with(db(), &t, &LayoutOptions { flow, ..grid.clone() });
|
||||
let frame = l.frames.get(1).copied().unwrap();
|
||||
assert_eq!((frame.x0, frame.y0), second, "{flow:?}");
|
||||
let line = l.lines.iter().find(|li| li.region == 1).expect("text reaches the second cell");
|
||||
assert!(line.x0 >= second.0 - 1e-6 && line.baseline >= second.1, "{flow:?}: {line:?}");
|
||||
assert!(l.lines.iter().all(|li| l.lines.iter().all(|m| m.region >= li.region || m.start <= li.start)), "{flow:?}: text runs in cell order");
|
||||
}
|
||||
// The object's own option lays out the same way.
|
||||
let mut by_rows = t.clone();
|
||||
(by_rows.area.rows, by_rows.area.columns, by_rows.area.gutter, by_rows.area.flow) = (2, 2, 20.0, TextFlow::Rows);
|
||||
assert_eq!(layout(db(), &by_rows).frames.get(1).map(|f| (f.x0, f.y0)), Some((220.0, 0.0)));
|
||||
}
|
||||
|
||||
// ---------- area flow ----------
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user