1

Continuation of topics tcolorbox: specific width and height settings for odd and even pages and Distance Variable in front or back pages.

I'd like to set lengths conditionally based on the expansion of a macro (\C here, which is set by datatool in my code) and whether we are on an odd or even page:

\C = 1 & ifoddpage = true  => \xLength = 2cm, \yLength = 1.5cm
\C = 1 & ifoddpage = false => \xLength = 4cm, \yLength = 2cm
\C = 2 & ifoddpage = true  => \xLength = 3cm, \yLength = 2cm
\C = 2 & ifoddpage = false => \xLength = 5cm, \yLength = 3cm
\C = 3 & ifoddpage = true  => \xLength = 4cm, \yLength = 3cm
\C = 3 & ifoddpage = false => \xLength = 4cm, \yLength = 1.5cm
…
\C = 10...

Here is minimal code based on code from the above links:

\documentclass{article}
\usepackage{datatool}
\usepackage[strict]{changepage}
\usepackage{filecontents}
\usepackage{tcolorbox}
\usepackage[absolute,overlay]{textpos}
\usepackage{xcolor}
\begin{filecontents*}{test1.csv}
Acol, Bcol,Ccol
Ax,Bx,1
Ay,By,3
A1,B22,1
A2,B44,2
A3,B11,3
A4,B55,4
\end{filecontents*}

\DTLloaddb{mydata1}{test1.csv}

\newlength{\xlengthForOddPages}
\newlength{\xlengthForEvenPages}
\newlength{\ylengthForOddPages}
\newlength{\ylengthForEvenPages}

\setlength{\xlengthForOddPages}{2cm}
\setlength{\xlengthForEvenPages}{10cm}
\setlength{\ylengthForOddPages}{1.5cm}
\setlength{\ylengthForEvenPages}{5cm}

\newif\ifmyoddpage              % always set globally, contrary to \ifoddpage

\newcommand*{\xpageDependent}{%
  \ifmyoddpage
    \expandafter
    \xlengthForOddPages
  \else
    \expandafter
    \xlengthForEvenPages
  \fi
}

\newcommand*{\ypageDependent}{%
  \ifmyoddpage
    \expandafter
    \ylengthForOddPages
  \else
    \expandafter
    \ylengthForEvenPages
  \fi
}

\newcommand*{\mycheckoddpage}{%
  \checkoddpage
  \global\let\ifmyoddpage=\ifoddpage
}

\newlength{\xLength}
\newlength{\yLength}

\begin{document}

\DTLforeach*{mydata1}{\A=Acol,\B=Bcol,\C=Ccol}%
{%
\begin{tcolorbox}[phantom={\mycheckoddpage},
                  width=\xpageDependent,height=\ypageDependent,
                  title={Title}]
  \C
\end{tcolorbox}
% --> Here, \xLength and \yLength should be set according
%     to the conditions described above. <--
\begin{textblock*}{5cm}(\xLength,\yLength)
\color{red} text abc
\end{textblock*}

\newpage
}
\end{document}

Thanks in advance.

  • 1
    Are you sure that the width of the tcolorbox should be the same as the horizontal offset of your text positioned with textblock* (your example and text before the example use \xpageDependent for both)? Same question for \ypageDependent, of course. (Also, \xpageDependent and \ypageDependent should be used only we you can be sure that the most recent call to \mycheckoddpage placed its internal label on the same page. I assume this is the case here for their second use, i.e., in the arguments of \begin{textblock*}. – frougon Jul 13 at 12:37
  • [continuation of the previous comment] In other words, you can guarantee that the textblock* is on the same page as the corresponding tcolorbox.) Or maybe you want different lengths, but obtain these through a redefinition of \xpageDependent and \ypageDependent between the tcolorbox and when the textblock* is set up? – frougon Jul 13 at 12:48
  • @frougon should be different length. I just want to test. Sorry & Thanks – latexforti Jul 13 at 16:14
  • Since the tcolorbox doesn't seem to play a significant role here, I believe one might want to remove it from the question. Of course, the screenshots in my answer would then need to be redone (changing the code is quicker)... – frougon Jul 13 at 18:51
2

If I correctly understood your needs, the following should do what you want:

\documentclass{article}
\usepackage{datatool}
\usepackage[strict]{changepage}
\usepackage{filecontents}
\usepackage{tcolorbox}
\usepackage[absolute,overlay]{textpos}
\usepackage{xcolor}
\usepackage{xparse}

