user 3.14159
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.)
Anonymous 2084
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)

Enter question or answer id or url (and optionally further answer ids/urls from the same question) from

Separate each id/url with a space. No need to list your own answers; they will be imported automatically.