0
$\begingroup$

There are a few questions related to laying out strings of text along curves, Text along a lemniscate curve, How can I wrap text around a circle? and others.

See the GIST.

Most answers either layout the Characters of a string in order, or use a neat trick of importing the text's "Outlines" from a string-exported pdf of the text. I'm trying to layout some nonlinear text (i.e. integrals, sub/superscripts) along a curve. The former strategy certainly fails: while Characters@"\[Integral]" is fine, something like

Characters@"\!\(\*UnderoverscriptBox[\(\[Integral]\), \(0\), \(1\)]\)"

returns what is typed above. Upon reflection, it's weird that mma allows you to input such strings in non-verbose form (with escintesc, ctrl4 0 ctrl5 1). I certainly don't want to layout that input string along a curve. The latter strategy fails, perhaps in a machine dependent way, because of export/import inconsistencies. This is understandable; PDF is a complex file format, and many renderable PDFs are rejected by mma. Consider

expimpas[fmt_:"PDF",str_:"\!\(\*OverscriptBox[UnderscriptBox[\(\[Sum]\),\(n=1\)],
\(\[Infinity]\)]\)"]:=Graphics@Cases[ImportString[ExportString[str,fmt],
"TextMode"->"Outlines"],FilledCurve[a__]:>FilledCurve[a],\[Infinity]];expimpas[]

On my machine this throws Unknown font type /CIDFontType0. Obviously there are weird glyphs involved. This is helped with EPS instead of PDF

expimpas["EPS"]

Unfortunately, EPS still fails with some nonlinear boxes

expimpas["EPS","\!\(\*UnderoverscriptBox[\(\[Integral]\),\(0\),\(1\)]\)"]

