GithubHelp home page GithubHelp logo

pop-os / cosmic-text Goto Github PK

View Code? Open in Web Editor NEW
1.5K 32.0 87.0 90.14 MB

Pure Rust multi-line text handling

Home Page: https://pop-os.github.io/cosmic-text/cosmic_text/

License: Apache License 2.0

Shell 0.69% Rust 99.31%

cosmic-text's Introduction

COSMIC Text

crates.io docs.rs license Rust workflow

Pure Rust multi-line text handling.

COSMIC Text provides advanced text shaping, layout, and rendering wrapped up into a simple abstraction. Shaping is provided by rustybuzz, and supports a wide variety of advanced shaping operations. Rendering is provided by swash, which supports ligatures and color emoji. Layout is implemented custom, in safe Rust, and supports bidirectional text. Font fallback is also a custom implementation, reusing some of the static fallback lists in browsers such as Chromium and Firefox. Linux, macOS, and Windows are supported with the full feature set. Other platforms may need to implement font fallback capabilities.

Screenshots

Arabic translation of Universal Declaration of Human Rights Arabic screenshot

Hindi translation of Universal Declaration of Human Rights Hindi screenshot

Simplified Chinese translation of Universal Declaration of Human Rights Simplified Chinses screenshot

Roadmap

The following features must be supported before this is "ready":

  • Font loading (using fontdb)
    • Preset fonts
    • System fonts
  • Text styles (bold, italic, etc.)
    • Per-buffer
    • Per-span
  • Font shaping (using rustybuzz)
    • Cache results
    • RTL
    • Bidirectional rendering
  • Font fallback
    • Choose font based on locale to work around "unification"
    • Per-line granularity
    • Per-character granularity
  • Font layout
    • Click detection
    • Simple wrapping
    • Wrapping with indentation
    • No wrapping
    • Ellipsize
  • Font rendering (using swash)
    • Cache results
    • Font hinting
    • Ligatures
    • Color emoji
  • Text editing
    • Performance improvements
    • Text selection
    • Can automatically recreate https://unicode.org/udhr/ without errors (see below)
    • Bidirectional selection
    • Copy/paste
  • no_std support (with default-features = false)
    • no_std font loading
    • no_std shaping
    • no_std layout
    • no_std rendering

The UDHR (Universal Declaration of Human Rights) test involves taking the entire set of UDHR translations (almost 500 languages), concatenating them as one file (which ends up being 8 megabytes!), then via the editor-test example, automatically simulating the entry of that file into cosmic-text per-character, with the use of backspace and delete tested per character and per line. Then, the final contents of the buffer is compared to the original file. All of the 106746 lines are correct.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

cosmic-text's People

Contributors

0hypercube avatar 13r0ck avatar aapoalas avatar cryze avatar dovreshef avatar dtzxporter avatar ehfive avatar geieredgar avatar genusistimelord avatar grovesnl avatar hecrj avatar hojjatabdollahi avatar ids1024 avatar imberflur avatar jackpot51 avatar jasper-bekkers avatar joshuamegnauth54 avatar kettle11 avatar marijns95 avatar mosal avatar nicoburns avatar notgull avatar rhelmot avatar tarkah avatar tigregalis avatar totalkrill avatar urlordjames avatar vorporeal avatar whynothugo avatar xorgy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cosmic-text's Issues

Reduce the number of font loading implementations used by this crate

Excerpt of cargo bloat when run on an example programming containing winit and piet-hardware, which uses cosmic-text for text:

 File  .text    Size                  Crate Name
 0.6%   2.1% 77.7KiB                 x11_dl x11_dl::xlib::Xlib::open::{{closure}}
 0.2%   0.7% 27.8KiB                  swash swash::scale::bitmap::resize
 0.2%   0.6% 23.6KiB                  swash swash::scale::glyf::hint::Hinter::execute
 0.2%   0.6% 22.3KiB                    std addr2line::ResDwarf<R>::parse
 0.2%   0.6% 22.3KiB                  winit winit::platform_impl::platform::wayland::event_loop::EventLoop<T>::run
 0.2%   0.5% 20.1KiB                   glow glow::gl46::struct_commands::GlFns::load_all_with_dyn
 0.2%   0.5% 19.9KiB                    std std::backtrace_rs::symbolize::gimli::resolve::{{closure}}
 0.1%   0.5% 17.4KiB                  winit winit::platform_impl::platform::x11::event_processor::EventProcessor<T>::process_event
 0.1%   0.4% 14.8KiB            cosmic_text cosmic_text::shape::ShapeLine::layout
 0.1%   0.4% 14.6KiB                   piet piet::samples::samples_main
 0.1%   0.4% 14.2KiB                    png png::decoder::stream::StreamingDecoder::update
 0.1%   0.3% 13.0KiB            wayland_sys wayland_sys::client::WaylandClient::open
 0.1%   0.3% 13.0KiB             ttf_parser ttf_parser::tables::cff::cff1::_parse_char_string
 0.1%   0.3% 13.0KiB             ttf_parser ttf_parser::tables::cff::cff1::_parse_char_string
 0.1%   0.3% 12.6KiB           glutin_winit glutin_winit::finalize_window
 0.1%   0.3% 12.2KiB                  winit winit::platform_impl::platform::x11::window::UnownedWindow::new
 0.1%   0.3% 11.5KiB             ttf_parser ttf_parser::tables::cff::cff2::_parse_char_string
 0.1%   0.3% 11.5KiB             ttf_parser ttf_parser::tables::cff::cff2::_parse_char_string
 0.1%   0.3% 11.1KiB                  swash swash::scale::cff::cff::Glyph::parse_imp
 0.1%   0.3% 10.8KiB smithay_client_toolkit smithay_client_toolkit::seat::keyboard::ffi::XkbCommon::open

