我如何描绘并随后填充任意文本的占位符?

我如何描绘并随后填充任意文本的占位符?

有没有办法分配占位符并在文档的后面设置内容?首先,我意识到这需要多次传递才能正确呈现,但我看到引用系统做了类似的事情:

请参阅第 Y 页的图 X。

X 和 Y 值在文档稍后的某个时刻确定,下一个渲染过程将填充正确的数字。我研究过如何劫持参考系统,但无法弄清楚如何让它填充除图片或页码之外的任何内容。

让问题复杂化的是,除了通用的反向引用之外,我还想要两个额外的功能。

  1. 我希望随着文档的进展,在多个位置添加更多占位符实例,并且当填充请求到来时,只有最后一个划定的实例发生变化。当实例化新的占位符槽时,所有先前的占位符应该基本上锁定到它们包含的任何内容。

  2. 不如 #1 重要,但我希望能够多次“填充”反向引用,并且只保留最后一个赋值。能够附加将是一个不错的奖励。

伪代码看起来像这样:

\section*{Section 1 \namePlaceHolder{}}

Lorum ipsum...
\setLastName{-- my stuff}
Lorum ipsum...

\section*{Section 2 \namePlaceHolder{}}

Lorum ipsum...
Lorum ipsum...

\section*{Section 3 \namePlaceHolder{}}

Lorum ipsum...
\setLastName{-- your stuff}
Lorum ipsum...
\setLasteName{-- reassigned placeholder text}

\section*{Section 4 \namePlaceHolder{}}

Lorum ipsum...
\setLastName{-- his stuff}
Lorum ipsum...

将会呈现如下内容:

期望输出

这样做的‘原因’是,我的内容包含很多\input{}来自其他文件的内容,我希望这些文件的内容能够返回并设置一些以前输入的文本中的值。

我可以采用什么机制来实现如此延迟的替换?

答案1

我仍然不完全确定你到底想如何实现它,但可以尝试一下。

基本原则:

  1. .aux“用户”界面通过以通常的方式使用在文档的开始和结束处读入的一组命令写入文件来运行。

  2. 如果您只是希望最后一条命令能够生效,那么这将非常简单:每条命令都可以覆盖前一条定义。但您的要求相当复杂。因此,基本上我们使用两个计数器和一个切换开关来跟踪事物。

  3. 占位符有两种类型:\fixplaceholder一种是“阻止”材料向后填充,\useplaceholder另一种是不会。MWE 展示了它们的工作原理。

  4. 显然,这意味着需要进行多次运行才能把事情理顺。

MWE 如下:

\documentclass{article}