I see two good ways to proceed. Strategy (3): render text as an image, and warp the image along a curve. IMO this is slightly nasty, I have an example at the end. Stragegy (4): import vector graphics in an effective and consistent way. I wrote a very nasty SVG parser, and exporting SVG does fine (you can't import SVG, at least in 12.1). It shouldn't be too hard to import the SVG as Bezier curves and warp these.

Sorry for going on and on.

GIST

Hypothetical best approach (5): grab $(x,y)$ coordinates or Graphics primitives from an expression (with MakeBoxes or something like that), and warp those so that they lie along a curve. This has to be possible without exiting the Wolfram Language, right? No rasterization, exporting or nothing. Of course, custom and proper vector importing might be nice if you want to directly grab EPS from $\LaTeX$ (I'm interested in this too).

APPENDIX

Example approach (3)

ParametricPlot[-{Sin@u, Cos@u} v, {u, 0, 2 \[Pi]}, {v, 5, 10}, 
PlotStyle -> {Opacity@1, 
Texture@Rasterize[
Style["\!\(\*OverscriptBox[UnderscriptBox[\(\[Sum]\), \(n = \
1\)], \(\[Infinity]\)]\)\!\(\*OverscriptBox[UnderscriptBox[\(\
\[Integral]\), \(0\)], \(1\)]\)\!\(\*OverscriptBox[UnderscriptBox[\(\
\[Integral]\), \(0\)], \(1\)]\)(-x y\!\(\*SuperscriptBox[\()\), \(n - \
1\)]\)\[DifferentialD]y\[DifferentialD]x=\!\(\*OverscriptBox[\
UnderscriptBox[\(\[Integral]\), \(0\)], \
\(1\)]\)\!\(\*OverscriptBox[UnderscriptBox[\(\[Integral]\), \(0\)], \
\(1\)]\)\!\(\*FractionBox[\(\[DifferentialD]y \[DifferentialD]x\), \
\(1 + x\\\ \
y\)]\)=2\!\(\*OverscriptBox[UnderscriptBox[\(\[Integral]\), \
\(-\*FractionBox[\(1\), \(2\)]\)], FractionBox[\(1\), \
\(2\)]]\)\!\(\*OverscriptBox[UnderscriptBox[\(\[Integral]\), \(\
\[LeftBracketingBar]\[Alpha]\[RightBracketingBar]\)], \(1 - \
\[LeftBracketingBar]\[Alpha]\[RightBracketingBar]\)]\)\!\(\*\
FractionBox[\(\[DifferentialD]\[Beta] \[DifferentialD]\[Alpha]\), \(1 \
+ \((\[Alpha] + \[Beta])\) \((\[Beta] - \
\[Alpha])\)\)]\)=4\!\(\*OverscriptBox[UnderscriptBox[\(\[Integral]\), \
\(0\)], FractionBox[\(1\), \
\(2\)]]\)\!\(\*OverscriptBox[UnderscriptBox[\(\[Integral]\), \(\
\[Alpha]\)], \(1 - \[Alpha]\)]\)\!\(\*FractionBox[\(\[DifferentialD]\
\[Beta] \[DifferentialD]\[Alpha]\), \(1 + \
\*SuperscriptBox[\(\[Beta]\), \(2\)] - \*SuperscriptBox[\(\[Alpha]\), \
\(2\)]\)]\)", FontFamily -> "Courier", Bold, Black], 
RasterSize -> 1000]}]

Example SVG parser towards (4), incomplete

With[{poormansvg = 
Function[dstr, 
Module[{allcommands = (Partition[#, UpTo@2] & /@
If[First@# == {}, Rest@#, #] &@
Function[list, 
TakeList[list, 
Prepend[Append[Differences@#, All], First@# - 1]] &[
Position[list, "M" | "m"] /. {e_} :> e]]@
StringSplit[
dstr, # -> # & /@ 
Characters@"MmLlHhVvZzQqTtAaCc"]) /. {a_String, 
b_String} :> 
Prepend[StringCases[b, x : NumberString :> ToExpression@x], 
a]}, Rest@
FoldList[
Function[{lastlist, pathcommands}, 
Function[nearlycomplete, 
nearlycomplete /. "z" -> First@nearlycomplete]@
Rest@FoldList[
Switch[First@#2, "M" | "L", Rest@#2, 
"m" | "l", #1 + Rest@#2, "H", {Last@#2, Last@#1}, 
"h", #1 + {Last@#2, 0}, "V", {First@#1, Last@#2}, 
"v", #1 + {0, Last@#2}, 
"C" | "Q", {Last@Most@#2, Last@#2}, 
"c" | "q", #1 + {Last@Most@#2, Last@#2}, "Z" | "z", 
"z"] &, Last@lastlist, pathcommands]], {{0, 0}}, 
allcommands]]], 
svgstrings = 
First[StringSplit[#, "\""]] & /@ 
Rest@StringSplit[
ExportString[
"\!\(\*OverscriptBox[UnderscriptBox[\(\[Sum]\), \(n = 1\)], \(\
\[Infinity]\)]\)\!\(\*FractionBox[SuperscriptBox[\((\(-1\))\), \
\(n\)], \(-\*SuperscriptBox[\(n\), \
\(2\)]\)]\)=\!\(\*OverscriptBox[UnderscriptBox[\(\[Sum]\), \(n = \
1\)], \(\[Infinity]\)]\)\!\(\*OverscriptBox[UnderscriptBox[\(\
\[Integral]\), \(0\)], \(1\)]\)\!\(\*OverscriptBox[UnderscriptBox[\(\
\[Integral]\), \(0\)], \(1\)]\)(-x y\!\(\*SuperscriptBox[\()\), \(n - \
1\)]\)\[DifferentialD]y\[DifferentialD]x=\!\(\*OverscriptBox[\
UnderscriptBox[\(\[Integral]\), \(0\)], \
\(1\)]\)\!\(\*OverscriptBox[UnderscriptBox[\(\[Integral]\), \(0\)], \
\(1\)]\)\!\(\*FractionBox[\(\[DifferentialD]y \[DifferentialD]x\), \
\(1 + x\\\ y\)]\)", "svg"], " d=\""]}, 
ListLinePlot[(poormansvg@#) /. {{x_ /; NumericQ@x, 
y_ /; NumericQ@y} :> {x, -y}}, AspectRatio -> 1] & /@ 
svgstrings]

Not all SVG commands are handled here, and cubics and quadratics are of course just treated as straight lines. Also there's some craziness with referencing <def>'d paths that I haven't begun to implement (shouldn't be too bad since importing xml gives rules). Works alright though.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.