Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

This question led to a new package:
spath3

(Technically, this question led to a subpackage of the spath3 package; the spath3 package provides some foundations on which a TikZ library calligraphy was built and the TikZ library is bundled with the spath3 library.)

While there are many fine fonts available, occasionally it would be nice to simulate the effects you can get 'by hand' with a poster nib (for a fountain pen). This would let you create a number of 'special effects' easily. Is there an easy way to do this (I guess featuring Tikz).

share|improve this question
1  
An easy way out would be to use fonts that look like drawn with poster nibs, and use fontspec with xelatex. Since that is probably not what you want, I can't say I quite understand what you want to achieve. A picture/illustration would be very helpful. –  ipavlic Apr 28 '11 at 21:42
    
@ipavlic: See chat.stackexchange.com/transcript/message/902455#902455 for the context to this question! –  Joseph Wright Apr 28 '11 at 21:50
    
I see. I guess the devil does find work for idle hands to do :). –  ipavlic Apr 28 '11 at 22:11
    
The inspirational images from chat, since they're not properly displayed there anymore: i.stack.imgur.com/SMJFm.png, i.stack.imgur.com/qI852.png, i.stack.imgur.com/ZFqVp.png, i.stack.imgur.com/bEacF.png –  doncherry Nov 2 '11 at 14:27
add comment

1 Answer

up vote 25 down vote accepted

Update This is now available on CTAN (see above). Bugfixes and improvements will generally be available on LaunchPad before being uploaded to CTAN. See below for the instructions on getting the files from LaunchPad, but note that the relevant .dtx file is now: spath3.dtx. This generates all the necessary files.


Update: I've added this to the TeX-SX package which was announced at What are your favourite TikZ/PGF answers?. The dtx can be downloaded from http://bazaar.launchpad.net/+branch/tex-sx/files. Run pdflatex on it to produce the sty file and the documentation.


Hmm, I guess you're looking for something a bit like this:

calligraphic TeX

It may not be easy, but I'll have a go ...

\documentclass{standalone}
\usepackage{tikz}

%%% TODO:
%
% 1. Be able to define a number of different pens and use appropriately
% 2. Add an annotation style (arrows with numbers)
% 3. Check how this interacts with colours and so forth

\makeatletter
\long\def\ge@addto@macro#1#2{%
  \begingroup
  \toks@\expandafter\expandafter\expandafter{\expandafter#1#2}%
  \xdef#1{\the\toks@}%
  \endgroup}

\long\def\ge@addbefore@macro#1#2{%
  \begingroup
  \toks@\expandafter\expandafter\expandafter{\expandafter#2#1}%
  \xdef#1{\the\toks@}%
  \endgroup}

\long\def\ge@addbefore@macro#1#2{%
  \begingroup
  \toks@\expandafter\expandafter\expandafter{\expandafter#2#1}%
  \xdef#1{\the\toks@}%
  \endgroup}

\long\def\g@addbefore@macro#1#2{%
  \begingroup
    \toks@\expandafter{\expandafter#2#1}%
    \xdef#1{\the\toks@}%
  \endgroup}

