Updated version:
This version works also with characters that consist of several parts or even with several characters.
er[tr_, x_] := (1 - 2*Boole[PositivelyOrientedPoints[#]]) x*
Normalize[#[[2]] - TriangleCenter[#, "Incenter"]] + #[[2]] &@tr
ff2[ch_, e_, pts_, pts1_, pts2_] :=
Block[{n1, n2},
n1 = er[#, e] & /@ Partition[pts[[pts1]], 3, 1, 1] // RotateRight;
n2 = (er[#, -e] & /@ Partition[pts[[#]], 3, 1, 1] //
RotateRight) & /@ pts2;
{Polygon[
Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[#]], 2, 1,
1], Reverse /@
Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[#]], 2, 1,
1]}]] & /@ pts2,
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[pts1]], 2,
1, 1], Reverse /@
Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[pts1]], 2, 1, 1]}]],
If[n2 === {}, Polygon[(Append[#, 0] + {0, 0, -e} & /@ n1)],
Polygon[(Append[#, 0] + {0, 0, -e} & /@
n1) -> ((Append[#, 0] + {0, 0, -e} & /@ #) & /@ n2)]],
If[n2 === {}, Polygon[(Append[#, 0] + {0, 0, 1 + e} & /@ n1)],
Polygon[(Append[#, 0] + {0, 0, 1 + e} & /@
n1) -> ((Append[#, 0] + {0, 0, 1 + e} & /@ #) & /@ n2)]],
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[#[[1]]]],
2, 1, 1],
Reverse /@
Partition[Append[#, 0] + {0, 0, -e} & /@ #[[2]], 2, 1,
1]}]] & /@ Thread[{pts2, n2}],
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[#[[1]]]],
2, 1, 1],
Reverse /@
Partition[Append[#, 0] + {0, 0, 1 + e} & /@ #[[2]], 2, 1,
1]}]] & /@ Thread[{pts2, n2}],
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[pts1]], 2,
1, 1], Reverse /@
Partition[Append[#, 0] + {0, 0, -e} & /@ n1, 2, 1, 1]}]],
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[pts1]], 2,
1, 1], Reverse /@
Partition[Append[#, 0] + {0, 0, 1 + e} & /@ n1, 2, 1, 1]}]]}]
ff3[ch_, e_] :=
Block[{r, data, pts},
r = BoundaryDiscretizeGraphics[
Text[Style[ch, FontFamily -> "Helvetica"]], _Text];
data =
BoundaryDiscretizeGraphics[
Text[Style[ch, FontFamily -> "Helvetica"]], _Text] // Show;
pts = data[[1, 1]];
ff2[ch, e,
pts, #[[1]], #[[2]]] & /@ (Join[{First@#,
Rest@#} & /@ (Cases[#, Line[x__] :> x, All] & /@
Cases[data, _FilledCurve, All]),
If[# === {}, {},
Append[{#}, {}] & /@ #[[1]]] &@((Cases[data,
Polygon[x__] :> x, All]))])
]
Examples:
Graphics3D[ff3["‰", 0.2], Boxed -> False]
Graphics3D[ff3["i~", 0.2], Boxed -> False]
Graphics3D[ff3["8€", 0.2], Boxed -> False]
Graphics3D[ff3["φπ", 0.2], Boxed -> False]

Old version:
This does not work with characters that consist of several parts like "i", or %. For those characters the mesh should be divided into pieces and code executed separately for each piece.
er[tr_, x_] := (1 - 2*Boole[PositivelyOrientedPoints[#]]) x*
Normalize[#[[2]] - TriangleCenter[#, "Incenter"]] + #[[2]] &@tr
ff[ch_, e_] :=
Block[{r, data, pts, pts1, pts2, n1, n2},
r = BoundaryDiscretizeGraphics[
Text[Style[ch, FontFamily -> "Helvetica"]], _Text];
data =
ToExpression[
StringReplace[ToString[r, InputForm],
"BoundaryMeshRegion" -> "List"]];
pts = data[[1]];
pts1 = DeleteDuplicates[Flatten[List @@ data[[2, 1]]]];
pts2 = DeleteDuplicates[Flatten[List @@ #]] & /@ data[[3 ;; -2, 1]];
n1 = er[#, e] & /@ Partition[pts[[pts1]], 3, 1, 1] // RotateRight;
n2 = (er[#, -e] & /@ Partition[pts[[#]], 3, 1, 1] //
RotateRight) & /@ pts2;
{Polygon[
Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[#]], 2, 1,
1], Reverse /@
Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[#]], 2, 1,
1]}]] & /@ pts2,
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[pts1]], 2,
1, 1], Reverse /@
Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[pts1]], 2, 1, 1]}]],
If[n2 === {}, Polygon[(Append[#, 0] + {0, 0, -e} & /@ n1)],
Polygon[(Append[#, 0] + {0, 0, -e} & /@
n1) -> ((Append[#, 0] + {0, 0, -e} & /@ #) & /@ n2)]],
If[n2 === {}, Polygon[(Append[#, 0] + {0, 0, 1 + e} & /@ n1)],
Polygon[(Append[#, 0] + {0, 0, 1 + e} & /@
n1) -> ((Append[#, 0] + {0, 0, 1 + e} & /@ #) & /@ n2)]],
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[#[[1]]]],
2, 1, 1],
Reverse /@
Partition[Append[#, 0] + {0, 0, -e} & /@ #[[2]], 2, 1,
1]}]] & /@ Thread[{pts2, n2}],
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[#[[1]]]],
2, 1, 1],
Reverse /@
Partition[Append[#, 0] + {0, 0, 1 + e} & /@ #[[2]], 2, 1,
1]}]] & /@ Thread[{pts2, n2}],
Polygon[Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 0} & /@ pts[[pts1]], 2,
1, 1], Reverse /@
Partition[Append[#, 0] + {0, 0, -e} & /@ n1, 2, 1, 1]}]],
Polygon[
Flatten[#, 1] & /@
Thread[{Partition[Append[#, 0] + {0, 0, 1} & /@ pts[[pts1]], 2,
1, 1], Reverse /@
Partition[Append[#, 0] + {0, 0, 1 + e} & /@ n1, 2, 1, 1]}]]}]
Here are some examples:
Graphics3D[ff["a", 0.2], Boxed -> False]
Graphics3D[ff["@", 0.2], Boxed -> False]
Graphics3D[ff["B", 0.2], Boxed -> False]
Graphics3D[ff["Q", 0.2], Boxed -> False]
Graphics3D[ff["&", 0.2], Boxed -> False]
Graphics3D[ff["#", 0.2], Boxed -> False]
Graphics3D[ff["§", 0.2], Boxed -> False]
Graphics3D[ff["y", 0.2], Boxed -> False]


FindShortestCurvein Scope > Mesh regions, then last example (3D). $\endgroup$FindShortestTour. $\endgroup$