1

I've an idle thought on how to use the dastardly tikzmark TikZ library to put nodes around pre-packaged boxes, specifically (but not limited to) the boxes that the awesome AMSMath packages use when assembling environments such as align and gather with a view to doing for systems of equations what the listings extension to tikzmark does for code: place marks at lots of useful places.

Looking at the AMSMath code, environments such as those are built up by putting each "piece" in a box, \z@, before assembling it in an halign. What I would like to do is to pretend that that box is the text box for a TikZ/PGF node and save its location and dimensions just prior to unboxing it.

Specifically, just before unboxing that box, I would do the following:

  1. Place a \pgfmark at the current location (this is the least invasive of the various marking systems that tikzmark defines; it simply invokes the low-level "remember the coordinates of this point" code appropriate to the TeX engine involved and writes a couple of lines to the aux file).
  2. Measure the box and do a slew of assignments which are the same as the result of that box being a PGF node, but without setting up a TikZ environment or anything.
  3. Then unbox the box.

Apart from the fact that the AMSMath code uses the same box for everything (including roots and labels), in my admittedly basic tests this doesn't seem to cause any problems.

So my question is: what issues might this cause, and what should I do to robustly test this code?

(Just to note, if this ever made it into the actual tikzmark package then I would almost certainly make it an extra thing that had to be loaded explicitly rather than something that was automatically included, and I don't think I'd make it automatically switched on for all such boxes but rather have it so that it can be switched in by choice when needed.)

(Also, it did occur to me that there could be useful hooks in LaTeX3 around putting maths on the page which I could use instead but my - admittedly limited - searching didn't find any.)

Here's some code for anyone who wants to test this or wants to see more details. This should very definitely not be thought of as something to cut and paste into your thesis that is due in tomorrow!

\documentclass{article}

\usepackage{amsmath}

\usepackage{tikz}
\usetikzlibrary{tikzmark,fit}

\makeatletter

\def\tikzmark@box#1#2{%
  \begingroup
  \pgfmark{#1}%
  \let\pgfnodeparttextbox=#2%
  \edef\pgfpictureid{pgfid\the\pgf@picture@serial@count}%
  \def\tikz@fig@name{#1}%
  \pgfpointorigin
  \advance\pgf@x by .5\wd\pgfnodeparttextbox%
  \advance\pgf@y by .5\ht\pgfnodeparttextbox%
  \advance\pgf@y by -.5\dp\pgfnodeparttextbox%
  \pgftransformshift{}%
  \setbox\@tempboxa=\hbox\bgroup
  {%
    \tikzset{inner sep=0pt, minimum size=0pt, outer sep=0pt, anchor=base}%
    \let\pgf@sh@savedmacros=\pgfutil@empty% MW
    \let\pgf@sh@savedpoints=\pgfutil@empty%
    \def\pgf@sm@shape@name{rectangle}% CJ % TT added prefix!
    \pgf@sh@s@rectangle%
    \pgf@sh@savedpoints%
    \pgf@sh@savedmacros% MW
    \pgftransformshift{%
      \pgf@sh@reanchor{rectangle}{center}%
      \pgf@x=-\pgf@x%
      \pgf@y=-\pgf@y%
    }%
    \expandafter\pgfsavepgf@process\csname pgf@sh@sa@\tikz@fig@name\endcsname{%
      \pgf@sh@reanchor{rectangle}{center}% FIXME : this is double work!
    }%
    % Save the saved points and the transformation matrix
    \edef\pgf@node@name{\tikz@fig@name}%
    \ifx\pgf@node@name\pgfutil@empty%
    \else%
    \expandafter\xdef\csname pgf@sh@ns@\pgf@node@name\endcsname{rectangle}%
    \edef\pgf@sh@@temp{%
      \noexpand\gdef\expandafter\noexpand\csname pgf@sh@np@\pgf@node@name\endcsname}%
    \expandafter\pgf@sh@@temp\expandafter{%
      \pgf@sh@savedpoints}%
    \edef\pgf@sh@@temp{%
      \noexpand\gdef\expandafter\noexpand\csname pgf@sh@ma@\pgf@node@name\endcsname}% MW
    \expandafter\pgf@sh@@temp\expandafter{\pgf@sh@savedmacros}% MW
    \pgfgettransform\pgf@temp
    \expandafter\xdef\csname pgf@sh@nt@\pgf@node@name\endcsname{\pgf@temp}%
    \expandafter\xdef\csname pgf@sh@pi@\pgf@node@name\endcsname{\pgfpictureid}%
    \fi%
  }%
  \egroup
  \box\pgfnodeparttextbox%
  \endgroup
}


\let\tikz@boxz@=\boxz@
\newcounter{tikzmarkequation}

\def\boxz@{%
  \stepcounter{tikzmarkequation}%
  \tikzmark@box{equation \the\c@tikzmarkequation}{\z@}}


\makeatother

\begin{document}

\begin{gather}
a = b + c \\
d = e + f
\end{gather}

\begin{align*}
a &= b + c \\
d &= e + f
\end{align*}

\begin{align}
a &= b + c & g&= h + i \\
d &= e + f
\end{align}

\begin{gather}
a^2 = b^2 + c^2 \\
\end{gather}

\begin{gather}
a = \sqrt[2]{b^2 + c^2}
\end{gather}

\begin{tikzpicture}[remember picture, overlay]
\foreach \k in {1,...,\value{tikzmarkequation}} {
  \draw[red]  (pic cs:equation \k) -- ++({\k*(180-360/\the\value{tikzmarkequation})}:1) node[draw,red,circle,font=\tiny] {\k};
  \node[draw,green,fit=(equation \k),inner sep=0pt] {};
}
\end{tikzpicture}

\end{document}

(Note: the original code had \refstepcounter. Thanks to the comments, I've changed it now to \stepcounter.)

14
  • 1
    well \usepackage{hyperref} could happen ... Aug 17 at 11:09
  • 1
    also it's not clear if you want the box measurements as the visible size of the unboxed material will be different if it stretches or shrinks (allowing that is why its unboxed) what I suspect you really want is the underlying \halign template to drop referenceable anchors at the corners of each cell. Aug 17 at 11:30
  • 1
    details.... possibly yes if you do exactly as I suggest or you add the anchors inside the content before the left and right fill glue to get the actual visible width, but that's more invasive. Aug 17 at 11:42
  • 1
    with \H@refstepcounter{tikzmarkequation}% it behaves better (\H@refstepcounter is the original \refstepcounter which doesn't create a destination). Aug 17 at 11:50
  • 1
    sure if you don't need labels to store the counter then \stepcounter is the best, I thought you use \refstepcounter for a reason. Aug 17 at 11:57

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.