Advertisement
IncludeUrl

OCBF

May 1st, 2018
435
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 4.40 KB | None | 0 0
  1. local unicode = require("unicode")
  2. local fs = require("filesystem")
  3.  
  4. local charpattern = utf8 and utf8.charpattern or "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"
  5.  
  6. local function readstr(f)
  7.   local count, reason = f:read(1)
  8.   if not count then return nil, reason or "unexpected EOF" end
  9.  
  10.   count = string.unpack(">B", count)
  11.   local data, reason = f:read(count)
  12.   if not data or #data ~= count then return nil, reason or "unexpected EOF" end
  13.  
  14.   return data
  15. end
  16.  
  17. local function readchr(f)
  18.   local c1, reason = f:read(1)
  19.   if not c1 then return nil, reason or "unexpected EOF" end
  20.  
  21.   local ctr, c = -1, math.max(c1:byte(), 128)
  22.  
  23.   repeat
  24.     ctr = ctr + 1
  25.     c = (c - 128) * 2
  26.   until c < 128
  27.  
  28.   local crest, reason = f:read(ctr)
  29.   if not crest or #crest ~= ctr then return nil, reason or "unexpected EOF" end
  30.  
  31.   return c1 .. crest
  32. end
  33.  
  34. local fontBase = {}
  35. fontBase.__index = fontBase
  36.  
  37. local function loadOCBF(path)
  38.   local font = setmetatable({sizes = {}}, fontBase)
  39.  
  40.   local f, reason = io.open(path, "rb")
  41.   if not f then return nil, reason end
  42.  
  43.   if f:read(4) ~= "ocbf" then return nil, "bad signature" end
  44.  
  45.   font.family, reason = readstr(f)
  46.   if not font.family then return nil, reason end
  47.  
  48.   font.style, reason = readstr(f)
  49.   if not font.style then return nil, reason end
  50.  
  51.   while true do
  52.     local char = readchr(f)
  53.     if not char then break end
  54.  
  55.     local sizewidth, reason = f:read(2)
  56.     if not sizewidth or #sizewidth ~= 2 then
  57.       return nil, reason or "unexpected EOF"
  58.     end
  59.  
  60.     local size, width = string.unpack(">BB", sizewidth)
  61.     local len = math.ceil(size * width / 8)
  62.  
  63.     local data = f:read(len)
  64.     if not data or #data ~= len then return nil, reason or "unexpected EOF" end
  65.  
  66.     if not font.sizes[size] then font.sizes[size] = {} end
  67.     font.sizes[size][char] = {}
  68.     font.sizes[size][char] = {width = width, data = data}
  69.   end
  70.  
  71.   f:close()
  72.   return font
  73. end
  74.  
  75. function fontBase:drawChar(set, size, char, x, y)
  76.   checkArg(1, set, "function")
  77.   checkArg(2, size, "number")
  78.   checkArg(3, char, "string")
  79.   checkArg(4, x, "number")
  80.   checkArg(5, y, "number")
  81.  
  82.   if not self.sizes[size] then
  83.     error("no " .. tostring(size) .. " size in ocbf font", 2)
  84.   end
  85.  
  86.   local char = self.sizes[size][char]
  87.   local i = 0
  88.  
  89.   for by = 0, size - 1 do
  90.     for bx = 0, char.width - 1 do
  91.       local bytei = math.floor((by * char.width + bx) / 8)
  92.       local biti = 7 - (by * char.width + bx) % 8
  93.       local byte = string.byte(char.data:sub(bytei + 1, bytei + 1))
  94.  
  95.       if byte >> biti & 1 == 1 then
  96.         set(bx + x, by + y, 1)
  97.       else
  98.         set(bx + x, by + y, 0)
  99.       end
  100.       i = i + 1
  101.     end
  102.   end
  103. end
  104.  
  105. function fontBase:draw(set, size, str, x, y)
  106.   checkArg(1, set, "function")
  107.   checkArg(2, size, "number")
  108.   checkArg(3, str, "string")
  109.   checkArg(4, x, "number")
  110.   checkArg(5, y, "number")
  111.  
  112.   if not self.sizes[size] then
  113.     error("no " .. tostring(size) .. " size in ocbf font", 2)
  114.   end
  115.  
  116.   local ix = x
  117.   for i = 1, unicode.len(str) do
  118.     local char = unicode.sub(str, i, i)
  119.     if char == "\n" then
  120.       x = ix
  121.       y = y + size
  122.     else
  123.       self:drawChar(set, size, char, x, y)
  124.       x = x + self.sizes[size][char].width
  125.     end
  126.   end
  127. end
  128.  
  129. function fontBase:width(size, str)
  130.   checkArg(1, size, "number")
  131.   checkArg(2, str, "string")
  132.  
  133.   local width = 0
  134.  
  135.   for i = 1, unicode.len(str) do
  136.     local char = unicode.sub(str, i, i)
  137.     width = width + self.sizes[size][char].width
  138.   end
  139.  
  140.   return width
  141. end
  142.  
  143. function fontBase:__tostring()
  144.   local out = "font: " .. self.family .. " " .. self.style .. " ["
  145.   local i = 1
  146.  
  147.   for size in pairs(self.sizes) do
  148.     out = out .. (i == 1 and "" or ", ") .. tostring(size)
  149.     i = i + 1
  150.   end
  151.  
  152.   return out .. "]"
  153. end
  154.  
  155. local ocbf = {}
  156.  
  157. ocbf.path = "/usr/share/fonts:/home/.fonts:."
  158.  
  159. function ocbf.load(family, style)
  160.   checkArg(1, family, "string")
  161.   checkArg(2, style, "string", "nil")
  162.  
  163.   local path
  164.  
  165.   if family:sub(-5, -1) == "ocbf" then
  166.     path = family
  167.   else
  168.     path = ocbf.search(family, style)
  169.   end
  170.  
  171.   if not path then return nil end
  172.  
  173.   return loadOCBF(path)
  174. end
  175.  
  176. function ocbf.search(family, style)
  177.   checkArg(1, family, "string")
  178.   checkArg(2, style, "string")
  179.  
  180.   for path in ocbf.path:gmatch("[^:]+") do
  181.     path = fs.concat(path, family, style .. ".ocbf")
  182.     if fs.exists(path) then
  183.       return path
  184.     end
  185.   end
  186. end
  187.  
  188. return ocbf
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement