Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions examples/swash_render/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use image::codecs::png::PngEncoder;
use image::{self, Pixel, Rgba, RgbaImage};
use parley::layout::{Alignment, Glyph, GlyphRun, Layout, PositionedLayoutItem};
use parley::style::{FontStack, FontWeight, StyleProperty, TextStyle};
use parley::{AlignmentOptions, FontContext, InlineBox, LayoutContext, LineHeight};
use parley::{AlignmentOptions, FontContext, InlineBox, InlineBoxKind, LayoutContext, LineHeight};
use std::fs::File;
use swash::FontRef;
use swash::scale::image::Content;
Expand Down Expand Up @@ -94,6 +94,7 @@ fn main() {

builder.push_inline_box(InlineBox {
id: 0,
kind: InlineBoxKind::InFlow,
index: 0,
width: 50.0,
height: 50.0,
Expand All @@ -103,6 +104,7 @@ fn main() {

builder.push_inline_box(InlineBox {
id: 1,
kind: InlineBoxKind::InFlow,
index: 50,
width: 50.0,
height: 30.0,
Expand Down Expand Up @@ -152,12 +154,14 @@ fn main() {

builder.push_inline_box(InlineBox {
id: 0,
kind: InlineBoxKind::InFlow,
index: 40,
width: 50.0,
height: 50.0,
});
builder.push_inline_box(InlineBox {
id: 1,
kind: InlineBoxKind::InFlow,
index: 50,
width: 50.0,
height: 30.0,
Expand All @@ -171,7 +175,7 @@ fn main() {

// Perform layout (including bidi resolution and shaping) with start alignment
layout.break_all_lines(max_advance);
layout.align(max_advance, Alignment::Start, AlignmentOptions::default());
layout.align(Alignment::Start, AlignmentOptions::default());

// Create image to render into
let width = layout.width().ceil() as u32 + (padding * 2);
Expand Down
5 changes: 3 additions & 2 deletions examples/tiny_skia_render/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use parley::{
Alignment, AlignmentOptions, FontContext, FontWeight, GenericFamily, GlyphRun, InlineBox,
Layout, LayoutContext, LineHeight, PositionedLayoutItem, StyleProperty,
InlineBoxKind, Layout, LayoutContext, LineHeight, PositionedLayoutItem, StyleProperty,
};
use skrifa::{
GlyphId, MetadataProvider, OutlineGlyph,
Expand Down Expand Up @@ -88,6 +88,7 @@ fn main() {

builder.push_inline_box(InlineBox {
id: 0,
kind: InlineBoxKind::InFlow,
index: 40,
width: 50.0,
height: 50.0,
Expand All @@ -98,7 +99,7 @@ fn main() {

// Perform layout (including bidi resolution and shaping) with start alignment
layout.break_all_lines(max_advance);
layout.align(max_advance, Alignment::Start, AlignmentOptions::default());
layout.align(Alignment::Start, AlignmentOptions::default());
let width = layout.width().ceil() as u32;
let height = layout.height().ceil() as u32;
let padded_width = width + padding * 2;
Expand Down
14 changes: 9 additions & 5 deletions parley/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use super::layout::Layout;
use alloc::string::String;
use core::ops::RangeBounds;

use crate::InlineBoxKind;
use crate::inline_box::InlineBox;
use crate::resolve::tree::ItemKind;

Expand Down Expand Up @@ -112,11 +113,14 @@ impl<B: Brush> TreeBuilder<'_, B> {
}

pub fn push_inline_box(&mut self, mut inline_box: InlineBox) {
self.lcx.tree_style_builder.push_uncommitted_text(false);
self.lcx.tree_style_builder.set_is_span_first(false);
self.lcx
.tree_style_builder
.set_last_item_kind(ItemKind::InlineBox);
if inline_box.kind == InlineBoxKind::InFlow {
self.lcx.tree_style_builder.push_uncommitted_text(false);
self.lcx.tree_style_builder.set_is_span_first(false);
self.lcx
.tree_style_builder
.set_last_item_kind(ItemKind::InlineBox);
}

// TODO: arrange type better here to factor out the index
inline_box.index = self.lcx.tree_style_builder.current_text_len();
self.lcx.inline_boxes.push(inline_box);
Expand Down
8 changes: 4 additions & 4 deletions parley/src/editing/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,9 @@ fn cursor_rect<B: Brush>(cluster: &Cluster<'_, B>, at_end: bool, size: f32) -> B
let metrics = line.metrics();
BoundingBox::new(
line_x as f64,
metrics.min_coord as f64,
metrics.block_min_coord as f64,
(line_x + size) as f64,
metrics.max_coord as f64,
metrics.block_max_coord as f64,
)
}

Expand All @@ -454,9 +454,9 @@ fn last_line_cursor_rect<B: Brush>(layout: &Layout<B>, size: f32) -> BoundingBox
let metrics = line.metrics();
BoundingBox::new(
0.0,
metrics.min_coord as f64,
metrics.block_min_coord as f64,
size as f64,
metrics.max_coord as f64,
metrics.block_max_coord as f64,
)
} else {
BoundingBox::default()
Expand Down
2 changes: 1 addition & 1 deletion parley/src/editing/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ where
self.layout = builder.build(&self.buffer);
self.layout.break_all_lines(self.width);
self.layout
.align(self.width, self.alignment, AlignmentOptions::default());
.align(self.alignment, AlignmentOptions::default());
self.selection = self.selection.refresh(&self.layout);
self.layout_dirty = false;
self.generation.nudge();
Expand Down
10 changes: 5 additions & 5 deletions parley/src/editing/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ impl Selection {
let h_pos = self
.h_pos
.unwrap_or_else(|| self.focus.geometry(layout, 0.0).x0 as f32);
let y = line.metrics().max_coord - line.metrics().ascent * 0.5;
let y = line.metrics().block_max_coord - line.metrics().ascent * 0.5;
let new_focus = Cursor::from_point(layout, h_pos, y);
let h_pos = Some(h_pos);
if extend {
Expand Down Expand Up @@ -531,8 +531,8 @@ impl Selection {
continue;
};
let metrics = line.metrics();
let line_min = metrics.min_coord as f64;
let line_max = metrics.max_coord as f64;
let line_min = metrics.block_min_coord as f64;
let line_max = metrics.block_max_coord as f64;
// Trailing whitespace to indicate that the newline character at the
// end of this line is selected. It's based on the ascent and
// descent so it doesn't change with the line height.
Expand All @@ -547,7 +547,7 @@ impl Selection {
if line_ix == line_start_ix || line_ix == line_end_ix {
// We only need to run the expensive logic on the first and
// last lines
let mut start_x = metrics.offset as f64;
let mut start_x = metrics.offset as f64 + metrics.inline_min_coord as f64;
let mut cur_x = start_x;
let mut cluster_count = 0;
let mut box_advance = 0.0;
Expand Down Expand Up @@ -598,7 +598,7 @@ impl Selection {
);
}
} else {
let x = metrics.offset as f64;
let x = metrics.offset as f64 + metrics.inline_min_coord as f64;
let width = metrics.advance as f64;
f(
BoundingBox::new(x, line_min, x + width + newline_whitespace, line_max),
Expand Down
23 changes: 23 additions & 0 deletions parley/src/inline_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub struct InlineBox {
/// User-specified identifier for the box, which can be used by the user to determine which box in
/// parley's output corresponds to which box in its input.
pub id: u64,
/// Whether the box is in-flow (takes up space in the layout) or out-of-flow (e.g. absolutely positioned or floated)
pub kind: InlineBoxKind,
/// The byte offset into the underlying text string at which the box should be placed.
/// This must not be within a Unicode code point.
pub index: usize,
Expand All @@ -15,3 +17,24 @@ pub struct InlineBox {
/// The height of the box in pixels
pub height: f32,
}

/// Whether a box is in-flow (takes up space in the layout) or out-of-flow (e.g. absolutely positioned)
/// or custom-out-of-flow (line-breaking should yield control flow)
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum InlineBoxKind {
/// `InFlow` boxes take up space in the layout and flow in line with text
///
/// They correspond to `display: inline-block` boxes in CSS.
InFlow,
/// `OutOfFlow` boxes are assigned a position as if they were a zero-sized inline box, but
/// do not take up space in the layout.
///
/// They correspond to `position: absolute` boxes in CSS.
OutOfFlow,
/// `CustomOutOfFlow` boxes also do not take up space in the layout, but they are not assigned a position
/// by Parley. When they are encountered, control flow is yielded back to the caller who is then responsible
/// for laying out the box.
///
/// They can be used to implement advanced layout modes such as CSS's `float`
CustomOutOfFlow,
}
4 changes: 2 additions & 2 deletions parley/src/layout/accessibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ impl LayoutAccessibility {

node.set_bounds(accesskit::Rect {
x0: x_offset + run_offset as f64,
y0: y_offset + metrics.min_coord as f64,
y0: y_offset + metrics.block_min_coord as f64,
x1: x_offset + (run_offset + run.advance()) as f64,
y1: y_offset + metrics.max_coord as f64,
y1: y_offset + metrics.block_max_coord as f64,
});
node.set_text_direction(if run.is_rtl() {
TextDirection::RightToLeft
Expand Down
6 changes: 2 additions & 4 deletions parley/src/layout/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,9 @@ impl Default for AlignmentOptions {
/// Prior to re-line-breaking or re-aligning, [`unjustify`] has to be called.
pub(crate) fn align<B: Brush>(
layout: &mut LayoutData<B>,
alignment_width: Option<f32>,
alignment: Alignment,
options: AlignmentOptions,
) {
layout.alignment_width = alignment_width.unwrap_or(layout.width);
layout.is_aligned_justified = alignment == Alignment::Justify;

align_impl::<_, false>(layout, alignment, options);
Expand Down Expand Up @@ -109,8 +107,8 @@ fn align_impl<B: Brush, const UNDO_JUSTIFICATION: bool>(
}

// Compute free space.
let free_space =
layout.alignment_width - line.metrics.advance + line.metrics.trailing_whitespace;
let line_width = line.metrics.inline_max_coord - line.metrics.inline_min_coord;
let free_space = line_width - line.metrics.advance + line.metrics.trailing_whitespace;

if !options.align_when_overflowing && free_space <= 0.0 {
if is_rtl {
Expand Down
5 changes: 2 additions & 3 deletions parley/src/layout/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl<'a, B: Brush> Cluster<'a, B> {
let mut path = ClusterPath::default();
if let Some((line_index, line)) = layout.line_for_offset(y) {
path.line_index = line_index as u32;
let mut offset = line.metrics().offset;
let mut offset = line.metrics().offset + line.metrics().inline_min_coord;
let last_run_index = line.len().saturating_sub(1);
for item in line.items_nonpositioned() {
match item {
Expand Down Expand Up @@ -553,8 +553,7 @@ mod tests {

fn cluster_from_position_with_alignment(alignment: Alignment) {
let mut layout = create_unaligned_layout();
let width = layout.full_width();
layout.align(Some(width + 100.), alignment, AlignmentOptions::default());
layout.align(alignment, AlignmentOptions::default());
assert_eq!(
layout.len(),
1,
Expand Down
8 changes: 5 additions & 3 deletions parley/src/layout/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::inline_box::InlineBox;
use crate::layout::{ContentWidths, Glyph, LineMetrics, RunMetrics, Style};
use crate::style::Brush;
use crate::util::nearly_zero;
use crate::{FontData, LineHeight, OverflowWrap};
use crate::{FontData, InlineBoxKind, LineHeight, OverflowWrap};
use core::ops::Range;

use swash::text::cluster::{Boundary, Whitespace};
Expand Down Expand Up @@ -571,8 +571,10 @@ impl<B: Brush> LayoutData<B> {
}
LayoutItemKind::InlineBox => {
let ibox = &self.inline_boxes[item.index];
min_width = min_width.max(ibox.width);
running_max_width += ibox.width;
if ibox.kind == InlineBoxKind::InFlow {
min_width = min_width.max(ibox.width);
running_max_width += ibox.width;
}
prev_cluster = None;
}
}
Expand Down
18 changes: 9 additions & 9 deletions parley/src/layout/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ impl<B: Brush> Layout<B> {
&self.data.styles
}

/// Returns available width of the layout.
pub fn available_width(&self) -> f32 {
self.data.alignment_width
}

/// Returns the width of the layout.
pub fn width(&self) -> f32 {
self.data.width
Expand Down Expand Up @@ -126,14 +131,9 @@ impl<B: Brush> Layout<B> {
/// You must perform line breaking prior to aligning, through [`Layout::break_lines`] or
/// [`Layout::break_all_lines`]. If `container_width` is not specified, the layout's
/// [`Layout::width`] is used.
pub fn align(
&mut self,
container_width: Option<f32>,
alignment: Alignment,
options: AlignmentOptions,
) {
pub fn align(&mut self, alignment: Alignment, options: AlignmentOptions) {
unjustify(&mut self.data);
align(&mut self.data, container_width, alignment, options);
align(&mut self.data, alignment, options);
}

/// Returns the index and `Line` object for the line containing the
Expand Down Expand Up @@ -166,9 +166,9 @@ impl<B: Brush> Layout<B> {
return Some((0, self.get(0)?));
}
let maybe_line_index = self.data.lines.binary_search_by(|line| {
if offset < line.metrics.min_coord {
if offset < line.metrics.block_min_coord {
Ordering::Greater
} else if offset >= line.metrics.max_coord {
} else if offset >= line.metrics.block_max_coord {
Ordering::Less
} else {
Ordering::Equal
Expand Down
Loading