profile
viewpoint
Chris Morgan chris-morgan Out in the country in western Victoria, Australia https://chrismorgan.info/

chris-morgan/anymap 160

A safe and convenient store for one value of each type

chris-morgan/arewewebyet 44

OBSOLETE. Are we web yet? A simple reckoning of Rust’s readiness for Web-related things.

chris-morgan/CodeMirror2 2

In-browser code editor

chris-morgan/classy 1

A classy JavaScript class library

chris-morgan/django-plans 1

Django application for managing account plans and quotas

chris-morgan/django-shop 1

A Django based shop system

chris-morgan/0.30000000000000004 0

Floating Point Math Examples

chris-morgan/abandoned-partially-complete-harfbuzz-api-port-to-idiomatic-rust 0

An abandoned, partially-complete port of the HarfBuzz API to idiomatic Rust

chris-morgan/alacritty 0

A cross-platform, GPU-accelerated terminal emulator

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)

Also on the markup employed: I’m a markup purist, and strongly dislike the semantics of using an unordered list with ordered markers as text for this, and the visual result of “• (a)”. The proper technique is markup like this:

<style>
    @counter-style parenthesised-lower-alpha {
        system: extends lower-alpha;
        prefix: "(";
        suffix: ")  ";
    }

    ol[type="a"] {
        list-style-type: lower-alpha; /* “a. ” (fallback for browsers that don’t support @counter-style) */
        list-style-type: parenthesised-lower-alpha; /* “(a)  ” */
    }
</style>

<ol type="a">
    <li>…
    <li>…
    <li>…
</ol>

(Markdown is bad enough that you’d need to drop into HTML here for the list. For comparison, reStructuredText pays attention to whether you wrote “1.”, “a)” or “(i)” in the source, and its HTML writer turns that into the right ol[type], even if it sadly doesn’t try to make a @counter-style for each prefix/system/suffix combo.)

But unfortunately, although Firefox implemented CSS Counter Styles 3 six years ago, no one else has followed suit. So the whole world is still reaching for awful ::before and counter() hacks whenever they want to do things like this, if they don’t like a fallback of “a.” or “1.” in other browsers.

This is really just a bit of a rant, decide for yourself whether you want to do anything with it! 🙂

raphlinus

comment created time in 3 days

PullRequestReviewEvent

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram++### Paragraph segmentation++The coarsest, and also simplest, segmentation task is paragraph segmentation. Most of the time, paragraphs are simply separated by newline (U+000A) characters, though Unicode in its infinite wisdom specifies a number of code point sequences that function as paragraph separators in plain text:++* U+000A LINE FEED+* U+000B VERTICAL TAB+* U+000C FORM FEED+* U+000D CARRIAGE RETURN+* U+000D U+000A (CR + LF)+* U+0085 NEXT LINE+* U+2008 LINE SEPARATOR+* U+2009 PARAGRAPH SEPARATOR++In rich text, paragraphs are usually indicated through markup rather than special characters, for example `<p>` or `<div>` in HTML. But in this post, as in most text layout APIs, we'll treat rich text as plain text + attribute spans.++### Rich text style++A paragraph of rich text may contain *spans* that can affect formatting. In particular, choice of font, font weight, italic or no, and a number of other attributes can affect text layout. Thus, each paragraph is typically broken into a some number of *style runs,* so that within a run the style is consistent.++Note that some style changes don't *necessarily* affect text layout. A classic example is color. Firefox, rather famously, does *not* define segmentation boundaries here for color changes. If a color boundary cuts a ligature, it uses fancy graphics techiques to render parts of the ligature in different color. But this is a subtle refinement and I think not required for basic text rendering. For more details, see [Text Rendering Hates You].++### Bidirectional analysis++Completely separate from the style spans, a paragraph may in general contain both left-to-right and right-to-left text. The need for bidirectional (BiDi) text is certainly one of the things that makes text layout more complicated.++Fortunately, this part of the stack is defined by a standard (UAX #9), and there are a number of good implementations. The interested reader is referred to [Unicode Bidirectional Algorithm basics]. The key takeaway here is that BiDi analysis is done on the plain text of the entire paragraph, and the result is a sequence of *level runs,* where the level of each run defines whether it is LTR or RTL.++The level runs and the style runs are then merged, so that in subsequent stages each run is of a consistent style and directionality. As such, for the purpose of defining the hierarchy, the result of BiDi analysis could alternatively be considered an implicit or derived rich text span.++### Itemization (font coverage)++Itemization is the trickiest and least well specified part of the hierarchy. There is no standard for it, and no common implementation. Rather, each text layout engine deals with it in its own special way.++Essentially, the result of itemization is to choose a single concrete font for a run, from a *font collection.* Generally a font collection consists of a main font (selected by font name from system fonts, or loaded as a custom asset), backed by a *fallback stack,* which are usually system fonts, but thanks to [Noto] it is possible to bundle a fallback font stack with an application, if you don't mind spending a few hundred megabytes for the assets.++Why is it so tricky? A few reasons, which I'll touch on.++First, it's not so easy to determine whether a font can render a particular string of text. One reason is [Unicode normalization]. For example, the string "é" can be encoded as U+00E9 (in NFC encoding) or as U+0065 U+0301 (in NFD encoding). Due to the principle of [Unicode equivalence], these should be rendered identically, but a font may have coverage for only one or the other in its [Character to Glyph Index Mapping] (cmap) table. The shaping engine has all the Unicode logic to handle these cases.++Of course, realistic fonts with Latin coverage will have both of these particular sequences covered in the cmap table, but edge cases certainly do happen, both in extended Latin ranges, and other scripts such as Hangul, which has complex normalization rules (thanks in part to a Korean standard for normalization which is somewhat at odds with Unicode). It's worth noting that [DirectWrite gets Hangul normalization quite wrong].++I believe a similar situation exists with the Arabic presentation forms; see [Developing Arabic fonts] for more detail on that.++Because of these tricky normalization and presentation issues, the most robust way to determine whether a font can render a string is to try it. This is how LibreOffice has worked for a while, and in 2015 [Chromium followed](https://lists.freedesktop.org/archives/harfbuzz/2015-October/005168.html). See also [Eliminating Simple Text](https://www.chromium.org/teams/layout-team/eliminating-simple-text) for more background on the Chromium text layout changes.++*Another* whole class of complexity is emoji. A lot of emoji can be rendered with either [text or emoji presentation], and there are no hard and fast rules to pick one or the other. Generally the text presentation is in a symbol font, and the emoji presentation is in a separate color font. A particularly tough example is the smiling emoji, which began its encoding life as 0x01 in [Code page 437], the standard 8-bit character encoding of the original IBM PC, and is now U+263A in Unicode. However, the suggested default presentation is text, which won't do in a world which expects color. Apple on iOS unilaterally chose an emoji presentation, so many text stacks follow Apple's lead. (Incidentally, the most robust way to encode such emoji is to append a [variation selector] to pin down the presentation).++Another source of complexity when trying to write a cross-platform text layout engine is querying the system fonts. See [Font fallback deep dive] for more information about that.++I should note one thing, which might help people doing archaeology of legacy text stacks: it used to be pretty common for text layout to resolve "compatibility" forms such as NFKC and NFKD, and this can lead to various problems. But today it is more common to solve that particular problem by providing a font stack with *massive* Unicode coverage, including all the code points in the relevant compatibility ranges.++### Script++The *shaping* of text, or the transformation of a sequence of code points into a sequence of positioned glyphs, depends on the script. Some scripts, such as Arabic and Devanagari, have extremely elaborate shaping rules, while others, such as Chinese, are a fairly straightforward mapping from code point into glyph. Latin is somewhere in the middle, starting with a straightforward mapping, but ligatures and kerning are also required for high quality text layout.++Determining script runs is reasonably straightforward - many characters have a Unicode script property which uniquely identifies which script they belong to. However, some characters, such as space, are "common," so the assigned script just continues the previous run.++A simple example is "hello мир". This string is broken into two script runs: "hello " is Latn, and "мир" is Cyrl.++### Shaping (cluster)++At this point, we have a run of constant style, font, direction, and script. It is ready for *shaping.* Shaping is a complicated process that converts a string (sequence of Unicode code points) into positioned glyphs. For the purpose of this blog post, we can generally treat it as a black box. Fortunately, a very high quality open source implementation exists, in the form of HarfBuzz.++We're not *quite* done with segmentation, though, as shaping assigns substrings in the input to [clusters] of glyphs. The correspondence depends a lot on the font. In Latin, the string "fi" is often shaped to a single glyph ( a ligature). For complex scripts such as Devanagari, a cluster is most often a syllable in the source text, and complex reordering can happen within the cluster.++Clusters are important for *hit testing,* or determining the correspondence between a physical cursor position in the text layout and the offset within the text. Generally, they can be ignored if the text will only be rendered, not edited (or selected).++Note that these shaping clusters are distinct from grapheme clusters. The "fi" example has two grapheme clusters but a single shaping cluster, so a grapheme cluster boundary can cut a shaping cluster. Since it's possible to move the cursor between the "f" and "i", one tricky problem is to determine the cursor location in that case. Fonts *do* have a [caret table], but implementation is spotty. A more robust solution is to portion the width of the cluster equally to each grapheme cluster within the cluster. See also [Let’s Stop Ascribing Meaning to Code Points] for a detailed dive into grapheme clusters.++## Line breaking++While short strings can be considered a single strip, longer strings require breaking into lines. Doing this properly is quite a tricky problem. In this post, we treat it as a separate (small) hierarchy, parallel to the main text layout hierarchy above.++The problem can be factored into identifying line break *candidates*, then choosing a subset of those candidates as line breaks that satisfy the layout constraints. The main constraint is that lines should fit within the specified maximum width. It's common to use a greedy algorithm, but high end typography tends to use an algorithm that minimizes a raggedness score for the paragraph. Knuth and Plass have a famous paper, [Breaking Paragraphs into Lines], that describes the algorithm used in TeX in detail. But we'll focus on the problems of determining candidates and measuring the widths, as these are tricky enough.++In theory, the Unicode Line Breaking Algorithm ([UAX #14]) identifies positions in a string that are candidate line breaks. In practice, there are some additional subtleties. For one, some languages (Thai is the most common) don't use spaces to divide words, so need some kind of natural language processing (based on a dictionary) to identify word boundaries. For two, automatic [hyphenation] is often desirable, as it fills lines more efficiently and makes the right edge less ragged. Liang's algorithm is most common for automatically inferring "soft hyphens," and there are many good implementations of it.++Android's line breaking implementation (in the [Minikin] library) applies an additional refinement: since email addresses and URLs are common in strings displayed on mobile devices, and since the UAX #14 rules give poor choices for those, it has an additional parser to detect those cases and apply different rules.++Finally, if words are very long or the maximum width is very narrow, it's possible for a word to exceed that width. In some cases, the line can be "overfull," but it's more common to break the word at the last grapheme cluster boundary that still fits inside the line. In Android, these are known as "desperate breaks."++So, to recap, after the paragraph segmentation (also known as "hard breaks"), there is a loose hierarchy of 3 line break candidates: word breaks as determined by UAX #14 (with possible "tailoring"), soft hyphens, and finally grapheme cluster boundaries. The first is preferred, but the other two may be used in order to satisfy the layout constraints.++This leaves another problem, which is suprisingly tricky to get fully right: how to measure the width of a line between two candidate breaks, in order to validate that it fits within the maximum width (or, in the more general case, to help compute a global raggedness score). For Latin text in a normal font, this seems almost ridiculously easy: just measure the width of each word, and add them up. But in the general case, things are nowhere nearly so simple.++First, while in Latin, most line break candidates are at space characters, in the fully general case they can cut anywhere in the text layout hierarchy, even in the middle of a cluster. An additional complication is that hyphenation can add a hyphen character.++Even without hyphenation, because [shaping is Turing Complete], the width of a line (a substring between two line break candidates) can be any function. Of course, such extreme cases are rare; it's most common for the widths to be exactly equal to the sum of the widths of the words, and even in the other cases this tends to be a good approximation.++So getting this exactly right in the general case is conceptually not difficult, but is horribly inefficient: for each candidate for the end of the line, perform text layout (mostly shaping) on the substring from the beginning of the line (possibly inserting a hyphen), and measure the width of that layout.++Very few text layout engines even try to handle this general case, using various heuristics and approximations which work well most of the time, but break down when presented with a font with shaping rules that change widths aggressively. DirectWrite does, however, using very clever techniques that took several years of iteration. The full story is in [harfbuzz/harfbuzz#1463 (comment)]. Further analysis, towards a goal of getting this implemented in an open source text layout engine, is in [yeslogic/allsorts#29]. If and when either HarfBuzz or Allsorts implements the lower-level logic, I'll probably want to write another blog post explaining in more detail how a higher level text layout engine can take advantage of it.++## Implementations to study++While I still feel a need for a solid, high-level, cross-platform text layout engine, there are good implementations to study. In open source, on of my favorites (though I am biased), is the Android text stack, based on [Minikin] for its lower levels. It is fairly capable and efficient, and also makes a concerted effort to get "all of Unicode" right, including emoji. It is also reasonably simple and the code is accessible.++While not open source, [DirectWrite] is also well worth study, as it is without question one of the most capable engines, supporting Word and the previous iteration of Edge before it was abandonded in favor of Chromium. Note that there is a [proposal][DWriteCore proposal] for a cross-platform implementation and also potentially to take it open-source. If that were to happen, it would be something of a game changer.++### Android++Paragraph and style segmentation (with BiDi) is done at higher levels, in [Layout.java] and [StaticLayout.java]. At that point, runs are handed to [Minikin] for lower-level processing. Most of the rest of the hierarchy is in Layout.cpp, and ultimately shaping is done by HarfBuzz.++Minikin also contains a sophisticated line breaking implementation, including Knuth-Plass style optimized breaking.++Android deals with shaping boundaries by using heuristics to further segment the text to implied word boundaries (which are also used as the grain for layout cache). If a font does shaping across these boundaries, the shaping context is simply lost. This is a reasonable compromise, especially in mobile, as results are always consistent, ie the width for measurement never mismatches the width for layout. And none of the fonts in the system stack have exotic behavior such as shaping across spaces.++Android does base its itemization on cmap coverage, and builds sophisticated bitmap structures for fast queries. As such, it can get normalization issues wrong, but overall this seems like a reasonable compromise. In particular, most of the time you'll run into normalization issues is with Latin and the combining diacritical marks, both of which are supplied by Roboto, which in turn has massive Unicode coverage (and thus less need to rely on normalization logic). But with custom fonts, handling may be less than ideal, resulting in more fallback to Roboto than might actually be needed.++Note that Minikin was also the starting point for [libTxt](https://github.com/flutter/flutter/issues/11092), the text layout library used in Flutter.++## DirectWrite++Some notes on things I've found while studying the API; these observations are quite a bit in the weeds, but might be useful to people wanting to deeply understand or engage the API.++Hit testing in DirectWrite is based on leading/trailing positions, while in Android it's based on primary and secondary. The latter is more useful for text edition, but leading/trailing is a more well-defined concept (for one, it doesn't rely on paragraph direction). For more information on this topic, see [linebender/piet#323](https://github.com/linebender/piet/issues/323). My take is that proper hit testing requires iterating through the text layout to access lower level structures.++While Core Text (see below) exposes a hierarchy of objects, DirectWrite uses the [TextLayout](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritetextlayout) as the primary interface, and exposes internal structure (even including lines) by iterating over a callback per run in the confusingly named [Draw](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritetextlayout-draw) method. The granularity of this callback is a [glyph run](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_glyph_run), which corresponds to "script" in the hierarchy above. Cluster information is provided in an associated [glyph run description](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_glyph_run_description) structure.++There are other ways to access lower level text layout capabilities, including +[TextAnalyzer](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritetextanalyzer), which computes BiDi and line break opportunities, script runs, and shaping. In fact, the various methods on that interface represents much of the internal structure of the text layout engine. Itemization, however, is done in the [FontFallback](https://docs.microsoft.com/en-us/windows/win32/api/dwrite_2/nn-dwrite_2-idwritefontfallback) interface, which was added later.++### Core Text++Another high quality implementation is [Core Text]. I don't personally find it as well designed as DirectWrite, but it does get the job done. In general, though, Core Text is considered a lower level interface, and applications are recommended to use a higher level mechanism (Cocoa text on macOS, Text Kit on iOS).++When doing text layout on macOS, it's probably better to use the platform-provided itemization method (), rather than getting the font list and doing itemization in the client. See [linebender/skribo#14](https://github.com/linebender/skribo/issues/14) for more information on this tradeoff.++### Druid/Piet++At this point, the Druid GUI toolkit does not have its own native text layout engine, but rather *does* provide a [cross-platform API][Piet text layout API] which is delegated to platform text layout engines, DirectWrite and Core Text in particular.++The situation on Linux is currently unsatisfactory, as it's based on the Cairo toy text API. There is work ongoing to improve this, but no promises when.++While the Piet text API is currently fairly basic, I do think it's a good starting point for text layout, especially in the Rust community. While the complexity of Web text basically forces browsers to do all their text layout from scratch, for UI text there are serious advantages to using the platform.

