WMOTFonts.Mod 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. MODULE WMOTFonts; (** AUTHO "PL"; PURPOSE "OpenType Support" *)
  2. IMPORT
  3. KernelLog, WMGraphics, Raster, WMFontManager, Strings, WMRectangles,
  4. Files, OpenType, OpenTypeInt, OpenTypeScan;
  5. CONST
  6. ScreenDPI = 71;
  7. Debug = FALSE;
  8. TYPE
  9. Glyph* = RECORD
  10. img- : WMGraphics.Image;
  11. code- : LONGINT; (* import only *)
  12. dx, x, y, w, h : LONGINT;
  13. END;
  14. GlyphArray* = POINTER TO ARRAY OF Glyph;
  15. GlyphRange* = RECORD
  16. firstCode-, lastCode- : LONGINT; (* inclusive *)
  17. glyphs- : GlyphArray;
  18. loaded- : BOOLEAN;
  19. END;
  20. GlyphRangeArray* = POINTER TO ARRAY OF GlyphRange;
  21. TYPE
  22. RasterData = RECORD (OpenType.RasterData)
  23. cache-: POINTER TO ARRAY OF ARRAY OF CHAR;
  24. END;
  25. PROCEDURE FillRect*(llx, lly, urx, ury, opacity: INTEGER; VAR data: OpenType.RasterData0);
  26. VAR x,y: LONGINT;
  27. BEGIN
  28. WITH data: RasterData DO
  29. FOR y := lly TO ury-1 DO
  30. FOR x := llx TO urx-1 DO
  31. IF (y < LEN(data.cache)) & (x < LEN(data.cache[0])) THEN
  32. IF CHR(opacity) > data.cache[y,x] THEN
  33. data.cache[y,x] := CHR(opacity);
  34. END;
  35. END;
  36. END;
  37. END;
  38. END;
  39. END FillRect;
  40. TYPE
  41. Font* = OBJECT(WMGraphics.Font)
  42. VAR nofGlyphs- : LONGINT;
  43. nofGlyphRanges- : LONGINT;
  44. glyphRanges : GlyphRangeArray;
  45. placeholderimg : WMGraphics.Image;
  46. fontFile : Files.File;
  47. empty : WMRectangles.Rectangle;
  48. fname-, subfam- : ARRAY 256 OF CHAR;
  49. ofont : OpenType.Font;
  50. inst: OpenType.Instance;
  51. glyph : OpenType.Glyph;
  52. cache-: POINTER TO ARRAY OF ARRAY OF CHAR;
  53. ras: OpenTypeScan.Rasterizer;
  54. PROCEDURE &Init*;
  55. VAR mode : Raster.Mode; pix : Raster.Pixel;
  56. BEGIN
  57. Init^;
  58. nofGlyphRanges := 0;
  59. empty := WMRectangles.MakeRect(0, 0, 0, 0); (* save the proc call *)
  60. NEW(placeholderimg); Raster.Create(placeholderimg, 16, 16, Raster.A1);
  61. Raster.InitMode(mode, Raster.srcCopy);
  62. Raster.SetRGBA(pix, 255, 0, 0, 0);
  63. Raster.Fill(placeholderimg, 0, 0, 15, 15, pix, mode);
  64. ascent := 16; descent := 5;
  65. END Init;
  66. (* support the oberon encoding scheme *)
  67. PROCEDURE MapCode(VAR code : LONGINT);
  68. BEGIN
  69. IF (code >= 126) & (code <= 155) THEN code := OpenType.CharToUnicode[code] END;
  70. END MapCode;
  71. PROCEDURE GetGlyphMap*(code : LONGINT; VAR map : WMGraphics.Image);
  72. VAR g : Glyph; range : LONGINT;
  73. BEGIN
  74. IF FindGlyphRange(code, range) THEN
  75. IF FindGlyph(code, g) THEN
  76. map := g.img
  77. ELSE map := placeholderimg
  78. END
  79. ELSE map := placeholderimg
  80. END
  81. END GetGlyphMap;
  82. PROCEDURE HasChar*(code : LONGINT) : BOOLEAN;
  83. VAR dummy : LONGINT;
  84. BEGIN
  85. RETURN FindGlyphRange(code, dummy)
  86. END HasChar;
  87. PROCEDURE GetGlyphSpacings*(code : LONGINT; VAR glyphSpacings : WMGraphics.GlyphSpacings);
  88. VAR g : Glyph; range : LONGINT;
  89. BEGIN
  90. IF FindGlyphRange(code, range) THEN
  91. IF FindGlyph(code, g) THEN
  92. glyphSpacings.width := g.w;
  93. glyphSpacings.ascent := ascent; glyphSpacings.descent := descent;
  94. glyphSpacings.bearing.l := g.x;
  95. glyphSpacings.bearing.r := g.dx - (g.w + g.x);
  96. glyphSpacings.height := g.h;
  97. glyphSpacings.dy := ascent - g.h - g.y
  98. ELSE glyphSpacings.width := 5; glyphSpacings.height := 15; glyphSpacings.ascent := 16;
  99. END
  100. ELSE
  101. KernelLog.String("code= "); KernelLog.Int(code, 0); KernelLog.String("out of range"); KernelLog.Ln;
  102. glyphSpacings.width := 5; glyphSpacings.height := 15; glyphSpacings.ascent := 16;
  103. END
  104. END GetGlyphSpacings;
  105. PROCEDURE LoadGlyphRange(gri : LONGINT);
  106. VAR i: LONGINT;
  107. BEGIN
  108. IF glyphRanges[gri].glyphs = NIL THEN
  109. NEW(glyphRanges[gri].glyphs, glyphRanges[gri].lastCode - glyphRanges[gri].firstCode + 1);
  110. FOR i := glyphRanges[gri].firstCode TO glyphRanges[gri].lastCode DO
  111. glyphRanges[gri].glyphs[i - glyphRanges[gri].firstCode].code := -1;
  112. END;
  113. END;
  114. (* epxeriment *)
  115. glyphRanges[gri].loaded := TRUE;
  116. RETURN;
  117. FOR i := glyphRanges[gri].firstCode TO glyphRanges[gri].lastCode DO
  118. ReadGlyph(i, glyphRanges[gri].glyphs[i - glyphRanges[gri].firstCode])
  119. END;
  120. glyphRanges[gri].loaded := TRUE;
  121. END LoadGlyphRange;
  122. PROCEDURE FindGlyphRange(code : LONGINT; VAR gri : LONGINT) : BOOLEAN;
  123. VAR a, b, m : LONGINT;
  124. BEGIN
  125. gri := 0;
  126. a := 0; b := LEN(glyphRanges)- 1;
  127. WHILE (a < b) DO m := (a + b) DIV 2;
  128. IF glyphRanges[m].lastCode < code THEN a := m + 1
  129. ELSE b := m
  130. END
  131. END;
  132. IF (glyphRanges[a].firstCode <= code) & (glyphRanges[a].lastCode >= code) THEN
  133. IF ~glyphRanges[a].loaded THEN LoadGlyphRange(a) END;
  134. gri := a; RETURN TRUE
  135. ELSE RETURN FALSE
  136. END
  137. END FindGlyphRange;
  138. PROCEDURE FindGlyph(code : LONGINT; VAR glyph : Glyph) : BOOLEAN;
  139. VAR gri : LONGINT;
  140. BEGIN
  141. IF FindGlyphRange(code, gri) THEN
  142. IF glyphRanges[gri].glyphs[code - glyphRanges[gri].firstCode].code # code THEN
  143. ReadGlyph(code, glyphRanges[gri].glyphs[code - glyphRanges[gri].firstCode])
  144. END;
  145. glyph := glyphRanges[gri].glyphs[code - glyphRanges[gri].firstCode];
  146. RETURN TRUE
  147. ELSE RETURN FALSE
  148. END
  149. END FindGlyph;
  150. PROCEDURE CountGlyphes():LONGINT;
  151. VAR i, c : LONGINT;
  152. BEGIN
  153. FOR i := 0 TO nofGlyphRanges - 1 DO
  154. c := c + glyphRanges[i].lastCode - glyphRanges[i].firstCode + 1;
  155. END;
  156. RETURN c
  157. END CountGlyphes;
  158. PROCEDURE ReadGlyph(code : LONGINT; VAR g : Glyph);
  159. VAR no, dw, dh, y, x: LONGINT; mode: Raster.Mode; data: RasterData;
  160. BEGIN{EXCLUSIVE}
  161. Raster.InitMode(mode, Raster.srcCopy);
  162. no := OpenType.UnicodeToGlyph(ofont, code);
  163. IF Debug THEN KernelLog.String("Reading Glyph Nr: "); KernelLog.Int(no, 0); KernelLog.String(" Code: u"); KernelLog.Hex(code, 4); KernelLog.Ln END;
  164. OpenType.LoadGlyph(inst, glyph, ras, no, {OpenType.Hinted, OpenType.Width , OpenType.Raster, OpenType.Grey});
  165. g.dx := glyph.awx; (* advance *)
  166. g.x := glyph.hbx; (* horizontal bearing x *)
  167. g.y := glyph.hby; (* horizontal bearing y *)
  168. g.w := glyph.rw; (* image width *)
  169. g.h := glyph.rh; (* image height *)
  170. g.code := code;
  171. dh := glyph.rh;
  172. dw := glyph.rw;
  173. IF dw*dh # 0 THEN
  174. IF (cache = NIL) OR (LEN(cache,0) < dh) OR (LEN(cache,1) < dw) THEN
  175. NEW(cache,dh,dw);
  176. END;
  177. data.cache := cache;
  178. data.rect := FillRect;
  179. FOR y := 0 TO dh-1 DO
  180. FOR x := 0 TO dw-1 DO
  181. cache[y,x] := 0X;
  182. END;
  183. END;
  184. OpenType.EnumRaster(ras, data);
  185. NEW(g.img);
  186. Raster.Create(g.img, dw, dh, Raster.A8);
  187. FOR y := 0 TO dh-1 DO
  188. Raster.PutPixels(g.img, 0, dh - y -1, dw, Raster.A8, cache[y], 0, mode);
  189. END;
  190. OpenTypeScan.DisposeRasterizer(ras);
  191. END;
  192. END ReadGlyph;
  193. PROCEDURE Load(filename : ARRAY OF CHAR; size : LONGINT) : BOOLEAN;
  194. VAR i, j, k, ngri, splitCount : LONGINT; res : INTEGER;
  195. ascent, descent : LONGINT;
  196. BEGIN
  197. fontFile := Files.Old(filename); (* open file *)
  198. IF fontFile = NIL THEN RETURN FALSE END;
  199. ofont := OpenType.Open(filename); (* read file *)
  200. IF ofont = NIL THEN KernelLog.String("OT: Could not open Font: "); KernelLog.String(filename); KernelLog.Ln; RETURN FALSE END;
  201. NEW(glyph);
  202. OpenType.InitGlyph(glyph, ofont);
  203. res := ScreenDPI;
  204. OpenType.GetInstance(ofont, 40H*size, res, res, OpenType.Identity, inst); (* get instance *)
  205. IF inst = NIL THEN KernelLog.String("OT: Could not get Instance: "); KernelLog.String(filename); KernelLog.Ln; RETURN FALSE END;
  206. OpenType.GetName(ofont, 1, fname); (* get Name *)
  207. OpenType.GetName(ofont, 2, subfam); (* get SubFamily *)
  208. nofGlyphs := glyph.font.maxp.numGlyphs; (* number of glyphs *)
  209. nofGlyphRanges := 0; (*ofont.cmap.segCount;*) (* number of ranges *)
  210. (* split into ranges of max size 256 *)
  211. ngri := ofont.cmap.segCount;
  212. FOR i := 0 TO ngri - 1 DO
  213. IF (ofont.cmap.seg[i].end # 0) THEN
  214. INC(nofGlyphRanges, 1 + ((ofont.cmap.seg[i].end - ofont.cmap.seg[i].start) MOD 10000H) DIV 100H)
  215. END
  216. END;
  217. NEW(glyphRanges, nofGlyphRanges); i := 0; k := 0;
  218. IF Debug THEN KernelLog.String("-- Building Ranges: "); KernelLog.Int(nofGlyphRanges, 0); KernelLog.Ln END;
  219. WHILE k < ngri DO
  220. IF ofont.cmap.seg[k].end # 0 THEN
  221. splitCount := ((ofont.cmap.seg[k].end - ofont.cmap.seg[k].start) MOD 10000H) DIV 100H; j := 0;
  222. WHILE j < splitCount DO
  223. 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;
  224. 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;
  225. INC(j);
  226. END;
  227. glyphRanges[i+j].firstCode := (ofont.cmap.seg[k].start MOD 10000H) + 100H*splitCount ; glyphRanges[i+j].lastCode := ofont.cmap.seg[k].end MOD 10000H;
  228. 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;
  229. INC(i, splitCount+1);
  230. END;
  231. INC(k)
  232. END;
  233. (* height := inst.font.hhea.ascender + inst.font.hhea.descender + inst.font.hhea.lineGap;
  234. SELF.height := SHORT(OpenTypeInt.MulDiv(height, inst.yppm, 40H*LONG(inst.font.head.unitsPerEm))); (* height *)
  235. *)
  236. ascent := inst.font.hhea.ascender;
  237. SELF.ascent := SHORT(OpenTypeInt.MulDiv(ascent, inst.yppm, 40H*LONG(inst.font.head.unitsPerEm))); (* ascent *)
  238. descent := inst.font.hhea.descender;
  239. SELF.descent := -SHORT(OpenTypeInt.MulDiv(descent, inst.yppm, 40H*LONG(inst.font.head.unitsPerEm))); (* descent *)
  240. RETURN TRUE
  241. END Load;
  242. END Font;
  243. (* ------------------------------------------------- *)
  244. VAR bit: ARRAY 100H, 8 OF BOOLEAN; (* Bit[b, i] means bit i in byte b is set *)
  245. PROCEDURE InitBitTable;
  246. VAR b, i: LONGINT;
  247. BEGIN
  248. FOR b := 0 TO 0FFH DO
  249. FOR i := 0 TO 7 DO
  250. bit[b, i] := ODD(ASH(b, -i))
  251. END
  252. END
  253. END InitBitTable;
  254. PROCEDURE LoadFont(name : ARRAY OF CHAR; size : LONGINT) : Font;
  255. VAR font: Font;
  256. BEGIN
  257. IF Debug THEN KernelLog.String("Loading Font: "); KernelLog.String(name); KernelLog.Ln END;
  258. NEW(font); IF ~font.Load(name, size) THEN RETURN NIL END;
  259. RETURN font
  260. END LoadFont;
  261. PROCEDURE LoadExactFont*(fi : WMFontManager.FontInfo) : WMGraphics.Font;
  262. VAR exactName : ARRAY 256 OF CHAR; f : WMGraphics.Font; try: LONGINT;
  263. BEGIN
  264. try := 0;
  265. LOOP
  266. COPY(fi.name^, exactName);
  267. (* possible suffixes
  268. bold: b, B, bd, Bd, _bd, -Bold
  269. italic: i, I, _i, -Italic
  270. bold+italic: bi, BI, _bi, -BoldItalic
  271. *)
  272. IF WMGraphics.FontBold IN fi.style THEN
  273. IF WMGraphics.FontItalic IN fi.style THEN
  274. CASE try OF
  275. |0: Strings.Append(exactName, "bi");
  276. |1: Strings.Append(exactName, "-BoldItalic");
  277. |2: Strings.Append(exactName, "_bi");
  278. |3: Strings.Append(exactName, "BI");
  279. |4: Strings.Append(exactName, "-BoldOblique");
  280. ELSE EXIT
  281. END
  282. ELSE
  283. CASE try OF
  284. |0: Strings.Append(exactName, "b");
  285. |1: Strings.Append(exactName, "-Bold");
  286. |2: Strings.Append(exactName, "bd");
  287. |3: Strings.Append(exactName, "_bd");
  288. |4: Strings.Append(exactName, "B");
  289. |5: Strings.Append(exactName, "Bd");
  290. ELSE EXIT
  291. END
  292. END;
  293. ELSIF WMGraphics.FontItalic IN fi.style THEN
  294. CASE try OF
  295. |0: Strings.Append(exactName, "i");
  296. |1: Strings.Append(exactName, "-Italic");
  297. |2: Strings.Append(exactName, "_i");
  298. |3: Strings.Append(exactName, "I");
  299. |4: Strings.Append(exactName, "-Oblique");
  300. ELSE
  301. EXIT
  302. END;
  303. ELSE
  304. CASE try OF
  305. 0:;
  306. |1: Strings.Append(exactName, "-Regular");
  307. ELSE
  308. EXIT;
  309. END;
  310. END;
  311. Strings.Append(exactName, ".ttf");
  312. f := LoadFont(exactName, fi.size);
  313. IF f # NIL THEN EXIT END;
  314. INC( try )
  315. END;
  316. IF f # NIL THEN
  317. COPY(fi.name^, f.name);
  318. f.size := fi.size;
  319. f.style := fi.style;
  320. END;
  321. RETURN f
  322. END LoadExactFont;
  323. PROCEDURE LoadApproximateFont*(fi : WMFontManager.FontInfo) : WMGraphics.Font;
  324. VAR exactName : ARRAY 256 OF CHAR; f : WMGraphics.Font;
  325. BEGIN
  326. COPY(fi.name^, exactName);
  327. Strings.Append(exactName, ".ttf");
  328. f := LoadFont(exactName, fi.size);
  329. IF f # NIL THEN
  330. COPY(fi.name^, f.name);
  331. f.size := fi.size;
  332. f.style := fi.style
  333. END;
  334. RETURN f
  335. END LoadApproximateFont;
  336. PROCEDURE MultiTest*;
  337. VAR name : ARRAY 256 OF CHAR; flags : SET; time, date, size : LONGINT;
  338. enumerator : Files.Enumerator;
  339. f : WMGraphics.Font;
  340. BEGIN
  341. NEW(enumerator);
  342. enumerator.Open("*.ttf", {});
  343. KernelLog.String("*** TrueType MultiTester v0.1 ***"); KernelLog.Ln;
  344. WHILE enumerator.HasMoreEntries() DO
  345. IF enumerator.GetEntry(name, flags, time, date, size) THEN
  346. KernelLog.String(" Testing File: "); KernelLog.String(name);
  347. f := LoadFont(name, 40);
  348. IF f # NIL THEN
  349. KernelLog.String(" all ok")
  350. ELSE
  351. KernelLog.String(" failed")
  352. END;
  353. KernelLog.Ln
  354. END
  355. END;
  356. KernelLog.String("*** all done ***"); KernelLog.Ln;
  357. enumerator.Close;
  358. END MultiTest;
  359. BEGIN
  360. InitBitTable
  361. END WMOTFonts.
  362. --------------------------------------------
  363. System.Free WMOTFonts~
  364. WMOTFonts.MultiTest~