The CFF font parsing tables from ttf_parser add about 50 KiB to the binary while the one that swash uses adds about 12 KiB to the binary. It would be nice if only one instance of the CFF table was present in the final binary.

cc dfrg/swash#36

Let a `Buffer` figure out its `height` during `set_size`

While it is possible to use i32::MAX if there is no height limit for a Buffer, the resulting Buffer will use i32::MAX as the final dimension. This means that Buffer::visible_lines will not be very useful.

I think it'd be more useful to let layouting dictate the final height of a Buffer.

FontSystem::new() takes a significant amount of time

On my system (Xubuntu), running FontSystem::new() takes a significant amount of time. On release, it takes around a second to run. On debug, it takes up to ten seconds.

There should probably be some measures taken to make sure FontSystem runs quickly. However, it should also be noted somewhere that FontSystem takes a while to load.

Very frequent log messages about font fallback

When running the examples I get very frequent (dozens per second) log messages like that:

[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'Noto Sans'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'DejaVu Sans'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'FreeSans'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'Noto Sans Mono'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'DejaVu Sans Mono'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'FreeMono'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'Noto Sans Symbols'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] failed to find family 'Noto Sans Symbols2'
[2023-06-04T20:40:18Z DEBUG cosmic_text::font::fallback] Failed to find preset fallback for [] locale 'en-US', used 'Verdana': 'Word'

It seems like the library is constantly trying to resolve fallback fonts instead of doing it once and caching the outcome.

The resulting performance is extremely bad as well. For example, I need to run the editor in release mode for it to be somewhat responsive.

Non-default weights break emojis

It seems like the font selection might be fairly strict, so because there's no bold version of Emojis it can't find any font and falls back to the Tofu instead.

image

[Examples] Issues running examples

Hi! First of all, thanks for the crate!

I was trying to run the examples but faced some issues and I was wondering
Would accept PRs to improve them?

The issues I faced are the following. Some of them are from the "first-user perspective", I just want to help.

  1. I was not sure what each example was about. There was no README or module documentation on the source code.
  2. The terminal example uses termion, which does not compile on Windows... So I could not run it.
  3. The editor-orbclient example compiles, but panic with [2022-11-23T10:09:18Z ERROR editor_orbclient] failed to load "": The system cannot find the path specified.
  4. The editor-libcosmic example failed to build because xdg::BaseDirectories was not found.

(I am on Windows running rustc 1.64.0)
Some preliminary thoughts about these issues are the following.

  1. The terminal example uses termion modules color and cursor. A more platform-friendly crate with these features may be console. It also ranks higher in https://lib.rs/std so I thought the change would be an improvement.

I have not checked the other examples in detail yet.

Upgrade to fontdb 0.14

fontdb version 0.14 contains a breaking change that allows one to get the IDs from a font that was just registered, which helps when it comes to implementing a "load font family" function. It would be nice to bump the version of this crate. Since this would be a breaking change, I understand if it's not high priority; it should probably be bundled with another breaking change once the time comes.

Locale override for Han unification and other tasks

Currently only the primary system locale is used. Allow the use of secondary system locales for resolving Han unification and other things, and also allow a span of text to be given an explicit locale.

Do you need Unicode text segmentation?

Hi, I'm from the ICU4X team. We're an open source project building i18n components in Rust.

I noticed in your README that you support "basic" line layout. If you need to support full UAX 14 line break, including models for Southeast Asian languages, I wanted to suggest ICU4X, the icu_segmenter crate.

icu4x.unicode.org

feature request: Add text justification

