% \iffalse meta-comment % % File: postnotes.dtx % % This file is part of the LaTeX package "postnotes". % % Copyright (C) 2022-2024 gusbrs % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file: % % https://www.latex-project.org/lppl.txt % % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % % This work is "maintained" (as per LPPL maintenance status) by gusbrs. % % This work consists of the files postnotes.dtx, % postnotes.ins, % postnotes-doc.tex, % postnotes-code.tex, % and the files generated from them. % % The released version of this package is available from CTAN. % % ----------------------------------------------------------------------- % % The development version of the package can be found at % % https://github.com/gusbrs/postnotes % % for those people who are interested. % % ----------------------------------------------------------------------- % % \fi % % \iffalse %<*driver> \documentclass{l3doc} % Have \GetFileInfo pick up date and version data and used in the % documentation. \usepackage{postnotes} \begin{document} \DocInput{postnotes.dtx} \end{document} % % % \fi % % \DoNotIndex{\\} % % \NewDocumentCommand\githubissue{m}{^^A % issue~\href{https://github.com/gusbrs/postnotes/issues/#1}{\##1}} % % \NewDocumentCommand\githubPR{m}{^^A % PR~\href{https://github.com/gusbrs/postnotes/pull/#1}{\##1}} % % \NewDocumentCommand\contributor{m}{#1} % \NewDocumentCommand\username{m}{`\texttt{#1}'} % % \NewDocumentCommand\opt{m}{\texttt{#1}} % % \pdfstringdefDisableCommands{^^A % \def\opt#1{#1} % } % % \AddToHook{env/macro/after}{\medskip{}} % \AddToHook{env/variable/after}{\medskip{}} % \AddToHook{env/function/after}{\medskip{}} % % % ^^A Have the Index at 'section' level rather than 'part'. Otherwise it is % ^^A just the same definition from 'l3doc.cls'. % \IndexPrologue{^^A % \section*{Index} % \markboth{Index}{Index} % \addcontentsline{toc}{section}{Index} % The italic numbers denote the pages where the corresponding entry is % described, numbers underlined point to the definition, all others indicate % the places where it is used.^^A % } % % % \GetFileInfo{postnotes.sty} % % \title{^^A % The \pkg{postnotes} package^^A % \texorpdfstring{\\{}\medskip{}}{ - }^^A % Code documentation^^A % \texorpdfstring{\medskip{}}{}^^A % } % % \author{^^A % \texorpdfstring{\texttt{gusbrs}\\[0.8em] % \url{https://github.com/gusbrs/postnotes}\\ % \url{https://www.ctan.org/pkg/postnotes}}{gusbrs}} % % \date{Version \fileversion\ -- \filedate} % % \maketitle % % % \tableofcontents % % % \clearpage{} % % \section{Initial setup} % Start the \pkg{DocStrip} guards. % \begin{macrocode} %<*package> % \end{macrocode} % % Identify the internal prefix (\LaTeX3 \pkg{DocStrip} convention). % \begin{macrocode} %<@@=postnotes> % \end{macrocode} % % % The new syntax for file/package hooks, which the package assumes, requires % kernel 2021-11-15 (\texttt{ltnews34}, \texttt{ltfilehook}). Furthermore, % the kernel of 2022-06-01 introduced a couple of very nice features which % simplifies the relation with \pkg{hyperref} (\texttt{ltnews35}, % \texttt{hyperref-linktarget}): the provision of \cs{MakeLinkTarget} and the % definition by the kernel of the starred version of \cs{ref}, which we can % use regardless of \pkg{hyperref} being loaded. Also, since we followed the % move to \texttt{e}-type expansion, to play safe we require the 2023-11-01 % kernel or newer. Finally, the tagging sockets and block code required for % tagging support need the 2024-06-01 kernel. % % \begin{macrocode} \def\postnotes@required@kernel{2024-06-01} \NeedsTeXFormat{LaTeX2e}[\postnotes@required@kernel] \providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion} \IfFormatAtLeastTF{\postnotes@required@kernel} {} {% \PackageError{postnotes}{LaTeX kernel too old} {% 'postnotes' requires a LaTeX kernel \postnotes@required@kernel\space or newer.% }% }% % \end{macrocode} % % % \begin{macrocode} \ProvidesExplPackage {postnotes} {2024-11-14} {0.4.1} {Endnotes for LaTeX} % \end{macrocode} % % % \begin{macro} % { % \l_@@_tmpa_tl , % \l_@@_tmpb_tl , % \l_@@_tmpa_seq , % \l_@@_tmpb_seq , % \l_@@_tmpa_box , % } % Temporary scratch variables. % \begin{macrocode} \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_tmpb_seq \box_new:N \l_@@_tmpa_box % \end{macrocode} % \end{macro} % % % \section{Data} % % % \begin{macro}[EXP]{\@@_data_name:n} % Returns the name of the property list variable which stores the data of % the \cs{postnote} with \meta{note id} number. % \begin{syntax} % \cs{@@_data_name:n} \Arg{note id} % \end{syntax} % \begin{macrocode} \cs_new:Npn \@@_data_name:n #1 { g_@@_ #1 _data_prop } \cs_generate_variant:Nn \@@_data_name:n { e } % \end{macrocode} % \end{macro} % % % \pkg{postnotes} provides a number of hooks from the new hook system to grant % some points of access in key places of the package. Note that hooks created % with \cs{NewHook} are meant to be public interfaces (see % \url{https://chat.stackexchange.com/transcript/message/62955941#62955941}, % and following discussion). % % % \begin{macro}{\@@_store:nn} % Stores the metadata and \meta{note content} of \cs{postnote} with ID % \meta{note id}, from where it is called. The % \texttt{postnotes/note/store} hook is intended to add further data to the % note, when required to support packages with specific needs. % \begin{syntax} % \cs{@@_store:nn} \Arg{note id} \Arg{note content} % \end{syntax} % \begin{macrocode} \NewHook { postnotes/note/store } \cs_new_protected:Npn \@@_store:nn #1#2 { \prop_new:c { \@@_data_name:e {#1} } \prop_gput:cnn { \@@_data_name:e {#1} } { type } { note } \prop_gput:cne { \@@_data_name:e {#1} } { mark } { \l_@@_mark_tl } \prop_gput:cne { \@@_data_name:e {#1} } { counter } { \int_use:N \c@postnote } \prop_gput:cne { \@@_data_name:e {#1} } { sortnum } { \bool_if:NTF \l_@@_manual_sortnum_bool { \fp_use:N \l_@@_sort_num_fp } { \int_use:N \c@postnote } } \cs_if_exist:cT { chapter } { \prop_gput:cne { \@@_data_name:e {#1} } { thechapter } { \thechapter } } \prop_gput:cne { \@@_data_name:e {#1} } { thesection } { \thesection } \prop_gput:cne { \@@_data_name:e {#1} } { pnsectname } { \g_@@_section_name_tl } \prop_gput:cne { \@@_data_name:e {#1} } { pnsectid } { \int_use:N \g_@@_sectid_int } \prop_gput:cne { \@@_data_name:e {#1} } { multibool } { \bool_to_str:N \l_@@_maybe_multi_bool } \prop_gput:cnn { \@@_data_name:e {#1} } { content } {#2} \UseHook { postnotes/note/store } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_store_section:nn} % Stores the metadata and \meta{note content} of \cs{postnotesection} with % ID \meta{note id}, from where it is called. % \begin{syntax} % \cs{@@_store_section:nn} \Arg{note id} \Arg{note content} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_store_section:nn #1#2 { \prop_new:c { \@@_data_name:e {#1} } \prop_gput:cnn { \@@_data_name:e {#1} } { type } { section } \cs_if_exist:cT { chapter } { \prop_gput:cne { \@@_data_name:e {#1} } { thechapter } { \thechapter } } \prop_gput:cne { \@@_data_name:e {#1} } { thesection } { \thesection } \prop_gput:cnn { \@@_data_name:e {#1} } { content } {#2} } \cs_generate_variant:Nn \@@_store_section:nn { ne } % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \@@_prop_get:nnN , % \@@_prop_item:nn , % \@@_prop_gclear:n , % } % Convenience functions to retrieve and clear data from a note based on the % ID number. % \begin{syntax} % \cs{@@_prop_get:nnN} \Arg{note id} \Arg{property} \Arg{tl var to set} % \cs{@@_prop_item:nn} \Arg{note id} \Arg{property} % \cs{@@_prop_gclear:n} \Arg{note id} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_prop_get:nnN #1#2#3 { \prop_get:cnNF { \@@_data_name:e {#1} } {#2} #3 { \tl_clear:N #3 } } \cs_new:Npn \@@_prop_item:nn #1#2 { \prop_item:cn { \@@_data_name:e {#1} } {#2} } \cs_new_protected:Npn \@@_prop_gclear:n #1 { \prop_gclear:c { \@@_data_name:e {#1} } } % \end{macrocode} % \end{macro} % % % \begin{macro}[int] % { % \post@note , % \@@_store_labelseq:nn , % \@@_step_counteraux:nnn , % } % \cs{post@note} is the \cs{newlabel} equivalent for \pkg{postnotes}. % Based on the kernel's \cs{@newl@bel} so that we get \LaTeX{} checks for % multiple and changed references for free (procedure learnt from % \pkg{zref}). \cs{post@note}, when the \file{.aux} file is read, defines % macros named \texttt{\textbackslash{}postnote@r@\meta{label % type}@\meta{id}}, according to the prefix set by \cs{c_@@_ref_prefix_tl}. % \cs{@@_store_labelseq:nn} is an auxiliary function to store the sequence % of the labels in the \file{.aux} file, used to check for possible sorting % problems due to floats. \cs{@@_step_counteraux:nnn} handles counter % stepping and storing for the \opt{counteraux} option. % \begin{syntax} % \cs{post@note} \Arg{label type} \Arg{id} \Arg{label content (page)} % ~~\Arg{counteraux step} % \cs{@@_store_labelseq:nn} \Arg{label type} \Arg{id} % \cs{@@_step_counteraux:nnn} \Arg{label type} \Arg{id} % ~~\Arg{counteraux step} % \end{syntax} % \begin{macrocode} \tl_const:Nn \c_@@_ref_prefix_tl { postnote@r } \seq_new:N \g_@@_labelseq_seq \int_new:N \g_@@_postnote_counteraux_int \prop_new:N \g_@@_counteraux_prop \bool_new:N \g_@@_firstrun_bool \bool_gset_true:N \g_@@_firstrun_bool \cs_new_protected:Npn \@@_store_labelseq:nn #1#2 { \bool_lazy_any:nT { { \str_if_eq_p:nn {#1} { mark } } { \str_if_eq_p:nn {#1} { section } } { \str_if_eq_p:nn {#1} { preprint } } } { \seq_gput_right:Nn \g_@@_labelseq_seq { {#1} {#2} } } } \cs_new_protected:Npn \@@_step_counteraux:nnn #1#2#3 { \bool_lazy_and:nnT { \g_@@_counteraux_bool } { \str_if_eq_p:nn {#1} { mark } } { \int_gadd:Nn \g_@@_postnote_counteraux_int { #3 } \prop_gput:Nne \g_@@_counteraux_prop { #2 } { \int_use:N \g_@@_postnote_counteraux_int } } } \cs_new_protected:Npe \post@note #1#2#3#4 { \exp_not:N \bool_gset_false:N \exp_not:N \g_@@_firstrun_bool \exp_not:N \@@_store_labelseq:nn { #1 } { #2 } \exp_not:N \@@_step_counteraux:nnn { #1 } { #2 } { #4 } \exp_not:N \@newl@bel { \c_@@_ref_prefix_tl } { #1 @ #2 } { #3 } } % \end{macrocode} % \end{macro} % % And ensure \cs{post@note}, \cs{postnote@setcounteraux}, and % \cs{postnote@addtocounteraux} are defined in the \file{.aux} file. The % hooks are the same used by \pkg{hyperref} for similar purpose. % % \begin{macrocode} \AddToHook { begindocument } { \legacy_if:nT { @filesw } { \iow_now:Ne \@mainaux { \token_to_str:N \providecommand \token_to_str:N \post@note [4] { } } \iow_now:Ne \@mainaux { \token_to_str:N \providecommand \token_to_str:N \postnote@setcounteraux [1] { } } \iow_now:Ne \@mainaux { \token_to_str:N \providecommand \token_to_str:N \postnote@addtocounteraux [1] { } } } } \AddToHook { include/before } { \legacy_if:nT { @filesw } { \iow_now:Ne \@mainaux { \token_to_str:N \providecommand \token_to_str:N \post@note [4] { } } \iow_now:Ne \@mainaux { \token_to_str:N \providecommand \token_to_str:N \postnote@setcounteraux [1] { } } \iow_now:Ne \@mainaux { \token_to_str:N \providecommand \token_to_str:N \postnote@addtocounteraux [1] { } } } } % \end{macrocode} % % % \begin{macro} % { % \@@_set_label:nnnn , % \@@_set_mark_page_label:nn , % \@@_set_section_page_label:n , % \@@_set_text_page_label:n , % \@@_set_print_page_label:n , % \@@_set_pre_print_label:n , % } % Label setting functions for each pertinent context. They must use % \cs{iow_shipout_e:Nn}, since the main information we are interested in is % the \texttt{page}. % \begin{syntax} % \cs{@@_set_label:nnnn} \Arg{label type} \Arg{note id} \Arg{value} % ~~\Arg{counteraux step} % \cs{@@_set_mark_page_label:nn} \Arg{note id} \Arg{counteraux step} % \cs{@@_set_section_page_label:n} \Arg{note id} % \cs{@@_set_text_page_label:n} \Arg{note id} % \cs{@@_set_print_page_label:n} \Arg{note id} % \cs{@@_set_pre_print_label:n} \Arg{note id} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_set_label:nnnn #1#2#3#4 { \legacy_if:nT { @filesw } { \iow_shipout_e:Nn \@auxout { \token_to_str:N \post@note { #1 } { #2 } { #3 } { #4 } } } } \cs_new_protected:Npn \@@_set_mark_page_label:nn #1#2 { \@@_set_label:nnnn { mark } { #1 } { \thepage } { #2 } } \cs_generate_variant:Nn \@@_set_mark_page_label:nn { ee } \cs_new_protected:Npn \@@_set_section_page_label:n #1 { \@@_set_label:nnnn { section } { #1 } { \thepage } { } } \cs_generate_variant:Nn \@@_set_section_page_label:n { e } \cs_new_protected:Npn \@@_set_text_page_label:n #1 { \@@_set_label:nnnn { text } { #1 } { \int_use:N \c@page } { } } \cs_generate_variant:Nn \@@_set_text_page_label:n { e } \cs_new_protected:Npn \@@_set_print_page_label:n #1 { \@@_set_label:nnnn { print } { #1 } { \int_use:N \c@page } { } } \cs_generate_variant:Nn \@@_set_print_page_label:n { e } \cs_new_protected:Npn \@@_set_pre_print_label:n #1 { \@@_set_label:nnnn { preprint } { #1 } { } { } } \cs_generate_variant:Nn \@@_set_pre_print_label:n { e } % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \@@_get_pageref:Nn , % \@@_extract_pageref:n , % } % Reference data extraction functions. % \begin{syntax} % \cs{@@_get_pageref:Nn} \Arg{tl var to set} \Arg{label name} % \cs{@@_extract_pageref:n} \Arg{label name} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_get_pageref:Nn #1#2 { \cs_if_exist:cTF { \c_@@_ref_prefix_tl @ #2 } { \tl_set:Nv #1 { \c_@@_ref_prefix_tl @ #2 } } { \tl_clear:N #1 } } \cs_generate_variant:Nn \@@_get_pageref:Nn { Ne } \cs_new:Npn \@@_extract_pageref:n #1 { \cs_if_exist:cTF { \c_@@_ref_prefix_tl @ #1 } { \exp_not:v { \c_@@_ref_prefix_tl @ #1 } } { \c_empty_tl } } \cs_generate_variant:Nn \@@_extract_pageref:n { e } % \end{macrocode} % \end{macro} % % % \section{Options} % % \subsection*{\opt{heading} option} % % \begin{macrocode} \keys_define:nn { postnotes/setup } { heading .cs_set_protected:Np = \pnheading , heading .value_required:n = true , } % \end{macrocode} % % \begin{macro}[int]{\pnheading} % Provide default value for \cs{pnheading}. % \begin{macrocode} \cs_if_exist:cTF { chapter } { \cs_new_protected:Npn \pnheading { \chapter*{\pntitle} \@mkboth{\pnheaderdefault}{\pnheaderdefault} } } { \cs_new_protected:Npn \pnheading { \section*{\pntitle} \@mkboth{\pnheaderdefault}{\pnheaderdefault} } } % \end{macrocode} % \end{macro} % % % \subsection*{\opt{format} option} % % \begin{macrocode} \tl_new:N \l_@@_print_format_tl \keys_define:nn { postnotes/setup } { format .tl_set:N = \l_@@_print_format_tl , format .initial:n = { \small } , format .value_required:n = true , } % \end{macrocode} % % % \subsection*{\opt{listenv} option} % % \begin{macrocode} \tl_new:N \l_@@_print_env_tl \bool_new:N \l_@@_print_as_list_bool \keys_define:nn { postnotes/setup } { listenv .code:n = { \tl_if_eq:nnTF {#1} { none } { \bool_set_false:N \l_@@_print_as_list_bool \tl_set:Nn \l_@@_post_printnote_tl { \par } % \end{macrocode} % A sensible default just in case. It should not get to be used though. % \begin{macrocode} \tl_set:Nn \l_@@_print_env_tl { itemize } } { \bool_set_true:N \l_@@_print_as_list_bool \tl_set:Nn \l_@@_print_env_tl {#1} } } , listenv .initial:n = { postnoteslist } , listenv .value_required:n = true , } % \end{macrocode} % % % A couple of built-in list environments provided for convenience, and % \texttt{postnoteslist} as default. The horizontal setup of the label in % these lists is based on the \texttt{description} environment of the standard % classes (see the \emph{The \LaTeX{} Companion}). % % \begin{macrocode} \NewDocumentEnvironment { postnoteslist } { } { \list { } { \setlength { \leftmargin } { 0pt } \setlength { \labelwidth } { 0pt } \setlength { \itemindent } { .5\parindent } \cs_set_eq:NN \makelabel \@@_list_makelabel:n \setlength { \rightmargin } { 0pt } \setlength { \listparindent } { \parindent } \setlength { \parsep } { \parskip } \setlength { \itemsep } { 0pt } \setlength { \topsep } { .5\topsep } \setlength { \partopsep } { .5\partopsep } } } { \endlist } \NewDocumentEnvironment { postnoteslisthang } { } { \list { } { \setlength { \leftmargin } { 1em } \setlength { \labelwidth } { -\leftmargin } \setlength { \itemindent } { -2\leftmargin } \cs_set_eq:NN \makelabel \@@_list_makelabel:n \setlength { \rightmargin } { 0pt } \setlength { \listparindent } { \parindent } \setlength { \parsep } { \parskip } \setlength { \itemsep } { 0pt } \setlength { \topsep } { .5\topsep } \setlength { \partopsep } { .5\partopsep } } } { \endlist } \cs_new:Npn \@@_list_makelabel:n #1 { \hspace { \labelsep } \normalfont ~ #1 } % \end{macrocode} % % % \subsection*{\opt{makemark} and \opt{maketextmark} options} % % The arguments are: \texttt{\#1} is the mark, \texttt{\#2} and \texttt{\#3} % are, respectively, the start and the end of the backlink. % % \begin{macrocode} \keys_define:nn { postnotes/setup } { makemark .cs_set:Np = \@@_make_mark:nnn #1#2#3 , makemark .value_required:n = true , % \end{macrocode} % From the default kernel definition of \cs{@makefnmark}. % \begin{macrocode} makemark .initial:n = { #2 \hbox { \@textsuperscript { \normalfont #1 } } #3 } , maketextmark .cs_set:Np = \@@_make_text_mark:nnn #1#2#3 , maketextmark .value_required:n = true , maketextmark .initial:n = { #2 #1 . #3 } , } \cs_generate_variant:Nn \@@_make_mark:nnn { Vnn } % \end{macrocode} % % % \subsection*{\opt{pretextmark}, \opt{posttextmark}, \opt{postprintnote} options} % % \begin{macrocode} \tl_new:N \l_@@_pre_textmark_tl \tl_new:N \l_@@_post_textmark_tl \tl_new:N \l_@@_post_printnote_tl \keys_define:nn { postnotes/setup } { pretextmark .tl_set:N = \l_@@_pre_textmark_tl , pretextmark .value_required:n = true , posttextmark .tl_set:N = \l_@@_post_textmark_tl , posttextmark .value_required:n = true , postprintnote .tl_set:N = \l_@@_post_printnote_tl , postprintnote .value_required:n = true , } % \end{macrocode} % % % \subsection*{\opt{style} option} % % \begin{macrocode} \keys_define:nn { postnotes/setup } { style .choice: , style / endnotes .meta:n = { listenv = none , format = { \footnotesize \setlength { \rightskip } { 0pt } \setlength { \leftskip } { 0pt } \setlength { \parindent } { 1.8em } } , % \end{macrocode} % \pkg{endnotes} uses a zero width box to get the desired alignment to the % right, but that does not play well with the backlinks, so we have a little % more work to do to get this right. % \begin{macrocode} maketextmark = { \hbox_set:Nn \l_@@_tmpa_box { \@textsuperscript { \normalfont ##1 } } \skip_horizontal:n { - \box_wd:N \l_@@_tmpa_box } ##2 \box_use:N \l_@@_tmpa_box ##3 } , } , style / pagenote .meta:n = { listenv = none , format = { } , pretextmark = { \noindent } , maketextmark = { { \normalfont ##2 ##1 . ##3 } } , posttextmark = { ~ } , } , } % \end{macrocode} % % % \subsection*{\opt{hyperref} and \opt{backlink} options} % % \begin{macrocode} \bool_new:N \l_@@_hyperlink_bool \bool_new:N \l_@@_hyperref_warn_bool \bool_new:N \l_@@_backlink_bool \keys_define:nn { postnotes/setup } { hyperref .choice: , hyperref / auto .code:n = { \bool_set_true:N \l_@@_hyperlink_bool \bool_set_false:N \l_@@_hyperref_warn_bool } , hyperref / true .code:n = { \bool_set_true:N \l_@@_hyperlink_bool \bool_set_true:N \l_@@_hyperref_warn_bool } , hyperref / false .code:n = { \bool_set_false:N \l_@@_hyperlink_bool \bool_set_false:N \l_@@_hyperref_warn_bool } , hyperref .initial:n = auto , hyperref .default:n = true , backlink .bool_set:N = \l_@@_backlink_bool , backlink .initial:n = true , backlink .default:n = true , } % \end{macrocode} % % \begin{macrocode} \AddToHook { begindocument } { \IfPackageLoadedTF { hyperref } { } { \bool_if:NT \l_@@_hyperref_warn_bool { \msg_warning:nn { postnotes } { missing-hyperref } } \bool_set_false:N \l_@@_hyperlink_bool } \keys_define:nn { postnotes/setup } { hyperref .code:n = { \msg_warning:nnn { postnotes } { option-preamble-only } { hyperref } } , backlink .code:n = { \msg_warning:nnn { postnotes } { option-preamble-only } { backlink } } , } } \msg_new:nnn { postnotes } { option-preamble-only } { Option~'#1'~only~available~in~the~preamble~\msg_line_context:. } \msg_new:nnn { postnotes } { missing-hyperref } { Missing~'hyperref'~package.~Setting~'hyperref=false'. } % \end{macrocode} % % % \subsection*{\opt{multiple}, \opt{multisep} options} % % As far as I can tell, the \opt{multiple} option has its origins in the % \pkg{footmisc} package, which offers this feature for footnotes. About this % option, \file{footmisc.dtx} observes: % % \begin{quote} % This (revised) code derives from a suggestion by Alexander Rozhenko (the % author of the \pkg{manyfoot} package): the intention is that % \pkg{footmisc} and \pkg{manyfoot} should be able to `interwork', in the % sense that each would recognize the other's footnote marks and behave % appropriately. The trick is that both \cs{footnote} and \cs{footnotemark} % insert a marker (a cancelling pair of kerns of \cs{multiplefootnotemarker} % (of opposite signs), which is detected in following \cs{footnote} or % \cs{footnotemark} commands. % \end{quote} % % About the same option, \file{manyfoot.dtx} notes: % % \begin{quote} % To support \opt{multiple} option from \pkg{footmisc} we add the % \cs{FN@mf@prepare} command from \pkg{footmisc} (suggested by Frank % Mittelbach). % \end{quote} % % Whatever the exact origin of this feature, the fact is that it has spread % throughout the ecosystem, using not only the same basic mechanism but, % typically, using \emph{the same variables}: \cs{multiplefootnotemarker} and % \cs{multfootsep}. With the intention, naturally, that different classes and % packages can ``interwork'' with regard to this feature. And this became a % sort of ``shared feature'' in the ecosystem (the list includes % \pkg{footmisc}, \texttt{KOMA-Script}, \pkg{eledmac}, \pkg{reledmac}, % \cls{tufte}, \cls{memoir}, \pkg{parnotes}, \pkg{sidenotes}, and now also % \pkg{postnotes}, see discussion at % \url{https://chat.stackexchange.com/transcript/message/66421777#66421777}). % What is crucial for this interplay, however, is not quite the variables % themselves, but \emph{the value of the canceling pair of kerns}, stored in % \cs{multiplefootnotemarker}. The fact that the same variables are used by % most of the classes and packages which provide the feature speaks for % convenience, in that a change in one of them reflects in all participating % in the ``pool''. % % Convenient as it is, this shared use of the same variables can only work as % long as the community agrees in what these variables contain to some degree. % As far as \cs{multiplefootnotemarker} goes, I see no divergence, and % everybody uses the value of \texttt{3sp} for the canceling kerns from % \cs{FN@mf@prepare}. \file{latex-lab-footnotes.dtx} even documents this % value for this purpose in its list of ``use of kerns to mark h-mode % positions'' (see % \url{https://chat.stackexchange.com/transcript/message/66421893#66421893}). % Things are not as smooth with regard to \cs{multfootsep} though. % \pkg{footmisc} stores a plain comma in \cs{multfootsep} and applies % formatting around it in \cs{FN@mf@check} (hard-coded as % \cs{textsuperscript}). \texttt{KOMA-Script} also stores plain content in % \cs{multfootsep} and applies the formatting in the check function. % \cls{memoir}, however, stores the formatting directly \cs{multfootsep} and % applies no formatting later. Also, \file{latex-lab-footmisc.ltx}, in adding % PDF tagging support for \pkg{footmisc} stores the tagging code directly in % \cs{multfootsep}. I don't know if there are others, but these cases are % already relevant enough to spoil things. The problem is not that they % disagree on the value of \cs{multfootsep}, but on the function(s) this macro % is supposed to perform. That given, any other parties trying to partake in % this feature have to handle things differently depending on the value of % \cs{multfootsep}. All in all, \pkg{postnotes} takes the cautious stance of % using internal variables, instead of the shared ones, to implement the % \opt{multiple} option. The only thing that really needs to be common is the % value of the canceling kerns of \texttt{3sp} in \cs{@@_multiple_prepare:}. % % \begin{macrocode} \tl_new:N \l_@@_multisep_tl \tl_const:Nn \c_postnotes_multi_notemarker_tl { 3sp } \bool_new:N \l_@@_multiple_bool \tl_new:N \l_@@_saved_spacefactor_multi_tl \keys_define:nn { postnotes/setup } { multiple .bool_set:N = \l_@@_multiple_bool , multiple .default:n = true , multiple .initial:n = false , multisep .tl_set:N = \l_@@_multisep_tl , multisep .value_required:n = true , multisep .initial:n = {,} , } % \end{macrocode} % % I'm using the definitions in \pkg{latex-lab-footmisc.ltx} as base, see % \texttt{texdoc latex-lab-footnotes}. For the formatting of the separator, % though, I take inspiration from \texttt{KOMA-Script} and use % \cs{@@_make_mark:nnn}, instead of hard-coding \cs{textsuperscript}. % \begin{macrocode} \cs_new_protected:Npn \@@_multiple_prepare: { \bool_if:NT \l_@@_multiple_bool { \kern -\c_postnotes_multi_notemarker_tl \kern \c_postnotes_multi_notemarker_tl \scan_stop: } } % \end{macrocode} % Note that \cs{@@_multiple_check:} has to be called before any whatsits % (labels, anchors, etc.) are placed, since they destroy \cs{lastkern} (see % \url{https://chat.stackexchange.com/transcript/message/66554870#66554870}, % thanks \contributor{Ulrike Fischer}). % \begin{macrocode} \cs_new_protected:Npn \@@_multiple_check: { \bool_if:NT \l_@@_multiple_bool { \dim_compare:nNnT { \c_postnotes_multi_notemarker_tl } = { \lastkern } { \tl_set:Ne \l_@@_saved_spacefactor_multi_tl { \int_use:N \spacefactor } \unkern \unkern \tag_socket_use:n { postnotes/multisep/begin } \@@_make_mark:Vnn \l_@@_multisep_tl { } { } \tag_socket_use:n { postnotes/multisep/end } \spacefactor \l_@@_saved_spacefactor_multi_tl \scan_stop: } } } % \end{macrocode} % % % \subsection*{\opt{sort} option} % % \begin{macrocode} \bool_new:N \l_@@_sort_bool \keys_define:nn { postnotes/setup } { sort .bool_set:N = \l_@@_sort_bool , sort .initial:n = true , sort .default:n = true , } % \end{macrocode} % % % \subsection*{\opt{checkduplicates} and \opt{checkfloats} options} % % \begin{macrocode} \bool_new:N \l_@@_check_dupli_bool \bool_new:N \l_@@_check_floats_bool \keys_define:nn { postnotes/setup } { checkduplicates .bool_set:N = \l_@@_check_dupli_bool , checkduplicates .default:n = true , checkduplicates .initial:n = true , checkfloats .bool_set:N = \l_@@_check_floats_bool , checkfloats .default:n = true , checkfloats .initial:n = false , } % \end{macrocode} % % % \subsection*{\opt{maybemulti} option} % % \begin{macrocode} \bool_new:N \l_@@_maybe_multi_bool \keys_define:nn { postnotes/setup } { maybemulti .bool_set:N = \l_@@_maybe_multi_bool , maybemulti .default:n = true , maybemulti .initial:n = false , } % \end{macrocode} % % % \subsection*{\opt{counteraux} option} % % \begin{macrocode} \bool_new:N \g_@@_counteraux_bool \keys_define:nn { postnotes/setup } { counteraux .bool_gset:N = \g_@@_counteraux_bool , counteraux .default:n = true , counteraux .initial:n = false , } % \end{macrocode} % % % \begin{macrocode} \AddToHook { begindocument/before } { \bool_if:NT \g_@@_counteraux_bool { \postnotesetup { sort=false } } \keys_define:nn { postnotes/setup } { counteraux .code:n = { \msg_warning:nnn { postnotes } { option-preamble-only } { counteraux } } , } } % \end{macrocode} % % % \begin{macro}[int] % { % \pnsetcounteraux , % \pnaddtocounteraux , % \postnote@setcounteraux , % \postnote@addtocounteraux , % } % \begin{syntax} % \cs{pnsetcounteraux}\marg{int} % \cs{pnaddtocounteraux}\marg{int} % \cs{postnote@setcounteraux}\marg{int} % \cs{postnote@addtocounteraux}\marg{int} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \postnote@setcounteraux #1 { \int_gset:Nn \g_@@_postnote_counteraux_int { #1 } } \cs_new_protected:Npn \postnote@addtocounteraux #1 { \int_gadd:Nn \g_@@_postnote_counteraux_int { #1 } } \NewDocumentCommand \pnsetcounteraux { m } { \@bsphack \legacy_if:nT { @filesw } { \iow_shipout_e:Nn \@auxout { \token_to_str:N \postnote@setcounteraux { #1 } } } \@esphack } \NewDocumentCommand \pnaddtocounteraux { m } { \@bsphack \legacy_if:nT { @filesw } { \iow_shipout_e:Nn \@auxout { \token_to_str:N \postnote@addtocounteraux { #1 } } } \@esphack } % \end{macrocode} % \end{macro} % % % % \subsection*{\cs{postnotesetup}} % % % \begin{macro}[int]{\postnotesetup} % Provide \cs{postnotesetup}. % \begin{syntax} % \cs{postnotesetup}\marg{options} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \postnotesetup { m } { \keys_set:nn { postnotes/setup } {#1} } % \end{macrocode} % \end{macro} % % % \section{\cs{postnote}} % % Different from the traditional \cs{footnotemark} / \cs{footnotetext} system, % in the context of end notes, the functionality which corresponds to % \cs{footnotetext} is simply to store the data to be typeset later. Hence, % some of the problems that afflict footnotes do not apply to end notes. % Namely, and as far as I can tell, they can be used in ``inner horizontal % mode'' (\cs{mbox} etc.), and in math mode, and if the ``text'' will be % typeset in the same page as the ``mark'' is of little concern. % % However, the separation between ``mark'' and ``text'' is still useful in % other contexts: floats and contexts where multiple typesetting passes are % performed. \contributor{David Carlisle} and \contributor{Ulrike Fischer} % shared some thoughts on the matter at the TeX.SX chat: % \url{https://chat.stackexchange.com/transcript/message/60754383#60754383}. % % The interesting questions here are: if they are replaceable in their roles % in these contexts and how much would we lose by providing them. In % analyzing this, we have to distinguish two situations: when % \cs{footnotemark} is called with no argument (and thus steps the counter), % and when it is called with the optional argument (and thus refrains from % stepping the counter). % % For floats, the problem they pose is that they may disturb the % \emph{ordering} of the notes. This particular issue can be solved by using % \cs{footnotemark} without argument, and manually adjusting the counter on % subsequent calls to \cs{footnotetext}. A good example of the technique is % \url{https://tex.stackexchange.com/a/43694}. True, a user may wish to % specify the mark explicitly, but doesn't necessarily need to do it to solve % the ordering issue. % % Multiple typesetting passes of content are much harder. And they abound: % the standard classes' \cs{caption} typesets the caption once, if it is % short, but twice if it is longer than a line; \pkg{amsmath}'s math % environments perform a measuring pass before actually typesetting the % equations; \pkg{amsmath}'s \cs{text} macro runs the contents through % \cs{mathchoice} (which typesets the contents four times) when in math mode; % \pkg{tabularx} and \pkg{tabularray} also perform measuring passes of their % tables; so does \pkg{csquotes}' blockquotes; and certainly more that I'm % unaware. A number of these places offer some one or another way to mitigate % the issue: \pkg{amsmath}, \pkg{tabularx}, \pkg{csquotes} and (optionally) % \pkg{tabularray} restore counter values after measuring steps; \pkg{amsmath} % offers a boolean to indicate when it is a measuring pass; \pkg{csquotes} % offers further handles. But the standard \cs{caption} offers none, and % neither does \pkg{amsmath}'s \cs{text} macro. Well, the pkg{caption} % package can disable the multiple passes for \cs{caption} with the option % \opt{singlelinecheck}, but it is not reasonable to require it for our % purposes, so we must assume the worst case. % % Enrico Gregorio is categorical in stating that \cs{endnotemark} and % \cs{endnotetext} are required for \pkg{enotez} to handle \cs{caption}, which % apparently it didn't offer originally: ``The package should implement % \cs{endnotemark} and \cs{endnotetext} for this case. According to the % documentation, the author deems them to not be needed: he's wrong.'' % (\url{https://tex.stackexchange.com/a/314937}). See also % \url{https://tex.stackexchange.com/a/43794} and % \url{https://tex.stackexchange.com/a/358207}. % % In this scenario, when there's no way around the multiple passes, % \cs{footnotemark} can only handle the general case if used with an argument, % precisely because it inhibits the stepping of the counter. Otherwise the % counter is stepped multiple times, and we'd get the wrong number (and mark). % In some circumstances, if we know the number of passes is deterministic, we % might get away by adjusting the counter manually (\cs{caption} may be dealt % with this way: if we know it to be two lines, we can decrement the counter % before it and get correct results, even hyperlinked). But in cases which % adjusting the counter is sufficient, end notes can be dealt with in the same % way, and doesn't need the separation between ``mark'' and ``text''. So, % what is distinctive of the kernel's footnote apparatus, which allows it as % much flexibility as one would like, is receiving an arbitrary number as % argument and not stepping the counter. And as far as \cs{footnotemark} and % \cs{footnotetext} are concerned, the main point of the optional argument is % really to ``manually establish the relation'' between the two of them. So, % if \emph{not stepping the counter} is what is needed to handle the general % case, is it viable to do so? What would we loose in so doing? % % When receiving an arbitrary number as argument, as the kernel functionality % for footnotes and other endnotes packages do, this value is expected to the % printed as such, hence it must correspond to the \texttt{postnote} counter % (in our case). But this counter is in the hands of the user, and can be % reset along the document, thus its uniqueness cannot be ensured. But not % stepping \texttt{postnote} is perfectly viable, as it just aims at storing % how the mark is to be typeset. However, not stepping the ID counter would % complicate things considerably. Not doing so implies we'd lose the % connection we have between the ``mark'' and the corresponding ``text''. We % might add the ``text'' to the queue, but all the metadata would be lost, % including the \pkg{hyperref} anchor, but really the set of data without % which the kind of functionality offered would be nonviable, or severely % hampered. Not stepping \texttt{postnote} but stepping the ID counter also % is not sufficient, because we'd get a note in duplicity. We could naively % think that a gap in the ID is not a problem, and just not add the duplicate % to the queue. But how could we tell the difference between a legitimate and % an illegitimate step of the ID counter? % % I have not been able to devise a way to ``reconnect'' ``text'' and ``mark'' % in the absence of the unique ID counter. The most promising idea was to % have mandatory arguments to \cs{postnotemark} and \cs{postnotetext} % receiving a \meta{label} which we could use to identify their counterparts, % but I was not able to go through with this, and the attempts all increased % complexity considerably. It is not just a label/ref system, there's got to % be a one-to-one correspondence between the sets, uniqueness has to be % ensured on both sides, and there cannot be ``lone'' marks or texts (a % bijection). Besides, this label based system of identification would have % to live side-by-side with the one based on the counter. So, even if we'd % have unique IDs, we wouldn't know beforehand in what form it comes. % Considering the ID is used to build the variable name in which we store the % note's information, this would also complicate things. % % Besides, there are ways to get things working with multiple passes without % the ``mark''/``text'' partition. As mentioned, there are a number of cases % which offer some kind of ``handle'' or way to identify the multiple passes. % \pkg{csquotes} has a dedicated hook that can be used. \pkg{amsmath} sets % the \texttt{measuring@} boolean (which \pkg{hyperref} also defines). So, % not all cases are as tricky as \cs{caption} or \cs{text}, and even that can % be decently dealt with without a separation between ``mark'' and ``text''. % Besides, in difficult cases, the package offers a \opt{nomark} option to % \cs{postnote} to place a note, but typeset no mark. Then we can typeset a % mark with \cs{postnoteref} referring to a \cs{label} in the note of % interest. This would result in a correct mark without duplicity, and in a % correct link from there to the note's text at \cs{printpostnotes}. The % drawback is that the placement of \cs{postnote} would be important, and % results sensitive to it. All the metadata is collected at the point of % \cs{postnote}, anchor included, not at the point of \cs{postnoteref}. So % the consequences are a slightly off backlink, possibly imprecise metadata, % etc. Considering \pkg{hyperref} itself shies away completely from linking % \cs{footnotemark} with an argument, I'd say there's some gain. % % The truth is there are some trade-offs, there's no ``ideal'' solution. % Still, all in all, my judgment is that the unique ID counter is worth more % than the inconveniences of an ocasional \texttt{\cs{postnote}[nomark]} % referenced with \cs{postnoteref}, and even that should not be needed much. % So, for the time being, until something else shakes this balance, I won't be % offering \cs{postnotemark} and \cs{postnotetext}. % % \bigskip{} % % For the \pkg{hyperref} support for cross-references in \cs{postnote}, I've % moved back and forth quite a lot. One of the ideas I fancied was using % \cs{refstepcounter} and let \pkg{hyperref} do its job. But, since I want to % have control of the anchor/destination name on both ``sides'', I'd have to % set \cs{theHpostnote} locally before calling \cs{refstepcounter}, otherwise % results might sensitive to user calls to \cs{counterwithin} (see % \url{https://github.com/latex3/hyperref/issues/230}, thanks % \contributor{Ulrike Fischer}). However, even if that worked well for the % default case, we still had to setup things manually, in case of a manually % supplied mark. All in all, I'm just calling \cs{stepcounter}, setting the % relevant cross-reference variables once and setting the anchor manually. % % % \bigskip{} % % \texttt{postnote} is the public, user facing, counter for \cs{postnote}. It % determines how the note's mark gets to be typeset. It can be reset, set, % and have its printed representation changed. Of course, whether those are % meaningful is up to the user. % % \begin{macrocode} \newcounter { postnote } % \end{macrocode} % % \begin{macro}[int] % { % \g_@@_note_id_int , % \l_postnotes_note_id_tl , % \g_@@_queue_seq , % \l_@@_counteraux_step_int , % \l_@@_mark_typeset_tl , % \l_@@_note_set_labels_tl , % } % \cs{g_@@_note_id_int} is the internal, unique counter which provides the % ID number of each note. It ties ``mark'' and ``text'' together, is also % the connection between each note and its data, including the content, % which is stored in a property list named according to \cs{@@_data_name:n} % and the ID number. \cs{l_postnotes_note_id_tl} is a convenience variable % storing the counter's value. \cs{g_@@_queue_seq} stores the sequence of % notes' IDs to be processed by the next call of \cs{printpostnotes}. % \begin{macrocode} \int_new:N \g_@@_note_id_int \tl_new:N \l_postnotes_note_id_tl \tl_set:Nn \l_postnotes_note_id_tl { \int_use:N \g_@@_note_id_int } \seq_new:N \g_@@_queue_seq \int_new:N \l_@@_counteraux_step_int \tl_new:N \l_@@_mark_typeset_tl \tl_new:N \l_@@_note_set_labels_tl % \end{macrocode} % \end{macro} % % % \begin{macro}[int]{\postnote} % Provide \cs{postnote}. % \begin{syntax} % \cs{postnote}\oarg{options}\marg{note text} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \postnote { O { } +m } { \@@_note:nn {#1} {#2} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_note:nn} % The internal version of \cs{postnote}. The \texttt{postnotes/note/begin} % hook is meant to provide a place from where some additional setup for the % note can be performed. This is being used for adding support for some % features/packages, but can also be used, for example, to add an extra % local property for \pkg{zref}. % \begin{syntax} % \cs{@@_note:nn} \oarg{options} \marg{note content} % \end{syntax} % \begin{macrocode} \NewHook { postnotes/note/begin } \cs_new_protected:Npn \@@_note:nn #1#2 { \group_begin: \keys_set:nn { postnotes/note } {#1} \bool_if:NT \l_@@_nomark_bool { \@bsphack } \@@_inhibit_note:F { \int_gincr:N \g_@@_note_id_int \tl_if_empty:NTF \l_@@_mark_tl { \stepcounter { postnote } \int_set:Nn \l_@@_counteraux_step_int { 1 } \bool_if:NT \g_@@_counteraux_bool { \exp_args:NNe \prop_gpop:NnNT \g_@@_counteraux_prop { \l_postnotes_note_id_tl } \l_@@_tmpa_tl { \int_set:Nn \c@postnote { \l_@@_tmpa_tl } } \tl_clear:N \l_@@_tmpa_tl } \tl_set:Ne \l_@@_mark_tl { \thepostnote } } { \int_set:Nn \l_@@_counteraux_step_int { 0 } } \UseHook { postnotes/note/begin } \seq_gput_right:Ne \g_@@_queue_seq { \l_postnotes_note_id_tl } \cs_set:Npn \@currentcounter { postnote } \cs_set:Npe \@currentlabel { \p@postnote \l_@@_mark_tl } \cs_gset:Npe \@currentHref { postnote. \l_postnotes_note_id_tl .mark } \@@_store:nn { \l_postnotes_note_id_tl } {#2} \tl_set_eq:NN \l_@@_mark_typeset_tl \l_@@_mark_tl % \end{macrocode} % Prefer \opt{label} for typesetting measuring passes, if available, see % comments at \cs{@@_inhibit_note:F}. % \begin{macrocode} \bool_lazy_or:nnT { \g_@@_counteraux_bool } { \l_@@_maybe_multi_bool } { \bool_lazy_and:nnT { ! \g_@@_firstrun_bool } { ! \cs_if_exist_p:c { \c_@@_ref_prefix_tl @ mark @ \l_postnotes_note_id_tl } } { \@@_get_label_if_exist:N \l_@@_mark_typeset_tl } } \tl_set:Nn \l_@@_note_set_labels_tl { \MakeLinkTarget* { postnote. \l_postnotes_note_id_tl .mark } \@@_set_mark_page_label:ee { \l_postnotes_note_id_tl } { \int_use:N \l_@@_counteraux_step_int } \@@_set_user_labels: } \bool_if:NTF \l_@@_nomark_bool { \tag_socket_use:n { postnotes/nomark/begin } \l_@@_note_set_labels_tl \tag_socket_use:n { postnotes/nomark/end } } { \@@_typeset_mark:eVN { \l_postnotes_note_id_tl } \l_@@_mark_typeset_tl \l_@@_note_set_labels_tl } } \bool_if:NT \l_@@_nomark_bool { \@esphack } \group_end: } % \end{macrocode} % \end{macro} % % % Options for \cs{postnote}. % % \begin{macrocode} \tl_new:N \l_@@_mark_tl \bool_new:N \l_@@_nomark_bool \fp_new:N \l_@@_sort_num_fp \str_new:N \l_@@_note_label_str \bool_new:N \l_@@_manual_sortnum_bool \keys_define:nn { postnotes/note } { markstr .tl_set:N = \l_@@_mark_tl , markstr .value_required:n = true , sortnum .code:n = { \fp_set:Nn \l_@@_sort_num_fp {#1} \bool_set_true:N \l_@@_manual_sortnum_bool } , sortnum .value_required:n = true , mark .meta:n = { markstr = {#1} , sortnum = {#1} , } , mark .value_required:n = true , nomark .bool_set:N = \l_@@_nomark_bool , nomark .default:n = true , label .str_set:N = \l_@@_note_label_str , label .value_required:n = true , maybemulti .bool_set:N = \l_@@_maybe_multi_bool , maybemulti .default:n = true , } % \end{macrocode} % % % \begin{macro}{\@@_inhibit_note:TF} % In contexts of multiple passes of content, it may be needed, or preferred, % to inhibit the note altogether to avoid side effects and duplicity. This % conditional, obviously, will always return the true branch unless % something is done in the \texttt{postnotes/note/inhibit} hook. This hook % is meant to handle support for packages or features which may justify note % inhibition, and the code there should set \cs{l_@@_inhibit_note_bool}, % \cs{l_@@_print_plain_mark_bool} and % \cs{l_@@_print_plain_mark_stepcounter_bool} as appropriate to the case. % \begin{macrocode} \bool_new:N \l_@@_inhibit_note_bool \bool_new:N \l_@@_print_plain_mark_bool \bool_new:N \l_@@_print_plain_mark_stepcounter_bool \NewHook { postnotes/note/inhibit } \prg_new_protected_conditional:Npnn \@@_inhibit_note: { F } { \bool_set_false:N \l_@@_inhibit_note_bool \bool_set_false:N \l_@@_print_plain_mark_bool \bool_set_false:N \l_@@_print_plain_mark_stepcounter_bool \UseHook { postnotes/note/inhibit } % \end{macrocode} % Printing a plain mark here may be needed because, if we are inhibiting the % note in a ``measuring context'' and omit it completely, the measuring being % performed will be off by the size of the mark. So, to ensure the measuring % can be done correctly, we place the mark. What to do with the counter % itself, depends on the situation. In places that are known to restore the % counter values after the measuring pass, we can let the counter be stepped. % And, actually we should do so, for example, in a \env{tabularx} with % multiple postnotes, if we don't step the counter, all the measuring will be % done with the number of the first note. Otherwise, we don't actually step % the counter but, to typeset correctly the mark that would be printed if the % counter had been stepped, we increment \cs{c@postnote} locally and grouped, % and smuggle \cs{thepostnote} out of the group. % \begin{macrocode} \bool_lazy_all:nT { { \l_@@_inhibit_note_bool } { \l_@@_print_plain_mark_bool } { ! \l_@@_nomark_bool } } { \tl_if_empty:NTF \l_@@_mark_tl { \bool_if:NTF \l_@@_print_plain_mark_stepcounter_bool { \stepcounter { postnote } \tl_set:Ne \l_@@_mark_typeset_tl { \thepostnote } } { \group_begin: \int_incr:N \c@postnote \exp_args:NNNe \group_end: \tl_set:Nn \l_@@_mark_typeset_tl { \thepostnote } } % \end{macrocode} % If the note has a \opt{label}, use a cross-reference to that as the mark % instead. In principle, the procedure above is expected to work reasonably % well for cases where we know whether we are in a measuring pass or not, and % how it handles the counters (if it restores counters or not). This is true % though, only if we are going in the expansion order of the document. If we % are using the \opt{counteraux} option, the mere sequence of the notes is no % longer an indicator of who is a measuring pass of who, indeed the measuring % pass does not even get to the \file{.aux} file. If the label exists, % though, it is \emph{known to be right} regardless of the case, since it % belongs to the pass which actually gets typeset. Hence, if we have a label, % it is more general and more reliable: use it. % \begin{macrocode} \@@_get_label_if_exist:N \l_@@_mark_typeset_tl } { \tl_set_eq:NN \l_@@_mark_typeset_tl \l_@@_mark_tl } \group_begin: \socket_assign_plug:nn { tagsupport/postnotes/multisep/begin } { noop } \socket_assign_plug:nn { tagsupport/postnotes/multisep/end } { noop } \@@_typeset_mark_wrapper:nnn { \@@_make_mark:Vnn \l_@@_mark_typeset_tl { } { } } { } { } \group_end: } \bool_if:NTF \l_@@_inhibit_note_bool { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_label_if_exist:N} % \begin{syntax} % \cs{@@_get_label_if_exist:N} \meta{\cs{l_@@_mark_tl}} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_get_label_if_exist:N #1 { \str_if_empty:NTF \l_@@_note_label_str { \str_if_empty:NF \l_@@_note_zlabel_str { \exp_args:NV \zref@ifrefundefined \l_@@_note_zlabel_str { } { \exp_args:NV \zref@ifrefcontainsprop \l_@@_note_zlabel_str { postnote@mark } { \exp_args:NNNo \exp_args:NNo \tl_set:Nn #1 { \zref@extract { \l_@@_note_zlabel_str } { postnote@mark } } } { } } } } { \exp_args:Ne \property_if_recorded:nnT { postnotes@ \l_@@_note_label_str } { postnotes/mark } { \tl_set:Ne #1 { \property_ref:ee { __postnotes_ \l_@@_note_label_str } { postnotes/mark } } } } } % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \@@_typeset_mark:nnN , % \@@_typeset_mark_wrapper:nnn , % } % Auxiliary functions for mark typesetting in \cs{@@_note:nn}. % \cs{@@_typeset_mark_wrapper:nnn} is based on the definition of % \cs{@footnotemark} in the kernel. % \begin{syntax} % \cs{@@_typeset_mark:nnN} \Arg{note id} \Arg{mark} \Arg{labels} % \cs{@@_typeset_mark_wrapper:nnn} \Arg{mark} % ~~\Arg{begin tagging} \Arg{end tagging} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_typeset_mark:nnN #1#2#3 { \@@_typeset_mark_wrapper:nnn { #3 \bool_if:NTF \l_@@_hyperlink_bool { \@@_make_mark:nnn {#2} { \hyper@linkstart { link } { postnote. #1 .text } } { \hyper@linkend } } { \@@_make_mark:nnn {#2} { } { } } } { \tag_socket_use:n { postnotes/mark/begin } } { \tag_socket_use:n { postnotes/mark/end } } } \cs_generate_variant:Nn \@@_typeset_mark:nnN { eVN } \tl_new:N \l_@@_saved_spacefactor_tl \cs_new_protected:Npn \@@_typeset_mark_wrapper:nnn #1#2#3 { \mode_leave_vertical: \mode_if_horizontal:T { \tl_set:Ne \l_@@_saved_spacefactor_tl { \int_use:N \spacefactor } \@@_multiple_check: \nobreak } #2 % begin tagging socket #1 % mark #3 % end tagging socket \@@_multiple_prepare: \mode_if_horizontal:T { \spacefactor \l_@@_saved_spacefactor_tl } \scan_stop: } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_set_user_labels:} % Auxiliary function for user label setting in \cs{@@_note:nn}. Supports % the \opt{label} and \opt{zlabel} options of \cs{postnote}. % \begin{macrocode} \cs_new_protected:Npn \@@_set_user_labels: { \str_if_empty:NF \l_@@_note_label_str { \exp_args:NV \label \l_@@_note_label_str \property_record:ee { __postnotes_ \l_@@_note_label_str } { postnotes/mark } } \str_if_empty:NF \l_@@_note_zlabel_str { \exp_args:NV \zlabel \l_@@_note_zlabel_str } } \property_new:nnnn { postnotes/mark } { now } { } { \l_@@_mark_tl } % \end{macrocode} % \end{macro} % % % \section{\cs{postnoteref}} % % \begin{macro}[int]{\postnoteref} % Provide \cs{postnoteref}. % \begin{syntax} % \cs{postnoteref}\meta{*}\marg{label} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \postnoteref { s m } { \@@_note_ref:nn {#1} {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_note_ref:nn} % The internal version of \cs{postnoteref}. % \begin{syntax} % \cs{@@_note_ref:nn} \Arg{star bool} \Arg{label} % \end{syntax} % \begin{macrocode} \tl_new:N \l_@@_note_ref_label_tl \cs_new_protected:Npn \@@_note_ref:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_note_ref_label_tl {#2} \@@_typeset_mark_wrapper:nnn { \bool_lazy_and:nnTF { ! #1 } { \l_@@_hyperlink_bool } { \hyperref [#2] { \@@_make_mark:nnn { \ref*{#2} } { } { } } } { \@@_make_mark:nnn { \ref*{#2} } { } { } } } { \tag_socket_use:n { postnotes/postnoteref/begin } } { \tag_socket_use:n { postnotes/postnoteref/end } } \group_end: } % \end{macrocode} % \end{macro} % % % \section{\cs{postnotesection}} % % \begin{macro}[int]{\postnotesection} % Provide \cs{postnotesection}. % \begin{syntax} % \cs{postnotesection}\oarg{options}\marg{section content} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \postnotesection { O { } +m } { \@bsphack \@@_section:nn {#1} {#2} \@esphack } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_section:nn} % The internal version of \cs{postnotesection}. % \begin{syntax} % \cs{@@_section:nn} \Arg{options} \Arg{content} % \end{syntax} % \begin{macrocode} \int_new:N \g_@@_sectid_int \cs_new_protected:Npn \@@_section:nn #1#2 { \group_begin: \int_gincr:N \g_@@_sectid_int \int_gincr:N \g_@@_note_id_int \seq_gput_right:Ne \g_@@_queue_seq { \l_postnotes_note_id_tl } \tl_gclear:N \g_@@_section_name_tl \keys_set:nn { postnotes/section } {#1} \@@_set_section_page_label:e { \l_postnotes_note_id_tl } \bool_if:NTF \l_@@_section_exp_bool { \@@_store_section:ne { \l_postnotes_note_id_tl } {#2} } { \@@_store_section:nn { \l_postnotes_note_id_tl } {#2} } \group_end: } % \end{macrocode} % \end{macro} % % % Options for \cs{postnotesection}. Actually, I would have preferred to use % ``label'' for the \opt{name} option, but I feared I might need it further % down the road for the traditional meaning. % % \begin{macrocode} \tl_new:N \g_@@_section_name_tl \bool_new:N \l_@@_section_exp_bool \keys_define:nn { postnotes/section } { name .tl_gset:N = \g_@@_section_name_tl , name .value_required:n = true , exp .bool_set:N = \l_@@_section_exp_bool , exp .initial:n = false , exp .default:n = true , } % \end{macrocode} % % % \section{\cs{printpostnotes}} % % \begin{macro}[int]{\printpostnotes} % Provide \cs{printpostnotes}. % \begin{syntax} % \cs{printpostnotes} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \printpostnotes { } { \@@_print_notes: } % \end{macrocode} % \end{macro} % % % \begin{macro}[int] % { % \pnthechapter , % \pnthesection , % \pnthechapternextnote , % \pnthesectionnextnote , % \pnthepage , % \pnidnextnote , % } % User facing variables, aimed at making available some of the notes' and % sections' metadata for the user at specific contexts. % \begin{macrocode} \tl_new:N \pnthechapter \tl_new:N \pnthesection \tl_new:N \pnidnextnote \tl_new:N \pnthechapternextnote \tl_new:N \pnthesectionnextnote \tl_new:N \pnthepage % \end{macrocode} % \end{macro} % % \begin{macro}[int] % { % \g_@@_print_postnotes_int , % \g_@@_print_queue_seq , % \l_postnotes_print_note_id_tl , % \l_@@_print_note_id_next_tl , % \l_@@_print_counter_tl , % \l_@@_print_mark_tl , % \l_@@_print_typeset_mark_tl , % \l_@@_print_type_curr_tl , % \l_@@_print_type_next_tl , % \l_@@_print_type_prev_tl , % \l_@@_print_content_tl , % \l_@@_clear_queue_seq , % } % Auxiliary variables for \cs{@@_print_notes:}. % \begin{macrocode} \int_new:N \g_@@_print_postnotes_int \seq_new:N \g_@@_print_queue_seq \tl_new:N \l_postnotes_print_note_id_tl \tl_new:N \l_@@_print_note_id_next_tl \tl_new:N \l_@@_print_counter_tl \tl_new:N \l_@@_print_mark_tl \tl_new:N \l_@@_print_typeset_mark_tl \tl_new:N \l_@@_print_type_curr_tl \tl_new:N \l_@@_print_type_next_tl \tl_new:N \l_@@_print_type_prev_tl \tl_new:N \l_@@_print_content_tl \seq_new:N \l_@@_clear_queue_seq % \end{macrocode} % \end{macro} % % % \cs{@@_print_notes:}' hooks. Both meant at providing points of entry for % additional setup, specially to add support to packages and features which % require it. The \texttt{postnotes/print/begin} hook is run early in % \cs{@@_print_notes:} and only once per call, after the user options have % been processed. The \texttt{postnotes/print/note/begin} hook is run once % for each note, at the point where environment variables are being set or % restored, before the typesetting of either the mark or the text, but within % a group of its own of each note. % % \begin{macrocode} \NewHook { postnotes/print/begin } \NewHook { postnotes/print/note/begin } % \end{macrocode} % % % The \texttt{postnotetext} is a counter used to restore the original value of % \texttt{postnote} at the time of printing, for the purposes of % cross-referencing, it should be different from \texttt{postnote} if a note % may occur inside \cs{printpostnotes}. The \texttt{postnotesection} is a % counter which is stepped for every postnote section which gets to be % actually typeset. It's aim is to provide a valid ``enclosing counter'' to % \texttt{postnote} in the context of \cs{printpostnotes}. Since we don't % know where \texttt{postnote} may have been reset along the document, in the % general case, we can't rely on any other preexisting counter. This means % that the particular value of \texttt{postnotesection} is of little practical % meaning, it really is just meant to provide recognizable ``bounds'' for % \texttt{postnote} along the printing of the notes. Indeed, it is % initialized to a very high value (larger than the conceivable number of % postnote sections in a document), so that ``marks'' and ``texts'' don't mix % in the same reference list, which would occur if the enclosing counters of % both belonged to the same range, and with somewhat arbitrary results, since % we cannot ensure the step of the enclosing counter along the document % matches \texttt{postnotesection}. This is actually a tricky problem from % the cross-referencing standpoint: two different things, which should be of % the same type, are reset along the document, but shouldn't really be mixed % together. They are both \LaTeXe{} counters, since they may be required % externally. Their main intended use case is to support \pkg{zref-clever}, % but in principle they can be of general use. % % \begin{macrocode} \newcounter { postnotetext } \newcounter { postnotesection } \setcounter { postnotesection } { 10000 } % \end{macrocode} % % % \begin{macro}{\@@_print_notes:} % The internal version of \cs{printpostnotes}. % \begin{syntax} % \cs{@@_print_notes:} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_print_notes: { \group_begin: \int_gincr:N \g_@@_print_postnotes_int \@@_split_labelseq: % \end{macrocode} % We make the cut at this point. \cs{g_@@_print_queue_seq} is stored won't % receive any more notes for the duration of this call of \cs{printpostnotes}, % any notes added to the queue from this point on belong to the next call of % \cs{printpostnotes}. % \begin{macrocode} \bool_lazy_and:nnTF { \g_@@_counteraux_bool } { ! \g_@@_firstrun_bool } { \seq_gset_eq:NN \g_@@_print_queue_seq \g_@@_print_labelseq_queue_seq } { \seq_gset_eq:NN \g_@@_print_queue_seq \g_@@_queue_seq } \seq_set_eq:NN \l_@@_clear_queue_seq \g_@@_print_queue_seq \seq_gclear:N \g_@@_queue_seq % \end{macrocode} % The purpose of this label is to provide a point for splitting the labelseq % with the \opt{counteraux} option. It only needs to exist, it doesn't even % store the page value. The \cs{@@_set_print_page_label:e} done at % \cs{@@_set_headers_vars_first:} right below does not suffice for this % purpose for two reasons. It won't be set if the queue is empty (or not yet % populated), and also it comes after the heading (as it must), which means % \cs{postnotesection}s added through hooks to it will come before it. % \begin{macrocode} \@@_set_pre_print_label:e { \int_use:N \g_@@_print_postnotes_int } \seq_if_empty:NTF \g_@@_print_queue_seq { \msg_warning:nn { postnotes } { empty-printpostnotes } } { \pnheading \UseHook { postnotes/print/begin } \tl_set:Nn \l_@@_print_type_prev_tl { open } \@@_check_duplicates:N \g_@@_print_queue_seq \@@_sort_queue:N \g_@@_print_queue_seq \@@_check_floats:N \g_@@_print_queue_seq \bool_gset_true:N \g_@@_header_vars_next_bool \@@_get_headers_data:N \g_@@_print_queue_seq \@@_set_headers_vars_first: % \end{macrocode} % Ensure the first note after a heading has paragraph indentation when % \opt{listenv} is \texttt{none}. \pkg{endnotes} uses a workaroundish % solution in \cs{enoteheading}, setting a box and then skipping back a line. % Enrico Gregorio is correct, though, in criticizing it at % \url{https://tex.stackexchange.com/q/575905#comment1450213_575915}, and % suggests the use of \cs{@afterindenttrue}, which is what \pkg{indentfirst} % does (we do the same, just locally). % \begin{macrocode} \bool_if:NF \l_@@_print_as_list_bool { \cs_set_eq:NN \@afterindentfalse \@afterindenttrue \@afterindenttrue } \bool_until_do:nn { \seq_if_empty_p:N \g_@@_print_queue_seq } { \seq_gpop_left:NN \g_@@_print_queue_seq \l_postnotes_print_note_id_tl \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { type } \l_@@_print_type_curr_tl \tl_if_eq:NnTF \l_@@_print_type_curr_tl { section } { % type_curr = `section' \seq_if_empty:NTF \g_@@_print_queue_seq { \tl_set:Nn \l_@@_print_note_id_next_tl { noid } \tl_set:Nn \l_@@_print_type_next_tl { close } } { \seq_get_left:NN \g_@@_print_queue_seq \l_@@_print_note_id_next_tl \@@_prop_get:nnN { \l_@@_print_note_id_next_tl } { type } \l_@@_print_type_next_tl } % \end{macrocode} % We only process the entry if \texttt{type_next} is \texttt{note}: here are % skipped empty sections. % \begin{macrocode} \tl_if_eq:NnT \l_@@_print_type_next_tl { note } { \stepcounter { postnotesection } \group_begin: \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { thechapter } \pnthechapter \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { thesection } \pnthesection \tl_set:NV \pnidnextnote \l_@@_print_note_id_next_tl \@@_prop_get:nnN { \l_@@_print_note_id_next_tl } { thechapter } \pnthechapternextnote \@@_prop_get:nnN { \l_@@_print_note_id_next_tl } { thesection } \pnthesectionnextnote \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { content } \l_@@_print_content_tl \l_@@_print_content_tl \group_end: % \end{macrocode} % Set \texttt{type_prev} for the next iteration. % \begin{macrocode} \tl_set:NV \l_@@_print_type_prev_tl \l_@@_print_type_curr_tl } } { % type_curr = `note' \seq_if_empty:NTF \g_@@_print_queue_seq { \tl_set:Nn \l_@@_print_note_id_next_tl { noid } \tl_set:Nn \l_@@_print_type_next_tl { close } } { \seq_get_left:NN \g_@@_print_queue_seq \l_@@_print_note_id_next_tl \@@_prop_get:nnN { \l_@@_print_note_id_next_tl } { type } \l_@@_print_type_next_tl } \tl_if_eq:NnF \l_@@_print_type_prev_tl { note } { \bool_if:NTF \l_@@_print_as_list_bool { \exp_args:NV \begin \l_@@_print_env_tl } { \group_begin: } \tag_socket_use:n { postnotes/printlist/begin } \l_@@_print_format_tl } \group_begin: \UseHook { postnotes/print/note/begin } \@@_get_pageref:Ne \pnthepage { mark@ \l_postnotes_print_note_id_tl } \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { mark } \l_@@_print_mark_tl \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { counter } \l_@@_print_counter_tl \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { content } \l_@@_print_content_tl \cs_set:Npn \@currentcounter { postnotetext } \int_set:Nn \c@postnotetext { \l_@@_print_counter_tl } \cs_set:Npe \@currentlabel { \p@postnote \l_@@_print_mark_tl } \tl_set:Nn \l_@@_print_typeset_mark_tl { \tag_socket_use:n { postnotes/printmark/begin } \MakeLinkTarget* { postnote. \l_postnotes_print_note_id_tl .text } \@@_set_text_page_label:e { \l_postnotes_print_note_id_tl } \l_@@_pre_textmark_tl \@@_typeset_text_mark:eV { \l_postnotes_print_note_id_tl } \l_@@_print_mark_tl \l_@@_post_textmark_tl \tag_socket_use:n { postnotes/printmark/end } } % \end{macrocode} % Note that the placement of the tagging socket for the list case may depend % on the tagging structure, in other words, on the content of the socket. It % currently does nothing for the list case, so I've placed it in the % ``potentially most useful place''. Review this if the content changes. % Leave vertical mode after \cs{item} for the list case to avoid ``perhaps a % missing \cs{item}'' error for empty notes (see % \file{pn-bug-empty-postnote01.lvt}). And leave vertical mode before the % note (and the tagging socket) for \opt{listenv=none} (see % \url{https://github.com/gusbrs/postnotes/issues/8#issuecomment-2429501962}, % thanks \contributor{Ulrike Fischer}). % \begin{macrocode} \bool_if:NTF \l_@@_print_as_list_bool { \item [ \tag_socket_use:n { postnotes/printnote/begin } \l_@@_print_typeset_mark_tl ] \mode_leave_vertical: } { \mode_leave_vertical: \tag_socket_use:n { postnotes/printnote/begin } \l_@@_print_typeset_mark_tl } \tag_socket_use:n { postnotes/printtext/begin } \l_@@_print_content_tl \tag_socket_use:n { postnotes/printtext/end } \tag_socket_use:n { postnotes/printnote/end } \l_@@_post_printnote_tl \group_end: \tl_if_eq:NnF \l_@@_print_type_next_tl { note } { \tag_socket_use:n { postnotes/printlist/end } % \end{macrocode} % Ensure \cs{par} at the end of \cs{printpostnotes} (see % \url{https://github.com/u-fischer/tagpdf/issues/68#issuecomment-1587343876}, % thanks \contributor{Ulrike Fischer}). % \begin{macrocode} \bool_if:NTF \l_@@_print_as_list_bool { \exp_args:NV \end \l_@@_print_env_tl \par } { \par \group_end: } } % \end{macrocode} % Set \texttt{type_prev} for the next iteration. % \begin{macrocode} \tl_set:NV \l_@@_print_type_prev_tl \l_@@_print_type_curr_tl } } \AddToHookNext { shipout/after } { \bool_gset_false:N \g_@@_header_vars_next_bool } % \end{macrocode} % We won't use the variables anymore, clear them to reduce memory usage. % \begin{macrocode} \seq_map_inline:Nn \l_@@_clear_queue_seq { \@@_prop_gclear:n { ##1 } } } \group_end: } % \end{macrocode} % \end{macro} % % \begin{macrocode} \msg_new:nnn { postnotes } { empty-printpostnotes } { Empty~'\iow_char:N\\printpostnotes'~\msg_line_context:. } % \end{macrocode} % % % \begin{macro}{\@@_typeset_text_mark:nn} % Auxiliary function for mark typesetting in \cs{@@_print_notes:}. % \begin{syntax} % \cs{@@_typeset_text_mark:nn} \Arg{note id} \Arg{mark} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_typeset_text_mark:nn #1#2 { \bool_lazy_and:nnTF { \l_@@_hyperlink_bool } { \l_@@_backlink_bool } { \@@_make_text_mark:nnn {#2} { \hyper@linkstart { link } { postnote. #1 .mark } } { \hyper@linkend } } { \@@_make_text_mark:nnn {#2} { } { } } } \cs_generate_variant:Nn \@@_typeset_text_mark:nn { eV } % \end{macrocode} % \end{macro} % % % \subsection*{Print auxiliary} % % The conditions used to split \cs{g_@@_labelseq_seq} are subtly different % depending on whether we are using \cs{g_@@_counteraux_bool} or not. In the % standard case, the numbering of the floats are set at ``expansion time'', so % they belong where they are set. With \opt{counteraux}, the numbering of % floats are set at ``shipout time'', so they belong where they are typeset. % In other words, with \opt{counteraux} notes on floats can cross the % boundaries of \cs{printpostnotes}, while without it, they must not. % Furthermore, the purpose of \cs{g_@@_labelseq_seq} is different in each % case. With \opt{counteraux} it is used to build the actual print queue, % while in the standard case it is only used in \cs{@@_check_floats:N}. % Therefore, with \opt{counteraux} the cut is made at the place the preprint % label for the current \cs{printpostnotes} is found, while in the standard % case, \cs{g_@@_note_id_int} is used directly to distribute the elements % between the current \cs{printpostnotes} and future ones. % % % \begin{macro}{\@@_split_labelseq:} % \begin{macrocode} \seq_new:N \g_@@_print_labelseq_queue_seq \cs_new_protected:Npn \@@_split_labelseq: { \group_begin: \seq_clear:N \l_@@_tmpa_seq \seq_clear:N \l_@@_tmpb_seq \bool_if:NTF \g_@@_counteraux_bool { \tl_set:Ne \l_@@_tmpa_tl { { preprint } { \int_use:N \g_@@_print_postnotes_int } } % \end{macrocode} % The preprint label of a \cs{printpostnotes} at the end of the document may % not have been written: if it's empty, it may not be shipped out at all. % But, since it's a counter, stepped sequentially and not floating, even if it % is transitorily missing, it will be at the end. In other words, if this one % is not found, there will be no subsequent preprints in the sequence. % \begin{macrocode} \seq_if_in:NVF \g_@@_labelseq_seq \l_@@_tmpa_tl { \seq_gput_right:NV \g_@@_labelseq_seq \l_@@_tmpa_tl } \bool_do_until:nn { \tl_if_eq_p:NN \l_@@_tmpb_tl \l_@@_tmpa_tl } { \seq_gpop_left:NN \g_@@_labelseq_seq \l_@@_tmpb_tl \str_case:enT { \tl_item:Nn \l_@@_tmpb_tl { 1 } } { { mark } { } { section } { } } { % \end{macrocode} % If the id of the labelseq item is larger than the current note id, we don't % have data for the note at this point, and cannot print it. Period. Now, % usually this will occur due to transitory effects. But it is theoretically % possible that a float is sent to the top of the page and gets typeset before % a ``future \cs{printpostnotes}''. So what we cannot print, we give back to % the label sequence of the next \cs{printpostnotes}. If they are transitory, % they will eventually go away. If they are not, better to keep them with the % wrong numbering than dropping it altogether. Alas, there's nothing else we % could do, short of writing the whole data to the \file{.aux} file, which is % clearly not worth this corner case. % \begin{macrocode} \int_compare:nNnTF { \tl_item:Nn \l_@@_tmpb_tl { 2 } } > { \g_@@_note_id_int } { \seq_put_right:Ne \l_@@_tmpb_seq \l_@@_tmpb_tl } { \seq_put_right:Ne \l_@@_tmpa_seq { \tl_item:Nn \l_@@_tmpb_tl { 2 } } } } % \end{macrocode} % No need for the \texttt{F} branch of \cs{str_case:enT}, at this point the % preprint of past \cs{printpostnotes} can no longer be here, and we don't go % further than the current one. In theory, we could even go without % \cs{str_case:enT}, but let's play safe and keep the function robust against % future changes of the code. % \begin{macrocode} } \seq_gset_eq:NN \g_@@_print_labelseq_queue_seq \l_@@_tmpa_seq \seq_concat:NNN \l_@@_tmpa_seq \l_@@_tmpb_seq \g_@@_labelseq_seq \seq_gset_eq:NN \g_@@_labelseq_seq \l_@@_tmpa_seq } { \seq_map_inline:Nn \g_@@_labelseq_seq { \str_case:enT { \tl_item:nn { ##1 } { 1 } } { { mark } { } { section } { } } { \int_compare:nNnTF { \tl_item:nn { ##1 } { 2 } } > { \g_@@_note_id_int } { \seq_put_right:Nn \l_@@_tmpb_seq { ##1 } } { \seq_put_right:Ne \l_@@_tmpa_seq { \tl_item:nn { ##1 } { 2 } } } } % \end{macrocode} % Also no need for the \texttt{F} branch here, but for a different reason. % The preprint label plays no role whatsoever if \opt{couteraux=false}, so we % just drop them altogether if found. % \begin{macrocode} } \seq_gset_eq:NN \g_@@_print_labelseq_queue_seq \l_@@_tmpa_seq \seq_gset_eq:NN \g_@@_labelseq_seq \l_@@_tmpb_seq } \group_end: } % \end{macrocode} % \end{macro} % % % \cs{@@_check_duplicates:N} provides a general procedure for handling cases % of multiple passes of content. Ideally, the job should be done at % \cs{@@_inhibit_note:F}, if at all possible. But, failing that, we can rely % on the fact that the labels of \cs{postnote}s of measuring/trial passes, % being delayed \cs{write}s (whatsits), don't end up being written to the % \file{.aux} file. However, despite this being a general test, and a % reasonable one, I'd like to restrain it's use to the minimum possible. % Using this criterion across the board could result in large swings on the % content of \cs{printpostnotes} and spurious warnings. Hence, we only apply % this check for ``eligible'' items. For signaling this eligibility, the note % must have been stored with the \cs{l_@@_maybe_multi_bool} set to % \texttt{true}, which is then saved in the \texttt{multibool} property. One % implication of this procedure is that, if there are any new notes marked as % \texttt{multibool}, three rounds of compilation will be needed, since the % labels of the printed notes will be written only on the second run and the % document will thus require a third one to stabilize. % % \begin{macro}{\@@_check_duplicates:N} % \begin{syntax} % \cs{@@_check_duplicates:N} \meta{\cs{g_@@_print_queue_seq}} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_check_duplicates:N #1 { % \end{macrocode} % On a first run, don't even try to check for duplicates. Better % \texttt{transitorily} let some duplicates pass than to drop every legitimate % note. % \begin{macrocode} \bool_lazy_and:nnT { ! \g_@@_firstrun_bool } { ! \g_@@_counteraux_bool } { \group_begin: \seq_clear:N \l_@@_tmpa_seq \seq_map_inline:Nn #1 { \bool_lazy_or:nnTF { \cs_if_exist_p:c { \c_@@_ref_prefix_tl @ mark @ ##1 } } % \end{macrocode} % Always keep sections. Empty sections are discarded anyway, and they are % unlikely to occur in places performing multiple passes. % \begin{macrocode} { \str_if_eq_p:ee { \@@_prop_item:nn {##1} { type } } { section } } { \seq_put_right:Nn \l_@@_tmpa_seq {##1} } { % \end{macrocode} % If \texttt{multibool} is true for the note, we drop it silently, otherwise % we include it, but warn of possible duplicate. % \begin{macrocode} \str_if_eq:eeF { \@@_prop_item:nn {##1} { multibool } } { true } { \seq_put_right:Nn \l_@@_tmpa_seq {##1} \bool_if:NT \l_@@_check_dupli_bool { \msg_warning:nne { postnotes } { possible-duplicate } { \@@_prop_item:nn {##1} { mark } } } } } } \seq_gset_eq:NN #1 \l_@@_tmpa_seq \group_end: } } % \end{macrocode} % \end{macro} % % \begin{macrocode} \msg_new:nnn { postnotes } { possible-duplicate } { Possible~duplicate~*around*~note~'#1'~\msg_line_context:. } % \end{macrocode} % % % \begin{macro}{\@@_check_floats:N} % \begin{syntax} % \cs{@@_check_floats:N} \meta{\cs{g_@@_print_queue_seq}} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_check_floats:N #1 { \bool_lazy_and:nnT { \l_@@_check_floats_bool } % \end{macrocode} % Ditto. In this case, the queue is not touched, but it would still be a % spurious warning. % \begin{macrocode} { ! \g_@@_firstrun_bool } { \group_begin: % \end{macrocode} % Only compare sequence net of non-existing labels. % \begin{macrocode} \seq_set_filter:NNn \l_@@_tmpa_seq #1 { \bool_lazy_or_p:nn { \cs_if_exist_p:c { \c_@@_ref_prefix_tl @ mark @ ##1 } } { \cs_if_exist_p:c { \c_@@_ref_prefix_tl @ section @ ##1 } } } % \end{macrocode} % Not very \texttt{expl3}-y, I know. But I don't see a \cs{seq_if_eq:NNTF} % available and, technically, sequences are just structured token lists. % \begin{macrocode} \tl_if_eq:NNF \g_@@_print_labelseq_queue_seq \l_@@_tmpa_seq { \msg_warning:nn { postnotes } { possible-shuffle } } \group_end: } } % \end{macrocode} % \end{macro} % % \begin{macrocode} \msg_new:nnn { postnotes } { possible-shuffle } { Possible~out~of~sequence~notes~due~to~floats~\msg_line_context:. } % \end{macrocode} % % % \begin{macro}{\@@_sort_queue:N} % Sorting function for \cs{@@_print_notes:}. % \begin{syntax} % \cs{@@_sort_queue:N} \meta{\cs{g_@@_print_queue_seq}} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_sort_queue:N #1 { \bool_if:NT \l_@@_sort_bool { \group_begin: \seq_gsort:Nn #1 { \@@_prop_get:nnN {##1} { pnsectid } \l_@@_tmpa_tl \@@_prop_get:nnN {##2} { pnsectid } \l_@@_tmpb_tl \tl_if_eq:NNTF \l_@@_tmpa_tl \l_@@_tmpb_tl { \@@_prop_get:nnN {##1} { type } \l_@@_tmpa_tl \@@_prop_get:nnN {##2} { type } \l_@@_tmpb_tl \bool_lazy_and:nnTF { \str_if_eq_p:Vn \l_@@_tmpa_tl { note } } { \str_if_eq_p:Vn \l_@@_tmpb_tl { note } } { \@@_prop_get:nnN {##1} { sortnum } \l_@@_tmpa_tl \@@_prop_get:nnN {##2} { sortnum } \l_@@_tmpb_tl \fp_compare:nNnTF { \l_@@_tmpa_tl } > { \l_@@_tmpb_tl } { \sort_return_swapped: } { \sort_return_same: } } { \sort_return_same: } } { \sort_return_same: } } \group_end: } } % \end{macrocode} % \end{macro} % % % \begin{macrocode} \AddToHook { enddocument/afterlastpage } { \group_begin: \bool_if:NTF \g_@@_counteraux_bool { \seq_set_filter:NNn \l_@@_tmpa_seq \g_@@_labelseq_seq { \str_if_eq_p:ee { \tl_item:nn {#1} { 1 } } { mark } } } { \seq_set_filter:NNn \l_@@_tmpa_seq \g_@@_queue_seq { \str_if_eq_p:ee { \@@_prop_item:nn {#1} { type } } { note } } } \seq_if_empty:NF \l_@@_tmpa_seq { \msg_warning:nne { postnotes } { stray-notes } { \seq_count:N \l_@@_tmpa_seq } } \group_end: } \msg_new:nnn { postnotes } { stray-notes } { There~are~'#1'~stray~notes~after~the~last~'\iow_char:N\\printpostnotes'~ \msg_line_context:.~At~this~point,~they~are~lost. } % \end{macrocode} % % % % \section{Headers} % % The headers infrastructure of \pkg{postnotes} is comprised of three basic % parts: % % \begin{enumerate} % \item For each \cs{postnote}, labels are set storing the \texttt{page} where % the note occurs. Each note actually generates a pair of such labels, once % when \cs{postnote} is called (with the mark), and another where the note % is printed (in \cs{printpostnotes}). The former ones store \cs{thepage}, % since we want the printed representation of it for typesetting purposes, % the latter ones store the value of the \texttt{page} counter, since we % don't need to typeset it, but do need to perform algebraic operations with % it. These labels are set by \cs{@@_set_mark_page_label:nn}, % \cs{@@_set_text_page_label:n}, and \cs{@@_set_print_page_label:n} at the % appropriate places. The set of these labels provides a mapping from each % note's ``mark'' and ``text'' to the page where it occurs. % \item This information set is processed by \cs{@@_get_headers_data:N} at the % beginning of \cs{@@_print_notes:} to identify the first and last note of % each page in \cs{printpostnotes}, and to generate a mapping from these % first and last notes on each page to the pages where their corresponding % marks occur. We also take the opportunity to enrich this mapping with % other metadata of each note. So we get also mappings from the first and % last note on each page to \cs{thechapter}, \cs{thesection}, and the % \opt{name} of the section in which they occur. These mappings are stored % in property lists \cs[no-index]{g_@@_header_\meta{info}_first_prop} and % \cs[no-index]{g_@@_header_\meta{info}_last_prop} where the \texttt{key} is % the page in \cs{printpostnotes} where their note's content is typeset (or % rather where it starts to be typeset, it is the page where the text's mark % is printed). % \item Based on these mappings, along the span of notes section we run % \cs{@@_set_headers_vars_next:} at each \texttt{shipout/before} hook to set % user facing variables for the \emph{next} page, which will be available % when their heading gets typeset. Given that at \texttt{shipout} we can % rely on a correct value of the \texttt{page} counter, we use it as % \texttt{key} to query the property lists generated in the previous step. % These user facing variables are called \cs[no-index]{pnhd\meta{info}first} % and \cs[no-index]{pnhd\meta{info}last}. Since we cannot rely on the % shipout hook for the first page of \cs{printpostnotes}, % \cs{@@_set_headers_vars_first:} is run at its beginning to ensure correct % values are in place on the first page of the notes section. % \end{enumerate} % % These \cs[no-index]{pnhd\meta{info}first} and % \cs[no-index]{pnhd\meta{info}last} variables can then be used to build % simple functions which can be passed to mark commands to achieve rich % contextual running headers. % % % \begin{macro}[int] % { % \pnhdpagefirst , % \pnhdpagelast , % \pnhdchapfirst , % \pnhdchaplast , % \pnhdsectfirst , % \pnhdsectlast , % \pnhdnamefirst , % \pnhdnamelast , % } % User facing variables, aimed at making available header data for the user. % Setting these variables with correct values at the moment the header gets % typeset is \emph{the} objective of the whole headers infrastructure of the % package. % \begin{macrocode} \tl_new:N \pnhdpagefirst \tl_new:N \pnhdpagelast \tl_new:N \pnhdchapfirst \tl_new:N \pnhdchaplast \tl_new:N \pnhdsectfirst \tl_new:N \pnhdsectlast \tl_new:N \pnhdnamefirst \tl_new:N \pnhdnamelast % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \g_@@_header_page_first_prop , % \g_@@_header_page_last_prop , % \g_@@_header_chap_first_prop , % \g_@@_header_chap_last_prop , % \g_@@_header_sect_first_prop , % \g_@@_header_sect_last_prop , % \g_@@_header_name_first_prop , % \g_@@_header_name_last_prop , % \g_@@_header_prev_last_page_tl , % \g_@@_header_prev_last_chap_tl , % \g_@@_header_prev_last_sect_tl , % \g_@@_header_prev_last_name_tl , % \l_@@_prev_text_page_tl , % \l_@@_curr_text_page_tl , % \l_@@_prev_mark_page_tl , % \l_@@_prev_mark_chap_tl , % \l_@@_prev_mark_sect_tl , % \l_@@_prev_mark_name_tl , % } % Auxiliary variables for the headers infrastructure. % \begin{macrocode} \prop_new:N \g_@@_header_page_first_prop \prop_new:N \g_@@_header_page_last_prop \prop_new:N \g_@@_header_chap_first_prop \prop_new:N \g_@@_header_chap_last_prop \prop_new:N \g_@@_header_sect_first_prop \prop_new:N \g_@@_header_sect_last_prop \prop_new:N \g_@@_header_name_first_prop \prop_new:N \g_@@_header_name_last_prop \tl_new:N \g_@@_header_prev_last_page_tl \tl_new:N \g_@@_header_prev_last_chap_tl \tl_new:N \g_@@_header_prev_last_sect_tl \tl_new:N \g_@@_header_prev_last_name_tl \tl_new:N \l_@@_prev_text_page_tl \tl_new:N \l_@@_curr_text_page_tl \tl_new:N \l_@@_prev_mark_page_tl \tl_new:N \l_@@_prev_mark_chap_tl \tl_new:N \l_@@_prev_mark_sect_tl \tl_new:N \l_@@_prev_mark_name_tl % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_get_headers_data:N} % Process header data for \cs{@@_set_headers_vars:n}. % \begin{syntax} % \cs{@@_get_headers_data:N} \meta{\cs{g_@@_print_queue_seq}} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_get_headers_data:N #1 { \group_begin: \tl_gclear:N \pnhdpagefirst \tl_gclear:N \pnhdpagelast \tl_gclear:N \pnhdchapfirst \tl_gclear:N \pnhdchaplast \tl_gclear:N \pnhdsectfirst \tl_gclear:N \pnhdsectlast \tl_gclear:N \pnhdnamefirst \tl_gclear:N \pnhdnamelast \prop_gclear:N \g_@@_header_page_first_prop \prop_gclear:N \g_@@_header_page_last_prop \prop_gclear:N \g_@@_header_chap_first_prop \prop_gclear:N \g_@@_header_chap_last_prop \prop_gclear:N \g_@@_header_sect_first_prop \prop_gclear:N \g_@@_header_sect_last_prop \prop_gclear:N \g_@@_header_name_first_prop \prop_gclear:N \g_@@_header_name_last_prop \tl_gclear:N \g_@@_header_prev_last_page_tl \tl_gclear:N \g_@@_header_prev_last_chap_tl \tl_gclear:N \g_@@_header_prev_last_sect_tl \tl_gclear:N \g_@@_header_prev_last_name_tl \tl_clear:N \l_@@_prev_text_page_tl \tl_clear:N \l_@@_curr_text_page_tl \tl_clear:N \l_@@_prev_mark_page_tl \tl_clear:N \l_@@_prev_mark_chap_tl \tl_clear:N \l_@@_prev_mark_sect_tl \tl_clear:N \l_@@_prev_mark_name_tl \seq_map_inline:Nn #1 { \exp_args:Ne \tl_if_eq:nnT { \@@_prop_item:nn {##1} { type } } { note } { \@@_get_pageref:Nn \l_@@_curr_text_page_tl { text@ ##1 } \tl_if_empty:NF \l_@@_curr_text_page_tl { \tl_if_eq:NNTF \l_@@_prev_text_page_tl \l_@@_curr_text_page_tl { % \end{macrocode} % We are on the same page as the previous note, just update the % \texttt{prev_mark} data. % \begin{macrocode} \@@_get_pageref:Nn \l_@@_prev_mark_page_tl { mark@ ##1 } \@@_prop_get:nnN {##1} { thechapter } \l_@@_prev_mark_chap_tl \@@_prop_get:nnN {##1} { thesection } \l_@@_prev_mark_sect_tl \@@_prop_get:nnN {##1} { pnsectname } \l_@@_prev_mark_name_tl } { % \end{macrocode} % We are on the transition between two pages, current ID is the first note of % the new page (or on the very first note of \cs{printpostnotes}, given % \cs{l_@@_prev_text_page_tl} is initialized to empty). % % Set `last' values for previous page, based on the last valid % \texttt{prev_mark} stored ones. There is no previous page to the first one % of \cs{printpostnotes}, so we don't set `last' values for it (conditioning % on \cs{l_@@_prev_text_page_tl} being empty, which only occurs on the first % note). % \begin{macrocode} \tl_if_empty:NF \l_@@_prev_text_page_tl { \prop_gput:Nee \g_@@_header_page_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_page_tl } \prop_gput:Nee \g_@@_header_chap_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_chap_tl } \prop_gput:Nee \g_@@_header_sect_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_sect_tl } \prop_gput:Nee \g_@@_header_name_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_name_tl } } % \end{macrocode} % % Set `first' values for current page, based on the current note ID. % \begin{macrocode} \prop_gput:Nee \g_@@_header_page_first_prop { \l_@@_curr_text_page_tl } { \@@_extract_pageref:n { mark@ ##1 } } \prop_gput:Nee \g_@@_header_chap_first_prop { \l_@@_curr_text_page_tl } { \@@_prop_item:nn {##1} { thechapter } } \prop_gput:Nee \g_@@_header_sect_first_prop { \l_@@_curr_text_page_tl } { \@@_prop_item:nn {##1} { thesection } } \prop_gput:Nee \g_@@_header_name_first_prop { \l_@@_curr_text_page_tl } { \@@_prop_item:nn {##1} { pnsectname } } % \end{macrocode} % % Store \texttt{prev_mark} data for the first note on the page. % \begin{macrocode} \@@_get_pageref:Nn \l_@@_prev_mark_page_tl { mark@ ##1 } \@@_prop_get:nnN {##1} { thechapter } \l_@@_prev_mark_chap_tl \@@_prop_get:nnN {##1} { thesection } \l_@@_prev_mark_sect_tl \@@_prop_get:nnN {##1} { pnsectname } \l_@@_prev_mark_name_tl % \end{macrocode} % % Set \cs{l_@@_prev_text_page_tl} for the next page % (\cs{l_@@_curr_text_page_tl} is never empty at this point, since we % conditioned to it). % \begin{macrocode} \tl_set:NV \l_@@_prev_text_page_tl \l_@@_curr_text_page_tl } } } } % \end{macrocode} % We can't catch the transition from the last page of \cs{printpostnotes} to % the following one through the mapping above, but the \texttt{prev_mark} % values of the last note in the loop are the ones we want, so we set `last' % values for the last page based on them. % \begin{macrocode} \tl_if_empty:NF \l_@@_prev_text_page_tl { \prop_gput:Nee \g_@@_header_page_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_page_tl } \prop_gput:Nee \g_@@_header_chap_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_chap_tl } \prop_gput:Nee \g_@@_header_sect_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_sect_tl } \prop_gput:Nee \g_@@_header_name_last_prop { \l_@@_prev_text_page_tl } { \l_@@_prev_mark_name_tl } } \group_end: } % \end{macrocode} % \end{macro} % % % The sequence of pages processed in \cs{@@_get_headers_data:N} is not ensured % to be continuous, since not every page of \cs{printpostnotes} starts a note. % There may be notes that fill whole pages, or the last page of the notes may % end with a note that started on the penultimate page. We must handle this % case at \cs{@@_set_headers_vars:n}. For every page for which there is % information provided by \cs{@@_get_headers_data:N} we store a % \texttt{header_prev_last} (the last value of the previous header) for each % of the variables of interest. If the next page is skipped in the sequence % (no notes starting on it), we can use these stored values to set both % `first' and `last' variables based on them for that page. % % % \begin{macro}{\@@_set_headers_vars:n} % Set user facing variables based on data generated by % \cs{@@_get_headers_data:N}. % \begin{syntax} % \cs{@@_set_headers_vars:n} \Arg{page number} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_set_headers_vars:n #1 { \group_begin: \prop_get:NnNTF \g_@@_header_page_first_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdpagefirst \l_@@_tmpa_tl } { \tl_gset:NV \pnhdpagefirst \g_@@_header_prev_last_page_tl } \prop_get:NnNTF \g_@@_header_page_last_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdpagelast \l_@@_tmpa_tl \tl_gset:NV \g_@@_header_prev_last_page_tl \l_@@_tmpa_tl } { \tl_gset:NV \pnhdpagelast \g_@@_header_prev_last_page_tl } \prop_get:NnNTF \g_@@_header_chap_first_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdchapfirst \l_@@_tmpa_tl } { \tl_gset:NV \pnhdchapfirst \g_@@_header_prev_last_chap_tl } \prop_get:NnNTF \g_@@_header_chap_last_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdchaplast \l_@@_tmpa_tl \tl_gset:NV \g_@@_header_prev_last_chap_tl \l_@@_tmpa_tl } { \tl_gset:NV \pnhdchaplast \g_@@_header_prev_last_chap_tl } \prop_get:NnNTF \g_@@_header_sect_first_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdsectfirst \l_@@_tmpa_tl } { \tl_gset:NV \pnhdsectfirst \g_@@_header_prev_last_sect_tl } \prop_get:NnNTF \g_@@_header_sect_last_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdsectlast \l_@@_tmpa_tl \tl_gset:NV \g_@@_header_prev_last_sect_tl \l_@@_tmpa_tl } { \tl_gset:NV \pnhdsectlast \g_@@_header_prev_last_sect_tl } \prop_get:NnNTF \g_@@_header_name_first_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdnamefirst \l_@@_tmpa_tl } { \tl_gset:NV \pnhdnamefirst \g_@@_header_prev_last_name_tl } \prop_get:NnNTF \g_@@_header_name_last_prop {#1} \l_@@_tmpa_tl { \tl_gset:NV \pnhdnamelast \l_@@_tmpa_tl \tl_gset:NV \g_@@_header_prev_last_name_tl \l_@@_tmpa_tl } { \tl_gset:NV \pnhdnamelast \g_@@_header_prev_last_name_tl } \group_end: } \cs_generate_variant:Nn \@@_set_headers_vars:n { e } % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \@@_set_headers_vars_next: , % \@@_set_headers_vars_first: , % } % The functions that actually call \cs{@@_set_headers_vars:n} at the % appropriate contexts with appropriate page values. Though we set % \cs{@@_set_headers_vars_next:} to run at every \texttt{shipout/before} % hook of the document, it is made no-op by \cs{g_@@_header_vars_next_bool} % which only has a \texttt{true} value inside \cs{printpostnotes}. % \cs{@@_set_headers_vars_first:} must set a label and retrieve its value to % be able to have a reliable value of its own page. % \begin{macrocode} \AddToHook { shipout/before } [ ./header ] { \@@_set_headers_vars_next: } \bool_new:N \g_@@_header_vars_next_bool \cs_new_protected:Npn \@@_set_headers_vars_next: { \bool_if:NT \g_@@_header_vars_next_bool { \@@_set_headers_vars:e { \int_eval:n { \c@page + 1 } } } } \cs_new_protected:Npn \@@_set_headers_vars_first: { \@@_set_print_page_label:e { \int_use:N \g_@@_print_postnotes_int } \@@_set_headers_vars:e { \@@_extract_pageref:e { print@ \int_use:N \g_@@_print_postnotes_int } } } % \end{macrocode} % \end{macro} % % % \begin{macro}[int]{\pnheaderdefault} % A basic header function to be used as default in the \opt{heading} option. % It produces a header in the form ``Notes to pages N--M'', with a text % which can be localized (see Section~\ref{sec:languages}). % \begin{syntax} % \cs{pnheaderdefault} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \pnheaderdefault {} { \tl_if_eq:NNTF \pnhdpagefirst \pnhdpagelast { \pnhdnotes{} ~ \pnhdtopage{} ~ \pnhdpagefirst } { \pnhdnotes{} ~ \pnhdtopages{} ~ \pnhdpagefirst -- \pnhdpagelast } } % \end{macrocode} % \end{macro} % % % \section{Compatibility} % % A dedicated temp variable for restoring data. % % \begin{macrocode} \tl_new:N \l_@@_restore_tmp_tl % \end{macrocode} % % \subsection*{\cs{caption}} % % For \cs{caption}'s possible two passes. This catches more than just % captions, of course, but is not overkill. A hook to \cs{@makecaption} would % be better, but \pkg{ltcmdhooks} does not allow it, and using lower level % methods for this is a bad idea. % % From the user's perspective, one-line captions will just work. For two-line % captions, there are two alternatives: i) % \texttt{\textbackslash{}stepcounter\{postnote\}} before the caption, then % call \cs{postnote} with \texttt{mark=\textbackslash{}arabic\{postnote\}}; or % ii) right before the caption, call % \texttt{\textbackslash{}postnote[nomark]\{\textbackslash{}label\{mynote\}...\}}, % then use \texttt{\textbackslash{}postnoteref\{mynote\}} inside the caption. % % \begin{macrocode} \AddToHook { postnotes/note/begin } [ ./compat/caption ] { \cs_if_exist:NT \@captype { \postnotesetup { maybemulti } } } % \end{macrocode} % % % \subsection*{\pkg{biblatex}} % % Thanks \contributor{Moritz Wemheuer}: % \url{https://tex.stackexchange.com/q/597359#comment1594585_597389}. % % % \begin{macrocode} \AddToHook { package/biblatex/after } [ ./compat/biblatex ] { % \end{macrocode} % Let \pkg{biblatex} know we are in a ``notes'' context. See % \url{https://tex.stackexchange.com/a/304464}, including comments. % \begin{macrocode} \AddToHook { postnotes/print/begin } [ postnotes/compat/biblatex ] { \toggletrue { blx@footnote } } % \end{macrocode} % Make \pkg{biblatex}'s \cs{mkbibendnote} use \cs{postnote}. This is very % likely desired in most cases, but may occasionally not be, so we add it to % an individually labeled hook, which can be disabled with % \texttt{\textbackslash{}RemoveFromHook\{begindocument/before\}[postnotes/mkbibendnote]} % in the preamble. % \begin{macrocode} \AddToHook { begindocument/before } [ postnotes/compat/biblatex ] { \cs_set:Npn \blx@theendnote { \postnote } \cs_set:Npn \blx@theendnotetext { \blx@err@endnote \footnotetext } } } % \end{macrocode} % % % \begin{macrocode} %<*gobble> % \end{macrocode} % % I had made an initial experimental attempt to support \pkg{biblatex}'s % \texttt{refsegment}s, \texttt{refcontext}s and \texttt{refsection}s. % However, this attempt was rash. Even if I could get many example files to % work for \texttt{refsegment}s and \texttt{refcontext}s, I could not do so % for \texttt{refsection}s. More importantly, with this partial % implementation, I could also generate documents which confused % \pkg{biblatex} more than it helped. Things I couldn't understand well, or % fix. All in all, I don't think this partial implementation is tenable, and % I could not take it further. Hence, \pkg{postnotes} support for this % feature set of \pkg{biblatex} will depend, as it should, on proper upstream % support for ``saving'' and ``restoring'' citation ``context'' information. % % I have made a feature request at \pkg{biblatex} for this % (\url{https://github.com/plk/biblatex/issues/1226}), which was % (understandably) classified as ``long term, no promises''. % % % The attempt was the following (currently ``gobbled'' from the package): % % \begin{macrocode} \AddToHook { package/biblatex/after } [ ./compat/biblatex ] { % \end{macrocode} % Store \pkg{biblatex} variables for each note. % \begin{macrocode} \AddToHook { postnotes/note/store } [ postnotes/compat/biblatex ] { \prop_gput:cne { \@@_data_name:e { \l_postnotes_note_id_tl } } { biblatex@refsection } { \int_use:N \c@refsection } \prop_gput:cne { \@@_data_name:e { \l_postnotes_note_id_tl } } { biblatex@refsegment } { \int_use:N \c@refsegment } \prop_gput:cne { \@@_data_name:e { \l_postnotes_note_id_tl } } { biblatex@refcontextbool } { \iftoggle { blx@refcontext } { true } { false } } \prop_gput:cnV { \@@_data_name:e { \l_postnotes_note_id_tl } } { biblatex@refcontext } \blx@refcontext@context } % \end{macrocode} % \pkg{biblatex} setup, once for \cs{printpostnotes} call. % \begin{macrocode} \AddToHook { postnotes/print/begin } [ postnotes/compat/biblatex ] { \@@_biblatex_endrefcontext_local: \@@_biblatex_citereset_local: } % \end{macrocode} % Restore \pkg{biblatex} variables for each note. % \begin{macrocode} \AddToHook { postnotes/print/note/begin } [ postnotes/compat/biblatex ] { \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { biblatex@refsection } \l_@@_restore_tmp_tl \int_set:Nn \c@refsection { \l_@@_restore_tmp_tl } \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { biblatex@refsegment } \l_@@_restore_tmp_tl \int_set:Nn \c@refsegment { \l_@@_restore_tmp_tl } \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { biblatex@refcontextbool } \l_@@_restore_tmp_tl \use:c { toggle \l_@@_restore_tmp_tl } { blx@refcontext } \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { biblatex@refcontext } \l_@@_restore_tmp_tl \blx@edef@refcontext { \l_@@_restore_tmp_tl } } % \end{macrocode} % Auxiliary functions. % % \begin{macro}{\@@_biblatex_endrefcontext_local:} % Replicate the job of \cs{endrefcontext}, but with local effects, % restrained to the group of \cs{printpostnotes}. % \begin{macrocode} \cs_new_protected:Npn \@@_biblatex_endrefcontext_local: { \togglefalse { blx@refcontext } \tl_clear:N \blx@refcontext@labelprefix \tl_clear:N \blx@refcontext@labelprefix@real \tl_set:Ne \blx@refcontext@sortingtemplatename { \blx@sorting } \tl_set:Nn \blx@refcontext@sortingnamekeytemplatename { global } \tl_set:Nn \blx@refcontext@uniquenametemplatename { global } \tl_set:Nn \blx@refcontext@labelalphanametemplatename { global } \blx@edef@refcontext { \blx@refcontext@sortingtemplatename / \blx@refcontext@sortingnamekeytemplatename / / \blx@refcontext@uniquenametemplatename / \blx@refcontext@labelalphanametemplatename } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_biblatex_citereset_local:} % Replicate the job of \cs{citereset}, but with local effects, restrained to % the group of \cs{printpostnotes}. % \begin{macrocode} \cs_new_protected:Npn \@@_biblatex_citereset_local: { % \end{macrocode} % \noindent % {\em\scriptsize\verb|\global\cslet{blx@bsee@\the\c@refsection}\@empty|} \\ % {\em\scriptsize\verb|\global\cslet{blx@fsee@\the\c@refsection}\@empty|} % \begin{macrocode} \tl_clear:c { blx@bsee@ \int_use:N \c@refsection } \tl_clear:c { blx@fsee@ \int_use:N \c@refsection } % \end{macrocode} % {\em\scriptsize\verb|\blx@ibidreset@force|} % \begin{macrocode} \undef \blx@lastkey@text \undef \blx@lastkey@foot % \end{macrocode} % {\em\scriptsize\verb|\blx@idemreset@force|} % \begin{macrocode} \undef \blx@lasthash@text \undef \blx@lasthash@foot % \end{macrocode} % {\em\scriptsize\verb|\blx@opcitreset@force|} % \begin{macrocode} \clist_map_inline:Nn \blx@trackhash@text { \csundef { blx@lastkey@text@ ##1 } } \tl_clear:N \blx@trackhash@text \clist_map_inline:Nn \blx@trackhash@foot { \csundef { blx@lastkey@foot@ ##1 } } \tl_clear:N \blx@trackhash@foot % \end{macrocode} % {\em\scriptsize\verb|\blx@loccitreset@force|} % \begin{macrocode} \clist_map_inline:Nn \blx@trackkeys@text { \csundef { blx@lastnote@text@ ##1 } } \tl_clear:N \blx@trackkeys@text \clist_map_inline:Nn \blx@trackkeys@foot { \csundef { blx@lastnote@foot@ ##1 } } \tl_clear:N \blx@trackkeys@foot % \end{macrocode} % {\em\scriptsize{}and all of them do:} % \begin{macrocode} \cs_set_eq:NN \blx@lastmpfn \z@ } % \end{macrocode} % \end{macro} % % \begin{macrocode} } % \end{macrocode} % % \pkg{biblatex}'s \texttt{refsections}, contrary to \texttt{refsegment}s and % \texttt{refcontext}s which are handled in the \LaTeX{} side of things (as % far as I can tell), need to go through \texttt{biber}, and must have correct % corresponding citation data written to the \file{.bcf} file. And the way % \cs{refsection} is implemented presumes each section is only ever begun once % (fair\dots{}), thus making it difficult to ``reopen'' it, or append new % citations to it later on, when the notes are printed. The start of a % \texttt{refsection} must be registered on the \file{.bcf} file, and this is % done by \cs{refsection} (and its auxiliary functions). However, a number of % its characteristics make things particularly difficult for the purpose at % hand: i) it unconditionally sets a label for the section which, of course, % cannot be done twice; and, critically, ii) the optional argument of the % environment (which receives the \meta{resources}) is used to set a local % assignment to \cs{blx@bibfiles}, based on which the relevant information is % written to the \file{.bcf} file, and when the group closes the information % is gone. My best attempt is below but it is not good. It feels a wrong % approach to ``go around'' the intended use of \cs{refsection} so much, and % it can't handle at all its optional argument, for the reasons above. It's % also incomplete, since it does not handle restoring % \cs{l_@@_biblatex_orig_refsection_tl}. % % \begin{macrocode} \AddToHook { package/biblatex/after } [ ./compat/biblatex ] { \tl_new:N \l_@@_biblatex_orig_refsection_tl \tl_new:N \g_@@_biblatex_prev_refsection_tl \AddToHook { postnotes/print/begin } [ postnotes/compat/biblatex ] { \tl_set:Ne \l_@@_biblatex_orig_refsection_tl { \int_use:N \c@refsection } \tl_gset:Ne \g_@@_biblatex_prev_refsection_tl { \l_@@_biblatex_orig_refsection_tl } } \AddToHook { postnotes/print/note/begin } [ postnotes/compat/biblatex ] { \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { biblatex@refsection } \l_@@_restore_tmp_tl \tl_if_eq:NNF \l_@@_restore_tmp_tl \g_@@_biblatex_prev_refsection_tl { \int_set:Nn \c@blx@maxsection { \l_@@_restore_tmp_tl - 1 } \tl_gset_eq:NN \g_@@_biblatex_prev_refsection_tl \l_@@_restore_tmp_tl \group_begin: \cs_set_eq:NN \label \use_none:n \cs_set_eq:NN \blx@info \use_none:n \blx@endrefsection \refsection \group_end: } } } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % % \subsection*{\pkg{zref-user}} % % \begin{macro}{\l_@@_note_zlabel_str} % Even though the \opt{zlabel} option is provided only when \pkg{zref-user} % is loaded, \cs{l_@@_note_zlabel_str} must be unconditionally defined, % since it is presumed to exist by \cs{@@_set_user_labels:} and elsewhere. % \begin{macrocode} \str_new:N \l_@@_note_zlabel_str % \end{macrocode} % \end{macro} % % \begin{macrocode} \AddToHook { package/zref-user/after } [ ./compat/zref-user ] { % \end{macrocode} % Provide \opt{zlabel} option. % \begin{macrocode} \keys_define:nn { postnotes/note } { zlabel .str_set:N = \l_@@_note_zlabel_str , zlabel .value_required:n = true , } % \end{macrocode} % Provide property to store the mark for measuring passes. % \begin{macrocode} \zref@newprop { postnote@mark } [] { \l_@@_mark_tl } \AddToHook { postnotes/note/begin } [ postnotes/compat/zref-user ] { \zref@localaddprop { main } { postnote@mark } } % \end{macrocode} % % \begin{macro}[int]{\postnotezref} % Provide \cs{postnotezref}. % \begin{syntax} % \cs{postnotezref}\meta{*}\marg{label} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \postnotezref { s m } { \@@_note_zref:nn {#1} {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_note_zref:nn} % The internal version of \cs{postnotezref}. % \begin{syntax} % \cs{@@_note_zref:nn} \Arg{star bool} \Arg{label} % \end{syntax} % \begin{macrocode} \tl_new:N \l_@@_note_zref_zlabel_tl \cs_new_protected:Npn \@@_note_zref:nn #1#2 { \group_begin: \tl_set:Nn \l_@@_note_zref_zlabel_tl {#2} \@@_typeset_mark_wrapper:nnn { \bool_lazy_all:nTF { { ! #1 } { \l_@@_hyperlink_bool } { \l_@@_zrefhyperref_bool } } { \hyperlink { \zref@extractdefault {#2} { anchor } { } } { \@@_make_mark:nnn { \zref{#2} } { } { } } } { \@@_make_mark:nnn { \zref{#2} } { } { } } } { \tag_socket_use:n { postnotes/postnotezref/begin } } { \tag_socket_use:n { postnotes/postnotezref/end } } \group_end: } % \end{macrocode} % \end{macro} % % \begin{macrocode} } % \end{macrocode} % % \begin{macrocode} \bool_new:N \l_@@_zrefhyperref_bool \AddToHook { package/zref-hyperref/after } [ ./compat/zref-hyperref ] { \bool_set_true:N \l_@@_zrefhyperref_bool } % \end{macrocode} % % % \subsection*{\pkg{zref-clever}} % % \begin{macrocode} \AddToHook { package/zref-clever/after } [ ./compat/zref-clever ] { \zcsetup { countertype = { postnote = endnote } , countertype = { postnotetext = endnote } , } \AddToHook { postnotes/print/begin } [ postnotes/compat/zref-clever ] { \zcsetup { counterresetby = { postnotetext = postnotesection } } } } % \end{macrocode} % % % \subsection*{\pkg{zref-check}} % % \begin{macrocode} \AddToHook { package/zref-check/after } [ ./compat/zref-check ] { \IfPackageAtLeastTF { zref-check } { 2022-07-05 } { \AddToHook { postnotes/note/store } [ postnotes/compat/zref-check ] { \prop_gput:cne { \@@_data_name:e { \l_postnotes_note_id_tl } } { zref-check@abschap } { \int_use:N \c@zc@abschap } \prop_gput:cne { \@@_data_name:e { \l_postnotes_note_id_tl } } { zref-check@abssec } { \int_use:N \c@zc@abssec } } \AddToHook { postnotes/print/note/begin } [ postnotes/compat/zref-check ] { \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { zref-check@abschap } \l_@@_restore_tmp_tl \int_set:Nn \c@zc@abschap { \l_@@_restore_tmp_tl } \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { zref-check@abssec } \l_@@_restore_tmp_tl \int_set:Nn \c@zc@abssec { \l_@@_restore_tmp_tl } } } { } } % \end{macrocode} % % % \subsection*{\pkg{amsmath}} % % \begin{macrocode} \AddToHook { package/amsmath/after } [ ./compat/amsmath ] { % \end{macrocode} % Testing for \cs{ifmeasuring@} is sufficient to get things right for the % measuring passes in math environments. % \begin{macrocode} \AddToHook { postnotes/note/inhibit } [ postnotes/compat/amsmath ] { \legacy_if:nT { measuring@ } { \bool_set_true:N \l_@@_inhibit_note_bool \bool_set_true:N \l_@@_print_plain_mark_bool \bool_set_true:N \l_@@_print_plain_mark_stepcounter_bool } } % \end{macrocode} % However, the \cs{text} macro, defined by \pkg{amstext} (required by % \pkg{amsmath}), poses problems if its own. Despite my best efforts, I could % not salvage things from the use of \cs{mathchoice} and the redefinitions of % \cs{setcounter} and \cs{addtocounter} performed by \pkg{amstext}. Setting % \opt{maybemulti} when \texttt{firstchoice@} is \texttt{false} grants us a % working situation for display style. But the use of \cs{postnote} inside % \cs{text} (and, if \pkg{amsmath} is loaded, \cs{textnormal}, \cs{textup}, % etc.) in inline math environments is not supported. If a note really needs % to be there, one can use the \opt{nomark} option and \cs{postnoteref}. % Things should work in text mode and in display style. For some related % discussion with regard to footnotes, see % \url{https://tex.stackexchange.com/a/82820} and, in particular, Barbara % Beeton's comment: ``This is certainly bravura code. I do hope it doesn't % result in a request to add \cs{footnote} capabilities to \pkg{amsmath}'s % multi-line display facilities. (The answer will almost certainly be "no". % We agree with Kopka \& Daly.)'' % \begin{macrocode} \AddToHook { postnotes/note/begin } [ postnotes/compat/amsmath ] { \legacy_if:nF { firstchoice@ } { \postnotesetup { maybemulti } } } } % \end{macrocode} % % % \subsection*{\pkg{csquotes}} % % \begin{macrocode} \AddToHook { package/csquotes/after } [ ./compat/csquotes ] { \bool_new:N \l_@@_csquotes_measuring_bool \BlockquoteDisable { \bool_set_true:N \l_@@_csquotes_measuring_bool } \AddToHook { postnotes/note/inhibit } [ postnotes/compat/csquotes ] { \bool_if:NT \l_@@_csquotes_measuring_bool { \bool_set_true:N \l_@@_inhibit_note_bool \bool_set_true:N \l_@@_print_plain_mark_bool \bool_set_true:N \l_@@_print_plain_mark_stepcounter_bool } } } % \end{macrocode} % % % \subsection*{\pkg{tabularx}} % % For the identification of the trial passes in \pkg{tabularx}, see % \url{https://tex.stackexchange.com/a/640035} (including discussion in the % comments, thanks \contributor{David Carlisle}), and also % \url{https://tex.stackexchange.com/a/227155} and % \url{https://tex.stackexchange.com/a/352134}. % % \begin{macrocode} \AddToHook { package/tabularx/after } [ ./compat/tabularx ] { \bool_new:N \l_@@_tabularx_inside_env_bool \AddToHook { env/tabularx/begin } [ postnotes/compat/tabularx ] { \bool_set_true:N \l_@@_tabularx_inside_env_bool \cs_set_eq:NN \@@_tabularx_saved_write:Nn \write } \AddToHook { postnotes/note/inhibit } [ postnotes/compat/tabularx ] { \bool_lazy_and:nnT { \l_@@_tabularx_inside_env_bool } { ! \cs_if_eq_p:NN \write \@@_tabularx_saved_write:Nn } { \bool_set_true:N \l_@@_inhibit_note_bool \bool_set_true:N \l_@@_print_plain_mark_bool \bool_set_true:N \l_@@_print_plain_mark_stepcounter_bool } } \AddToHook { package/xltabular/after } [ postnotes/compat/xltabular ] { \AddToHook { env/xltabular/begin } [ postnotes/compat/xltabular ] { \bool_set_true:N \l_@@_tabularx_inside_env_bool \cs_set_eq:NN \@@_tabularx_saved_write:Nn \write } } } % \end{macrocode} % % % \subsection*{\pkg{tabularray}} % % \begin{macrocode} \AddToHook { package/tabularray/after } [ ./compat/tabularray ] { % \end{macrocode} % Since version \texttt{2023A}, from 2023-03-01, \pkg{tabularray} offers the % \cs{lTblrMeasuringBool} which is true when measuring and false otherwise. % See \url{https://tex.stackexchange.com/q/675818} % and \url{https://github.com/lvjr/tabularray/issues/179} (thanks % \contributor{Ulrike Fischer}). % \begin{macrocode} \bool_if_exist:NT \lTblrMeasuringBool { % \end{macrocode} % I'd be inclined to restrict the inhibition effect to known \pkg{tabularray} % environments to ``keep things under control''. However this is a dedicated % and public boolean, and users can create arbitrary new \pkg{tabularray} % environments with \cs{NewTblrEnviron}, which we either wouldn't catch or % have to provide an user interface for. So, for the time being, let's trust % this boolean won't be misused by third-parties or users. Note that setting % \cs{l_@@_print_plain_mark_stepcounter_bool} to true presumes % \pkg{tabularray}'s \texttt{counter} module is enabled. But, since this is % the only way to get the measuring right in this context if there is more % than one \cs{postnote} inside a given table, \pkg{postnotes} expects and % requires the \texttt{counter} module. % \begin{macrocode} \AddToHook { postnotes/note/inhibit } [ postnotes/compat/tabularray ] { \bool_if:NT \lTblrMeasuringBool { \bool_set_true:N \l_@@_inhibit_note_bool \bool_set_true:N \l_@@_print_plain_mark_bool \bool_set_true:N \l_@@_print_plain_mark_stepcounter_bool } } } } % \end{macrocode} % % % \subsection*{PDF Tagging (experimental)} % % Note: All of this mostly presumes % \texttt{\cs{DocumentMetadata}\{testphase=phase-III\}} and was tested with % it. For \opt{listenv=none}, I'd expect things to work with % \texttt{phase-II}, but this is only lightly tested. % % A first thing to consider in tagging endnotes is how we want to represent % them in the PDF structure. My first thought, for lack of another, was: % emulate footnotes. There's no relevant semantic difference at the structure % level between the two, and the tagging support for footnotes was done by the % pros. And one distinctive characteristic the the footnotes tagging is that % the footnote itself is placed in the structure as a child to the (parent) % \texttt{text} element which surrounds the footnote mark. However, for % endnotes this introduces a number of problems and complicates things. While % footnotes ``float around'' and have no real structure of their own, that is % not true for endnotes. Endnotes comprise a proper document section, and may % be printed in a list environment, etc. So when the tagging of footnotes % places the footnote structure element as a child element of the mark's % surrounding text, this is arguably for a lack of other options. Where else, % after all? Indeed, a typical \texttt{html} would render footnotes at the % end of the page, and not inline. True the normal \texttt{html} page is much % smaller than our typical PDF, but the point stands. We can have a hover % over call out for footnotes, but the same could be done for end notes, % regardless of the parent-child relation (as long as the required % cross-references are in place). On the other hand, for endnotes this is not % an issue: they have a natural place to be plugged into. Furthermore, making % an endnote a child of the text surrounding its mark leaves an empty % ``skeleton'' of the endnotes section: the heading, the list structure, etc. % Technically, we could clean that too, but clearly that's not the way to % go\dots{}. % % Finally, the parent-child relation is not required by \texttt{PDF} standards % for the relevant structure types. The \texttt{PDF} 2.0 standard % (ISO~32000-2:2020), says the following about \texttt{FENote}: % % \begin{quote} % Used to markup footnotes and endnotes. Footnotes and endnotes are content % that is not normally read as part of the enclosing content from which it % is referenced, but rather consulted at the reading person's discretion. % In order for text to be considered a footnote or endnote, there should be % a reference from the enclosing content to the footnote or endnote. Such % reference may be achieved by means of a Link structure element through a % structure destination in its link annotation (see ``Table 368 — General % inline level structure types''), or use of Ref in structure elements (see % ``Table 355 — Entries in a structure element dictionary''). % \end{quote} % % The \texttt{PDF} 1.7 standard (PDF~32000-1:2008), says the following about % \texttt{Note}: % % \begin{quote} % An item of explanatory text, such as a footnote or an endnote, that is % referred to from within the body of the document. It may have a label % (structure type Lbl; see ``List Elements'' in 14.8.4.3, ``Block-Level % Structure Elements'') as a child. The note may be included as a child of % the structure element in the body text that refers to it, or it may be % included elsewhere (such as in an endnotes section) and accessed by means % of a reference (structure type Reference). % % Tagged PDF does not prescribe the placement of footnotes in the page % content order. They may be either inline or at the end of the page, at % the discretion of the conforming writer. % \end{quote} % % So, the note \emph{may} be included as a child of the surrounding text, but % that's not required (PDF 2.0 does not even mention that). What is required % is the reference between the elements. All in all, let's not follow % footnotes in establishing the parent-child relation. % % % Another smaller but related issue is how to treat the list structure in % \cs{printpostnotes} when \opt{listenv} is used. The natural thing here % would be to use an \env{enumerate} type list (or, in PDF lingo, for the % \texttt{ListNumbering} attribute to be \texttt{Ordered}), where the mark is % used as the item's label (even if, technically, we use the list like a % \env{description} in that we feed the label of every \cs{item} and though % there is an implicit underlying counter, the list itself has no bearing upon % it). The problem here is that the PDF 2.0 standards determine that the % \texttt{FENote} structure element cannot be a child of a \texttt{LI} (list % item). However, at least in principle, we also would like to have the % \texttt{endnotelabel} element to be a child of the \texttt{endnote} element. % Thus, we have a conflict, the mc-chunk can only be used once, and can be % either the \texttt{Lbl} of the \texttt{LI}, or the \texttt{endnotelabel} for % the \texttt{endnote}. Currently, for the list case, I'm using an % \texttt{EndnotesList} class, which we define to have \texttt{ListNumbering} % as \texttt{Ordered}, with the mark as \texttt{Lbl} for \texttt{LI}, and % letting \texttt{endnote} be a child of \texttt{LBody}. In a way, the % possibility of exporting the tagged content to different formats makes me % think that this is the most appropriate for the this case. For the case of % \texttt{listenv=none}, the \texttt{endnote}s were made child of the % \texttt{Sect} in which they occur. The \texttt{endnotelabel} was then % included as part (child) of the \texttt{endnote}. In this, this treatment % emulates the one given for footnotes in the kernel. But, at the same time, % it is less than ideal for machine readability purposes, since whether the % \texttt{endnotelabel} is part of \texttt{endnote} or not depends on if there % is a list environment involved. Alas, I see no easy way around the PDF % standard restriction for the list case. % % % On the \LaTeX{} side of things, adding support for tagging entails two basic % tasks: i) applying the tagging markup at the appropriate places; ii) % generating references between the ``mark(s)'' and the ``text'' (plus % cross-reference commands to their respective targets). % % Regarding tagging references, we have three different cases: i) a regular % \cs{postnote}; ii) a \cs{postnoteref} to a note labeled from inside the % note; and iii) a \cs{postnoteref} to a note labeled with the \opt{label} % option. % % For regular \cs{postnote}s it is trivial to establish the reference using % the \texttt{label} / \texttt{ref} options of \pkg{tagpdf}. % % For \cs{postnoteref}s, however labeled, the connection \emph{must} be % established at \cs{postnoteref}, for the simple fact that any \cs{postnote} % can be referenced by arbitrarily many \cs{postnoteref}s. So we need to be % able to retrieve the note ID from the ``text'' to which the reference refers % to at \cs{postnoteref}. However, at \cs{postnoteref} the only information we % have is the \meta{label} but, since it is unique, we can establish a % connection through it. % % When the label is set from inside the note, it is actually set at % \cs{printpostnotes}, at which place we have access to % \cs{l_postnotes_print_note_id_tl} so we can use the \meta{label} to pass % that information around to \cs{postnoteref}. With a standard \cs{label} we % must set an additional \pkg{ltproperties} label, using the new % \texttt{label} hook, which \cs{postnoteref} can retrieve from its own % \meta{label} argument. For \cs{zlabel} this particular task is trivial, % since we can simply add a property to store the note ID with the label, % which \cs{postnotezref} can extract. % % When the label is set from the option, things are slightly more complicated, % because the label is set at \cs{postnote}, at which place we do not have % access to the note ID of the ``text''. What we do here is then store the % label with the note and restore it later at \cs{printpostnotes} as usual. % Then, at that point, we can set an auxiliary label which can be retrieved % from \cs{postnoteref} from its own \meta{label} argument, as we did for the % case of labels inside the note. Auxiliary labels are handled with % \pkg{ltproperties} labels. % % % Unconditionally define tagging support sockets. % \begin{macrocode} \socket_new:nn { tagsupport/postnotes/mark/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/mark/end }{ 0 } \socket_new:nn { tagsupport/postnotes/nomark/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/nomark/end }{ 0 } \socket_new:nn { tagsupport/postnotes/multisep/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/multisep/end }{ 0 } \socket_new:nn { tagsupport/postnotes/printlist/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/printlist/end }{ 0 } \socket_new:nn { tagsupport/postnotes/printnote/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/printnote/end }{ 0 } \socket_new:nn { tagsupport/postnotes/printmark/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/printmark/end }{ 0 } \socket_new:nn { tagsupport/postnotes/printtext/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/printtext/end }{ 0 } \socket_new:nn { tagsupport/postnotes/postnoteref/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/postnoteref/end }{ 0 } \socket_new:nn { tagsupport/postnotes/postnotezref/begin }{ 0 } \socket_new:nn { tagsupport/postnotes/postnotezref/end }{ 0 } % \end{macrocode} % % \begin{macrocode} \bool_lazy_and:nnT { \cs_if_exist_p:N \tag_if_active_p: } { \tag_if_active_p: } { % \end{macrocode} % FIXME Review or remove these settings if/when they are included upstream % (see \url{https://github.com/latex3/tagging-project/issues/728}). % \begin{macrocode} \tagpdfsetup { role/new-tag = { tag=endnote, role=FENote } , role/new-tag = { tag=endnotemark, role=Lbl } , role/new-tag = { tag=endnotelabel, role=Lbl } , role/new-attribute = { EndnoteType } { /O /FENote /NoteType /Endnote } , role/new-attribute = { EndnotesList } { /O /List /ListNumbering /Ordered } , } % \end{macrocode} % \cs{postnote} % \begin{macrocode} \socket_new_plug:nnn { tagsupport/postnotes/mark/begin } { default } { \tag_mc_end_push: \tag_struct_begin:n { tag = endnotemark , label = { postnotemark. \l_postnotes_note_id_tl } , ref = { postnote. \l_postnotes_note_id_tl } , } \@@_tagsup_store_sctructnum:nN { postnotemark } \l_postnotes_note_id_tl \tag_mc_begin:n { } } \socket_new_plug:nnn { tagsupport/postnotes/mark/end } { default } { \tag_mc_end: \tag_struct_end: % endnotemark \tag_mc_begin_pop:n { } } \socket_new_plug:nnn { tagsupport/postnotes/nomark/begin } { default } { \tag_struct_begin:n { tag = NonStruct , label = { postnotemark. \l_postnotes_note_id_tl } , ref = { postnote. \l_postnotes_note_id_tl } , } \@@_tagsup_store_sctructnum:nN { postnotemark } \l_postnotes_note_id_tl } \socket_new_plug:nnn { tagsupport/postnotes/nomark/end } { default } { \tag_struct_end: } % NonStruct \socket_assign_plug:nn { tagsupport/postnotes/mark/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/mark/end } { default } \socket_assign_plug:nn { tagsupport/postnotes/nomark/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/nomark/end } { default } % \end{macrocode} % \opt{multiple} % \begin{macrocode} \socket_new_plug:nnn { tagsupport/postnotes/multisep/begin } { default } { \tag_mc_end_push: \tag_mc_begin:n { artifact } } \socket_new_plug:nnn { tagsupport/postnotes/multisep/end } { default } { \tag_mc_end: \tag_mc_begin_pop:n { } } \socket_assign_plug:nn { tagsupport/postnotes/multisep/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/multisep/end } { default } % \end{macrocode} % \cs{printpostnotes} % \begin{macrocode} \socket_new_plug:nnn { tagsupport/postnotes/printlist/begin } { default } { \tag_tool:n { para/tagging=false } } \socket_new_plug:nnn { tagsupport/postnotes/printlist/end } { default } { } \socket_assign_plug:nn { tagsupport/postnotes/printlist/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/printlist/end } { default } \socket_new_plug:nnn { tagsupport/postnotes/printnote/begin } { default } { \bool_if:NF \l_@@_print_as_list_bool { \tag_struct_begin:n { tag = endnote , attribute-class = EndnoteType , label = { postnote. \l_postnotes_print_note_id_tl } , % \end{macrocode} % CHECK Should we really add a back reference here? I couldn't find any hint % about this in the standards, but \pkg{latex-lab-footnotes} does it. No % harm, I guess. % \begin{macrocode} ref = { postnotemark. \l_postnotes_print_note_id_tl } , } \@@_tagsup_store_sctructnum:nN { postnote } \l_postnotes_print_note_id_tl } } \socket_new_plug:nnn { tagsupport/postnotes/printnote/end } { default } { \bool_if:NF \l_@@_print_as_list_bool { \tag_struct_end: } % endnote } \socket_assign_plug:nn { tagsupport/postnotes/printnote/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/printnote/end } { default } \socket_new_plug:nnn { tagsupport/postnotes/printmark/begin } { default } { \bool_if:NF \l_@@_print_as_list_bool { \tag_struct_begin:n { tag=endnotelabel } \tag_mc_begin:n { tag=Lbl } } } \socket_new_plug:nnn { tagsupport/postnotes/printmark/end } { default } { \bool_if:NF \l_@@_print_as_list_bool { \tag_mc_end: \tag_struct_end: % endnotelabel } } \socket_assign_plug:nn { tagsupport/postnotes/printmark/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/printmark/end } { default } \socket_new_plug:nnn { tagsupport/postnotes/printtext/begin } { default } { \bool_if:NTF \l_@@_print_as_list_bool { \tag_struct_begin:n { tag = endnote , attribute-class = EndnoteType , label = { postnote. \l_postnotes_print_note_id_tl } , % \end{macrocode} % CHECK Ditto. % \begin{macrocode} ref = { postnotemark. \l_postnotes_print_note_id_tl } , } \@@_tagsup_store_sctructnum:nN { postnote } \l_postnotes_print_note_id_tl \tag_struct_begin:n { tag=text-unit } \tag_struct_begin:n { tag=text } \tag_tool:n { para/tagging=true } \tag_mc_begin:n { } } { \tag_struct_begin:n { tag=text-unit } \tag_struct_begin:n { tag=text } \tag_tool:n { para/tagging=true } \tag_mc_begin:n { } } } \socket_new_plug:nnn { tagsupport/postnotes/printtext/end } { default } { \bool_if:NTF \l_@@_print_as_list_bool { \tag_mc_end: \tag_tool:n { para/tagging=false } \tag_struct_end: % text \tag_struct_end: % text-unit \tag_struct_end: % endnote } { \tag_mc_end: \tag_tool:n { para/tagging=false } \tag_struct_end: % text \tag_struct_end: % text-unit } } \socket_assign_plug:nn { tagsupport/postnotes/printtext/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/printtext/end } { default } % \end{macrocode} % Provide \pkg{xtemplate} based redefinitions of \env{postnoteslist} and % \pkg{postnoteslisthang}. This is needed because, as far as I can tell, it % is the only way to set \opt{tag} and \opt{attribute-class} for the list % struct without tampering with \pkg{latex-lab-testphase-block}'s internals. % \begin{macrocode} \IfInstanceExistsT { blockenv } { list } { \DeclareInstance { blockenv } { postnoteslist } { display } { env-name = postnoteslist , tag-name = L , tag-class = EndnotesList , tagging-recipe = list , inner-level-counter = , level-increase = true , setup-code = , block-instance = list , inner-instance = postnoteslist , } \DeclareInstanceCopy { blockenv } { postnoteslisthang } { postnoteslist } \EditInstance { blockenv } { postnoteslisthang } { env-name = postnoteslisthang } \DeclareInstance { list } { postnoteslist } { std } { item-instance = postnoteslist } \DeclareInstance { item } { postnoteslist } { std } { label-format = { \hspace { \labelsep } \normalfont ~ #1 } , label-align = left , } \RenewDocumentEnvironment { postnoteslist } { } { \UseInstance { blockenv } { postnoteslist } { leftmargin = 0pt , label-width = 0pt , item-indent = .5\parindent , rightmargin = 0pt , parindent = \parindent , par-skip = \parskip , item-skip = 0pt , beginsep = .5\topsep , begin-par-skip = .5\partopsep , } } { \endblockenv } \RenewDocumentEnvironment { postnoteslisthang } { } { \UseInstance { blockenv } { postnoteslisthang } { leftmargin = 1em , label-width = -\leftmargin , item-indent = -2\leftmargin , rightmargin = 0pt , parindent = \parindent , par-skip = \parskip , item-skip = 0pt , beginsep = .5\topsep , begin-par-skip = .5\partopsep , } } { \endblockenv } } % \end{macrocode} % Setup for \cs{label} and \cs{zlabel} inside the note. % \begin{macrocode} \bool_new:N \l_@@_inside_note_bool \AddToHookWithArguments { label } [ postnotes/tagsup ] { \bool_if:NT \l_@@_inside_note_bool { \property_record:nn { postnote@label@innote. #1 } { postnotes/tagsup@noteid } } } \AddToHook { postnotes/print/note/begin } [ postnotes/tagsup ] { \bool_set_true:N \l_@@_inside_note_bool } \property_new:nnnn { postnotes/tagsup@noteid } { now } { 0 } { \l_postnotes_print_note_id_tl } \AddToHook { package/zref-user/after } [ postnotes/tagsup ] { \zref@newprop { postnotes@tagsup@noteid } [ 0 ] { \l_postnotes_print_note_id_tl } \AddToHook { postnotes/print/note/begin } [ postnotes/tagsup ] { \zref@localaddprop { main } { postnotes@tagsup@noteid } } } % \end{macrocode} % Setup for \opt{label} and \opt{zlabel} options. % \begin{macrocode} \AddToHook { postnotes/note/store } [ postnotes/tagsup ] { \str_if_empty:NF \l_@@_note_label_str { \prop_gput:cnV { \@@_data_name:e { \l_postnotes_note_id_tl } } { label } \l_@@_note_label_str } } \AddToHook { package/zref-user/after } [ postnotes/tagsup ] { \AddToHook { postnotes/note/store } [ postnotes/tagsup ] { \str_if_empty:NF \l_@@_note_zlabel_str { \prop_gput:cnV { \@@_data_name:e { \l_postnotes_note_id_tl } } { zlabel } \l_@@_note_zlabel_str } } } \AddToHook { postnotes/print/note/begin } [ postnotes/tagsup ] { \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { label } \l_@@_restore_tmp_tl \tl_if_empty:NF \l_@@_restore_tmp_tl { \exp_args:Ne \property_record:nn { postnote@label@option. \l_@@_restore_tmp_tl } { postnotes/tagsup@noteid } } \@@_prop_get:nnN { \l_postnotes_print_note_id_tl } { zlabel } \l_@@_restore_tmp_tl \tl_if_empty:NF \l_@@_restore_tmp_tl { \exp_args:Ne \property_record:nn { postnote@zlabel@option. \l_@@_restore_tmp_tl } { postnotes/tagsup@noteid } } } % \end{macrocode} % CHECK \pkg{latex-lab-footnotes} creates the footnote structure element % (\texttt{FENote} tag) and adds to it a \texttt{/Ref} entry pointing to the % structures of \emph{all} marks related to the note, and that includes % \cs{footref}s. I don't see anything stating something of the sort in the % standards, the backref of the original mark is already a stretch. I also % fail to see why this is needed, and how it could be used. But\dots{} I % trust Ulrike knows better than me. % \begin{macro} % { % \@@_tagsup_store_sctructnum:nN % \@@_tagsup_store_crossref:nN % } % \begin{syntax} % \cs{@@_tagsup_store_sctructnum:nN} \Arg{ref type} \Arg{ID number of note} % \cs{@@_tagsup_store_crossref:nN} \Arg{ref type} \Arg{ID number of note} % \end{syntax} % \meta{ref type} is either ``\texttt{postnote}'' or % ``\texttt{postnotemark}''. % \begin{macrocode} \prop_new:N \g_@@_tagsup_structnums_prop \cs_new_protected:Npn \@@_tagsup_store_sctructnum:nN #1#2 { \prop_gput:Nee \g_@@_tagsup_structnums_prop { #1 . #2 } { \tag_get:n { struct_num } } } \prop_new:N \g_@@_tagsup_crossrefs_prop \cs_new_protected:Npn \@@_tagsup_store_crossref:nN #1#2 { \prop_gput:Nee \g_@@_tagsup_crossrefs_prop { \tag_get:n { struct_num } } { #1 . #2 } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_tagsup_gput_ref:nn} % \begin{syntax} % \cs{@@_tagsup_gput_ref:nn} \Arg{structnum refering from} % ~~~~\Arg{structnum being referenced to} % \end{syntax} % See \cs{__fnote_gput_ref:nn}. % \begin{macrocode} \cs_new_protected:Npn \@@_tagsup_gput_ref:nn #1#2 { \tag_if_active:T { \tag_struct_gput:ene {#1} {ref} { \tag_struct_object_ref:e {#2} } } } % \end{macrocode} % \end{macro} % The actual inclusion of the reference has to be done at the end, since the % \opt{ref} option called by \cs{tag_struct_begin:n} does not check if the % variable to store the refs already exists, resulting in a clash if we add it % immediately and a \cs{posnoteref} is made before \cs{printpostnotes}, and % also so that the ``main'' reference always comes first at the list. % \begin{macrocode} \AddToHook { tagpdf/finish/before } [ postnotes/tagsup ] { \prop_map_inline:Nn \g_@@_tagsup_crossrefs_prop { \@@_tagsup_gput_ref:nn { \prop_item:Nn \g_@@_tagsup_structnums_prop {#2} } {#1} } } % \end{macrocode} % \cs{postnoteref} % \begin{macrocode} \socket_new_plug:nnn { tagsupport/postnotes/postnoteref/begin } { default } { \tag_mc_end_push: \property_if_recorded:eeTF { postnote@label@innote. \l_@@_note_ref_label_tl } { postnotes/tagsup@noteid } { % \end{macrocode} % Label coming from a \cs{label} inside the note. % \begin{macrocode} \tl_set:Ne \l_@@_tmpa_tl { \property_ref:ee { postnote@label@innote. \l_@@_note_ref_label_tl } { postnotes/tagsup@noteid } } \tag_struct_begin:n { tag = endnotemark , ref = { postnote. \l_@@_tmpa_tl } , } \@@_tagsup_store_crossref:nN { postnote } \l_@@_tmpa_tl } { \property_if_recorded:eeTF { postnote@label@option. \l_@@_note_ref_label_tl } { postnotes/tagsup@noteid } { % \end{macrocode} % Label coming from a \opt{label} option. % \begin{macrocode} \tl_set:Ne \l_@@_tmpa_tl { \property_ref:ee { postnote@label@option. \l_@@_note_ref_label_tl } { postnotes/tagsup@noteid } } \tag_struct_begin:n { tag = endnotemark , ref = { postnotemark. \l_@@_tmpa_tl } , } \@@_tagsup_store_crossref:nN { postnotemark } \l_@@_tmpa_tl } { \tag_struct_begin:n { tag = endnotemark } } } \tag_mc_begin:n { } } \socket_new_plug:nnn { tagsupport/postnotes/postnoteref/end } { default } { \tag_mc_end: \tag_struct_end: % endnotemark \tag_mc_begin_pop:n { } } \socket_assign_plug:nn { tagsupport/postnotes/postnoteref/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/postnoteref/end } { default } % \end{macrocode} % \cs{postnotezref} % \begin{macrocode} \AddToHook { package/zref-user/after } [ postnotes/tagsup ] { \socket_new_plug:nnn { tagsupport/postnotes/postnotezref/begin } { default } { \tag_mc_end_push: \zref@ifrefcontainsprop { \l_@@_note_zref_zlabel_tl } { postnotes@tagsup@noteid } { % \end{macrocode} % Label coming from a \cs{zlabel} inside the note. % \begin{macrocode} \tl_set:Ne \l_@@_tmpa_tl { \zref@extract { \l_@@_note_zref_zlabel_tl } { postnotes@tagsup@noteid } } \tag_struct_begin:n { tag = endnotemark , ref = { postnote. \l_@@_tmpa_tl } , } \@@_tagsup_store_crossref:nN { postnote } \l_@@_tmpa_tl } { \property_if_recorded:eeTF { postnote@zlabel@option. \l_@@_note_zref_zlabel_tl } { postnotes/tagsup@noteid } { % \end{macrocode} % Label coming from a \opt{zlabel} option. % \begin{macrocode} \tl_set:Ne \l_@@_tmpa_tl { \property_ref:ee { postnote@zlabel@option. \l_@@_note_zref_zlabel_tl } { postnotes/tagsup@noteid } } \tag_struct_begin:n { tag = endnotemark , ref = { postnotemark. \l_@@_tmpa_tl } , } \@@_tagsup_store_crossref:nN { postnotemark } \l_@@_tmpa_tl } { \tag_struct_begin:n { tag = endnotemark } } } \tag_mc_begin:n { } } \socket_new_plug:nnn { tagsupport/postnotes/postnotezref/end } { default } { \tag_mc_end: \tag_struct_end: % endnotemark \tag_mc_begin_pop:n { } } \socket_assign_plug:nn { tagsupport/postnotes/postnotezref/begin } { default } \socket_assign_plug:nn { tagsupport/postnotes/postnotezref/end } { default } } } % \end{macrocode} % % % \section{Languages} % \label{sec:languages} % % \begin{macro}[int] % { % \pntitle , % \pnhdnotes , % \pnhdtopage , % \pnhdtopages , % } % Set of language specific user variables. They are used in the default % value of the \opt{heading} option and in \cs{pnheaderdefault} which, % ultimately, is also used in the same place. % \begin{macrocode} \tl_new:N \pntitle \tl_new:N \pnhdnotes \tl_new:N \pnhdtopage \tl_new:N \pnhdtopages \tl_set:Nn \pntitle { Notes } \tl_set:Nn \pnhdnotes { Notes } \tl_set:Nn \pnhdtopage { to~page } \tl_set:Nn \pnhdtopages { to~pages } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_define_language:nn} % Defines language specific values for \meta{postnote language} by storing a % set of assignments for the language specific variables in \meta{setup}. % \meta{postnote language} is an internal name, typically the ``main'' name % of the language, based on which we can set specific \pkg{babel} or % \pkg{polyglossia} languages or variants. % \begin{syntax} % \cs{@@_define_language:nn} \Arg{postnote language} \Arg{setup} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_define_language:nn #1#2 { \tl_new:c { g_@@_language_ #1 _tl } \tl_gset:cn { g_@@_language_ #1 _tl } {#2} } % \end{macrocode} % \end{macro} % % % For \pkg{babel} we use the new hook system, it's clean, and avoids the % \cs{addto} pitfalls. The appropriate hook to use is % \texttt{babel/\meta{language}/beforeextras} so that users can override it % with a traditional % \texttt{\textbackslash{}addto\textbackslash{}extras\meta{language}}. % % Note that, for \pkg{babel}, the captions are currently handled in two % different ways -- the ``old way'' and the ``new way'' -- and which of them % is used depends on the language. Most still use the ``old way'', but the % problem is that it is not universal. And the ``new way'' uses a different % naming scheme -- \texttt{\textbackslash{}\meta{language}\meta{caption}}, % which is meant to be set with \cs{setlocalecaption}, and not suitable for % our needs. The \texttt{\textbackslash{}extras\meta{language}} macros are % meant for ``arbitrary'' code to be run when the language is selected, which % is what we want. The captions used to work in the same way, but no longer % for languages which use the ``new way''. % % Note also that there seems to exist some qualms about \pkg{babel}'s % \cs{addto}. A number of packages define their own versions of it. Do so at % least \pkg{varioref} (probably the original), \pkg{backref}, and % \pkg{cleveref}. The latter comments that \cs{addto} is ``flawed''. % \pkg{babel} itself comments the definition recognizing that there is an % ``inconsistency'': depending on the case, the operation will be either local % or global. This is documented in the manual, which explains this % inconsistent behavior is preserved for backward compatibility, and % recommends \pkg{etoolbox}'s facilities if available. \pkg{polyglossia} also % recommends \pkg{etoolbox}'s \cs{gappto}. All in all, if there's need to use % the traditional way instead of the new hooks, just rely on \texttt{expl3} % and use \cs{tl_gput_right:Nn}. % % \begin{macro}{\@@_set_babel_language:nn} % Sets \meta{babel language} to execute the setup defined by % \cs{@@_define_language:nn} for \meta{postnote language} at the % \texttt{babel/\meta{language}/beforeextras} hook. % \begin{syntax} % \cs{@@_set_babel_language:nn} \Arg{babel language} \Arg{postnote language} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_set_babel_language:nn #1#2 { \ActivateGenericHook { babel/#1/beforeextras } \exp_args:Nnv \AddToHook { babel/#1/beforeextras } { g_@@_language_ #2 _tl } } % \end{macrocode} % \end{macro} % % % \pkg{polyglossia} uses a similar set of macros for setting up languages as % \pkg{babel} does. However, the % \texttt{\textbackslash{}blockextras@\meta{language}} macros are % unfortunately internal (despite what the manual says, that's what the code % does), thus requiring \cs{makeatletter}/\cs{makeatother} for user % configuration, which would be an inconvenience. On the other hand, % \pkg{polyglossia}'s \texttt{\textbackslash{}captions\meta{language}} works % as in \pkg{babel}'s ``old way'', meaning it is just a ``hook'' to which we % can append some code. So we use % \texttt{\textbackslash{}captions\meta{language}} for \pkg{polyglossia}. % Things may complicate here if there's need to set up different values for % different language variants, since the hooks available are all necessarily % internal, but I doubt we'll ever need variants for these simple strings. % % \begin{macro}{\@@_set_polyglossia_language:nn} % Sets \meta{polyglossia language} to execute the setup defined by % \cs{@@_define_language:nn} for \meta{postnote language} at the % \pkg{polyglossia} \texttt{\textbackslash{}captions\meta{language}} hook. % \begin{syntax} % \cs{@@_set_polyglossia_language:nn} \Arg{polyglossia language} % ~~\Arg{postnote language} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_set_polyglossia_language:nn #1#2 { \AddToHook { package/polyglossia/after } { \exp_args:Nnv \csgappto { captions #1 } { g_@@_language_ #2 _tl } } } % \end{macrocode} % \end{macro} % % % \subsection*{English} % % \begin{macrocode} \@@_define_language:nn { english } { \tl_set:Nn \pntitle { Notes } \tl_set:Nn \pnhdnotes { Notes } \tl_set:Nn \pnhdtopage { to~page } \tl_set:Nn \pnhdtopages { to~pages } } \@@_set_babel_language:nn { english } { english } \@@_set_babel_language:nn { british } { english } \@@_set_babel_language:nn { american } { english } \@@_set_babel_language:nn { canadian } { english } \@@_set_babel_language:nn { australian } { english } \@@_set_babel_language:nn { newzealand } { english } \@@_set_babel_language:nn { UKenglish } { english } \@@_set_babel_language:nn { USenglish } { english } \@@_set_polyglossia_language:nn { english } { english } % \end{macrocode} % % % \subsection*{Portuguese} % % \begin{macrocode} \@@_define_language:nn { portuguese } { \tl_set:Nn \pntitle { Notas } \tl_set:Nn \pnhdnotes { Notas } \tl_set:Nn \pnhdtopage { da~página } \tl_set:Nn \pnhdtopages { das~páginas } } \@@_set_babel_language:nn { portuguese } { portuguese } \@@_set_babel_language:nn { brazilian } { portuguese } \@@_set_babel_language:nn { portuges } { portuguese } \@@_set_babel_language:nn { brazil } { portuguese } \@@_set_polyglossia_language:nn { portuguese } { portuguese } % \end{macrocode} % % % \subsection*{French} % % French localization validated by \contributor{\username{Pika78}} at % \githubissue{1}. % % \pkg{babel-french} also has \file{.ldf}s for \texttt{francais}, % \texttt{frenchb}, and \texttt{canadien}, but they are deprecated as options % and, if used, they fall back to either \texttt{french} or \texttt{acadian}. % % \begin{macrocode} \@@_define_language:nn { french } { \tl_set:Nn \pntitle { Notes } \tl_set:Nn \pnhdnotes { Notes } \tl_set:Nn \pnhdtopage { de~la~page } \tl_set:Nn \pnhdtopages { des~pages } } \@@_set_babel_language:nn { french } { french } \@@_set_babel_language:nn { acadian } { french } \@@_set_polyglossia_language:nn { french } { french } % \end{macrocode} % % % \subsection*{German} % % German localization provided by \contributor{Herbert Voß} at % \githubissue{2}. % % \pkg{babel-german} also has \file{.ldf}s for \texttt{germanb} and % \texttt{ngermanb}, but they are deprecated as options and, if used, they % fall back respectively to \texttt{german} and \texttt{ngerman}. % % \begin{macrocode} \@@_define_language:nn { german } { \tl_set:Nn \pntitle { Endnoten } \tl_set:Nn \pnhdnotes { Endnoten } \tl_set:Nn \pnhdtopage { zu~Seite } \tl_set:Nn \pnhdtopages { zu~Seiten } } \@@_set_babel_language:nn { german } { german } \@@_set_babel_language:nn { ngerman } { german } \@@_set_babel_language:nn { austrian } { german } \@@_set_babel_language:nn { naustrian } { german } \@@_set_babel_language:nn { swissgerman } { german } \@@_set_babel_language:nn { nswissgerman } { german } \@@_set_polyglossia_language:nn { german } { german } % \end{macrocode} % % % \begin{macrocode} % % \end{macrocode} % % % \PrintIndex % % \endinput % % Local Variables: % jinx-local-words: "endnote endnotes" % End: