linebender / parley Goto Github PK
View Code? Open in Web Editor NEWRich text layout library
License: Apache License 2.0
Rich text layout library
License: Apache License 2.0
Add support for Unicode aware line breaking according to UAX #14.
Minimal implementation of the piet TextLayout trait for single line layouts.
Style spans are supplied to the TextLayoutBuilder trait in arbitrary order with potentially overlapping ranges. Sort and break/merge ranges to generate non-overlapping spans with full coverage of the source text.
Integrate the fount crate for font enumeration. Assign desired fonts by style spans, choosing defaults for missing fonts or selecting fallbacks for scripts according to glyph coverage.
It is typical for a font stack to include a GenericFamily. Android and CoreText both have information about โUIโ type fonts in fallback, differentiated from general text fonts; and Android has support for setting fallback families for generic familes. We should be able to support preferentially matching system-ui
to variant="compact"
when available on Android, and matching serif
(or ui-serif
) to fallback fonts with fallbackFor="serif"
.
This information probably belongs in FallbackKey
but it would need to be wired through the style resolver.
I am looking into creating my own GUI library in rust. I was researching how I would implement text rendering and stumbled upon the very appealing stack of using swash + fount + parley. But I see there has not been a commit here for a while. That got me wondering if usage of this stack is too early and that I should use fontdue instead.
Is it too early to use?
Extend the TextLayoutBuilder/TextLayout trait implementations to generate simple, single line text runs with nominal glyph mapping and default system fonts.
One may wish to mix textual and non-textual content and have the non-textual content laid out in flow with the text. For example, in order to display images or even whole widgets within paragraphs of text. This is necessary in order to implement web-style "inline/flow" layout, but it's use is not limited to web layout contexts: it is a feature that is more generally useful to anyone wishing to layout mixed content.
The functionality required from the text layout system in order to implement "inline layout" is laying out fixed size boxes, possibly with some kind of supplementary baseline alignment / vertical alignment information. There is no need for the text layout system to size such boxes.
I think we can avoid involving inline boxes in ranged style resolution. Based on that assumption, my proposal is as follows:
struct InlineBox {
/// The width and height of the box in pixels
size: kurbo::Vec2,
/// The index into the text string at which the box should be placed.
index: usize,
/// An arbitrary user-defined id that consumers of parley can use to determine
/// which inline box is which in the resultant layout
id: u64,
}
inline_boxes: Vec<InlineBox>
property to LayoutContext
push_inline_box(box: InlineBox)
method RangedBuilder
which pushes to the inline_boxes
property in the layout contextRangedBuilder::finish
shape::shape_text
to break text runs at text indexes where an inline box is present (in addition to all of the places where it already does so).enum RunOrBox {
Run(RunData)
Box(InlineBox)
}
LayoutData.runs
to LayoutData.runs_or_boxes
using the new enumshape::shape_text
to push inline boxes to LayoutData.runs_or_boxes
struct InlineBoxPosition {
/// The x and y position of the box
position: kurbo::Vec2,
/// The unique id of the box
id: u64,
}
enum LineRunOrBox {
Run(LineRunData)
Box(InlineBoxPosition)
}
LayoutData.line_runs
to LayoutData.line_runs_or_boxes
using the new enumBreakLines::break_next
to account for inline boxes when line breaking. This should:
(x, y)
location for the box (either global or line-relative)BreakLines::finish
to account for boxes when performing alignmentProduce a minimal implementation of the piet TextLayoutBuilder trait.
Implement support for accurate, per cluster hit testing and cursor movement.
We want Parley to implement types and methods that Masonry and other editors will be able to use to create a text-editing widget, or to handle selection in non-editable text.
After some discussion we've settled on the following:
MoveLeft
, MoveToNextWord
, SelectToNextParagraph
, RemovePreviousWord
, etc.Possible signatures:
enum TextMovement {
MoveLeft,
MoveRight,
// ...
}
struct StringDiff {
removed_span: usize..usize,
new_position: usize,
added_text: String,
}
fn apply_movement(string: &str, movement: TextMovement, byte_offset: usize) -> StringDiff;
fn map_span(span: usize..usize, diff: StringDiff) -> Option<usize..usize>;
We night use Parley's Cursor type instead of byte offsets in these interfaces.
When a Cursor
is created with Cursor::from_point
, is_rtl
is never set, so is always false
.
Currently, the CoreText source is searching in "/System/Library/Fonts/"
for fonts, but it should probably be searching in more than that.
Fonts can be found in:
"/Library/Fonts"
"/System/Library/Fonts"
"/Network/Library/Fonts"
The user's home directory might be tricky due to sandboxing and so we shouldn't just put together the paths ourselves, but use the system APIs for this instead. (In sandboxed applications, one doesn't have direct access to ~/Library/
but has access to something in ~/Library/Containers/<application id>/data/Library
which contains a symlink to the Fonts
directory.)
I think the right thing to do would be to use NSSearchPathForDirectoriesInDomains
(https://developer.apple.com/documentation/foundation/1414224-nssearchpathfordirectoriesindoma?language=objc) with NSLibraryDirectory
or (NSAllLibrariesDirectory
, I don't know which) and a mask of NSAllDomainsMask
. We should be able to get this from objc2_foundation
(https://docs.rs/objc2-foundation/latest/objc2_foundation/fn.NSSearchPathForDirectoriesInDomains.html)
And finally ... there are ALSO fonts in /System/Library/Fonts/Supplemental
which should be scanned.
Add support for applying font variation settings by axis tag/value pairs. Coordinate with piet on API.
Minimal implementation of the piet Text trait with support for creating a TextLayoutBuilder.
Using the swash crate, shape text runs according to script, locale and selected font.
playing around with piet-gpu and parley using your piet-gpu integration. however I haven't gotten it to work yet. my guess is that since fount mentions it only supports windows and macos that this is currently expected.
MESA-INTEL: warning: Performance support disabled, consider sysctl dev.i915.perf_stream_paranoid=0
thread 'main' panicked at 'range end index 1 out of range for slice of length 0', /home/dvc/cloudpeer/parley/src/layout/line/greedy.rs:510:26
stack backtrace:
0: rust_begin_unwind
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5
1: core::panicking::panic_fmt
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
2: core::slice::index::slice_end_index_len_fail
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/slice/index.rs:41:5
3: <core::ops::range::Range<usize> as core::slice::index::SliceIndex<[T]>>::index
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/slice/index.rs:240:13
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/slice/index.rs:15:9
5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/alloc/src/vec/mod.rs:2496:9
6: parley::layout::line::greedy::commit_line
at /home/dvc/cloudpeer/parley/src/layout/line/greedy.rs:510:26
7: parley::layout::line::greedy::BreakLines<B>::break_next
at /home/dvc/cloudpeer/parley/src/layout/line/greedy.rs:222:12
8: parley::layout::line::greedy::BreakLines<B>::break_remaining
at /home/dvc/cloudpeer/parley/src/layout/line/greedy.rs:257:15
9: parley::layout::Layout<B>::break_all_lines
at /home/dvc/cloudpeer/parley/src/layout/mod.rs:110:9
10: <piet_parley::ParleyTextLayoutBuilder as piet::text::TextLayoutBuilder>::build
at /home/dvc/cloudpeer/parley/piet-parley/src/lib.rs:148:9
11: engine_rs::render
at ./main.rs:201:18
12: engine_rs::main::{{closure}}
at ./main.rs:93:17
13: winit::platform_impl::platform::sticky_exit_callback
at /home/dvc/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/mod.rs:753:5
14: winit::platform_impl::platform::wayland::event_loop::EventLoop<T>::run_return
at /home/dvc/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/wayland/event_loop/mod.rs:394:21
15: winit::platform_impl::platform::wayland::event_loop::EventLoop<T>::run
at /home/dvc/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/wayland/event_loop/mod.rs:209:9
16: winit::platform_impl::platform::EventLoop<T>::run
at /home/dvc/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/platform_impl/linux/mod.rs:669:56
17: winit::event_loop::EventLoop<T>::run
at /home/dvc/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.26.1/src/event_loop.rs:154:9
18: engine_rs::main
at ./main.rs:50:5
19: core::ops::function::FnOnce::call_once
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
parley is currently used in xilem for text stuff.
I did some heap profiling and discovered that FontCache::new()
consumes an absurd amount of memory (1.8 GiB, precise measurements in the link)...
It seems that was parley does is to load every font of my system into memory?
Implement support for applying OpenType feature settings by feature tag/value pairs. Coordinate with piet on API design.
Add support for selection affinity to handle selections that border on mixed directional runs. Implement both logical and visual cursor movement. Requires coordinate with piet.
My code looks something like this, based on your code in https://github.com/dfrg/piet-gpu-text
// let font = Font::from_file("assets/fonts/Inter Variable/Inter.ttf", 0).unwrap();
let font = Font::from_file("assets/fonts/ttf/FiraCode-Regular.ttf", 0).unwrap();
let font = font.as_ref();
// experimenting with the scaler, glyph outlines are returned fine
let mut context = ScaleContext::new();
let mut scaler = context
.builder(font)
.hint(true)
.size(14.)
.variations(&[("wght", 400.0)])
.build();
let glyph_id = font.charmap().map('H');
let outline = scaler.scale_outline(glyph_id).unwrap();
// -- Layout
let mut font_ctx = FontContext::new();
let font_family = font_ctx.register_fonts(font.data.to_vec()).unwrap();
let mut layout_ctx: LayoutContext<[u8; 4]> = LayoutContext::new();
let mut builder = layout_ctx.ranged_builder(&mut font_ctx, "a quick brown fox?", 1.);
builder.push_default(&StyleProperty::FontStack(FontStack::Single(
FontFamily::Named(&font_family),
)));
builder.push_default(&StyleProperty::FontSize(14.));
builder.push_default(&StyleProperty::Brush([255, 255, 255, 255]));
let layout = builder.build();
for line in layout.lines() {
let mut last_x = 0.0;
let mut last_y = 0.0;
// base transform [1, 0, 0, -1]
println!("hi!");
for glyph_run in line.glyph_runs() {
let run = glyph_run.run();
// let color = &glyph_run.style().brush.0;
let font = run.font();
let font = font.as_ref();
let mut first = true;
// TODO: move let scaler here
for glyph in glyph_run.positioned_glyphs() {
// ...
}
}
}
I had to make a small modification to font.rs
to compile on Linux since Library::default
is missing.
let mut builder = LibraryBuilder::default();
builder.build()
For some reason the layout
is always empty and contains no data. I'm not relying on any system paths and instead manually registering the font, then using the font family name returned by FontContext
.
https://github.com/servo/unicode-bidi
This crate is widely used across the Rust ecosystem including by Gecko, Servo, and the url
crate. I believe it could be used to replace most of the code in https://github.com/linebender/parley/blob/main/src/bidi.rs
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.