GRUB 2 font: Nim parsing library

Bitmap font format for the GRUB 2 bootloader.

Application

GRUB 2

File extension

pf2

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of GRUB 2 font using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Nim source code to parse GRUB 2 font

grub2_font.nim

import kaitai_struct_nim_runtime
import options

type
  Grub2Font* = ref object of KaitaiStruct
    `magic`*: seq[byte]
    `sections`*: seq[Grub2Font_Section]
    `parent`*: KaitaiStruct
  Grub2Font_PtszSection* = ref object of KaitaiStruct
    `fontPointSize`*: uint16
    `parent`*: Grub2Font_Section
  Grub2Font_FamiSection* = ref object of KaitaiStruct
    `fontFamilyName`*: string
    `parent`*: Grub2Font_Section
  Grub2Font_WeigSection* = ref object of KaitaiStruct
    `fontWeight`*: string
    `parent`*: Grub2Font_Section
  Grub2Font_MaxwSection* = ref object of KaitaiStruct
    `maximumCharacterWidth`*: uint16
    `parent`*: Grub2Font_Section
  Grub2Font_DescSection* = ref object of KaitaiStruct
    `descentInPixels`*: uint16
    `parent`*: Grub2Font_Section
  Grub2Font_Section* = ref object of KaitaiStruct
    `sectionType`*: string
    `lenBody`*: uint32
    `body`*: KaitaiStruct
    `parent`*: Grub2Font
    `rawBody`*: seq[byte]
  Grub2Font_AsceSection* = ref object of KaitaiStruct
    `ascentInPixels`*: uint16
    `parent`*: Grub2Font_Section
  Grub2Font_ChixSection* = ref object of KaitaiStruct
    `characters`*: seq[Grub2Font_ChixSection_Character]
    `parent`*: Grub2Font_Section
  Grub2Font_ChixSection_Character* = ref object of KaitaiStruct
    `codePoint`*: uint32
    `flags`*: uint8
    `ofsDefinition`*: uint32
    `parent`*: Grub2Font_ChixSection
    `definitionInst`: Grub2Font_ChixSection_CharacterDefinition
    `definitionInstFlag`: bool
  Grub2Font_ChixSection_CharacterDefinition* = ref object of KaitaiStruct
    `width`*: uint16
    `height`*: uint16
    `xOffset`*: int16
    `yOffset`*: int16
    `deviceWidth`*: int16
    `bitmapData`*: seq[byte]
    `parent`*: Grub2Font_ChixSection_Character
  Grub2Font_MaxhSection* = ref object of KaitaiStruct
    `maximumCharacterHeight`*: uint16
    `parent`*: Grub2Font_Section
  Grub2Font_NameSection* = ref object of KaitaiStruct
    `fontName`*: string
    `parent`*: Grub2Font_Section
  Grub2Font_SlanSection* = ref object of KaitaiStruct
    `fontSlant`*: string
    `parent`*: Grub2Font_Section

