% \iffalse meta-comment % % File: zref-check.tex % % This file is part of the LaTeX package "zref-check". % % Copyright (C) 2021-2022 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 zref-check.dtx, % zref-check.ins, % zref-check-doc.tex, % zref-check-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/zref-check % % for those people who are interested. % % ----------------------------------------------------------------------- % % \fi \documentclass{l3doc} % Have \GetFileInfo pick up date and version data \usepackage{zref-check} \setlength{\marginparsep}{2\labelsep} \NewDocumentCommand\opt{m}{\texttt{#1}} \makeatletter \newcommand{\zcrequiredkernel}{\zrefcheck@required@kernel} \makeatother \begin{document} \GetFileInfo{zref-check.sty} \title{% The \pkg{zref-check} package% \texorpdfstring{\\{}\medskip{}}{ - }% User manual% \texorpdfstring{\medskip{}}{}% } \author{% \texorpdfstring{\texttt{gusbrs}\\[0.8em] \url{https://github.com/gusbrs/zref-check}\\ \url{https://www.ctan.org/pkg/zref-check}}{gusbrs}} \date{Version \fileversion\ -- \filedate} \maketitle \begin{abstract} \pkg{zref-check} provides an user interface for making \LaTeX{} cross-references flexibly, while allowing to have them checked for consistency with the document structure as typeset. Statements such as ``above'', ``on the next page'', ``previously'', ``as will be discussed'', ``on the previous chapter'' and so on can be given to \cs{zcheck} in free-form, and a set of ``checks'' can be specified to be run against a given \texttt{label}, which will result in a warning at compilation time if any of these checks fail. \cs{zctarget} and the \texttt{zcregion} environment are also defined as a means to easily set label targets to arbitrary places in the text which can be referred to by \cs{zcheck}. \end{abstract} \tableofcontents \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 fails, 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} \pkg{zref-check} can be loaded with the usual: \begin{syntax} \cs{usepackage}|{zref-check}| \end{syntax} The package does not accept load-time options, package options must be set using \cs{zrefchecksetup} (see Section~\ref{sec:user-interface}). \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 \zcrequiredkernel{} is required as well. 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{checks/options}\marg{labels}\marg{text} \end{syntax} Typesets \meta{text}, as given, while performing a list of \meta{checks} on each of the \meta{labels}. When \pkg{hyperref} support is enabled, \meta{text} will be made a hyperlink to \emph{the first} \meta{label} in \meta{labels}. The starred version of the command does the same as the plain one, just does not form a link. The \meta{options} are (mostly) the same as those of the package, and can be given to local effect. \meta{checks} and \meta{options} can be given side by side as a comma separated list in the optional argument. \meta{labels} is also a comma separated list. \end{function} \begin{function}{\zctarget} \begin{syntax} \cs{zctarget}\marg{label}\marg{text} \end{syntax} Typesets \meta{text}, as given, and places a pair of \texttt{zlabel}s, one at the start of \meta{text}, using \meta{label} as label name, another one (internal) at the end of \meta{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} \bigskip{} 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. \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. \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 \meta{checks} is performed against each of the \meta{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 \meta{checks} and the \meta{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}|[before]{mylabel}{...}|. 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[otherpage] \meta{label} occurs on a page different from that of \cs{zcheck}, that is, it does \emph{not} occur on \opt{thispage}. \item[pagegap] There is a page gap between \meta{label} and \cs{zcheck}, in other words, \meta{label} does \emph{not} occur on \opt{thispage}, \opt{prevpage} or \opt{nextpage}. \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}. \item[manual] A check which always fails. Can be used to keep track of relations not covered by the other regular checks. \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 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 \meta{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{infoifdraft}, \opt{warniffinal}. 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{infoifdraft} corresponds to \opt{info} if option \opt{draft} is passed to \cs{documentclass}, and to \opt{warn} otherwise. \opt{warniffinal} corresponds to \opt{warn} if option \opt{final} is (explicitly) passed to \cs{documentclass} and \opt{info} otherwise. \opt{ignore} and \opt{ok} are provided as convenience aliases for \opt{msglevel=none} for local use only. This option only affects the messages related to 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{labelseqifdraft}, \opt{msgiffinal}. 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{labelseqifdraft} corresponds to \opt{labelseq} if option \opt{draft} is passed to \cs{documentclass} and to \opt{msg} otherwise. \opt{msgiffinal} 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. \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 \meta{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 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{labelseqifdraft} or \opt{msgiffinal} 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. \section{Change history} A change log with relevant changes for each version, eventual upgrade instructions, and upcoming changes, is maintained in the package's repository, at \url{https://github.com/gusbrs/zref-check/blob/main/CHANGELOG.md}. The change log is also distributed with the package's documentation through CTAN upon release so, most likely, \texttt{texdoc zref-check/changelog} should provide easy local access to it. An archive of historical versions of the package is also kept at \url{https://github.com/gusbrs/zref-check/releases}. \end{document}