I’m not sure what you’re saying here in the second sentence.

raphlinus

comment created time in 3 days

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram

A pyramid diagram, I imagine? As I’ve just commented on Zulip, I’m getting the impression that rich text style and BiDi are at the same level. (Just making sure that gets recorded here.) That fits nicely into a pyramid diagram.

raphlinus

comment created time in 3 days

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram++### Paragraph segmentation++The coarsest, and also simplest, segmentation task is paragraph segmentation. Most of the time, paragraphs are simply separated by newline (U+000A) characters, though Unicode in its infinite wisdom specifies a number of code point sequences that function as paragraph separators in plain text:++* U+000A LINE FEED+* U+000B VERTICAL TAB+* U+000C FORM FEED+* U+000D CARRIAGE RETURN+* U+000D U+000A (CR + LF)+* U+0085 NEXT LINE+* U+2008 LINE SEPARATOR+* U+2009 PARAGRAPH SEPARATOR++In rich text, paragraphs are usually indicated through markup rather than special characters, for example `<p>` or `<div>` in HTML. But in this post, as in most text layout APIs, we'll treat rich text as plain text + attribute spans.++### Rich text style++A paragraph of rich text may contain *spans* that can affect formatting. In particular, choice of font, font weight, italic or no, and a number of other attributes can affect text layout. Thus, each paragraph is typically broken into a some number of *style runs,* so that within a run the style is consistent.++Note that some style changes don't *necessarily* affect text layout. A classic example is color. Firefox, rather famously, does *not* define segmentation boundaries here for color changes. If a color boundary cuts a ligature, it uses fancy graphics techiques to render parts of the ligature in different color. But this is a subtle refinement and I think not required for basic text rendering. For more details, see [Text Rendering Hates You].++### Bidirectional analysis++Completely separate from the style spans, a paragraph may in general contain both left-to-right and right-to-left text. The need for bidirectional (BiDi) text is certainly one of the things that makes text layout more complicated.++Fortunately, this part of the stack is defined by a standard (UAX #9), and there are a number of good implementations. The interested reader is referred to [Unicode Bidirectional Algorithm basics]. The key takeaway here is that BiDi analysis is done on the plain text of the entire paragraph, and the result is a sequence of *level runs,* where the level of each run defines whether it is LTR or RTL.++The level runs and the style runs are then merged, so that in subsequent stages each run is of a consistent style and directionality. As such, for the purpose of defining the hierarchy, the result of BiDi analysis could alternatively be considered an implicit or derived rich text span.++### Itemization (font coverage)++Itemization is the trickiest and least well specified part of the hierarchy. There is no standard for it, and no common implementation. Rather, each text layout engine deals with it in its own special way.++Essentially, the result of itemization is to choose a single concrete font for a run, from a *font collection.* Generally a font collection consists of a main font (selected by font name from system fonts, or loaded as a custom asset), backed by a *fallback stack,* which are usually system fonts, but thanks to [Noto] it is possible to bundle a fallback font stack with an application, if you don't mind spending a few hundred megabytes for the assets.++Why is it so tricky? A few reasons, which I'll touch on.++First, it's not so easy to determine whether a font can render a particular string of text. One reason is [Unicode normalization]. For example, the string "é" can be encoded as U+00E9 (in NFC encoding) or as U+0065 U+0301 (in NFD encoding). Due to the principle of [Unicode equivalence], these should be rendered identically, but a font may have coverage for only one or the other in its [Character to Glyph Index Mapping] (cmap) table. The shaping engine has all the Unicode logic to handle these cases.++Of course, realistic fonts with Latin coverage will have both of these particular sequences covered in the cmap table, but edge cases certainly do happen, both in extended Latin ranges, and other scripts such as Hangul, which has complex normalization rules (thanks in part to a Korean standard for normalization which is somewhat at odds with Unicode). It's worth noting that [DirectWrite gets Hangul normalization quite wrong].++I believe a similar situation exists with the Arabic presentation forms; see [Developing Arabic fonts] for more detail on that.++Because of these tricky normalization and presentation issues, the most robust way to determine whether a font can render a string is to try it. This is how LibreOffice has worked for a while, and in 2015 [Chromium followed](https://lists.freedesktop.org/archives/harfbuzz/2015-October/005168.html). See also [Eliminating Simple Text](https://www.chromium.org/teams/layout-team/eliminating-simple-text) for more background on the Chromium text layout changes.++*Another* whole class of complexity is emoji. A lot of emoji can be rendered with either [text or emoji presentation], and there are no hard and fast rules to pick one or the other. Generally the text presentation is in a symbol font, and the emoji presentation is in a separate color font. A particularly tough example is the smiling emoji, which began its encoding life as 0x01 in [Code page 437], the standard 8-bit character encoding of the original IBM PC, and is now U+263A in Unicode. However, the suggested default presentation is text, which won't do in a world which expects color. Apple on iOS unilaterally chose an emoji presentation, so many text stacks follow Apple's lead. (Incidentally, the most robust way to encode such emoji is to append a [variation selector] to pin down the presentation).++Another source of complexity when trying to write a cross-platform text layout engine is querying the system fonts. See [Font fallback deep dive] for more information about that.++I should note one thing, which might help people doing archaeology of legacy text stacks: it used to be pretty common for text layout to resolve "compatibility" forms such as NFKC and NFKD, and this can lead to various problems. But today it is more common to solve that particular problem by providing a font stack with *massive* Unicode coverage, including all the code points in the relevant compatibility ranges.++### Script++The *shaping* of text, or the transformation of a sequence of code points into a sequence of positioned glyphs, depends on the script. Some scripts, such as Arabic and Devanagari, have extremely elaborate shaping rules, while others, such as Chinese, are a fairly straightforward mapping from code point into glyph. Latin is somewhere in the middle, starting with a straightforward mapping, but ligatures and kerning are also required for high quality text layout.++Determining script runs is reasonably straightforward - many characters have a Unicode script property which uniquely identifies which script they belong to. However, some characters, such as space, are "common," so the assigned script just continues the previous run.++A simple example is "hello мир". This string is broken into two script runs: "hello " is Latn, and "мир" is Cyrl.++### Shaping (cluster)++At this point, we have a run of constant style, font, direction, and script. It is ready for *shaping.* Shaping is a complicated process that converts a string (sequence of Unicode code points) into positioned glyphs. For the purpose of this blog post, we can generally treat it as a black box. Fortunately, a very high quality open source implementation exists, in the form of HarfBuzz.++We're not *quite* done with segmentation, though, as shaping assigns substrings in the input to [clusters] of glyphs. The correspondence depends a lot on the font. In Latin, the string "fi" is often shaped to a single glyph ( a ligature). For complex scripts such as Devanagari, a cluster is most often a syllable in the source text, and complex reordering can happen within the cluster.++Clusters are important for *hit testing,* or determining the correspondence between a physical cursor position in the text layout and the offset within the text. Generally, they can be ignored if the text will only be rendered, not edited (or selected).++Note that these shaping clusters are distinct from grapheme clusters. The "fi" example has two grapheme clusters but a single shaping cluster, so a grapheme cluster boundary can cut a shaping cluster. Since it's possible to move the cursor between the "f" and "i", one tricky problem is to determine the cursor location in that case. Fonts *do* have a [caret table], but implementation is spotty. A more robust solution is to portion the width of the cluster equally to each grapheme cluster within the cluster. See also [Let’s Stop Ascribing Meaning to Code Points] for a detailed dive into grapheme clusters.

