RISC OS Font control parsing in Python

This repository contains a class (FontControlParser) for parsing font control codes from a
byte squence, in Python.

It is part of the RISC OS Pyromaniac font system, and provides support for the SWIs which operate
on the font strings:

  • Font_Paint – uses the controls to determine what should be rendered.
  • Font_ScanString – uses the controls to determine the size or break points of a string.

And all the other calls which call on to Font_ScanString:

  • Font_StringWidth – reads the width of a string.
  • Font_StringBBox – reads the coverage of a string.
  • Font_FindCaret – finds the position of the caret within a string.
  • Font_FindCaretJ – a variant of the Font_FindCaret call.

The FontContext provides information which allows the operation of the following calls:

  • Font_CurrentFont – reads the current font.
  • Font_FutureFont – reads what the font would be after one of the sizing calls.
  • Font_CurrentRGB – reads the current colour.
  • Font_FutureRGB – reads what the colour would be after one of the sizing calls.

Usage

Inside RISC OS Pyromaniac…

  • The FontControlParser is subclassed to allow the memory access to occur within the
    emulated memory, not using the bytes.
  • The FontSpacing is subclassed to create spacing from the memory blocks.
  • The FontContext is subclassed to allow the GCOL/RGB operations, font lookups, sizing and rendering operations to be performed on the RISC OS graphics system.

A font context is created on initialisation, and will be updated by different operations:

self.context = FontContextPyromaniac(self.ro, self.fonts)

The font parser is constructed and supplied the memory buffers to parse:

memstring = self.ro.memory[regs[1]]
fc = FontControlParserPyromaniac(self.ro)
fc.debug_enable = self.debug_fontparser
fc.parse(memstring, string_length)

Once the other parameters for Font_Paint have been decoded and spacing and transformed written
to the FontContext, the paint operation is called.

self.context.x = xmilli
self.context.y = ymilli
self.context.transform = transform

with self.ro.kernel.graphics.vducursor.disable():
    self.context.paint(fc.sequence, spacing)

    # Update OS_ChangedBox
    x0 = int(self.context.bounds.x0 / riscos.graphics.font.FontConstants.Font_OSUnit) >> xeig
    y0 = int(self.context.bounds.y0 / riscos.graphics.font.FontConstants.Font_OSUnit) >> xeig
    x1 = int(self.context.bounds.x1 / riscos.graphics.font.FontConstants.Font_OSUnit) >> yeig
    y1 = int(self.context.bounds.y1 / riscos.graphics.font.FontConstants.Font_OSUnit) >> yeig
    self.ro.kernel.graphics.changedbox_update(x0, y0, x1, y1)

Font_ScanString is similar, but instead of operating on the current context, the future context is updated:

self.context.copy(to=self.future_context)
...
memstring = self.ro.memory[regs[1]]
fc = FontControlParserPyromaniac(self.ro)
fc.debug_enable = self.debug_fontparser
fc.parse(memstring, string_length)
...
(split_offset, splits) = self.future_context.size(fc.sequence, spacing=spacing, limits=(xmilli, ymilli),
                                                  split_char=split_char)

This allows all the Font_Future* calls to query the self.future_context

Tests

Tests exist to show that the module is working properly, intended for use on GitLab.
Code coverage is reported as well.

To test, use:

make tests PYTHON=python2

Tests don’t work on Python 3 yet, due to some problems with the processing of byte sequences.

To run coverage, use:

make coverage

GitHub

View Github