I am currently developing a cosmic-text-based implementation of the piet text API, see here. The only bits from piet that are currently unimplemented are variable font sizes (already raised in #64) and text justification.

piet expects there to be utilities for text justification, with start (default), right, center and justified alignments. See this enum for more information.

Are there any plans to add text alignment to cosmic-text?

Allow access to &mut Database from FontSystem

I'd like to be able to access the underlying Database in a FontSystem mutably, so I can easily add fonts without needing to convert to the underlying Database and back.

However, the Database is currently self-referentially borrowed for caching the font-ID-to-font relationship. It looks like this is due to the font lookup in fontdb being an O(n) process. RazrFalcon/fontdb#47 would fix that problem and allow us to solve this issue.

Support vertical alignment

Often for a simple layout it's desirable to centre vertically. Since a Buffer considers the size an input, this library should probably also support top/center/bottom vertical alignment?

#70 affects this slightly; there's still a need to be able to measure height after layout (also #42).

Loading fonts incrementally

I see #43 added support for constructing a FontSystem with custom fonts loaded, but is there a way to get a mutable reference to the font database after the system is constructed so that more fonts can be loaded afterwards?

BTree Support

Replace all Range driven Vecs with a Btree using https://crates.io/crates/rangemap

This should boost performance. I am currently Added the missing derives we need in a PR for rangemap. If they don't add them we can remake the library into something else worse case.

assertion failed when source text contains \r character

MRE:

// cosmic-text = "0.7.0"
use cosmic_text::{Attrs, Buffer, FontSystem, Metrics};

fn main() {
    let font_system = FontSystem::new();
    let metrics = Metrics::new(14, 20);
    let mut buffer = Buffer::new(&font_system, metrics);
    buffer.set_size(80, 25);
    let attrs = Attrs::new();
    let message = "A\rB"; // Problem!
    buffer.set_text(message, attrs);
}

Discovered while trying to develop an X11 application- the Enter key sent a "\r" instead of a "\n", which my prototype application (not using Editor) simply appended to the end of the rendered buffer, causing a crash when the next character was appended.

The cause seems to be that \r is treated as a paragraph separator by unicode-bidi, but not by the lines iterator from std uses to do paragraph separator in buffer.set_text(), so when the paragraph is sent to be shaped it's split into two paragraphs and triggers the assert.

Font fallback fails if main font's properties don't match fallback font

It seems like font fallback generally works pretty well, but if a certain property, such as italics are not available for the fallback font, the font is not chosen at all and tofus are rendered instead.

Here's an example where Calibri is the main font. Calibri by itself does not support Hangul, so it falls back to a different font (seems to be Malgun Gothic). This works fine until you choose Calibri Italic, at which point it can't find an italic version of Malgun Gothic and shows tofus.

image

This may or may not be related to #58. I'm surprised the fallback is rejected if found in the first place though, so it may not actually be related.

BIDI Layout is "random"

Depending on whether it's a mixed layout or not, the arabic characters are either reversed or not:

https://i.imgur.com/WNUdgIy.png

This is with the latest master.

Here you see a debugger view of just the arabic characters:

https://i.imgur.com/scFc9kv.png

This may or may not be intended, but is super weird to deal with. To properly handle this I need to properly mix the shape line's BIDI level with the shape span's BIDI level and then depending on what combination it is, iterate the glyphs in reverse or not.

I don't even understand why cosmic-text (sometimes) reverses what rustybuzz spits out in the first place, as I need to unreverse it to draw the glyphs in the proper order. That seems like a lot of wasted work that cosmic-text does.

Numbers inside of a mixed RTL/LTR line cause issues with the layout

There is an issue with numbers inside a mixed RTL/LTR sentence. Currently a span only contains a boolean that says if it is RTL or LTR. To get a correct layout we need to also consider the BidiClass. Some Bidi Class values are strong LTR or RTL, but some are Weak or Neutral which means when we are creating the layout although the span might be written LTR for layouting purposes it should be treated as an RTL span (if the surrounding text is also RTL).

The simplest example is numbers. Consider the following example:

The latest version of Pop OS is 22.04. آخرین ورژن سیستم عامل پاپ ۲۲.۰۴ است.

This is currently rendered like this:

farsi

As you can see the numbers in the RTL span are treated as an LTR span which causes the layout to mess up.

Change `opt_color` of a `BufferLine` without triggering a reshape / relayout

The main use case here is being able to reuse a Buffer both for layout and drawing. iced does not know the color of the text during layout.

Color doesn't really affect anything but drawing, right? It seems it'd make more sense to provide a list of color spans as an argument to draw? Or maybe we could change BufferLine::set_attrs_list to not trigger a reset if only the colors differ?

if attrs_list != self.attrs_list {
self.attrs_list = attrs_list;
self.reset();
true

example `editor-libcosmic` does not properly apply scale

Running the editor-libcosmic example, I get this:

image

There is no text rendered anywhere. Clicking on the big textfield and typing does nothing. Opening a file changes nothing. The pulldown menus and toggle buttons respond, but do nothing.

Building on Fedora 36

Hi y'all,

I was just trying out this project on my Fedora 36 machine. I managed to run the examples after installing the dependencies with dnf install SDL2-devel SDL2-static and exporting RUSTFLAGS="-Lnative=/usr/lib64/". It'd be cool if you could mention that in the README so that others don't run into the same issue as me.

Thanks for the cool project.

Improve clean debug build speeds 25% by changing `unicode-linebreak` to `xi-unicode`

Hi! I'm quite excited for this library.

I'm a big fan of super lean build times so I try to audit dependencies to find areas to improve. In this case it seems unicode-linebreak implements a rather chunky build.rs script that makes it your second slowest building dependency (after swash).

xi-unicode pre-bakes the unicode tables without a build.rs so it builds much faster.

Swapping is an easy one-line change:

Change this line:

for (end_lb, _) in unicode_linebreak::linebreaks(span) {

to this:

for (end_lb, _) in xi_unicode::LineBreakIterator::new(span) {

Alongside the obvious cargo.toml changes.

Edit: I ran editor-test and it reported "All lines matched!". I haven't ran further tests.

Icons not found for examples

I'm on macOS 13.2.1, with M1. When I run examples, I get something like this:

svg icon 'Name("format-justify-left")' size 20 not found
svg icon 'Name("format-justify-center")' size 20 not found
svg icon 'Name("format-justify-right")' size 20 not found
svg icon 'Name("format-justify-fill")' size 20 not found

Running editor-libcosmic looks like this:

Screenshot 2023-03-08 at 17 24 44

Panic with strange file

Modifying editor-test

    let text = if let Some(arg) = env::args().nth(1) {
        fs::read_to_string(&arg).expect("failed to open file")
    } else {
        #[cfg(feature = "mono")]
        let default_text = include_str!("../../../sample/a.txt");
        #[cfg(not(feature = "mono"))]
        let default_text = include_str!("../../../sample/a.txt");
        default_text.to_string()
    };

and putting to sample file - ss.txt.zip

crash app with info

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Cursor { line: 0, index: 11747, affinity: After }`,
 right: `Cursor { line: 0, index: 11743, affinity: After }`', examples/editor-test/src/main.rs:99:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

96.8MB memory usage in rich-text example

Originally I found this issue in the advanced text branch for iced. My system reports that 96.8MB is used when running. It seems like a bit much to use that much memory for just displaying some text. Iced gave memory usage of around 100MB for the checkbox example on the advanced-text branch.

I ran dhat from valgrind for iced and it seems that some parts of rustybuzz appears to be the cause of 12MB of memory usage.

I am running on Linux on aarch64.

Allow setting the fallback fonts

I'm trying to add support for WASM to my piet-cosmic-text crate by embedding a font into the executable. However, it seems that, even after I set the font using set_sans_serif_font on the fontdb::Database, I get this panic:

panicked at 'no default font found', /home/jtnunley/.cargo/registry/src/github.com-1ecc6299db9ec823/cosmic-text-0.8.0/src/shape.rs:130:33

It would be nice to be able to set the fallback font to something such that it can be used in the event where none are available.

cc notgull/piet-cosmic-text#11

Better rendering API

Having the glyph cache inside of cosmic-text wastes memory if the image will be immediately uploaded to GPU, and the API returning pixel positions and colors instead of images decreases performance.

Constrained buffer with unconstrained buffer's width does not produce the same result

Reproduction: https://github.com/tigregalis/cosmic-text-width-constraint-bug

out-expected.png

When we draw text in Bevy, we currently do 2+ "size" passes (1+ node measurement pass during layout, and 1 rendering pass); the calculated width from the first pass determines the size of the node.

A rare bug(?) occurs when laying out the text (6) SomewhatBoringDisplayTransform with a font size of 18.0 pixels and the font FiraMono-Medium.ttf. This shows up in a specific Bevy example, and I haven't been able to reproduce it with other text / font sizes / fonts, but the possibility exists.

We lay out this text in a buffer (buffer1) with an UNBOUNDED size; the buffer has one line (one layout run) with a certain width1 (run.line_w).

Later, we lay out the same line in a buffer (buffer2) with a maximum size of width1 pixels wide.

That is, the first buffer's width determines the width constraint of the second buffer. I expected that they would produce the same result.

However, this second buffer (buffer2) is not considered wide enough to fit that text, so the second word breaks onto a second line.

Even ceiling the width still causes it to break. However, ceiling it and adding some small value (e.g. 0.001) gets around this behaviour.

Request: Allow buffer dimensions to be undefined

Currently you have to set the Buffer dimensions before shaping. But it is common requirement to size a box containing some text based on the size of the text itself. It would therefore be great if it were possible to shape/layout with one or both dimensions being unset/infinite (it should be possible to independently set each dimension to infinite).

This could be represented with an Option<i32> (or Option<u32> - does a negative buffer size make sense?) or possibly just an f32, making use of the floating point Infinity value.

Incorrect alignment when using Wrap::None

When calling layout with Wrap::None, the w property of VisualLine is not updated, which breaks alignment options such as Align::center:

cosmic-text/src/shape.rs

Lines 664 to 670 in bfb5eef

if wrap == Wrap::None {
for (span_index, span) in self.spans.iter().enumerate() {
current_visual_line
.ranges
.push((span_index, (0, 0), (span.words.len(), 0)));
}
} else {

Besides, I don't really understand why Wrap::None has to be handled differently from other wrapping options. I might be wrong here, but wouldn't replacing fit_x - word_width >= 0. with wrap == Wrap::None || fit_x - word_width >= 0. inside the second branch be sufficient? Is this done for performance reasons?

editor-test failing

editor-test.sh fails on

[2022-10-24T21:59:35Z DEBUG editor_test] Line "  STARGΛ\u{30a}TE SG-1, a = v\u{307} = r\u{308}, a\u{20d1} ⊥ b\u{20d1}"
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `TextCursor { line: TextLineIndex(57), index: 9 }`,
 right: `TextCursor { line: TextLineIndex(57), index: 7 }`', examples/editor-test/src/main.rs:129:17
stack backtrace:
   0:     0x5631652662bd - std::backtrace_rs::backtrace::libunwind::trace::h9135f25bc195152c
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x5631652662bd - std::backtrace_rs::backtrace::trace_unsynchronized::h015ee85be510df51
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x5631652662bd - std::sys_common::backtrace::_print_fmt::h5fad03caa9652a2c
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:66:5
   3:     0x5631652662bd - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h2b42ca28d244e5c7
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:45:22
   4:     0x5631652830ac - core::fmt::write::h401e827d053130ed
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/fmt/mod.rs:1198:17
   5:     0x563165263e21 - std::io::Write::write_fmt::hffec93268f5cde32
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/io/mod.rs:1672:15
   6:     0x563165267995 - std::sys_common::backtrace::_print::h180c4c706ee1d3fb
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:48:5
   7:     0x563165267995 - std::sys_common::backtrace::print::hd0c35d18765761c9
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:35:9
   8:     0x563165267995 - std::panicking::default_hook::{{closure}}::h1f023310983bc730
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:295:22
   9:     0x5631652676b1 - std::panicking::default_hook::h188fec3334afd5be
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:314:9
  10:     0x563165267f26 - std::panicking::rust_panic_with_hook::hf26e9d4f97b40096
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:698:17
  11:     0x563165267e17 - std::panicking::begin_panic_handler::{{closure}}::hfab912107608087a
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:588:13
  12:     0x5631652667b4 - std::sys_common::backtrace::__rust_end_short_backtrace::h434b685ce8d9965b
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:138:18
  13:     0x563165267b49 - rust_begin_unwind
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:584:5
  14:     0x563164ff5033 - core::panicking::panic_fmt::ha6dc7f2ab2479463
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:142:14
  15:     0x563165281d08 - core::panicking::assert_failed_inner::h433285798fdd5aeb
  16:     0x563164fee9cb - core::panicking::assert_failed::h06cf6ad39bd1347f
  17:     0x563164ff701a - editor_test::main::h25866e9413ee35c9
  18:     0x563164ff75a3 - std::sys_common::backtrace::__rust_begin_short_backtrace::h0afbcc2875b404f1
  19:     0x563164ff7779 - std::rt::lang_start::{{closure}}::h6671d31bbca3e562
  20:     0x56316525f45e - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::hcdfee62722e5e4b8
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/ops/function.rs:280:13
  21:     0x56316525f45e - std::panicking::try::do_call::h84ca51609826746f
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:492:40
  22:     0x56316525f45e - std::panicking::try::hd58075e533b8e0cb
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:456:19
  23:     0x56316525f45e - std::panic::catch_unwind::h1ebac24d83cb6ce2
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panic.rs:137:14
  24:     0x56316525f45e - std::rt::lang_start_internal::{{closure}}::h0145388a1edd1640
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/rt.rs:128:48
  25:     0x56316525f45e - std::panicking::try::do_call::h7630182e534a0a32
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:492:40
  26:     0x56316525f45e - std::panicking::try::h05b6544f0c6331dc
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:456:19
  27:     0x56316525f45e - std::panic::catch_unwind::h77b2ba8fd3309f34
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panic.rs:137:14
  28:     0x56316525f45e - std::rt::lang_start_internal::h6612c8a7a6861b8b
                               at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/rt.rs:128:20
  29:     0x563164ff7222 - main
  30:     0x7f1d989f2d90 - <unknown>
  31:     0x7f1d989f2e40 - __libc_start_main
  32:     0x563164ff52d5 - _start
  33:                0x0 - <unknown>

Vector path output

It would be useful to have a way to output vector paths as an alternative to rendering pixels to an image.

Ability to obtain baseline Y offset

I try to utilize cosmic-text to draw text to a bitmap and to obtain a tight bounding box around the result. Right now, I am confused by the calculation of the vertical offset of the text. To elaborate further, let us assume that we draw a single glyph, i.e. the letter "g", using a Buffer. For the sake of simplicity, we set line_height == font_size == 72.0 (i.e. we do not apply leading aka additional distance between the lines). When we call Buffer::draw(), the code first creates a LayoutRunIter whose line_y property is initialized as follows:

line_y: buffer.metrics.y_offset(),

In our case, y_offset() evaluates to 0.0 (no leading, see above) and therefore, our run starts at a vertical offset of 0.0. Now, we call next() on the iterator to draw the first run and observe that line_y is incremented by the line height which is equal to the font size (aka 72.0):

self.line_y += self.buffer.metrics.line_height;

Finally, we obtain the glyph image from `swash' and draw it to the given offset:

cosmic-text/src/buffer.rs

Lines 718 to 720 in bfb5eef

cache.with_pixels(font_system, cache_key, glyph_color, |x, y, color| {
f(x_int + x, run.line_y as i32 + y_int + y, 1, 1, color);
});

run.line_y still evaluates to 72.0 in our example. However, if I understand correctly, the swash image is positioned relatively to the baseline of the glyph. I did not find explicit confirmation for this assumption in the swash documentation, but the swash_demo repo provides an extensive sample for this. When drawing its layout, it incorporates the baseline into the vertical offset as seen here:

https://github.com/dfrg/swash_demo/blob/55aedbc8604201f1b4925e2b986db59b838dc062/src/main.rs#L493

In opposition, cosmic-text effectively places the baseline at the bottom of the line. When I try to draw my "g" glyph to the top-left corner of a bitmap and set my bounding box height to 72 pix, I end up with the following result:

g

How to fix this? I am not adept in typography, but for my understanding, the vertical offset must be corrected by the descent of the font as displayed here:

glyph

Indeed, to draw my glyphs to the bitmap, I tried to calculate the baseline of the font by myself to apply this correction. This approach works, but cosmic-text makes it pretty hard to implement it. Basically, I have to layout my line, obtain the FontID from the glyphs, query the Font from the FontSystem and do something like this:

let metrics = font.as_swash().metrics(&[]);
let total_height = metrics.ascent + metrics.descent;
let baseline = font_size * metrics.ascent / total_height;

In fact, this is about the same calculation that is done in the swash demo, see here:

https://github.com/dfrg/swash_demo/blob/d08ced5f1e92b62cd637415c1f66c440d737b31b/src/layout/line_breaker.rs#L324-L326

By using baseline instead of font_size (resp. the total line height) as vertical offset, I finally get the correct result. I can live with this approach, but it would be nice if cosmic-text made it easier for me to access the baseline offsets of the layout lines.

Sorry for the long explanation. I hope the issue has been made clear - and thanks for your awesome work on this crate!

Spans of font sizes

This is a highly requested feature from a rich text editor - being able to change font size within a line.

I looked into doing this myself but couldn't figure out a good way to get the attrs into layout without adding a font size field to the shaping structs, which seemed to be against the spirit of the design. If there's any advice on what changes would need to be made where to make this happen, I'll gladly do it.

editor-libcosmic Fails to Build

Hi there,

I attempted to build editor-libcosmic under examples, but I get the following error:

  --- stderr
  `"pkg-config" "--libs" "--cflags" "gdk-3.0" "gdk-3.0 >= 3.18"` did not exit successfully: exit status: 1
  error: could not find system library 'gdk-3.0' required by the 'gdk-sys' crate

I kept reading this as gtk-3.0, but I realized it says gdk and I looked around for that package and couldn't find it on the ubuntu repos. I'm sure I'm missing something super simple, so sorry if this one is obvious.

Info about my system:

NAME="Pop!_OS"
VERSION="22.04 LTS"
ID=pop
ID_LIKE="ubuntu debian"
PRETTY_NAME="Pop!_OS 22.04 LTS"
VERSION_ID="22.04"
HOME_URL="https://pop.system76.com"
SUPPORT_URL="https://support.system76.com"
BUG_REPORT_URL="https://github.com/pop-os/pop/issues"
PRIVACY_POLICY_URL="https://system76.com/privacy"
VERSION_CODENAME=jammy
UBUNTU_CODENAME=jammy
LOGO=distributor-logo-pop-os
NVIDIA Corporation TU116M [GeForce GTX 1660 Ti Mobile]

Thanks in advance

IME support

Will need a winit example to experiment with this

Implement CSS Font Matching

cosmic-text currently implements a very basic 1:1 matching algorithm where if anything doesn't perfectly match, the font will never be chosen:

cosmic-text/src/attrs.rs

Lines 166 to 175 in b9fef72

pub fn matches(&self, face: &fontdb::FaceInfo) -> bool {
//TODO: smarter way of including emoji
face.post_script_name.contains("Emoji") ||
(
face.style == self.style &&
face.weight == self.weight &&
face.stretch == self.stretch &&
face.monospaced == self.monospaced
)
}

It should implement the CSS Fonts Module Level 3 - 5. Font Matching Algorithm.

Also it's super odd how Emoji is considered the fallback font and not the actual specified fallback font.

`editor-libcosmic` example crashes on changing font size

I'm on macOS 13.2.1, with M1. When I run the example and open sample/hello.txt and trying to change the font size to Title 4 the application panics with:

thread 'main' panicked at 'attempt to subtract with overflow', src/shape.rs:711:37

The issue appears only on debug build.

make text shaping use swash optionally

Rustybuzz is very accurate text shaping library, but as they say in their own readme, their implementation is slower than HarfBuzz. Swash also provides high quality text shaping while being more performant than rustybuzz, though it is still a bit unstable and has some bugs.

It doesn't seem to be implemented at the moment, so why not make a feature that uses swash for shaping instead of rustybuzz?

Request: The ability to layout arbitrary boxes in line with text

It's often the case that UIs want to intersperse non-textual content in line with text. Examples of this would include inline images and form fields. But in theory this extends to arbitrary content. It seems to me that:

  • A UI framework wanting to implement such functionality would not be able to build it on top of the text-only functionality that cosmic-text currently offers.
  • It would be relatively easy for cosmic-text to offer support for laying out boxes (rectangles) in line with text.

I don't really care what the API looks like, but I would suggest something like:

  • Cosmic text takes a Vec<InlineBox> as input in addition to it's existing inputs
  • Each InlineBox consists of:
    • a unique identifier
    • a width
    • a height
    • an index into the text string specifying the position at which the box should be laid out
    • Possibly some kind of baseline/alignment specifying for vertical alignment
  • Cosmic text output a Vec of box positions for each line consisting of:
    • the unique identifier
    • an x position
    • a y position

example editor-libcosmic crashes at startup

Running:

cargo clean
cargo run

I crashes at startup:

    Finished dev [unoptimized + debuginfo] target(s) in 0.12s
     Running `/home/hugo/src/github.com/pop-os/cosmic-text/target/debug/editor-libcosmic`
svg icon 'Name("format-justify-left")' size 20 not found
svg icon 'Name("format-justify-center")' size 20 not found
svg icon 'Name("format-justify-right")' size 20 not found
svg icon 'Name("format-justify-fill")' size 20 not found
thread 'main' panicked at 'misaligned pointer dereference: address must be a multiple of 0x2 but is 0x7ffa320c4413', /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/internal/parse.rs:452:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'panic in a function that cannot unwind', library/core/src/panicking.rs:126:5
stack backtrace:
   0:     0x55723dbc2e6a - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hdf22feec95880e0d
   1:     0x55723dbe754f - core::fmt::write::h235c37951d00a89c
   2:     0x55723dbbfb65 - std::io::Write::write_fmt::h9bc27794617a6db9
   3:     0x55723dbc2c35 - std::sys_common::backtrace::print::h88a8b0be3878a062
   4:     0x55723dbc42de - std::panicking::default_hook::{{closure}}::h6164333d411d28b9
   5:     0x55723dbc4085 - std::panicking::default_hook::hab575ecd0a58206d
   6:     0x55723dbc483e - std::panicking::rust_panic_with_hook::h00441c5461358113
   7:     0x55723dbc46f2 - std::panicking::begin_panic_handler::{{closure}}::ha10326a5adba7763
   8:     0x55723dbc32d6 - std::sys_common::backtrace::__rust_end_short_backtrace::he5fc416511606524
   9:     0x55723dbc4492 - rust_begin_unwind
  10:     0x55723c427f83 - core::panicking::panic_nounwind_fmt::hc94c7db7a384e0e4
  11:     0x55723c428031 - core::panicking::panic_nounwind::h3073504210c4b8f8
  12:     0x55723c4281c3 - core::panicking::panic_cannot_unwind::hc75b58def32de0a1
  13:     0x55723cbf9aff - <u16 as swash::internal::parse::FromBeData>::from_be_data_unchecked::h99595792a7416591
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/internal/parse.rs:450:5
  14:     0x55723cbf9aff - <i16 as swash::internal::parse::FromBeData>::from_be_data_unchecked::h446e05bc48a6d378
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/internal/parse.rs:461:9
  15:     0x55723cbf935c - swash::internal::parse::Stream::read::h66d836185c5a71d4
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/internal/parse.rs:216:30
  16:     0x55723cb59eaf - swash::scale::glyf::scale::Scaler::load::h03a8f82d972ac272
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/scale/glyf/scale.rs:118:33
  17:     0x55723cb58d1b - swash::scale::glyf::scale::Scaler::scale::h535ce8c014dd64a7
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/scale/glyf/scale.rs:39:9
  18:     0x55723cc0c5b0 - swash::scale::Scaler::scale_outline_impl::he8b445a1ba1b85e7
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/scale/mod.rs:560:20
  19:     0x55723cc0e2e7 - swash::scale::Render::render_into::h0933b9ca6121aa40
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/scale/mod.rs:842:24
  20:     0x55723cc0e7fd - swash::scale::Render::render::hda4c73187d0bed4f
                               at /home/hugo/.local/state/cargo/registry/src/index.crates.io-6f17d22bba15001f/swash-0.1.6/src/scale/mod.rs:974:12
  21:     0x55723ca93101 - cosmic_text::swash::swash_image::hc33a1cf5f4537c4d
                               at /home/hugo/.local/state/cargo/git/checkouts/cosmic-text-92dc585b4973ff15/e788c17/src/swash.rs:43:5
  22:     0x55723ca933d4 - cosmic_text::swash::SwashCache::get_image::{{closure}}::h8fa25964bd3b858b
                               at /home/hugo/.local/state/cargo/git/checkouts/cosmic-text-92dc585b4973ff15/e788c17/src/swash.rs:126:32
  23:     0x55723ca94e0a - std::collections::hash::map::Entry<K,V>::or_insert_with::h94a2f9259e3403a4
                               at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/collections/hash/map.rs:2558:43
  24:     0x55723ca93394 - cosmic_text::swash::SwashCache::get_image::hbf3e517cabb9f625
                               at /home/hugo/.local/state/cargo/git/checkouts/cosmic-text-92dc585b4973ff15/e788c17/src/swash.rs:124:9
  25:     0x55723c96e0e0 - iced_softbuffer::surface::draw_primitive::h0970a481c3b4741e
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/softbuffer/src/surface.rs:287:42
  26:     0x55723c4761f4 - iced_softbuffer::surface::Surface::present::{{closure}}::h5e9b10fd95213aef
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/softbuffer/src/surface.rs:80:21
  27:     0x55723c5006fa - iced_graphics::renderer::Renderer<B,T>::with_primitives::h780b7d08389fe880
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/graphics/src/renderer.rs:51:9
  28:     0x55723c4760cb - iced_softbuffer::surface::Surface::present::hb0ae66d43477e711
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/softbuffer/src/surface.rs:78:13
  29:     0x55723c511775 - <iced_softbuffer::window::compositor::Compositor<Theme> as iced_graphics::window::compositor::Compositor>::present::h9ee1693909a5e7fb
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/softbuffer/src/window/compositor.rs:77:9
  30:     0x55723c450e48 - iced_winit::application::run_instance::{{closure}}::hec88d22234ccea44
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/winit/src/application.rs:458:23
  31:     0x55723c453ba9 - iced_winit::application::run::{{closure}}::h45de27d32162e4bb
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/winit/src/application.rs:224:24
  32:     0x55723c5038e1 - winit::platform_impl::platform::sticky_exit_callback::hdb4634255939e89d
                               at /home/hugo/.local/state/cargo/git/checkouts/winit-40d7deb2b0c08730/d35712b/src/platform_impl/linux/mod.rs:854:9
  33:     0x55723c4d5411 - winit::platform_impl::platform::wayland::event_loop::EventLoop<T>::run_return::h6cf7ad6e506a868c
                               at /home/hugo/.local/state/cargo/git/checkouts/winit-40d7deb2b0c08730/d35712b/src/platform_impl/linux/wayland/event_loop/mod.rs:529:21
  34:     0x55723c502bcd - winit::platform_impl::platform::EventLoop<T>::run_return::h3df2a42843711a4b
                               at /home/hugo/.local/state/cargo/git/checkouts/winit-40d7deb2b0c08730/d35712b/src/platform_impl/linux/mod.rs:753:56
  35:     0x55723c436eaa - <winit::event_loop::EventLoop<T> as winit::platform::run_return::EventLoopExtRunReturn>::run_return::hcb4fc8f721868fbc
                               at /home/hugo/.local/state/cargo/git/checkouts/winit-40d7deb2b0c08730/d35712b/src/platform/run_return.rs:62:9
  36:     0x55723c48340d - iced_winit::application::platform::run::h478018cc34c91229
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/winit/src/application.rs:776:17
  37:     0x55723c45371a - iced_winit::application::run::h10c3b95d5ebd8873
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/winit/src/application.rs:199:5
  38:     0x55723c49e4c6 - iced::application::Application::run::h074aaf50cb55273c
                               at /home/hugo/.local/state/cargo/git/checkouts/libcosmic-b367e32ffc370f4f/035ec88/iced/src/application.rs:216:12
  39:     0x55723c4fa8bd - editor_libcosmic::main::had6a56cb3faf4b8b
                               at /home/hugo/src/github.com/pop-os/cosmic-text/examples/editor-libcosmic/src/main.rs:81:5
  40:     0x55723c4ac8b2 - core::ops::function::FnOnce::call_once::h9a5a7cdd054932c7
                               at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/ops/function.rs:250:5
  41:     0x55723c4c65b5 - std::sys_common::backtrace::__rust_begin_short_backtrace::hcfa98379b0a7c986
                               at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/sys_common/backtrace.rs:134:18
  42:     0x55723c4e6c06 - std::rt::lang_start::{{closure}}::hedf5d57ef652265b
                               at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/rt.rs:166:18
  43:     0x55723dbba5b2 - std::rt::lang_start_internal::h1181aa36d76c1285
  44:     0x55723c4e6bda - std::rt::lang_start::hb4772950ea42ef68
                               at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/rt.rs:165:17
  45:     0x55723c4ff1fe - main
  46:     0x7ffa37499880 - libc_start_main_stage2
                               at /home/buildozer/aports/main/musl/src/1.2.4/src/env/__libc_start_main.c:95:2
thread caused non-unwinding panic. aborting.
zsh: abort      cargo run

Current HEAD is 2c8d7c0bb9b486b75b8282a66fb25afc0c4fdc17.

System info

I'm on alpine, using the system compiler:

> rustc --version
rustc 1.70.0 (90c541806 2023-05-31) (Alpine Linux 1.70.0-r3)
> rustup show
Default host: x86_64-unknown-linux-musl
rustup home:  /home/hugo/.local/state/rustup

installed toolchains
--------------------

nightly-x86_64-unknown-linux-musl
system (default)

active toolchain
----------------

system (default)
rustc 1.70.0 (90c541806 2023-05-31) (Alpine Linux 1.70.0-r3)
> cat /etc/os-release 
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.18.0
PRETTY_NAME="Alpine Linux v3.18"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"

I'm not entirely sure if the issue is in libcosmic itself, or a dependency.

Text is blurry

I'm on macOS 13.2.1, with M1. When I run the editor-libcosmic example, the text looks blurry compared to native text. Some time ago I head this issue with nuklear. It was related to antialiasing. (Notice also on the screenshot some symbols aren't rendered, but I'm not sure whether it's related to cosmic-text.)
Screenshot 2023-03-08 at 17 39 40

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.