\begin{filecontents*}{test1.csv}
Acol, Bcol,Ccol
Ax,Bx,1
Ay,By,3
A1,B22,1
A2,B44,2
A3,B11,3
\end{filecontents*}
% If you include a line such as 'A4,B55,4' without saying how \xLength and
% \yLength should be set in this case, you'll get the error message:
%
%   Package lforti Error: Encountered value '4', but no case was written for it.

\DTLloaddb{mydata1}{test1.csv}

\newlength{\xlengthForOddPages}
\newlength{\xlengthForEvenPages}
\newlength{\ylengthForOddPages}
\newlength{\ylengthForEvenPages}

\setlength{\xlengthForOddPages}{2cm}
\setlength{\xlengthForEvenPages}{10cm}
\setlength{\ylengthForOddPages}{1.5cm}
\setlength{\ylengthForEvenPages}{5cm}

\newif\ifmyoddpage              % always set globally, contrary to \ifoddpage

\newcommand*{\xpageDependent}{%
  \ifmyoddpage
    \expandafter
    \xlengthForOddPages
  \else
    \expandafter
    \xlengthForEvenPages
  \fi
}

\newcommand*{\ypageDependent}{%
  \ifmyoddpage
    \expandafter
    \ylengthForOddPages
  \else
    \expandafter
    \ylengthForEvenPages
  \fi
}

\newcommand*{\mycheckoddpage}{%
  \checkoddpage
  \global\let\ifmyoddpage=\ifoddpage
}

\ExplSyntaxOn

\msg_new:nnn { lforti } { unhandled-value }
  { Encountered~value~'\exp_not:n~{#1}',~but~no~case~was~written~for~it. }

\tl_new:N \l__lforti_cond_length_setup_conds_tl
\seq_new:N \l__lforti_cond_length_setup_tmpa_seq
\seq_new:N \l__lforti_cond_length_setup_tmpb_seq

