Large lettering

A how-to guide: set a word in an outsized mosaic face, on a stripe of colour, for a masthead.

from sextile import Custom, OnOneFrame, Page, PageLayout, PageRequest, PageRouter, Sextile
from sextile.viewdata import lettering
from sextile.viewdata.canvas import Canvas
from sextile.viewdata.composition import Align, Composition
from sextile.viewdata.controls import Colour
from sextile.viewdata.font import load_font
from sextile.viewdata.frame import COLUMNS
from sextile.viewdata.lettering import Spacing

router = PageRouter()

_FACE = load_font("boldbash")


@router.page("7", name="masthead", title="Weather")
async def masthead(request: PageRequest) -> Page:
    def draw(canvas: Canvas, row: int) -> None:
        layout = Composition()
        stripe = layout.panel(
            row, Align.START, colour=Colour.BLUE, width=COLUMNS - 1, rows=lettering.rows_needed(_FACE)
        )
        lettering.place(
            layout, Align.CENTRE, "WEATHER", _FACE, Colour.YELLOW, within=stripe, spacing=Spacing.KERNED
        )
        layout.draw(canvas)

    return PageLayout(
        parts=[OnOneFrame(Custom(rows=lettering.rows_needed(_FACE), draw=draw))],
    ).build(request)


app = Sextile(name="Weather", pages=[*router])
 WEATHER                              7a
  
          🬹🬦🬓🬹🬹🬓🬵🬹🬏🬹🬹🬹🬦🬓🬹🬦🬹🬹🬦🬹🬱         
          █🬻▌█🬋 █🬷▌ █ ▐🬛█▐🬛🬃▐🬺🬝         
          🬆🬁🬄🬎🬎🬄🬎🬉🬄 🬎 🬉🬄🬎🬉🬎🬎🬉🬄🬎         
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
  
 0 index                                

load_font(name) loads one of the faces font_names() lists — the catalogue sets a specimen of each, and boldbash has the heavy strokes a title wants. lettering.place sets the text into a Composition, centred within the panel it is given, and layout.draw(canvas) lays the letters down as mosaic cells. Set the word in front of a background instead with Letter on a background, or in a ruled box with Boxed banners.

Choose the spacing

Spacing.FIXED advances every glyph by the same width, PROPORTIONAL by each glyph’s own width, and KERNED lets a pair lean together. AVATAR is the kerning case: its A/V and A/T pairs overlap, so kerned takes 16 cells against 18 proportional and 23 fixed.

from sextile import Custom, OnOneFrame, Page, PageLayout, PageRequest, PageRouter, Sextile
from sextile.viewdata import lettering
from sextile.viewdata.canvas import Canvas
from sextile.viewdata.composition import Composition, Style
from sextile.viewdata.controls import Colour
from sextile.viewdata.font import load_font
from sextile.viewdata.lettering import Spacing, rows_needed

_FACE = load_font("grotesque")
_SPACINGS = [("Fixed", Spacing.FIXED), ("Proportional", Spacing.PROPORTIONAL), ("Kerned", Spacing.KERNED)]

router = PageRouter()


@router.page("1", name="spacing", title="Spacing")
async def spacing(request: PageRequest) -> Page:
    tall = rows_needed(_FACE)

    def draw(canvas: Canvas, row: int) -> None:
        for index, (label, kind) in enumerate(_SPACINGS):
            top = row + index * (tall + 1)
            layout = Composition()
            layout.text(top, 0, label, style=Style(colour=Colour.WHITE))
            lettering.place(layout, top, "AVATAR", _FACE, Colour.YELLOW, column=13, spacing=kind)
            layout.draw(canvas)

    return PageLayout(parts=[OnOneFrame(Custom(rows=3 * (tall + 1), draw=draw))]).build(request)


app = Sextile(name="Fonts", pages=[*router])
 SPACING                              1a
  
Fixed         🬖🬏 🬦 🬦  🬖🬏 🬇🬩🬋  🬖🬏 🬦🬋🬢    
             🬦🬮🬶 🬁🬓🬔 🬦🬮🬶  ▐  🬦🬮🬶 ▐🬩🬅    
             🬉 🬉  🬉  🬉 🬉  🬉  🬉 🬉 🬉 🬈    
                                        
                                        
Proportional 🬞🬢 🬓 🬓🬞🬢 🬋🬚🬃🬞🬢 🬚🬋🬏         
             🬳🬯🬓🬧🬦🬀🬳🬯🬓 ▌ 🬳🬯🬓🬛🬚🬀         
             🬄 🬄 🬄 🬄 🬄 🬄 🬄 🬄🬄🬁🬃         
                                        
                                        
Kerned       🬞🬢🬦 🬦🬞🬢🬇🬩🬋🬞🬢 🬚🬋🬏           
             🬳🬯🬔🬓🬔🬳🬯🬓▐ 🬳🬯🬓🬛🬚🬀           
             🬄 🬄🬉 🬄 🬄🬉 🬄 🬄🬄🬁🬃           
                                        
                                        
                                        
                                        
                                        
                                        
                                        
  
 0 index                                

cells_needed(word, face, spacing=) measures a word without drawing it, so a page can pick a spacing that fits the row. Why proportional at all, and why the face carries the advance rather than the renderer, is in the mosaic-fonts explanation.

A face of your own

read_font and write_font read and write a human-readable, dependency-free text format that carries a per-glyph advance in blocks — the measurement the importable bitmap formats leave out. Name glyphs by code point and draw each in # and .; see sextile.viewdata.font for the format and Fonts for the faces already shipped.

Use a font from the hoard

Load a YAFF font — the format of the hoard of bitfonts — with read_yaff, and set it exactly like a shipped face:

from pathlib import Path

from sextile.viewdata.yaff import read_yaff

_FACE = read_yaff(Path("saa5050-uk.yaff").read_text("utf-8"))
 REPORT                               8a
  
          🬦🬚🬩🬏🬹🬋🬋🬦🬚🬩🬏🬵🬋🬱🬦🬚🬩🬖🬩🬚🬃         
          ▐🬛🬻🬀█🬋🬃▐🬛🬍🬀█ █▐🬛🬻🬀▐▌          
          🬉🬄🬉🬄🬎🬋🬋🬉🬄  🬊🬋🬆🬉🬄🬉🬄🬉🬄          
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
  
 0 index                                

The frame is drawn with the shipped acorn face; a hoard face loaded with read_yaff sets the same way. read_yaff keeps the glyphs a Font needs and copies the file’s licence into font.terms — the hoard’s licences are per font, so check the one you load. teletext/saa5050-uk.yaff in the hoard is the SAA5050 viewdata display font itself; the format and what is read are in Fonts and sextile.viewdata.yaff.