login

<     >

2021-04-25 11:08:37 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

TTF: add string rendering with kerning, anchoring & rotation

diff --git a/ttf.lua b/ttf.lua
index a2894f0..9399d91 100644
--- a/ttf.lua
+++ b/ttf.lua
@@ -1,5 +1,7 @@
 local bit = require "bit"
 
+local aff = require "aff"
+
 local bnot = bit.bnot
 local bor, band = bit.bor, bit.band
 local lshift, rshift =  bit.lshift,  bit.rshift
@@ -335,27 +337,20 @@ function Face:char_index(code)
     end
 end
 
--- used in Face:glyph(); resolution in DPI
-function Face:set_size(resolution, point_size)
-    self.scale = point_size * resolution / (72 * self.units_per_em)
-end
-
 -- helper for Face:glyph()
 function Face:pack_outline(points, end_points)
     local outline = {}
-    local s = self.scale
-    local h = self.ascent
+    local h = self.cap_height
     local p, q
     local j = 1
     for i = 1, #end_points do
         local contour = {}
         while j <= end_points[i] do
             p = points[j]
-            q = {p.x*s, (h-p.y)*s, p.on_curve}
+            q = {p.x, h-p.y, p.on_curve}
             table.insert(contour, q)
             j = j + 1
         end
-        table.insert(contour, contour[1]) -- close contour
         table.insert(outline, contour)
     end
     return outline
@@ -530,6 +525,62 @@ function Face:glyph(id)
     return self:pack_outline(points, end_points)
 end
 
+function Face:string(s, pt, x, y, anchor, a)
+    anchor = anchor or "tl"
+    a = a or 0
+    local codes = utf8to32(s)
+    local cur_x = 0
+    local contours = {}
+    local outline
+    local li, ri
+    local advance, bearing
+    for i, code in ipairs(codes) do
+        ri = self:char_index(code)
+        if i > 1 and self.num_kernings > 0 then
+            cur_x = cur_x + self:get_kerning(li, ri)
+        end
+        outline = self:glyph(ri)
+        for j, contour in ipairs(outline) do
+            for k, point in ipairs(contour) do
+                point[1] = point[1] + cur_x
+            end
+            table.insert(contours, contour)
+        end
+        advance, bearing = self:hmetrics(ri)
+        cur_x = cur_x + advance
+        li = ri
+    end
+    local ax, ay -- anchor position
+    local av, ah = anchor:sub(1, 1), anchor:sub(2, 2)
+    if av == "t" then
+        ay = 0
+    elseif av == "m" then
+        ay = self.cap_height / 2
+    elseif av == "b" then
+        ay = self.cap_height
+    end
+    if ah == "l" then
+        ax = 0
+    elseif ah == "c" then
+        ax = cur_x / 2
+    elseif ah == "r" then
+        ax = cur_x
+    end
+    if ax == nil or ay == nil then
+        error("invalid anchor: "..anchor)
+    end
+    local scl = pt * self.resolution / (72 * self.units_per_em)
+    local t = aff.new_affine()
+    t:add_translate(-ax, -ay)
+    t:add_scale(scl)
+    t:add_rotate(a)
+    t:add_translate(x, y)
+    for i, contour in ipairs(contours) do
+        t:apply(contour)
+    end
+    return contours
+end
+
 local function load_face(f)
     if type(f) == "string" then f = io.open(f, "rb") end
     local self = setmetatable({fp=f}, Face)
@@ -554,10 +605,8 @@ local function load_face(f)
     else
         log("no x-height and Cap-Height (OS/2)")
     end
+    self.resolution = 300 -- dpi
     return self
 end
 
-return {
-    utf8to32=utf8to32,
-    load_face=load_face
-}
+return {load_face=load_face}