我想定义两个命令:\RecordTagText{tag}{text}
存储每个唯一的tag
一些text
,并\ShowTagText{list of tags}
打印text
对应于给定标签列表的命令,但是在条件下每页text
相同的内容tag
只应打印一次(因此,如果指定输出两次,则第二次应被忽略)。
为此,我的想法是定义\RecordTagText
简单地将标签/文本信息写入辅助文件,如下所示:
\prop_new:N \g__mymodule_tag_text_prop
\NewDocumentCommand \RecordTagText { m m }
{
\iow_now:cn { @auxout }
{
\@RecordTagText { #1 } { #2 }
}
}
\NewDocumentCommand \@RecordTagText { m m }
{
\prop_gput:Nnn \g__mymodule_tag_text_prop { #1 } { #2 }
}
但是对于\ShowTagText
,我不清楚该怎么做。我的想法是为每个 放置一个标签\ShowTagText
,以便访问其页码,然后为每个标签TAG
创建一个逗号列表\g__mymodule_tag_ TAG _clist
,以记录使用此标签的标签,然后为每个页码xxx
和每个标签TAG
创建一个逗号列表,\g__mymodule_tag_ TAG _page_ xxx _clist
以记录在 页面上使用此标签的标签xxx
,最后基于这些准备来定义\ShowTagText
。
这就是我感到困惑的地方。我有以下问题:
\g__mymodule_tag_ TAG _clist
只能在第一次运行后确定,但我\clist_new
首先应该在哪里声明这个 clist via?\g__mymodule_tag_ TAG _page_ xxx _clist
应该基于 来创建\g__mymodule_tag_ TAG _clist
,但我不确定它在哪个阶段可以获得 的值\g__mymodule_tag_ TAG _clist
(尝试输出它的值,我总是得到结果empty
)。
由于无法\g__mymodule_tag_ TAG _page_ xxx _clist
正确创建,我不确定下一步该做什么,所以我的代码就停在这里。以下是我当前的代码。
如何正确定义这两个命令?(也许你有比我上面描述的更好的想法,如果是这样,请告诉我。)
\documentclass{article}
\usepackage{refcount}
\makeatletter
\ExplSyntaxOn
% get the total page number
\hook_gput_code:nnn { enddocument } { mymodule }
{
\label{mymodule_last-page}
}
\int_new:N \g_mymodule_total_page_int
\hook_gput_code:nnn { begindocument } { mymodule }
{
\int_gset:Nn \g_mymodule_total_page_int { \getpagerefnumber{mymodule_last-page} }
}
\prop_new:N \g__mymodule_tag_text_prop
\NewDocumentCommand \RecordTagText { m m }
{
\iow_now:cn { @auxout }
{
\@RecordTagText { #1 } { #2 }
}
}
\NewDocumentCommand \@RecordTagText { m m }
{
\prop_gput:Nnn \g__mymodule_tag_text_prop { #1 } { #2 }
}
\newcounter{show-tag}
\int_new:N \g__mymodule_show_tag_counter_int
\NewDocumentCommand \ShowTagText { m }
{
\int_gdecr:N \g__mymodule_show_tag_counter_int
\addtocounter { show-tag } { -1 }
\refstepcounter { show-tag }
\label { show-tag- \int_eval:n { \g__mymodule_show_tag_counter_int } }
% for each tag, record the labels that use it
\clist_map_inline:nn { #1 }
{
\clist_gput_right:ce { g__mymodule_tag_ ##1 _clist } { \int_eval:n { \g__mymodule_show_tag_counter_int } }
}
%% What next?
% \clist_map_inline:nn { #1 }
% {
% \par
% \prop_item:Nn \g__mymodule_tag_text_prop { ##1 }
% \par
% }
}
\hook_gput_code:nnn { begindocument } { mymodule/tag }
{
\prop_map_inline:Nn \g__mymodule_tag_text_prop
{
% generate the list of label numbers that use the given tag
\clist_new:c { g__mymodule_tag_ #1 _clist }
\int_step_inline:nn { \int_eval:n { \g_mymodule_total_page_int } }
{
% generate the per page tag-label clist
\clist_new:c { g__mymodule_tag_ #1 _page_ ##1 _clist }
%% What next?
}
}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\ShowTagText{tag1,tag2}
\ShowTagText{tag2,tag3}
\clearpage
\ShowTagText{tag1,tag2}
\ShowTagText{tag2,tag4}
\RecordTagText{tag1}{Text for TAG1.}
\RecordTagText{tag2}{Text for TAG2.}
\RecordTagText{tag3}{Text for TAG3.}
\RecordTagText{tag4}{Text for TAG4.}
\end{document}
答案1
我们将尝试编写与标签相关的文本短语称为“标签调用”。
看起来,该命令\ShowTagText{<list of tags>}
应该执行一系列的标记调用。
以防万一
- 你有一个最新的 LaTeX 2ε,其中包含 ltproperties.dtx,并且
- 你不介意编译文档至少三次,
您可以尝试以下方法:
通过整数变量计数\g_mymodule_tagcalls_int
由于标签调用而记录的绝对页码。
将\ShowTagText
逗号列表中的 map 作为<list of tags>
函数提供\mymodule_Show_text_of_this_tag:n{<tag>}
,该函数执行以下操作:
% 在标记短语的开头记录绝对页码和标记:
- 全局增量
\g_mymodule_tagcalls_int
。 - 将当前绝对页码记录到标签中
mymodule.tag.calls.(<value of \g_mymodule_tagcalls_int>)
。 - 通过写入名称标签以及表示要显示的名称
mymodule.tag.calls.(<value of \g_mymodule_tagcalls_int>)(<current absolute page number>)
的属性值来将标签调用记录到 .aux 文件中。mymodule-tag
<tag>
% 检查页面上是否已经出现了对此标签的另一个引用,如果没有,则引用该标签:
- 如果
<tag>
未定义:
尝试引用标签以获取??
和警告。
否则:
循环直到标签mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)
未定义或标签mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)
定义为产生<tag>
,在循环的每次迭代中(本地)递减<tag-call-number>
。如果没有发生标签定义为产生
的情况,则引用mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)
<tag>
<tag>
。
% 在标记短语末尾记录绝对页码和标记:
- 全局增量
\g_mymodule_tagcalls_int
。 - 将当前绝对页码记录到标签中
mymodule.tag.calls.(<value of \g_mymodule_tagcalls_int>)
。 - 通过写入名称标签以及表示要显示的名称
mymodule.tag.calls.(<value of \g_mymodule_tagcalls_int>)(<current absolute page number>)
的属性值来将标签调用记录到 .aux 文件中。mymodule-tag
<tag>
\makeatletter\ExplSyntaxOn
%=======================================================================
% Let's have a token-list-variable which holds the text associated to a
% tag:
%.......................................................................
\tl_new:N \g_mymodule_tag_text_tl
%-----------------------------------------------------------------------
% Let's have a string-variable which holds characters denoting a tag:
%.......................................................................
\str_new:N \g_mymodule_tag_str
%-----------------------------------------------------------------------
% Let's have a property "mymodule-tag-text":
%.......................................................................
\property_new:nnnn {mymodule-tag-text}
{now}
{\nfss@text{\reset@font\bfseries ??}}
{\tl_use:N \g_mymodule_tag_text_tl}
%-----------------------------------------------------------------------
% Let's have \RecordTagText{<tag>}{<text>} write a label
% "mymodule.recorded-tags.<tag>" with property "mymodule-tag-text"
% whose value is <text>:
%.......................................................................
\cs_new_protected:Npn \RecordTagText #1 #2
{
\@bsphack
\str_gset:Nn \g_mymodule_tag_str {#1}
\tl_gset:Nn \g_mymodule_tag_text_tl {#2}
\exp_args:Ne
\property_record:nn
{mymodule.recorded-tags.\str_use:N \g_mymodule_tag_str}
{mymodule-tag-text}
\@esphack
}
%=======================================================================
% Let's call the attempt of writing a phrase of text that is associated
% to a tag a "tag-call".
%
% Seems, the command \ShowTagText{<list of tags>} shall do a sequence of
% tag-calls.
%
% This can be done by mapping on the comma-list provided as
% <list of tags>, passing each element to a function
% \mymodule_Show_text_of_this_tag:n .
%.......................................................................
\cs_new_protected:Npn \ShowTagText #1
{
\bool_gset_false:N \g_mymodule_tagtextseparator_bool
\clist_map_function:nN {#1} \mymodule_Show_text_of_this_tag:n
}
%-----------------------------------------------------------------------
% Let's have a conditional denotimg whether \ShowTagText is referencing
% the first element of its list.
%.......................................................................
\bool_new:N \g_mymodule_tagtextseparator_bool
%-----------------------------------------------------------------------
% As checking on the presence of tags in property-values of
% referencing-labels needs to be done, let's have a property
% "mymodule-tag"
% whose value is to come from a string-variable \g_mymodule_tag_str,
% which in turn is to hold the characters that make up the tag.
%.......................................................................
\property_new:nnnn {mymodule-tag}{now}{}
{\str_use:N \g_mymodule_tag_str}
%-----------------------------------------------------------------------
% Using the integer \g_mymodule_tagcalls_int for counting the recording
% of absolute page numbers due to \mymodule_Show_text_of_this_tag:n:
%.......................................................................
\int_new:N \g_mymodule_tagcalls_int
%-----------------------------------------------------------------------
% \mymodule_Show_text_of_this_tag:n {<tag>}
%
% With each tag-call do:
% - Set the value of \g_mymodule_tag_str to <tag>.
% ------------------------------------------------------------------
% - Globally _increment_ \g_mymodule_tagcalls_int.
% - Record the <current absolute page number> as label of name
% "mymodule.tag.calls.(<tag-call-number>)".
% - Record the tag-call to the .aux-file with writing at shipout-time
% as label of name
% "mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)"
% with value of property "<mymodule-tag> denoting <tag>".
% ------------------------------------------------------------------
% - If the <tag> is not defined:
% Reference the tag to get `??` and warning-messages.
% Otherwise:
% Loop until the label
% "mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)"
% is undefined or the label
% "mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)"
% is defined to yield <tag>, in each iteration locally and expandably
% decrementing <tag-call-number>/in each iteration as <tag-call-number>
% providing a set of tokens denoting a <number> where the value is
% decremented.
% If during the loop the case of a label
% "mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)"
% being defined to yield <tag> did not occur, then do
% \property_ref:nn {mymodule.recorded-tags.\str_use:N \g_mymodule_tag_str} {mymodule-tag-text}.
% ------------------------------------------------------------------
% - Globally _increment_ \g_mymodule_tagcalls_int.
% - Record the <current absolute page number> as label of name
% "mymodule.tag.calls.(<tag-call-number>)".
% - Record the tag-call to the .aux-file with writing at shipout-time
% as label of name
% "mymodule.tag.calls.(<tag-call-number>)(<current absolute page number>)"
% with value of property "<mymodule-tag> denoting <tag>".
%.......................................................................
\cs_new_protected:Nn \mymodule_Show_text_of_this_tag:n
{
\str_gset:Nn \g_mymodule_tag_str {#1}
%
\mymodule_Show_text_of_this_tag_writenextlabel:
%
\exp_args:Ne
\property_if_recorded:nnTF {mymodule.recorded-tags.\str_use:N \g_mymodule_tag_str} {mymodule-tag-text}
{
\exp_args:Nf \mymodule_Show_text_of_this_tag_loop:nn { \int_eval:n {\g_mymodule_tagcalls_int-1} }{#1}
}
{\use:n}
{
\bool_if:NTF \g_mymodule_tagtextseparator_bool {~} {\bool_gset_true:N \g_mymodule_tagtextseparator_bool}
\exp_args:Ne
\property_ref:nn {mymodule.recorded-tags.\str_use:N \g_mymodule_tag_str} {mymodule-tag-text}
}
% -----------------------------------------------------
% In case you wish the tag-text to be repeatable on
% the subsequent page when it was broken across pages
% with the following lines swap the commenting:
% -----------------------------------------------------
\mymodule_Show_text_of_this_tag_writenextlabel:
}
\cs_new:Nn \mymodule_Show_text_of_this_tag_loop:nn
{
\exp_args:Ne
\property_if_recorded:nnTF{mymodule.tag.calls.(\int_use:N \g_mymodule_tagcalls_int)}{abspage}
{
\exp_args:Ne
\property_if_recorded:nTF {
mymodule.tag.calls.(#1)(
\exp_args:Ne\property_ref:nn{mymodule.tag.calls.(\int_use:N \g_mymodule_tagcalls_int)}{abspage}
)
}
{
\str_if_eq:eeTF
{ #2 }
{
\exp_args:Ne
\property_ref:nn{
mymodule.tag.calls.(#1)(
\exp_args:Ne \property_ref:nn{mymodule.tag.calls.(\int_use:N \g_mymodule_tagcalls_int)}{abspage}
)
}{mymodule-tag}
}
{ \use_none:n }
{ \exp_args:Nf \mymodule_Show_text_of_this_tag_loop:nn { \int_eval:n {#1-1} }{#2} }
}{
\use:n
}
}{
\use:n
}
}
\cs_new:Nn \mymodule_Show_text_of_this_tag_writenextlabel:
{
\int_gincr:N \g_mymodule_tagcalls_int
\exp_args:Ne
\property_record:nn {mymodule.tag.calls.(\int_use:N \g_mymodule_tagcalls_int)}{abspage}
\exp_args:Ne
\property_if_recorded:nnTF{mymodule.tag.calls.(\int_use:N \g_mymodule_tagcalls_int)}{abspage}{
\exp_args:Ne
\property_record:nn {mymodule.tag.calls.(\int_use:N \g_mymodule_tagcalls_int)(
\exp_args:Ne
\property_ref:nn {mymodule.tag.calls.(\int_use:N \g_mymodule_tagcalls_int)}{abspage}
)}{mymodule-tag}
}{}
}
\ExplSyntaxOff\makeatother
\documentclass{article}
\begin{document}
\ShowTagText{tag1,tag2}
\ShowTagText{tag2,tag3}
\clearpage
\ShowTagText{tag1,tag2}
\ShowTagText{tag2,tag4}
% Within the 2nd argument of \RecordTagText don't insert material that yields
% breakpoints at the begin and/or at the end of the text denoted by the tag.
\RecordTagText{tag1}{Text for TAG1.}
\RecordTagText{tag2}{Text for TAG2.}
\RecordTagText{tag3}{Text for TAG3.}
\RecordTagText{tag4}{Text for TAG4.}
\end{document}
答案2
以下方法不需要 ltproperties,可能只需要两次 LaTeX 运行即可,但它依赖于通过整数变量提供的绝对页码\g_shipout_readonly_int
以下方法不需要 ltproperties,可能只需要两次 LaTeX 运行即可,但它依赖于通过新钩子系统引入的
该机制的核心是将宏调用写入 .aux 文件
\TagCallDataRecord
{⟨id of the instance of \ShowTagText that produced the record⟩}
{⟨tag⟩}
{⟨absolute page⟩}
{⟨begin/end of tag-text⟩}
{⟨id of the record⟩}
。
用作⟨id of the record⟩
要通过 创建的交叉引用标签名称的组成部分\newlabel
。创建该交叉引用标签的目的是在出现以下情况时获得警告:\TagCallDataRecord
发生更改时获取警告。
\TagCallDataRecord
在 LaTeX 运行开始时读取上一次 LaTeX 运行的 .aux 文件时,以及在当前 LaTeX 运行结束时读取当前 LaTeX 运行期间新建的 .aux 文件时,都会执行此操作。在当前 LaTeX 运行结束时,宏设置为等于 。\@newl@bel
此\@testdef
事实可用于将\TagCallDataRecord
在 LaTeX 运行结束时变为无操作,从而触发有关需要重新运行 LaTeX 的消息,以防在 LaTeX 运行之间情况发生变化。
当在 LaTeX 运行开始时读取前一个 LaTeX 运行的 .aux 文件时,\TagCallDataRecord
用于创建和维护两种序列:
- 对于每个引用标签的绝对页码,都会创建一个序列,其中包含该页面上引用的标签。
- 为每个序列实例
\ShowTagText
创建一个包含该实例所引用的标签的序列。
标签\TagCallDataRecord
仅当该序列尚未包含标签时,才会将
的标签仅当保存页面上引用的标签的序列尚未包含它并且不表示标签文本的结尾时才会\TagCallDataRecord
添加到保存该实例所引用的标签的序列中。后者仅当您不希望标签文本在第二页上重复以防跨页时才使用。\ShowTagText
\TagCallDataRecord
\makeatletter\ExplSyntaxOn
%===============================================================================
% Part 1: Interface for defining/recording and referencing tags
%===============================================================================
% \RecordTagText{<tag>}{<text>} write a `\newlabel`-entry
%...............................................................................
\cs_new_protected:Npn \RecordTagText #1 #2
{
\@bsphack
\iow_shipout:Nx \@auxout {
\token_to_str:N \newlabel{\tl_to_str:e {#1}}{\tl_to_str:e {#2}}
}
\@esphack
}
%-------------------------------------------------------------------------------
% \RefTagText{<tag>}
%...............................................................................
\cs_new_protected:Npn \RefTagText #1 { \exp_args:Nc \@setref{r@#1}\@empty{#1} }
%===============================================================================
% Part 2: Interface for referencing tags only once per page
%===============================================================================
% \TagCallDataRecord
% {<id of the instance of \ShowTagText that produced the record>}
% {<tag>}
% {<abspage>}
% {<begin/end>}
% {<id of the record>}
%...............................................................................
\cs_new_protected:Npn \TagCallDataRecord #1#2#3#4#5
{
\exp_args:NNe \use:nn \mymodule_TagCallDataRecord_stringified:nnnnn
{
{\tl_to_str:n{#1}}
{\tl_to_str:n{#2}}
{\tl_to_str:n{#3}}
{\tl_to_str:n{#4}}
{\tl_to_str:n{#5}}
}
}
\cs_new_protected:Nn \mymodule_TagCallDataRecord_stringified:nnnnn
{
%---------------------------------------------------------------------------
% The only purpose of this \newlabel is raising a message in case s.th.
% changes between LaTeX runs.
% The only purpose of \g_mymodule_ShowTagTextRecords_int is providing a
% primary key for the name of the label as the arguments of
% \mymodule_TagCallDataRecord_stringified:nnnn are not suitable for forming
% a primary key.
%---------------------------------------------------------------------------
\newlabel {mymodule_TagCall(#5)}{{#1}{#2}{#3}{#4}}
%...........................................................................
% When the aux-file created during the LaTeX-run is read at the end of the
% LaTeX-run, \@newl@bel is set equal to \@testdef by the LaTeX 2e kernel.
%...........................................................................
\token_if_eq_meaning:NNF \@newl@bel \@testdef {
\seq_if_exist:cF
{g_mymodule_tags_per_call_id_(#1)_seq}
{ \seq_clear_new:c {g_mymodule_tags_per_call_id_(#1)_seq} }
\seq_if_exist:cF
{g_mymodule_tags_on_abspage_(#3)_seq}
{ \seq_clear_new:c {g_mymodule_tags_on_abspage_(#3)_seq} }
\seq_if_in:cnF {g_mymodule_tags_on_abspage_(#3)_seq} {#2} {
\seq_gput_right:cn {g_mymodule_tags_on_abspage_(#3)_seq} {#2}
%.......................................................................
% With the following \str_if_eq:nnF-comparison commented out, in
% case the text associated to the tag was broken across pages,
% so that part of it is on the subsequent page, the tag-text will
% not be repeated on that subsequent page.
%.......................................................................
%\str_if_eq:nnF {#4} {end} {
\seq_gput_right:cn {g_mymodule_tags_per_call_id_(#1)_seq} {#2}
%}
}
}
}
%-------------------------------------------------------------------------------
\cs_generate_variant:Nn \tl_to_str:n { e }
%-------------------------------------------------------------------------------
% \mymodule_write_tag_call:nn {<tag>}{<begin/end>}
%...............................................................................
\cs_new_protected:Nn \mymodule_write_tag_call:nn
{
\iow_shipout_x:Nx \@auxout {
\token_to_str:N \TagCallDataRecord
{\tl_to_str:e {\int_use:N \g_mymodule_ShowTagTextInstances_int}}
{\tl_to_str:e {#1}}
{\exp_not:n{\int_use:N \g_shipout_readonly_int}}
{\tl_to_str:e {#2}}
{\tl_to_str:e {\int_use:N \g_mymodule_ShowTagTextRecords_int}}
}
}
%-------------------------------------------------------------------------------
% \mymodule_refer_tag_call:n {<tag>}
%...............................................................................
\tl_new:N \g_mymodule_scratch_tl
\cs_new:Npn \mymodule_refer_tag_call:n #1
{
\seq_if_exist:cTF
{
g_mymodule_tags_per_call_id_(
\int_use:N \g_mymodule_ShowTagTextInstances_int
)_seq
}
{
\seq_get:cN
{
g_mymodule_tags_per_call_id_(
\int_use:N \g_mymodule_ShowTagTextInstances_int
)_seq
}
\g_mymodule_scratch_tl
\exp_args:No
\quark_if_no_value:nTF
{ \g_mymodule_scratch_tl }
{ \use_none:n }
{ \str_if_eq:eeT { \tl_use:N \g_mymodule_scratch_tl }{ #1 } }
}
{
\use:n
}
{
\seq_if_exist:cT
{
g_mymodule_tags_per_call_id_(
\int_use:N \g_mymodule_ShowTagTextInstances_int
)_seq
}
{
\seq_gpop:cN
{
g_mymodule_tags_per_call_id_(
\int_use:N \g_mymodule_ShowTagTextInstances_int
)_seq
}
\g_mymodule_scratch_tl
}
\bool_if:NTF \g_mymodule_tagtextseparator_bool
{~}
{\bool_gset_true:N \g_mymodule_tagtextseparator_bool}
%.........................................................................
% Do the reference - this actually is the only place where the interface
% for defining/recording and referencing tags and the interface for
% referencing tags only once per page come into touch:
%.........................................................................
\RefTagText{#1}
%.........................................................................
}
}
%-------------------------------------------------------------------------------
% Let's have a conditional denotimg whether \ShowTagText is referencing the
% first element of its list.
%...............................................................................
\bool_new:N \g_mymodule_tagtextseparator_bool
%-------------------------------------------------------------------------------
% \mymodule_Show_text_of_this_tag:n{<tag>}
%...............................................................................
\int_new:N \g_mymodule_ShowTagTextInstances_int
\int_new:N \g_mymodule_ShowTagTextRecords_int
\cs_new:Nn \mymodule_Show_text_of_this_tag:n
{
\int_gincr:N \g_mymodule_ShowTagTextRecords_int
\mymodule_write_tag_call:nn {#1}{begin}
\mymodule_refer_tag_call:n {#1}
\int_gincr:N \g_mymodule_ShowTagTextRecords_int
\mymodule_write_tag_call:nn {#1}{end}
}
%-------------------------------------------------------------------------------
% \ShowTagText{<tag list>}
%...............................................................................
\cs_new_protected:Npn \ShowTagText #1
{
\bool_gset_false:N \g_mymodule_tagtextseparator_bool
\int_gincr:N \g_mymodule_ShowTagTextInstances_int
\clist_map_function:nN {#1} \mymodule_Show_text_of_this_tag:n
}
%===============================================================================
\ExplSyntaxOff\makeatother
\documentclass{article}
\begin{document}
\ShowTagText{tag1,tag2,tag2}
\ShowTagText{tag2,tag3,tag3}
\clearpage
\ShowTagText{tag1,tag2}
\ShowTagText{tag2,tag4}
% Within the 2nd argument of \RecordTagText don't insert material that yields
% breakpoints at the begin and/or at the end of the text denoted by the tag.
\RecordTagText{tag1}{Text for TAG1.}
\RecordTagText{tag2}{Text for TAG2.}
\RecordTagText{tag3}{Text for TAG3.}
\RecordTagText{tag4}{Text for TAG4.}
\end{document}