Now maybe this is very hard and more work than its worth. I read the doc on
varioref and it seems like a good approach to this kind of thing, but I'm
dealing with technical docs of over 5,000 pages (I think the memory
requirements of the varioref approach will be too much).
I don't know if there's a good way to walk through the aux file, looking for
the page on which the next section begins.
Any ideas on this project much appreciated,
--Tim Arnold
For referencing the name of a section, you can use either Donald
Arseneau's titleref-package or the nameref-package which is part
of the hyperref-bundle.
A naive approach for finding out whether a section goes over a
page-break could be:
Place label on the start of the section, place another label to
the end of the section.
(Getting the start of the section is hard - I think that you
actually would have to place a label right after the very first
letter of the section-heading...)
Create a macro which extracts the page-numbers of both labels
(page-number is the second "argument" from the expansion of
\r@<label>).
If page-numbers are equal, the section fits on one page,
otherwise it goes over a pagebreak.
(Some cases might need more special treatment:
- undefined labels
- providing labels in the wrong order
- resetting the page-number for some obscure reason
- referencing the current section.)
\documentclass[a4paper]{article}
\usepackage{nameref}
\makeatletter
%%
% This takes two labels, extracts page-numbers to \@tempa
% and \@tempb and delivers (pseudo-code):
%
% \nameref{<label1>}
% IF \@tempa=\@tempb beginning ENDIF
% on page \pageref{<label1>}
%
% In case that the second label is undefined, a warning-
% message occurs.
%%
\DeclareRobustCommand*\checkbeginref[2]{%
\nameref{#1}
% Deliver warnings in case that the second label is
% undefined:
\expandafter\ifx\csname r@#2\endcsname\relax
\protect\G@refundefinedtrue
\@latex@warning{Range-delimiting label `#2' on page
\thepage\space undefined}%
\fi
% start a group so that definitions are local
{%
% This delivers the 2nd argument from a row of arguments:
% ("borrowed" from Heiko Oberdiek's macros...)
\def\@cartwo##1##2##3\@nil{##2}%
% put page-number of first label/argument in \@tempa
\edef\@tempa{%
\expandafter\expandafter
\expandafter\@cartwo
\csname r@#1\endcsname\empty\empty\empty\@nil
}%
\edef\@tempb{%
\expandafter\expandafter
\expandafter\@cartwo
\csname r@#2\endcsname\empty\empty\empty\@nil
}%
%Compare before closing the group:
\expandafter}\ifx\@tempa\@tempb\else
beginning
\fi
on page \pageref{#1}%
}%
\makeatother
\begin{document}
\section{test section without page-break}
\label{start1} some text some text.\label{stop1}
\section{second test section with page-break}
\newcounter{loopcnt}%
\label{start2} some text some text.
\newpage
some text some text.\label{stop2}
\section{section for referencing}
\noindent let's refer to the \checkbeginref{start1}{stop1}.
\noindent let's refer to the \checkbeginref{start2}{stop2}.
\end{document}
I hope, it helps.
Sincerely
Ulrich
> I don't know if there's a good way to walk through the aux
> file, looking for the page on which the next section begins.
In case that there is some \cleardoublepage or the like after a
section for some obscure reason, the start of the next section
is not a criterium for a section without page-break.
> Now maybe this is very hard and more work than its worth. I
> read the doc on varioref and it seems like a good approach to
> this kind of thing, but I'm dealing with technical docs of
> over 5,000 pages (I think the memory requirements of the
> varioref approach will be too much).
If I got it right, the "varioref-approach" suggests dealing with
two labels per block/range. I overlooked that in my first
posting - the suggestion therein describes more or less the same
approach. Sorry. As each label causes defining a new macro while
reading the aux-file, things might get "memory-intensive".
In case that memory is an issue _but not time_, one could
implement an alternative version of the \label-mechanism which
causes the page-number and the "alternative-label"-name to be
added to an "argument-list"-macro while reading the aux-file at
start instead of creating a new macro. The crucial point is:
Each time when you wish to regain info on such a page-number,
you'll have to iterate the (possibly very long) list. This is
time-intensive... Nonetheless I took it for a nice "learning-
exercise" to implement such a "time-intensive" mechanism.
The "\label"-macro creates "\newlabel"-entries in the aux-file.
While reading the aux-file, from these \newlabel-entries macros
\r@<label's name> get defined for each label.
The alternative mechanism in the "time-intensive"-examples
below is analogous:
Using the "\setendmarker"-macro in the main-document creates a
"\addtosectionendmarkers"-entry in the aux-file which contains a
value-pair nested into one single argument:
{{name of endmarker}{page-number at endmarker}}.
When reading the aux-file, all the information provided by these
entries is gathered up into the macro "\sectionendmarkers". In
order to regain the page-number which is associated to a
section-endmarker, the \sectionendmarker-macro needs to be
iterated until the corresponding "endmarker" is found so that
the associated page-number can get extracted.
This is done by the macro "\checkbeginref": It takes two
arguments: The first is the label from the start of the section,
the second is the endmarker of the section created by
"\setendmarker". From the label, the start-page of the section
gets extracted, from the endmarker -by iterating the
\sectionendmarker-macro- the ending-page of the section gets
extracted. If both numbers are equal, there was no page-break,
otherwise there was...
In case that you decide to use the same name for \labels at
start of a section and section-endmarkers by "\setendmarker"
at the end of a section, you can do so without problems.
In this case you can also implement a macro which takes
only one argument and supplies it's value to the
"\checkbeginref" both as "start-label-argument" and as
"endmarker-argument".
I decided to stick to labels for the first argument as the first
argument determines what is "visibly" referred to. I didn't want
to also implement nameref- and hyperref-support for the
referenced argument...
You also mentioned the xr-package. Therefore I decided to
implement the "\addtosectionendmarkers"-macro in a way so that
it takes an optional argument. After patching the "\XR@test"-
macro from XR/XR-hyper as done in the examples below, the
\externaldocument-prefix gets added to "endmarker"-names via this
optional argument so that imported "endmarkers" can also be
distinguished from non-imported-ones.
Example A illustrates the usage of "\setendmarker" and
"\checkbeginref".
Example B is the same as example A except that it imports
the aux-file/labels of Example A and takes reference to
them also.
Sincerely
Ulrich
<------------------snip-mark for examplea.tex------------------>
\documentclass[a4paper]{memoir}
\usepackage{titleref}
\usepackage{xr}%
%\usepackage{xr-hyper}%
\makeatletter% You could put the stuff between \makeatletter
% and \makeatother into a .sty-file on it's own
% which must be loaded _after_ xr/xr-hyper!
%
% Patch macros from xr-package to add prefix not only to labels
% and inputs but also to "sectionendmarkers" by supplying the
% prefix as optional argument to "\addtosectionendmarkers":
%
\@ifpackageloaded{xr}%
{%
\long\def\XR@test#1#2#3#4\XR@{%
\ifx#1\newlabel
\newlabel{\XR@prefix#2}{#3}%
\else\ifx#1\@input
\edef\XR@list{\XR@list#2\relax}%
\else\ifx#1\addtosectionendmarkers
\expandafter\addtosectionendmarkers\expandafter[\XR@prefix]{#2}%
\fi\fi\fi
\ifeof\@inputcheck\expandafter\XR@aux
\else\expandafter\XR@read\fi}%
}%
\relax
%%
\@ifpackageloaded{xr-hyper}%
{%
\long\def\XR@test#1#2#3#4\XR@{%
\ifx#1\newlabel
\expandafter\protected@xdef\csname r@\XR@prefix#2\endcsname
{\XR@addURL{#3}}%
\else\ifx#1\@input
\edef\XR@list{\XR@list\filename@area#2\relax}%
\else\ifx#1\addtosectionendmarkers
\expandafter\addtosectionendmarkers\expandafter[\XR@prefix]{#2}%
\fi\fi\fi
\ifeof\@inputcheck\expandafter\XR@aux
\else\expandafter\XR@read\fi}%
}%
\relax
%
% \sectionendmarkers is a list of arguments
% {{endmarker}{page-number}}...{{endmarker}{page-number}}\@nil
%
\global\newcommand*\sectionendmarkers{\@nil}%
%
% generate an error in case of duplicate end-markers:
%
\newcommand*\@checkforduplicates[1]{{%
\edef\@tempa{\@car#1\@nil}%
\expandafter\seekendmark\expandafter{\@tempa}%
\ifx\@tempb\@empty\else
\@latex@error{Duplicate range-delimititer `\@tempa'}%
\fi
}}%
%
% This adds an element to \sectionendmarkers
% - internally used by the aux-file
% The optional argument specifies a Prefix e.g. during import
% into other files by the xr/xr-hyper-package.
%
\global\newcommand*\addtosectionendmarkers[2][]{%
{%
\def\@tempb##1{%
\def\@tempb####1####2{%
\def\@tempb{{{#1####1}{####2}}}%
}%
\@tempb##1%
}%
\@tempb#2%
% One could first browse the list and issue an error in case
% an equal-named endmarker exists, but it takes time, so you
% might comment it out:
\expandafter\@checkforduplicates\@tempb
%%%
\expandafter\expandafter
\expandafter \gdef
\expandafter\expandafter
\expandafter \sectionendmarkers
\expandafter\expandafter
\expandafter {%
\expandafter\@tempb
\sectionendmarkers}%
}%
}%
% The aux-file is re-read at the end of the document but we
% don't need to add the section-endmarkers again. This would
% take time and lead to unwanted error-messages.
\AtEndDocument{\let\addtosectionendmarkers\@gobble}%
%
% This is used to mark the end of a textblock/section by
% "telling" the aux-file to add an element to
% \sectionendmarkers:
%
\newcommand*\setendmarker[1]{%
\@bsphack
\protected@write\@auxout{}%
{\string\addtosectionendmarkers{{{#1}{\thepage}}}}%
\@esphack
}%
%
% This browses the \sectionendmarkers-macro until "marker" is
% found the first time. Then \@tempb is defined to expand to the
% associated page-number. If no marker is found, it is \@empty
% Internally used by "\checkbeginref".
%
\newcommand*\seekendmark[1]{%
{% start group for keeping assignments local
\def\@tempd{}% will later get the assigned page-value
\def\@tempa{#1}%
\def\innerseekendmark##1{%
% compare current element to \@nil to find out if
% the end of the list is reached:
\def\@tempb{##1}%
\def\@tempc{\@nil}%
\ifx\@tempb\@tempc
\else
% Put the first of the value-pair into \@tempd and test
% if it equals #1 in order to find out if the desired
% end-marker #1 was found:
\expandafter\def\expandafter\@tempd\expandafter{\@car##1\@nil}%
\ifx\@tempa\@tempd
% If it's the endmarker, set \@tempd to the second
% value/page-number...
\expandafter\def\expandafter\@tempd\expandafter{\@cdr##1\@nil}%
% ... and redefine the recursion to gobble the
% remainder of the list:
\def\innerseekendmark####1\@nil{}%
\else
% If it's not the endmarker, reset \@tempd
\def\@tempd{}%
\fi
% next iterative-call of \innerseekmark
\expandafter\innerseekendmark
\fi
}%
\expandafter\innerseekendmark\sectionendmarkers
\expandafter}% before ending the group define \@tempb
\expandafter\def % according to the value of \@tempd
\expandafter\@tempb
\expandafter{\@tempd}%
}%
%%
% This takes a label and an endmarker, extracts page-numbers
% to \@tempa and \@tempb and delivers (pseudo-code):
%
% \nameref{<label1>}
% IF \@tempa=\@tempb beginning ENDIF
% on page \pageref{<label1>}
%
% In case that the endmarker is undefined, a warning-
% message occurs.
%%
\DeclareRobustCommand*\checkbeginref[2]{%
%\nameref{#1}
\titleref{#1}
% start a group so that definitions are local
{%
% This delivers the 2nd argument from a row of arguments:
% ("borrowed" from Heiko Oberdiek's macros...)
\def\@cartwo##1##2##3\@nil{##2}%
% put page-number of first label/argument in \@tempa
\edef\@tempa{%
\expandafter\expandafter
\expandafter\@cartwo
\csname r@#1\endcsname\empty\empty\empty\@nil
}%
\seekendmark{#2}%
% Deliver warning in case that the endmarker is not found:
\ifx\@tempb\@empty
\@latex@warning{end-marker `#1' on page \thepage\space
undefined}%
\fi
%Compare before closing the group:
\expandafter}\ifx\@tempa\@tempb\else
beginning
\fi
on page \pageref{#1}%
}%
\makeatother% You could put the stuff between \makeatletter
% and \makeatother into a .sty-file on it's own
% which must be loaded _after_ xr/xr-hyper!
\begin{document}
\section{test section without page-break}
\label{section1} some text some text.\setendmarker{endsection1}
\section{test section with page-break}
\newcounter{loopcnt}%
\label{section2} some text some text.
\newpage
some text some text.\setendmarker{section2}
\section{section for referencing}
\noindent let's refer to the \checkbeginref{section1}{endsection1}.
\noindent let's refer to the \checkbeginref{section2}{section2}.
\end{document}
<-------------------end-mark for examplea.tex------------------>
<------------------snip-mark for exampleb.tex------------------>
\documentclass[a4paper]{memoir}
\usepackage{titleref}
\usepackage{xr}%
%\usepackage{xr-hyper}%
\makeatletter% You could put the stuff between \makeatletter
% and \makeatother into a .sty-file on it's own
% which must be loaded _after_ xr/xr-hyper!
%
% Patch macros from xr-package to add prefix not only to labels
% and inputs but also to "sectionendmarkers" by supplying the
% prefix as optional argument to "\addtosectionendmarkers":
%
\@ifpackageloaded{xr}%
{%
\long\def\XR@test#1#2#3#4\XR@{%
\ifx#1\newlabel
\newlabel{\XR@prefix#2}{#3}%
\else\ifx#1\@input
\edef\XR@list{\XR@list#2\relax}%
\else\ifx#1\addtosectionendmarkers
\expandafter\addtosectionendmarkers\expandafter[\XR@prefix]{#2}%
\fi\fi\fi
\ifeof\@inputcheck\expandafter\XR@aux
\else\expandafter\XR@read\fi}%
}%
\relax
%%
\@ifpackageloaded{xr-hyper}%
{%
\long\def\XR@test#1#2#3#4\XR@{%
\ifx#1\newlabel
\expandafter\protected@xdef\csname r@\XR@prefix#2\endcsname
{\XR@addURL{#3}}%
\else\ifx#1\@input
\edef\XR@list{\XR@list\filename@area#2\relax}%
\else\ifx#1\addtosectionendmarkers
\expandafter\addtosectionendmarkers\expandafter[\XR@prefix]{#2}%
\fi\fi\fi
\ifeof\@inputcheck\expandafter\XR@aux
\else\expandafter\XR@read\fi}%
}%
\relax
%
% \sectionendmarkers is a list of arguments
% {{endmarker}{page-number}}...{{endmarker}{page-number}}\@nil
%
\global\newcommand*\sectionendmarkers{\@nil}%
%
% generate an error in case of duplicate end-markers:
%
\newcommand*\@checkforduplicates[1]{{%
\edef\@tempa{\@car#1\@nil}%
\expandafter\seekendmark\expandafter{\@tempa}%
\ifx\@tempb\@empty\else
\@latex@error{Duplicate range-delimititer `\@tempa'}%
\fi
}}%
%
% This adds an element to \sectionendmarkers
% - internally used by the aux-file
% The optional argument specifies a Prefix e.g. during import
% into other files by the xr/xr-hyper-package.
%
\global\newcommand*\addtosectionendmarkers[2][]{%
{%
\def\@tempb##1{%
\def\@tempb####1####2{%
\def\@tempb{{{#1####1}{####2}}}%
}%
\@tempb##1%
}%
\@tempb#2%
% One could first browse the list and issue an error in case
% an equal-named endmarker exists, but it takes time, so you
% might comment it out:
\expandafter\@checkforduplicates\@tempb
%%%
\expandafter\expandafter
\expandafter \gdef
\expandafter\expandafter
\expandafter \sectionendmarkers
\expandafter\expandafter
\expandafter {%
\expandafter\@tempb
\sectionendmarkers}%
}%
}%
% The aux-file is re-read at the end of the document but we
% don't need to add the section-endmarkers again. This would
% take time and lead to unwanted error-messages.
\AtEndDocument{\let\addtosectionendmarkers\@gobble}%
%
% This is used to mark the end of a textblock/section by
% "telling" the aux-file to add an element to
% \sectionendmarkers:
%
\newcommand*\setendmarker[1]{%
\@bsphack
\protected@write\@auxout{}%
{\string\addtosectionendmarkers{{{#1}{\thepage}}}}%
\@esphack
}%
%
% This browses the \sectionendmarkers-macro until "marker" is
% found the first time. Then \@tempb is defined to expand to the
% associated page-number. If no marker is found, it is \@empty
% Internally used by "\checkbeginref".
%
\newcommand*\seekendmark[1]{%
{% start group for keeping assignments local
\def\@tempd{}% will later get the assigned page-value
\def\@tempa{#1}%
\def\innerseekendmark##1{%
% compare current element to \@nil to find out if
% the end of the list is reached:
\def\@tempb{##1}%
\def\@tempc{\@nil}%
\ifx\@tempb\@tempc
\else
% Put the first of the value-pair into \@tempd and test
% if it equals #1 in order to find out if the desired
% end-marker #1 was found:
\expandafter\def\expandafter\@tempd\expandafter{\@car##1\@nil}%
\ifx\@tempa\@tempd
% If it's the endmarker, set \@tempd to the second
% value/page-number...
\expandafter\def\expandafter\@tempd\expandafter{\@cdr##1\@nil}%
% ... and redefine the recursion to gobble the
% remainder of the list:
\def\innerseekendmark####1\@nil{}%
\else
% If it's not the endmarker, reset \@tempd
\def\@tempd{}%
\fi
% next iterative-call of \innerseekmark
\expandafter\innerseekendmark
\fi
}%
\expandafter\innerseekendmark\sectionendmarkers
\expandafter}% before ending the group define \@tempb
\expandafter\def % according to the value of \@tempd
\expandafter\@tempb
\expandafter{\@tempd}%
}%
%%
% This takes a label and an endmarker, extracts page-numbers
% to \@tempa and \@tempb and delivers (pseudo-code):
%
% \nameref{<label1>}
% IF \@tempa=\@tempb beginning ENDIF
% on page \pageref{<label1>}
%
% In case that the endmarker is undefined, a warning-
% message occurs.
%%
\DeclareRobustCommand*\checkbeginref[2]{%
%\nameref{#1}
\titleref{#1}
% start a group so that definitions are local
{%
% This delivers the 2nd argument from a row of arguments:
% ("borrowed" from Heiko Oberdiek's macros...)
\def\@cartwo##1##2##3\@nil{##2}%
% put page-number of first label/argument in \@tempa
\edef\@tempa{%
\expandafter\expandafter
\expandafter\@cartwo
\csname r@#1\endcsname\empty\empty\empty\@nil
}%
\seekendmark{#2}%
% Deliver warning in case that the endmarker is not found:
\ifx\@tempb\@empty
\@latex@warning{end-marker `#1' on page \thepage\space
undefined}%
\fi
%Compare before closing the group:
\expandafter}\ifx\@tempa\@tempb\else
beginning
\fi
on page \pageref{#1}%
}%
\makeatother% You could put the stuff between \makeatletter
% and \makeatother into a .sty-file on it's own
% which must be loaded _after_ xr/xr-hyper!
\externaldocument[imported]{examplea}
\begin{document}
\section{test section without page-break}
\label{section1} some text some text.\setendmarker{endsection1}
\section{test section with page-break}
\newcounter{loopcnt}%
\label{section2} some text some text.
\newpage
some text some text.\setendmarker{section2}
\section{section for referencing}
\noindent let's refer to the \checkbeginref{section1}{endsection1}.
\noindent let's refer to the \checkbeginref{section2}{section2}.
\noindent let's refer to the
\checkbeginref{importedsection1}{importedendsection1}
of the imported example~A.
\noindent let's refer to the
\checkbeginref{importedsection2}{importedsection2}
of the imported example~A.
\end{document}
<-------------------end-mark for exampleb.tex------------------>
--
Due to a need of protection against spam-e-mail, the address in
the posting-header is invalid. Here is my correct e-mail-
address: ulric...@alumni.uni-tuebingen.de
<great example snipped>
Hi Ulrich, thanks very much for taking the time to think about this.
I'm still re-reading your example and learning from it--I'm starting to
understand. The begin-and-end-label system seems the way to go. Now I'm
thinking that a run of LaTeX (or possibly two) followed by a postprocessing
step will do the end-label work for me automatically--in that way, I can
write an external program to insert the \setendmarker{label}. In fact, that
information would be good to have anyway for section encapsulation in case
we convert this to xml or html later on.
thanks again. I'm still reading your code.
--Tim Arnold
> Now I'm
> thinking that a run of LaTeX (or possibly two) followed by a postprocessing
> step will do the end-label work for me automatically--in that way, I can
> write an external program to insert the \setendmarker{label}.
Implementing that postprocessing-step sounds much more interesting than
what I did in the "\setendmarker-example"!
If I got you right, you are about to create an external program which
"reads" a LaTeX-document and detects where sections end for inserting
markers into the source at these places.
How will the end of a section in the source be detected by your
external program?
(You can probably go through the toc/aux files for detecting the start of
sections. But the start of a section does not mean in all cases that the
previous section ended on the same page/page before...
Or did you insert \newpage after each section while each section
is in the toc and no "starred" sections exist?)
[During the process you can probably gather up the
\sectionendmarkers-macro (or something similar) directly for writing
it's (global) definition to the aux- or some other external file which gets
"\inputiffileexists" within the preample of your document instead of
inserting something into the original source-file by a post-process...]
By the way:
I think I made a little mistake in the \seekendmark-macro:
old version:
new version:
\newcommand*\seekendmark[1]{%
{% start group for keeping assignments local
\def\@tempd{}% will later get the assigned page-value
\def\@tempa{#1}%
\def\innerseekendmark##1{%
% compare current element to \@nil to find out if
% the end of the list is reached:
\def\@tempb{##1}%
\def\@tempc{\@nil}%
\ifx\@tempb\@tempc
\else
\edef\@tempa{#1}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% During writing to aux, parameter is
% expanded completely, better it's done so here also.
% \csname ... in labels also expands completely...
% you can now write:
% \def\something{test}
% \setendmarker{\something}
% and you will get the endmarker "test" as you would
% obtain the label "test" from \label{\something} whereby
% \ref{\something} = \ref{test}
% Put the first of the value-pair into \@tempd and test
% if it equals #1 in order to find out if the desired
% end-marker #1 was found:
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\edef\@tempd{\@car##1\@nil}% EDEF IS FASTER!
\ifx\@tempa\@tempd
% If it's the endmarker, set \@tempd to the second
% value/page-number...
\edef\@tempd{\@cdr##1\@nil}% EDEF IS FASTER!
% ... and redefine the recursion to gobble the
% remainder of the list:
\def\innerseekendmark####1\@nil{}%
\else
% If it's not the endmarker, reset \@tempd
\def\@tempd{}%
\fi
% next iterative-call of \innerseekmark
\expandafter\innerseekendmark
\fi
}%
\expandafter\innerseekendmark\sectionendmarkers
\expandafter}% before ending the group define \@tempb
\expandafter\def % according to the value of \@tempd
\expandafter\@tempb
\expandafter{\@tempd}%
}%
Sincerely
Ulrich