6

I want to create a simple tikz style highlight which will draw ellipse around the object and write label at requested position. To avoid providing the font through the text argument, I've placed it directly in the tikz style.

Here is the MWE:

\documentclass {article}
\RequirePackage [utf8] {inputenc}

\RequirePackage {tikz}
\RequirePackage {xparse}

\usetikzlibrary {fit}
\usetikzlibrary {shapes}

\ExplSyntaxOn

    \tl_const:Nn \c_highlight_font {\fontsize {20} {24} \selectfont \bfseries}

    % #1 Angle
    % #2 Relative position
    % #3 Text
    \tikzset
    {
        highlight/.style~n~args = 3
        {
            ellipse,
            inner~sep = 0mm,
            draw = blue,
            dashed,
            very~thick,
            label={[#2, blue]#1:\c_highlight_font #3}
        }
    }

\ExplSyntaxOff

\begin {document}
    \ExplSyntaxOn
        \begin{tikzpicture}
            \node (node_to_highlight) [minimum~size = 1cm, draw] at (0, 0) {};
            \node (highlight) [highlight = {210}{below~left}{1}, fit = (node_to_highlight)] {};
        \end{tikzpicture}
    \ExplSyntaxOff
\end {document}

This code has the issue.
The colon (:) in the label definition has changed catcode because it is inside the \ExplSyntaxOn environment and is therefore treated as the letter, not part of the tikz syntax.
This ends up writing 210:1 as the label, instead of 1 at angle 210.

I am aware of the obvious workaround:
Define tikz style outside of \ExplSyntaxOn environment and change \c_highlight_font with something like \def\highlightFont which can work outside of \ExplSyntaxOn because it doesn't have problematic _ in the name.

I would prefer to keep LaTeX3 variables if it is possible.
Is there a way to add some "escape sequence" or something similar to make tikz realize that : is part of the syntax?

I have tried:

  1. Replacing the : with \c_colon_str but it had the same result.
  2. Replacing the : with \char_generate:nn{58}{12}, but it also didn't help. Neither did any other catcode for the colon (1-11,13).
8

The problem with both your attempts is that neither was expanded to generate an actual :12. This means that TikZ sees the control sequence \c_colon_str instead of its expansion, :12.

To have this working you need to expand \c_colon_str (I'm ignoring the \char_generate:nn approach because it would generate the same thing, with more effort) before \tikzset does it's thing. In general there are two ways to do what you want, but as you say in the comments, both are quite verbose.


The first is the good ol' \edef\x{<stuff-to-expand>}\x. This is provided by expl3 under the name \use:x, which does as the name suggests: it does an x-expansion (\edef) in the argument, then uses it. There are two main issues with this approach: 1) everything is expanded, and 2) parameter tokens (#) need to be doubled. In your case the label would change from (simplified for brevity):

                 \tikzset{label={[ #2,blue] #1\c_colon_str          \c_iskustvo_highlight_font_tl #3}}

to

\use:x{\exp_not:N\tikzset{label={[##2,blue]##1\c_colon_str\exp_not:N\c_iskustvo_highlight_font_tl##3}}}

Note the added \exp_not:N before every control sequence (and every active character should also be \exp_not:N'ed), and the doubled #. You could avoid doubling hashes by either wrapping everything else in \exp_not:n (not N):

\use:x{\exp_not:N\tikzset{label={\exp_not:n{[#2,blue]#1}\c_colon_str\exp_not:n{\c_iskustvo_highlight_font_tl#3}}}}

which is not exactly shorter. To avoid doubling the hashes you can use the e-type expansion instead of x, but there's not much else to gain from it. The e-type is the e-TeX primitive \expanded (introduced in pdfTeX and XeTeX in TeXLive 2019 and LuaTeX much earlier) or, in older distribution, an emulation of it written in expl3 (which makes it much slower).


The other approach is to define a macro which takes as argument the token you want to replace. The basic idea is to do \def\__temp:w#1{<stuff with #1>} and \exp_args:No\__temp:w{\c_colon_str}, then the expansion of \c_colon_str will be inserted in the proper places. In your case, the label would change from (spaces added for clarity):

    \tikzset { label = {[##2, blue]##1 \c_colon_str \c_iskustvo_highlight_font_tl ##3} }

to:

\cs_set_protected:Npn \__iskustvo_tmp:w #1
  { \tikzset { label = {[##2, blue]##1      #1      \c_iskustvo_highlight_font_tl ##3} } }
\exp_args:NV \__iskustvo_tmp:w \c_colon_str

The advantage of this method is that you'll hardly run into expansion problems. The downside is that there is a whole lot of boilerplate code to define the temporary macro and use it, plus all hashes must be doubled. It's basically a matter of choice now.


However TikZ is not designed work under unusual catcode regimes (such as expl3's), so I recommend you leave TikZ code separate from expl3 code to avoid this kind of problem. Indeed, interfacing LaTeX3 code with LaTeX2e code is a recurrent problem, mainly due to the different catcode settings. One way to do that is to replace _ by @ in a variable name, so that \c_iskustvo_highlight_font_tl becomes \c@iskustvo@highlight@font@tl. In fact this is done in expl3.sty to keep track of load-time options before loading the LaTeX3 kernel itself. My suggestion for your code:

\documentclass{article}
\RequirePackage[utf8]{inputenc}
\RequirePackage{tikz}
\RequirePackage{xparse}
\usetikzlibrary{fit}
\usetikzlibrary{shapes}
\makeatletter
\ExplSyntaxOn
\tl_const:Nn \c@iskustvo@highlight@font@tl {\fontsize {20} {24} \selectfont \bfseries}
\ExplSyntaxOff
% #1 Angle
% #2 Relative position
% #3 Text
\tikzset
  {
    highlight/.style n args = 3%<--
      {
        ellipse,
        inner sep = 0mm,
        draw = blue,
        dashed,
        very thick,
        label = {[#2, blue]#1:\c@iskustvo@highlight@font@tl #3}
      }
  }
\makeatother
\begin{document}
\begin{tikzpicture}
    \node (node_to_highlight) [minimum size = 1cm, draw] at (0, 0) {};
    \node (highlight) [highlight = {210}{below left}{1}, fit = (node_to_highlight)] {};
\end{tikzpicture}
\end{document}

A side note: the convention for naming variables is \⟨scope⟩_⟨module⟩_⟨name⟩_⟨type⟩. ⟨scope⟩ is either local, global, or constant, ⟨module⟩ should be the prefix of the package your are writing or something else unique for your code, ⟨name⟩ is a descriptive name for the variable, and ⟨type⟩ is the type of the variable. In your case, it's a constant (because you used \tl_const:Nn), you left out a module name so I used your user name, iskustvo, the name of the variable is highlight_font, and the type is a token list variable. Putting it all together you have \c_iskustvo_highlight_font_tl.

  • 2
    TikZ works under the normal category code regime for LaTeX2e, which is the same as for plain TeX, except after \makeatletter. There, : is of category “other” (12), cf. the TeXbook p. 37. – frougon Jul 25 '19 at 10:47
  • 1
    @Iskustvo Oh, I feel stupid now ;-) But (unless you load babel with a language that makes : active, like French) the : is catcode 12, as frougoun said. – Phelype Oleinik Jul 25 '19 at 12:19

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.