Date: Wed, 18 Jan 2023 23:06:40 -0800
From: Bret Victor
Subject: Re: fonts and styled text
Haha, font deletion is pretty low priority.  I just wanted to document the behavior.  :)

A few more updates today, as Realtalk continues its transformation into a typographic powerhouse:


Meta key to type extended characters

On the Mac, you can type many extended characters by holding Option (or Option+Shift) while pressing a key.  Those are now all available in Realtalk by holding the Meta key (labelled "Win" on our keyboards).  e.g. Meta-6 types "§".  The ones I use a lot are Meta-hyphen and Meta-shift-hyphen for en-dash and em-dash, and Meta-[ and Meta-shift-[ for curly quotes.

Meta-e, u, i, n, ` are "dead keys" -- instead of typing a character immediately, they apply a diacritic to the next character typed.  To type an accented ó, first press Meta-e, and then o.

To type any Unicode character at all, press Meta-u (for "unicode"), and then type the 4 or 5 digit unicode hex code.  For example, Meta-u 2 6 6 5 types a heart, U+2665. ♥  (Making use of our fallback font!)

The implementation of this whole scheme is 12 lines of code in "Keyboards", plus 12 lines of character tables.


Typographic settings for drawing multiline text

line height:  vertical distance between lines of text, in inches (defaults to font size)
line height multiple:  line height, but as a multiple of font size
paragraph spacing:  extra vertical space after a newline


Soft newlines

To draw text wrapped to a specified width, we do this:

    Wish (you) draws "text" (text) with width (4).

Normally when drawing text, a newline in the text generates a line break in the drawing.  But because our editor isn't a proper word processor, we often write text like this:

    This is some text where I

    have added manually newlines

    because my text editor

    doesn't wrap lines.

    

    Paragraphs are separated by

    a blank line.


If this text is drawn with soft newlines (true)

    Wish (you) draws "text" (text) with width (4) soft newlines (true).

it will rewrap to the specified width, with a single line break (plus paragraph spacing) between each paragraph.


Control-W to wrap selected text

To help with writing text that way, you can select some text in the editor and press control-W, and it will be wrapped to the page width, indented by the indentation of the first line.

This works with comments too, so

    -- Pretend this is a very long comment in your program.


will get wrapped to

    -- Pretend this is a 

    -- very long comment

    -- in your program.



Text widths and positioning are finally correct oh my god

When drawing text, NanoVG attempted to snap glyphs to pixel boundaries for "sharp rendering".  This meant that glyph positions and text width varied with font size.  This has always been incredibly confusing, and now that I'm measuring and rendering text that needs to exactly match the printed text, it was time to get in there and fix it.

Now the width of a text in font size (1) is exactly twice the width in font size (0.5), and prints out exactly the same width as it renders, and exactly matches text printed from the Mac.  Our long non-deterministic-text-positioning nightmare is over.



On Jan 18, 2023, at 2:51 PM, Luke Iannini <****************> wrote:

Radical!! I will check into adding a font deletion API to NanoVG

I was gonna bundle this up in a January Update but since it might be immediately useful, Realtalk can now rasterize PDFs via Ghostscript:
<IMG_0205.jpeg>
I haven't merged up yet since I'm still in the middle of things but you should be able to just grab "Browsing PDFs" and "PDF to image" from Print Kit safely!

Relevant wishes/whens are:
Wish (you) shows PDF (pdf_url) with page number (page_number). -- The basic wish. You can also pass DPI to force a DPI rather than the coarse auto-estimate
Wish (you) shows PDF (pdf_url) thumbnails with rows (3) columns (4). -- thumbnails are boxes, so you can use View PDF page (42572) to point at them and view full size
Wish (you) browses PDF (pdf_url). -- Point a keyboard at this to page through the PDF with the arrow keys. Also takes DPI. Hold alt/ctrl/shift to jump forward by *10 (stacks with more modifiers)
When PDF /pdf/ has page count /page_count/: -- Get the number of pages in a PDF

I was hoping to get the per-page text out too but that Ghostscript command seems buggy, hopefully soon!

(and I guess we'll probably parse and render PDF ourselves someday but this will still be handy to check that Ghostscript renders our PDFs accurately!)

On Jan 17, 2023, at 11:47 PM, Bret Victor <****************> wrote:

We can't unload fonts, so once you wish for a font, you're stuck with it.

To clarify -- once you wish for a font, it's available until the area restarts.  We're not installing anything persistent.  A page/kit should actively wish for the fonts it wants to use, in case we are someday able to unload fonts.


On Jan 17, 2023, at 7:52 PM, Bret Victor <****************> wrote:

I had been printing captions for the progress report using Pages on the Mac, mostly because I wanted to use Avenir and Realtalk couldn't embed fonts into PDFs.  Yesterday I was roadblocked by a pair of back-to-back bugs in Pages, and in my unquenchable rage I ditched the laptop and updated Realtalk's text and font capabilities.  Further reports forthcoming if I actually succeed in turning Realtalk into a page layout wonderland.


Adding fonts

Here's how to make a non-default font available:

    Wish font face "Avenir" uses font file (Ref "8e8bdd3a55a1b0ea022e25a54a7795e8.ttf").

Thereafter, you can measure and draw with it just like any font.

    Wish (you) is labelled "The worst." with font face "Avenir".

We can't unload fonts, so once you wish for a font, you're stuck with it.

If you want to see all the fonts that are currently available:

    When font face /face/ is available:

The default fonts are in "Base fonts":

    Wish font face "Helvetica"    uses font file (Ref "2ce7bccbe71ea34eeb77b96f72ac56a9.ttf") with required (true).
    Wish font face "Helvetica I"  uses font file (Ref "1abbd4cf2d371faa8e43d4e989a7c492.ttf") with required (true).
    (etc)

("required" ensures that the font is downloaded before we create the rendering context, so there are no missing-font surprises.)


Printing with fonts

Draw on a proof with your new font, then print it or convert to PDF.  It just works.

Nonstandard fonts are embedded in the PDF.  They are not subsetted; the whole font is just dropped in.  They're usually pretty small (e.g. Avenir is 53K).


Rescaled fonts

If you ever noticed that our "Serif" font ("Goudy Bookletter 1911"?!) seemed too small, it's because NanoVG was scaling fonts wrong.  That's been fixed, so Serif (and others) renders at the right height and is correctly printable.  The fix ended up being simple, but only after an overlong descent into the font metrics abyss, where only madness reigns.

(Details for future reference:  unitsPerEm from the font's "head" table is the proper way to scale from glyph units to pixels/inches, but NanoVG was scaling by (ascender - descender) from the "hhea" table.  (ascender - descender) == unitsPerEm for many fonts, but not all of them.  Probably nobody ever noticed because we're the first one to try to get NanoVG's output to match PDF.)


Styled ranges

I need my italics.  We can now draw text with styled ranges.

    Wish (you) draws "text" "The worst." with
        ranges { { start_pos=5, end_pos=9, font_face="Helvetica I", color="yellow" } }.

A range can specify font_face, font_size, and/or color, and they will override the text's base settings.

<IMG_695701212.jpeg>


Styled markup

Sometimes you want to use markup instead of ranges.

    Wish (you) draws "text" "The <i>worst</i>." with
        markup { i = { font_face="Helvetica I", color="yellow" } }.

You can use whatever HTML-like tags you want, and provide a dictionary from tag name to style.  Tags cannot overlap.  There are default styles for some common tags ("i", "b", "em", "strong").

    local text = "I'd <i>like</i> to <big>tell</big> you <mono>about</mono> my <green>problems</green>."
    local styles = {
        big  ={ font_face="Serif", font_size=0.7, color="pink" },
        mono ={ font_face="Mono B", font_size=0.5, color="orange" },
        green={ color="green", font_face="Helvetica B" }
    }
    Wish (you) draws "text" (text) with width (2.5) font size (0.3) background "blue" markup (styles).    

<IMG_695705322.jpeg>

If you want to convert from markup to ranges for some reason, you can use the function 

    local text, ranges = text_and_ranges_for_markup(html, styles)