3

I want to slightly modify pgflibraryshapes.callouts.code.tex to create a small tikz callout package that solves Tikz callout: positioning start of pointer.

I think I have located the single relevant line

        \pgfmultipartnode{coordinate}{center}{pgf@lib@callout@pointer}

but I do not know how to modify it, since I do not understand the very short description of pgfmultipartnode in the pgf manual, and do not understand the parameters.

So: How do I need to modify the code line to position the start of the pointer? Optimally, I want a parameter that fixes where the pointer reaches the callout (e.g. north, north west, west, ....) or even simpler: always west, since that's all I need for the start.


Detail: Here is the beginning of pgflibraryshapes.callouts.code.tex, which should contain all relevant information:

% Copyright 2007 by Mark Wibrow
% This file may be distributed and/or modified
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
% See the file doc/generic/pgf/licenses/LICENSE for more details.

\usepgflibrary{shapes.symbols}

% Keys for callouts
%
% Common to all callouts:
%   /pgf/callout absolute pointer
%   /pgf/callout relative pointer
%
% ellipse callout only:
%   /pgf/callout pointer arc    
%
% rectangle callout only:    
%   /pgf/callout pointer width      
%
% cloud callout only:
%   /pgf/callout pointer start size
%   /pgf/callout pointer end size
%   /pgf/callout pointer segments
%
\newif\ifpgf@lib@callout@absolutepointer
\pgfkeys{/pgf/.cd,
    callout pointer arc/.initial=15,
    callout pointer width/.initial=.25cm,
    callout pointer start size/.initial=.2 of callout,
    callout pointer end size/.initial=.1 of callout,
    callout pointer segments/.initial=2,
    callout absolute pointer/.code={\pgf@lib@callout@makeabsolutepointer{#1}},
    callout relative pointer/.code={\pgf@lib@callout@makerelativepointer{#1}},
    callout pointer shorten/.initial=0cm
}



\def\pgf@lib@callout@makeabsolutepointer#1{%
    \pgf@lib@callout@absolutepointertrue%
    {%
        \pgftransformshift{#1}%
        \pgfmultipartnode{coordinate}{center}{pgf@lib@callout@pointer}{}%
    }%
}

\def\pgf@lib@callout@makerelativepointer#1{%
    \pgf@lib@callout@absolutepointerfalse%
    \def\pgf@lib@callout@relativepointer{#1}%
}
\pgfkeys{/pgf/callout relative pointer=\pgfpointpolar{300}{.5cm}}

% Copyright 2007 by Mark Wibrow
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
%
% See the file doc/generic/pgf/licenses/LICENSE for more details.

\usepgflibrary{shapes.symbols}

% Keys for callouts
%
% Common to all callouts:
%   /pgf/callout absolute pointer
%   /pgf/callout relative pointer
%
% ellipse callout only:
%   /pgf/callout pointer arc    
%
% rectangle callout only:    
%   /pgf/callout pointer width      
%
% cloud callout only:
%   /pgf/callout pointer start size
%   /pgf/callout pointer end size
%   /pgf/callout pointer segments
%
\newif\ifpgf@lib@callout@absolutepointer
\pgfkeys{/pgf/.cd,
    callout pointer arc/.initial=15,
    callout pointer width/.initial=.25cm,
    callout pointer start size/.initial=.2 of callout,
    callout pointer end size/.initial=.1 of callout,
    callout pointer segments/.initial=2,
    callout absolute pointer/.code={\pgf@lib@callout@makeabsolutepointer{#1}},
    callout relative pointer/.code={\pgf@lib@callout@makerelativepointer{#1}},
    callout pointer shorten/.initial=0cm
}



\def\pgf@lib@callout@makeabsolutepointer#1{%
    \pgf@lib@callout@absolutepointertrue%
    {%
        \pgftransformshift{#1}%
        \pgfmultipartnode{coordinate}{center}{pgf@lib@callout@pointer}{}%
    }%
}

\def\pgf@lib@callout@makerelativepointer#1{%
    \pgf@lib@callout@absolutepointerfalse%
    \def\pgf@lib@callout@relativepointer{#1}%
}
\pgfkeys{/pgf/callout relative pointer=\pgfpointpolar{300}{.5cm}}

Update:

Using pgflibrarymalipivo.callouts.patch.v1.code.tex (see answer below), I can create the callout examples as depicted in the answer below :) However, for both examples, I get error messages:

For pdflatex calloutMinimalExample.tex (first example in the answer):

(/home/dball/texmf/tex/generic/pgf210/tex/generic/pgf/frontendlayer/tikz/librar
ies/tikzlibraryshapes.multipart.code.tex
(/home/dball/texmf/tex/generic/pgf210/tex/generic/pgf/libraries/shapes/pgflibra
ryshapes.multipart.code.tex))) (./pgflibrarymalipivo.callouts.patch.v1.code.tex
)
No file calloutMinimalExample.aux.
ABD: EveryShipout initializing macros
(/usr/share/texmf-texlive/tex/context/base/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
)
! Undefined control sequence.
\pgf@sh@bg@rectangle callout ...f@sh@np@\pgf@test 
                                                  \noexpand \endcsname }\ede...
l.14 ...karounds; no exceptions to optimizations};

? q
OK, entering \batchmode

For pdflatex calloutLargerExample.tex (second example), I get the following error message:

(./pgflibrarymalipivo.callouts.patch.v1.code.tex
) (/usr/share/texmf-texlive/tex/latex/tools/multicol.sty)
No file calloutLargerExample.aux.
ABD: EveryShipout initializing macros
(/usr/share/texmf-texlive/tex/context/base/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
)
! Undefined control sequence.
\pgf@sh@bg@rectangle callout ...f@sh@np@\pgf@test 
                                                  \noexpand \endcsname }\ede...
l.25 \end{tikzpicture}\par}\vspace{1cm}}}
                                         % The end of \foreach cycles
