pcb-rnd knowledge pool

 

Font rendering: font v2

font2 by Tibor 'Igor2' Palinkas on 2023-10-02

Tags: insight, font

node source

 

 

Abstract: librnd from 4.1.0 features an upgradeed font engine, called "font2". This pool node describes all the upgrades compared to the original font engine.

 

1. Brief history

gEDA/pcb had a custom vector stroke font ever since it offered a text object. This home grown font engine was very unusual both in implementation and in terminology. Text object origin is top left and glyphs contain only line objects. Since gEDA/pcb supported only one font per board, it was not widespread to have any other font than the default font. Only 8-bit ASCII is supported (but effectively only 7-bit ASCII is provided in the default font).

After the fork, pcb-rnd upgraded the text object and font support. The new pcb-rnd font engine was built on the original gEDA/pcb engine because it had to be backward compatible and render the original font the same to the nanometer. This upgrade included an option for multiple font per board plus arcs and polygons to glyph.

After librnd got split out from pcb-rnd and some time passed, the pcb-rnd font engine got moved to librnd.

From librnd 4.1.0, the font engine got upgraded once again and is not called font v2 (file format) or font2 (the code that implements it). The original pre-4.1.0 librnd font is now called font v1.

2. Reasons for the upgrade

For pcb-rnd, font v1 is good enough. For some time both camv-rnd and alpha testing versions of sch-rnd used to use ttf. However, it turned out there are some tasks that are real hard to solve with ttf, plus ensuring an existing ttf font that's present on any system, without having to ship one and without depending on fontconfig (which is larger than sch-rnd) is too hard.

The trivial solution was to remove ttf support and simply use pcb-rnd's font engine. Thus the font engine got moved from pcb-rnd to librnd and camv-rnd and sch-rnd got switched over to our own vector font.

This switchover worked out well but also exposed a set of limitations in the font engine - details that matter in sch-rnd but don't in pcb-rnd. None of these features are critical, but they still can add a lot.

3. The new features

3.1. line_height: multiline rendering

Font v1 did not support multiline text objects because they are very rarely needed on a PCB. On a schematics it's more common to have large comment blocks thus multiline is required.

Font v2 features the optional line_height field, which is the vertical advance (in coord units). When multiline rendering is enabled and the font engine encounters a \n character, it returns x to the left origin of the text object and advances y to the next line. When line_height is specified, that value is used for the y advance, else a fallback value is computed.

3.2. tab_width: tabulator positions

When sch-rnd renders a non-graphical sheet, it is typically rendering an indentated text - most often tab indentated text. Since our font is not mono-space, tab can not be computed on a per character basis.

If tab_width is specified in font v2, it's the distance between two tab positions, the zeroth tab position being at the origin of the text object. If tab rendering is enabled and the engine encounters a \t character on input, advance is modified to the next tab position.

When not specified, a fallback value is computed. tab_width is specified in coord units.

3.3. baseline

Glyph origin is at top left and with font v1 it matches the origin of the text object. If the user wants to render two text objects with different font "in the same line", this breaks because of different font heights and internal glyph vertical offsets.

This problem can be fixed with font v2 by specifying the baseline (in coord units), it is the distance from glyph top to the baseline of the font. The baseline is a font-gobal setting, not a per glyph setting. When the text object is rendered glyphs are moved up by baseline. In practice this means the rendered font's baseline-left point is placed on the text object's origin. As long as both fonts have a properly specified baseline, two text objects of different font or height with the same y origin value will line up properly.

When not specified, 0 is assumed and the original top-left glyph origin is applied.

3.4. &entity;

The font engine supports 255 glyphs per font (glyph 0 is unusable because of C string termination). The lower 127 glyphs, the 7 bit ASCII set, is well specified and trivial to use. Most of the existing pcb-rnd fonts don't specify more glyphs than the 7 bit ASCII printables.

There are a few non-ASCII characters that are often useful in EDA context, like the micro or Ohm symbols. These can easily be placed somewhere above 127, but it would require some mapping - assigning standard numbers to the extra symbols so all fonts would use the same number for the same symbol. Effectively creating an rnd code page. But this would introduce a new limitation (256 characters may be enough per font, but is not enough globally). Plus placing non-printable characters in plain text files is not a good idea. And Ringdove programs don't use unicode/utf8.

To overcome these problems and limitations font v2 has an option for interpreting html-like &entity; names translating each entity into a single character. The & entity is always translated to & by code. The rest of the entities are looked up in the font's entity table, which contains pairs of entity_name-to-glyph-index entries. If an entity name is found in the table, the glyph pointed by the entry is rendered. Multiple entity names may point to the same glyph. One entity name may present only once in the table. Entity names are case sensitive. If the text string addresses an entity name that is not present in the table, the unknown glyph is rendered.

In practice a string may look like "120 Ω". This is the form the string is stored in files and presented on the user interface but when the text object has entity translation enabled the font rendering engine will look up and render a single glyph for Ω.

3.5. kerning table

The horizontal advance is when the engine finished rendering the current glyph and moves right to render the next glyph. The amount of this move normally depends on the width of the current glyph and a small amount of extra spacing, also specified per glyph. This mechanism works fine most of the times, but there are some corner cases.

For example rendering the combination of capital AV may appear to have more space between the two glyphs because of the shape of the glyphs. Such corner cases can be fine tuned using the optional kerning table.

The kerning table contains two columns: a character pair and an offset value. After revering a glyph and making the standard advance, the font engine looks ahead at the next character of input and looks up the current character-next character pair in the kerning table. When found, the corresponding offset is added to the advance. For example the kerning table may contain an entry with A-V=-0.4mm to move V to the left by 0.4mm (in the 1:1 reference scale factor) when V is rendered after an A.

For non-printable and special characters, dash included, the &n glyph index is specified, where n is a decimal integer between 1 and 254.

3.6. alignment in bbox based rendering

Normally the text string is rendered at a given origin and it takes up as much space as needed. In bbox based rendering the caller specifies a bounding box in the unrotated (but scaled) text's coordinate system. This bbox is larger than the natural size of the text. The font engine then figures how to place the smaller text object in this larger box.

Vertical alignment can be one of start, end, center and justify. Start means the text is placed close to the upper side (excess free space allocated on the bottom). End means text is placed on bottom, center means excess space is split in two and distribted evenly around the text. Justify plays role only in multiline text: excess space is distributed evenly between text lines so the first line starts at top of the box and the last line ends at the bottom.

Horizontal alignment works genereally the same, with start begin left aligned, end being right aligned. For multiline text horizontal alignment is applied line by line. There are two justify options for the horizontal case: glyph and word. The former distributes excess space between glyphs and the latter distributes them among whitespace characters only.