diff options
Diffstat (limited to 'tex/context/base/mkxl/mlib-fnt.lmt')
-rw-r--r-- | tex/context/base/mkxl/mlib-fnt.lmt | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/tex/context/base/mkxl/mlib-fnt.lmt b/tex/context/base/mkxl/mlib-fnt.lmt new file mode 100644 index 000000000..e53ded38a --- /dev/null +++ b/tex/context/base/mkxl/mlib-fnt.lmt @@ -0,0 +1,548 @@ +if not modules then modules = { } end modules ['font-mps'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local type, tonumber, tostring = type, tonumber, tostring +local concat, insert, remove = table.concat, table.insert, table.remove +local formatters, match = string.formatters, string.match +local utfbyte = utf.byte + +-- QP0 [QP1] QP2 => CP0 [CP1 CP2] CP3 + +-- CP0 = QP0 +-- CP3 = QP2 +-- +-- CP1 = QP0 + 2/3 *(QP1-QP0) +-- CP2 = QP2 + 2/3 *(QP1-QP2) + +fonts = fonts or { } +local metapost = fonts.metapost or { } +fonts.metapost = metapost + +local f_moveto = formatters["(%N,%N)"] +local f_lineto = formatters["--(%N,%N)"] +local f_curveto = formatters["..controls(%N,%N)and(%N,%N)..(%N,%N)"] +local s_cycle = "--cycle" + +local f_nofill = formatters["nofill %s;"] +local f_dofill = formatters["fill %s;"] + +local f_draw_trace = formatters["drawpathonly %s;"] +local f_draw = formatters["draw %s;"] + +local f_rectangle = formatters["((%N,%N)--(%N,%N)--(%N,%N)--(%N,%N)--cycle)"] +local f_line = formatters["((%N,%N)--(%N,%N))"] + +function metapost.boundingbox(d,factor) + local bounds = d.boundingbox + local factor = factor or 1 + local llx = factor*bounds[1] + local lly = factor*bounds[2] + local urx = factor*bounds[3] + local ury = factor*bounds[4] + return f_rectangle(llx,lly,urx,lly,urx,ury,llx,ury) +end + +function metapost.baseline(d,factor) + local bounds = d.boundingbox + local factor = factor or 1 + local llx = factor*bounds[1] + local urx = factor*bounds[3] + return f_line(llx,0,urx,0) +end + +function metapost.widthline(d,factor) + local bounds = d.boundingbox + local factor = factor or 1 + local lly = factor*bounds[2] + local ury = factor*bounds[4] + local width = factor*d.width + return f_line(width,lly,width,ury) +end + +function metapost.zeroline(d,factor) + local bounds = d.boundingbox + local factor = factor or 1 + local lly = factor*bounds[2] + local ury = factor*bounds[4] + return f_line(0,lly,0,ury) +end + +function metapost.paths(d,xfactor,yfactor) + local sequence = d.sequence + local segments = d.segments + local list = { } + local path = { } -- recycled + local size = 0 + local xfactor = xfactor or 1 + local yfactor = yfactor or xfactor + if sequence then + local i = 1 + local n = #sequence + while i < n do + local operator = sequence[i] + if operator == "m" then -- "moveto" + if size > 0 then + size = size + 1 + path[size] = s_cycle + list[#list+1] = concat(path,"",1,size) + size = 1 + else + size = size + 1 + end + path[size] = f_moveto(xfactor*sequence[i+1],yfactor*sequence[i+2]) + i = i + 3 + elseif operator == "l" then -- "lineto" + size = size + 1 + path[size] = f_lineto(xfactor*sequence[i+1],yfactor*sequence[i+2]) + i = i + 3 + elseif operator == "c" then -- "curveto" + size = size + 1 + path[size] = f_curveto(xfactor*sequence[i+1],yfactor*sequence[i+2],xfactor*sequence[i+3],yfactor*sequence[i+4],xfactor*sequence[i+5],yfactor*sequence[i+6]) + i = i + 7 + elseif operator =="q" then -- "quadraticto" + size = size + 1 + -- first is always a moveto + local l_x = xfactor*sequence[i-2] + local l_y = yfactor*sequence[i-1] + local m_x = xfactor*sequence[i+1] + local m_y = yfactor*sequence[i+2] + local r_x = xfactor*sequence[i+3] + local r_y = yfactor*sequence[i+4] + path[size] = f_curveto ( + l_x + 2/3 * (m_x-l_x), + l_y + 2/3 * (m_y-l_y), + r_x + 2/3 * (m_x-r_x), + r_y + 2/3 * (m_y-r_y), + r_x, r_y + ) + i = i + 5 + else + -- weird + i = i + 1 + end + end + elseif segments then +-- inspect(segments) + for i=1,#segments do + local segment = segments[i] + local operator = segment[#segment] + if operator == "m" then -- "moveto" + if size > 0 then + size = size + 1 + path[size] = s_cycle + list[#list+1] = concat(path,"",1,size) + size = 1 + else + size = size + 1 + end + path[size] = f_moveto(xfactor*segment[1],yfactor*segment[2]) + elseif operator == "l" then -- "lineto" + size = size + 1 + path[size] = f_lineto(xfactor*segment[1],yfactor*segment[2]) + elseif operator == "c" then -- "curveto" + size = size + 1 + path[size] = f_curveto(xfactor*segment[1],yfactor*segment[2],xfactor*segment[3],yfactor*segment[4],xfactor*segment[5],yfactor*segment[6]) + elseif operator == "q" then -- "quadraticto" + size = size + 1 + -- first is always a moveto + local prev = segments[i-1] + local l_x = xfactor*prev[#prev-2] + local l_y = yfactor*prev[#prev-1] + local m_x = xfactor*segment[1] + local m_y = yfactor*segment[2] + local r_x = xfactor*segment[3] + local r_y = yfactor*segment[4] + path[size] = f_curveto ( + l_x + 2/3 * (m_x-l_x), + l_y + 2/3 * (m_y-l_y), + r_x + 2/3 * (m_x-r_x), + r_y + 2/3 * (m_y-r_y), + r_x, r_y + ) + else + -- weird + end + end + else + return + end + if size > 0 then + size = size + 1 + path[size] = s_cycle + list[#list+1] = concat(path,"",1,size) + end + return list +end + +function metapost.fill(paths) + local r = { } + local n = #paths + for i=1,n do + if i < n then + r[i] = f_nofill(paths[i]) + else + r[i] = f_dofill(paths[i]) + end + end + return concat(r) +end + +function metapost.draw(paths,trace) + local r = { } + local n = #paths + for i=1,n do + if trace then + r[i] = f_draw_trace(paths[i]) + else + r[i] = f_draw(paths[i]) + end + end + return concat(r) +end + +function metapost.maxbounds(data,index,factor) + local maxbounds = data.maxbounds + local factor = factor or 1 + local glyphs = data.glyphs + local glyph = glyphs[index] + local boundingbox = glyph.boundingbox + local xmin, ymin, xmax, ymax + if not maxbounds then + xmin = 0 + ymin = 0 + xmax = 0 + ymax = 0 + for i=1,#glyphs do + local d = glyphs[i] + if d then + local b = d.boundingbox + if b then + if b[1] < xmin then xmin = b[1] end + if b[2] < ymin then ymin = b[2] end + if b[3] > xmax then xmax = b[3] end + if b[4] > ymax then ymax = b[4] end + end + end + end + maxbounds = { xmin, ymin, xmax, ymax } + data.maxbounds = maxbounds + else + xmin = maxbounds[1] + ymin = maxbounds[2] + xmax = maxbounds[3] + ymax = maxbounds[4] + end + local llx = boundingbox[1] + local lly = boundingbox[2] + local urx = boundingbox[3] + local ury = boundingbox[4] + local width = glyph.width + if llx > 0 then + llx = 0 + end + if width > urx then + urx = width + end + return f_rectangle( + factor*llx,factor*ymin, + factor*urx,factor*ymin, + factor*urx,factor*ymax, + factor*llx,factor*ymax + ) +end + +-- This is a nice example of tex, metapost and lua working in tandem. Each kicks in at the +-- right time. It's probably why I like watching https://www.youtube.com/watch?v=c5FqpddnJmc +-- so much: precisely (and perfectly) timed too. + +local texgetbox = tex.getbox + +local nodecodes = nodes.nodecodes -- no nuts yet +local rulecodes = nodes.rulecodes + +local rule_code = nodecodes.rule + +local normalrule_code = rulecodes.normal +local outlinerule_code = rulecodes.outline +local userrule_code = rulecodes.user +local emptyrule_code = rulecodes.empty + +local nuts = nodes.nuts +local getwhd = nuts.getwhd +local getexpansion = nuts.getexpansion +local getscales = nuts.getscales +local isglyph = nuts.isglyph + +local fonthashes = fonts.hashes +local fontcharacters = fonthashes.characters +local fontparameters = fonthashes.parameters +local fontshapes = fonthashes.shapes +local fontdescriptions = fonthashes.descriptions + +local topaths = metapost.paths + +local f_text = formatters["mfun_do_outline_text_flush(%q,%i,%N,%N,%q)(%,t);"] +local f_rule = formatters["mfun_do_outline_rule_flush(%q,%N,%N,%N,%N);"] +local f_bounds = formatters["checkbounds(%N,%N,%N,%N);"] +local s_nothing = "(origin scaled 10)" + +local sc = 10 +local fc = number.dimenfactors.bp + +local function glyph(kind,font,char,advance,shift,ex,s, sx,sy) + local character = fontcharacters[font][char] + if character then + local index = character.index + if index then + local shapedata = fontshapes[font] + local glyphs = shapedata.glyphs + if glyphs then + local glyf = glyphs[index] + if glyf then + local units = 1000 -- factor already takes shapedata.units into account + local yfactor = (sc/units) * fontparameters[font].factor / 655.36 + local xfactor = yfactor + local shift = shift or 0 + local advance = advance or 0 + local exfactor = ex or 0 + local wfactor = 1 + local detail = kind == "p" and tostring(char) or "" + if exfactor ~= 0 then + wfactor = (1+(ex/units)/1000) + xfactor = xfactor * wfactor + end + if s then + xfactor = (s/1000) * ((sx or 1000)/1000) * xfactor + yfactor = (s/1000) * ((sy or 1000)/1000) * yfactor + end + local paths = topaths(glyf,xfactor,yfactor) + if paths then + return f_text(kind,#paths,advance,shift,detail,paths) -- , character.width * fc * wfactor + end + end + end + end + end +end + +metapost.glyph = glyph + +local kind = "" +local buffer = { } +local b = 0 + +local function reset() + buffer = { } + b = 0 +end + +local function flushcharacter(current, pos_h, pos_v, pod_r, font, char) + local char, font = isglyph(current) + local s, sx, sy = getscales(current) + local code = glyph(kind,font,char,pos_h*fc,pos_v*fc,getexpansion(current),s,sx,sy) + if code then + b = b + 1 + buffer[b] = code + end +end + +local function flushrule(current, pos_h, pos_v, pos_r, size_h, size_v, subtype) + if subtype == normalrule_code then + b = b + 1 + buffer[b] = f_rule(kind,pos_h*fc,pos_v*fc,size_h*fc,size_v*fc) + elseif subtype == outlinerule_code then + b = b + 1 + buffer[b] = f_rule("d",pos_h*fc,pos_v*fc,size_h*fc,size_v*fc) + elseif subtype == userrule_code then + -- print("USER RULE") + -- b = b + 1 + -- buffer[b] = f_rule("d",size_h*fc,size_v*fc,pos_h*fc,pos_v*fc) + elseif subtype == emptyrule_code then + -- ignore + else + -- b = b + 1 + -- buffer[b] = f_rule("f",pos_h*fc,pos_v*fc,size_h*fc,size_v*fc) + end +end + +local function flushsimplerule(pos_h, pos_v, pos_r, size_h, size_v) + flushrule(false,pos_h,pos_v,pos_r,size_h,size_v,normalrule_code) +end + +local function flushspecialrule(pos_h, pos_v, pos_r, w, h, d, l, outline) + flushrule(false,pos_h,pos_v-d,pos_r,w,h+d,outline and outlinerule_code or normalrule_code) +end + +-- installer + +drivers.install { + name = "mpo", + actions = { + initialize = function() + reset() + end, + finalize = function(driver,details) + local bb = details.boundingbox + local llx = bb[1] * fc + local lly = bb[2] * fc + local urx = bb[3] * fc + local ury = bb[4] * fc + b = b + 1 + buffer[b] = f_bounds(llx,lly,urx,ury) + -- inspect(buffer) + end, + }, + flushers = { + updatefontstate = updatefontstate, + character = flushcharacter, + rule = flushrule, + simplerule = flushsimplerule, + specialrule = flushspecialrule, + } +} + +function metapost.boxtomp(n,k) + kind = k + nodes.handlers.finalizebox(n) + drivers.converters.lmtx(drivers.instances.mpo,texgetbox(n),"box",1) + local result = concat(buffer,";") + reset() + return result +end + +-- This is a new set of commands: + +local loaded = table.setmetatableindex(function(t,k) + local v = fonts.definers.internal({ name = k } ,"<lmt:glyphshape:font>") + t[k] = v + return v +end) + +local mpdata = 0 +local mpstack = { } + +function mp.lmt_glyphshape_start(id,character) + if type(id) == "string" then + id = loaded[id] + end + local fontid = (id and id ~= 0 and id) or font.current() + local shapedata = fontshapes [fontid] -- by index + local characters = fontcharacters [fontid] -- by unicode + local descriptions = fontdescriptions[fontid] -- by unicode + local mathgaps = mathematics.gaps -- not yet loaded + local shapeglyphs = shapedata.glyphs or { } + if type(character) == "string" and character ~= "" then + local hex = match(character,"^0x(.+)") + if hex then + character = tonumber(hex,16) + else + character = utfbyte(character) + end + else + character = tonumber(character) + end + local unicode = mathgaps[character] or character + local chardata = characters[unicode] + local descdata = descriptions[unicode] + if chardata then + glyph = shapeglyphs[chardata.index] + if glyph then + mpdata = glyph.mpdata + if not mpdata then + if glyph.segments or glyph.sequence then + local units = shapedata.units or 1000 + local factor = 100/units + local width = (descdata.width or 0) * factor + local height = descdata.boundingbox[4] * factor + local depth = descdata.boundingbox[2] * factor + local math = descdata.math + local italic = (math and math.italic or 0) * factor + local accent = (math and math.accent or 0) * factor + mpdata = { + paths = metapost.paths(glyph,factor), + boundingbox = metapost.boundingbox(glyph,factor), + baseline = metapost.baseline(glyph,factor), + width = width, + height = height, + depth = depth, + italic = italic, + accent = accent, + usedbox = f_rectangle(0,depth,width,depth,width,height,0,height), + usedline = f_line(0,0,width,0), + } + glyph.mpdata = mpdata + else + print("CHECK 1",id,character) + end + end + end + else + print("CHECK 2",id,character) + end + insert(mpstack, mpdata) +end + +local mpprint = mp.print +local injectpair = mp.inject.pair +local injectnumeric = mp.inject.numeric + +function mp.lmt_glyphshape_stop() + mpdata = remove(mpstack) +end + +function mp.lmt_glyphshape_n() + if mpdata then + mpprint(#mpdata.paths) + else + injectnumeric(0) + end +end + +function mp.lmt_glyphshape_path(i) + if mpdata then + mpprint(mpdata.paths[i]) + else + injectpair(0,0) + end +end + +function mp.lmt_glyphshape_boundingbox() + if mpdata then + mpprint(mpdata.boundingbox) + else + injectpair(0,0) + end +end +function mp.lmt_glyphshape_usedbox() + if mpdata then + mpprint(mpdata.usedbox) + else + injectpair(0,0) + end +end + +function mp.lmt_glyphshape_baseline() + if mpdata then + mpprint(mpdata.baseline) + else + injectpair(0,0) + end +end +function mp.lmt_glyphshape_usedline() + if mpdata then + mpprint(mpdata.usedline) + else + injectpair(0,0) + end +end + +function mp.lmt_glyphshape_width () injectnumeric(mpdata and mpdata.width or 0) end +function mp.lmt_glyphshape_depth () injectnumeric(mpdata and mpdata.depth or 0) end +function mp.lmt_glyphshape_height() injectnumeric(mpdata and mpdata.height or 0) end +function mp.lmt_glyphshape_italic() injectnumeric(mpdata and mpdata.italic or 0) end +function mp.lmt_glyphshape_accent() injectnumeric(mpdata and mpdata.accent or 0) end |