? 

Update 2 for calloutMinimalExample.tex:

Using

\makeatletter(\usetikzlibrary{shapes}\makeatletter) 
\makeatother(\usepgflibrary{malipivo.callouts.patch.v1}\makeatother)

results in the error message

! LaTeX Error: Missing \begin{document}.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.6 \makeatletter(
                  \usetikzlibrary{shapes}\makeatletter)

Using instead

\makeatletter
\usetikzlibrary{shapes} 
\makeatother
\usepgflibrary{malipivo.callouts.patch.v1}

results in the error message posted in update 1.

Using

\usetikzlibrary{malipivo.callouts.patch.v1.spaces}

results in the error message

! I can't find file `tikzlibrarymalipivo.callouts.patch.v1.spaces.code.tex'.
<argument> ...nput tikzlibrary\pgf@temp .code.tex 
                                                  \catcode `\@ =\csname tikz...
l.5 ...zlibrary{malipivo.callouts.patch.v1.spaces}

(Press Enter to retry, or Control-D to exit)
Please type another input file name: 

Renaming your pdflibrarymalipivo.callouts.patch.v1.code.tex to tikzlibrarymalipivo.callouts.patch.v1.spaces.code.tex results in the error message posted in Update 1.


Update

While working with Malipivo's solution (gflibrarymalipivo.callouts.patch.v1.code.tex), I encountered a problem: Can I somehow specify at which callout border (north, south, east, west) the pointer comes in? The automatic solution doesn't look good. Furthermore, the graphic is sometimes broken where the pointer comes into the callout rectangle, see pic. It looks to me like both problems would be solved if I could force the pointer to come in at the west border.

I can play around with callout pointer move=...mm, but it doesn't help much: I do not understand how to decide the callout border with it, and cannot fix the broken graphics. So: How can I specify the border? How do I use move=...mm? Or how do I fix gflibrarymalipivo.callouts.patch.v1.code.tex?

enter image description here

  • Please try to add \makeatletter (\usetikzlibrary{shapes}\makeatletter) and \makeatother (\usepgflibrary{malipivo.callouts.patch.v1}\makeatother), that's my first thought. In my case, the \usepgflibrary command is arranging that for me. – Malipivo Mar 18 '14 at 9:38
  • Second tip: Please try to use \usetikzlibrary instead of \usepgflibrary: \usetikzlibrary{malipivo.callouts.patch.v1.spaces}. – Malipivo Mar 18 '14 at 9:44
  • Thanks for the fast reply, Malipivo :) Unfortunately, both suggestions result in error messages - see Update 2 in my question. – DaveBall aka user750378 Mar 18 '14 at 10:07
  • Renaming the file accordingly (see Update 2) does not solve the problem either :( – DaveBall aka user750378 Mar 18 '14 at 10:14
  • 1
    Finally, I am emulating your problem on virtual Linux machine, I'm working on it... Yes, there is a way, for a moment put \batchmode at the very beginning of the TeX file(s), but I don't recommend it for a daily work even now we would lost track if we are improving the situation or not. – Malipivo Mar 18 '14 at 12:59
7
+100

I would like to present some of my findings, if you excuse me my source code shuffle digging. It would be much easier to draw/program it from scratch with easy access via TikZ interface for improvements, but it was a challenge to follow request as close as possible, it has its reasons, I suppose. I took it as an exercise to improve my TeX source code reading.

The code for all callouts is indeed in \tex\generic\pgf\libraries\shapes\pgflibraryshapes.callouts.code.tex, it contains large blocks of code, it isn't easy to redefine them directly. It wouldn't be easy to separate parts anyway as it consists mainly of computations. The file is commented partly, the names of commands helped in a way.

I created a backup file and started with editing (pgflibrarymalipivo.callouts.patch.v1.code.tex). I focused primarily on absolute+relative rectangle callouts, the others included circle+ellipse related computations. I needed:

  1. One new dimension/definition or a new pgfkey.
  2. Store the pgfkey.
  3. Use the pgfkey properly.

To 1. I have extended \pgfkeys{/pgf/.cd by callout pointer move/.initial=0cm at the beginning of the file.

To 2. I have entered \pgfmathsetlengthmacro\moveleftright{\pgfkeysvalueof{/pgf/callout pointer move}}% and \addtosavedmacro\moveleftright% inside the \pgf@lib@rectanglecallout@pointer command.

To 3. And finally, I have modified the pgf@xb dimension which is an x coordinate of the \borderpoint, almost before the final stage of computation (then testing if that point lies inside the region of the rectangle follows, I haven't modified that part).

Edit: I have tried to polish the code, so please download it to your local directory as it is (uncut file for downloading, or partly polished version below) along with two TeX examples in this answer. We needed to copy \pgfdeclareshape in the polished version, otherwise there was an unwanted message on Linux (I guess that was due to \savedmacro and \addtosavedmacro). Tested with pdflatex, lualatex, xelatex and latex+dvips (TeXLive 2013) on Windows XP and Linux Ubuntu 13.10.

Please use it at your own risk. This is pgflibrarymalipivo.callouts.patch.v1.code.tex:

% A patch to the c:\texlive\2013\texmf-dist\tex\generic\pgf\libraries\shapes\pgflibraryshapes.callouts.code.tex file.
%
% Copyright 2007 by Mark Wibrow
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
%
% See the file doc/generic/pgf/licenses/LICENSE for more details.

%\usepgflibrary{shapes.symbols}

% Keys for callouts
%
% Common to all callouts:
%   /pgf/callout absolute pointer
%   /pgf/callout relative pointer
%
% ellipse callout only:
%   /pgf/callout pointer arc    
%
% rectangle callout only:    
%   /pgf/callout pointer width      
%
% cloud callout only:
%   /pgf/callout pointer start size
%   /pgf/callout pointer end size
%   /pgf/callout pointer segments
%
%\newif\ifpgf@lib@callout@absolutepointer
\pgfkeys{/pgf/.cd,
    %callout pointer arc/.initial=15,
    %callout pointer width/.initial=.25cm,
    %callout pointer start size/.initial=.2 of callout,
    %callout pointer end size/.initial=.1 of callout,
    %callout pointer segments/.initial=2,
    %callout absolute pointer/.code={\pgf@lib@callout@makeabsolutepointer{#1}},
    %callout relative pointer/.code={\pgf@lib@callout@makerelativepointer{#1}},
    %callout pointer shorten/.initial=0cm,
     callout pointer move/.initial=0cm % malipivo
}

\pgfdeclareshape{rectangle callout}{%
\savedmacro\rectanglecalloutpoints{%
    %
    \pgfmathsetlength\pgf@x{\pgfkeysvalueof{/pgf/inner xsep}}%
    \advance\pgf@x.5\wd\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/pgf/minimum width}}%
    \ifdim\pgf@x<.5\pgf@xa%
        \pgf@x.5\pgf@xa%
    \fi%
    \edef\xtemp{\the\pgf@x}%
    \pgfmathaddtolength\pgf@x{\pgfkeysvalueof{/pgf/outer xsep}}%
    %
    \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/pgf/inner ysep}}%
    \advance\pgf@y.5\ht\pgfnodeparttextbox%
    \advance\pgf@y.5\dp\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/minimum height}}%
    \ifdim\pgf@y<.5\pgf@ya%
        \pgf@y.5\pgf@ya%
    \fi%
    \edef\ytemp{\the\pgf@y}%
    \pgfmathaddtolength\pgf@y{\pgfkeysvalueof{/pgf/outer ysep}}%
    %
    \edef\xlength{\the\pgf@x}%
    \edef\ylength{\the\pgf@y}%
    \addtosavedmacro\xlength%
    \addtosavedmacro\ylength%
    %
    \pgfmathsetlengthmacro\pointerwidth{\pgfkeysvalueof{/pgf/callout pointer width}}%
    \addtosavedmacro\pointerwidth%
    %
    \pgfextract@process\centerpoint{%
        \pgf@x.5\wd\pgfnodeparttextbox%
        \pgf@y.5\ht\pgfnodeparttextbox%
        \advance\pgf@y-.5\dp\pgfnodeparttextbox%
    }%
    %
    % Process the relative callout pointer.
    %
    \ifpgf@lib@callout@absolutepointer%
    \else%
        \pgfextract@process\calloutpointer{%
            \pgfextract@process\borderpoint{%
                \expandafter\pgfpointborderrectangle\expandafter{\pgf@lib@callout@relativepointer}%
                    {\pgfqpoint{\xtemp}{\ytemp}}%
            }%
            \pgfmathanglebetweenpoints{\pgfpointorigin}{\borderpoint}%
            \let\pointerangle\pgfmathresult%
            \expandafter\pgf@process\expandafter{\pgf@lib@callout@relativepointer}%
            \pgfmathveclen@{\pgfmath@tonumber{\pgf@x}}{\pgfmath@tonumber{\pgf@y}}%
            \edef\pointerradius{\pgfmathresult pt}%
            \pgfpointadd{\borderpoint}{\pgfqpointpolar{\pointerangle}{\pointerradius}}%
            \pgf@xa\pgf@x%
            \pgf@ya\pgf@y%
            \centerpoint%
            \advance\pgf@x\pgf@xa%
            \advance\pgf@y\pgf@ya%
        }%
        \pgf@lib@callouts@shortenpointer%
        \addtosavedmacro\calloutpointer%
        \pgf@lib@rectanglecallout@pointer%
        \addtosavedmacro\calloutpointeranchor%
        \addtosavedmacro\beforecalloutpointer%
        \addtosavedmacro\aftercalloutpointer%
        \addtosavedmacro\firstpoint%
        \addtosavedmacro\secondpoint%
        \addtosavedmacro\thirdpoint%
        \addtosavedmacro\fourthpoint%
    \fi%
}
\savedanchor\centerpoint{%
    \pgf@x.5\wd\pgfnodeparttextbox%
    \pgf@y.5\ht\pgfnodeparttextbox%
    \advance\pgf@y-.5\dp\pgfnodeparttextbox%
}
\savedanchor\basepoint{%
    \pgf@x.5\wd\pgfnodeparttextbox%
    \pgf@y0pt\relax%
}
\savedanchor\midpoint{%
    \pgf@x.5\wd\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@y{+.5em}%
}
\anchor{center}{\centerpoint}
\anchor{mid}{\midpoint}
\anchor{mid east}{%
    \rectanglecalloutpoints%
    \midpoint%
    \advance\pgf@x\xlength\relax%       
}
\anchor{mid west}{%
    \rectanglecalloutpoints%
    \midpoint%
    \advance\pgf@x-\xlength\relax%      
}
\anchor{base}{\basepoint}
\anchor{base east}{%
    \rectanglecalloutpoints%
    \basepoint%
    \advance\pgf@x\xlength\relax%       
}
\anchor{base west}{%
    \rectanglecalloutpoints%
    \basepoint%
    \advance\pgf@x-\xlength\relax%      
}
\anchor{north}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@y\ylength\relax%
}%
\anchor{south}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@y-\ylength\relax%
}%
\anchor{east}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@x\xlength\relax%
}%
\anchor{west}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@x-\xlength\relax%
}%
\anchor{north east}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@x\xlength\relax%
    \advance\pgf@y\ylength\relax%
}%
\anchor{south west}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@x-\xlength\relax%
    \advance\pgf@y-\ylength\relax%
}%
\anchor{south east}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@x\xlength\relax%
    \advance\pgf@y-\ylength\relax%
}%
\anchor{north west}{%
    \rectanglecalloutpoints%
    \centerpoint%
    \advance\pgf@x-\xlength\relax%
    \advance\pgf@y\ylength\relax%
}%
\anchor{pointer}{%
    \rectanglecalloutpoints%
    \calloutpointeranchor%
}%
\backgroundpath{%
    \rectanglecalloutpoints%
    \pgf@x\xlength\relax%
    \pgf@y\ylength\relax%
    \pgfmathaddtolength\pgf@x{-\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfmathaddtolength\pgf@y{-\pgfkeysvalueof{/pgf/outer ysep}}%
    \edef\xtemp{\the\pgf@x}%
    \edef\ytemp{\the\pgf@y}%
    %
    % The absolute pointer must be calculated here because the
    % anchor of the shape (which is calculated after the saved
    % macros and points) affects how the pointer joins the 
    % main rectangle. 
    %
    \ifpgf@lib@callout@absolutepointer%
        \pgfextract@process\calloutpointer{%
            \pgfpointanchor{pgf@lib@callout@pointer}{center}%
        }%
        \pgf@lib@callouts@shortenpointer%
        \pgfmathsetlengthmacro\pointerwidth{\pgfkeysvalueof{/pgf/callout pointer width}}%
        \pgf@lib@rectanglecallout@pointer%
        %
        % \pgf@node@name = the shape name (from \pgfmultipartnode)
        %
        \ifx\pgf@node@name\pgfutil@empty%
        \else%
            %
            % Now hack an extra saved anchor \calloutpointeranchor,
            % with the new anchor for the callout pointer.
            %
            \edef\pgf@sh@@temp{\noexpand\expandafter\noexpand\pgfutil@g@addto@macro\noexpand\csname pgf@sh@np@\pgf@node@name\noexpand\endcsname}%
            \edef\pgf@sh@@@temp{%
                \noexpand\def\noexpand\calloutpointeranchor{%
                    \noexpand\pgf@x\the\pgf@x%
                    \noexpand\pgf@y\the\pgf@y%
                }%
            }%
            \expandafter\pgf@sh@@temp\expandafter{\pgf@sh@@@temp}%
        \fi%
    \fi%
    {%
        \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
        \pgfpathmoveto{\beforecalloutpointer}%
    }%
    \pgfpathlineto{\calloutpointer}%
    {%
        \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
        \pgfpathlineto{\aftercalloutpointer}%
    }%
    {%
        \pgftransformshift{\centerpoint}%
        \pgfpathlineto{\firstpoint}%
        \pgfpathlineto{\secondpoint}%
        \pgfpathlineto{\thirdpoint}%
        \pgfpathlineto{\fourthpoint}%
        {%
            \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
            \pgfpathclose%
        }%
    }           
}
\anchorborder{%
    \pgfextract@process\externalpoint{}%
    \rectanglecalloutpoints%
    \pgfpointadd{\centerpoint}%
    {%
        \pgfpointborderrectangle{\pgfpointadd{\centerpoint}{\externalpoint}}%
        {\pgfqpoint{\xlength}{\ylength}}%
    }%
}%
}


% \pgf@lib@rectanglecallout@pointer
%
% Internal macro for calculations relating to the rectangle callout.
%
% Requires the following to be set up:
%
% \calloutpointer - the location of the callout point.
% \xtemp          - the half width of the rectangle
% \ytemp          - the half height of the rectangle
% \pointerwidth   - the width of the pointer.
%
\def\pgf@lib@rectanglecallout@pointer{%
    %
    % Ensure that the pointer never connects to the main shape
    % too near to a corner. This is done for two reasons:
    % 1. It can look ugly.
    % 2. If the corners are rounded, a mess can result.
    %
% By malipivo, 2014-03-17, two lines
\pgfmathsetlengthmacro\moveleftright{\pgfkeysvalueof{/pgf/callout pointer move}}%
\addtosavedmacro\moveleftright%
    \pgfextract@process\borderpoint{%
            \pgfpointborderrectangle{%
                \centerpoint%
                \pgf@xa\pgf@x%
                \pgf@ya\pgf@y%
                \calloutpointer%
                \advance\pgf@x-\pgf@xa%
            }{\pgfqpoint{\xtemp}{\ytemp}}%
        }%
    \pgfmathanglebetweenpoints{\pgfpointorigin}{\borderpoint}%
    \let\borderangle\pgfmathresult%
    %
    \pgfutil@tempdima\pointerwidth\relax%
    \pgf@xa\xtemp\relax%
    \advance\pgf@xa-\pgfutil@tempdima%
    \pgf@ya\ytemp\relax%
    \advance\pgf@ya-\pgfutil@tempdima%
    %
    \pgf@process{%
        \pgfutil@ifundefined{pgf@corner@arc}{\pgfpointorigin}{%
            \expandafter\pgfqpoint\pgf@corner@arc}%
    }%
    \advance\pgf@xa-\pgf@x%
    \advance\pgf@ya-\pgf@y%
    %
    \borderpoint%
    \pgf@xb\pgf@x%
\advance\pgf@xb by \moveleftright% malipivo
    \pgf@yb\pgf@y%
    %
    \pgf@xc0pt\relax%
    \pgf@yc0pt\relax%
    %
    \pgfmathanglebetweenpoints{\pgfpointorigin}{\pgfqpoint{\xtemp}{\ytemp}}%
    \ifdim\borderangle pt<\pgfmathresult pt\relax%
        \pgf@yc.5\pgfutil@tempdima%
        \ifdim\pgf@yb>\pgf@ya%
            \pgf@yb\pgf@ya%
        \fi%
        %
        % Establish the order for drawing the rectangle corners.
        %
        \edef\firstpoint{\noexpand\pgfqpoint{\xtemp}{\ytemp}}%
        \edef\secondpoint{\noexpand\pgfqpoint{-\xtemp}{\ytemp}}%
        \edef\thirdpoint{\noexpand\pgfqpoint{-\xtemp}{-\ytemp}}%
        \edef\fourthpoint{\noexpand\pgfqpoint{\xtemp}{-\ytemp}}%
    \else%
        \pgfmathanglebetweenpoints{\pgfpointorigin}{\pgfqpoint{-\xtemp}{\ytemp}}%
        \ifdim\borderangle pt<\pgfmathresult pt\relax%
            \pgf@xc-.5\pgfutil@tempdima%
            \ifdim\pgf@xb>\pgf@xa%
                \pgf@xb\pgf@xa%
            \else%
                \ifdim\pgf@xb<-\pgf@xa%
                    \pgf@xb-\pgf@xa%
                \fi%
            \fi%
            \edef\firstpoint{\noexpand\pgfqpoint{-\xtemp}{\ytemp}}%
            \edef\secondpoint{\noexpand\pgfqpoint{-\xtemp}{-\ytemp}}%
            \edef\thirdpoint{\noexpand\pgfqpoint{\xtemp}{-\ytemp}}%
            \edef\fourthpoint{\noexpand\pgfqpoint{\xtemp}{\ytemp}}%
        \else%
            \pgfmathanglebetweenpoints{\pgfpointorigin}{\pgfqpoint{-\xtemp}{-\ytemp}}%
            \ifdim\borderangle pt<\pgfmathresult pt\relax%
                \pgf@yc-.5\pgfutil@tempdima%
                \ifdim\pgf@yb>\pgf@ya%
                    \pgf@yb\pgf@ya%
                \else%
                    \ifdim\pgf@yb<-\pgf@ya%
                        \pgf@yb-\pgf@ya%
                    \fi%
                \fi%
                \edef\firstpoint{\noexpand\pgfqpoint{-\xtemp}{-\ytemp}}%
                \edef\secondpoint{\noexpand\pgfqpoint{\xtemp}{-\ytemp}}%
                \edef\thirdpoint{\noexpand\pgfqpoint{\xtemp}{\ytemp}}%
                \edef\fourthpoint{\noexpand\pgfqpoint{-\xtemp}{\ytemp}}%
            \else%
                \pgfmathanglebetweenpoints{\pgfpointorigin}{\pgfqpoint{\xtemp}{-\ytemp}}%
                \ifdim\borderangle pt<\pgfmathresult pt\relax%
                    \pgf@xc.5\pgfutil@tempdima%
                    \ifdim\pgf@xb>\pgf@xa%
                        \pgf@xb\pgf@xa%
                    \else%
                        \ifdim\pgf@xb<-\pgf@xa%
                            \pgf@xb-\pgf@xa%
                        \fi%
                    \fi%
                    \edef\firstpoint{\noexpand\pgfqpoint{\xtemp}{-\ytemp}}%
                    \edef\secondpoint{\noexpand\pgfqpoint{\xtemp}{\ytemp}}%
                    \edef\thirdpoint{\noexpand\pgfqpoint{-\xtemp}{\ytemp}}%
                    \edef\fourthpoint{\noexpand\pgfqpoint{-\xtemp}{-\ytemp}}%
                \else%
                    \pgf@yc.5\pgfutil@tempdima%
                    \ifdim\pgf@yb<-\pgf@ya%
                        \pgf@yb-\pgf@ya%
                    \fi%
                    \edef\firstpoint{\noexpand\pgfqpoint{\xtemp}{\ytemp}}%
                    \edef\secondpoint{\noexpand\pgfqpoint{-\xtemp}{\ytemp}}%
                    \edef\thirdpoint{\noexpand\pgfqpoint{-\xtemp}{-\ytemp}}%
                    \edef\fourthpoint{\noexpand\pgfqpoint{\xtemp}{-\ytemp}}%
                \fi%
            \fi%
        \fi%
    \fi%
    \pgfextract@process\beforecalloutpointer{%
        \centerpoint%
        \advance\pgf@x\pgf@xb%
        \advance\pgf@y\pgf@yb%
        \advance\pgf@x-\pgf@xc%     
        \advance\pgf@y-\pgf@yc%
    }%  
    \pgfextract@process\aftercalloutpointer{%
        \centerpoint%
        \advance\pgf@x\pgf@xb%
        \advance\pgf@y\pgf@yb%
        \advance\pgf@x\pgf@xc%      
        \advance\pgf@y\pgf@yc%
    }%  
    %
    % Now calculate the pointer anchor.
    %
    \pgfmathsetlength\pgf@x{\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/pgf/outer ysep}}%
    \ifdim\pgf@x>\pgf@y%
        \edef\outersep{\the\pgf@x}%
    \else%
        \edef\outersep{\the\pgf@y}%
    \fi%
    \pgf@lib@callouts@pointeranchor%
}

\endinput

This is a minimal working example for {lua|pdf|xe|}latex, users of other formats probably know what to do with such a code as TikZ is working in Plain TeX and ConTeXt (PDF version):

\documentclass[10pt,a4paper]{article}
\pagestyle{empty}
\usepackage{tikz}
\usetikzlibrary{shapes}
\usepgflibrary{malipivo.callouts.patch.v1}
\begin{document}
\begin{tikzpicture}
\node[draw, rounded corners=2pt, rectangle] 
   at (2.6,-1.7) (c22) {Further Rectangle};
\node[rectangle callout, draw, red, inner sep=2pt, 
   callout absolute pointer={(c22.west)},
   callout pointer move=-23mm,
   callout pointer width=9mm,
   ] at (1.48,-.95) {no workarounds; no exceptions to optimizations}; 
\end{tikzpicture}
\end{document}

MWE

The pointer is 9 mm wide (it's a regular option in TikZ) and moved to the left side by 23 mm. I experimented a bit with these two parameters so you can get a better view of what's going on. I enclose a non minimal example and its preview. The first page with magenta rectangles is an example of absolute positioning, the next two pages illustrate relative positioning (PDF version).

\documentclass[10pt,a4paper]{article}
\pagestyle{empty} % No page numbers please...
\usepackage{tikz}  % The core of graphics engine...
\usetikzlibrary{shapes}  % The core for callouts...
\usepgflibrary{malipivo.callouts.patch.v1} % An experiment...
\usepackage{multicol} \columnsep=10mm
\voffset=-1.5in \hoffset=-1.5in
\addtolength{\textwidth}{8cm}
\addtolength{\textheight}{7cm}
\begin{document}
\begin{multicols}{3}
\foreach\mytype/\mycolor in {absolute/magenta, relative/blue} {\newpage
\foreach\mymove in {-10,-8,...,10} {% Different moves and
\foreach\mywidth in {2,4,...,10} {% different widths of pointers.
\begin{tikzpicture}
\node[draw, rounded corners=2pt, rectangle] at (2.6,-1.7) (c22) {Further Rectangle};
\node[rectangle callout,
   % ellipse callout, % Unsolved case...
   % cloud callout, % Unsolved case...
   draw, inner sep=2pt, \mycolor, align=center, text width=5cm,
   callout \mytype\space pointer={(c22.west)}, % Type of pointer
   callout pointer width=\mywidth mm, % Variable width (TikZ)
   callout pointer move=\mymove mm, % Variable move (experiment)
   ] at (1.48,-.95) {width=\mywidth mm; move=$\mymove$ mm}; 
\end{tikzpicture}\par}\vspace{1cm}}}% The end of \foreach cycles
\end{multicols}
\end{document}

nMWE1 nMWE2 nMWE3

  • 2
    Haha, nice! A bit hypnotizing but nice :) – percusse Mar 18 '14 at 5:34
  • 1
    Nice! Even if it is a bit lengthy: With respect to longevity, please include the patch also in your answer. – Daniel Mar 18 '14 at 8:20
  • @Daniel Thank you for your feedback. I tried to cut it down a bit (there is a limit of 30000 letters per answer), check the patch functionality (test has been passed) and it is included in the post now. – Malipivo Mar 18 '14 at 8:59
  • Wow, thanks a lot, Malipivo! I can create the pdf files with pdflatex, but I get error messages. I have updated my question accordingly. Would be great if you were able to fix it. – DaveBall aka user750378 Mar 18 '14 at 9:23
  • @Malipivo: Thanks again for your solution. I have used it a couple of times by now, but am having some problems, which I described at the bottom of my original post in a new update. Would be great if you had a solution. – DaveBall aka user750378 Jul 14 '14 at 7:52

Your Answer

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

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