Related here is the good old Firefox “f soft-hyphen f” that can splits a ligature glyph across lines. And of course there’s no universally-ideal answer here, because the ligature probably changes the metrics and thus potentially the line break position. That’s one of my favourite still-extant bugs. (I haven’t delved deeply, but I think this sort of thing is part of what the harfbuzz#1463 and allsorts#29 issues are about.)

raphlinus

comment created time in 3 days

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram++### Paragraph segmentation++The coarsest, and also simplest, segmentation task is paragraph segmentation. Most of the time, paragraphs are simply separated by newline (U+000A) characters, though Unicode in its infinite wisdom specifies a number of code point sequences that function as paragraph separators in plain text:++* U+000A LINE FEED+* U+000B VERTICAL TAB+* U+000C FORM FEED+* U+000D CARRIAGE RETURN+* U+000D U+000A (CR + LF)+* U+0085 NEXT LINE+* U+2008 LINE SEPARATOR+* U+2009 PARAGRAPH SEPARATOR++In rich text, paragraphs are usually indicated through markup rather than special characters, for example `<p>` or `<div>` in HTML. But in this post, as in most text layout APIs, we'll treat rich text as plain text + attribute spans.++### Rich text style++A paragraph of rich text may contain *spans* that can affect formatting. In particular, choice of font, font weight, italic or no, and a number of other attributes can affect text layout. Thus, each paragraph is typically broken into a some number of *style runs,* so that within a run the style is consistent.++Note that some style changes don't *necessarily* affect text layout. A classic example is color. Firefox, rather famously, does *not* define segmentation boundaries here for color changes. If a color boundary cuts a ligature, it uses fancy graphics techiques to render parts of the ligature in different color. But this is a subtle refinement and I think not required for basic text rendering. For more details, see [Text Rendering Hates You].++### Bidirectional analysis++Completely separate from the style spans, a paragraph may in general contain both left-to-right and right-to-left text. The need for bidirectional (BiDi) text is certainly one of the things that makes text layout more complicated.++Fortunately, this part of the stack is defined by a standard (UAX #9), and there are a number of good implementations. The interested reader is referred to [Unicode Bidirectional Algorithm basics]. The key takeaway here is that BiDi analysis is done on the plain text of the entire paragraph, and the result is a sequence of *level runs,* where the level of each run defines whether it is LTR or RTL.++The level runs and the style runs are then merged, so that in subsequent stages each run is of a consistent style and directionality. As such, for the purpose of defining the hierarchy, the result of BiDi analysis could alternatively be considered an implicit or derived rich text span.++### Itemization (font coverage)++Itemization is the trickiest and least well specified part of the hierarchy. There is no standard for it, and no common implementation. Rather, each text layout engine deals with it in its own special way.++Essentially, the result of itemization is to choose a single concrete font for a run, from a *font collection.* Generally a font collection consists of a main font (selected by font name from system fonts, or loaded as a custom asset), backed by a *fallback stack,* which are usually system fonts, but thanks to [Noto] it is possible to bundle a fallback font stack with an application, if you don't mind spending a few hundred megabytes for the assets.++Why is it so tricky? A few reasons, which I'll touch on.++First, it's not so easy to determine whether a font can render a particular string of text. One reason is [Unicode normalization]. For example, the string "é" can be encoded as U+00E9 (in NFC encoding) or as U+0065 U+0301 (in NFD encoding). Due to the principle of [Unicode equivalence], these should be rendered identically, but a font may have coverage for only one or the other in its [Character to Glyph Index Mapping] (cmap) table. The shaping engine has all the Unicode logic to handle these cases.++Of course, realistic fonts with Latin coverage will have both of these particular sequences covered in the cmap table, but edge cases certainly do happen, both in extended Latin ranges, and other scripts such as Hangul, which has complex normalization rules (thanks in part to a Korean standard for normalization which is somewhat at odds with Unicode). It's worth noting that [DirectWrite gets Hangul normalization quite wrong].++I believe a similar situation exists with the Arabic presentation forms; see [Developing Arabic fonts] for more detail on that.++Because of these tricky normalization and presentation issues, the most robust way to determine whether a font can render a string is to try it. This is how LibreOffice has worked for a while, and in 2015 [Chromium followed](https://lists.freedesktop.org/archives/harfbuzz/2015-October/005168.html). See also [Eliminating Simple Text](https://www.chromium.org/teams/layout-team/eliminating-simple-text) for more background on the Chromium text layout changes.++*Another* whole class of complexity is emoji. A lot of emoji can be rendered with either [text or emoji presentation], and there are no hard and fast rules to pick one or the other. Generally the text presentation is in a symbol font, and the emoji presentation is in a separate color font. A particularly tough example is the smiling emoji, which began its encoding life as 0x01 in [Code page 437], the standard 8-bit character encoding of the original IBM PC, and is now U+263A in Unicode. However, the suggested default presentation is text, which won't do in a world which expects color. Apple on iOS unilaterally chose an emoji presentation, so many text stacks follow Apple's lead. (Incidentally, the most robust way to encode such emoji is to append a [variation selector] to pin down the presentation).++Another source of complexity when trying to write a cross-platform text layout engine is querying the system fonts. See [Font fallback deep dive] for more information about that.++I should note one thing, which might help people doing archaeology of legacy text stacks: it used to be pretty common for text layout to resolve "compatibility" forms such as NFKC and NFKD, and this can lead to various problems. But today it is more common to solve that particular problem by providing a font stack with *massive* Unicode coverage, including all the code points in the relevant compatibility ranges.++### Script++The *shaping* of text, or the transformation of a sequence of code points into a sequence of positioned glyphs, depends on the script. Some scripts, such as Arabic and Devanagari, have extremely elaborate shaping rules, while others, such as Chinese, are a fairly straightforward mapping from code point into glyph. Latin is somewhere in the middle, starting with a straightforward mapping, but ligatures and kerning are also required for high quality text layout.++Determining script runs is reasonably straightforward - many characters have a Unicode script property which uniquely identifies which script they belong to. However, some characters, such as space, are "common," so the assigned script just continues the previous run.++A simple example is "hello мир". This string is broken into two script runs: "hello " is Latn, and "мир" is Cyrl.++### Shaping (cluster)++At this point, we have a run of constant style, font, direction, and script. It is ready for *shaping.* Shaping is a complicated process that converts a string (sequence of Unicode code points) into positioned glyphs. For the purpose of this blog post, we can generally treat it as a black box. Fortunately, a very high quality open source implementation exists, in the form of HarfBuzz.++We're not *quite* done with segmentation, though, as shaping assigns substrings in the input to [clusters] of glyphs. The correspondence depends a lot on the font. In Latin, the string "fi" is often shaped to a single glyph ( a ligature). For complex scripts such as Devanagari, a cluster is most often a syllable in the source text, and complex reordering can happen within the cluster.++Clusters are important for *hit testing,* or determining the correspondence between a physical cursor position in the text layout and the offset within the text. Generally, they can be ignored if the text will only be rendered, not edited (or selected).++Note that these shaping clusters are distinct from grapheme clusters. The "fi" example has two grapheme clusters but a single shaping cluster, so a grapheme cluster boundary can cut a shaping cluster. Since it's possible to move the cursor between the "f" and "i", one tricky problem is to determine the cursor location in that case. Fonts *do* have a [caret table], but implementation is spotty. A more robust solution is to portion the width of the cluster equally to each grapheme cluster within the cluster. See also [Let’s Stop Ascribing Meaning to Code Points] for a detailed dive into grapheme clusters.++## Line breaking++While short strings can be considered a single strip, longer strings require breaking into lines. Doing this properly is quite a tricky problem. In this post, we treat it as a separate (small) hierarchy, parallel to the main text layout hierarchy above.++The problem can be factored into identifying line break *candidates*, then choosing a subset of those candidates as line breaks that satisfy the layout constraints. The main constraint is that lines should fit within the specified maximum width. It's common to use a greedy algorithm, but high end typography tends to use an algorithm that minimizes a raggedness score for the paragraph. Knuth and Plass have a famous paper, [Breaking Paragraphs into Lines], that describes the algorithm used in TeX in detail. But we'll focus on the problems of determining candidates and measuring the widths, as these are tricky enough.++In theory, the Unicode Line Breaking Algorithm ([UAX #14]) identifies positions in a string that are candidate line breaks. In practice, there are some additional subtleties. For one, some languages (Thai is the most common) don't use spaces to divide words, so need some kind of natural language processing (based on a dictionary) to identify word boundaries. For two, automatic [hyphenation] is often desirable, as it fills lines more efficiently and makes the right edge less ragged. Liang's algorithm is most common for automatically inferring "soft hyphens," and there are many good implementations of it.++Android's line breaking implementation (in the [Minikin] library) applies an additional refinement: since email addresses and URLs are common in strings displayed on mobile devices, and since the UAX #14 rules give poor choices for those, it has an additional parser to detect those cases and apply different rules.++Finally, if words are very long or the maximum width is very narrow, it's possible for a word to exceed that width. In some cases, the line can be "overfull," but it's more common to break the word at the last grapheme cluster boundary that still fits inside the line. In Android, these are known as "desperate breaks."++So, to recap, after the paragraph segmentation (also known as "hard breaks"), there is a loose hierarchy of 3 line break candidates: word breaks as determined by UAX #14 (with possible "tailoring"), soft hyphens, and finally grapheme cluster boundaries. The first is preferred, but the other two may be used in order to satisfy the layout constraints.++This leaves another problem, which is suprisingly tricky to get fully right: how to measure the width of a line between two candidate breaks, in order to validate that it fits within the maximum width (or, in the more general case, to help compute a global raggedness score). For Latin text in a normal font, this seems almost ridiculously easy: just measure the width of each word, and add them up. But in the general case, things are nowhere nearly so simple.++First, while in Latin, most line break candidates are at space characters, in the fully general case they can cut anywhere in the text layout hierarchy, even in the middle of a cluster. An additional complication is that hyphenation can add a hyphen character.++Even without hyphenation, because [shaping is Turing Complete], the width of a line (a substring between two line break candidates) can be any function. Of course, such extreme cases are rare; it's most common for the widths to be exactly equal to the sum of the widths of the words, and even in the other cases this tends to be a good approximation.++So getting this exactly right in the general case is conceptually not difficult, but is horribly inefficient: for each candidate for the end of the line, perform text layout (mostly shaping) on the substring from the beginning of the line (possibly inserting a hyphen), and measure the width of that layout.++Very few text layout engines even try to handle this general case, using various heuristics and approximations which work well most of the time, but break down when presented with a font with shaping rules that change widths aggressively. DirectWrite does, however, using very clever techniques that took several years of iteration. The full story is in [harfbuzz/harfbuzz#1463 (comment)]. Further analysis, towards a goal of getting this implemented in an open source text layout engine, is in [yeslogic/allsorts#29]. If and when either HarfBuzz or Allsorts implements the lower-level logic, I'll probably want to write another blog post explaining in more detail how a higher level text layout engine can take advantage of it.++## Implementations to study++While I still feel a need for a solid, high-level, cross-platform text layout engine, there are good implementations to study. In open source, on of my favorites (though I am biased), is the Android text stack, based on [Minikin] for its lower levels. It is fairly capable and efficient, and also makes a concerted effort to get "all of Unicode" right, including emoji. It is also reasonably simple and the code is accessible.++While not open source, [DirectWrite] is also well worth study, as it is without question one of the most capable engines, supporting Word and the previous iteration of Edge before it was abandonded in favor of Chromium. Note that there is a [proposal][DWriteCore proposal] for a cross-platform implementation and also potentially to take it open-source. If that were to happen, it would be something of a game changer.++### Android++Paragraph and style segmentation (with BiDi) is done at higher levels, in [Layout.java] and [StaticLayout.java]. At that point, runs are handed to [Minikin] for lower-level processing. Most of the rest of the hierarchy is in Layout.cpp, and ultimately shaping is done by HarfBuzz.++Minikin also contains a sophisticated line breaking implementation, including Knuth-Plass style optimized breaking.++Android deals with shaping boundaries by using heuristics to further segment the text to implied word boundaries (which are also used as the grain for layout cache). If a font does shaping across these boundaries, the shaping context is simply lost. This is a reasonable compromise, especially in mobile, as results are always consistent, ie the width for measurement never mismatches the width for layout. And none of the fonts in the system stack have exotic behavior such as shaping across spaces.++Android does base its itemization on cmap coverage, and builds sophisticated bitmap structures for fast queries. As such, it can get normalization issues wrong, but overall this seems like a reasonable compromise. In particular, most of the time you'll run into normalization issues is with Latin and the combining diacritical marks, both of which are supplied by Roboto, which in turn has massive Unicode coverage (and thus less need to rely on normalization logic). But with custom fonts, handling may be less than ideal, resulting in more fallback to Roboto than might actually be needed.++Note that Minikin was also the starting point for [libTxt](https://github.com/flutter/flutter/issues/11092), the text layout library used in Flutter.++## DirectWrite++Some notes on things I've found while studying the API; these observations are quite a bit in the weeds, but might be useful to people wanting to deeply understand or engage the API.++Hit testing in DirectWrite is based on leading/trailing positions, while in Android it's based on primary and secondary. The latter is more useful for text edition, but leading/trailing is a more well-defined concept (for one, it doesn't rely on paragraph direction). For more information on this topic, see [linebender/piet#323](https://github.com/linebender/piet/issues/323). My take is that proper hit testing requires iterating through the text layout to access lower level structures.++While Core Text (see below) exposes a hierarchy of objects, DirectWrite uses the [TextLayout](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritetextlayout) as the primary interface, and exposes internal structure (even including lines) by iterating over a callback per run in the confusingly named [Draw](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritetextlayout-draw) method. The granularity of this callback is a [glyph run](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_glyph_run), which corresponds to "script" in the hierarchy above. Cluster information is provided in an associated [glyph run description](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_glyph_run_description) structure.++There are other ways to access lower level text layout capabilities, including +[TextAnalyzer](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritetextanalyzer), which computes BiDi and line break opportunities, script runs, and shaping. In fact, the various methods on that interface represents much of the internal structure of the text layout engine. Itemization, however, is done in the [FontFallback](https://docs.microsoft.com/en-us/windows/win32/api/dwrite_2/nn-dwrite_2-idwritefontfallback) interface, which was added later.++### Core Text++Another high quality implementation is [Core Text]. I don't personally find it as well designed as DirectWrite, but it does get the job done. In general, though, Core Text is considered a lower level interface, and applications are recommended to use a higher level mechanism (Cocoa text on macOS, Text Kit on iOS).++When doing text layout on macOS, it's probably better to use the platform-provided itemization method (), rather than getting the font list and doing itemization in the client. See [linebender/skribo#14](https://github.com/linebender/skribo/issues/14) for more information on this tradeoff.

Empty parentheses; I think some link was intended.

raphlinus

comment created time in 3 days

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram++### Paragraph segmentation++The coarsest, and also simplest, segmentation task is paragraph segmentation. Most of the time, paragraphs are simply separated by newline (U+000A) characters, though Unicode in its infinite wisdom specifies a number of code point sequences that function as paragraph separators in plain text:++* U+000A LINE FEED+* U+000B VERTICAL TAB+* U+000C FORM FEED+* U+000D CARRIAGE RETURN+* U+000D U+000A (CR + LF)+* U+0085 NEXT LINE+* U+2008 LINE SEPARATOR+* U+2009 PARAGRAPH SEPARATOR++In rich text, paragraphs are usually indicated through markup rather than special characters, for example `<p>` or `<div>` in HTML. But in this post, as in most text layout APIs, we'll treat rich text as plain text + attribute spans.++### Rich text style++A paragraph of rich text may contain *spans* that can affect formatting. In particular, choice of font, font weight, italic or no, and a number of other attributes can affect text layout. Thus, each paragraph is typically broken into a some number of *style runs,* so that within a run the style is consistent.++Note that some style changes don't *necessarily* affect text layout. A classic example is color. Firefox, rather famously, does *not* define segmentation boundaries here for color changes. If a color boundary cuts a ligature, it uses fancy graphics techiques to render parts of the ligature in different color. But this is a subtle refinement and I think not required for basic text rendering. For more details, see [Text Rendering Hates You].++### Bidirectional analysis++Completely separate from the style spans, a paragraph may in general contain both left-to-right and right-to-left text. The need for bidirectional (BiDi) text is certainly one of the things that makes text layout more complicated.++Fortunately, this part of the stack is defined by a standard (UAX #9), and there are a number of good implementations. The interested reader is referred to [Unicode Bidirectional Algorithm basics]. The key takeaway here is that BiDi analysis is done on the plain text of the entire paragraph, and the result is a sequence of *level runs,* where the level of each run defines whether it is LTR or RTL.++The level runs and the style runs are then merged, so that in subsequent stages each run is of a consistent style and directionality. As such, for the purpose of defining the hierarchy, the result of BiDi analysis could alternatively be considered an implicit or derived rich text span.++### Itemization (font coverage)++Itemization is the trickiest and least well specified part of the hierarchy. There is no standard for it, and no common implementation. Rather, each text layout engine deals with it in its own special way.++Essentially, the result of itemization is to choose a single concrete font for a run, from a *font collection.* Generally a font collection consists of a main font (selected by font name from system fonts, or loaded as a custom asset), backed by a *fallback stack,* which are usually system fonts, but thanks to [Noto] it is possible to bundle a fallback font stack with an application, if you don't mind spending a few hundred megabytes for the assets.++Why is it so tricky? A few reasons, which I'll touch on.++First, it's not so easy to determine whether a font can render a particular string of text. One reason is [Unicode normalization]. For example, the string "é" can be encoded as U+00E9 (in NFC encoding) or as U+0065 U+0301 (in NFD encoding). Due to the principle of [Unicode equivalence], these should be rendered identically, but a font may have coverage for only one or the other in its [Character to Glyph Index Mapping] (cmap) table. The shaping engine has all the Unicode logic to handle these cases.++Of course, realistic fonts with Latin coverage will have both of these particular sequences covered in the cmap table, but edge cases certainly do happen, both in extended Latin ranges, and other scripts such as Hangul, which has complex normalization rules (thanks in part to a Korean standard for normalization which is somewhat at odds with Unicode). It's worth noting that [DirectWrite gets Hangul normalization quite wrong].++I believe a similar situation exists with the Arabic presentation forms; see [Developing Arabic fonts] for more detail on that.++Because of these tricky normalization and presentation issues, the most robust way to determine whether a font can render a string is to try it. This is how LibreOffice has worked for a while, and in 2015 [Chromium followed](https://lists.freedesktop.org/archives/harfbuzz/2015-October/005168.html). See also [Eliminating Simple Text](https://www.chromium.org/teams/layout-team/eliminating-simple-text) for more background on the Chromium text layout changes.++*Another* whole class of complexity is emoji. A lot of emoji can be rendered with either [text or emoji presentation], and there are no hard and fast rules to pick one or the other. Generally the text presentation is in a symbol font, and the emoji presentation is in a separate color font. A particularly tough example is the smiling emoji, which began its encoding life as 0x01 in [Code page 437], the standard 8-bit character encoding of the original IBM PC, and is now U+263A in Unicode. However, the suggested default presentation is text, which won't do in a world which expects color. Apple on iOS unilaterally chose an emoji presentation, so many text stacks follow Apple's lead. (Incidentally, the most robust way to encode such emoji is to append a [variation selector] to pin down the presentation).++Another source of complexity when trying to write a cross-platform text layout engine is querying the system fonts. See [Font fallback deep dive] for more information about that.++I should note one thing, which might help people doing archaeology of legacy text stacks: it used to be pretty common for text layout to resolve "compatibility" forms such as NFKC and NFKD, and this can lead to various problems. But today it is more common to solve that particular problem by providing a font stack with *massive* Unicode coverage, including all the code points in the relevant compatibility ranges.++### Script++The *shaping* of text, or the transformation of a sequence of code points into a sequence of positioned glyphs, depends on the script. Some scripts, such as Arabic and Devanagari, have extremely elaborate shaping rules, while others, such as Chinese, are a fairly straightforward mapping from code point into glyph. Latin is somewhere in the middle, starting with a straightforward mapping, but ligatures and kerning are also required for high quality text layout.++Determining script runs is reasonably straightforward - many characters have a Unicode script property which uniquely identifies which script they belong to. However, some characters, such as space, are "common," so the assigned script just continues the previous run.++A simple example is "hello мир". This string is broken into two script runs: "hello " is Latn, and "мир" is Cyrl.++### Shaping (cluster)++At this point, we have a run of constant style, font, direction, and script. It is ready for *shaping.* Shaping is a complicated process that converts a string (sequence of Unicode code points) into positioned glyphs. For the purpose of this blog post, we can generally treat it as a black box. Fortunately, a very high quality open source implementation exists, in the form of HarfBuzz.++We're not *quite* done with segmentation, though, as shaping assigns substrings in the input to [clusters] of glyphs. The correspondence depends a lot on the font. In Latin, the string "fi" is often shaped to a single glyph ( a ligature). For complex scripts such as Devanagari, a cluster is most often a syllable in the source text, and complex reordering can happen within the cluster.

s/\( /(/

raphlinus

comment created time in 3 days

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram++### Paragraph segmentation++The coarsest, and also simplest, segmentation task is paragraph segmentation. Most of the time, paragraphs are simply separated by newline (U+000A) characters, though Unicode in its infinite wisdom specifies a number of code point sequences that function as paragraph separators in plain text:++* U+000A LINE FEED+* U+000B VERTICAL TAB+* U+000C FORM FEED+* U+000D CARRIAGE RETURN+* U+000D U+000A (CR + LF)+* U+0085 NEXT LINE+* U+2008 LINE SEPARATOR+* U+2009 PARAGRAPH SEPARATOR++In rich text, paragraphs are usually indicated through markup rather than special characters, for example `<p>` or `<div>` in HTML. But in this post, as in most text layout APIs, we'll treat rich text as plain text + attribute spans.++### Rich text style++A paragraph of rich text may contain *spans* that can affect formatting. In particular, choice of font, font weight, italic or no, and a number of other attributes can affect text layout. Thus, each paragraph is typically broken into a some number of *style runs,* so that within a run the style is consistent.++Note that some style changes don't *necessarily* affect text layout. A classic example is color. Firefox, rather famously, does *not* define segmentation boundaries here for color changes. If a color boundary cuts a ligature, it uses fancy graphics techiques to render parts of the ligature in different color. But this is a subtle refinement and I think not required for basic text rendering. For more details, see [Text Rendering Hates You].++### Bidirectional analysis++Completely separate from the style spans, a paragraph may in general contain both left-to-right and right-to-left text. The need for bidirectional (BiDi) text is certainly one of the things that makes text layout more complicated.++Fortunately, this part of the stack is defined by a standard (UAX #9), and there are a number of good implementations. The interested reader is referred to [Unicode Bidirectional Algorithm basics]. The key takeaway here is that BiDi analysis is done on the plain text of the entire paragraph, and the result is a sequence of *level runs,* where the level of each run defines whether it is LTR or RTL.++The level runs and the style runs are then merged, so that in subsequent stages each run is of a consistent style and directionality. As such, for the purpose of defining the hierarchy, the result of BiDi analysis could alternatively be considered an implicit or derived rich text span.++### Itemization (font coverage)++Itemization is the trickiest and least well specified part of the hierarchy. There is no standard for it, and no common implementation. Rather, each text layout engine deals with it in its own special way.++Essentially, the result of itemization is to choose a single concrete font for a run, from a *font collection.* Generally a font collection consists of a main font (selected by font name from system fonts, or loaded as a custom asset), backed by a *fallback stack,* which are usually system fonts, but thanks to [Noto] it is possible to bundle a fallback font stack with an application, if you don't mind spending a few hundred megabytes for the assets.++Why is it so tricky? A few reasons, which I'll touch on.++First, it's not so easy to determine whether a font can render a particular string of text. One reason is [Unicode normalization]. For example, the string "é" can be encoded as U+00E9 (in NFC encoding) or as U+0065 U+0301 (in NFD encoding). Due to the principle of [Unicode equivalence], these should be rendered identically, but a font may have coverage for only one or the other in its [Character to Glyph Index Mapping] (cmap) table. The shaping engine has all the Unicode logic to handle these cases.++Of course, realistic fonts with Latin coverage will have both of these particular sequences covered in the cmap table, but edge cases certainly do happen, both in extended Latin ranges, and other scripts such as Hangul, which has complex normalization rules (thanks in part to a Korean standard for normalization which is somewhat at odds with Unicode). It's worth noting that [DirectWrite gets Hangul normalization quite wrong].++I believe a similar situation exists with the Arabic presentation forms; see [Developing Arabic fonts] for more detail on that.++Because of these tricky normalization and presentation issues, the most robust way to determine whether a font can render a string is to try it. This is how LibreOffice has worked for a while, and in 2015 [Chromium followed](https://lists.freedesktop.org/archives/harfbuzz/2015-October/005168.html). See also [Eliminating Simple Text](https://www.chromium.org/teams/layout-team/eliminating-simple-text) for more background on the Chromium text layout changes.++*Another* whole class of complexity is emoji. A lot of emoji can be rendered with either [text or emoji presentation], and there are no hard and fast rules to pick one or the other. Generally the text presentation is in a symbol font, and the emoji presentation is in a separate color font. A particularly tough example is the smiling emoji, which began its encoding life as 0x01 in [Code page 437], the standard 8-bit character encoding of the original IBM PC, and is now U+263A in Unicode. However, the suggested default presentation is text, which won't do in a world which expects color. Apple on iOS unilaterally chose an emoji presentation, so many text stacks follow Apple's lead. (Incidentally, the most robust way to encode such emoji is to append a [variation selector] to pin down the presentation).

Last two characters should be swapped.

raphlinus

comment created time in 3 days

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram++### Paragraph segmentation++The coarsest, and also simplest, segmentation task is paragraph segmentation. Most of the time, paragraphs are simply separated by newline (U+000A) characters, though Unicode in its infinite wisdom specifies a number of code point sequences that function as paragraph separators in plain text:++* U+000A LINE FEED+* U+000B VERTICAL TAB+* U+000C FORM FEED+* U+000D CARRIAGE RETURN+* U+000D U+000A (CR + LF)+* U+0085 NEXT LINE+* U+2008 LINE SEPARATOR+* U+2009 PARAGRAPH SEPARATOR++In rich text, paragraphs are usually indicated through markup rather than special characters, for example `<p>` or `<div>` in HTML. But in this post, as in most text layout APIs, we'll treat rich text as plain text + attribute spans.++### Rich text style++A paragraph of rich text may contain *spans* that can affect formatting. In particular, choice of font, font weight, italic or no, and a number of other attributes can affect text layout. Thus, each paragraph is typically broken into a some number of *style runs,* so that within a run the style is consistent.++Note that some style changes don't *necessarily* affect text layout. A classic example is color. Firefox, rather famously, does *not* define segmentation boundaries here for color changes. If a color boundary cuts a ligature, it uses fancy graphics techiques to render parts of the ligature in different color. But this is a subtle refinement and I think not required for basic text rendering. For more details, see [Text Rendering Hates You].++### Bidirectional analysis++Completely separate from the style spans, a paragraph may in general contain both left-to-right and right-to-left text. The need for bidirectional (BiDi) text is certainly one of the things that makes text layout more complicated.++Fortunately, this part of the stack is defined by a standard (UAX #9), and there are a number of good implementations. The interested reader is referred to [Unicode Bidirectional Algorithm basics]. The key takeaway here is that BiDi analysis is done on the plain text of the entire paragraph, and the result is a sequence of *level runs,* where the level of each run defines whether it is LTR or RTL.

I expected “UAX #9” to be a link to http://www.unicode.org/reports/tr9/.

raphlinus

comment created time in 3 days

PullRequestReviewEvent

Pull request review commentraphlinus/raphlinus.github.io

Add draft of text layout post

+---+layout: post+title:  "Text layout is a loose hierarchy of segmentation"+date:   2020-10-25 15:00:42 -0700+categories: [text]+---+I love text layout, and have been working with it in one form or other for over 35 years. Yet, knowledge about it is quite arcane. I don't believe there is a single place where it's all properly written down. I have some explanation for that: while basic text layout is very important for UI, games, and other contexts, a lot of the "professional" needs around text layout are embedded in *much* more complicated systems such as Microsoft Word or a modern Web browser.++A complete account of text layout would be at least a small book. Since there's no way I can write that now, this blog post is a small step towards that - in particular, an attempt to describe the "big picture," using the conceptual framework of a "loose hierarchy." Essentially, a text layout engine breaks the input into finer and finer grains, then reassembles the results into a text layout object suitable for drawing, measurement, and hit testing.++The main hierarchy is concerned with laying out a single strip of text. Line breaking is also important, but has a separate, parallel hierarchy.++## The main text layout hierarchy++The hierarchy is:+- (a) paragraph segmentation+- (b) rich text style+- (c) BiDi+- (d) itemization (coverage by font)+- (e) script+- (f) shaping (cluster)++TODO: diagram++### Paragraph segmentation++The coarsest, and also simplest, segmentation task is paragraph segmentation. Most of the time, paragraphs are simply separated by newline (U+000A) characters, though Unicode in its infinite wisdom specifies a number of code point sequences that function as paragraph separators in plain text:++* U+000A LINE FEED+* U+000B VERTICAL TAB+* U+000C FORM FEED+* U+000D CARRIAGE RETURN+* U+000D U+000A (CR + LF)+* U+0085 NEXT LINE+* U+2008 LINE SEPARATOR+* U+2009 PARAGRAPH SEPARATOR++In rich text, paragraphs are usually indicated through markup rather than special characters, for example `<p>` or `<div>` in HTML. But in this post, as in most text layout APIs, we'll treat rich text as plain text + attribute spans.

Probably worth including <br> in the HTML examples here, because it’s an inline element, where <p> and <div> are both block elements. (Maybe instead of <div> if you want to keep it to two.)

raphlinus

comment created time in 3 days

PullRequestReviewEvent

Pull request review commentlinebender/skribo

Add some docs, especially top-level.

+//! A library to help convert from a unicode string and attributes to a list of glyphs and+//! positions.+//!+//! Converting unicode strings to glyphs is a complex process, requiring knowledge of+//!  - which fonts to use (and choosing an appropriate font given a size, script, typeface, weight,+//!    etc.),+//!  - a description of how to render a font (e.g. a truetype font file)+//!  - the space available for drawing and what to do if it's not enough,+//!  - and information on the user's locale.+//!+//! # Font terms+//!+//! The terminology of printing can be confusing. Partly this is because printing has been so+//! important for so long, and so has historical baggage, but it is also because unifying the+//! written methods of many different cultures requires a complicated system with many parts.+//! Unfortunately, a consequence of this is that most of the terms used in typesetting have+//! different meanings to different people. The unicode standard is useful in that it precisely+//! defines the terms it uses, but these meanings aren't necessarily the same as those that are in+//! common usage.  Below are some important terms in font rendering and the meanings some ascribe+//! to them:+//!+//! This is a work in progress and will be wrong in places (for now).+//!+//!  - *typography* - The study of drawing text to paper, screen, or some other medium. Drawing+//!    text is also known as *typesetting*, or *rendering*.+//!  - *symbol* - An image drawn or printed on a canvas, sheet of paper, or screen. Physically the+//!    same as a picture.+//!  - *glyph* - A symbol from a collection of symbols with some semantic meaning. The collection+//!    could be a script (like latin - `ABCDE....abcde...`, or it could be from the set of+//!    mathemtatical symbols (`+-×÷`). In the case of languages, another word more commonly used to

Also, because you’ve used the proper symbols × and ÷ I feel like quibbling over the use of - (U+002D HYPHEN-MINUS) where − (U+2212 MINUS SIGN) is available. I’m like that. 😁

derekdreery

comment created time in 4 days

PullRequestReviewEvent

issue openedepiforecasts/covid

Indian map is using old state boundaries that lack Telangana

In 2014, the new state of Telangana was carved out of the north west of Andhra Pradesh.

The map at the top of https://epiforecasts.io/covid/posts/national/india/ shows the old boundaries.

created time in 5 days

issue openedholmgr/cargo-sweep

Various flags lack long forms

  • -d should have --dry-run
  • -r should have --recursive
  • -v should have --verbose

All three of these long forms are well-established conventions. Amusingly, their short forms are actually not quite so universal. --dry-run is normally -n (for “no action”, I imagine; this is probably worth changing, though of course it’s a breaking change) or may even lack a short form, and --recursive and --verbose are occasionally capitalised where the lowercase was already used for something else (e.g. -v for --version).

I give this as a rule of thumb for CLI design: always start with a long form, and then consider whether to add a short form, erring on the side of not doing so.

created time in 7 days

issue openedholmgr/cargo-sweep

`cargo-sweep` and `cargo sweep` are wonkily different

cargo-sweep … and cargo sweep … should be equivalent, with the possible (but not mandatory) exception of usage information differing by that one character.

Instead, cargo sweep … is equivalent to cargo-sweep sweep …. This leads to inconsistency with well-established conventions, and the help page being confusing, with cargo sweep --help referring to cargo-sweep-sweep and talking about running the command as cargo-sweep sweep.

created time in 7 days

issue commentlinebender/runebender

Crash when clicking while context menu is open

Can’t reproduce on the druid multiwin example, only on Runebender.

Connum

comment created time in 8 days

issue commentlinebender/runebender

Crash when clicking while context menu is open

I haven’t managed to reproduce this with my laptop’s touchpad any more after that first time, but I now have a consistent reproduction for it, by doing the right click with long press on the touchscreen with my finger. Some sort of fine timing issue with reentrancy, I think.

Connum

comment created time in 8 days

issue commentlinebender/runebender

Crash when clicking while context menu is open

I got a crash on this a few hundred milliseconds after my first experimental right click, no left clicks involved.

     Running `target\release\runebender.exe`
DEBUG [druid::localization] available locales [], current en-US
DEBUG [druid::localization] resolved: [en-US]
DEBUG [druid_shell::platform::windows::window] dxgi factory pointer = 0x21c8560f700
DEBUG [druid_shell::platform::windows::window] 0x21c8560ff10: desc = Some("Intel(R) HD Graphics 520"), vram = 134217728
DEBUG [druid_shell::platform::windows::window] 0x21c85618a80: desc = Some("Microsoft Basic Render Driver"), vram = 0
DEBUG [druid_shell::platform::windows::window] adapter = 0x21c8560ff10
DEBUG [druid_shell::platform::windows::window] swap chain res = 0x0, pointer = 0x21c856a7e50
WARN  [druid_shell::hotkey] warning: HotKey HotKey { mods: Shift, key: Character("u") } includes shift, but text is lowe
rcase. Text is matched literally; this may cause problems.
DEBUG [druid_shell::platform::windows::window] dxgi factory pointer = 0x21c8dc0cea0
DEBUG [druid_shell::platform::windows::window] 0x21c8dc12530: desc = Some("Intel(R) HD Graphics 520"), vram = 134217728
DEBUG [druid_shell::platform::windows::window] 0x21c8dc129c0: desc = Some("Microsoft Basic Render Driver"), vram = 0
DEBUG [druid_shell::platform::windows::window] adapter = 0x21c8dc12530
DEBUG [druid_shell::platform::windows::window] swap chain res = 0x0, pointer = 0x21c884419c0
ERROR [druid_shell::platform::windows::window] dropped message 0x215, hwnd=0x2203e0, wparam=0x0, lparam=0x2203e0
ERROR [druid_shell::platform::windows::window] dropped message 0x2a3, hwnd=0x2203e0, wparam=0x0, lparam=0x0
thread 'main' panicked at 'already borrowed: BorrowMutError', /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\library\co
re\src\cell.rs:867:31
stack backtrace:
   0: std::backtrace_rs::backtrace::dbghelp::trace
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\..\..\backtrace\src\backtrace\dbghelp.r
s:98
   1: std::backtrace_rs::backtrace::trace_unsynchronized
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\sys_common\backtrace.rs:77
   3: std::sys_common::backtrace::_print::{{impl}}::fmt
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\sys_common\backtrace.rs:58
   4: core::fmt::write
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\core\src\fmt\mod.rs:1117
   5: std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\io\mod.rs:1510
   6: std::sys_common::backtrace::_print
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\sys_common\backtrace.rs:61
   7: std::sys_common::backtrace::print
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\sys_common\backtrace.rs:48
   8: std::panicking::default_hook::{{closure}}
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\panicking.rs:198
   9: std::panicking::default_hook
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\panicking.rs:217
  10: std::panicking::rust_panic_with_hook
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\panicking.rs:526
  11: std::panicking::begin_panic_handler
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\panicking.rs:437
  12: core::panicking::panic_fmt
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\core\src\panicking.rs:85
  13: core::option::expect_none_failed
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\core\src\option.rs:1274
  14: indexmap::map::core::raw::<impl indexmap::map::core::IndexMapCore<K,V>>::get_index_of
  15: <druid_shell::platform::windows::window::MyWndProc as druid_shell::platform::windows::window::WndProc>::window_pro
c
  16: druid_shell::platform::windows::window::win_proc_dispatch
  17: CallWindowProcW
  18: DispatchMessageW
  19: SendMessageTimeoutW
  20: KiUserCallbackDispatcher
  21: NtUserTrackPopupMenuEx
  22: TrackPopupMenu
  23: druid_shell::platform::windows::window::WindowHandle::show_context_menu
  24: druid_shell::window::WindowHandle::show_context_menu
  25: core::slice::rotate::ptr_rotate
  26: core::slice::rotate::ptr_rotate
  27: core::slice::rotate::ptr_rotate
  28: indexmap::map::core::raw::<impl indexmap::map::core::IndexMapCore<K,V>>::get_index_of
  29: <druid_shell::platform::windows::window::MyWndProc as druid_shell::platform::windows::window::WndProc>::window_pro
c
  30: druid_shell::platform::windows::window::win_proc_dispatch
  31: CallWindowProcW
  32: DispatchMessageW
  33: druid_shell::platform::windows::application::Application::run
  34: druid_shell::application::Application::run
  35: druid::app::AppLauncher<T>::launch
  36: runebender::widgets::fontinfo::font_info
  37: <T as core::any::Any>::type_id
  38: std::rt::lang_start_internal::{{closure}}
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\rt.rs:52
  39: std::panicking::try::do_call
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\panicking.rs:348
  40: std::panicking::try
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\panicking.rs:325
  41: std::panic::catch_unwind
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\panic.rs:394
  42: std::rt::lang_start_internal
             at /rustc/6e87bacd37539b7e7cd75152dffd225047fa983a\/library\std\src\rt.rs:51
  43: main
  44: invoke_main
             at d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  45: __scrt_common_main_seh
             at d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  46: BaseThreadInitThunk
  47: RtlUserThreadStart
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: process didn't exit successfully: `target\release\runebender.exe` (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVE
RRUN)

(Hmm, I’m running rustc 1.47.0-nightly (6e87bacd3 2020-07-31), I should probably update that. I don’t build stuff on Windows very often. Toolchain is nightly-x86_64-pc-windows-msvc, BTW.)

Connum

comment created time in 8 days

issue commentraphlinus/crochet

Hook in responsive localization early

Thanks for your thoughts; it’s nice interacting with others that care about these topics as well!

Localisation is something that we care about, and which will definitely be solved well inside Druid. In fact, Druid already uses Fluent, having some half-baked localisation support where it stores the Fluent resources for the current locale in Env, so that any lens or widget can access it, and has a type LocalizedString that makes strings translate properly with lensing, so that you can have Label::new(LocalizedString::new("key1").with_arg("emailCount", Self::email_count)), and it’ll update when the locale or the data change. (—I’m actually not certain if locale changing is hooked up at this time, but it’s designed soundly so that it can work. As I said, it’s half-baked; but the foundations that have been designed are sound.)

We’re also well aware of all of the other nuances of localisation, that it’s not just text labels, but other properties as well.

So that’s Druid. This is Crochet, an experiment in replacing Druid’s lensing approach.

One of the motivating reasons for Crochet is that lensing means that the widget must manage reactivity itself—so Label uses a lens for its text, but if you want colour to be reactive, the Label widget needs to have deliberately used a lens there, and if it hasn’t, you’re out of luck. But under Crochet, all attributes become reactive at very little developer or runtime cost. This will make Crochet even better for localisation: now you can do much more involved UI changes by locale without the widgets having had to be carefully designed for it.

Back to what Crochet is at present. It’s an experiment in one particular area: replacing lensing. Consequently, we’ve stayed away from adding any localisation yet, because we know it’ll work within the model (more easily, in fact), and implementing it at this time would be just a distraction from testing the viability of the overarching concept.

Notwithstanding that, your post has made me think a bit more about how Crochet’s concept of attributes (not yet implemented, but discussed on our Zulip chat—join us if you like!) might fit in with Fluent attributes. There’s scope for some some really nice ergonomic synergies <sub><sup>[there, I said it]</sup></sub> there. I think it should end up at least as nice as the concepts you’ve listed there, which it definitely couldn’t have done in the lens-based approach.

Summary:

  1. We agree with you about the importance of localisation.
  2. Druid has had localisation designed into it, with half-baked implementation.
  3. We’re confident that localisation will fit into Crochet very nicely, better than in Druid’s lens design, and our confidence is backed up by plenty of practical experience in software localisation.
  4. But I don’t think we feel the time is right to implement it just yet—we’re prototyping other things and proving other concepts for now.
  5. Come join us on Zulip if you’re interested!
zbraniecki

comment created time in a month

push eventraphlinus/crochet

Chris Morgan

commit sha 32f7b8d63f3e924a4693422c5dfd8b31047e5d0d

Fix reorderable_list example The refactoring in ecf53b406a6c8f9554b73a15a162214326543a69 accidentally broke this.

view details

push time in a month

Pull request review commentrust-lang/rust

Remove intra-doc link disambiguation prefix aliases

 impl Disambiguator {                 "enum" => Kind(DefKind::Enum),                 "trait" => Kind(DefKind::Trait),                 "union" => Kind(DefKind::Union),-                "module" | "mod" => Kind(DefKind::Mod),+                "mod" => Kind(DefKind::Mod),                 "const" | "constant" => Kind(DefKind::Const),                 "static" => Kind(DefKind::Static),-                "function" | "fn" | "method" => Kind(DefKind::Fn),+                "fn" | "method" => Kind(DefKind::Fn),                 "derive" => Kind(DefKind::Macro(MacroKind::Derive)),                 "type" => NS(Namespace::TypeNS),                 "value" => NS(Namespace::ValueNS),                 "macro" => NS(Namespace::MacroNS),-                "prim" | "primitive" => Primitive,+                "primitive" => Primitive,

I meant the disambiguation prefixes, which I think it’s fair to say are niche; I had forgotten that intra-doc links as a whole were only now becoming stable!

chris-morgan

comment created time in a month

PullRequestReviewEvent

pull request commentrust-lang/rust

Remove intra-doc link disambiguation prefix aliases

RFC 2988 is relevant because I like having the prefixes match the filenames (especially when it’s hijacking link hrefs), which is also why I haven’t pulled constant out already. It’s not a big deal either way, I’d be content to remove the two further aliases.

But really, I just like consistency. You say rustdoc URLs aren’t user-facing, but I strongly disagree—and in fact I’d say that a large part of the purpose of RFC 2988 is that they are user-facing to at least some degree, even if you think that the precise spelling of the URLs shouldn’t be. But I manually type rustdoc URLs moderately regularly (more regularly than I would need to if I arranged better tooling for myself!).

chris-morgan

comment created time in a month

Pull request review commentrust-lang/rust

Remove intra-doc link disambiguation prefix aliases

 impl Disambiguator {                 "enum" => Kind(DefKind::Enum),                 "trait" => Kind(DefKind::Trait),                 "union" => Kind(DefKind::Union),-                "module" | "mod" => Kind(DefKind::Mod),+                "mod" => Kind(DefKind::Mod),                 "const" | "constant" => Kind(DefKind::Const),                 "static" => Kind(DefKind::Static),-                "function" | "fn" | "method" => Kind(DefKind::Fn),+                "fn" | "method" => Kind(DefKind::Fn),                 "derive" => Kind(DefKind::Macro(MacroKind::Derive)),                 "type" => NS(Namespace::TypeNS),                 "value" => NS(Namespace::ValueNS),                 "macro" => NS(Namespace::MacroNS),-                "prim" | "primitive" => Primitive,+                "primitive" => Primitive,

I can do this, but I’m a little surprised you’d suggest it: this is niche, previously-unstable functionality, and it wouldn’t surprise me if literally no one has used these aliases.

chris-morgan

comment created time in a month

PullRequestReviewEvent

PR opened rust-lang/rust

Remove intra-doc link disambiguation prefix aliases

(That title is gnarly!)

Follow-up to https://github.com/rust-lang/rust/pull/75815#issuecomment-694476719.

Read the commit messages for details and remaining questions about the method and constant aliases. https://github.com/rust-lang/rfcs/pull/2988 is relevant to this consideration.

/cc @jyn514

+62 -62

0 comment

14 changed files

pr created time in a month

push eventchris-morgan/rust

Chris Morgan

commit sha 901c9440a225864ae1f65f6554b1fec7096dca7e

Remove `prim@` rustdoc intra-doc link prefix alias “Prim” is rustc jargon; everywhere else uses the normal English word “primitive”, including the file names like primitive.u8.html, which it’s nice to match. This only touches the `prim@` disambiguation prefix. rustc still refers to “prim” internally in some places, but being an outsider to rustc I’m unqualified to judge whether any change is called for there. (The uses are: PrimIntCast/prim-int-cast; PrimTy, short for PrimitiveType; and an identifier prim, most commonly of type PrimitiveType.)

view details

Chris Morgan

commit sha 0f8c9339002f5ce3d5dec619dedc1278b183cd15

Remove some intra-doc link disambiguation aliases Following on from what I think should be the fairly uncontroversial killing of the prim@ disambiguation prefix in favour of primitive@, we have a few more aliases that it may be worth evicting from rustdoc. - `function` is removed in favour of `fn`, which matches the keyword and the fn.*.html filenames. - `module` is removed in favour of `mod`, which matches the keyword. Modules become folders, so there’s no filename argument. I have kept two aliases at this time because I think more discussion is called for: - `method` is retained as an alias of `fn`; it matches the method.* fragment that methods are given. Well, unless they’re tymethod.*. Should tymethod therefore be allowed as an alias? As I say, discussion is called for. Also, subjectively people may prefer to call a thing a method than a function, though that argument could be applied to retaining the “function” and “module” spellings. - `constant` is retained as an alias of `const`; their filenames are currently of the form constant.*.html.

view details

push time in a month

push eventchris-morgan/rust

Laurence Tratt

commit sha 73ada2d40429488aaaacf37b608bababc137b910

Explicitly document the size guarantees that Option makes. Triggered by a discussion on wg-unsafe-code-guidelines about which layouts of `Option<T>` one can guarantee are optimised to a single pointer.

view details

Laurence Tratt

commit sha f5118a525fcf9db4102d903650331039158eff11

Clarify and add guarantee about `transmute`.

view details

Laurence Tratt

commit sha 83f47aa11bd664ed8a15ef9833063833b7b3e71c

Be clear about the reverse `transmute` guarantees.

view details

Laurence Tratt

commit sha f3d7196caec3f54e572c7389b1cef9fd9e62c1ed

Be clearer about Some/None transmute.

view details

Laurence Tratt

commit sha 8cb8955d570c76631840bfc98825ca49c0dd8eea

Change notation. Co-authored-by: Ralf Jung <post@ralfj.de>

view details

Laurence Tratt

commit sha 55802e3bf3bf6d1db5c76aea581a7912bd752890

Add Rust function pointers. Co-authored-by: Ralf Jung <post@ralfj.de>

view details

Laurence Tratt

commit sha 68209c3fe4e0f5c3758f18e98efc175af31c2e51

Rename the types for clarity.

view details

Laurence Tratt

commit sha 9bac5774d7b452b2227c9fb77a4c6de3f432ee55

Grammar tweak.

view details

Vali Schneider

commit sha 459969f88ff95c94b7b34043a7f0e13de91de4f8

added restriction lint that prohibits the usage of unimplemented, unreachable or panic in a function of type result or option

view details

Vali Schneider

commit sha ceab1a9167655eba9f9556f8766f8702e49dfef3

removed unnecessary comment

view details

Vali Schneider

commit sha 8462cce96081b87eba7a5bc89130a1a09fe1f6d0

edited documentation

view details

Vali Schneider

commit sha b2d8ca9a766703469178ea37d4d46067bb6fa926

ran cargo dev update lints

view details

Vali Schneider

commit sha b006522393a3c3c2656e1ccdfbb0076ff1bd7e99

added lint for todo and removed option

view details

Vali Schneider

commit sha a424a2c1676a29c147252873037e8943d54941d3

changed check_impl_item to check_fn and added a few more test cases

view details

Vali Schneider

commit sha 73a3288282e733bfc5893e9920d29f1de5a21591

uncommented fn

view details

Erik Desjardins

commit sha d3b9ece4c0028ff7e36e34df2d2b26366f9c0648

add tests related to tuple scalar layout with ZST in last field

view details

Erik Desjardins

commit sha e5d85f917b8965a5e62513c17cbb887366b152bc

allow reordering of the last field of a MaybeUnsized struct if it's a ZST

view details

Erik Desjardins

commit sha e9bc3ddb073f2261ac46832d985efe8db863ed6a

test that we do not change the offset of ZST tuple fields when unsizing

view details

Erik Desjardins

commit sha 68217c9e0f1269d29b4c1c72d08fb1b95bf441cd

ignore zst offsets instead

view details

Erik Desjardins

commit sha 6fc1d8bc13124bb134d7ab54e56237821a55912e

add codegen test

view details

push time in a month

pull request commentrust-lang/rust

Report an ambiguity if both modules and primitives are in scope for intra-doc links

OK, I didn’t read carefully enough to notice that; thanks for pointing it out.

But my point still mostly stands, because the I-think-unprecedented “prim” is still both supported and what the note suggests. If I submit another PR that drops prim in favour of primitive, would it be accepted?

jyn514

comment created time in a month

pull request commentrust-lang/rust

Report an ambiguity if both modules and primitives are in scope for intra-doc links

I very much dislike this use of “prim”, which feels unprecedented as a keyword (I dunno, maybe the compiler uses it, maybe not, but as a user of the language I haven’t seen anyone do it this way). Could we change it to the full word “primitive”? That would match Rust’s general style of identifiers and the filenames rustdoc emits for the primitives (primitive.u8.html, &c.).

jyn514

comment created time in a month

push eventchris-morgan/druid

ForLoveOfCats

commit sha 69d46e8b03cc5b7fca73e48e1f41758c005dc339

Add bar radius, edge, opacity, and fade delay to theme

view details

ForLoveOfCats

commit sha c9f845b9916af27662f9d7f2ef95bb7a4f400506

Merge pull request #287 from ForLoveOfCats/ReadmeCorrection Correct README slightly

view details

ForLoveOfCats

commit sha 1b483d3ffbd475248bd307529ffb9a0fb68dc42d

Merge pull request #285 from ForLoveOfCats/ScrollbarAddToTheme Add scrollbar constants and magic numbers to theme

view details

Hilmar Gústafsson

commit sha 552e9a590a9efb0c19f9734f6f0f25ce4a793205

Add optional window event handlers (#286) AppDelegate now has two new handlers: window_removed and window_added

view details

Hilmar Gústafsson

commit sha a454327bee7d2692bae806d8b5bc4f3315b1a495

Fix call to wrong method (#290)

view details

Colin Rofls

commit sha 56d30192bdacdbc6b2a6ffa66915780e759e2279

Update deps & prepare for a release

view details

Paul Miller

commit sha 85e9933e4627769a661dc0bab9d46d524943637c

add StyleChild widget

view details

Paul Miller

commit sha dc205ea4612c1625ccd22490342da889cf3b9e4b

apply new_env to all trait methods

view details

Paul Miller

commit sha bd860cac00e71484c72518fb870927cee2b7c3af

be generic over widget type

view details

Paul Miller

commit sha 1f3a7a4fa35cdf14051eafe6083e64c8677e5bc2

simplify type signature and fix docs honor clippy typo

view details

Paul Miller

commit sha ae3509cac6cddd6d90fa37cda87a328d15d3d79e

Merge pull request #279 from futurepaul/style-children Add EnvScope widget to mutate env for theming children

view details

Colin Rofls

commit sha 4a147a27517259f87526b65a2a1d342aee91de86

Add ability to bring a window to the front and give it focus This is a quick patch for some functionality I need in runebender. I've included the mac impl here, but I have skipped the windows and gtk impls for the sake of time; I'll open an issue for that if this gets merged.

view details

Anna Scholtz

commit sha eb2e1a959f5d98c86ba0318023f18bd04369c04a

Add de-DE locale

view details

Raph Levien

commit sha eea8db0f5d83588aaf4f401ff1f26f10c052c270

Merge pull request #275 from xi-editor/fix_text_crash Fix crash when typing on Windows

view details

Colin Rofls

commit sha e23a2a4d6fd06c9a702c225c4c05791f0aa25a36

Make WindowId available in WindowDesc, tweak AppDelegate API This was motivated by actually trying to use the new AppDelegate methods as I'd intended in Runebender. I would like some way to track a window from the point where it is requested to the point where it actually connects. The simplest way to do this was to make `WindowId` be decided when `WindowDesc` is created; you can then stash the `WindowId` at that point, and check it against whatever id comes back when a window connects. In addition, this tweaks the AppDelegate::event method somewhat; it removes the `WinCtx` param. It isn't clear that access to the `WinCtx` is actually necessary here; if it is we can revisit at some point in the future. This also refactors the `delegate_event` fn in win_handler.rs to use the new `with_delegate` helper method. As this breaks existing AppDelegate use I'm going to go ahead and make this v0.4.0.

view details

Roman Zaynetdinov

commit sha b5d4e491a82cfed85f621abf552294cc9d55177b

Make list example dynamic

view details

Roman Zaynetdinov

commit sha bb19ee8acad69bdba5eb63a918406c6b5db13008

Remove closure generic parameter

view details

Christoph Herzog

commit sha 0084bc0d31c2496d6e310601e4ab0862408bef72

Allow #[derive(Data)] on empty structs Detect structs with no fields and generate no-op compare function accordingly.

view details

Christoph Herzog

commit sha 1310dab10895b86f0c2e8ca362da25eea36caefc

Add tests for #[derive(Data)]

view details

Colin Rofls

commit sha f290c14d0e47f2ca20762ce9d5f51ac2c766d69e

Fix everything being broken if no AppDelegate is set This was a logic error where we would always return `None` when attempting to forward an event to the AppDelegate, if there was no delegate set. We should instead always return `Some(event)`.

view details

push time in 2 months

push eventrust-lang/rust.vim

Flying-Toast

commit sha 2a6736852cbe64e2883adc70a427cb47cb3305bc

Highlight SAFETY comments (#418)

view details

push time in 2 months

PR merged rust-lang/rust.vim

Highlight SAFETY comments
+1 -1

2 comments

1 changed file

Flying-Toast

pr closed time in 2 months

pull request commentrust-lang/rust.vim

Highlight SAFETY comments

I haven’t come across // SAFETY: … comments before, but I see there are about 615 occurrences of this pattern in the standard library (rg SAFETY | wc -l in ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library), and it makes sense as a style for Rust, so it seems reasonable to include. Thanks!

Flying-Toast

comment created time in 2 months

issue openeduBlockOrigin/uAssets

Can’t open attachments of type PDF from within Firefox’s PDF reader

URL(s) where the issue occurs

https://www.jeffersonscher.com/temp/Links-attachments-PDFs.pdf https://img.fireden.net/tg/image/1446/29/1446297117123.pdf

Describe the issue

When you open a PDF file that contains an attachment that is another PDF file, and you click to open that attachment, a new tab opens for an instant and closes again, and nothing more happens. When uBlock Origin is disabled, the new tab is not immediately closed, and loads the attached PDF file as expected.

(When reproducing, if you don’t see the attachments, open pdf.js’s sidebar with the icon in the top left corner, and switch to the paperclip tab in the sidebar that appears. Attachments will then be listed below.)

Versions

  • Browser/version: Firefox Nightly 82.0a1, 2020-08-23
  • uBlock Origin version: 1.29.2

Settings

No changes: visible on an empty Firefox profile with a fresh uBlock Origin installation.

Notes

This is the code that runs when you click on a PDF PDF attachment link in pdf.js. (That’s from upstream pdf.js; in Firefox’s own copy of pdf.js, it’s at resource://pdf.js/web/viewer.js, function _bindPdfLink.) It extracts the attachment from the original PDF file, creates a blob URL for it, and calls window.open(…).

Then this filter from EasyList kills the popup:

|blob:$popup

This can’t be fixed for pdf.js deployments on the web at large, but I think it should be fixable for Firefox’s built-in PDF viewer, by adding an exception for popup URLs starting with blob:resource://pdf.js/. (The URL it opens is like blob:resource://pdf.js/1ee56593-160d-4c43-ad3e-05ce6d7eb043#filename=Amarin_COVID-19%20menu%20(01191490).PDF.)

But I will note that, per the linked code, pdf.js is designed to fall back to triggering a download of the file; but it requires that window.open(…) throw an exception for that to happen. uBlock Origin is instead letting the window.open(…) call succeed, and then killing the popup. Not sure if anything can be done about that. (At a wild, uninformed guess, it might require more WebExtension surface area so you can hook into open() calls.)

created time in 2 months

CommitCommentEvent

PR opened thoiberg/advent-of-code-2019

Day 4 optimisations

As discussed in the meetup. See the commit messages.

+19 -21

0 comment

1 changed file

pr created time in 3 months

push eventchris-morgan/thoiberg-advent-of-code-2019

Chris Morgan

commit sha c79ebb76a92509f60380aef83e8a7c7d1043a698

Convert each number to a string once only This reduces allocations enormously.

view details

Chris Morgan

commit sha 735c3e5bda8974c3d0078744b17f9b51d537b85b

Change O(n) string access to O(1) .chars().nth(i) is O(n) because of UTF-8. Since we know that the string is ASCII, we can use bytewise access instead, which allows O(1) access. .bytes().nth(i) can theoretically be optimised from O(n) to O(1), but I’m not sure if that happens in general, so I’m going the “safe” route with .as_bytes().get(i).cloned() and the likes. (.get() produces Option<&u8>, .cloned() turns that into Option<u8>.) A fairly messy way of writing it still, which could be improved, but it’s enough.

view details

push time in 3 months

issue openedDrorHarari/keypirinha-cvt

Celsius is misspelled

It’s Celcius but should be Celsius. I suppose fixing this will be a breaking change.

created time in 3 months

issue openedDrorHarari/keypirinha-cvt

Non-alphanumeric characters don’t work as unit aliases

I want this:

[unit/distance/Inches]
aliases = ″

[unit/distance/Feet]
aliases = ′

[unit/temperature/Celcius]
aliases = °,°C

[unit/temperature/Fahrenheit]
aliases = °F

(With the help of my Compose key, thanks to WinCompose, I genuinely type all of these things quite casually. Well, as casually as I ever type anything in feet or inches. And admittedly I’d use half of those symbols more comfortably for angular measurements in degrees, minutes and seconds. But yeah, dropping the degrees symbol and writing “-40F” instead of “−40°F” is genuinely a nuisance for me! For that matter, I’d rather like (MINUS SIGN) to work rather than just - (HYPHEN-MINUS). I’m weird like this.)

But this doesn’t work; from a quick glance, it looks like this’ll be due to the input parser regular expression only being interested in alphanumeric characters.

created time in 3 months

issue commentShareX/ShareX

Cursor not included in entire screen and active window captures

Let it be known that I hate the @stale bot and think it should be utterly destroyed from existence. All it has ever done to me is closed genuine issues that simply haven’t been fixed. Really waters down the meaning of “closed” on a ticket, which should have meant “it was fixed, or a duplicate, or we decided to definitely not do this”, but now half the time it’s just… there probably wasn’t any will to fix this in the short term. And so then you’ll get duplicates created, because people will be searching open issues.

chris-morgan

comment created time in 3 months

more