5

I can use layers as:

Some
\begin{tikzpicture}[baseline]
\node [anchor=base,inner sep=0pt] (X) at (0,0) {Highlighted};
\begin{pgfonlayer}{background layer}
    \draw [fill=orange, fill opacity=0.9, baseline] (X.south west) rectangle (X.north east); 
\end{pgfonlayer}
\end{tikzpicture}  Text v1.

which produces the the desired output, the first line in the image below:

enter image description here

However, this is quite combersome. It is much easier to use \MyTikzMark to create a node when needed

Some \MyTikzMark{MyNode}{Highlighted} Text v2.

and then do the drawing later by accesing the MyNode. However, with this mode, I am unable to get the highlighting to be on background layer and end up with the second line in the above image.

What is further suprising is that using a brute force approach which tries to mimic the "cumbersome" solution fails:

\newcommand{\MyTikzMarkA}[2]{% Same behavior as \MyTikzMark.
    \begin{tikzpicture}[baseline,remember picture] 
        \begin{pgfonlayer}{foreground layer}
            \node[anchor=base,inner sep=0pt] (#1) {#2};%
        \end{pgfonlayer}
    \end{tikzpicture}%
}%

Code:

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{backgrounds}
\pgfdeclarelayer{background layer} 
\pgfdeclarelayer{foreground layer} 
\pgfsetlayers{background layer,main,foreground layer}

\newcommand{\MyTikzMark}[2]{%
    \tikz[baseline,remember picture] \node[anchor=base,inner sep=0pt] (#1) {#2};%
}%
%\newcommand{\MyTikzMarkA}[2]{% Same behavior as above.
%    \begin{tikzpicture}[baseline,remember picture] 
%        \begin{pgfonlayer}{foreground layer}
%            \node[anchor=base,inner sep=0pt] (#1) {#2};%
%        \end{pgfonlayer}
%    \end{tikzpicture}%
%}%


\begin{document}
% This works!
Some
\begin{tikzpicture}[baseline]
\node [anchor=base,inner sep=0pt] (X) at (0,0) {Highlighted};
\begin{pgfonlayer}{background layer}
    \draw [fill=orange, fill opacity=0.9, baseline] (X.south west) rectangle (X.north east); 
\end{pgfonlayer}
\end{tikzpicture}  Text v1.

%% But how to get this to work?
Some \MyTikzMark{MyNode}{Highlighted} Text v2.
\begin{tikzpicture}[remember picture, overlay]
\begin{pgfonlayer}{background layer}
    \draw [fill=orange, fill opacity=0.9, baseline] (MyNode.south west) rectangle (MyNode.north east); 
\end{pgfonlayer}
\end{tikzpicture}%

\end{document}

2 Answers 2

1

You could simply draw the background first by using tikzmark. You need to a bit careful with page breaks (but you would need this anyway). It needs two compilations.

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{tikzmark}

\newsavebox\mynode{}
\newcommand{\MyTikzMark}[2]{%
    \savebox\mynode{#1}\raisebox{-\dp\mynode}{\tikzmark{#1begin}}\usebox\mynode\raisebox{\ht\mynode}{\tikzmark{#1end}}}


\begin{document}

\iftikzmark{MyNodebegin}
 {\tikz[remember picture,overlay]\draw [fill=orange, fill opacity=0.9, baseline] (pic cs:MyNodebegin) rectangle (pic cs:MyNodeend);}
 {}%
Some \MyTikzMark{MyNode}{Highlighted} Text v2.

\end{document}

enter image description here

4
  • Yeah, this sure looks simpler. Does this version of \MyTikzMark duplicate \tikz[baseline,remember picture] \node[anchor=base,inner sep=0pt] (#1)? It seems to, but just wanted to make sure. Also, if you eliminate the \iftikzmark{MyNodebegin} then only two runs are needed -- otherwise it seems that three are needed. Commented Apr 21, 2017 at 8:48
  • I think you are right, the \iftikzmark is probably not needed. Commented Apr 21, 2017 at 8:51
  • I thought a bit about your problem and imho the idea to separate node and highlighting is a bit odd. I would suggest to move the background in the \MyTikzMark code along with some switch, and write a command so that you can do \switchonhighlight{MyNode1,MyNode2,...} (or even with a tikzset-key). Commented Apr 21, 2017 at 9:24
  • Yeah I did think about doing that, but I prefer to I separate the \tikzmark and text from any drawing. For me, that is the whole point of \tikzmark. I haven't use the tikzmark library as I often need to access various anchors, and using a \node does that for me. So, if your answer of using a node and adjusting its vertical position is the same as what \tikz[baseline,remember picture] \node[anchor=base,inner sep=0pt] (#1) does then what you suggested is the way to go. Commented Apr 21, 2017 at 9:58
6

First approach

Perhaps you can use a box to remember the content and put it back.

Here I am lazy and use spy to set up the remembering box. Since there is only one remembering box, you cannot \tikzmark two words and then highlight them. But this is possible if you are willing to manage the boxes by yourself. For example replace

      \global\setbox\tikz@lib@spybox=\hbox{\copy\tikz@lib@spybox}%

by

      \global\setbox\your@remembering@box=\hbox{\copy\tikz@lib@spybox}%

code

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{spy}

\newcommand{\MyTikzMark}[2]{%
    \tikz[baseline,remember picture,spy scope]\node[anchor=base,inner sep=0pt](#1){#2};%
}%

\begin{document}

\makeatletter
\tikzset{
  spy scope/.append style={
    execute at end scope={%
      \global\setbox\tikz@lib@spybox=\hbox{\copy\tikz@lib@spybox}%
    }
  }
}

%% But how to get this to work?
Some \MyTikzMark{MyNode}{Highlighted} Text v2.
\begin{tikzpicture}[remember picture, overlay]
    \draw [fill=orange, fill opacity=0.9] (MyNode.south west) rectangle (MyNode.north east);
    \node at(MyNode.base){\copy\tikz@lib@spybox};
\end{tikzpicture}%

\end{document}

Second Approach

The approach uses pgfpages (not pdfpages). Firstly set up two logical pages: the first for highlight and the second for the actual content. Whenever you want to highlight something, add the highlighting code to the first logical page. (You can highlight multiple words at once.) The package will do the rest.

This approach does not work if the the actual content is more than one pages. It works if you are using beamer.

\documentclass{article}
\usepackage{tikz}
\usepackage{pgfpages}
\pgfpagesdeclarelayout{one page but with background}{}{%
  \pgfpagesphysicalpageoptions{         logical pages=2,%
    physical width=\paperwidth, first logical shipout=2,%
    physical height=\paperheight,last logical shipout=2,%
  }
  \pgfpageslogicalpageoptions1{center=\pgfqpoint{.5\paperwidth}{.5\paperheight}}
  \pgfpageslogicalpageoptions2{center=\pgfqpoint{.5\paperwidth}{.5\paperheight}}
}
\pgfpagesuselayout{one page but with background}%

\newcommand{\MyTikzMark}[2]{%
    \tikz[baseline,remember picture]\node[anchor=base,inner sep=0pt](#1){#2};%
}

\begin{document}
%% But how to get this to work?
Some \MyTikzMark{MyNode}{Highlighted} Text v2.

\pgfpagesshipoutlogicalpage1\vbox{
\tikz[remember picture, overlay]
    \draw[fill=orange, fill opacity=0.9](MyNode.south west)rectangle(MyNode.north east); 
}

\end{document}

Third approach

This is insane. Basically it writes everything you want to know to the aux file. When the aux file is included (in the second compilation), it will add a hook before the targeted tikzpicture. Therefore before the targeted tikzpicture is drawn (in the third compilation), the hook draws the background for you.

Again, this is insane. It actually requires the targeted node being rectangle. Although these are not difficult to overcome, I will leave the imperfect answer here.

The good news is: it is not affected by page-breaking.

\documentclass{article}
\usepackage{tikz,lipsum}

\newcommand{\MyTikzMark}[2]{%
    \dopicturebackground%
    \tikz[baseline,remember picture]\node[anchor=base,inner sep=0pt](#1){#2};%
}

\makeatletter
\newcount\pgf@picture@serial@count@temp
% #1: associated node name
% #2: the background code you want to add
\def\addbackgroundto(#1)#2{%
    \edef\pgf@tempnodename{#1}%
    \edef\pgf@tempgfid{{\csname pgf@sh@pi@\pgf@tempnodename\endcsname}}%
    \def\southwest{\string\southwest}%
    \def\northeast{\string\northeast}%
    \edef\pgf@tempnodeinfor{{%
        \string\pgfutil@namedef{pgf@sh@ns@\pgf@tempnodename}{\csname pgf@sh@ns@\pgf@tempnodename\endcsname}%
        \string\pgfutil@namedef{pgf@sh@np@\pgf@tempnodename}{\csname pgf@sh@np@\pgf@tempnodename\endcsname}%
        \string\pgfutil@namedef{pgf@sh@nt@\pgf@tempnodename}{\csname pgf@sh@nt@\pgf@tempnodename\endcsname}%
        \string\pgfutil@namedef{pgf@sh@pi@\pgf@tempnodename}{\csname pgf@sh@pi@\pgf@tempnodename\endcsname}%
        \string\pgfutil@namedef{pgf@sh@ma@\pgf@tempnodename}{\csname pgf@sh@ma@\pgf@tempnodename\endcsname}%
    }}%
    \expandafter\pgfsys@addnodebackground\expandafter\pgf@tempgfid\pgf@tempnodeinfor{\tikz[remember picture,overlay]{#2}}%
}
% #1: pgfid of the desination
% #2: all node information
% #3: the background code you want to add
\def\pgfsys@addnodebackground#1#2#3{%
  \pgfutil@writetoaux{\string\pgfsysaddpicturebackground#1{#2\detokenize{#3}}}%
}
% #1: pgfid of the destination
% #2: all node information
%     and the background code you want to add
\def\pgfsysaddpicturebackground#1#2{%
    \expandafter\gdef\csname picturebackground#1\endcsname{#2}%
}
\def\dopicturebackground{%
    \global\pgf@picture@serial@count@temp=\pgf@picture@serial@count\relax%
    \pgf@picture@serial@count=10000\relax%
    \csname picturebackgroundpgfid\the\numexpr\pgf@picture@serial@count@temp+1\endcsname%
    \global\pgf@picture@serial@count=\pgf@picture@serial@count@temp\relax%
}

\begin{document}

%% But how to get this to work?
Some \MyTikzMark{My  Node}{Highlighted} Text v2.

\clearpage

second page

\addbackgroundto(My  Node){
    \draw[fill=orange, fill opacity=0.9](My  Node.south west)rectangle(My  Node.north east);
}
\end{document}
5
  • Did not know about the pgfpages library. However, reading the documentation it states "A word of warning: using pgfpages ... hyperlinks ... will appear at totally wrong positions on the final output". So, not sure that I can use this solution. Commented Apr 21, 2017 at 4:18
  • @PeterGrill Yes you can. The hyperlinks appear at wrong positions because the usual usage of pgfpages involves moving logical pages around. But in this case, two logical pages are coincide with the physical page. So there will be no problem.
    – Symbol 1
    Commented Apr 21, 2017 at 4:40
  • Ahhh, I see. That explains why the small test case I made did not seem to have any problems with the hyperlink positions. Commented Apr 21, 2017 at 8:14
  • I haven't used the spy library yet so wondering if modifying the spy scope may effect my ability to use that in the future. One change that seems safer, is is to use My Spy Scope in the \MyTikzMark and then specify \tikzset{My Spy Scope/.style={spy scope, execute at end scope...}. Thus spy scope is unaltered. The issue left then is the use of the \tikz@lib@spybox - I don't know how that will effect other uses of spy. If a simpler solution does not show up, I'll probably go with this solution, if I can get it working when there is more than a single use of \MyTikzMark . Commented Apr 21, 2017 at 8:40
  • @PeterGrill What spy scope does is roughly execute at begin scope={ \tikz@lib@spybox=\hbox\bgroup } and then execute at end scope={ \egroup }. So our job is simply to smuggle this box outside the tikzpicture before it is too late. The only difference between the old spy scope and the appended one is \your@remembering@box.
    – Symbol 1
    Commented Apr 21, 2017 at 13:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .