Как сделать перекрестные ссылки на подписи с разными счетчиками?

Как сделать перекрестные ссылки на подписи с разными счетчиками?

Я хочу определить гибкий способ включения фрагментов исходного кода и/или рисунков в группу, которая будет действовать как плавающая среда рисунков.

Идея состоит в том, чтобы иметь возможность выбрать счетчик с параметром typeкоманды \insertCaption. Если это «Listing», используйте один счетчик, если это «Figure», используйте другой.

Проблема, с которой я столкнулся, заключается в том, что я не могу правильно выбрать счетчик. Он отображается в заголовке, но всякий раз, когда я ссылаюсь на группу, он не показывает правильную нумерацию. Пожалуйста, рассмотрите следующий нерабочий пример:

\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}

Предполагая, что текстовые и графические файлы присутствуют, я получаю следующий PDF-файл:

PDF-файл, полученный из кода выше

Как видно, цифры в подписи и перекрестных ссылках не совпадают.

решение1

Я не предлагаю решения, поскольку вы не уточнили, используете ли вы это в качестве учебного упражнения или для реального применения, поэтому я не знаю, какой ответ дать.

Это ответ на вопрос «почему?», а не на вопрос «как?».

Каждый раз, когда вы используете \captionвнутри figureсреды, figureсчетчик увеличивается. Информация записывается в .auxфайл для использования в перекрестных ссылках и т.д.

Вы изменили внешний вид формата метки заголовка, но не затронули используемый счетчик. Ваш формат не использует используемый счетчик. То есть он не использует figure. Поэтому вы не видите значение, используемое в figureсамой среде. Вы видите только свои пользовательские счетчики. Но они не используются для перекрестной ссылки. Поэтому, когда вы ссылаетесь на него позже, вы получаете информацию, записанную в .aux, который использует figure. И указанный вами формат здесь не активен, поэтому на этот раз используется значение счетчика.

Чтобы понять, что происходит, посмотрите на .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}{}{}{}}

Обратите внимание, что здесь используются именно те данные, которые мы ожидаем.

\@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}{}{}{}}

Вот наша первая цифра.

\@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}{}{}{}}

Здесь мы видим информацию, записанную для второго float. Обратите внимание, что значение, записанное в , .auxиспользует figureсчетчик, хотя это нигде не используется в формате заголовка.

\@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 }

Итак, теперь мы получаем 3для третьего float. Опять же, значение figureне используется при наборе подписей.

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

Опять же, мы figureувеличиваем счетчик, хотя это и не должно быть так, figureпоскольку это происходит внутри figureсреды.

\@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}

Если вы предпочитаете видеть, что происходит в скомпилированном документе, вот код, который может помочь. Вам придется скомпилировать его, так как мои изображения сгорели из-за ошибки Okular. Будьте осторожны: фактический вывод крайне уродлив.

% 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}

Связанный контент