% \iffalse % This is versonotes.dtx, which supports adding notes on verso pages, % opposite annotation on the recto pages. Still sketchy %% %% Release version 0.2, 2015 February 16. %% %% Copyright 2014, 2015 Norman Gray %% %% This work may be distributed and/or modified under the %% conditions of the LaTeX Project Public License, either version 1.3 %% of this license or (at your option) any later version. %% The latest version of this license is in %% http://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 has the LPPL maintenance status `maintained'. %% %% The Current Maintainer of this work is Norman Gray %% %% This work consists of the files versonotes.dtx and versonotes.ins %% and the derived file versonotes.sty. %% %%%% File: versonotes.dtx %<+package|driver|example>%%%% Source: c64b7a550082, 2015-02-16T18:33:46+00:00 %% %<+package>\NeedsTeXFormat{LaTeX2e} %<+package>\ProvidesPackage{versonotes}[2015/02/16 v0.2] % %<*driver> \documentclass{ltxdoc} \title{versonotes: Notes on verso pages} \author{Norman Gray\\\url{http://nxg.me.uk}} \date{Release 0.2} \usepackage{url} \newcommand\Lopt[1]{\textsf {#1}} % options \newcommand\file[1]{\texttt {#1}} \newcommand\Lcount[1]{\textsl {\small#1}} \newcommand\Lenv[1]{\texttt{\{#1\}}} %environments \newcommand\Lpackage[1]{\{\textsf{#1}\}} %packages %Make command strings easier to write %{\catcode`\<=\active \gdef<#1>{\meta{#1}}} {\catcode`\<=\active \gdef<#1>{{\ensuremath\langle\normalfont\textsl{#1}\ensuremath\rangle}}} \def\cmd{\begingroup \catcode`\\=12 \catcode`\{=12 \catcode`\}=12 \catcode`\<=\active \catcode`\|=12 \docmd} \def\docmd|#1|{\texttt{#1}\endgroup} %%% \url macro (url.sty does this better) %\def\setpathdots{\discretionary{.}{}{.}} %\def\setpathslash{\discretionary{/}{}{/}} %{\catcode`\.=\active % \catcode`\/=\active % \gdef\pathcats{% % \catcode`\%=12 \catcode`\~=12 % \catcode`\.=\active \let.\setpathdots % \catcode`\/=\active \let/\setpathslash % \catcode`\#=12 \catcode`\_=12}% % } %\def\setpath#1{\ttfamily <\nobreak #1\nobreak>\endgroup} %\def\url{\begingroup\pathcats\setpath} \def\bs{$\backslash$} % Uncomment \OnlyDescription to skip the implementation \OnlyDescription \begin{document} \maketitle %\tableofcontents %\bigskip \DocInput{versonotes.dtx} \end{document} % % % \fi % % \noindent This package allows you to place notes on the verso pages % of an otherwise single-sided document. If, in the run % of text, you include a call to the macro % |\versonote{This is a remark}|, % then that text will be placed on the opposite (ie, `verso') page, % lined up with the macro call. % The notes may contain more than one paragraph. % % All the pages in the document appear as recto pages % (ie, right-hand pages), % and are numbered as such; % the document should be formatted with that in mind. % % Since there is no page opposite the first page, any notes which are % positioned to appear opposite page 1 are silently discarded. % % To invoke the package, use the command |\usepackage{versonotes}|. % % The dimensions |\versoleftmargin| and |\versotextwidth| specify the % position of the text block on the verso page. The defaults for % these are based on the |\textwidth| and |\evensidemargin| of the % document at the time the package is loaded. % By default, verso paragraphs are set ragged-left; with the % declarations in |\versolayout| setting the paragraph parameters. % You may reset the first two dimensions with for example % |\versoleftmargin=4cm|, and use |\renewcommand\versolayout{...}| to % override the paragraph formatting (this will typically require % changing |\rightskip|, |\leftskip| and |\parfillskip|). For more % elaborate effects (for example changing the note font size and % spacing), you will need to redefine |\versolayout|. % % If two notes come too close after one another, the second would % overprint the first if we did not take steps to avoid this. By % default, they are separated from each other by a short horizontal % line, but if the \Lopt{mergecollisions} option is provided when % loading the package, then such notes are instead merged into a % single block of text. If a sequence of notes needs to be merged, % then they will be added to the merged block one at a time in % successive \LaTeX\ runs, since merging one note may make it % unnecessary to merge the next. If a large block of such notes need % to be merged, it will therefore take several runs before the package % finds a stable state. If you find yourself inconvenienced by this, % it sounds as if you are preparing a critical edition, and you might % want to venture into a closer relationship with the % \Lpackage{eledmac} % package.\footnote{\texttt{http://www.ctan.org/pkg/eledmac}} % % \subsection*{Notes} % \begin{itemize} % \item The package uses the |\pdfsavepos| primitive, and so requires % \texttt{pdflatex} or \texttt{xelatex} (and \texttt{luatex}?). % % \item The notes are handled using the |.aux| file, and so (i) they will % only appear on the second and subsequent runs; and (ii) they will be % located opposite the position where the |\versonote| macro was % called on the \emph{previous} run. % % \end{itemize} % \StopEventually{} % \newpage % \section{Implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % The package contains a single user-visible command, and three % configuration parameters. % % Configuration options: % \begin{macrocode} \def\verso@resetmergestate{\verso@mergestate=2} \DeclareOption{mergecollisions}{\def\verso@resetmergestate{\verso@mergestate=0}} \ProcessOptions % \end{macrocode} % % The single user-visible command is |\versonote|. % Use |\unexpanded| (from e-\TeX) so that macros in the argument are % not expanded into the aux file. % \begin{macrocode} \long\def\versonote#1{\@bsphack \pdfsavepos \write\@auxout{\string\verso@note{\the\c@page}{\the\pdflastypos sp}\unexpanded{{#1}}}% \@esphack} % \end{macrocode} % % Now the layout parameters, and defaults. % The dimensions |\versoleftmargin| and |\versotextwidth| specify the % position of the text block on the verso page, and the declarations % in |\versolayout| set the paragraph parameters. % \begin{macrocode} \newdimen\versotextwidth \versotextwidth=\textwidth \newdimen\versoleftmargin \versoleftmargin=\paperwidth \advance\versoleftmargin -\textwidth \advance\versoleftmargin -\evensidemargin \advance\versoleftmargin -\hoffset \advance\versoleftmargin -1in \def\versolayout{ \leftskip=0pt plus 1fil \rightskip=0pt \parfillskip=0pt \parindent=0pt \parskip=\medskipamount } % \end{macrocode} % % Add a sentinel at the end of the document % \begin{macrocode} \def\end@versonote{% \write\@auxout{\string\verso@note{\the\c@page}{-1pt}{}}} \AtEndDocument{\end@versonote} % \end{macrocode} % % Define various bits of working storage. % \begin{macrocode} \newtoks\verso@pages \newtoks\verso@currentpage \newcount\verso@currentpagenum \verso@currentpagenum=1 \newcount\verso@currentnotenum \verso@currentnotenum=0 \newdimen\verso@spacerskip \verso@spacerskip=10pt % \end{macrocode} % Flag |\if@verso@processversonotes| is set true if there are any % |\verso@note|s found in the aux file. If there are none -- so that % this is still false at the first |\shipout| -- then the package % carefully does nothing. % \begin{macrocode} \newif\if@verso@processversonotes \@verso@processversonotesfalse % \end{macrocode} % % We need to be able to calculate our position on the page. % The |\verso@pagebottom| is the position of the bottom of the page, % in the same coordinates as |\verso@currentposition|, and is % (textheight + top margin + \dots). % \begin{macrocode} \newdimen\verso@pagebottom \verso@pagebottom=1in \advance\verso@pagebottom \topskip \advance\verso@pagebottom \topmargin \advance\verso@pagebottom \headheight \advance\verso@pagebottom \headsep \advance\verso@pagebottom \textheight \newdimen\verso@currentposition % \end{macrocode} % The paper size options to class files set |\paperheight| but not (or % not necessarily) |\pdfpageheight|. If we don't set this, then % output vertical positions will come out incorrectly if the latter % parameter doesn't default to the right value. The code in this package is % specific to pdf\TeX and friends, so we don't have to conditionalise. % \begin{macrocode} \pdfpageheight=\paperheight % \end{macrocode} % % Macro |\verso@note| is what's written to the aux file, and so is % what we process on the next run, to discover the set of verso-notes % we need to produce. % \begin{macrocode} \long\def\verso@note#1#2#3{% \global\@verso@processversonotestrue % \end{macrocode} % Store the target page number, and issue a warning if this was % indicated to appear on the (ignored) first page; such notes are % silently discarded below. % \begin{macrocode} \@tempcnta=#1 \ifnum\@tempcnta=1 \PackageWarning{versopages}{page 1 verso text will be ignored} \fi % \end{macrocode} % Gather pages from the .aux file, assembling them into a single list % in |\verso@pages| with one group per page, each containing a % sequence of |\\{...}| note specifications. This inserts empty % groups for pages which have no verso-notes. % \begin{macrocode} \@tempdima=#2 % #2 < 0pt is the end-of-document flag \loop \@tempswafalse \ifnum\@tempcnta>\verso@currentpagenum \@tempswatrue \fi \ifdim\@tempdima < 0pt \@tempswatrue \@tempdima=0pt \fi \if@tempswa \edef\@tempa{\the\verso@pages {\the\verso@currentpage}} \global\verso@pages=\expandafter{\@tempa} \advance\verso@currentpagenum 1 \verso@currentpage={} \repeat \verso@currentpage=\expandafter{\the\verso@currentpage \\{{#1}{#2}{#3}}} } % \end{macrocode} % % Disable |\verso@note| in time for the end-document aux reading. % This isn't quite necessary, but it's tidy. % \begin{macrocode} \AtEndDocument{\long\def\verso@note#1#2#3{}} % \end{macrocode} % % Test whether we want to merge the current note with the following % one. The arguments to this macro are the contents of % |\verso@mergenotelist|, which is assembled from the % |\verso@mergenote| commands read from the aux file. % \begin{macrocode} \def\verso@testmergenote#1,#2\@nil{ \def\@tempa{#1} \ifx\@tempa\@empty \@tempcnta=-1 \else \@tempcnta=#1 \fi \advance\@tempcnta -1 \ifnum\@tempcnta=\verso@currentnotenum % \end{macrocode} % Set |\if@tempswa| true, set |\verso@mergenotelist| to the tail of % the list, and ensure that this note is merged next time round. % \begin{macrocode} \@tempswatrue \xdef\verso@mergenotelist{#2} \immediate\write\@auxout{\string\verso@mergenote{#1}} \else \@tempswafalse \fi } % \end{macrocode} % % The macro |\verso@setnote| is the one which does the work of % positioning a single note on the (verso) page. We potentially work % quite hard to avoid notes overwriting each other here. If % |\verso@mergestate| is greater than one (by default, it's % initialised to~2, but any number greater than~1 would be equivalent) % then when two notes collide, we simply put a horizontal bar between % them, and hope for the best. Each time we have a collision we % increment this number, and if we find it has a value~1 (ie, the % first collision) we we record in the aux file that this note should % be merged with its predecessor, by writing a |\verso@mergenote| % command. Next time round, when we are about to set the note % \emph{before} that one, we avoid setting it, and instead start % accumulating the text of such merged notes, in the token register % |\verso@accumulatenote|. We only add a single note to the list of % merged notes on each \LaTeX\ run, since merging one note may make it % unnecessary to merge any more. The only way to delete the % collections of merged notes is to delete the aux file. % \begin{macrocode} \newcount\verso@mergestate \verso@resetmergestate \newtoks\verso@accumulatenote \verso@accumulatenote={} % \end{macrocode} % % Set the note. The arguments are (1) the page number, (2) the % position on the page, and (3) the text of the note. % \begin{macrocode} \long\def\verso@setnote#1#2#3{ \@tempdima=\paperheight \advance\@tempdima -#2 % target position; will become skip to add \@tempdimb=\verso@currentposition \global\advance\verso@currentnotenum 1 % \end{macrocode} % Check whether we're to merge the following note into this one. % \begin{macrocode} \expandafter\verso@testmergenote\verso@mergenotelist,\@nil \if@tempswa \verso@accumulatenote=\expandafter{% \the\verso@accumulatenote #3% \quad\P~\nobreak} %\PackageInfo{versonotes}{\typeout{Merging notes on p#1}} \else % \end{macrocode} % No merging: set the current note, possibly along with any % accumulated notes, in a box, and then start to work out where to put it. % \begin{macrocode} \setbox0=\vtop{ \versolayout \the\verso@accumulatenote #3\par} \verso@accumulatenote={} % \end{macrocode} % We now know how big this box is (|\ht0|), how far down the page the % accumulated boxes are (|\verso@currentposition|), and where the box % is to appear, as an offset from the top of the paper (|\@tempdima|). % Work how how much of a skip we have to add to get there. % \begin{macrocode} \ifdim\@tempdima>\verso@currentposition % \end{macrocode} % The target position is below the current position; add a skip. This % is the straightforward case, and leaves the skip in |\@tempdima| and % the new current position in |\@tempdimb|. % \begin{macrocode} \advance\@tempdima -\verso@currentposition \advance\@tempdima -\ht0 \advance\@tempdimb \@tempdima \verso@resetmergestate \else % \end{macrocode} % Or the target position is above the current position; % thus the new material will have to be moved down % (I'm not sure how best to handle the case where this skip results % in the note hitting the bottom of the page. Should it be merged % with that case below, somehow?). % \begin{macrocode} % note: \PackageWarning seems to produce odd effects here \message{^^Jversonotes warning: Moved versonote text on p.#1.} \advance\verso@mergestate 1 \ifnum\verso@mergestate=1 \immediate\write\@auxout{\string\verso@mergenote{\the\verso@currentnotenum}} \fi \nointerlineskip \vbox to \verso@spacerskip{\vfil \hbox to \hsize{\hfil\vrule height 0.4pt width 10em} \vfil} \advance\@tempdimb \verso@spacerskip \@tempdima=0pt % ...so don't add any more skip \fi \advance\@tempdimb \ht0 \advance\@tempdimb \dp0 % \end{macrocode} % Parameter |\@tempdimb| now holds the expected new value of % |\verso@currentposition|. Check to see if this would be beyond the bottom % of the page, and move the text upwards in that case. % \begin{macrocode} \ifdim\@tempdimb>\verso@pagebottom \message{^^Jversonotes warning: versonote text on p.#1 hit bottom of page.} \advance\@tempdima -\@tempdimb \advance\@tempdima \verso@pagebottom \@tempdimb=\verso@pagebottom \fi % \end{macrocode} % Finally, do the skip and add the box. % \begin{macrocode} \vskip\@tempdima \verso@currentposition=\@tempdimb \nointerlineskip \box0 \fi } % \end{macrocode} % % Log notes where a previous run has discovered they collide. % \begin{macrocode} \def\verso@mergenotelist{} \def\verso@mergenote#1{\xdef\verso@mergenotelist{\verso@mergenotelist #1,}} % \end{macrocode} % % Save the original definition of |\shipout|, prior to our % redefining it below. We could do this perfectly well using the % \Lpackage{everyshi} package, but we do it by hand in order to avoid % the extra dependency. % \begin{macrocode} \let\verso@orig@shipout\shipout % \end{macrocode} % % % Process a single page of verso-notes. % Macro |\verso@processonepage@| consumes the next group in the % |\verso@pages| list. This is invoked as part of the redefined |\shipout|. % \begin{macrocode} \long\def\verso@processonepage@#1#2\@nil{ \def\@tempa{#1} % \end{macrocode} % We can't have verso-notes opposite the first page. Any notes % which would appear in such a position are silently discarded. % \begin{macrocode} \ifnum\c@page>1 % \end{macrocode} % If there are no verso-notes on this page, then ship out an empty box. % \begin{macrocode} \ifx\@tempa\@empty \verso@orig@shipout\vbox{} % \end{macrocode} % This is the normal case: % create a box containing the |\\{...}| contents of the % current page's verso-notes % \begin{macrocode} \else \begingroup % \end{macrocode} % Within the box we're about to create, we have to `un-protect' things. % Robust commands (including for example |\emph| and other font-changing commands) are % defined using |\protect|, which in the present context, inside % |\@outputpage|, is |\let| equal to |\noexpand|. % We must set this to be simply |\relax|, % as it is in the normal run of text elsewhere % (see \url{http://tex.stackexchange.com/questions/206673/}). % \begin{macrocode} \let\protect\relax % \end{macrocode} % Set everything up for setting the verso page. % \begin{macrocode} \hoffset=-1in \advance\hoffset \versoleftmargin \voffset=-1in \hsize=\versotextwidth \topskip=0pt \topmargin=0pt \headheight=0pt \headsep=0pt \vsize=1000mm % larger than any actual note \normalfont\normalsize % \end{macrocode} % Construct and ship out the page contents. % \begin{macrocode} \long\def\\##1{\verso@setnote ##1} \verso@orig@shipout\vbox{ \verso@currentposition=0pt #1} \endgroup \fi \fi % \end{macrocode} % Finally, set |\verso@pages| to the tail of the list. % \begin{macrocode} \global\verso@pages={#2}} % \end{macrocode} % % The command |\verso@processonepage| is invoked once on every |\shipout|. % \begin{macrocode} \def\verso@processonepage{ \if@verso@processversonotes \expandafter\verso@processonepage@\the\verso@pages{}{}\@nil \else % we don't seem to have any versonotes in this file: don't come here again \global\let\verso@processonepage\relax \fi } % \end{macrocode} % % And finally, redefine |\shipout|. % \begin{macrocode} \def\shipout{\verso@processonepage \verso@orig@shipout} % \end{macrocode} % % That's all. % \begin{macrocode} % % \end{macrocode} % \Finale % \endinput