Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tree style builder #76

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
115 changes: 84 additions & 31 deletions examples/swash_render/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,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};
use parley::style::{FontStack, FontWeight, StyleProperty, TextStyle};
use parley::{FontContext, InlineBox, LayoutContext};
use peniko::Color;
use std::fs::File;
Expand Down Expand Up @@ -44,40 +44,93 @@ fn main() {
let mut layout_cx = LayoutContext::new();
let mut scale_cx = ScaleContext::new();

// Create a RangedBuilder
let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, display_scale);

// Set default text colour styles (set foreground text color)
// Setup some Parley text styles
let brush_style = StyleProperty::Brush(text_color);
builder.push_default(&brush_style);

// Set default font family
let font_stack = FontStack::Source("system-ui");
let font_stack_style = StyleProperty::FontStack(font_stack);
builder.push_default(&font_stack_style);
builder.push_default(&StyleProperty::LineHeight(1.3));
builder.push_default(&StyleProperty::FontSize(16.0));

// Set the first 4 characters to bold
let font_stack_style: StyleProperty<Color> = StyleProperty::FontStack(font_stack);
let bold = FontWeight::new(600.0);
let bold_style = StyleProperty::FontWeight(bold);
builder.push(&bold_style, 0..4);

builder.push_inline_box(InlineBox {
id: 0,
index: 40,
width: 50.0,
height: 50.0,
});
builder.push_inline_box(InlineBox {
id: 1,
index: 50,
width: 50.0,
height: 30.0,
});

// Build the builder into a Layout
let mut layout: Layout<Color> = builder.build();

let mut layout = if std::env::args().any(|arg| arg == "--tree") {
// TREE BUILDER
// ============

// TODO: cleanup API

let root_style = TextStyle {
brush: text_color,
font_stack,
line_height: 1.3,
font_size: 16.0,
..Default::default()
};

let mut builder = layout_cx.tree_builder(&mut font_cx, display_scale, &root_style);

builder.push_style_modification_span(&[bold_style]);
builder.push_text(&text[0..5]);
builder.pop_style_span();

builder.push_text(&text[5..40]);

builder.push_inline_box(InlineBox {
id: 0,
index: 0,
width: 50.0,
height: 50.0,
});

builder.push_text(&text[40..50]);

builder.push_inline_box(InlineBox {
id: 1,
index: 50,
width: 50.0,
height: 30.0,
});

builder.push_text(&text[50..]);

// Build the builder into a Layout
// let mut layout: Layout<Color> = builder.build(&text);
let (layout, _text): (Layout<Color>, String) = builder.build();
layout
} else {
// RANGE BUILDER
// ============

// Creatse a RangedBuilder
let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, display_scale);

// Set default text colour styles (set foreground text color)
builder.push_default(&brush_style);

// Set default font family
builder.push_default(&font_stack_style);
builder.push_default(&StyleProperty::LineHeight(1.3));
builder.push_default(&StyleProperty::FontSize(16.0));

// Set the first 4 characters to bold
builder.push(&bold_style, 0..4);

builder.push_inline_box(InlineBox {
id: 0,
index: 40,
width: 50.0,
height: 50.0,
});
builder.push_inline_box(InlineBox {
id: 1,
index: 50,
width: 50.0,
height: 30.0,
});

// Build the builder into a Layout
// let mut layout: Layout<Color> = builder.build(&text);
let layout: Layout<Color> = builder.build(&text);
layout
};

// Perform layout (including bidi resolution and shaping) with start alignment
layout.break_all_lines(max_advance);
Expand Down
2 changes: 1 addition & 1 deletion examples/tiny_skia_render/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ fn main() {
});

// Build the builder into a Layout
let mut layout: Layout<PenikoColor> = builder.build();
let mut layout: Layout<PenikoColor> = builder.build(&text);

// Perform layout (including bidi resolution and shaping) with start alignment
layout.break_all_lines(max_advance);
Expand Down
197 changes: 197 additions & 0 deletions parley/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright 2021 the Parley Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Context for layout.

use super::context::*;
use super::style::*;
use super::FontContext;

#[cfg(feature = "std")]
use super::layout::Layout;

use core::ops::RangeBounds;

use crate::inline_box::InlineBox;

/// Builder for constructing a text layout with ranged attributes.
pub struct RangedBuilder<'a, B: Brush> {
pub(crate) scale: f32,
pub(crate) lcx: &'a mut LayoutContext<B>,
pub(crate) fcx: &'a mut FontContext,
}

