Last active
May 6, 2024 00:18
-
-
Save dohyunkim/007eb8d8b25804b5322c14bbefbc30d2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\documentclass{article} | |
\usepackage{fontspec} | |
\setmainfont{TeX Gyre Pagella} | |
%\setmainfont{Noto Sans CJK KR} | |
\usepackage{luamplib} | |
\mplibsetformat{metafun} | |
\directlua{ mplibglyph = require'mplibglyph' } | |
\begin{document} | |
\mpfig* | |
def mplibglyph expr c of f = | |
runscript ( | |
"return mplibglyph.glyph('" | |
& if numeric f: decimal fi f | |
& "','" | |
& if numeric c: decimal fi c | |
& "')" | |
) | |
enddef; | |
def mplibdrawglyph expr g = | |
draw image( | |
save i; numeric i; i:=0; | |
for item within g: | |
i := i+1; | |
fill pathpart item | |
if i < length g: withpostscript "collect" fi; | |
endfor | |
) | |
enddef; | |
\endmpfig | |
\mpfig | |
picture g; | |
% g := mplibglyph "Dcaron" of \fontid\font scaled .2; % current font | |
% g := mplibglyph "g" of "TU/TeXGyrePagella(0)/m/n/10" scaled .2; | |
% g := mplibglyph "똠" of "NotoSansCJKkr-Regular.otf" scaled .15; | |
% g := mplibglyph 50 of "Times.ttc(1)" scaled .2; | |
g := mplibglyph "Q" of "Times.ttc(2)" scaled .2; | |
fill bbox g xscaled 2 withcolor .7[red,white]; | |
draw g shifted (xpart lrcorner g, 0); | |
mplibdrawglyph g withcolor .7white; | |
path p; | |
i:=0; | |
for item within g: | |
i:=i+1; | |
p := pathpart item; | |
drawarrow p withcolor if turningnumber p > 0: red else: blue fi; | |
label(decimal i, 5 unitvector direction 0 of p) shifted point 0 of p; | |
endfor | |
\endmpfig | |
\mpfig | |
picture Q, u, e; | |
Q = g; | |
u := mplibglyph "u" of "Times.ttc(2)" scaled .2 shifted lrcorner Q; | |
e := mplibglyph "e" of "Times.ttc(2)" scaled .2 shifted lrcorner u; | |
i:=0; | |
totallen := length Q + length u + length e; | |
for pic=Q, u, e: | |
for item within pic: | |
i:=i+1; | |
fill pathpart item | |
if i < totallen: withpostscript "collect"; fi | |
endfor | |
endfor | |
withshademethod "linear" | |
withshadedirection (0.5,2.5) | |
withshadecolors (.7red,.7yellow) | |
; | |
\endmpfig | |
\end{document} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local format = string.format | |
local insert = table.insert | |
local unpack = table.unpack | |
local concat = table.concat | |
local function getangle (a,b,c) | |
local r = math.deg(math.atan(c.y-b.y, c.x-b.x) - math.atan(b.y-a.y, b.x-a.x)) | |
if r > 180 then | |
r = r - 360 | |
elseif r < -180 then | |
r = r + 360 | |
end | |
return r | |
end | |
local function turning (t) | |
local r, n = 0, #t | |
for i=1,2 do | |
insert(t, t[i]) | |
end | |
for i=1,n do | |
r = r + getangle(t[i], t[i+1], t[i+2]) | |
end | |
return r/360 | |
end | |
local function err (str) | |
return format('hide(errmessage "%s")', str) | |
end | |
local function glpaths(t, fmt) | |
local q,p,r = {{},{}} | |
for i,v in ipairs(t) do | |
local cmd = v[#v] | |
if cmd == "m" then | |
p = {format('(%s,%s)',unpack(v))} | |
r = {{x=v[1],y=v[2]}} | |
else | |
local nt = t[i+1] | |
local last = not nt or nt[#nt] == "m" | |
if cmd == "l" then | |
local pt = t[i-1] | |
local seco = pt[#pt] == "m" | |
if (last or seco) and r[1].x == v[1] and r[1].y == v[2] then | |
else | |
insert(p, format('--(%s,%s)',unpack(v))) | |
insert(r, {x=v[1],y=v[2]}) | |
end | |
if last then | |
insert(p, '--cycle') | |
end | |
elseif cmd == "c" then | |
insert(p, format('..controls(%s,%s)and(%s,%s)',unpack(v))) | |
if last and r[1].x == v[5] and r[1].y == v[6] then | |
insert(p, '..cycle') | |
else | |
insert(p, format('..(%s,%s)',v[5],v[6])) | |
if last then | |
insert(p, '--cycle') | |
end | |
insert(r, {x=v[5],y=v[6]}) | |
end | |
else | |
return err"unknown operator" | |
end | |
if last then | |
insert(q[ turning(r) > 0 and 1 or 2 ], concat(p)) | |
end | |
end | |
end | |
r = { } | |
if fmt == "opentype" then | |
for _,v in ipairs(q[1]) do | |
insert(r, format('addto currentpicture contour %s;',v)) | |
end | |
for _,v in ipairs(q[2]) do | |
insert(r, format('addto currentpicture contour %s withcolor background;',v)) | |
end | |
else | |
for _,v in ipairs(q[2]) do | |
insert(r, format('addto currentpicture contour %s;',v)) | |
end | |
for _,v in ipairs(q[1]) do | |
insert(r, format('addto currentpicture contour %s withcolor background;',v)) | |
end | |
end | |
return format('image(%s)', concat(r)) | |
end | |
if not file then require"lualibs" end | |
local function mplibglyph(f, c) | |
local filename, subfont, instance, kind, shapedata, cachedir | |
local fid = tonumber(f) or font.id(f) -- string: fontname | |
if fid > 0 then | |
local fontdata = font.getfont(fid) | |
filename, subfont, kind = fontdata.filename, fontdata.subfont, fontdata.format | |
instance = fontdata.specification and fontdata.specification.instance | |
else | |
local name | |
f = f:match"^%s*(.+)%s*$" | |
name, subfont, instance = f:match"(.+)%((%d+)%)%[(.-)%]$" | |
if not name then | |
name, instance = f:match"(.+)%[(.-)%]$" -- SourceHanSansK-VF.otf[Heavy] | |
end | |
if not name then | |
name, subfont = f:match"(.+)%((%d+)%)$" -- Times.ttc(2) | |
end | |
name = name or f | |
subfont = (subfont or 0)+1 | |
instance = instance and instance:lower() | |
for _,ftype in ipairs{"opentype", "truetype"} do | |
filename = kpse.find_file(name, ftype.." fonts") | |
if filename then | |
kind = ftype; break | |
end | |
end | |
end | |
if kind ~= "opentype" and kind ~= "truetype" then | |
f = fid and fid > 0 and tex.fontname(fid) or f | |
if kpse.find_file(f, "tfm") then | |
c = tonumber(c) or format("%q",c) | |
return 'glyph '..c..' of "'..f..'"' | |
else | |
return err"font not found" | |
end | |
end | |
for _,dir in ipairs{ kpse.var_value"TEXMFVAR", "." } do | |
if dir and dir ~= "" then | |
dir = format("%s/luamplib_cache", dir) | |
if not lfs.isdir(dir) then | |
lfs.mkdirp(dir) | |
end | |
if file.is_writable(dir) then | |
cachedir = dir | |
break | |
end | |
end | |
end | |
local time = lfs.attributes(filename).modification | |
local newname = format("%s/shapes_%s%s%s.lua", | |
cachedir, | |
filename:gsub("%W","_"), | |
subfont and subfont > 1 and format("(%i)",subfont) or "", | |
instance and instance ~= "" and format("[%s]",instance) or "") | |
local newattr = lfs.attributes(newname) | |
local newtime = newattr and newattr.modification or 0 | |
if time == newtime then | |
shapedata = require(newname) | |
end | |
if not shapedata then | |
shapedata = fonts.handlers.otf.readers.loadshapes(filename,subfont,instance) | |
table.tofile(newname, shapedata, "return") | |
lfs.touch(newname, time, time) | |
end | |
local gid = tonumber(c) | |
if not gid then | |
local codepoint = utf8.codepoint(c) | |
for i,v in ipairs(shapedata.glyphs) do | |
if c == v.name or codepoint == v.unicode then | |
gid = i | |
break | |
end | |
end | |
end | |
if not gid then return err"cannot get glyph id" end | |
local fac = 1000 / (shapedata.units or 1000) | |
local t = shapedata.glyphs[gid].segments | |
if not t then return err"glyph has no contour. Maybe blank space" end | |
for i,v in ipairs(t) do | |
if type(v) == "table" then | |
for ii,vv in ipairs(v) do | |
if type(vv) == "number" then | |
t[i][ii] = format("%.0f", vv * fac) | |
end | |
end | |
end | |
end | |
return glpaths(t, kind) | |
end | |
return { | |
glyph = mplibglyph, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment