I am wondering if one can get the outlines of glyphs somehow into Ti*k*Z without too much hassle. In asymptote and to some extent in PSTricks this is possible, see e.g. https://tex.stackexchange.com/a/21593. (IMHO in PSTricks this is sort of a cheat, but I do not want to discuss this here.) In Ti*k*Z it seems to be possible only with tremendous effort, see e.g. https://tex.stackexchange.com/a/21594. However, this information is almost a decade old. So I am hoping that there are now more user-friendly methods available, ideally as easy to use as in asymptote. Why am I interested in this? One reason is that there is the perspective library, another reason is that there are generic nonlinear transformations. It would be really gorgeous if one could apply them to glyphs in Ti*k*Z. To be clear, what I am not interested in is something that can also be achieved with path fading. I really want to have an explicit path that can be transformed in an any way a path can be transformed, without limitations. And, of course, the more user-friendly, the better. What have I tried? Not much that I can present here. So no MWE, sorry. I could copy one from the links above, but do not see what this will add. (This is not because I am too lazy to forge an MWE, but I really do not see its purpose here.)

The present answer builds upon two good answers from Stack Exchange: - https://tex.stackexchange.com/a/451951 - https://tex.stackexchange.com/a/605893 Maybe Tikz can add a library for this. ``` \documentclass{article} \usepackage{fontspec} \usepackage{tikz} \usepackage{luacode} \begin{luacode*} local concat = table.concat local function f_moveto(...) return string.format("\\pgfsyssoftpath@movetotoken{%fpt}{%fpt}", ...) end local function f_lineto(...) return string.format("\\pgfsyssoftpath@linetotoken{%fpt}{%fpt}", ...) end local function f_curveto(...) return string.format( "\\pgfsyssoftpath@curvetosupportatoken{%fpt}{%fpt}" .. "\\pgfsyssoftpath@curvetosupportbtoken{%fpt}{%fpt}" .. "\\pgfsyssoftpath@curvetotoken{%fpt}{%fpt}", ...) end local s_cycle = "\\pgfsyssoftpath@closepath" -- from font-mps.lua local function glyph_to_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 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 -- from meta-imp-outlines.mkiv -- That's a simple reimplemetation of ConTeXt's \showshape macro function outlinepaths(character) local fontid = font.current() local shapedata = fonts.hashes.shapes[fontid] -- by index local chardata = fonts.hashes.characters[fontid] -- by unicode local shapeglyphs = shapedata.glyphs or { } character = utf.byte(character) local c = chardata[character] if c then if not c.index then return {} end local glyph = shapeglyphs[c.index] if glyph and (glyph.segments or glyph.sequence) then local units = shapedata.units or 1000 local factor = 100/units local paths = glyph_to_paths(glyph,factor) return paths end end end \end{luacode*} \begin{document} \makeatletter \directlua{ for i, path in ipairs(outlinepaths("A")) do token.set_macro("path" .. string.char(string.byte("A") + i - 1), path) end } \makeatother \begin{tikzpicture} \draw[use path=\pathA]; \draw[use path=\pathB]; \end{tikzpicture} \end{document} ``` Boring output is ![Screenshot](/image?hash=18f71734ca4d3b05f7a56bac9adca005b0c63e125c71a4dc642d8d1da5c077a3)