% Flexible length setup that may depend on whether it is called on an odd page
% or on an even page.
%
% #1: a LaTeX length command (or a TeX \skipdef token) to set according to
%     #2 and #3
% #2: the value to test against
% #3: a comma list describing the different cases:
%
%     {
%       {value_1, length-for-odd-pages, length-for-even-pages},
%       {value_2, length-for-odd-pages, length-for-even-pages},
%       ...
%       {value_n, length-for-odd-pages, length-for-even-pages},
%     }
%
% #2 is compared against value_1, value_2, ..., value_n to determine a
% matching “line”, then length command #1 is set to the length-for-odd-pages or
% length-for-even-pages of the matching line, depending on whether the label set
% by \checkoddpage (used at the beginning of the function) is located on an
% odd page or on an even page.
\cs_new_protected:Npn \lforti_cond_length_setup:Nnn #1#2#3
  {
    \checkoddpage               % make sure it is used on the relevant page
    \seq_set_from_clist:Nn \l__lforti_cond_length_setup_tmpa_seq {#3}
    \tl_clear:N \l__lforti_cond_length_setup_conds_tl

    \seq_map_inline:Nn \l__lforti_cond_length_setup_tmpa_seq
      {
        \seq_set_from_clist:Nn \l__lforti_cond_length_setup_tmpb_seq {##1}
        \tl_put_right:Nx \l__lforti_cond_length_setup_conds_tl
          {
            % Value to check against
            { \seq_item:Nn \l__lforti_cond_length_setup_tmpb_seq { 1 } }
            { % Corresponding setup code for #1
              \exp_not:n { \skip_set:Nn #1 }
                {
                  \seq_item:Nn
                    \l__lforti_cond_length_setup_tmpb_seq
                    { \ifoddpage 2 \else 3 \fi }
                }
            }
          }
      }

    % Handle value #2 according to the cases described by
    % \l__lforti_cond_length_setup_conds_tl, which was assembled from #3
    \str_case:nVF {#2} \l__lforti_cond_length_setup_conds_tl
      { \msg_error:nnn { lforti } { unhandled-value } {#2} }
  }

\cs_generate_variant:Nn \lforti_cond_length_setup:Nnn { Nx }

% Same as \lforti_cond_length_setup:Nnn (see above), except that the second
% argument will be fully expanded (\lforti_cond_length_setup:Nnn will only see
% the *result* of this expansion in its #2).
\NewDocumentCommand \lforticondlengthsetup { m m m }
  {
    \lforti_cond_length_setup:Nxn #1 {#2} {#3}
  }

\ExplSyntaxOff

\newlength{\xLength}
\newlength{\yLength}

\begin{document}

\DTLforeach*{mydata1}{\A=Acol,\B=Bcol,\C=Ccol}%
{%
\lforticondlengthsetup{\xLength}{\C}{%
  {1, 2cm, 4cm}, % When \C expands to 1, use 2cm on odd pages, 4cm on even pages
  {2, 3cm, 5cm}, % When \C expands to 2, use 3cm on odd pages, 5cm on even pages
  {3, 4cm, 4cm}, % When \C expands to 3, use 4cm on all pages
}%
\lforticondlengthsetup{\yLength}{\C}{% analogous setup for \yLength
  {1, 1.5cm, 2cm},
  {2, 2cm, 3cm},
  {3, 3cm, 1.5cm},
}%
\begin{textblock*}{5cm}(\xLength,\yLength)
  \color{blue!50!black}%
  \noindent
  \texttt{\string\xLength} = \the\xLength\\
  \texttt{\string\yLength} = \the\yLength
\end{textblock*}

\begin{tcolorbox}[phantom={\mycheckoddpage},
                  width=\xpageDependent,height=\ypageDependent,
                  title={Title}]
  \C
\end{tcolorbox}

\newpage
}

\end{document}

Here is the top of each page:

Page 1


Page 2


Page 3


Page 4


Page 5

My code relies on a flexible length-setting command that I called \lforticondlengthsetup. This commands expects its first argument to be a LaTeX length command (or a TeX \skipdef token). It fully expands its second argument and compares the result against the first element of each sublist specified in the third argument. For instance, \xLength and \yLength are set this way to follow the specifications from your question:

\lforticondlengthsetup{\xLength}{\C}{%
  {1, 2cm, 4cm}, % When \C expands to 1, use 2cm on odd pages, 4cm on even pages
  {2, 3cm, 5cm}, % When \C expands to 2, use 3cm on odd pages, 5cm on even pages
  {3, 4cm, 4cm}, % When \C expands to 3, use 4cm on all pages
}%
\lforticondlengthsetup{\yLength}{\C}{% analogous setup for \yLength
  {1, 1.5cm, 2cm},
  {2, 2cm, 3cm},
  {3, 3cm, 1.5cm},

You have to ensure that such length setup commands are issued on the same page where \xLength and \yLength are used. This should be the case in the full document I gave above.

The length values (second and third element of each sublist present in the third argument of \lforticondlengthsetup) can be anything that is a valid LaTeX3 〈skip expression〉. For instance, the following code is valid as long as:

  • \xLength and \yLength have been declared with \newlength;

  • \someMacro recursively expands to either 1, or foo, or bar baz (in your example, this role is played by the \C macro that expands to values located in the third column of your .csv file).

\lforticondlengthsetup{\yLength}{\someMacro}{
  {1, 0.7\xLength, 2cm plus 0.1cm minus 0.2cm},
  {foo, 2cm plus 1fill, 0pt plus 3fill},
  {bar baz, 3cm plus 1fil minus 0.2fill, 1.5cm},
}

Finally, since \checkoddpage uses a label/ref mechanism, don't forget that you need to compile several times—normally only twice—to get reliable results!

  • 1
    Yes, \aLength, \bLength, etc. should be quite fine as long as you have declared them with \newlength. \lforticondlengthsetup is fairly generic: you can use it with any LaTeX length command in the first argument (and you can even use lengths commands in the third argument: see the 0.7\xLength in the last example of my answer). The command works outside the loop too, this makes no difference. – frougon Jul 13 at 15:51
  • 1
    Also, if this makes things easier for you, it's quite simple to change the syntax from \lforticondlengthsetup{\someLength}{...}{...} to \lforticondlengthsetup{someLength}{...}{...} (notice the missing \ in the second one). This can make things slightly easier if you want to “construct” the length command names using macros (for instance, with something like \lforticondlengthsetup{#1Length}{...}{...} in the replacement text of one of your macros, where a, x, y, etc., could be passed as values for #1). You can always construct such names using \csname, but this requires... – frougon Jul 13 at 15:58
  • 1
    ... expanding the \csname before \lforticondlengthsetup is itself expanded: \expandafter\lforticondlengthsetup\expandafter{\csname #1Length\endcsname}{...}{...}. – frougon Jul 13 at 16:05
  • 1
    I did many tests like this one for the final example of my answer, I'm quite sure it works. Maybe \C wasn't defined where you used it? Otherwise, post the code at the end of your question (you can remove it later when it's solved). For instance, my final example can only be used at a point where \someMacro is defined. Did you compile twice? Any error message? – frougon Jul 13 at 16:14
  • 1
    The screenshot doesn't show the same code as the pastebin (Ccol vs. \C). The error in the pastebin code is that you ask \lforticondlengthsetup to expand \C at a point where \C is undefined (this is just after \begin{document}). That's exactly what I warned you against above... – frougon Jul 13 at 16:28

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.