3

This is a follow up question to Adding nodes in a style

If I use the following code, I obtain a strange behavior:

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{matrix,fit,backgrounds}
\tikzset{
  fill first cell/.style={
    append after command={
      \pgfextra
        \pgfonlayer{background}
          \tikzset{every node/.style=}
          \node[fit=(\tikzlastnode-\the\pgfmatrixcurrentcolumn-\the\pgfmatrixcurrentrow), inner sep=+0pt, fill=#1] {};
        \endpgfonlayer
      \endpgfextra
    }
  },
  fill first cell/.default=red!7
}
\begin{document}
\begin{tikzpicture}
\matrix (m) [matrix of nodes, fill first cell] {
  1 & 2 \\
  3 & 4 \\
};
\end{tikzpicture}
\end{document}

Output

output

Desired output

desired output

Everything looks as expected when printed. However, the PDF viewer shows all the letters slightly bolder. This behavior can be seen in the two outputs shown above. I strongly suspect that the content of the matrix is drawn twice.

I know it is drawn at least once after the code from my style. To prove it, remove the background layer command and the red rectangle will still appear in the background from the number 4.

Furthermore, if I use a pgfnode instead of a Tikz node the problem does not appear.

What is causing this behavior and how can I fix it?

Edit

With the help of @percusse, @DavidCarlisle, and @AndrewSwann I have been able to find a shorter MWE as well as a better way to check for the existence of the bug.

The following code exhibits the bug:

\documentclass[tikz]{standalone}
\begin{document}
\showoutput
\begin{tikzpicture}
\path node {x}
      \pgfextra
        \node{};
      \endpgfextra
;
\end{tikzpicture}
\end{document}

By using the following command pdflatex mwe.tex | grep cmr/ | wc -l you can check how many times the character x is being printed. 1 means no bug, 2 means bug.

By playing with the content of \pgfextra ... \endpgfextra I have made some additional observations:

  • \pgfnode{rectangle}{node}{}{}{} does not exhibit the bug, so it seems to be a Tikz problem
  • \draw (0,0) -- (0,1) -- (1,1) -- (1,0) -- cycle; does not exhibit the bug either
  • \draw (0,0) -- (0,1) -- (1,1) -- (1,0) -- cycle node {}; does exhibit the bug
  • \draw (0,0) -- (0,1) -- (1,1) -- (1,0) -- cycle; \node {}; does not exhibit the bug

The lines composing the path do not seem to be drawn twice. However, in the following code, both nodes, x and y, are printed twice.

\documentclass[tikz]{standalone}
\begin{document}
\showoutput
\begin{tikzpicture}
\path node {x} node {y}
      \pgfextra
        \node{};
      \endpgfextra
;
\end{tikzpicture}
\end{document}

Workaround (Obsolete, see the solution instead)

This question is not yet answered, however there is now a workaround available: just add a \path; before the \node{}; inside \pgfextra.

Solution

As mentionned in Adding nodes in a style and in @MarkWibrow's answer, the problem was a lack of protection of the outer path in \pgfextra. The classical way to protect is to use \pgfinterruptpath. This is however not enough when inserting TikZ nodes and it must be coupled with \setbox\tikz@figbox=\box\pgfutil@voidb@x. The fixed MWE looks like:

\documentclass[tikz]{standalone}
\begin{document}
\showoutput
\begin{tikzpicture}
\path node {x}
      \pgfextra
        \pgfinterruptpath
          \makeatletter\setbox\tikz@figbox=\box\pgfutil@voidb@x\makeatother
          \node{};
        \endpgfinterruptpath
      \endpgfextra
;
\end{tikzpicture}
\end{document}

The \pgfinterrupath serves two purposes. If on top of a node x we were drawing a path, it would make sure the node y would not delete it. In our case, it is needed for \makeatletter\setbox\tikz@figbox=\box\pgfutil@voidb@x\makeatother not discard the x node.

This solution works for TikZ/PGF version 2.10 and the CVS version. The problem mentionned by @MarkWibrow in the CVS version was the following: When the fit node was encountered, the nodes of the matrix were all inserted in the background layer instead of the main layer where they belong. This was a bug (http://sourceforge.net/p/pgf/bugs/260/) that is now fixed. If using exclusively the CVS version (after September 9 2013) the following code works:

\documentclass[tikz]{standalone}
\begin{document}
\showoutput
\begin{tikzpicture}
\path node {x}
      \pgfextra
        \pgfinterruptpath
          \node{};
        \endpgfinterruptpath
      \endpgfextra
;
\end{tikzpicture}
\end{document}
  • This looks like a viewer issue. Add \matrix (m) at (0,0) [matrix of nodes] { 1 & 2 \ 3 &|[inner sep=0pt,text=yellow]| 4 \}; to your code and there is no leak. There is however an issue of aliasing which is common in various viewers. See this http://tex.stackexchange.com/q/49614/3235 As far as I can see there is no redrawing in your code. – percusse Sep 01 '13 at 14:48
  • Thanks for the link, I will check it out. I am not sure what the code snippet should do. I do really think the content of the matrix is written twice given that the background layer has no effect. – Nicolas Dudebout Sep 01 '13 at 15:03
  • Background layer only draws the red rectangle after the matrix is drawn. There is no replication of the actual matrix content. – percusse Sep 01 '13 at 15:04
  • Try removing the two pgfonlayer lines and see that the number 4 stays on top of the red rectangle even though the rectangle is supposedly drawn after. – Nicolas Dudebout Sep 01 '13 at 15:05
  • Not really you are using append after command. That makes the node created but not placed immediately such that you can use its name and position for future path without actually drawing it so layering is redundant here. You can check \pgfpositionnodelater etc. in the manual for more low level explanations. – percusse Sep 01 '13 at 15:09
  • I do see the antialising that seem to indicate that the matrix content is drawn twice. Maybe I should not be using append after command to do what I am trying to do. – Nicolas Dudebout Sep 01 '13 at 15:12
  • With more testing it seems that the bug affects all the nodes created before the pgfextra. I will add this in the question as well – Nicolas Dudebout Sep 01 '13 at 19:40

3 Answers3

3
\documentclass[tikz]{standalone}
\usetikzlibrary{matrix,fit,backgrounds}
\tikzset{
  fill first cell/.style={
    append after command={
      \pgfextra
        \pgfonlayer{background}
          \tikzset{every node/.style=}
          \node[fit=(\tikzlastnode-\the\pgfmatrixcurrentcolumn-\the\pgfmatrixcurrentrow), inner sep=+0pt, fill=#1] {};
        \endpgfonlayer
      \endpgfextra
    }
  },
  fill first cell/.default=red!7
}
\begin{document}
\showoutput
\begin{tikzpicture}
\matrix (m) [matrix of nodes, fill first cell] {
  1 & 2 \\
  3 & 4 \\
};
\end{tikzpicture}
\end{document}

Then look at the log

$ grep "cmr/m/n/10" tkmatrix.log
.........................\OT1/cmr/m/n/10 1
.........................\OT1/cmr/m/n/10 2
.........................\OT1/cmr/m/n/10 3
.........................\OT1/cmr/m/n/10 4
.........................\OT1/cmr/m/n/10 1
.........................\OT1/cmr/m/n/10 2
.........................\OT1/cmr/m/n/10 3
.........................\OT1/cmr/m/n/10 4

You are correct in your observation that the matrix is drawn twice.

ShreevatsaR
  • 45,428
  • 10
  • 117
  • 149
David Carlisle
  • 757,742
3

The matrix is being drawn twice and this is caused by the presence of a drawing command in your \pgfextra. Here is a test file to show the behaviour.

\documentclass[tikz]{standalone}

\pdfcompresslevel=0

\tikzset{ff/.style={append after command={\pgfextra\node {};\endpgfextra}}}

\begin{document}
\begin{tikzpicture}
\node[ff] (0,0) {XY};
\end{tikzpicture}
\end{document}

pgfcompresslevel=0 means that the resulting pdf file is in more readable form. From the above code you get the following pdf code twice in the output

BT
/F8 9.9626 Tf 3.321 3.321 Td [(XY)]TJ
ET

the first on lines 26-28, the second on lines 51-53. Removing \node {}; from the pdfextra material, produces a pdf file with only one occurance of the printing command.

Note that I have not used any backgroud, fit or matrix libraries for this.

Andrew Swann
  • 95,762
  • It confirms what I have seen. Replacing \node {}; with \pgfnode {rectangle}{north}{}{foo}{Foo} also does not double the printing. It seems to be something in Tikz rather than PGF causing the problem. Not just drawing per se. – Nicolas Dudebout Sep 01 '13 at 15:32
  • @NicolasDudebout That is a good observation about \node vs. \pgfnode. – Andrew Swann Sep 01 '13 at 15:55
  • @NicolasDudebout I've checked the code a little and the problem is that the main node is suspended (but not interrupted) so when the additional node is finished it also finishes the main node. But since it is in the append after command it is resumed once again and reprinted afterwards. Quite perplexing indeed, I didn't know that before. \pgfnode doesn't have a specific path operation so waits until the main node issues a path operation. I really don't know how to explain it in proper terms hahah. Great stuff. – percusse Sep 01 '13 at 18:44
  • Should I report a bug somewhere? – Nicolas Dudebout Sep 01 '13 at 18:45
  • Actually, I tried putting a path instead of a pgfnode or a node and the problem is not here. I used \draw (0,0) -- (0,1) -- (1,1) -- (1,0) -- cycle; for example and I do not get doubling. – Nicolas Dudebout Sep 01 '13 at 18:54
  • Furthermore, if I put a \node{}; after the \draw ...; the bug is not there. There is definitely an interplay of \node{}; and paths. – Nicolas Dudebout Sep 01 '13 at 18:57
3

All nodes in a path are positioned in a special box called \tikz@figbox. When something is done with nodes, something like the following occurs at some point:

\setbox\tikz@figbox=\hbox\bgroup%
   \setbox\pgfutil@tempboxa=\copy\tikz@figbox%
   \unhbox\pgfutil@tempboxa%
   %
   % Code inserting/positioning node content
   %
\egroup%

At the end of a path the box is used and becomes void (which is why the insertion of \path; seems to work).

In the case given above the OP starts a path (a \node is a special kind of path - the \path is inserted automatically) inside another path (a \matrix is a type of node, which is, therefore, a kind of path).

As the \tikz@figbox contents from the matrix are not voided before the \node command, the matrix contents are also inserted into the box for the node contents. It is possible to put \setbox\tikz@figbox=\box\pgfutil@voidb@x before the node command to see that this corrects the issue.

Effectively what the OP has done (more or less) is this:

\newbox\mybox
\setbox\mybox=\hbox{matrix}
{\setbox\mybox=\hbox{\copy\mybox\space node}1. \box\mybox}
2. \box\mybox

Which produces

1. matrix node 2. matrix

I personally don't see it as a bug, the approach given by the OP is not really a good way to do things. \pgfextra has to be used with care if other paths/pictures are being used (e.g., using \pgfinuterruptpath \endpgfinterruptpath). Although in this case it doesn't solve the problem.

Either way, the duplication does not occur with the latest CVS versions, but it is also the case that the append after command key doesn't produce the required effect either.

Mark Wibrow
  • 70,437
  • Thank you for your answer. I follow the reasonning but I fail to understand what is your recommended course of action. I also do not know what is my "approach" is it the append after command or the workaround that is clearly a hack? – Nicolas Dudebout Sep 02 '13 at 15:00
  • If you think using append after command is a bad idea, I would welcome your input in the question mentionned at the beginning of the question. This is what triggered it all. – Nicolas Dudebout Sep 02 '13 at 15:03
  • @NicolasDudebout anything involving starting path inside another path without being careful is likely to result in unexpected results or errors. There is a /tikz/row <row> column <column> style (see "Cell Styles and Options" in the manual) which could be used in this MWE case (but obviously the cells have to specified manually), and layers wouldn't be needed. If this isn't satisfactory, or doesn't generalize sufficiently then AFAIK it cannot be done any other way than in the "Adding nodes in a style" question. – Mark Wibrow Sep 04 '13 at 16:39
  • That was the point of my question. I was asking what care was needed to use a node inside pgfextra. I have extracted the fix from your answer for easy reference in the future. Thank you for your help. Regarding the row and column styles, it does not work in my case as I am working on extending these to have real row and column support (not the every row that does not have a real notion of row but just applies to every node in a row). – Nicolas Dudebout Sep 05 '13 at 14:18
  • Thank you for your help. It has helped fix a bug in the CVS version. There is now no need for any protection in \pgfextra for inserting classical nodes. – Nicolas Dudebout Sep 09 '13 at 13:40