Giter Club home page Giter Club logo

skribo's People

Contributors

j0suetm avatar jdm avatar jneem avatar kant avatar linkmauve avatar pcwalton avatar raphlinus avatar sethdusek avatar simonsapin avatar tiffany352 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

skribo's Issues

Unique font id

The font loader should have an "Id" type that efficiently impl's PartialEq and Hash, that is guaranteed (with reasonably high confidence) to be equal when the fonts are equal. Such an Id is necessary for caching of any resource associated with a font (rendered glyphs, HarfBuzz font and face objects, table data, coverage bitmaps). Currently font-kit tends to use PostScript name for this purpose, but it's easy to see how this might fail uniqueness (for example, when a web font stack contains a different version of a font already present in the system). It should also be cheap to compute, because we will need it for every typeface that comes back from a fallback query.

Skia's SkTypeface has a uniqueID method. Its implementation largely rests on using a cache - when going from a native font object (for example, a DirectWrite font) to an SkTypeface, there's a comparison procedure to compare equality with existing fonts in the cache. An example for DirectWrite shows a fairly complex set of heuristics - it starts with pointer equality and other checks based on the way the font is loaded, then compares a bunch of name strings.

I'm not at all convinced font-kit should be based on a cache the way Skia does it, but it might be useful as a reference.

In any case, we should export the type, then implementations can refine the tactics to compare equality efficiently and reliably, without breaking client code. This is one reason I agree with @pcwalton it should be a type and not a small integer. Another reason is so the Debug formatting is informative.

Can shaping be reused for different line breaks?

Can a the result of shaping a Unicode string differ from concatenating the results of shaping the two parts, after splitting that string at a line break opportunity?

Or phrased differently: when making multiple layouts of an otherwise-identical paragraph by making different choices about which line break opportunities to take, can we reuse some of the results of shaping computation? This might come up for example when resizing a window or other container.

For CSS layout specifically, we sometimes need to compute the min-content and max-content intrinsic size of an element. For text, this means measuring the width of the widest line when taking respectively all or none of the break opportunities.

Assuming that shaping is somewhat expensive, it would be good if we can avoid doing it from scratch three times. (Twice for min- and max-content, then once more for “real” layout.)

High level goal: render to image

This is probably not the right place to ask this, but it's got to be one of them :)

I have a high level goal of being able to render to a plain image. No cache, no major performance constraints like fast scrolling windows - just simple text+markup+style -> image

In fact, my need for this is actually on the web, and the reason I need skribo or something similar is because there's no way to do this natively (assuming the user can edit rich text in some widget- i.e. html/css, not canvas text. The svg foreignObject hack doesn't work reliably either).

skribo as wasm would fill in the gaps wonderfully ... would something like this be on the roadmap? Am I barking up the wrong tree? Thanks!

Interaction with CSS font matching algorithm

https://drafts.csswg.org/css-fonts/#font-matching-algorithm specifies a fairly precise algorithm. font-kit has Source::select_best_match which claims part of it, but which takes no text or code point and only returns a single font.

Skribo’s input at the moment is a FontCollection which contains FontFamily’s which contains fonts. In CSS the input has an ordered list of family names (each a string, or one of the 5 generic categories). There is also special handing of @font-face fonts v.s. system fonts.

For the use case of implementing CSS, how much of this should be implemented in Skribo v.s. font-kit? How much should be left to the user?

CC @raphlinus, @pcwalton

Why provide Unicode functions in Rust rather than use Harfbuzz’s default?

Compare 51ed789 with https://github.com/harfbuzz/harfbuzz/blob/master/CONFIG.md#unicode-functions

By default, HarfBuzz ships with its own compact subset of properties from Unicode Character Database that it needs. This is a highly-optimized implementation that depending on compile settings (optimize-size or not) takes around ~40kb or ~60kb. Using this implementation (default) is highly recommended, as HarfBuzz always ships with data from latest version of Unicode. This implementation can be disabled by defining HB_NO_UCD.