proc read*(_: typedesc[Grub2Font], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Grub2Font
proc read*(_: typedesc[Grub2Font_PtszSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_PtszSection
proc read*(_: typedesc[Grub2Font_FamiSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_FamiSection
proc read*(_: typedesc[Grub2Font_WeigSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_WeigSection
proc read*(_: typedesc[Grub2Font_MaxwSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_MaxwSection
proc read*(_: typedesc[Grub2Font_DescSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_DescSection
proc read*(_: typedesc[Grub2Font_Section], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font): Grub2Font_Section
proc read*(_: typedesc[Grub2Font_AsceSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_AsceSection
proc read*(_: typedesc[Grub2Font_ChixSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_ChixSection
proc read*(_: typedesc[Grub2Font_ChixSection_Character], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_ChixSection): Grub2Font_ChixSection_Character
proc read*(_: typedesc[Grub2Font_ChixSection_CharacterDefinition], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_ChixSection_Character): Grub2Font_ChixSection_CharacterDefinition
proc read*(_: typedesc[Grub2Font_MaxhSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_MaxhSection
proc read*(_: typedesc[Grub2Font_NameSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_NameSection
proc read*(_: typedesc[Grub2Font_SlanSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_SlanSection

proc definition*(this: Grub2Font_ChixSection_Character): Grub2Font_ChixSection_CharacterDefinition


##[
Bitmap font format for the GRUB 2 bootloader.

@see <a href="https://grub.gibibit.com/New_font_format">Source</a>
]##
proc read*(_: typedesc[Grub2Font], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Grub2Font =
  template this: untyped = result
  this = new(Grub2Font)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let magicExpr = this.io.readBytes(int(12))
  this.magic = magicExpr

  ##[
  The "DATA" section acts as a terminator. The documentation says:
"A marker that indicates the remainder of the file is data accessed
via the character index (CHIX) section. When reading this font file,
the rest of the file can be ignored when scanning the sections."

  ]##
  block:
    var i: int
    while true:
      let it = Grub2Font_Section.read(this.io, this.root, this)
      this.sections.add(it)
      if it.sectionType == "DATA":
        break
      inc i

proc fromFile*(_: typedesc[Grub2Font], filename: string): Grub2Font =
  Grub2Font.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_PtszSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_PtszSection =
  template this: untyped = result
  this = new(Grub2Font_PtszSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let fontPointSizeExpr = this.io.readU2be()
  this.fontPointSize = fontPointSizeExpr

proc fromFile*(_: typedesc[Grub2Font_PtszSection], filename: string): Grub2Font_PtszSection =
  Grub2Font_PtszSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_FamiSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_FamiSection =
  template this: untyped = result
  this = new(Grub2Font_FamiSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let fontFamilyNameExpr = encode(this.io.readBytesTerm(0, false, true, true), "ASCII")
  this.fontFamilyName = fontFamilyNameExpr

proc fromFile*(_: typedesc[Grub2Font_FamiSection], filename: string): Grub2Font_FamiSection =
  Grub2Font_FamiSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_WeigSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_WeigSection =
  template this: untyped = result
  this = new(Grub2Font_WeigSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let fontWeightExpr = encode(this.io.readBytesTerm(0, false, true, true), "ASCII")
  this.fontWeight = fontWeightExpr

proc fromFile*(_: typedesc[Grub2Font_WeigSection], filename: string): Grub2Font_WeigSection =
  Grub2Font_WeigSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_MaxwSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_MaxwSection =
  template this: untyped = result
  this = new(Grub2Font_MaxwSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let maximumCharacterWidthExpr = this.io.readU2be()
  this.maximumCharacterWidth = maximumCharacterWidthExpr

proc fromFile*(_: typedesc[Grub2Font_MaxwSection], filename: string): Grub2Font_MaxwSection =
  Grub2Font_MaxwSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_DescSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_DescSection =
  template this: untyped = result
  this = new(Grub2Font_DescSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let descentInPixelsExpr = this.io.readU2be()
  this.descentInPixels = descentInPixelsExpr

proc fromFile*(_: typedesc[Grub2Font_DescSection], filename: string): Grub2Font_DescSection =
  Grub2Font_DescSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_Section], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font): Grub2Font_Section =
  template this: untyped = result
  this = new(Grub2Font_Section)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let sectionTypeExpr = encode(this.io.readBytes(int(4)), "ASCII")
  this.sectionType = sectionTypeExpr

  ##[
  Should be set to `0xFFFF_FFFF` for `section_type != "DATA"`
  ]##
  let lenBodyExpr = this.io.readU4be()
  this.lenBody = lenBodyExpr
  if this.sectionType != "DATA":
    block:
      let on = this.sectionType
      if on == "MAXH":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_MaxhSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "FAMI":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_FamiSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "PTSZ":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_PtszSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "MAXW":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_MaxwSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "SLAN":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_SlanSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "WEIG":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_WeigSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "CHIX":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_ChixSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "DESC":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_DescSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "NAME":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_NameSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      elif on == "ASCE":
        let rawBodyExpr = this.io.readBytes(int(this.lenBody))
        this.rawBody = rawBodyExpr
        let rawBodyIo = newKaitaiStream(rawBodyExpr)
        let bodyExpr = Grub2Font_AsceSection.read(rawBodyIo, this.root, this)
        this.body = bodyExpr
      else:
        let bodyExpr = this.io.readBytes(int(this.lenBody))
        this.body = bodyExpr

proc fromFile*(_: typedesc[Grub2Font_Section], filename: string): Grub2Font_Section =
  Grub2Font_Section.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_AsceSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_AsceSection =
  template this: untyped = result
  this = new(Grub2Font_AsceSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let ascentInPixelsExpr = this.io.readU2be()
  this.ascentInPixels = ascentInPixelsExpr

proc fromFile*(_: typedesc[Grub2Font_AsceSection], filename: string): Grub2Font_AsceSection =
  Grub2Font_AsceSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_ChixSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_ChixSection =
  template this: untyped = result
  this = new(Grub2Font_ChixSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  block:
    var i: int
    while not this.io.isEof:
      let it = Grub2Font_ChixSection_Character.read(this.io, this.root, this)
      this.characters.add(it)
      inc i

proc fromFile*(_: typedesc[Grub2Font_ChixSection], filename: string): Grub2Font_ChixSection =
  Grub2Font_ChixSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_ChixSection_Character], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_ChixSection): Grub2Font_ChixSection_Character =
  template this: untyped = result
  this = new(Grub2Font_ChixSection_Character)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent


  ##[
  Unicode code point
  ]##
  let codePointExpr = this.io.readU4be()
  this.codePoint = codePointExpr
  let flagsExpr = this.io.readU1()
  this.flags = flagsExpr
  let ofsDefinitionExpr = this.io.readU4be()
  this.ofsDefinition = ofsDefinitionExpr

proc definition(this: Grub2Font_ChixSection_Character): Grub2Font_ChixSection_CharacterDefinition = 
  if this.definitionInstFlag:
    return this.definitionInst
  let io = Grub2Font(this.root).io
  let pos = io.pos()
  io.seek(int(this.ofsDefinition))
  let definitionInstExpr = Grub2Font_ChixSection_CharacterDefinition.read(io, this.root, this)
  this.definitionInst = definitionInstExpr
  io.seek(pos)
  this.definitionInstFlag = true
  return this.definitionInst

proc fromFile*(_: typedesc[Grub2Font_ChixSection_Character], filename: string): Grub2Font_ChixSection_Character =
  Grub2Font_ChixSection_Character.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_ChixSection_CharacterDefinition], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_ChixSection_Character): Grub2Font_ChixSection_CharacterDefinition =
  template this: untyped = result
  this = new(Grub2Font_ChixSection_CharacterDefinition)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let widthExpr = this.io.readU2be()
  this.width = widthExpr
  let heightExpr = this.io.readU2be()
  this.height = heightExpr
  let xOffsetExpr = this.io.readS2be()
  this.xOffset = xOffsetExpr
  let yOffsetExpr = this.io.readS2be()
  this.yOffset = yOffsetExpr
  let deviceWidthExpr = this.io.readS2be()
  this.deviceWidth = deviceWidthExpr

  ##[
  A two-dimensional bitmap, one bit per pixel. It is organized as
row-major, top-down, left-to-right. The most significant bit of
each byte corresponds to the leftmost or uppermost pixel from all
bits of the byte. If a bit is set (1, `true`), the pixel is set to
the font color, if a bit is clear (0, `false`), the pixel is
transparent.

Rows are **not** padded to byte boundaries (i.e., a
single byte may contain bits belonging to multiple rows). The last
byte of the bitmap _is_ padded with zero bits at all unused least
significant bit positions so that the bitmap ends on a byte
boundary.

  ]##
  let bitmapDataExpr = this.io.readBytes(int((((this.width * this.height) + 7) div 8)))
  this.bitmapData = bitmapDataExpr

proc fromFile*(_: typedesc[Grub2Font_ChixSection_CharacterDefinition], filename: string): Grub2Font_ChixSection_CharacterDefinition =
  Grub2Font_ChixSection_CharacterDefinition.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_MaxhSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_MaxhSection =
  template this: untyped = result
  this = new(Grub2Font_MaxhSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let maximumCharacterHeightExpr = this.io.readU2be()
  this.maximumCharacterHeight = maximumCharacterHeightExpr

proc fromFile*(_: typedesc[Grub2Font_MaxhSection], filename: string): Grub2Font_MaxhSection =
  Grub2Font_MaxhSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_NameSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_NameSection =
  template this: untyped = result
  this = new(Grub2Font_NameSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let fontNameExpr = encode(this.io.readBytesTerm(0, false, true, true), "ASCII")
  this.fontName = fontNameExpr

proc fromFile*(_: typedesc[Grub2Font_NameSection], filename: string): Grub2Font_NameSection =
  Grub2Font_NameSection.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[Grub2Font_SlanSection], io: KaitaiStream, root: KaitaiStruct, parent: Grub2Font_Section): Grub2Font_SlanSection =
  template this: untyped = result
  this = new(Grub2Font_SlanSection)
  let root = if root == nil: cast[Grub2Font](this) else: cast[Grub2Font](root)
  this.io = io
  this.root = root
  this.parent = parent

  let fontSlantExpr = encode(this.io.readBytesTerm(0, false, true, true), "ASCII")
  this.fontSlant = fontSlantExpr

proc fromFile*(_: typedesc[Grub2Font_SlanSection], filename: string): Grub2Font_SlanSection =
  Grub2Font_SlanSection.read(newKaitaiFileStream(filename), nil, nil)