[Oberon] Module Fonts without reference to SYSTEM

Andreas Pirklbauer andreas_pirklbauer at yahoo.com
Fri Sep 4 22:04:50 CEST 2020


Extended Oberon has a module Fonts that does not access module SYSTEM
at all. I have been asked to provide one for Project Oberon 2013 as well.

Below is one way to do it. It used ORD for pointers (undocumented feature).

The performance penalty of using array ops instead of GET and PUT is negligible.

-ap


-----------------------------------------------------------




MODULE Fonts; (*JG 18.11.90; PDR 8.6.12; NW 18.1.2019 / AP 1.9.20*)
  IMPORT Files;

  CONST FontFileId = 0DBH;
    RasterDescSize = 568; (*excluding raster itself*) RasterSize = 2360; RasterExtSize = 2560;

  TYPE Font* = POINTER TO FontDesc;
    FontDesc* = RECORD
      name*: ARRAY 32 OF CHAR;
      height*, minX*, maxX*, minY*, maxY*: INTEGER;
      next*: Font;
      T: ARRAY 128 OF INTEGER;
      raster: ARRAY RasterSize OF BYTE
    END ;

    LargeFontDesc = RECORD (FontDesc) ext: ARRAY RasterExtSize OF BYTE END ;
    LargeFont = POINTER TO LargeFontDesc;
    RunRec = RECORD beg, end: BYTE END ;
    BoxRec = RECORD dx, x, y, w, h: BYTE END ;

  (* raster sizes: Syntax8 1367, Syntax10 1628, Syntax12 1688, Syntax14 1843, Syntax14b 1983,                                               
      Syntax16 2271, Syntax20 3034, Syntac24 4274, Syntax24b 4302  *)

  VAR Default*, root*: Font;

  PROCEDURE GetPat*(fnt: Font; ch: CHAR; VAR dx, x, y, w, h, patadr: INTEGER);
    VAR pa: INTEGER; yb: BYTE;
  BEGIN pa := fnt.T[ORD(ch) MOD 80H]; patadr := ORD(fnt) + RasterDescSize + pa;
    IF pa < RasterSize THEN
      dx := fnt.raster[pa-3]; x := fnt.raster[pa-2]; yb := fnt.raster[pa-1]; w := fnt.raster[pa]; h := fnt.raster[pa+1]
    ELSE dx := fnt(LargeFont).ext[pa-3-RasterSize];
      x := fnt(LargeFont).ext[pa-2-RasterSize]; yb := fnt(LargeFont).ext[pa-1-RasterSize];
      w := fnt(LargeFont).ext[pa-RasterSize]; h := fnt(LargeFont).ext[pa+1-RasterSize]
    END ;
    IF yb < 128 THEN y := yb ELSE y := yb - 256 END
  END GetPat;

  PROCEDURE This*(name: ARRAY OF CHAR): Font;
    VAR F: Font; LF: LargeFont;
      f: Files.File; R: Files.Rider;
      NofRuns, NofBoxes, height, minX, maxX, minY, maxY, b: BYTE;
      NofBytes, j, k, m, n, a: INTEGER;
      run: ARRAY 16 OF RunRec;
      box: ARRAY 512 OF BoxRec;

    PROCEDURE RdInt16(VAR R: Files.Rider; VAR b0: BYTE);
      VAR b1: BYTE;
    BEGIN Files.ReadByte(R, b0); Files.ReadByte(R, b1)
    END RdInt16;

  BEGIN F := root;
    WHILE (F # NIL) & (name # F.name) DO F := F.next END ;
    IF F = NIL THEN
      f := Files.Old(name);
      IF f # NIL THEN
        Files.Set(R, f, 0); Files.ReadByte(R, b);
        IF b = FontFileId THEN
          Files.ReadByte(R, b); (*abstraction*)
          Files.ReadByte(R, b); (*family*)
          Files.ReadByte(R, b); (*variant*)
          RdInt16(R, height); RdInt16(R, minX); RdInt16(R, maxX); RdInt16(R, minY); RdInt16(R, maxY); RdInt16(R, NofRuns);
          NofBoxes := 0; k := 0;
          WHILE k # NofRuns DO  (*read runs*)
            RdInt16(R, run[k].beg); RdInt16(R, run[k].end);
            NofBoxes := NofBoxes + run[k].end - run[k].beg;
            INC(k)
          END ;
          NofBytes := 5; j := 0;
          WHILE j # NofBoxes DO  (*read boxes*)
            RdInt16(R, box[j].dx); RdInt16(R, box[j].x); RdInt16(R, box[j].y);
            RdInt16(R, box[j].w); RdInt16(R, box[j].h);
            NofBytes := NofBytes + 5 + (box[j].w + 7) DIV 8 * box[j].h;
            INC(j)
          END ;
          IF NofBytes < 2300 THEN NEW(F) ELSE NEW(LF); F := LF END ;
          F.name := name;
          F.height := height; F.minX := minX; F.maxX := maxX; F.maxY := maxY;
          IF minY >= 80H THEN F.minY := minY - 100H ELSE F.minY := minY END ;
          F.raster[0] := 0; F.raster[1] := 0; F.raster[2] := 0; F.raster[3] := 0; F.raster[4] := 0;
          (*null pattern for characters not in a run*)
          a := 5; j := 0; k := 0; m := 0;
          WHILE k < NofRuns DO
            WHILE (m < run[k].beg) & (m < 128) DO F.T[m] := 3; INC(m) END ;
            WHILE (m < run[k].end) & (m < 128) DO F.T[m] := a+3;
              IF a < RasterSize THEN
		F.raster[a] := box[j].dx; F.raster[a+1] := box[j].x; F.raster[a+2] := box[j].y;
                F.raster[a+3] := box[j].w; F.raster[a+4] := box[j].h
              ELSE F(LargeFont).ext[a-RasterSize] := box[j].dx;
                F(LargeFont).ext[a+1-RasterSize] := box[j].x; F(LargeFont).ext[a+2-RasterSize] := box[j].y;
                F(LargeFont).ext[a+3-RasterSize] := box[j].w; F(LargeFont).ext[a+4-RasterSize] := box[j].h
              END ;
              INC(a, 5); n := (box[j].w + 7) DIV 8 * box[j].h; (*pattern length*)
              WHILE n # 0 DO DEC(n); Files.ReadByte(R, b);
		IF a < RasterSize THEN F.raster[a] := b ELSE F(LargeFont).ext[a-RasterSize] := b END ;
                INC(a)
              END ;
              INC(j); INC(m)
            END ;
            INC(k)
          END ;
          WHILE m < 128 DO F.T[m] := 3; INC(m) END ;
          F.next := root; root := F
        ELSE (*bad file id*) F := Default
        END
      ELSE (*font file not available*) F := Default
      END
    END ;
    RETURN F
  END This;

  PROCEDURE Free*;  (*remove all but first two from font list*)
  BEGIN IF root.next # NIL THEN root.next.next := NIL END
  END Free;

BEGIN root := NIL; Default := This("Oberon10.Scn.Fnt")
END Fonts.



More information about the Oberon mailing list