diff options
author | Karl Berry <karl@freefriends.org> | 2021-08-05 20:47:40 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-08-05 20:47:40 +0000 |
commit | eda08b76ad9d0ad91675a9e563c8c478df9af77a (patch) | |
tree | e86e02f996fe19e803fbb484b029dfc53cbb7912 /Master/texmf-dist/source/latex | |
parent | d728f6878800a22105df3da9d37de7d1cc0f0bcb (diff) |
zref-check (5aug21)
git-svn-id: svn://tug.org/texlive/trunk@60171 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex')
-rw-r--r-- | Master/texmf-dist/source/latex/zref-check/zref-check.dtx | 2218 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/zref-check/zref-check.ins | 98 |
2 files changed, 2316 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/zref-check/zref-check.dtx b/Master/texmf-dist/source/latex/zref-check/zref-check.dtx new file mode 100644 index 00000000000..3ef8af66d81 --- /dev/null +++ b/Master/texmf-dist/source/latex/zref-check/zref-check.dtx @@ -0,0 +1,2218 @@ +% \iffalse meta-comment +% +% File: zref-check.dtx +% +% This file is part of the LaTeX package "zref-check". +% +% Copyright (C) 2021 Gustavo Barros +% +% 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 +% Gustavo Barros. +% +% This work consists of the files zref-check.dtx, +% zref-check.ins, +% zref-check.tex, +% zref-check-code.tex, +% and the derived files zref-check.sty and +% zref-check.pdf, +% zref-check-code.pdf. +% +% The released version of this package is available from CTAN. +% +% ----------------------------------------------------------------------- +% +% The development version of the package can be found at +% +% https://github.com/gusbrs/zref-check +% +% for those people who are interested. +% +% ----------------------------------------------------------------------- +% +% \fi +% +% \iffalse +%<*driver> +\documentclass{l3doc} +% Have \GetFileInfo pick up date and version data +\usepackage{zref-check} +\NewDocumentCommand\opt{m}{\texttt{#1}} +\MakeShortVerb{\|} +\begin{document} + \DocInput{zref-check.dtx} +\end{document} +%</driver> +% \fi +% +% +% \begin{documentation} +% +% \section{Introduction} +% +% The \pkg{zref-check} package provides an user interface for making \LaTeX{} +% cross-references exploiting document contextual information to enrich the +% way the reference can be rendered, but at the same time ensuring the means +% that these cross-references can be done consistently with the document +% structure. +% +% The usual \LaTeX{} cross-reference is done by referring to a \texttt{label}, +% associated with one or another document structural element, and this +% reference will typeset for you some content based on the information which +% is stored in that label. \cs{zcheck}, the main user command of +% \pkg{zref-check}, has a somewhat different concept. Instead of trying to +% provide the text to be typeset based on the contextual information, +% \cs{zcheck} lets the user supply an arbitrary text and specify one or more +% checks to be done on the label(s) being referred to. If any of the checks +% fail, a warning is issued upon compilation, so that the user can go back to +% that cross-reference and correct it as needed, without having to rely on +% burdensome and error prone manual proof-reading. +% +% This grants a much increased flexibility for the cross-reference text, which +% means in practice that the writing style, the variety of expressions you may +% use for similar situations, does not need to be sacrificed for the +% convenience. \cs{zcheck}'s cross-references do not need to ``feel'' +% automated to be consistently checked. Localization is also not an issue, +% since the cross-reference text is provided directly by the user. Separating +% ``typesetting'' from ``checking'' also means there is a lot of document +% context we can leverage for this purpose (see Section~\ref{sec:checks}). +% +% A standard \LaTeX{} cross-reference is made to refer to specific numbered +% document elements -- chapters, sections, figures, tables, equations, etc. +% The cross-reference will normally produce that number (which is the +% element's ``id'') and, eventually, its ``type'' (the counter). We may also +% refer to the page that element occurs and even its ``title'' (in which case, +% atypically, we may even get to refer to an unnumbered section, provided we +% also implicitly supply by some means the ``id''). +% +% For references to these usual specific document elements, \pkg{zref-check} +% caters for a particular kind of cross-reference which is common: +% \emph{relational} statements based on them. \cs{zcheck} can typeset and +% meaningfully check cross-references such as ``above'', ``on the next page'', +% ``on the facing page'', ``on the previous section'', ``later on this +% chapter'' and so on. After all, if your reference is being made on page 2 +% and refers to something on the same page, ``on this page'' reads much better +% than ``on page~2''. If you are writing chapter~4, ``on the previous +% chapter'' sounds nicer than ``on chapter~3''. +% +% However, there is yet another kind of ``looser'' cross-reference we +% routinely do in our documents. Expressions such as ``previously'', ``as +% mentioned before'', ``as will be discussed'', and so on, are a powerful +% discursive instrument, which enriches the text, by offering hints to the +% arguments' threads, without necessarily pressing them too hard onto the +% reader. So, we might not want to say ``on footnote 57, pag.~34'', but +% prefer ``previously'', not ``on Section 3.4'', but rather ``below'', or +% ``later on''. Besides, we also may refer to certain passages in the text in +% this way, rather than to numbered document elements. And this kind of +% reference is not only hard to check and find, but also to fix. After all, +% if you are making one such reference, you are taking that statement as a +% premisse at the current point in the text. So, if that reference is +% missing, or relocated, you may need to bring in the support to the premisse +% for your argument to close, rather than just ``adjust the reference text''. +% \pkg{zref-check} also provides support for this kind of cross-reference, +% allowing for them to be consistently verified. +% +% +% +% \section{Loading the package} +% \label{sec:loading-package} +% +% As usual: +% +% \begin{syntax} +% \cs{usepackage}\oarg{options}|{zref-check}| +% \end{syntax} +% +% +% +% \section{Dependencies} +% +% \pkg{zref} is required, of course, but in particular, its modules +% \pkg{zref-user} and \pkg{zref-abspage} are loaded by default. \pkg{ifdraft} +% (from the \pkg{oberdiek} bundle) is also loaded by default. A \LaTeX{} +% kernel later than 2021-06-01 is required, since we rely on the new hook +% system from \pkg{ltcmdhooks} for the sectioning checks. If \pkg{hyperref} +% is loaded and option \pkg{hyperref} is given, \pkg{zref-check} makes use of +% it, but it does not load the package for you. +% +% +% \section{User interface} +% \label{sec:user-interface} +% +% \begin{function}{\zcheck} +% \begin{syntax} +% \cs{zcheck}\oarg{options}\marg{labels}\oarg{checks}\marg{text} +% \end{syntax} +% Typesets \marg{text}, as given, while performing the comma separated list +% of \oarg{checks} on each of the comma separated list of \marg{labels}. +% When \pkg{hyperref} support is enabled, \marg{text} will be made a +% hyperlink to \emph{the first} label in \marg{labels}. The starred version +% of the command does the same as the plain one, just does not form a link. +% The \oarg{options} are (mostly) the same as those of the package, and can +% be given to local effect. +% \end{function} +% +% \begin{function}{\zctarget} +% \begin{syntax} +% \cs{zctarget}\marg{label}\marg{text} +% \end{syntax} +% Typesets \marg{text}, as given, and places a pair of \texttt{zlabel}s, one +% at the start of \marg{text}, using \marg{label} as label name, another one +% (internal) at the end of \marg{text}. +% \end{function} +% +% +% \begin{function}{zcregion} +% \begin{syntax} +% |\begin{zcregion}|\marg{label} +% | ...| +% |\end{zcregion}| +% \end{syntax} +% An environment that does the same as \cs{zctarget}, for cases of longer +% stretches of text. +% \end{function} +% +% +% \begin{function}{\zrefchecksetup} +% \begin{syntax} +% \cs{zrefchecksetup}\marg{options} +% \end{syntax} +% Sets \pkg{zref-check}'s options (see Section~\ref{sec:options}). +% \end{function} +% +% +% \section{Checks} +% \label{sec:checks} +% +% \pkg{zref-check} provides several ``checks'' to be used with \cs{zcheck}. +% The checks may be combined in a \cs{zcheck} call, e.g.\ \opt{[close, +% after]}, or \opt{[thischap, before]}. In this case, each check in +% \oarg{checks} is performed against each of the \marg{labels}. This is done +% independently for each check, which means, in practice, that the checks bear +% a logical \texttt{AND} relation to the others. Whether the combination is +% meaningful, is up to the user. As is the correspondence between the +% \oarg{checks} and the \marg{text>} in \cs{zcheck}. +% +% The use of checks which perform ``within the page'' comparisons -- namely +% \opt{above} and \opt{below} and, through them, \opt{before} and \opt{after} +% -- comes with some caveats you should be acquainted with. +% Section~\ref{sec:within-page-checks} discusses their limitations and expands +% on the expected workflow for their use to ensure reliable results. +% +% Note that the naming convention of the checks adopts the perspective of +% \cs{zcheck}. That is, the name of the check describes the position of the +% label being referred to, relative to the \cs{zcheck} call being made. For +% example, the \opt{before} check should issue no message if +% \cs{ztarget}|{mylabel}{...}| occurs before +% \cs{zcheck}|{mylabel}[before]{...}|. +% +% The available checks are the following: +% +% \begin{description}[leftmargin=0pt, itemindent=0pt, align=right, +% labelsep=1em, font=\MacroFont] +% +% \item[thispage] \meta{label} occurs on the same page as \cs{zcheck}. +% +% \item[prevpage] \meta{label} occurs on the previous page relative to +% \cs{zcheck}. +% +% \item[nextpage] \meta{label} occurs on the next page relative to +% \cs{zcheck}. +% +% \item[facing] On a \texttt{twoside} document, both \meta{label} and +% \cs{zcheck} fall onto a double spread, each on one of the two facing +% pages. +% +% \item[above] \meta{label} and \cs{zcheck} are both on the same page, and +% \meta{label} occurs ``above'' \cs{zcheck}. +% +% \item[below] \meta{label} and \cs{zcheck} are both on the same page, and +% \meta{label} occurs ``below'' \cs{zcheck}. +% +% \item[pagesbefore] \meta{label} occurs on any page before the one of +% \cs{zcheck}. +% +% \item[ppbefore] Convenience alias for \opt{pagesbefore}. +% +% \item[pagesafter] \meta{label} occurs on any page after the one of +% \cs{zcheck}. +% +% \item[ppafter] Convenience alias for \opt{pagesafter}. +% +% \item[before] Either \opt{above} or \opt{pagesbefore}. +% +% \item[after] Either \opt{below} or \opt{pagesafter}. +% +% \item[thischap] \meta{label} occurs on the same chapter as \cs{zcheck}. +% +% \item[prevchap] \meta{label} occurs on the previous chapter relative to the +% one of \cs{zcheck}. +% +% \item[nextchap] \meta{label} occurs on the next chapter relative to the one +% of \cs{zcheck}. +% +% \item[chapsbefore] \meta{label} occurs on any chapter before the one of +% \cs{zcheck}. +% +% \item[chapsafter] \meta{label} occurs on any chapter after the one of +% \cs{zcheck}. +% +% \item[thissec] \meta{label} occurs on the same section as \cs{zcheck}. +% +% \item[prevsec] \meta{label} occurs on the previous section (of the same +% chapter) relative to the one of \cs{zcheck}. +% +% \item[nextsec] \meta{label} occurs on the next section (of the same chapter) +% relative to the one of \cs{zcheck}. +% +% \item[secsbefore] \meta{label} occurs on any section (of the same chapter) +% before the one of \cs{zcheck}. +% +% \item[secsafter] \meta{label} occurs on any section (of the same chapter) +% after the one of \cs{zcheck}. +% +% \item[close] \meta{label} occurs within a page range from \opt{closerange} +% pages before the one of \cs{zcheck} to \opt{closerange} pages after it +% (about the \opt{closerange} option, see Section~\ref{sec:options}). +% +% \item[far] Not \opt{close}. +% +% \end{description} +% +% +% +% \section{Options} +% \label{sec:options} +% +% Options are a standard \texttt{key=value} comma separated list, and can be +% set globally either as \cs{usepackage}\oarg{options} at load-time (see +% Section~\ref{sec:loading-package}), or by means of \cs{zrefchecksetup} (see +% Section~\ref{sec:user-interface}) in the preamble. Most options can also be +% used with local effects, through the optional argument \oarg{options} of +% \cs{zcheck}. +% +% \DescribeOption{hyperref} +% Controls the use of \pkg{hyperref} by \pkg{zref-check} and takes values +% \opt{auto}, \opt{true}, \opt{false}. The default value, \opt{auto}, makes +% \pkg{zref-check} use \pkg{hyperref} if it is loaded, meaning \cs{zcheck} can +% be hyperlinked to the \emph{first label} in \marg{labels}. \opt{true} does +% the same thing, but warns if \pkg{hyperref} is not loaded (\pkg{hyperref} is +% never loaded for you). In either of these cases, if \pkg{hyperref} is +% loaded, module \pkg{zref-hyperref} is also loaded by \pkg{zref-check}. +% \opt{false} means not to use \pkg{hyperref} regardless of its availability. +% This is a preamble only option, but \cs{zcheck} provides granular control of +% hyperlinking by means of its starred version. +% +% +% \DescribeOption{msglevel} +% Sets the level of messages issued by \cs{zcheck} failed checks and takes +% values \opt{warn}, \opt{info}, \opt{none}, \opt{obeydraft}, \opt{obeyfinal}. +% The default value, \opt{warn}, issues messages both to the terminal and to +% the log file, \opt{info} issues messages to the log file only, \opt{none} +% suppresses all messages. \opt{obeydraft} corresponds to \opt{info} if +% option \opt{draft} is passed to \cs{documentclass}, and to \opt{warn} +% otherwise. \opt{obeyfinal} corresponds to \opt{warn} if option \opt{final} +% is (explicitly) passed to \cs{documentclass} and \opt{info} otherwise. +% \opt{ignore} is provided as convenience alias for \opt{msglevel=none} for +% local use only. This option only affects the messages issued by the checks +% in \cs{zcheck}, not other messages or warnings of the package. In +% particular, it does not affect warnings issued for undefined labels, which +% just use \cs{zref@refused} and thus are the same as standard \LaTeX{} ones +% for this purpose. +% +% +% \DescribeOption{onpage} +% Allows to control the messaging style for ``within page checks'', and takes +% values \opt{labelseq}, \opt{msg}, \opt{obeydraft}, \opt{obeyfinal}. The +% default, \opt{labelseq}, uses the labels' shipout sequence, as retrieved +% from the \file{.aux} file, to infer relative position within the page. +% \opt{msg} also uses the same method for checking relative position, but +% issues a (different) message \emph{even if the check passes}, to provide a +% simple workflow for robust checking of ``false negatives'', considering the +% label sequence is not fool proof (for details and workflow recommendations, +% see Section~\ref{sec:within-page-checks}). \opt{msg} also issues its +% messages at the same level defined in \opt{msglevel}. \opt{obeydraft} +% corresponds to \opt{labelseq} if option \opt{draft} is passed to +% \cs{documentclass} and to \opt{msg} otherwise. \opt{obeyfinal} corresponds +% to \opt{msg} if option \opt{final} is (explicitly) passed to +% \cs{documentclass}, and to \opt{labelseq} otherwise. +% +% +% \DescribeOption{closerange} +% Defines the width of the range of pages, relative to the reference, that are +% considered ``close'' by the \opt{close} check. Takes a positive integer as +% value, with default 5. +% +% +% \DescribeOption{labelcmd} +% Defines the command used to set the user labels in \cs{zctarget} and +% \texttt{zcregion}. Takes a control sequence \emph{name} as value, and the +% default sets labels with the minimal required properties, those of the +% \texttt{zrefcheck} property list. This is a preamble only option. The +% specified control sequence must receive one mandatory argument (the +% \marg{label}) and must generate a \texttt{zref label} with at least the +% properties in the \texttt{zrefcheck} property list. The intended use case +% is that of the user creating a convenience macro which calls both \cs{label} +% and \cs{zlabel}, as suggested in Section~\ref{sec:labels}, so that the same +% labels are accessible either from the standard reference system or from +% \pkg{zref}. For example: +% +% \begin{verbatim} +% \NewDocumentCommand\mybothlabels{m}{\label{#1}\zlabel{#1}} +% \zrefchecksetup{labelcmd=mybothlabels} +% \end{verbatim} +% +% Note that the value of the underlying counter used for labels in +% \cs{zctarget} and \texttt{zcregion} -- what you'd get with a plain \cs{ref} +% here -- is not really meaningful. But you get to use +% \cs{pageref}\marg{label}, or \pkg{hyperref}'s +% \cs{hyperref}\oarg{label}\marg{text} on the labels used in \cs{zctarget} and +% \texttt{zcregion} with this procedure. +% +% +% \section{Labels} +% \label{sec:labels} +% +% \pkg{zref-check} depends on \pkg{zref}, as the name entails, which means it +% is able to work with \pkg{zref} labels, in general created by \cs{zlabel}, +% but also with \cs{zctarget} and the \texttt{zcregion} environment provided +% by this package. This has some advantages, particularly the data +% flexibility of \pkg{zref}, and the absence of the ubiquitous ``load-order'' +% and compatibility problems which are well known to afflict \LaTeX{} packages +% of this area of functionality. On the other hand, the reliance on +% \pkg{zref} labels may be seen as an inconvenience, since users of the +% standard cross-reference infrastructure may need to add extra labels for +% this. That's true. But \pkg{zref-check} is not meant to replace the +% existing functionality of the kernel or of the traditional packages in this +% area (to my knowledge, it only intersects directly with \pkg{varioref} and, +% even so, it is quite different in scope). Indeed, it is easy to see the use +% in tandem with standard references, for example: +% +% \begin{verbatim} +% ... Figure~\ref{fig:figure-1}, \zcheck*{fig:figure-1}[nextpage]{on +% the next page}. +% \end{verbatim} +% +% Besides, \pkg{zref} does not share the label name-space with the standard +% labels, so that you can call both \cs{label} and \cs{zlabel} with the same +% label name (manually, or through a convenience macro), to ease the label set +% administration. The example above presumes that was the case. +% +% All user commands of \pkg{zref-check} have their \marg{label} arguments +% protected for \pkg{babel} active characters using \pkg{zref}'s +% \cs{zref@wrapper@babel}, so that we should have equivalent support in that +% regard, as \pkg{zref} itself does. However, \pkg{zref-check} sets labels +% which either start with \texttt{zrefcheck@} or end with \texttt{@zrefcheck}, +% for internal use. Label names with either of those are considered reserved +% by the package. +% +% +% \section{Limitations} +% +% There are three qualitatively different kinds of checks being used by +% \cs{zcheck}, according to the source and reliability of the information they +% mobilize: page number checks, within page checks, and sectioning checks. +% +% +% \subsection{Page number checks} +% +% Page number checks -- \opt{thispage}, \opt{prevpage}, \opt{nextpage}, +% \opt{pagesbefore}, \opt{pagesafter}, \opt{facing} -- use the +% \texttt{abspage} property provided by the \pkg{zref-abspage} module. This +% is a solid piece of information, on which we can rely upon. However, +% despite that, page number checks may still become ill-defined, if the +% \marg{text} argument in \cs{zcheck}, when typeset, crosses page boundaries, +% starting in one page, and finishing in another. The same can happen with +% the text in \cs{zctarget} and the \texttt{zcregion} environment. +% +% This is why the user commands of this package set always a pair or labels +% around \meta{text}. So, when checking \cs{zcheck} against a regular +% \texttt{zlabel} both the start and the end of the \meta{text} are checked +% against the label, and the check fails if either of them fails. When +% checking \cs{zcheck} against a \cs{zctarget} or a \texttt{zcregion}, both +% beginnings and ends are checked against each other two by two, and if any of +% them fails, the check fails. In other words, if a page number checks +% passes, we know that the entire \meta{text} arguments pass it. +% +% This is a corner case (albeit relevant) which must be taken care of, and it +% is possible to do so robustly. Hence, we can expect reliable results in +% these tests. +% +% +% \subsection{Within page checks} +% \label{sec:within-page-checks} +% +% When both label and reference fall on the same page things become much +% trickier. This is basically the case of the checks \opt{above} and +% \opt{below} (and, through them, \opt{before} and \opt{after}). There is no +% equally reliable information (that I know of) as we have for the page number +% checks for this, especially when floats come into play. Which, of course, +% is the interesting case to handle. +% +% To infer relative position of label and reference on the same page, +% \pkg{zref-check} uses the labels' shipout sequence, which is retrieved at +% load-time from the order in which the labels occur in the \file{.aux} file. +% Indeed, \pkg{zref} writes labels to the \file{.aux} file at shipout (and, +% hence, in shipout order), and needs to do so, because a number of its +% properties are only available at that point. +% +% However, even if this method will buy us a correct check for a regular float +% on a regular page (which, to be fair, is a good result), it is not difficult +% do conceive situations in which this sequence may not be meaningful, or even +% correct, for the case. A number of cases which may do so are: two column +% documents, text wrapping, scaling, overlays, etc. (I don't know if those +% make the method fail, I just don't know if they don't). Therefore, the +% \texttt{labelseq} should be taken as a \emph{proxy} and not fully reliable, +% meaning that the user should be watchful of its results. +% +% For this reason, \pkg{zref-check} provides an easy way to do so, by allowing +% specific control of the messaging style of the checks which do within page +% comparisons though the option \opt{onpage}. The concern is not really with +% false positives (getting a warning when it was not due), but with false +% negatives (not getting a warning when it was due). Hence, setting +% \opt{onpage} to \opt{msg} at a final typesetting stage (or just set it to +% \opt{obeydraft} or \opt{obeyfinal} if that's part of your workflow) provides +% a way to easily identify all cases of such checks (failing or passing), and +% double-check them. In case the test is passing though, the message is +% different from that of a failing check, to quickly convey why you are +% getting the message. This option can also be set at the local level, if the +% page in question is known to be problematic, or just atypical. +% +% +% \subsection{Sectioning checks} +% +% The information used by sectioning checks is provided by means of dedicated +% counters for chapters and sections, similarly as standard counters for them, +% but which are stepped and reset regardless of whether these sectioning +% commands are numbered or not (that is, starred or not). And this for two +% reasons. First, we don't need the absolute counter value to be able to make +% the kind of relative statement we want to do here. Second, this allows us +% to have these checks work for numbered and unnumbered sectioning commands +% without having to worry about how those are used within the document. +% +% The caveat is that the package does this by hooking into \cs{chapter} and +% \cs{section}, which poses two restrictions for the proper working of these +% checks. First, we are using the new hook system for this, as provided by +% \pkg{ltcmdhooks}, which means a \LaTeX{} kernel later than 2021-06-01 is +% required. Second, since we are hooking into \cs{chapter} and \cs{section}, +% these checks presume these commands are being used by the document class for +% this purpose (either directly, or internally as, for example, KOMA-Script's +% \cs{addchap} and \cs{addsec} do). If that's not the case, additional setup +% may be needed for these checks to work as expected. +% +% +% \end{documentation} +% +% +% \begin{implementation} +% +% \section{Initial setup} +% +% Start the \pkg{DocStrip} guards. +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% Identify the internal prefix (\LaTeX3 \pkg{DocStrip} convention). +% \begin{macrocode} +%<@@=zrefcheck> +% \end{macrocode} +% +% +% For the \texttt{chapter} and \texttt{section} checks, \pkg{zref-check} uses +% the new hook system in \pkg{ltcmdhooks}, which was released with the +% 2021/06/01 \LaTeX{} kernel. +% \begin{macrocode} +\providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion} +\IfFormatAtLeastTF{2021-06-01} + {} + {% + \PackageError{zref-check}{LaTeX kernel too old} + {% + 'zref-check' requires a LaTeX kernel newer than 2021-06-01.% + \MessageBreak Loading will abort!% + }% + \endinput + }% +% \end{macrocode} +% +% Identify the package. +% \begin{macrocode} +\ProvidesExplPackage {zref-check} {2021-08-04} {0.1.0} + {Flexible cross-references with contextual checks based on zref} +% \end{macrocode} +% +% \section{Dependencies} +% +% \begin{macrocode} +\RequirePackage { zref-user } +\RequirePackage { zref-abspage } +\RequirePackage { ifdraft } +% \end{macrocode} +% +% +% \section{\pkg{zref} setup} +% +% \begin{variable}{\g_@@_abschap_int, \g_@@_abssec_int} +% Provide absolute counters for section and chapter, and respective +% \pkg{zref} properties, so that we can make checks about relation of +% chapters/sections regardless of internal counters, since we don't get +% those for the unnumbered (starred) ones. About the proper place to make +% the hooks for this purpose, see +% \url{https://tex.stackexchange.com/q/605533/105447} (thanks Ulrike +% Fischer). +% \begin{macrocode} +\int_new:N \g_@@_abschap_int +\int_new:N \g_@@_abssec_int +% \end{macrocode} +% \end{variable} +% +% +% If the documentclass does not define \cs{chapter} the only thing that +% happens is that the chapter counter is never incremented, and the section +% one never reset. +% \begin{macrocode} +\AddToHook { cmd / chapter / before } + { + \int_gincr:N \g_@@_abschap_int + \int_zero:N \g_@@_abssec_int + } +\zref@newprop { abschap } [0] { \int_use:N \g_@@_abschap_int } +\zref@addprop \ZREF@mainlist { abschap } +% \end{macrocode} +% +% \begin{macrocode} +\AddToHook { cmd / section / before } + { \int_gincr:N \g_@@_abssec_int } +\zref@newprop { abssec } [0] { \int_use:N \g_@@_abssec_int } +\zref@addprop \ZREF@mainlist { abssec } +% \end{macrocode} +% +% +% This is the list of properties to be used by \pkg{zref-check}, that is, the +% list of properties the references and targets store. This is the minimum +% set required, more properties may be added according to options. +% \begin{macrocode} +\zref@newlist { zrefcheck } +\zref@addprops { zrefcheck } + { + abspage , + abschap , + abssec , + page + } +% \end{macrocode} +% +% +% \section{Plumbing} +% +% \subsection{Messages} +% +% \begin{macro}{\@@_message:nnnn, \@@_message:nnnx} +% \begin{macrocode} +\cs_new:Npn \@@_message:nnnn #1#2#3#4 + { + \use:c { msg_ \l_@@_msglevel_tl :nnnnn } + { zref-check } {#1} {#2} {#3} {#4} + } +\cs_generate_variant:Nn \@@_message:nnnn { nnnx } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +\msg_new:nnn { zref-check } { check-failed } + { + Failed~check~'#1'~for~label~'#2' \iow_newline: + on~page~#3~on~input~line~\msg_line_number:. + } +\msg_new:nnn { zref-check } { double-check } + { + Double-check~'#1'~for~label~'#2' \iow_newline: + on~page~#3~on~input~line~\msg_line_number:. + } +% \end{macrocode} +% +% \begin{macrocode} +\msg_new:nnn { zref-check } { check-missing } + { Check~'#1'~not~defined~on~input~line~\msg_line_number:. } +\msg_new:nnn { zref-check } { property-undefined } + { Property~'#1'~not~defined~on~input~line~\msg_line_number:. } +\msg_new:nnn { zref-check } { property-not-in-label } + { Label~'#1'~has~no~property~'#2'~on~input~line~\msg_line_number:. } +\msg_new:nnn { zref-check } { property-not-integer } + { + Property~'#1'~for~label~'#2'~not~an~integer \iow_newline: + on~input~line~\msg_line_number:. + } +% \end{macrocode} +% +% \begin{macrocode} +\msg_new:nnn { zref-check } { hyperref-preamble-only } + { + Option~'hyperref'~only~available~in~the~preamble. \iow_newline: + Use~the~starred~version~of~'\noexpand\zcheck'~instead. + } +\msg_new:nnn { zref-check } { missing-hyperref } + { Missing~'hyperref'~package. \iow_newline: Setting~'hyperref=false'. } +\msg_new:nnn { zref-check } { ignore-document-only } + { + Option~'ignore'~only~available~in~the~document. \iow_newline: + Use~option~'msglevel'~instead. + } +\msg_new:nnn { zref-check } { option-preamble-only } + { + Option~'#1'~only~available~in~the~preamble \iow_newline: + on~input~line~\msg_line_number:. + } +\msg_new:nnn { zref-check } { closerange-not-positive-integer } + { + Option~'closerange'~not~a~positive~integer \iow_newline: + on~input~line~\msg_line_number:.~Using~default~value. + } +\msg_new:nnn { zref-check } { labelcmd-undefined } + { + Control~sequence~named~'#1'~used~in~option~'labelcmd'~is~not~defined.~ + Using~default~value. + } +% \end{macrocode} +% +% +% \subsection{Integer testing} +% +% \begin{macro}{\@@_is_integer:n, \@@_int_to_roman:w} +% From \url{https://tex.stackexchange.com/a/244405} (thanks Enrico Gregorio, +% aka `egreg'), also see \url{https://tex.stackexchange.com/a/19769}. +% Following the \texttt{l3styleguide}, I made a copy of +% \cs{__int_to_roman:w}, since it is an internal function from the +% \texttt{int} module, but we still get a warning from \texttt{l3build doc}, +% complaining about it. And we're using \cs{tl_if_empty:oTF} instead of +% \cs{tl_if_blank:oTF} as in egreg's answer, since \cs{romannumeral} is +% defined so that ``the expansion is empty if the number is zero or +% negative'', not ``blank''. A couple of comments about this technique: the +% underlying \cs{romannumeral} ignores space tokens and explicit signs +% (\texttt{+} and \texttt{-}) in the expansion and hence it can only be used +% to test positive integers; also the technique cannot distinguish whether +% it received an empty argument or if ``the expansion was empty'' as a +% result of receiving number as argument, so this must also be controlled +% for since, in our use case, this may happen. +% \begin{macrocode} +\cs_new_eq:NN \@@_int_to_roman:w \__int_to_roman:w +\prg_new_conditional:Npnn \@@_is_integer:n #1 { p, T , F , TF } + { + \tl_if_empty:oTF {#1} + { \prg_return_false: } + { + \tl_if_empty:oTF { \@@_int_to_roman:w -0#1 } + { \prg_return_true: } + { \prg_return_false: } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_is_integer_rgx:n} +% A possible alternative to \cs{@@_is_integer:n} is to use a straightforward +% regexp match (see \url{https://tex.stackexchange.com/a/427559}). It does +% not suffer from the mentioned caveats from the \cs{__int_to_roman:w} +% technique, however, while \cs{@@_is_integer:n} is expandable, +% \cs{@@_is_integer_rgx:n} is not. Also, \cs{@@_is_integer_rgx:n} is +% probably slower. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \@@_is_integer_rgx:n #1 { TF } + { + \regex_match:nnTF { \A\d+\Z } {#1} + { \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Options} +% +% +% \subsubsection*{\opt{hyperref} option} +% +% \begin{variable}{\l_@@_use_hyperref_bool, \l_@@_warn_hyperref_bool} +% \begin{macrocode} +\bool_new:N \l_@@_use_hyperref_bool +\bool_new:N \l_@@_warn_hyperref_bool +\keys_define:nn { zref-check } + { + hyperref .choice: , + hyperref / auto .code:n = + { + \bool_set_true:N \l_@@_use_hyperref_bool + \bool_set_false:N \l_@@_warn_hyperref_bool + } , + hyperref / true .code:n = + { + \bool_set_true:N \l_@@_use_hyperref_bool + \bool_set_true:N \l_@@_warn_hyperref_bool + } , + hyperref / false .code:n = + { + \bool_set_false:N \l_@@_use_hyperref_bool + \bool_set_false:N \l_@@_warn_hyperref_bool + } , + hyperref .initial:n = auto , + hyperref .default:n = auto + } +% \end{macrocode} +% \end{variable} +% +% \begin{macrocode} +\AtBeginDocument + { + \@ifpackageloaded { hyperref } + { + \bool_if:NT \l_@@_use_hyperref_bool + { + \RequirePackage { zref-hyperref } + \zref@addprop { zrefcheck } { anchor } + } + } + { + \bool_if:NT \l_@@_warn_hyperref_bool + { \msg_warning:nn { zref-check } { missing-hyperref } } + \bool_set_false:N \l_@@_use_hyperref_bool + } + \keys_define:nn { zref-check } + { + hyperref .code:n = + { \msg_warning:nn { zref-check } { hyperref-preamble-only } } + } + } +% \end{macrocode} +% +% +% \subsubsection*{\opt{msglevel} option} +% +% \begin{variable}{\l_@@_msglevel_tl} +% \begin{macrocode} +\tl_new:N \l_@@_msglevel_tl +\keys_define:nn { zref-check } + { + msglevel .choice: , + msglevel / warn .code:n = + { \tl_set:Nn \l_@@_msglevel_tl { warning } } , + msglevel / info .code:n = + { \tl_set:Nn \l_@@_msglevel_tl { info } } , + msglevel / none .code:n = + { \tl_set:Nn \l_@@_msglevel_tl { none } } , + msglevel / obeydraft .code:n = + { + \ifdraft + { \tl_set:Nn \l_@@_msglevel_tl { info } } + { \tl_set:Nn \l_@@_msglevel_tl { warning } } + } , + msglevel / obeyfinal .code:n = + { + \ifoptionfinal + { \tl_set:Nn \l_@@_msglevel_tl { warning } } + { \tl_set:Nn \l_@@_msglevel_tl { info } } + } , + msglevel .value_required:n = true , + msglevel .initial:n = warn , +% \end{macrocode} +% \opt{ignore} is a convenience alias for \opt{msglevel=none}, but only for +% use in the document body. +% \begin{macrocode} + ignore .code:n = + { \msg_warning:nn { zref-check } { ignore-document-only } } , + ignore .value_forbidden:n = true + } +% \end{macrocode} +% \end{variable} +% +% \begin{macrocode} +\AtBeginDocument + { + \keys_define:nn { zref-check } + { ignore .meta:n = { msglevel = none } } + } +% \end{macrocode} +% +% +% \subsubsection*{\opt{onpage} option} +% +% \begin{variable}{\l_@@_msgonpage_bool} +% \begin{macrocode} +\bool_new:N \l_@@_msgonpage_bool +\keys_define:nn { zref-check } + { + onpage .choice: , + onpage / labelseq .code:n = + { + \bool_set_false:N \l_@@_msgonpage_bool + } , + onpage / msg .code:n = + { + \bool_set_true:N \l_@@_msgonpage_bool + } , + onpage / obeydraft .code:n = + { + \ifdraft + { \bool_set_false:N \l_@@_msgonpage_bool } + { \bool_set_true:N \l_@@_msgonpage_bool } + } , + onpage / obeyfinal .code:n = + { + \ifoptionfinal + { \bool_set_true:N \l_@@_msgonpage_bool } + { \bool_set_false:N \l_@@_msgonpage_bool } + } , + onpage .value_required:n = true , + onpage .initial:n = labelseq + } +% \end{macrocode} +% \end{variable} +% +% +% \subsubsection*{\opt{closerange} option} +% +% \begin{variable}{\l_@@_close_range_int} +% \begin{macrocode} +\int_new:N \l_@@_close_range_int +\keys_define:nn { zref-check } + { + closerange .code:n = + { + \@@_is_integer_rgx:nTF {#1} + { \int_set:Nn \l_@@_close_range_int { \int_eval:n {#1} } } + { + \msg_warning:nn { zref-check } { closerange-not-positive-integer } + \int_set:Nn \l_@@_close_range_int { 5 } + } + } , + closerange .value_required:n = true , + closerange .initial:n = 5 + } +% \end{macrocode} +% \end{variable} +% +% +% \subsubsection*{\opt{labelcmd} option} +% +% \begin{variable}{\l_@@_target_label_tl} +% I'd love to receive the macro itself rather than it's name, but this would +% bring unwarranted complications: +% \url{https://tex.stackexchange.com/a/489570}. +% \begin{macrocode} +\tl_new:N \l_@@_target_label_tl +\bool_new:N \l_@@_target_label_bool +\keys_define:nn { zref-check } + { + labelcmd .code:n = + { + \tl_set:Nn \l_@@_target_label_tl {#1} + \bool_set_true:N \l_@@_target_label_bool + } , + labelcmd .value_required:n = true , + } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_target_label:n} +% Default definition of the function for user label setting in \cs{zctarget} +% and \texttt{zcregion}. It may be redefined at \texttt{begindocument} +% according to option \opt{labelcmd}. +% \begin{macrocode} +\cs_new:Npn \@@_target_label:n #1 + { \zref@labelbylist {#1} { zrefcheck } } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +\AtBeginDocument + { + \bool_if:NT \l_@@_target_label_bool + { + \tl_if_blank:VT \l_@@_target_label_tl + { \tl_clear:N \l_@@_target_label_tl } + \cs_if_exist:cTF { \l_@@_target_label_tl } + { + \cs_set:Npx \@@_target_label:n #1 + { + \exp_not:o + { \cs:w \l_@@_target_label_tl \cs_end: } + {#1} + } + } + { + \exp_args:NnnV \msg_warning:nnn { zref-check } + { labelcmd-undefined } { \l_@@_target_label_tl } + } + } + \keys_define:nn { zref-check } + { + labelcmd .code:n = + { + \msg_warning:nnn { zref-check } + { option-preamble-only } { labelcmd } + } + } + } +% \end{macrocode} +% +% +% \subsubsection*{Package options} +% +% Process load-time package options +% (\url{https://tex.stackexchange.com/a/15840}). +% \begin{macrocode} +\RequirePackage { l3keys2e } +\ProcessKeysOptions { zref-check } +% \end{macrocode} +% +% +% \begin{macro}[int]{\zrefchecksetup} +% Provide \cs{zrefchecksetup}. +% \begin{macrocode} +\NewDocumentCommand \zrefchecksetup { m } + { \keys_set:nn { zref-check } {#1} } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Position on page} +% +% Method for determining relative position within the page: the sequence in +% which the labels get shipped out, inferred from the sequence in which the +% labels occur in the \file{.aux} file. +% +% Some relevant info about the sequence of things: +% \url{https://tex.stackexchange.com/a/120978} and \texttt{texdoc lthooks}, +% section ``Hooks provided by |\begin{document}|''. +% +% +% One first attempt at this was to use \cs{zref@newlabel}, which is the macro +% in which \pkg{zref} stores the label information in the aux file. When the +% \file{.aux} file is read at the beginning of the compilation, this macro is +% expanded for each of the labels. So, by redefining this macro we can feed a +% variable (a L3 sequence), and then do what it usually does, which is to +% define each label with the internal macro \cs{@newl@bel}, when the +% \file{.aux} file is read. +% +% Patching this macro for this is not possible. First, \cs{zref@newlabel} is +% one of those ``commands that look ahead'' mentioned in \pkg{ltcmdhooks} +% documentation. Indeed, \cs{@newl@bel} receives 3 arguments, and +% \cs{zref@newlabel} just passes the first, the following two will be scanned +% ahead. Second, the \pkg{ltcmdhooks} hooks are not actually available when +% the \file{.aux} file is read, they come only after |\begin{document}|. +% Hence, redefinition would be the only alternative. My attempts at this +% ended up registered at \url{https://tex.stackexchange.com/a/604744}. But +% the best result in these lines was: +% +% \begin{verbatim} +% \ZREF@Robust\edef\zref@newlabel#1{ +% \noexpand\seq_gput_right:Nn \noexpand\g__zrefcheck_auxfile_lblseq_seq {#1} +% \noexpand\@newl@bel{\ZREF@RefPrefix}{#1} +% } +% \end{verbatim} +% +% +% However, better than the above is to just read it from the \file{.aux} file +% directly, which relieves us from hacking into any internals. That's what +% David Carlisle's answer at \url{https://tex.stackexchange.com/a/147705} +% does. This answer has actually been converted into the package +% \pkg{listlbls} by Norbert Melzer, but it is made to work with regular +% labels, not with \pkg{zref}'s. And it also does not really expose the +% information in a retrievable way (as far as I can tell). So, the below is +% adapted from Carlisle's answer's technique (a poor man's version of it...). +% +% There is some subtlety here as to whether this approach makes it safe for us +% to read the labels at this point without \cs{zref@wrapper@babel}. The +% common wisdom is that babel's shorthands are only active after +% |\begin{document}| (e.g., \url{https://tex.stackexchange.com/a/98897}). +% Alas, it is more complicated than that. Babel's documentation says (in +% section 9.5 Shorthands): ``To prevent problems with the loading of other +% packages after babel we reset the catcode of the character to the original +% one at the end of the package and of each language file (except with +% KeepShorthandsActive). It is re-activate[d] again at |\begin{document}|. +% We also need to make sure that the shorthands are active during the +% processing of the \file{.aux} file. Otherwise some citations may give +% unexpected results in the printout when a shorthand was used in the optional +% argument of |\bibitem| for example.'' This is done with +% |\if@filesw \immediate\write\@mainaux{...}|. In other words, the +% catcode change is written in the \file{.aux} file itself! Indeed, if you +% inspect the file, you'll find them there. Besides, there is still the +% ominous ``except with KeepShorthandsActive''. +% +% However, the \emph{method} we're using here is not quite the same as the +% usual run of the \file{.aux} file, because we're actively discarding the +% lines for which the first token is not equal to \cs{zref@newlabel}. I have +% tested the famous sensitive case for this: \pkg{babel} \opt{french} and +% labels with colons. And things worked as expected. Well, \emph{if} +% \opt{KeepShorthandsActive} is enabled \emph{with \opt{french}} and we load +% the package \emph{after babel} things do break, but not quite because of the +% colons in the labels. Even \pkg{siunitx} breaks in the same +% conditions\dots{} +% +% For reference: About what are valid characters for use in labels: +% \url{https://tex.stackexchange.com/a/18312}. About some problems with +% active colons: \url{https://tex.stackexchange.com/a/89470}. About the +% difference between L3 strings and token lists, see +% \url{https://tex.stackexchange.com/a/446381}, in particular Joseph Wright's +% comment: ``Strings are for data that will never be typeset, for example file +% names, identifiers, etc.: if the material may be used in typesetting, it +% should be a token list.'' See also moewe's (CW) answer in the same lines. +% Which suggests using L3 strings for the reference labels might be a good +% catch all approach, and possibly more robust. David Carlisle's comment +% about \pkg{inputenc} and how the strings work is a caveat (see +% \url{https://tex.stackexchange.com/q/446123#comment1516961_446381}, thanks +% David Carlisle). Still\dots{} let's stick to tradition as long as it works, +% \pkg{zref} already does a great job in this regard anyway. +% +% +% \begin{variable}{\g_@@_auxfile_lblseq_prop} +% \begin{macrocode} +\prop_new:N \g_@@_auxfile_lblseq_prop +% \end{macrocode} +% \end{variable} +% +% \begin{macrocode} +\tl_set:Nn \g_tmpa_tl { \c_sys_jobname_str .aux } +\file_if_exist:nT { \g_tmpa_tl } + { +% \end{macrocode} +% Retrieve the information from the \file{.aux} file, and store it in a +% property list, so that the sequence can be retrieved in key-value fashion. +% \begin{macrocode} + \ior_open:Nn \g_tmpa_ior { \g_tmpa_tl } + \group_begin: + \int_zero:N \l_tmpa_int + \tl_clear:N \l_tmpa_tl + \tl_clear:N \l_tmpb_tl + \bool_set_false:N \l_tmpa_bool + \ior_map_variable:NNn \g_tmpa_ior \l_tmpa_tl + { + \tl_map_variable:NNn \l_tmpa_tl \l_tmpb_tl + { + \tl_if_eq:NnTF \l_tmpb_tl { \zref@newlabel } + { +% \end{macrocode} +% Found a \cs{zref@label}, signal it. +% \begin{macrocode} + \bool_set_true:N \l_tmpa_bool + } + { + \bool_if:NTF \l_tmpa_bool + { + \bool_set_false:N \l_tmpa_bool + \int_incr:N \l_tmpa_int + \prop_gput:Nxx \g_@@_auxfile_lblseq_prop + { \l_tmpb_tl } { \int_use:N \l_tmpa_int } + } + { +% \end{macrocode} +% If there is not a match of the first token with \cs{zref@newlabel}, break +% the loop and discard the rest of the line, to ensure no babel calls to +% \cs{catcode} in the \file{.aux} file get expanded. This also breaks the +% loop and discards the rest of the \cs{zref@newlabel} lines after we got the +% label we wanted, since we reset \cs{l_tmpa_bool} in the \texttt{T} branch. +% \begin{macrocode} + \tl_map_break: + } + } + } + } + \group_end: + \ior_close:N \g_tmpa_ior + } +% \end{macrocode} +% +% +% +% The alternate method I had considered (more than that...) for this was using +% yx coordinates supplied by \pkg{zref}'s \pkg{savepos} module. However, this +% approach brought in a number of complexities, including the need to patch +% either \cs{zref@label} or \cs{ZREF@label}. In addition, the technique was +% at the bottom fundamentally flawed. Ulrike Fischer was very much right when +% she said that ``structure and position are two different beasts'' +% (\url{https://github.com/ho-tex/zref/issues/12#issuecomment-880022576}). It +% is true that the checks based on it behaved decently, in normal +% circumstances, and except for outrageous label placement by the user, it +% would return the expected results. We don't really need exact coordinates +% to decide ``above/below''. Besides, it would do an exact job for the +% dedicated target macros of this package. However, I could not conceive a +% situation where the \texttt{yx} criterion would perform clearly better than +% the \texttt{labelseq} one. And, if that's the case, and considering the +% complications it brings, this check was a slippery slope. All in all, I've +% decided to drop it. +% +% +% +% \subsection{Counter} +% +% We need a dedicated counter for the labels generated by the checks and +% targets. The value of the counter is not relevant, we just need it to be +% able to set proper anchors with \cs{refstepcounter}. And, since I couldn't +% find a \cs{refstepcounter} equivalent in L3, we use a standard 2e counter +% here. I'm also using the technique to ensure the counter is never reset +% that is used by \file{zref-abspage.sty} and \cs{zref@require@unique}. +% Indeed, the requirements are the same, we need numbers ensured to be +% \emph{unique} in the counter. +% +% \begin{macrocode} +\begingroup + \let \@addtoreset \ltx@gobbletwo + \newcounter { zrefcheck } +\endgroup +\setcounter { zrefcheck } { 0 } +% \end{macrocode} +% +% +% +% \subsection{Label formats} +% +% \begin{macro}{\@@_check_lblfmt:n} +% \begin{syntax} +% \cs{@@_check_lblfmt:n} \Arg{check id int} +% \end{syntax} +% \begin{macrocode} +\cs_new:Npn \@@_check_lblfmt:n #1 { zrefcheck@ \int_use:N #1 } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_end_lblfmt:n} +% \begin{syntax} +% \cs{@@_end_lblfmt:n} \Arg{label} +% \end{syntax} +% \begin{macrocode} +\cs_new:Npn \@@_end_lblfmt:n #1 { #1 @zrefcheck } +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Property values} +% +% +% \begin{macro}[int]{\zrefcheck_get_astl:nnn} +% A convenience function to retrieve property values from labels. Uses +% \cs{g_@@_auxfile_lblseq_prop} for \texttt{lblseq}, and calls +% \cs{zref@extractdefault} for everything else. +% +% We cannot use the ``return value'' of \cs{@@_get_astl:nnn} or +% \cs{@@_get_asint:nnn} directly, because we need to use the retrieved +% property values as arguments in the checks, however we use here a number +% of non-expandable operations. Hence, we receive a local \texttt{tl/int} +% variable as third argument and set that, so that it is available (and +% expandable) at the place of use. For this reason, we do not group here, +% because we are passing a local variable around, but it is expected this +% function will be called within a group. +% +% We're returning \cs{c_empty_tl} in case of failure to find the intended +% property value (explicitly in \cs{zref@extractdefault}, but that is also +% what \cs{tl_clear:N} does). +% +% \begin{syntax} +% \cs{zrefcheck_get_astl:nnn} \Arg{label} \Arg{prop} \Arg{tl var} +% \end{syntax} +% \begin{macrocode} +\cs_new:Npn \zrefcheck_get_astl:nnn #1#2#3 + { + \tl_clear:N #3 + \tl_if_eq:nnTF {#2} { lblseq } + { + \prop_get:NnNF \g_@@_auxfile_lblseq_prop {#1} #3 + { + \msg_warning:nnnn { zref-check } + { property-not-in-label } {#1} {#2} + } + } + { +% \end{macrocode} +% There are three things we need to check to ensure the information we are +% trying to retrieve here exists: the existence of \Arg{label}, the existence +% of \Arg{prop}, and whether the particular label being queried actually +% contains the property. If that's all in place, the value is passed to the +% checks, and it's their responsibility to verify the consistency of this +% value. +% +% The existence of the label is an user facing issue, and a warning for this +% is placed in \cs{@@_zcheck:nnnnn} (and done with \cs{zref@refused}). We do +% check here though for definition with \cs{zref@ifrefundefined} and silently +% do nothing if it is undefined, to reduce irrelevant warnings in a fresh +% compilation round. The other two are more ``internal'' problems, either +% some problem with the checks, or with the configuration of \pkg{zref} for +% their consumption. +% \begin{macrocode} + \zref@ifrefundefined {#1} + {} + { + \zref@ifpropundefined {#2} + { \msg_warning:nnnn { zref-check } { property-undefined } {#2} } + { + \zref@ifrefcontainsprop {#1} {#2} + { + \tl_set:Nx #3 + { \zref@extractdefault {#1} {#2} { \c_empty_tl } } + } + { + \msg_warning:nnnn + { zref-check } { property-not-in-label } {#1} {#2} + } + } + } + } + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{variable}{\l_@@_integer_bool} +% \cs{zrefcheck_get_asint:nnn} is a very convenient wrapper around the more +% general \cs{zrefcheck_get_astl:nnn}, since almost always we'll be wanting +% to compare numbers in the checks. However, it is quite hard for it to +% ensure an integer is \emph{always} returned in the case of errors. And +% those do occur, even in a well structured document (e.g., in a first round +% of compilation). To complicate things, the L3 integer predicates are +% \emph{very} sensitive to receiving any other kind of data, and they +% \emph{scream}. To handle this \cs{zrefcheck_get_asint:nnn} uses +% \cs{l_@@_integer_bool} to signal if an integer could not be returned. To +% use this function always set \cs{l_@@_integer_bool} to true first, then +% call it as much as you need. If any of these calls got is returning +% anything which is not an integer, \cs{l_@@_integer_bool} will have been +% set to false, and you should check that this hasn't happened before +% actually comparing the integers (\cs{bool_lazy_and:nnTF} is your friend). +% \begin{macrocode} +\bool_new:N \l_@@_integer_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_propval_tl} +% \begin{macrocode} +\tl_new:N \l_@@_propval_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[int]{\zrefcheck_get_asint:nnn} +% \begin{syntax} +% \cs{zrefcheck_get_asint:nnn} \Arg{label} \Arg{prop} \Arg{int var} +% \end{syntax} +% \begin{macrocode} +\cs_new:Npn \zrefcheck_get_asint:nnn #1#2#3 + { + \zrefcheck_get_astl:nnn {#1} {#2} { \l_@@_propval_tl } + \@@_is_integer:nTF { \l_@@_propval_tl } + { +% \end{macrocode} +% Make it an integer data type. +% \begin{macrocode} + \int_set:Nn #3 { \int_eval:n { \l_@@_propval_tl } } + } + { + \bool_set_false:N \l_@@_integer_bool + \zref@ifrefundefined {#1} +% \end{macrocode} +% Keep silent if ref is undefined to reduce irrelevant warnings in a fresh +% compilation round. Again, this is also not the point to check for undefined +% references, that's a task for \cs{@@_zcheck:nnnnn}. +% \begin{macrocode} + { } + { + \msg_warning:nnnn { zref-check } + { property-not-integer } {#2} {#1} + } + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% +% \section{User interface} +% +% +% \subsection{\cs{zcheck}} +% +% \begin{macro}[int]{\zcheck} +% The \marg{text} argument of \cs{zcheck} should not be long, since +% \cs{hyperlink} cannot receive a long argument. Besides, there is no +% reason for it to be. Note, also, that hyperlinks crossing page boundaries +% have some known issues: \url{https://tex.stackexchange.com/a/182769}, +% \url{https://tex.stackexchange.com/a/54607}, +% \url{https://tex.stackexchange.com/a/179907}. +% +% \begin{syntax} +% \cs{zcheck}\meta{*}\oarg{options}\marg{labels}\oarg{checks}\marg{text} +% \end{syntax} +% +% \begin{macrocode} +\NewDocumentCommand \zcheck + { s O { } > { \SplitList { , } } m > { \SplitList { , } } O { } m } + { \zref@wrapper@babel \@@_zcheck:nnnnn {#3} {#1} {#2} {#4} {#5} } +% \end{macrocode} +% \end{macro} +% +% +% \begin{variable} +% { +% \g_@@_id_int , +% \l_@@_checkbeg_tl , +% \l_@@_checkend_tl , +% \l_@@_link_label_tl , +% \l_@@_link_anchor_tl , +% \l_@@_link_star_tl +% } +% \begin{macrocode} +\int_new:N \g_@@_id_int +\tl_new:N \l_@@_checkbeg_tl +\tl_new:N \l_@@_checkend_tl +\tl_new:N \l_@@_link_label_tl +\tl_new:N \l_@@_link_anchor_tl +\bool_new:N \l_@@_link_star_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_zcheck:nnnnn} +% An intermediate internal function, which does the actual heavy lifting, +% and places \Arg{labels} as first argument, so that it can be protected by +% \cs{zref@wrapper@babel} in \cs{zcheck}. This is the same procedure as the +% one used in the definition of \cs{zref} in \file{zref-user.sty} for +% protection of \pkg{babel} active characters. +% +% \begin{syntax} +% \cs{@@_zcheck:nnnnn} \Arg{labels} \Arg{*} \Arg{options} \Arg{checks} \Arg{text} +% \end{syntax} +% +% \begin{macrocode} +\cs_new:Npn \@@_zcheck:nnnnn #1#2#3#4#5 + { + \group_begin: +% \end{macrocode} +% Process local options. +% \begin{macrocode} + \keys_set:nn { zref-check } {#3} +% \end{macrocode} +% Names of the labels for this zrefcheck call. +% \begin{macrocode} + \int_gincr:N \g_@@_id_int + \tl_set:Nx \l_@@_checkbeg_tl + { \@@_check_lblfmt:n { \g_@@_id_int } } + \tl_set:Nx \l_@@_checkend_tl + { \@@_end_lblfmt:n { \l_@@_checkbeg_tl } } +% \end{macrocode} +% Set checkbeg label. +% \begin{macrocode} + \zref@labelbylist { \l_@@_checkbeg_tl } { zrefcheck } +% \end{macrocode} +% Typeset \marg{text}, with hyperlink when appropriate. Even though the first +% argument can receive a list of labels, there is no meaningful way to set +% links to multiple targets. Hence, only the first one is considered for +% hyperlinking. +% \begin{macrocode} + \tl_set:Nn \l_@@_link_label_tl { \tl_head:n {#1} } + \bool_set:Nn \l_@@_link_star_tl {#2} + \zref@ifrefundefined { \l_@@_link_label_tl } +% \end{macrocode} +% If the reference is undefined, just typeset. +% \begin{macrocode} + {#5} + { + \bool_if:nTF + { + \l_@@_use_hyperref_bool && + ! \l_@@_link_star_tl + } + { + \exp_args:Nx \zrefcheck_get_astl:nnn + { \l_@@_link_label_tl } + { anchor } { \l_@@_link_anchor_tl } + \hyperlink { \l_@@_link_anchor_tl } {#5} + } + {#5} + } +% \end{macrocode} +% Set checkend label. +% \begin{macrocode} + \zref@labelbylist { \l_@@_checkend_tl } { zrefcheck } +% \end{macrocode} +% Check if \meta{labels} are defined. +% \begin{macrocode} + \tl_map_function:nN {#1} \zref@refused +% \end{macrocode} +% Run the checks. +% \begin{macrocode} + \@@_run_checks:nnV {#4} {#1} { \l_@@_checkbeg_tl } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Targets} +% +% \begin{macro}[int]{\zctarget} +% \begin{syntax} +% \cs{zctarget}\marg{label}\marg{text} +% \end{syntax} +% \begin{macrocode} +\NewDocumentCommand \zctarget { m +m } + { +% \end{macrocode} +% Group contents of \cs{zctarget} to avoid leaking the effects of +% \cs{refstepcounter} over \cs{@currentlabel}. The same care is not needed +% for \texttt{zcregion}, since the environment is already grouped. +% \begin{macrocode} + \group_begin: + \refstepcounter { zrefcheck } + \zref@wrapper@babel \@@_target_label:n {#1} + #2 + \zref@wrapper@babel + \zref@labelbylist { \@@_end_lblfmt:n {#1} } { zrefcheck } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{zcregion} +% \begin{syntax} +% |\begin{zcregion}|\marg{label} +% | ...| +% |\end{zcregion}| +% \end{syntax} +% \begin{macrocode} +\NewDocumentEnvironment {zcregion} { m } + { + \refstepcounter { zrefcheck } + \zref@wrapper@babel \@@_target_label:n {#1} + } + { + \zref@wrapper@babel + \zref@labelbylist { \@@_end_lblfmt:n {#1} } { zrefcheck } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \section{Checks} +% +% What is needed define a \pkg{zref-check} check? +% +% First, a conditional function defined with: +% +% \cs{prg_new_conditional:Npnn} \cs{@@_check_\meta{check}:nn} |#1#2 { F }| +% +% \noindent where \meta{check} is the name of the check, the first argument is +% the \Arg{label} and the second the \Arg{reference}. The existence of the +% check is verified by the existence of the function with this name-scheme +% (and signatures). As usual, this function must return either +% \cs{prg_return_true:} or \cs{prg_return_false:}. Of course, you can define +% other variants if you need them internally, it is just that what the package +% does expect and verifies is the existence of the \texttt{:nnF} variant. +% +% Note that the naming convention of the checks adopts the perspective of the +% \meta{reference}. That is, the ``before'' check should return true if the +% \meta{label} occurs before the ``reference''. +% +% The check conditionals are expected to retrieve \pkg{zref}'s label +% information with \cs{zrefcheck_get_astl:nnn} or +% \cs{zrefcheck_get_asint:nnn}. Also, technically speaking, the +% \meta{reference} argument is also a label, actually a pair of them, as set +% by \cs{zcheck}. For the ``labels'', any \pkg{zref} property in \pkg{zref}'s +% main list is available, the ``references'' store the properties in the +% \texttt{zrefcheck} list. Besides those, there is also the \texttt{lblseq} +% (fake) property (for either ``labels'' or ``references''), stored in +% \cs{g_@@_auxfile_lblseq_prop}. +% +% Second, the required properties of labels and references must be duly +% registered for \pkg{zref}. This can be done with \cs{zref@newprop}, +% \cs{zref@addprop} and friends, as usual. +% +% +% \subsection{Running} +% +% \begin{macro}{\@@_run_checks:nnn, \@@_run_checks:nnV} +% \begin{syntax} +% \cs{@@_run_checks:nnn} \Arg{checks} \Arg{labels} \Arg{reference} +% \end{syntax} +% \begin{macrocode} +\cs_new:Npn \@@_run_checks:nnn #1#2#3 + { + \group_begin: + \tl_map_inline:nn {#2} + { + \tl_map_inline:nn {#1} + { \@@_do_check:nnn {####1} {##1} {#3} } + } + \group_end: + } +\cs_generate_variant:Nn \@@_run_checks:nnn { nnV } +% \end{macrocode} +% \end{macro} +% +% +% \begin{variable} +% { +% \l_@@_passedcheck_bool , +% \l_@@_onpage_bool , +% \c_@@_onpage_checks_seq +% } +% \begin{macrocode} +\bool_new:N \l_@@_passedcheck_bool +\bool_new:N \l_@@_onpage_bool +\seq_new:N \c_@@_onpage_checks_seq +\seq_set_from_clist:Nn \c_@@_onpage_checks_seq + { above , below , before , after } +% \end{macrocode} +% \end{variable} +% +% +% Variant not provided by \pkg{expl3}. +% \begin{macrocode} +\cs_generate_variant:Nn \exp_args:Nnno { Nnoo } +% \end{macrocode} +% +% \begin{macro}{\@@_do_check:nnn} +% \begin{syntax} +% \cs{@@_do_check:nnn} \Arg{check} \Arg{label beg} \Arg{reference beg} +% \end{syntax} +% \begin{macrocode} +\cs_new:Npn \@@_do_check:nnn #1#2#3 + { + \group_begin: +% \end{macrocode} +% \meta{label beg} may be defined or not, it is arbitrary user input. Whether +% this is the case is checked in \cs{@@_zcheck:nnnnn}, and due warning already +% ensues. And there is no point in checking ``relative position'' of an +% undefined label. Hence, in the absence of |#2|, we do nothing at all here. +% \begin{macrocode} + \zref@ifrefundefined {#2} + {} + { + \tl_if_empty:nF {#1} + { + \bool_set_true:N \l_@@_passedcheck_bool + \bool_set_false:N \l_@@_onpage_bool + \cs_if_exist:cTF { @@_check_ #1 :nnF } + { +% \end{macrocode} +% ``label beg'' vs ``reference beg''. +% \begin{macrocode} + \use:c { @@_check_ #1 :nnF } + {#2} {#3} + { \bool_set_false:N \l_@@_passedcheck_bool } +% \end{macrocode} +% ``label beg'' vs ``reference end''. +% \begin{macrocode} + \exp_args:Nnno \use:c { @@_check_ #1 :nnF } + {#2} { \@@_end_lblfmt:n {#3} } + { \bool_set_false:N \l_@@_passedcheck_bool } +% \end{macrocode} +% ``label end'' \emph{may} have been created by the target commands. +% \begin{macrocode} + \zref@ifrefundefined { \@@_end_lblfmt:n {#2} } + {} + { +% \end{macrocode} +% ``label end'' vs ``reference beg''. +% \begin{macrocode} + \exp_args:Nno \use:c { @@_check_ #1 :nnF } + { \@@_end_lblfmt:n {#2} } {#3} + { \bool_set_false:N \l_@@_passedcheck_bool } +% \end{macrocode} +% ``label end'' vs ``reference end''. +% \begin{macrocode} + \exp_args:Nnoo \use:c { @@_check_ #1 :nnF } + { \@@_end_lblfmt:n {#2} } + { \@@_end_lblfmt:n {#3} } + { \bool_set_false:N \l_@@_passedcheck_bool } + } +% \end{macrocode} +% Handle option \opt{onpage=msg}. This is only granted for tests which +% perform ``within this page'' checks (\opt{above}, \opt{below}, \opt{before}, +% \opt{after}) \emph{and} if any of the two by two checks uses a ``within this +% page'' comparison. If both conditions are met, signal. +% \begin{macrocode} + \seq_if_in:NnT \c_@@_onpage_checks_seq {#1} + { + \@@_check_thispage:nnT + {#2} {#3} + { \bool_set_true:N \l_@@_onpage_bool } + \@@_check_thispage:nnT + {#2} { \@@_end_lblfmt:n {#3} } + { \bool_set_true:N \l_@@_onpage_bool } + \zref@ifrefundefined { \@@_end_lblfmt:n {#2} } + {} + { + \@@_check_thispage:nnT + { \@@_end_lblfmt:n {#2} } {#3} + { \bool_set_true:N \l_@@_onpage_bool } + \@@_check_thispage:nnT + { \@@_end_lblfmt:n {#2} } + { \@@_end_lblfmt:n {#3} } + { \bool_set_true:N \l_@@_onpage_bool } + } + } + \bool_if:NTF \l_@@_passedcheck_bool + { + \bool_if:nT + { + \l_@@_msgonpage_bool && + \l_@@_onpage_bool + } + { + \@@_message:nnnx { double-check } {#1} {#2} + { \zref@extractdefault {#3} {page} {'unknown'} } + } + } + { + \@@_message:nnnx { check-failed } {#1} {#2} + { \zref@extractdefault {#3} {page} {'unknown'} } + } + } + { \msg_warning:nnn { zref-check } { check-missing } {#1} } + } + } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Conditionals} +% +% \begin{variable} +% { +% \l_@@_lbl_int , +% \l_@@_ref_int , +% \l_@@_lbl_b_int , +% \l_@@_ref_b_int +% } +% More readable scratch variables for the tests. +% \begin{macrocode} +\int_new:N \l_@@_lbl_int +\int_new:N \l_@@_ref_int +\int_new:N \l_@@_lbl_b_int +\int_new:N \l_@@_ref_b_int +% \end{macrocode} +% \end{variable} +% +% +% \subsubsection{This page} +% +% \begin{macro}{\@@_check_thispage:nn} +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_check_thispage:nn #1#2 { T , F , TF } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int } && +% \end{macrocode} +% `0' is the default value of \texttt{abspage}, but this value should not +% happen normally for this property, since even the first page, after it gets +% shipped out, will receive value `1'. So, if we do find `0' here, better +% signal something is wrong. This comment extends to all page number checks. +% \begin{macrocode} + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{On page} +% +% \begin{macro}{\@@_check_above:nn, \@@_check_below:nn} +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_check_above:nn #1#2 { F , TF } + { + \group_begin: + \@@_check_thispage:nnTF {#1} {#2} + { + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { lblseq } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { lblseq } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } < { \l_@@_ref_int } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_below:nn #1#2 { F , TF } + { + \@@_check_thispage:nnTF {#1} {#2} + { + \@@_check_above:nnTF {#1} {#2} + { \prg_return_false: } + { \prg_return_true: } + } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Before / After} +% +% \begin{macro}{\@@_check_before:nn, \@@_check_after:nn} +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_check_before:nn #1#2 { F } + { + \@@_check_pagesbefore:nnTF {#1} {#2} + { \prg_return_true: } + { + \@@_check_above:nnTF {#1} {#2} + { \prg_return_true: } + { \prg_return_false: } + } + } +\prg_new_conditional:Npnn \@@_check_after:nn #1#2 { F } + { + \@@_check_pagesafter:nnTF {#1} {#2} + { \prg_return_true: } + { + \@@_check_below:nnTF {#1} {#2} + { \prg_return_true: } + { \prg_return_false: } + } + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Pages} +% +% \begin{macro} +% { +% \@@_check_nextpage:nn , +% \@@_check_prevpage:nn , +% \@@_check_pagesbefore:nn , +% \@@_check_ppbefore:nn , +% \@@_check_pagesafter:nn , +% \@@_check_ppafter:nn , +% \@@_check_facing:nn +% } +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_check_nextpage:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int + 1 } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_prevpage:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int - 1 } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_pagesbefore:nn #1#2 { F , TF } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } < { \l_@@_ref_int } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\cs_new_eq:NN \@@_check_ppbefore:nnF \@@_check_pagesbefore:nnF +\prg_new_conditional:Npnn \@@_check_pagesafter:nn #1#2 { F , TF } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } > { \l_@@_ref_int } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\cs_new_eq:NN \@@_check_ppafter:nnF \@@_check_pagesafter:nnF +\prg_new_conditional:Npnn \@@_check_facing:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { +% \end{macrocode} +% There exists no ``facing'' page if the document is not twoside. +% \begin{macrocode} + \legacy_if_p:n { @twoside } && +% \end{macrocode} +% Now we test ``facing''. +% \begin{macrocode} + ( + ( + \int_if_odd_p:n { \l_@@_ref_int } && + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int - 1 } + ) || + ( + \int_if_even_p:n { \l_@@_ref_int } && + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int + 1 } + ) + ) && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Close / Far} +% +% \begin{macro}{\@@_check_close:nn, \@@_check_far:nn} +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_check_close:nn #1#2 { F , TF } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \int_abs:n { \l_@@_lbl_int - \l_@@_ref_int } } + < + { \l_@@_close_range_int + 1 } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_far:nn #1#2 { F } + { + \@@_check_close:nnTF {#1} {#2} + { \prg_return_false: } + { \prg_return_true: } + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Chapter} +% +% \begin{macro} +% { +% \@@_check_thischap:nn , +% \@@_check_nextchap:nn , +% \@@_check_prevchap:nn , +% \@@_check_chapsafter:nn , +% \@@_check_chapsbefore:nn +% } +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_check_thischap:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int } && +% \end{macrocode} +% `0' is the default value of \texttt{abschap} property, and means here no +% \cs{chapter} has yet been issued, therefore it cannot be ``this chapter'', +% nor ``the next chapter'', nor ``the previous chapter'', it is just ``no +% chapter''. Note, however, that a statement about a ``future'' chapter does +% not require the ``current'' one to exist. This comment extends to all +% chapter checks. +% \begin{macrocode} + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_nextchap:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int + 1 } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_prevchap:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int - 1 } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_chapsafter:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } > { \l_@@_ref_int } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_chapsbefore:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_int } < { \l_@@_ref_int } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Section} +% +% \begin{macro} +% { +% \@@_check_thissec:nn , +% \@@_check_nextsec:nn , +% \@@_check_prevsec:nn , +% \@@_check_secsafter:nn , +% \@@_check_secsbefore:nn +% } +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_check_thissec:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abssec } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abssec } { \l_@@_ref_int } + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_b_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_b_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } && + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int } && +% \end{macrocode} +% `0' is the default value of \texttt{abssec} property, and means here no +% \cs{section} has yet been issued since its counter has been reset, which +% occurs at the beginning of the document and at every chapter. Hence, as is +% the case for chapters, `0' is just ``not a section''. The same observation +% about the need of the ``current'' section to exist to be able to refer to a +% ``future'' one also holds. This comment extends to all section checks. +% \begin{macrocode} + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_nextsec:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abssec } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abssec } { \l_@@_ref_int } + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_b_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_b_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } && + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int + 1 } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_prevsec:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abssec } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abssec } { \l_@@_ref_int } + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_b_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_b_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } && + \int_compare_p:nNn + { \l_@@_lbl_int } = { \l_@@_ref_int - 1 } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_secsafter:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abssec } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abssec } { \l_@@_ref_int } + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_b_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_b_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } && + \int_compare_p:nNn + { \l_@@_lbl_int } > { \l_@@_ref_int } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +\prg_new_conditional:Npnn \@@_check_secsbefore:nn #1#2 { F } + { + \group_begin: + \bool_set_true:N \l_@@_integer_bool + \zrefcheck_get_asint:nnn {#1} { abssec } { \l_@@_lbl_int } + \zrefcheck_get_asint:nnn {#2} { abssec } { \l_@@_ref_int } + \zrefcheck_get_asint:nnn {#1} { abschap } { \l_@@_lbl_b_int } + \zrefcheck_get_asint:nnn {#2} { abschap } { \l_@@_ref_b_int } + \bool_lazy_and:nnTF + { \l_@@_integer_bool } + { + \int_compare_p:nNn + { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } && + \int_compare_p:nNn + { \l_@@_lbl_int } < { \l_@@_ref_int } && + ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } && + ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 } + } + { \group_insert_after:N \prg_return_true: } + { \group_insert_after:N \prg_return_false: } + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \PrintIndex +% +% \end{implementation} +% diff --git a/Master/texmf-dist/source/latex/zref-check/zref-check.ins b/Master/texmf-dist/source/latex/zref-check/zref-check.ins new file mode 100644 index 00000000000..4a18782a2d1 --- /dev/null +++ b/Master/texmf-dist/source/latex/zref-check/zref-check.ins @@ -0,0 +1,98 @@ +% \iffalse meta-comment +% +% File: zref-check.ins +% +% This file is part of the LaTeX package "zref-check". +% +% Copyright (C) 2021 Gustavo Barros +% +% 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 +% Gustavo Barros. +% +% This work consists of the files zref-check.dtx, +% zref-check.ins, +% zref-check.tex, +% zref-check-code.tex, +% and the derived files zref-check.sty and +% zref-check.pdf, +% zref-check-code.pdf. +% +% The released version of this package is available from CTAN. +% +% ----------------------------------------------------------------------- +% +% The development version of the package can be found at +% +% https://github.com/gusbrs/zref-check +% +% for those people who are interested. +% +% ----------------------------------------------------------------------- +% +% \fi + +\input l3docstrip.tex + +\keepsilent + +\askforoverwritefalse + +\preamble + +This file was generated from file(s) of the LaTeX package "zref-check". + +Copyright (C) 2021 Gustavo Barros + +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 + Gustavo Barros. + +This work consists of the files zref-check.dtx, + zref-check.ins, + zref-check.tex, + zref-check-code.tex, + and the derived files zref-check.sty and + zref-check.pdf, + zref-check-code.pdf. + +The released version of this package is available from CTAN. + +----------------------------------------------------------------------- + +The development version of the package can be found at + + https://github.com/gusbrs/zref-check + +for those people who are interested. + +----------------------------------------------------------------------- + +\endpreamble + +\postamble +\endpostamble + +\generate{\file{zref-check.sty}{\from{zref-check.dtx}{package}}} + +\endbatchfile |