2

Background: I'm trying to typeset a two-sided document where both visible pages should be similarly spaced. Therefore I've implemented a custom auxiliary file that stores the height (saving will not be show in MWE).

Problem: Although saving works fine, reading and evaluating seems pretty hard. The macro \sfkatalog_getdim below works just fine when used to typeset a length, but it doesn't work when I use it in \vbox_to_ht:nn (I replace the hard-coded value 1.75cm with \sfkatalog_getdim:nV { 1 } \l_tmpb_dim). The error I get is:

! Missing number, treated as zero.
<to be read again> 
\cs_set_nopar:Npx 
l.71    \sftitle{Test}

Question: How can I get my macro to be evaluated as length by \vbox_to_ht or \dim_set (I currently have no clue where to start debugging)?

\documentclass{article}
\usepackage{xparse}
\begin{filecontents*}{\jobname.sfc}
1;1.75cm,1.25cm
2;5cm,3cm
\end{filecontents*}

\ExplSyntaxOn
\prop_new:N \g_sfkatalog_ior_prop
\cs_generate_variant:Nn \prop_gput:Nnn { Nxx }
\ior_open:Nn \g_tmpa_ior { \jobname.sfc }
\ior_str_map_inline:Nn \g_tmpa_ior
    {
        \seq_set_split:Nnn \l_tmpa_seq { ; } { #1 }
        \seq_remove_all:Nn \l_tmpa_seq { }
        \prop_gput:Nxx \g_sfkatalog_ior_prop { \seq_item:Nn \l_tmpa_seq { 1 } }
            { \seq_item:Nn \l_tmpa_seq { 2 } }
    }
\ior_close:N \g_tmpa_ior

% getdim #1 -> which one, #2 -> default (as dimension)
\cs_generate_variant:Nn \prop_get:NnN { Nx }
\cs_new_nopar:Npn \sfkatalog_getdim:nn #1#2
{
    \prop_get:NxN \g_sfkatalog_ior_prop { \int_div_truncate:nn { \thepage + 1 } { 2 } } \l_tmpa_tl
    \quark_if_no_value:NTF \l_tmpa_tl
        {
            \dim_eval:n { #2 }
        }
        {
            \prop_get:NxN \g_sfkatalog_ior_prop { \int_div_truncate:nn { \thepage + 1 } { 2 } } \l_tmpa_tl
            \seq_set_split:NnV \l_tmpa_seq { , } \l_tmpa_tl
            \seq_remove_all:Nn \l_tmpa_seq { }
            \dim_set:Nn \l_tmpa_dim { \seq_item:Nn \l_tmpa_seq { #1 } }
            \dim_use:N \l_tmpa_dim
        }
}
\cs_generate_variant:Nn \sfkatalog_getdim:nn { nV }

\NewDocumentCommand { \sftitle } { m }
    {
        \clearpage
        \coffin_clear:N \l_tmpa_coffin
        \hcoffin_set:Nn \l_tmpb_coffin
            {
                \vbox:n
                    {
                        \group_begin:
                            \bfseries\Large
                            #1
                            \par
                        \group_end:
                    }
            }
        \coffin_join:NnnNnnnn \l_tmpa_coffin { l } { b }
            \l_tmpb_coffin                     { l } { b }
            { 0pt } { 0pt }
        \dim_set:Nn \l_tmpb_dim { \coffin_ht:N \l_tmpa_coffin + \coffin_dp:N \l_tmpa_coffin }
        % this will print the correct value, but does not expand right:
        % \sfkatalog_getdim:nV { 1 } \l_tmpb_dim
        % and this is the hard-coded value I'd like to replace:
        \vbox_to_ht:nn { 1.75cm }
            {
                \coffin_typeset:Nnnnn \l_tmpa_coffin { l } { b } { 0pt } { 0pt }
            }
        \par
        \bigskip
    }
\ExplSyntaxOff

\begin{document}
    \sftitle{Test}
    This is a test.

    \sftitle{A very long test without subheading which will span more than one line}
    This is a test.

    \sftitle{Test}
    This is a test.

    \sftitle{A very long test without subheading which will span more than one line}
    This is a test.
\end{document}
  • You cannot directly define \sfkatalog_getdim:nV – egreg Jan 6 '18 at 10:15
  • @egreg Thanks, I've updated it, but it still throws the same error. – TeXnician Jan 6 '18 at 10:18
  • The function is still not expandable – egreg Jan 6 '18 at 10:22
  • @egreg So probably my question is how to make it expandable. Do I have to switch data structures (the unexpandable parts are prop and seq variables, aren't they)? – TeXnician Jan 6 '18 at 10:24
  • The unexpandable part is setting a sequences in order to extract the value – egreg Jan 6 '18 at 10:29
2

In order to be used in the first argument to \vbox_to_ht:nn, the function \sfkatalog_getdim:nn (or its variant \sfkatalog_getdim:nV) should be fully expandable, which yours isn't.

The splitting of the input should be done in advance; here I store the two parts as separate properties, so as to be able to use \prop_item:Nn (or a variant thereof) that's expandable.

\documentclass{article}
\usepackage{xparse}
\begin{filecontents*}{\jobname.sfc}
1;1.75cm,1.25cm
2;5cm,3cm
\end{filecontents*}

\ExplSyntaxOn
% kernel variants
\cs_generate_variant:Nn \prop_gput:Nnn { Nxx }
\cs_generate_variant:Nn \prop_item:Nn { Nf }
\cs_generate_variant:Nn \seq_set_split:Nnn { Nnx }
\prg_generate_conditional_variant:Nnn \prop_if_in:Nn { Nf } { p, T, F, TF }

% variables
\prop_new:N \g_sfkatalog_ior_prop

% initialize
\ior_open:Nn \g_tmpa_ior { \jobname.sfc }
\ior_str_map_inline:Nn \g_tmpa_ior
 {
  \seq_set_split:Nnn \l_tmpa_seq { ; } { #1 }
  \seq_set_split:Nnx \l_tmpb_seq { , } { \seq_item:Nn \l_tmpa_seq { 2 } }
  \prop_gput:Nxx \g_sfkatalog_ior_prop
   { \seq_item:Nn \l_tmpa_seq { 1 } }
   { \seq_item:Nn \l_tmpa_seq { 2 }}
  \prop_gput:Nxx \g_sfkatalog_ior_prop
   { \seq_item:Nn \l_tmpa_seq { 1 } | 1 }
   { \seq_item:Nn \l_tmpb_seq { 1 } }
  \prop_gput:Nxx \g_sfkatalog_ior_prop
   { \seq_item:Nn \l_tmpa_seq { 1 } | 2 }
   { \seq_item:Nn \l_tmpb_seq { 2 } }
 }
\ior_close:N \g_tmpa_ior

\prop_show:N \g_sfkatalog_ior_prop % for debugging

% getdim #1 -> which one, #2 -> default (as dimension)
\cs_new:Npn \sfkatalog_getdim:nn #1 #2
 {
  \prop_if_in:NfTF \g_sfkatalog_ior_prop
   { \int_div_truncate:nn { \value{page} + 1 } { 2 } }
   {% the property exists
    \prop_item:Nf \g_sfkatalog_ior_prop
     { \int_div_truncate:nn { \value{page} + 1 } { 2 } | #1 }
   }
   {% the property does not exist
    \dim_eval:n { #2 }
   }
 }
\cs_generate_variant:Nn \sfkatalog_getdim:nn { nV }

\NewDocumentCommand { \sftitle } { m }
    {
        \clearpage
        \coffin_clear:N \l_tmpa_coffin
        \hcoffin_set:Nn \l_tmpb_coffin
            {
                \vbox:n
                    {
                        \group_begin:
                            \bfseries\Large
                            #1
                            \par
                        \group_end:
                    }
            }
        \coffin_join:NnnNnnnn \l_tmpa_coffin { l } { b }
            \l_tmpb_coffin                     { l } { b }
            { 0pt } { 0pt }
        \dim_set:Nn \l_tmpb_dim { \coffin_ht:N \l_tmpa_coffin + \coffin_dp:N \l_tmpa_coffin }
        % this will print the correct value, but does not expand right:
        % \sfkatalog_getdim:nV { 1 } \l_tmpb_dim
        % and this is the hard-coded value I'd like to replace:
        \vbox_to_ht:nn { \sfkatalog_getdim:nV { 1 } \l_tmpb_dim }
            {
                \coffin_typeset:Nnnnn \l_tmpa_coffin { l } { b } { 0pt } { 0pt }
            }
        \par
        \bigskip
    }
\ExplSyntaxOff

\begin{document}

\sftitle{Test}
This is a test.

\sftitle{A very long test without subheading which will span more than one line}
This is a test.

\sftitle{Test}
This is a test.

\sftitle{A very long test without subheading which will span more than one line}
This is a test.

\end{document}

The console will show

The property list \g_sfkatalog_ior_prop contains the pairs (without outer braces):
>  {1}  =>  {1.75cm,1.25cm}
>  {1|1}  =>  {1.75cm}
>  {1|2}  =>  {1.25cm}
>  {2}  =>  {5cm,3cm}
>  {2|1}  =>  {5cm}
>  {2|2}  =>  {3cm}.

Note. I changed \thepage into \value{page}. Never trust in \thepage to expand to a numeric value.

The initialization can be much simpler, as the data should be in a controlled format.

% initialize
\cs_new_protected:Npn \__sfkatalog_store_item:w #1 ; #2 , #3 \q_stop
 {
  \prop_gput:Nnn \g_sfkatalog_ior_prop { #1 } { #2, #3 }
  \prop_gput:Nnn \g_sfkatalog_ior_prop { #1 | 1 } { #2 }
  \prop_gput:Nnn \g_sfkatalog_ior_prop { #1 | 2 } { #3 }
 }
\ior_open:Nn \g_tmpa_ior { \jobname.sfc }
\ior_str_map_inline:Nn \g_tmpa_ior
 {
  \__sfkatalog_store_item:w #1 \q_stop
 }
\ior_close:N \g_tmpa_ior

\prop_show:N \g_sfkatalog_ior_prop % for debugging
  • Great, thanks. I've just developed a similar approach with csname token lists, but I think this one is semantically better. – TeXnician Jan 6 '18 at 11:15
  • @TeXnician I'm adding a simpler initialization phase. – egreg Jan 6 '18 at 11:16

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.