Open design decisions

After digging into various details, I find myself facing quite a large number of design decisions. I'm going to try to summarize them in an issue, and hopefully converge over the next few days.

Caching vs no-cache performance

One of the most fundamental architectural decisions is whether to address slow shaping performance by adding a word cache, or try to optimize performance in the no-cache case.

A cache is likely to have a good hit rate, however it is not without its own issues. A cache consumes memory, has concurrency issues, and will always be slower in the cold case. Further, the cost of hashing and comparing cache keys is nontrivial, especially as the cache key must incorporate all inputs that can affect they layout result.

Further, it's not obvious where the cache boundaries are. In #4, behdad points out that the HarfBuzz hb_ot_layout_lookup_collect_glyphs() call can determine whether space participates in shaping, and disable the word cache.

Precedents: Minikin and Firefox both use a word cache. Blink LayoutNG does not.

This still feels like a difficult question, but I am leaning toward exploring no cache and seeing how well that works.

Layout substring queries

#4 makes a very good point, that in a common paragraph layout task, there will in general be 3 layout operations on the same text - maximum intrinsic width, minimum intrinsic width, and lines based on actual line breaks. Android deals with this by relying heavily on the cache - the first layout operation should warm the cache for subsequent operations. As I commented in #4, an appealing alternative is to retain the layout along with enough information that layout of any substring can be queried, usually by reusing the existing layout, but doing relayout if the layout is invalidated.

I am not aware of any low level text API that has this substring query, though it would not be surprising if higher levels in stacks like CoreText and DirectWrite internally did something like this. For example, the DirectWrite TexttLayout object retains an entire paragraph, has methods such as min intrinsic width, and so it would make sense to retain a layout object that supports substring queries.

More recent versions of Minikin have a LayoutPieces API which might be a useful reference.

I am leaning for this type of API; the advantages seem pretty clear, and the complexity is manageable (better than many other approaches for improving performance).

Interning of style parameters

This is related to both above topics, especially caching. The complete cache key for a cached element is a large data structure (in Minikin it has 17 fields, some of which (the locale list) are themselves composite). It is likely that many layout operations will happen with the same style parameters. One approach to this is to intern the parameters. For maximum efficiency, the interned style would be exposed in the public API.

That said, it adds a lot of complexity, and interned styles would need lifetimes managed.

The substring query API would allow interning of style once per major query, and avoiding it for substring queries. This would simplify lifetime management (the style would be simply owned by the queryable layout result) and public API.

I'm now leaning against interning, especially exposing it through public API.

Shaper-driven vs coverage driven itemization

