¿Cómo hacer referencias cruzadas de subtítulos con diferentes contadores?

¿Cómo hacer referencias cruzadas de subtítulos con diferentes contadores?

Quiero definir una manera flexible de incluir piezas de código fuente y/o figuras en un grupo que actúa como un entorno de figuras flotantes.

La idea es poder seleccionar el contador con el parámetro typedel comando \insertCaption. Si es 'Listado' use un contador, si es 'Figura', use el otro.

El problema al que me enfrento es que no puedo seleccionar correctamente el contador. Se muestra bien en el título, pero cada vez que hago referencia al grupo, no muestra la numeración correcta. Considere el siguiente ejemplo que no funciona:

\documentclass{book}
\usepackage{geometry}
\geometry{margin=0.2in,papersize={8.5in,11in}}
\usepackage{graphicx}
\usepackage{pgfkeys}
\usepackage{subcaption}
\usepackage{ifthen}
\usepackage{listings}
\usepackage{xcolor}
\definecolor{lightgray}{gray}{0.9}

\makeatletter

\pgfkeys{/fig/entities/.cd,
    file/.initial={},
    width/.initial={},
    caption/.initial={},
    label/.initial={},
}
\def\fig@set@keys#1{\pgfkeys{/fig/entities/.cd,#1}}
\def\fig@get#1{\pgfkeysvalueof{/fig/entities/#1}}
\def\fig@set@defaults{\pgfkeys{/fig/entities/.cd,
    file/.initial={DUMMY},
    width/.initial={0.6},
    caption/.initial={},
    label/.initial={},
}}
\newcommand\insertFigure[1]{%%
    \fig@set@defaults%%
    \fig@set@keys{#1}%%
    \bgroup\begin{subfigure}[b]{\fig@get{width}\textwidth}%%
        \centering%%
        \includegraphics[width=1\textwidth]{\fig@get{file}}%%
        \ifthenelse{\NOT\equal{\fig@get{caption}}{}}{%%
            \ifthenelse{\equal{\fig@get{caption}}{abc}}{%%
                \caption{}%%
            }{%%
                \caption{\fig@get{caption}}%%
            }%%
            \ifthenelse{\NOT\equal{\fig@get{label}}{}}{%%
                \label{\fig@get{label}}%%
            }{}%%
        }{}%%
    \end{subfigure}\egroup%%
}


\pgfkeys{/code/entities/.cd,
    file/.initial={},
    width/.initial={},
    caption/.initial={},
    label/.initial={},
}
\def\code@set@keys#1{\pgfkeys{/code/entities/.cd,#1}}
\def\code@get#1{\pgfkeysvalueof{/code/entities/#1}}
\def\code@set@defaults{\pgfkeys{/code/entities/.cd,
    file/.initial={DUMMY},
    width/.initial={0.6},
    caption/.initial={},
    label/.initial={},
}}
\newcommand\insertCode[1]{%%
    \code@set@defaults%%
    \code@set@keys{#1}%%
    \bgroup\begin{subfigure}[b]{\code@get{width}\textwidth}%%
        \centering%%
        \fcolorbox{lightgray}{lightgray}{%%
            \begin{minipage}[t]{0.98\textwidth}
                \hspace{0pt}\vspace{-1pt}%% hack to align \fbox with \lstinputlisting.
                \lstinputlisting[basicstyle=\ttfamily\scriptsize]%%
                                {\code@get{file}}%%
            \end{minipage}%%
        }%%
        \ifthenelse{\NOT\equal{\code@get{caption}}{}}{%%
            \ifthenelse{\equal{\code@get{caption}}{abc}}{%%
                \caption{}%%
            }{%%
                \caption{\code@get{caption}}%%
            }%%
            \ifthenelse{\NOT\equal{\code@get{label}}{}}{%%
                \label{\code@get{label}}%%
            }{}%%
        }{}%%
    \end{subfigure}\egroup%%
}


\pgfkeys{/capt/entities/.cd,
    type/.initial={},
    caption/.initial={},
    label/.initial={},
}
\def\capt@set@keys#1{\pgfkeys{/capt/entities/.cd,#1}}
\def\capt@get#1{\pgfkeysvalueof{/capt/entities/#1}}
\def\capt@set@defaults{\pgfkeys{/capt/entities/.cd,
    type/.initial={Figure},
    caption/.initial={},
    label/.initial={},
}}
% counter for captions of type 'Figure' and 'Listing'
\newcounter{figurecounter}[chapter]
\newcounter{codecounter}[chapter]
\DeclareCaptionLabelFormat{figurefmt}{Figure~\thechapter.\thefigurecounter}
\DeclareCaptionLabelFormat{codefmt}{Listing~\thechapter.\thecodecounter}

\newcommand\insertCaption[1]{%%
    \capt@set@defaults%%
    \capt@set@keys{#1}%%
    \ifthenelse{\NOT\equal{\capt@get{caption}}{}}{%%
        \ifthenelse{\equal{\capt@get{type}}{Listing}}{%%
            \captionsetup{name=Listing}%%
            \refstepcounter{codecounter}%%
            \captionsetup{labelformat=codefmt}%%
        }{%%
            \captionsetup{name=Figure}%%
            \refstepcounter{figurecounter}%%
            \captionsetup{labelformat=figurefmt}%%
        }%%
        \ifthenelse{\equal{\capt@get{caption}}{abc}}{%%
            \caption{}%%
        }{%%
            \caption{\capt@get{caption}}%%
        }%%
        \ifthenelse{\NOT\equal{\capt@get{label}}{}}{%%
            \label{\capt@get{label}}%%
        }{}%%
    }{}%%
}

\newcommand\FloatingElements[1]{%%
    \begin{figure}[h!]\centering#1\end{figure}%%
}

\begin{document}
\chapter{Chapter One}
\FloatingElements{
  \insertFigure{
    file={figA},
    width=0.2,
    caption={abc},
  }\hfil%%
  \insertCode{
    file={textA.txt},
    width=0.3,
    caption={abc},
    label={the-first-text}
  }%%
  \insertCaption{
    type=Figure,
    caption={This is a figure with some text, use the Figure counter.},
    label={group1},
  }%%
}

\FloatingElements{
  \insertCode{
    file={textB.txt},
    width=0.8,
  }%%
  \insertCaption{
    type=Listing,
    caption={This is just text B, so use the Listing counter.},
    label={group2},
  }%%
}

\FloatingElements{
  \insertFigure{
    file={figB},
    width=0.2,
  }%%
  \insertCaption{
    type=Figure,
    caption={This is just a figure, so use the Figure counter.},
    label={group3},
  }%%
}

\FloatingElements{
  \insertCode{
    file={textC.txt},
    width=0.2,
    caption={abc},
    label={text-c}
  }\hfil%%
  \insertCode{
    file={textD.txt},
    width=0.2,
    caption={abc},
  }%%
  \insertCaption{
    type=Listing,
    caption={These are texts C and D, so use the Listing counter.},
    label={group4},
  }%%
}

\textbf{Cross references:}\\
Figure~\ref{group1} has the figureA and textA. Listing~\ref{the-first-text} is within the same Figure~\ref{group1}.\\
Listing~\ref{group2} only has the textB.\\
Figure~\ref{group3} only has the figureB.\\
Listing~\ref{group4} has the texts C and D. And sub-listing~\ref{text-c} is textC.

\end{document}

Suponiendo que los archivos de texto e imagen estén ahí, obtengo el siguiente pdf:

PDF obtenido del código anterior

Como se puede observar, los números del título y las referencias cruzadas no coinciden.

Respuesta1

No estoy ofreciendo una solución porque no has aclarado si estás usando esto como un ejercicio de aprendizaje o para un uso real, así que no sé qué tipo de respuesta dar.

Esta es una respuesta al '¿por qué?' en lugar del '¿cómo?'

Cada vez que se utiliza \captiondentro de un figureentorno, el figurecontador se incrementa. La información se escribe en el .auxarchivo para su uso en referencias cruzadas, etc.

Ha modificado la apariencia del formato de la etiqueta de título, pero no ha tocado el contador que se utiliza. Su formato no utiliza el contador utilizado. Es decir, no utiliza figure. Por lo tanto, no se ve el valor utilizado en el figureentorno mismo. Solo verás tus contadores personalizados. Pero esos no se utilizan para la referencia cruzada. Entonces, cuando hagas referencia a él más adelante, obtendrás la información escrita en .aux, que usa figure. Y el formato que ha especificado no está activo aquí, por lo que esta vez se usa el valor del contador.

Para comprender lo que está sucediendo, mire .aux:

\relax
\@writefile{toc}{\contentsline {chapter}{\numberline {1}Chapter One}{1}{}\protected@file@percent }
\@writefile{lof}{\addvspace {10\p@ }}
\@writefile{lot}{\addvspace {10\p@ }}
\@writefile{lol}{\contentsline {lstlisting}{indentfirst.sty}{1}{}\protected@file@percent }
\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
\newlabel{the-first-text}{{1.1b}{1}{}{}{}}
\newlabel{sub@the-first-text}{{b}{1}{}{}{}}

Tenga en cuenta que estos utilizan lo que esperaríamos.

\@writefile{lof}{\contentsline {figure}{\numberline {1.1}{\ignorespaces This is a figure with some text, use the Figure counter.}}{1}{}\protected@file@percent }
\newlabel{group1}{{1.1}{1}{}{}{}}

Aquí está nuestra primera figura.

\@writefile{lol}{\contentsline {lstlisting}{indentfirst.sty}{2}{}\protected@file@percent }
\@writefile{lof}{\contentsline {figure}{\numberline {1.2}{\ignorespaces This is just text B, so use the Listing counter.}}{2}{}\protected@file@percent }
\newlabel{group2}{{1.2}{2}{}{}{}}

Aquí vemos la información escrita para la segunda carroza. Tenga en cuenta que el valor escrito en el .auxestá utilizando el figurecontador, aunque este no se utiliza en ninguna parte del formato del título.

\@writefile{lof}{\contentsline {figure}{\numberline {1.3}{\ignorespaces This is just a figure, so use the Figure counter.}}{2}{}\protected@file@percent }
\newlabel{group3}{{1.3}{2}{}{}{}}
\@writefile{lol}{\contentsline {lstlisting}{indentfirst.sty}{3}{}\protected@file@percent }

Así que ahora vamos 3por la tercera carroza. Nuevamente, el valor de figureno se utiliza al componer los subtítulos.

\newlabel{text-c}{{1.4a}{3}{}{}{}}
\newlabel{sub@text-c}{{a}{3}{}{}{}}

Nuevamente, incrementamos el figurecontador, aunque esto no pretende ser así figureporque está sucediendo dentro del figureentorno.

\@writefile{lol}{\contentsline {lstlisting}{indentfirst.sty}{3}{}\protected@file@percent }
\@writefile{lof}{\contentsline {figure}{\numberline {1.4}{\ignorespaces These are texts C and D, so use the Listing counter.}}{3}{}\protected@file@percent }
\newlabel{group4}{{1.4}{3}{}{}{}}
\gdef \@abspage@last{3}

Si prefiere ver lo que sucede en un documento compilado, aquí tiene un código que puede ayudar. Tendrás que compilarlo ya que mis imágenes están tostadas debido a un error de Okular. Tenga cuidado: el resultado real es extremadamente feo.

% WHY? NOT HOW?
\documentclass{book}
\usepackage{geometry}
\geometry{margin=0.2in,papersize={8.5in,11in}}
\usepackage[draft]{graphicx}
\usepackage{pgfkeys}
\usepackage{subcaption}
\usepackage{ifthen}
\usepackage{listings}
\usepackage{xcolor}
\definecolor{lightgray}{gray}{0.9}

\makeatletter

\pgfkeys{/fig/entities/.cd,
    file/.initial={},
    width/.initial={},
    caption/.initial={},
    label/.initial={},
}
\def\fig@set@keys#1{\pgfkeys{/fig/entities/.cd,#1}}
\def\fig@get#1{\pgfkeysvalueof{/fig/entities/#1}}
\def\fig@set@defaults{\pgfkeys{/fig/entities/.cd,
    file/.initial={DUMMY},
    width/.initial={0.6},
    caption/.initial={},
    label/.initial={},
}}
\newcommand\insertFigure[1]{%%
  \fig@set@defaults%%
  \fig@set@keys{#1}%%
  \bgroup\begin{subfigure}[b]{\fig@get{width}\textwidth}%%
    \centering%%
    \includegraphics[width=1\textwidth]{\fig@get{file}}%%
    \ifthenelse{\NOT\equal{\fig@get{caption}}{}}{%%
      \ifthenelse{\equal{\fig@get{caption}}{abc}}{%%
        \caption{}%%
      }{%%
        \caption{\fig@get{caption}}%%
      }%%
      \ifthenelse{\NOT\equal{\fig@get{label}}{}}{%%
        \label{\fig@get{label}}%%
      }{}%%
    }{}%%
  \end{subfigure}\egroup%%
}


\pgfkeys{/code/entities/.cd,
    file/.initial={},
    width/.initial={},
    caption/.initial={},
    label/.initial={},
}
\def\code@set@keys#1{\pgfkeys{/code/entities/.cd,#1}}
\def\code@get#1{\pgfkeysvalueof{/code/entities/#1}}
\def\code@set@defaults{\pgfkeys{/code/entities/.cd,
    file/.initial={DUMMY},
    width/.initial={0.6},
    caption/.initial={},
    label/.initial={},
}}
\newcommand\insertCode[1]{%%
  \code@set@defaults%%
  \code@set@keys{#1}%%
  \bgroup\begin{subfigure}[b]{\code@get{width}\textwidth}%%
    \centering%%
    \fcolorbox{lightgray}{lightgray}{%%
      \begin{minipage}[t]{0.98\textwidth}
        \hspace{0pt}\vspace{-1pt}%% hack to align \fbox with \lstinputlisting.
        \lstinputlisting[basicstyle=\ttfamily\scriptsize]%%
        {\code@get{file}}%%
      \end{minipage}%%
    }%%
    \ifthenelse{\NOT\equal{\code@get{caption}}{}}{%%
      \ifthenelse{\equal{\code@get{caption}}{abc}}{%%
        \caption{}%%
      }{%%
        \caption{\code@get{caption}}%%
      }%%
      \ifthenelse{\NOT\equal{\code@get{label}}{}}{%%
        \label{\code@get{label}}%%
      }{}%%
    }{}%%
  \end{subfigure}\egroup%%
}


\pgfkeys{/capt/entities/.cd,
  type/.initial={},
  caption/.initial={},
  label/.initial={},
}
\def\capt@set@keys#1{\pgfkeys{/capt/entities/.cd,#1}}
\def\capt@get#1{\pgfkeysvalueof{/capt/entities/#1}}
\def\capt@set@defaults{\pgfkeys{/capt/ent    type/.initial={Figure},
    caption/.initial={},
    label/.initial={},ities/.cd,
    type/.initial={Figure},
    caption/.initial={},
    label/.initial={},
}}
% counter for captions of type 'Figure' and 'Listing'
\newcounter{figurecounter}[chapter]
\newcounter{codecounter}[chapter]
\DeclareCaptionLabelFormat{figurefmt}{Figure~\thechapter.\thefigurecounter}
\DeclareCaptionLabelFormat{codefmt}{Listing~\thechapter.\thecodecounter}

\newcommand\insertCaption[1]{%%
  \capt@set@defaults%%
  \capt@set@keys{#1}%%
  \ifthenelse{\NOT\equal{\capt@get{caption}}{}}{%%
    \ifthenelse{\equal{\capt@get{type}}{Listing}}{%%
      \captionsetup{name=Listing}%%
      \refstepcounter{codecounter}%%
      \captionsetup{labelformat=codefmt}%%
    }{%%
      \captionsetup{name=Figure}%%
      \refstepcounter{figurecounter}%%
      \captionsetup{labelformat=figurefmt}%%
    }%%
    \ifthenelse{\equal{\capt@get{caption}}{abc}}{%%
      \caption{}%%
    }{%%
      \caption{\capt@get{caption}}%%
    }%%
    \ifthenelse{\NOT\equal{\capt@get{label}}{}}{%%
      \label{\capt@get{label}}%%
    }{}%%
  }{}%%
}

\NewDocumentCommand \displaycounter { O {figure} }
{%
  \texttt{\textbackslash the#1} is \csname the#1\endcsname
}
\newcommand\FloatingElements[1]{%%
  \displaycounter
  \begin{figure}[ht!]
    \displaycounter
    
    \centering#1
    
    \displaycounter
  \end{figure}\par
  \displaycounter
}

\begin{document}
\chapter{Chapter One}
\FloatingElements{
  \insertFigure{
    file={example-image-a},
    width=0.2,
    caption={abc},
  }\hfil
  \insertCode{
    file={indentfirst.sty},
    width=0.3,
    caption={abc},
    label={the-first-text}
  }
  
  \displaycounter
%   \displaycounter[lstlisting]
  \insertCaption{
    type=Figure,
    caption={This is a figure with some text, use the Figure counter.},
    label={group1},
  }
  
  \displaycounter
%   \displaycounter[lstlisting]
}

\FloatingElements{
  \insertCode{
    file={indentfirst.sty},
    width=0.8,
  }%%
  \insertCaption{
    type=Listing,
    caption={This is just text B, so use the Listing counter.},
    label={group2},
  }%%
}

\FloatingElements{
  \insertFigure{
    file={example-image-b},
    width=0.2,
  }%%
  \insertCaption{
    type=Figure,
    caption={This is just a figure, so use the Figure counter.},
    label={group3},
  }%%
}

\FloatingElements{
  \insertCode{
    file={indentfirst.sty},
    width=0.2,
    caption={abc},
    label={text-c}
  }\hfil%%
  \insertCode{
    file={indentfirst.sty},
    width=0.2,
    caption={abc},
  }%%
  \insertCaption{
    type=Listing,
    caption={These are texts C and D, so use the Listing counter.},
    label={group4},
  }%%
}\displaycounter

\textbf{Cross references:}

Figure~\ref{group1} has the figureA and textA. Listing~\ref{the-first-text} is within the same Figure~\ref{group1}.

Listing~\ref{group2} only has the textB.

Figure~\ref{group3} only has the figureB.

Listing~\ref{group4} has the texts C and D. And sub-listing~\ref{text-c} is textC.

\end{document}

información relacionada