impl<'a, B: Brush> RangedBuilder<'a, B> {
pub fn push_default(&mut self, property: &StyleProperty<B>) {
let resolved = self
.lcx
.rcx
.resolve_property(self.fcx, property, self.scale);
self.lcx.ranged_style_builder.push_default(resolved);
}

pub fn push(&mut self, property: &StyleProperty<B>, range: impl RangeBounds<usize>) {
let resolved = self
.lcx
.rcx
.resolve_property(self.fcx, property, self.scale);
self.lcx.ranged_style_builder.push(resolved, range);
}

pub fn push_inline_box(&mut self, inline_box: InlineBox) {
self.lcx.inline_boxes.push(inline_box);
}

#[cfg(feature = "std")]
pub fn build_into(&mut self, layout: &mut Layout<B>, text: impl AsRef<str>) {
// Apply RangedStyleBuilder styles to LayoutContext
self.lcx.ranged_style_builder.finish(&mut self.lcx.styles);

// Call generic layout builder method
build_into_layout(layout, self.scale, text.as_ref(), self.lcx, self.fcx);
}

#[cfg(feature = "std")]
pub fn build(&mut self, text: impl AsRef<str>) -> Layout<B> {
let mut layout = Layout::default();
self.build_into(&mut layout, text);
layout
}
}

/// Builder for constructing a text layout with a tree of attributes.
pub struct TreeBuilder<'a, B: Brush> {
pub(crate) scale: f32,
pub(crate) lcx: &'a mut LayoutContext<B>,
pub(crate) fcx: &'a mut FontContext,
}

impl<'a, B: Brush> TreeBuilder<'a, B> {
pub fn push_style_span(&mut self, style: TextStyle<B>) {
let resolved = self
.lcx
.rcx
.resolve_entire_style_set(self.fcx, &style, self.scale);
self.lcx.tree_style_builder.push_style_span(resolved);
}

pub fn push_style_modification_span<'s, 'iter>(
&mut self,
properties: impl IntoIterator<Item = &'iter StyleProperty<'s, B>>,
) where
's: 'iter,
B: 'iter,
{
self.lcx.tree_style_builder.push_style_modification_span(
properties
.into_iter()
.map(|p| self.lcx.rcx.resolve_property(self.fcx, p, self.scale)),
);
}

pub fn pop_style_span(&mut self) {
self.lcx.tree_style_builder.pop_style_span();
}

pub fn push_text(&mut self, text: &str) {
self.lcx.tree_style_builder.push_text(text);
}

pub fn push_inline_box(&mut self, mut inline_box: InlineBox) {
self.lcx.tree_style_builder.push_uncommitted_text(false);
// 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);
}

pub fn set_white_space_mode(&mut self, white_space_collapse: WhiteSpaceCollapse) {
self.lcx
.tree_style_builder
.set_white_space_mode(white_space_collapse);
}

#[cfg(feature = "std")]
pub fn build_into(&mut self, layout: &mut Layout<B>) -> String {
// Apply TreeStyleBuilder styles to LayoutContext
let text = self.lcx.tree_style_builder.finish(&mut self.lcx.styles);

self.lcx.analyze_text(&text);

// Call generic layout builder method
build_into_layout(layout, self.scale, &text, self.lcx, self.fcx);

text
}

#[cfg(feature = "std")]
pub fn build(&mut self) -> (Layout<B>, String) {
let mut layout = Layout::default();
let text = self.build_into(&mut layout);
(layout, text)
}
}

#[cfg(feature = "std")]
fn build_into_layout<B: Brush>(
layout: &mut Layout<B>,
scale: f32,
text: &str,
lcx: &mut LayoutContext<B>,
fcx: &mut FontContext,
) {
layout.data.clear();
layout.data.scale = scale;
layout.data.has_bidi = !lcx.bidi.levels().is_empty();
layout.data.base_level = lcx.bidi.base_level();
layout.data.text_len = text.len();

// println!("BUILD INTO ({})", text.len());
// for span in &lcx.styles {
// let stack = lcx.rcx.stack(span.style.font_stack);
// println!(
// "{:?} weight:{}, family: {:?}",
// span.range, span.style.font_weight, stack
// );
// }

let mut char_index = 0;
for (i, style) in lcx.styles.iter().enumerate() {
for _ in text[style.range.clone()].chars() {
lcx.info[char_index].1 = i as u16;
char_index += 1;
}
}

// Copy the visual styles into the layout
layout
.data
.styles
.extend(lcx.styles.iter().map(|s| s.style.as_layout_style()));

// Sort the inline boxes as subsequent code assumes that they are in text index order.
// Note: It's important that this is a stable sort to allow users to control the order of contiguous inline boxes
lcx.inline_boxes.sort_by_key(|b| b.index);

// dbg!(&lcx.inline_boxes);

Comment on lines +175 to +176
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// dbg!(&lcx.inline_boxes);

{
let query = fcx.collection.query(&mut fcx.source_cache);
super::shape::shape_text(
&lcx.rcx,
query,
&lcx.styles,
&lcx.inline_boxes,
&lcx.info,
lcx.bidi.levels(),
&mut lcx.scx,
text,
layout,
);
}

// Move inline boxes into the layout
layout.data.inline_boxes.clear();
core::mem::swap(&mut layout.data.inline_boxes, &mut lcx.inline_boxes);

layout.data.finish();
}
Loading