Minikin computes cmap coverage of fonts in a font list (using a sophisticated bitmap based data structure), and chooses the best-matching font for each character in the source text (I'm simplifying it slightly). An alternative is shaper-driven itemization, which is effectively iterating through all fonts in the list, asking the shaper to use each one, and stopping when there is successful layout.

Using the shaper to drive itemization is potentially more accurate than using Unicode coverage - among other things, the shaper can take Unicode normalization into account. For custom fonts, a strong argument can be made for letting the shaper drive the process.

For fallback fonts, the story is more complicated. For one, a typical fallback font stack is dozens of fonts; iterating through all of them might be quite slow. For two, platform access to the fallbacks varies. On Mac, you get a list of fonts (with priority order based on script list), but on Windows you basically get the one best-matching font for a particular string.

Should access to fallback fonts be the same interface on all platforms? The common-denominator interface is to pass in a string and get a font back (as this works on Windows). That API can be simulated on Mac and Android (I haven't carefully researched Linux yet), but requires logic to pick the font, most likely based on Unicode coverage. Otherwise, different interfaces can be provided on different platforms. Note: I think it should be a goal that custom fonts be itemized the same on all platforms; the differences should affect system fallbacks only (where it is by definition impossible to get cross-platform consistency).

A potential hybrid approach is to use different tactics depending on script. For some scripts (most languages that have coherent blocks in Unicode), the script should be enough information to choose a font, or to exclude it from consideration. For others (latin, common, and symbols) the story is more complex. We could use script to winnow the list down, then apply shaper driven itemization.

Font matching in font-kit vs skribo

This is basically a question of where to draw architectural lines between font-kit and skribo. Right now, it seems we should iterate the design of the two crates together.

Right now font-kit has a matching algorithm. This should probably be expanded to support fallback. That's a fairly major increase in API surface, and means a lot of the work of itemization will likely happen in font-kit, as well as likely access to data such as faux style synthesis. I don't see that as a bad thing.

Moving matching to skribo is conceptually appealing, but requires designing an API in font-kit that exposes all necessary information up to the higher level. That might be practical, but I don't immediately see how to do it.

Abstraction for platform low-level text?

The original design brief for skribo calls for an abstraction so that it can either do its own layout, or depend on the platform text stack. I'm now questioning whether this is a good idea; the abstraction should likely happen at the higher level, and skribo's scope should be be strictly cases where it does the layout itself. This simplifies the design, and obviates whether a "skribo but no font-kit" configuration should be supported.

That said, as pcwalton has said it might make sense to split out some data types (especially style representation) so that they can be in common between skribo and a future high-level text abstraction.

harfbuzz-sys build fails with cannot find input file: `test/Makefile.in'

I just tried to clone the repo and run cargo build, and I got this build error when compiling the harfbuzz-sys crate dependency:

--- stderr
config.status: error: cannot find input file: `test/Makefile.in'
make: *** [all] Error 1
thread 'main' panicked at 'assertion failed: Command::new("make").env("MAKEFLAGS",
                         env::var("CARGO_MAKEFLAGS").unwrap_or_default()).args(&["-R",
                                                                                 "-f",
                                                                                 "makefile.cargo"]).status().unwrap().success()', /usr/local/google/home/clupo/.cargo/registry/src/github.com-1ecc6299db9ec823/harfbuzz-sys-0.3.1/build.rs:31:9
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

warning: build failed, waiting for other jobs to finish...
error: build failed

This happens when I compile skribo on Debian Linux. However, when I do it on my Mac it builds without the error above.

These may be related:
servo/rust-harfbuzz#137
servo/rust-harfbuzz#136

Note that if I build rust-harfbuzz directly from source (as opposed to fetching it as a dependency of skribo), it builds without that error, also on Linux.

Choice of font fallback API on macOS

My writing on font fallback (see font fallback blog post) has generally suggested using CTFontCopyDefaultCascadeListForLanguages. Responding on Twitter, Jiang Jiang suggests we use CTFontCreateForString instead.

This is much more like the Windows approach, and saves the trouble of having to go through a long font list, but on the other hand involves calling into CoreText API's on a fine grained basis every time a layout is needed. Part of my idea of getting the cascade list is that it could be retained on a per-locale basis.

Probably measurement is needed to figure out which is better. If CTFontCreateForString is very fast, then maybe that is the best choice.

How to use Skribo for text layout

I'm trying to use LayoutSession and do text line breaking using it. However, the public API only exposes the offset values which isn't based on the advance and there is no evident way of getting the width of a run when calling LayoutSession::iter_substr.

Is there something I'm missing? I may create a PR that adds what I need if not.

Should skribo provide a shaping cache?

I'm finding that a lot of Pathfinder HTML canvas-like use cases end up slow for an annoying reason: they render the same text again and again, which ends up going through skribo and HarfBuzz to be shaped repeatedly. The solution is to cache on some level (per-word or per-run), but there's the question of who should be responsible for this caching. Should skribo offer this functionality, or should it be up to Pathfinder?

As detailed in #4, this is a difficult issue, and so it might be nice for skribo to implement per-word (or whatever) caching itself so that users of the crate don't have to implement it themselves. On the other hand, perhaps we don't intend skribo to have that many direct users, which would suggest that maybe caching should be handled at a higher level than skribo itself. I'm fine either way, but we should discuss and make a decision :)

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.