123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- MODULE WMOTFonts; (** AUTHO "PL"; PURPOSE "OpenType Support" *)
- IMPORT
- KernelLog, WMGraphics, Raster, WMFontManager, Strings, WMRectangles,
- Files, OpenType, OpenTypeInt, OpenTypeScan;
- CONST
- ScreenDPI = 71;
- Debug = FALSE;
- TYPE
- Glyph* = RECORD
- img- : WMGraphics.Image;
- code- : LONGINT; (* import only *)
- dx, x, y, w, h : LONGINT;
- END;
- GlyphArray* = POINTER TO ARRAY OF Glyph;
- GlyphRange* = RECORD
- firstCode-, lastCode- : LONGINT; (* inclusive *)
- glyphs- : GlyphArray;
- loaded- : BOOLEAN;
- END;
- GlyphRangeArray* = POINTER TO ARRAY OF GlyphRange;
- TYPE
- RasterData = RECORD (OpenType.RasterData)
- cache-: POINTER TO ARRAY OF ARRAY OF CHAR;
- END;
- PROCEDURE FillRect*(llx, lly, urx, ury, opacity: INTEGER; VAR data: OpenType.RasterData0);
- VAR x,y: LONGINT;
- BEGIN
- WITH data: RasterData DO
- FOR y := lly TO ury-1 DO
- FOR x := llx TO urx-1 DO
- IF (y < LEN(data.cache)) & (x < LEN(data.cache[0])) THEN
- IF CHR(opacity) > data.cache[y,x] THEN
- data.cache[y,x] := CHR(opacity);
- END;
- END;
- END;
- END;
- END;
- END FillRect;
- TYPE
- Font* = OBJECT(WMGraphics.Font)
- VAR nofGlyphs- : LONGINT;
- nofGlyphRanges- : LONGINT;
- glyphRanges : GlyphRangeArray;
- placeholderimg : WMGraphics.Image;
- fontFile : Files.File;
- empty : WMRectangles.Rectangle;
- fname-, subfam- : ARRAY 256 OF CHAR;
- ofont : OpenType.Font;
- inst: OpenType.Instance;
- glyph : OpenType.Glyph;
- cache-: POINTER TO ARRAY OF ARRAY OF CHAR;
- ras: OpenTypeScan.Rasterizer;
- PROCEDURE &Init*;
- VAR mode : Raster.Mode; pix : Raster.Pixel;
- BEGIN
- Init^;
- nofGlyphRanges := 0;
- empty := WMRectangles.MakeRect(0, 0, 0, 0); (* save the proc call *)
- NEW(placeholderimg); Raster.Create(placeholderimg, 16, 16, Raster.A1);
- Raster.InitMode(mode, Raster.srcCopy);
- Raster.SetRGBA(pix, 255, 0, 0, 0);
- Raster.Fill(placeholderimg, 0, 0, 15, 15, pix, mode);
- ascent := 16; descent := 5;
- END Init;
- (* support the oberon encoding scheme *)
- PROCEDURE MapCode(VAR code : LONGINT);
- BEGIN
- IF (code >= 126) & (code <= 155) THEN code := OpenType.CharToUnicode[code] END;
- END MapCode;
- PROCEDURE GetGlyphMap*(code : LONGINT; VAR map : WMGraphics.Image);
- VAR g : Glyph; range : LONGINT;
- BEGIN
- IF FindGlyphRange(code, range) THEN
- IF FindGlyph(code, g) THEN
- map := g.img
- ELSE map := placeholderimg
- END
- ELSE map := placeholderimg
- END
- END GetGlyphMap;
- PROCEDURE HasChar*(code : LONGINT) : BOOLEAN;
- VAR dummy : LONGINT;
- BEGIN
- RETURN FindGlyphRange(code, dummy)
- END HasChar;
- PROCEDURE GetGlyphSpacings*(code : LONGINT; VAR glyphSpacings : WMGraphics.GlyphSpacings);
- VAR g : Glyph; range : LONGINT;
- BEGIN
- IF FindGlyphRange(code, range) THEN
- IF FindGlyph(code, g) THEN
- glyphSpacings.width := g.w;
- glyphSpacings.ascent := ascent; glyphSpacings.descent := descent;
- glyphSpacings.bearing.l := g.x;
- glyphSpacings.bearing.r := g.dx - (g.w + g.x);
- glyphSpacings.height := g.h;
- glyphSpacings.dy := ascent - g.h - g.y
- ELSE glyphSpacings.width := 5; glyphSpacings.height := 15; glyphSpacings.ascent := 16;
- END
- ELSE
- KernelLog.String("code= "); KernelLog.Int(code, 0); KernelLog.String("out of range"); KernelLog.Ln;
- glyphSpacings.width := 5; glyphSpacings.height := 15; glyphSpacings.ascent := 16;
- END
- END GetGlyphSpacings;
- PROCEDURE LoadGlyphRange(gri : LONGINT);
- VAR i: LONGINT;
- BEGIN
- IF glyphRanges[gri].glyphs = NIL THEN
- NEW(glyphRanges[gri].glyphs, glyphRanges[gri].lastCode - glyphRanges[gri].firstCode + 1);
- FOR i := glyphRanges[gri].firstCode TO glyphRanges[gri].lastCode DO
- glyphRanges[gri].glyphs[i - glyphRanges[gri].firstCode].code := -1;
- END;
- END;
- (* epxeriment *)
- glyphRanges[gri].loaded := TRUE;
- RETURN;
- FOR i := glyphRanges[gri].firstCode TO glyphRanges[gri].lastCode DO
- ReadGlyph(i, glyphRanges[gri].glyphs[i - glyphRanges[gri].firstCode])
- END;
- glyphRanges[gri].loaded := TRUE;
- END LoadGlyphRange;
- PROCEDURE FindGlyphRange(code : LONGINT; VAR gri : LONGINT) : BOOLEAN;
- VAR a, b, m : LONGINT;
- BEGIN
- gri := 0;
- a := 0; b := LEN(glyphRanges)- 1;
- WHILE (a < b) DO m := (a + b) DIV 2;
- IF glyphRanges[m].lastCode < code THEN a := m + 1
- ELSE b := m
- END
- END;
- IF (glyphRanges[a].firstCode <= code) & (glyphRanges[a].lastCode >= code) THEN
- IF ~glyphRanges[a].loaded THEN LoadGlyphRange(a) END;
- gri := a; RETURN TRUE
- ELSE RETURN FALSE
- END
- END FindGlyphRange;
- PROCEDURE FindGlyph(code : LONGINT; VAR glyph : Glyph) : BOOLEAN;
- VAR gri : LONGINT;
- BEGIN
- IF FindGlyphRange(code, gri) THEN
- IF glyphRanges[gri].glyphs[code - glyphRanges[gri].firstCode].code # code THEN
- ReadGlyph(code, glyphRanges[gri].glyphs[code - glyphRanges[gri].firstCode])
- END;
- glyph := glyphRanges[gri].glyphs[code - glyphRanges[gri].firstCode];
- RETURN TRUE
- ELSE RETURN FALSE
- END
- END FindGlyph;
- PROCEDURE CountGlyphes():LONGINT;
- VAR i, c : LONGINT;
- BEGIN
- FOR i := 0 TO nofGlyphRanges - 1 DO
- c := c + glyphRanges[i].lastCode - glyphRanges[i].firstCode + 1;
- END;
- RETURN c
- END CountGlyphes;
- PROCEDURE ReadGlyph(code : LONGINT; VAR g : Glyph);
- VAR no, dw, dh, y, x: LONGINT; mode: Raster.Mode; data: RasterData;
- BEGIN{EXCLUSIVE}
- Raster.InitMode(mode, Raster.srcCopy);
- no := OpenType.UnicodeToGlyph(ofont, code);
- IF Debug THEN KernelLog.String("Reading Glyph Nr: "); KernelLog.Int(no, 0); KernelLog.String(" Code: u"); KernelLog.Hex(code, 4); KernelLog.Ln END;
- OpenType.LoadGlyph(inst, glyph, ras, no, {OpenType.Hinted, OpenType.Width , OpenType.Raster, OpenType.Grey});
- g.dx := glyph.awx; (* advance *)
- g.x := glyph.hbx; (* horizontal bearing x *)
- g.y := glyph.hby; (* horizontal bearing y *)
- g.w := glyph.rw; (* image width *)
- g.h := glyph.rh; (* image height *)
- g.code := code;
- dh := glyph.rh;
- dw := glyph.rw;
- IF dw*dh # 0 THEN
- IF (cache = NIL) OR (LEN(cache,0) < dh) OR (LEN(cache,1) < dw) THEN
- NEW(cache,dh,dw);
- END;
- data.cache := cache;
- data.rect := FillRect;
- FOR y := 0 TO dh-1 DO
- FOR x := 0 TO dw-1 DO
- cache[y,x] := 0X;
- END;
- END;
- OpenType.EnumRaster(ras, data);
- NEW(g.img);
- Raster.Create(g.img, dw, dh, Raster.A8);
- FOR y := 0 TO dh-1 DO
- Raster.PutPixels(g.img, 0, dh - y -1, dw, Raster.A8, cache[y], 0, mode);
- END;
- OpenTypeScan.DisposeRasterizer(ras);
- END;
- END ReadGlyph;
- PROCEDURE Load(filename : ARRAY OF CHAR; size : LONGINT) : BOOLEAN;
- VAR i, j, k, ngri, splitCount : LONGINT; res : INTEGER;
- ascent, descent : LONGINT;
- BEGIN
- fontFile := Files.Old(filename); (* open file *)
- IF fontFile = NIL THEN RETURN FALSE END;
- ofont := OpenType.Open(filename); (* read file *)
- IF ofont = NIL THEN KernelLog.String("OT: Could not open Font: "); KernelLog.String(filename); KernelLog.Ln; RETURN FALSE END;
- NEW(glyph);
- OpenType.InitGlyph(glyph, ofont);
- res := ScreenDPI;
- OpenType.GetInstance(ofont, 40H*size, res, res, OpenType.Identity, inst); (* get instance *)
- IF inst = NIL THEN KernelLog.String("OT: Could not get Instance: "); KernelLog.String(filename); KernelLog.Ln; RETURN FALSE END;
- OpenType.GetName(ofont, 1, fname); (* get Name *)
- OpenType.GetName(ofont, 2, subfam); (* get SubFamily *)
- nofGlyphs := glyph.font.maxp.numGlyphs; (* number of glyphs *)
- nofGlyphRanges := 0; (*ofont.cmap.segCount;*) (* number of ranges *)
- (* split into ranges of max size 256 *)
- ngri := ofont.cmap.segCount;
- FOR i := 0 TO ngri - 1 DO
- IF (ofont.cmap.seg[i].end # 0) THEN
- INC(nofGlyphRanges, 1 + ((ofont.cmap.seg[i].end - ofont.cmap.seg[i].start) MOD 10000H) DIV 100H)
- END
- END;
- NEW(glyphRanges, nofGlyphRanges); i := 0; k := 0;
- IF Debug THEN KernelLog.String("-- Building Ranges: "); KernelLog.Int(nofGlyphRanges, 0); KernelLog.Ln END;
- WHILE k < ngri DO
- IF ofont.cmap.seg[k].end # 0 THEN
- splitCount := ((ofont.cmap.seg[k].end - ofont.cmap.seg[k].start) MOD 10000H) DIV 100H; j := 0;
- WHILE j < splitCount DO
- glyphRanges[i+j].firstCode := (ofont.cmap.seg[k].start MOD 10000H) + 100H*j; glyphRanges[i+j].lastCode := (ofont.cmap.seg[k].start MOD 10000H) + 100H*(j+1) - 1;
- IF Debug THEN KernelLog.String(" SRange: "); KernelLog.Int(i+j, 0); KernelLog.String(" Start: "); KernelLog.Int(glyphRanges[i+j].firstCode, 0); KernelLog.String(" End: "); KernelLog.Int(glyphRanges[i+j].lastCode, 0); KernelLog.Ln END;
- INC(j);
- END;
- glyphRanges[i+j].firstCode := (ofont.cmap.seg[k].start MOD 10000H) + 100H*splitCount ; glyphRanges[i+j].lastCode := ofont.cmap.seg[k].end MOD 10000H;
- IF Debug THEN KernelLog.String(" Range: "); KernelLog.Int(i+j, 0); KernelLog.String(" Start: "); KernelLog.Int(glyphRanges[i+j].firstCode, 0); KernelLog.String(" End: "); KernelLog.Int(glyphRanges[i+j].lastCode, 0); KernelLog.Ln END;
- INC(i, splitCount+1);
- END;
- INC(k)
- END;
- (* height := inst.font.hhea.ascender + inst.font.hhea.descender + inst.font.hhea.lineGap;
- SELF.height := SHORT(OpenTypeInt.MulDiv(height, inst.yppm, 40H*LONG(inst.font.head.unitsPerEm))); (* height *)
- *)
- ascent := inst.font.hhea.ascender;
- SELF.ascent := SHORT(OpenTypeInt.MulDiv(ascent, inst.yppm, 40H*LONG(inst.font.head.unitsPerEm))); (* ascent *)
- descent := inst.font.hhea.descender;
- SELF.descent := -SHORT(OpenTypeInt.MulDiv(descent, inst.yppm, 40H*LONG(inst.font.head.unitsPerEm))); (* descent *)
- RETURN TRUE
- END Load;
- END Font;
- (* ------------------------------------------------- *)
- VAR bit: ARRAY 100H, 8 OF BOOLEAN; (* Bit[b, i] means bit i in byte b is set *)
- PROCEDURE InitBitTable;
- VAR b, i: LONGINT;
- BEGIN
- FOR b := 0 TO 0FFH DO
- FOR i := 0 TO 7 DO
- bit[b, i] := ODD(ASH(b, -i))
- END
- END
- END InitBitTable;
- PROCEDURE LoadFont(name : ARRAY OF CHAR; size : LONGINT) : Font;
- VAR font: Font;
- BEGIN
- IF Debug THEN KernelLog.String("Loading Font: "); KernelLog.String(name); KernelLog.Ln END;
- NEW(font); IF ~font.Load(name, size) THEN RETURN NIL END;
- RETURN font
- END LoadFont;
- PROCEDURE LoadExactFont*(fi : WMFontManager.FontInfo) : WMGraphics.Font;
- VAR exactName : ARRAY 256 OF CHAR; f : WMGraphics.Font; try: LONGINT;
- BEGIN
- try := 0;
- LOOP
- COPY(fi.name^, exactName);
- (* possible suffixes
- bold: b, B, bd, Bd, _bd, -Bold
- italic: i, I, _i, -Italic
- bold+italic: bi, BI, _bi, -BoldItalic
- *)
- IF WMGraphics.FontBold IN fi.style THEN
- IF WMGraphics.FontItalic IN fi.style THEN
- CASE try OF
- |0: Strings.Append(exactName, "bi");
- |1: Strings.Append(exactName, "-BoldItalic");
- |2: Strings.Append(exactName, "_bi");
- |3: Strings.Append(exactName, "BI");
- |4: Strings.Append(exactName, "-BoldOblique");
- ELSE EXIT
- END
- ELSE
- CASE try OF
- |0: Strings.Append(exactName, "b");
- |1: Strings.Append(exactName, "-Bold");
- |2: Strings.Append(exactName, "bd");
- |3: Strings.Append(exactName, "_bd");
- |4: Strings.Append(exactName, "B");
- |5: Strings.Append(exactName, "Bd");
- ELSE EXIT
- END
- END;
- ELSIF WMGraphics.FontItalic IN fi.style THEN
- CASE try OF
- |0: Strings.Append(exactName, "i");
- |1: Strings.Append(exactName, "-Italic");
- |2: Strings.Append(exactName, "_i");
- |3: Strings.Append(exactName, "I");
- |4: Strings.Append(exactName, "-Oblique");
- ELSE
- EXIT
- END;
- ELSE
- CASE try OF
- 0:;
- |1: Strings.Append(exactName, "-Regular");
- ELSE
- EXIT;
- END;
- END;
- Strings.Append(exactName, ".ttf");
- f := LoadFont(exactName, fi.size);
- IF f # NIL THEN EXIT END;
- INC( try )
- END;
- IF f # NIL THEN
- COPY(fi.name^, f.name);
- f.size := fi.size;
- f.style := fi.style;
- END;
- RETURN f
- END LoadExactFont;
- PROCEDURE LoadApproximateFont*(fi : WMFontManager.FontInfo) : WMGraphics.Font;
- VAR exactName : ARRAY 256 OF CHAR; f : WMGraphics.Font;
- BEGIN
- COPY(fi.name^, exactName);
- Strings.Append(exactName, ".ttf");
- f := LoadFont(exactName, fi.size);
- IF f # NIL THEN
- COPY(fi.name^, f.name);
- f.size := fi.size;
- f.style := fi.style
- END;
- RETURN f
- END LoadApproximateFont;
- PROCEDURE MultiTest*;
- VAR name : ARRAY 256 OF CHAR; flags : SET; time, date, size : LONGINT;
- enumerator : Files.Enumerator;
- f : WMGraphics.Font;
- BEGIN
- NEW(enumerator);
- enumerator.Open("*.ttf", {});
- KernelLog.String("*** TrueType MultiTester v0.1 ***"); KernelLog.Ln;
- WHILE enumerator.HasMoreEntries() DO
- IF enumerator.GetEntry(name, flags, time, date, size) THEN
- KernelLog.String(" Testing File: "); KernelLog.String(name);
- f := LoadFont(name, 40);
- IF f # NIL THEN
- KernelLog.String(" all ok")
- ELSE
- KernelLog.String(" failed")
- END;
- KernelLog.Ln
- END
- END;
- KernelLog.String("*** all done ***"); KernelLog.Ln;
- enumerator.Close;
- END MultiTest;
- BEGIN
- InitBitTable
- END WMOTFonts.
- --------------------------------------------
- System.Free WMOTFonts~
- WMOTFonts.MultiTest~
|