\pgfkeys{/tikz/store path in/.code={\pgfsyssoftpath@getcurrentpath{\my@path}\global\let#1=\my@path}}

\pgfkeys{/tikz/define pen/.style={preaction={store path in=\penpath}}}

\def\pen{\path[define pen]}
\def\calligraphy{\path[calligraphy]}

\pgfkeys{/tikz/calligraphy/.style={preaction={store path in=\calligraphypath},preaction={stroke with calligraphy pen=\penpath}}}
\pgfkeys{/tikz/stroke with calligraphy pen/.code={\thickenpath{\calligraphypath}{#1}}}

\def\thickenpartialpath#1#2{%
  \def\thick@path{}%
  \def\thick@action##1##2##3{%
    \edef\this@action{\string##1}%
    \ifx\this@action\cal@moveto
    \ifx\thick@path\@empty
    \else
     \lengthofsoftpath{\thick@path}
     \ifnum\value{softpathlength}=1
      \thickenpartialpathwithstroke{#1}{\thick@path}
     \else
      \thickenpartialpathwithfill{#1}{\thick@path}
     \fi
     \def\thick@path{}%
    \fi
    \fi
    \def\to@add{##1{##2}{##3}}
  }
  \let\thick@append=\ge@addto@macro
  \expandafter\walksoftpath\expandafter\thick@path\expandafter\thick@action\expandafter\thick@append#2\relax
  \lengthofsoftpath{\thick@path}
  \ifnum\value{softpathlength}=1
  \thickenpartialpathwithstroke{#1}{\thick@path}
  \else
  \thickenpartialpathwithfill{#1}{\thick@path}
  \fi
}

\def\thickenpartialpathwithfill#1#2{%
  \startsoftpath{#2}
  \let\pen@sx=\tr@xlen
  \let\pen@sy=\tr@ylen
  \endsoftpath{#2}
  \let\pen@ex=\tr@xlen
  \let\pen@ey=\tr@ylen
  \startsoftpath{#1}
  \let\path@sx=\tr@xlen
  \let\path@sy=\tr@ylen
  \endsoftpath{#1}
  \let\path@ex=\tr@xlen
  \let\path@ey=\tr@ylen
  \translatesoftpath{#1}{\pen@sx}{\pen@sy}%
  \let\lower@path=\trpath
  \translatesoftpath{#2}{\path@ex}{\path@ey}
  \let\right@path=\trpath
  \reversesoftpath{#1}
  \translatesoftpath{\revpath}{\pen@ex}{\pen@ey}
  \let\upper@path=\trpath
  \reversesoftpath{#2}
  \translatesoftpath{\revpath}{\path@sx}{\path@sy}
  \let\left@path=\trpath
  \catsoftpath{\lower@path}{\right@path}
  \catsoftpath{\catpath}{\upper@path}
  \catsoftpath{\catpath}{\left@path}
  \def\to@add{\pgfsyssoftpath@closepath{0pt}{0pt}}
  \ge@addto@macro\catpath\to@add
  \pgfsyssoftpath@setcurrentpath{\catpath}
  \pgfsyssoftpath@flushcurrentpath
  \pgfusepath{fill}
}

\def\thickenpartialpathwithstroke#1#2{%
  \startsoftpath{#2}
  \let\pen@sx=\tr@xlen
  \let\pen@sy=\tr@ylen
  \translatesoftpath{#1}{\pen@sx}{\pen@sy}%
  \pgfsyssoftpath@setcurrentpath{\trpath}
  \pgfsyssoftpath@flushcurrentpath
  \pgfusepath{stroke}
}

\def\catsoftpath#1#2{
  \let\catpath=#1
  \expandafter\trimfirst#2\relax
  \ge@addto@macro\catpath\trimed@path
}

\def\trimfirst#1#2#3#4\relax{%
  \edef\this@action{\string#1}%
  \ifx\this@action\cal@moveto
  \def\trimed@path{#4}%
  \else
  \def\trimed@path{#1{#2}{#3}#4}%
  \fi
}

\def\walksoftpath#1#2#3#4{
  \let\path@cmd=#4%
  \ifx\path@cmd\relax
  \let\next@action=\@gobblefour
  \else
  \let\next@action=\modifypath
  \fi
  \next@action{#1}{#2}{#3}{#4}%
}

\def\modifypath#1#2#3#4#5#6{%
  #2{#4}{#5}{#6}
  #3#1\to@add
  \walksoftpath{#1}{#2}{#3}}

\newcounter{softpathlength}
\def\lengthofsoftpath#1{%
  \def\len@path{}%
  \setcounter{softpathlength}{0}%
  \def\len@action##1##2##3{%
    \stepcounter{softpathlength}%
    \edef\to@add{}%
  }
  \let\len@append=\ge@addto@macro
  \expandafter\walksoftpath\expandafter\len@path\expandafter\len@action\expandafter\len@append#1\relax

}

\def\translatesoftpath#1#2#3{%
  \def\tr@path{}%
  \def\tr@action##1##2##3{%
    \pgfmathsetmacro{\tr@xlen}{##2+#2}
    \pgfmathsetmacro{\tr@ylen}{##3+#3}
    \edef\to@add{\noexpand##1{\tr@xlen pt}{\tr@ylen pt}}
  }
  \let\tr@append=\ge@addto@macro
  \expandafter\walksoftpath\expandafter\tr@path\expandafter\tr@action\expandafter\tr@append#1\relax
  \global\let\trpath=\tr@path
}

\def\trimlast#1\pgfsyssoftpath@movetotoken\relax{#1}

\foreach \cal@cpt in {
  moveto,
  lineto,
  curvetosupporta,
  curvetosupportb,
  curveto,
  rectcorner,
  rectsize%
} {
\expandafter\xdef\csname cal@\cal@cpt\endcsname{\expandafter\string\csname pgfsyssoftpath@\cal@cpt token\endcsname}
}
\edef\cal@closepath{\string\pgfsyssoftpath@closepath}

\def\reversesoftpath#1{%
  \def\re@path{}%
  \def\re@action##1##2##3{%
    \edef\this@re@action{\string##1}
    \ifx\this@re@action\cal@curvetosupporta
     \edef\to@add{\noexpand\pgfsyssoftpath@curvetosupportbtoken{##2}{##3}\noexpand\pgfsyssoftpath@curvetotoken}
    \else
     \ifx\this@re@action\cal@curvetosupportb
      \edef\to@add{\noexpand\pgfsyssoftpath@curvetosupportatoken{##2}{##3}}
     \else
      \ifx\this@re@action\cal@curveto
       \edef\to@add{{##2}{##3}}
      \else
       \edef\to@add{{##2}{##3}\noexpand##1}
      \fi
     \fi
    \fi
    }
  \let\re@append=\ge@addbefore@macro
  \expandafter\walksoftpath\expandafter\re@path\expandafter\re@action\expandafter\re@append#1\relax
  \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\re@path\expandafter\expandafter\expandafter{\expandafter\trimlast\re@path\relax}
  \g@addbefore@macro\re@path\pgfsyssoftpath@movetotoken
  \global\let\revpath=\re@path
}

\def\startsoftpath#1{%
  \def\st@path{}%
  \def\st@action##1##2##3{%
    \def\tr@xlen{##2}%
    \def\tr@ylen{##3}%
    \def\st@action####1####2####3{}
  }
  \let\st@append=\ge@addto@macro
  \expandafter\walksoftpath\expandafter\st@path\expandafter\st@action\expandafter\st@append#1\relax
}

\def\endsoftpath#1{%
  \def\end@path{}%
  \def\end@action##1##2##3{%
    \def\tr@xlen{##2}%
    \def\tr@ylen{##3}%
  }
  \let\end@append=\ge@addto@macro
  \expandafter\walksoftpath\expandafter\end@path\expandafter\end@action\expandafter\end@append#1\relax
}

\def\thickenpath#1#2{%
  \def\th@path{}%
  \def\th@action##1##2##3{%
    \edef\this@action{\string##1}
    \ifx\this@action\cal@moveto
     \ifx\th@path\@empty
    \else
      \thickenpartialpath\th@path{#2}
     \fi
    \def\th@path{}
    \fi
    \def\to@add{##1{##2}{##3}}
  }
  \let\th@append=\ge@addto@macro
  \expandafter\walksoftpath\expandafter\th@path\expandafter\th@action\expandafter\th@append#1\relax
  \thickenpartialpath\th@path{#2}
}

\makeatother

\begin{document}
\begin{tikzpicture}
\pen (-.25,-.125) -- (0,0) ++(.125,.0625) -- (.25,.125);
\calligraphy (0,0) .. controls +(.5,.5) and +(-.5,-.5) .. ++(2,0) (1,0) -- ++(0,-2) -- ++(-.125,-.125) .. controls +(.1,.1) and +(-.1,-.1) .. ++(.35,0);
\calligraphy (3,-1) .. controls +(-1.5,1) and +(-1.5,-1) .. ++(0,-2) ++(-1.1,1) -- ++(1,0) -- ++(-.25,-.25);
\calligraphy (3.25,0) .. controls +(.5,.5) and +(-.5,-.5) .. ++(1.5,-2) ++(0,2.5) .. controls +(-.75,.75) and +(.75,-.75) .. ++(-1.5,-3);
\calligraphy (0,-4) .. controls +(1,1) and +(-1,-1) .. ++(5,0);
\end{tikzpicture}
\end{document}

First, the history. I enjoy calligraphy, I like producing documents in TeX, and lately with the addition of TikZ I've started enjoying producing graphical stuff so the combination of calligraphy with TikZ has been at the back of my mind for a long time. There have been a few times when reading a question here that I've though "I could do that with a calligraphy pen.". Also, when I found that Inkscape had a calligraphy tool then I thought it would be great in combination with a graphics tablet, but I was never very happy with the result - they weren't elegant enough to replace a hand-drawn one, nor precise enough to have the benefits of being computer-rendered (though being able to make a screencast can be useful: see this page for such a screencast, also by looking at the source of the SVGs you can see that Inkscape's method for doing calligraphy is ... not elegant.).

Anyway, I was idly perusing the PGF manual when I came across the concept of soft paths and realised that this was the key to doing calligraphy. Basically, (see the manual for more details) when TikZ/PGF constructs a path then it does so in several layers and at each step one can interrupt the process to mess with the data so far before passing it on to the next. One step is a soft path. This is the path in basic form, so once all coordinates have been computed and all funny paths rendered into their more basic components (so circles are replaced by bezier curves and so forth), but it is not yet "baked hard" so can still be manipulated.

The above script steps in at this point and says, "That path you've just constructed, rather than render it as is, I'm going to 'thicken' it.". In detail, it takes the path together with another path (called the "pen") and forms a (cartesian) product of the two. This then produces a region which it fills. The effect is as if the pen had been dragged along the path. By using quite complicated pens, quite complicated effects can be achieved.

Most of the time, this filling method produces exactly the desired effect. Where it fails is where the path turns a corner tangential to the pen path. If one were truly dragging a pen along the path, this corner would be filled, but because this method works by translation, that doesn't happen (an example in the picture is the top of the "X" on the right). However, when doing calligraphy, one should never push the pen and drawing a stroke where the path turns tangential to the pen would involve pushing the pen (so the off-diagonal of the "X" should really be three strokes: the main path and the two ends).

Note that once I've packaged this into something (and one reason for prompting Joseph to ask this question was to justify including it in the proposed "TikZ from TeX-SX" package!), the usage will be something like:

\pen (-.25,-.125) -- (0,0) ++(.125,.0625) -- (.25,.125);
\calligraphy (0,0) .. controls +(.5,.5) and +(-.5,-.5) .. ++(2,0) (1,0) -- ++(0,-2) -- ++(-.125,-.125) .. controls +(.1,.1) and +(-.1,-.1) .. ++(.35,0);
\calligraphy (3,-1) .. controls +(-1.5,1) and +(-1.5,-1) .. ++(0,-2) ++(-1.1,1) -- ++(1,0) -- ++(-.25,-.25);
\calligraphy (3.25,0) .. controls +(.5,.5) and +(-.5,-.5) .. ++(1.5,-2) ++(0,2.5) .. controls +(-.75,.75) and +(.75,-.75) .. ++(-1.5,-3);
\calligraphy (0,-4) .. controls +(1,1) and +(-1,-1) .. ++(5,0);

which is really quite simple.

Apart from the fun of "Hey, look what I just managed to do!", I also wanted to put this somewhere public because I think that this ability to manipulate the soft path is quite useful for other effects. It's something like decorations, but not quite, and the concepts that I've identified here will be useful whenever there is a situation where one wants to say, "That path there, I'd like to use it, but in a weird way.". In particular, doing something more than once with the same path, but modifying the path slightly for each action.

share|improve this answer
2  
Wow, that's a pile of TeX macros :P. I love TikZ, but I have been messing around with Metapost a bit and it has a really nice syntax for achieving this sort of effect. See Section 1.11 "Pens" in the Metafun manual for an example of calligraphy. If you could replicate a similar setup in PGF that would be awesome! –  Sharpie May 1 '11 at 18:19
    
@Sharpie: Interesting. Some comments: "You should look at pens in the way an artist does. He follows a shape and in doing so he or she twists the pen (and thereby the nib) and puts more or less pressure on it." (apart from the bizarre pronoun choices ...). I'm a calligrapher and that description is not true for most pens that I use. The angle and pressure are kept the same, with flourishes added later (the main exception is copperplate where the pressure changes). (ctd) –  Andrew Stacey May 5 '11 at 13:04
    
@Sharpie: Also, looking at the diagram at the top of p38, the result of the implementation is pretty much the same as mine. In particular, where the ellipse goes around the tangent then the picture does not match what a real calligraphy pen would produce (neither does mine, and both fail in the same way - though I don't know the reason why the metafun one fails as I haven't looked at the code). But nice to see a different implementation all the same (and nice to see that it suffers the same failings as mine!). –  Andrew Stacey May 5 '11 at 13:07
    
Like it; very nice. –  Marc van Dongen Dec 30 '11 at 8:59
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.