\makeatletter
\newcommand{\DeclarePlaceHolder}[1]{%
  \expandafter\newcount\csname ph#1\endcsname
  \expandafter\newcount\csname phset#1\endcsname
  \csname phset#1\endcsname 0\relax}

\newcommand{\makeplaceholder}[3]{%
  \@ifundefined{ph#1}
    {\phwarnundefined{#1}}
    {\global\expandafter\def\csname #2\endcsname{#3}}}

\newcommand{\pherrorundefined}[1]{\GenericError{}
  {Undefined placeholder type '#1'}
  {You need to define a class of placeholders using \string\DeclarePlaceHolder{} 
  ^^Jbefore using them. (This could just be a typing error.)}}

\newcommand{\phwarnundefined}[1]{\GenericWarning{}
  {Warning: Reference to placeholder of type '#1' which has not been filled.}}

\newcommand{\useplaceholder}[1]{%
    \@ifundefined{ph#1}
      {\pherrorundefined{#1}}
      {\global\csname ph#1\endcsname\csname phset#1\endcsname\relax
       \def\@tempa{\expandafter\the\csname phset#1\endcsname}%
       \global\expandafter\def\csname ph\@tempa#1used\endcsname{}%
       \@ifundefined{ph@\@tempa#1value}
         {\phwarnundefined{#1}}
         {\csname ph@\@tempa #1value\endcsname}}}

\newcommand{\fixplaceholder}[1]{%
    \@ifundefined{ph#1}
      {\pherrorundefined{#1}}
      {\global\advance\csname phset#1\endcsname 1\relax
       \global\csname ph#1\endcsname\csname phset#1\endcsname\relax
       \def\@tempa{\expandafter\the\csname phset#1\endcsname}%
       \global\expandafter\def\csname ph\@tempa#1used\endcsname{}%
       \@ifundefined{ph@\@tempa#1value}
         {\phwarnundefined{#1}}
         {\csname ph@\@tempa #1value\endcsname}}}

\newcommand{\fillplaceholder}[2]{%
  \@ifundefined{ph#1}
    {\pherrorundefined{#1}}
    {\def\@tempa{\expandafter\the\csname ph#1\endcsname}%
       \@ifundefined{ph@\@tempa#1used}
       {\@ifundefined{ph@\@tempa#1set}
        {\global\advance\csname phset#1\endcsname 1\relax
         \global\expandafter\def\csname ph@\@tempa#1set\endcsname{}}%
         {}}%
       {}%
        \def\@tempa{\expandafter\the\csname ph#1\endcsname}%
        \protected@write\@auxout
       {}{\string\makeplaceholder{#1}{ph@\@tempa #1value}{#2}}}}


\makeatother

\DeclarePlaceHolder{name}

\begin{document}

\setlength{\parindent}{1em}

Placeholder labels must be pre-declared with \verb+\DeclarePlaceHolder{label}+.
Unless so declared an error will be produced.
% As can be verified by commenting out this line \setplaceholder{foo}

Placeholders are set with \verb+\fillplaceholder{label}{text}+. They only ever
\emph{backfill}, so if one is filled before any
have been used, it is effectively `dead', as the following
placeholder will be%
\fillplaceholder{name}{This will never be printed.} 
(not that there's anything to see).

Placeholders are used with either \verb+\useplaceholder{label}+ 
or with \verb+\fixplaceholder{label}+.

You can have a succession of \verb+\useplaceholder+ commands, 
and all will be `filled' back by the last \emph{active} 
\verb+\fillplaceholder+.

So these two will both be filled with \useplaceholder{name}! 
\useplaceholder{name}! \fillplaceholder{name}{happiness}


On the other hand if we use a succession of \verb+\fixplaceholder+
before a \verb+\fillplaceholder+ only the latest
will be filled (and any \verb+\useplaceholders+ \emph{before} it 
will also be empty): So these two will produce nothing
[\fixplaceholder{name}\useplaceholder{name}], 
whereas these two will: 
[\fixplaceholder{name}\useplaceholder{name}].
In other words a \verb+\fixplaceholder+ is a sort of dam, past which backfilling 
will not take place.\fillplaceholder{name}{filled}

If there are a succession of \verb+\fillplaceholder+ commands, with no 
intervening \verb+\useplaceholder+, only the last in time will `take': so 
this little placeholder will be filled with \useplaceholder{name}.

\fillplaceholder{name}{dread}% no!
\fillplaceholder{name}{unease}% no!!
\fillplaceholder{name}{satisfaction}

\section*{Section 1 \fixplaceholder{name}}

Lorum ipsum...
\fillplaceholder{name}{My Stuff}
Lorum ipsum...

\section*{Section 2 \fixplaceholder{name}}

Lorum ipsum...
Lorum ipsum...

\section*{Section 3 \fixplaceholder{name}}

Lorum ipsum...
\fillplaceholder{name}{Your Stuff}
Lorum ipsum...
\fillplaceholder{name}{Reassigned Text}

\section*{Section 4 \fixplaceholder{name}}

Lorum ipsum...
\fillplaceholder{name}{His Stuff}
Lorum ipsum...

But because placeholders only ever \emph{backfill}, 
a marooned \verb+\useplaceholder+ after everything has 
been set will be left undefined, in which case it will show 
nothing [\useplaceholder{name}] (and you get a warning).

\end{document}

输出(和使用说明)如下。正如我所说,我不确定我是否完全掌握了您的“用例”。

在此处输入图片描述

答案2

虽然Paul Stanley 的精彩回答解决了这个问题,值得“接受”并获得更多赞誉,但我在文档环境中使用它时遇到了一些小问题。以下是我对解决方案所做的调整,可能对有类似情况的其他人有用。

  • 我改变了占位符的实例化方式。我不再需要在使用前手动实例化每个占位符,也不再在占位符不存在时抛出错误,而是简单地调整了错误代码,以便在第一次使用时实例化占位符。

  • 其中两个函数有大量的通用代码,但两者之间只有一处微小的差异。我将重复的代码替换为在专用函数内部调用更通用的函数。

  • 在章节标题内放置占位符会导致目录出现问题。具体来说,使用章节编号会与计数器中的某些内容发生冲突。

    例如这个有效:

    \section*{My Section Name\FixPlaceHolder{name}}
    

    ...但事实并非如此:

    \section{My Section Name\FixPlaceHolder{name}}
    

    我想到解决方案(黑客警报!)是将占位符放在节标题格式中,如下所示:

    % Use package for customizing title formats
    \usepackage{tocloft}
    
    % Add a placeholder to the end of a string
    \newcommand*\InjectSectionPlaceHolder[1]{#1\FixPlaceHolder{name}}
    
    % Note the trailing function in the {before} section will get
    % passed the title contents as an argument
    \titleformat{section}[block]{}{}{}\InjectSectionPlaceHolder}[]
    

    添加编号部分后非常简单:

    \section{My Section}
    

    ...所有部分都将有一个专门插入的占位符。当然,您还需要为部分标题插入正确的格式,但我无论如何都会这样做。

  • 我遇到了一些名称空间冲突(据我所知与 Lilypond 和预处理有关lilypond-book),并调整了一些计数器名称以避免这种情况。更美观的是,我对顶层所有面向用户的函数名称使用了一致的大小写。

以下是我调整后的功能版本:

\newcommand*\DeclarePlaceHolder[1]{%
        \expandafter\newcount\csname ph#1\endcsname
        \expandafter\newcount\csname phset#1\endcsname
        \csname phset#1\endcsname 0\relax
}

\newcommand*\MakePlaceHolder[2]{%
        \global\expandafter\def\csname #1\endcsname{#2}
}

\newcommand*\UsePlaceHolder[1]{%
        \@ifundefined{ph#1}{\DeclarePlaceHolder{#1}}{}%
        \global\csname ph#1\endcsname\csname phset#1\endcsname\relax
        \def\@phtempa{\expandafter\the\csname phset#1\endcsname}%
        \global\expandafter\def\csname ph\@phtempa#1used\endcsname{}%
        \@ifundefined{ph@\@phtempa#1value}{%
        }{%
                \csname ph@\@phtempa #1value\endcsname
        }%
}

\newcommand*\FixPlaceHolder[1]{%
        \@ifundefined{ph#1}{\DeclarePlaceHolder{#1}}{}%
        \global\advance\csname phset#1\endcsname 1\relax
        \UsePlaceHolder{#1}
}

\newcommand\FillPlaceHolder[2]{%
        \@ifundefined{ph#1}{\DeclarePlaceHolder{#1}}{}%
        \def\@phtempa{\expandafter\the\csname ph#1\endcsname}%
        \@ifundefined{ph@\@phtempa#1used}{%
                \@ifundefined{ph@\@phtempa#1set}{%
                        \global\advance\csname phset#1\endcsname 1\relax
                        \global\expandafter\def\csname ph@\@phtempa#1set\endcsname{}%
                }{}%
        }{}%
        \def\@phtempa{\expandafter\the\csname ph#1\endcsname}%
        \protected@write\@mainaux{}{\string\MakePlaceHolder{ph@\@phtempa #1value}{#2}}
}

相关内容