24

I wish to put breadcrumb navigation with hyperlinks into the header of each page. Clicking on each part, chapter, section will take you to the beginning of that part/chapter/section.

I'm already using hyperref to link the ToC.

16
  • This sounds a lot like the 'side bar' navigation that you can get by default on most pdf viewers from latex-generated documents
    – cmhughes
    Jul 6 '13 at 18:18
  • @cmhuges Agreed, but this document will be primarily viewed from the web and Chrome's pdf viewer for example does not do sidebar nav. Jul 6 '13 at 18:20
  • Does that mean if you are in (say) section three of a document with eight sections, you'll only see links for up to section three, but if you are in section five, you'll see links up to that point? Or would you rather have all the links displayed, but the 'current' one(s) highlighted in some way?
    – jon
    Jul 6 '13 at 18:26
  • @jon It means that if you are currently in section II.3.5 then the header would look: <part II name> → <chap 3 name> → <section 5 name> as hyperlinks. Jul 6 '13 at 18:30
  • 1
    Alright, so just to clarify the desired behaviour: it needs to display the TOC-style names of all non-visible sectioning commands? That is, those sectioning commands that are not already visible on the page (as their main display) need to be visible as a header/footer? Jul 6 '13 at 23:33
15

How about automatically inserting a label at each part/chapter/section? Note this solution (modeled after How to get the section title by section number?) requires you to use part/chapter/section at least once each.


EDIT 3: Still WIP, but trying to generalize by keeping the breadcrumb as data in a sequence first, separating content from presentation. This basic tracking will enable more extensible functionality and different faces for the breadcrumbs. Those interested: keep checking GitHub for more information. (The stack code is not shown here, but can be browsed here.)


EDIT 2: Still WIP, but moved over to LaTeX3 syntax with egreg's help. Working on the previous. Bleeding edge on GitHub.


EDIT: Still a WIP, but now the breadcrumbs are variable-length per the comments. Still working on reversing the order of preference for chapterpage, sectionpage, etc, by digging through the fancyhdr sources. I'd also like the current section at page-begin to be part of the breadcrumb, unless page-begin is a section header.

bredcrmb.sty

% GOAL: introduce a \pagestyle "breadcrumb" that will place the breadcrumb as a header.
\RequirePackage{xparse}
\RequirePackage{everypage
\RequirePackage{atbegshi}
\RequirePackage{fancyhdr}

\let\myhook\AtBeginShipout % playing with the two to see if I can get the desired output

% All credit to egreg for the following generalization: (#122823)
% My only additions were the booleans and related stuff.
\ExplSyntaxOn
% Macro to insert labels at the end of other macros
\NewDocumentCommand{\labelize}{mm}
{
  \breadcrumbs_labelize:Nn #1 { #2 }
}
\myhook{\pagestyle{breadcrumb}}
\cs_new_protected:Npn \breadcrumbs_labelize:Nn #1 #2 {
  % LaTeX3-ify
  \bool_new:c { g_breadcrumbs_in_#2 }
  \myhook{
    \bool_gset_false:c { g_breadcrumbs_in_#2 }
  }

  \cs_set_eq:cN { original_ \cs_to_str:N #1 } #1
  \RenewDocumentCommand #1 { som }
  {
    % Put page style stuff here
    \bool_gset_true:c { g_breadcrumbs_in_#2 }
    \thispagestyle{#2:style}
    \IfBooleanTF{##1}
    {
      \use:c { original_ \cs_to_str:N #1 }*{##3}
    }
    {
      \IfNoValueTF{##2}
      {
        \use:c { original_ \cs_to_str:N #1 } {##3}
      }
      {
        \use:c { original_ \cs_to_str:N #1 } [##2]{##3}
      }
      \label{#2: \use:c{the\cs_to_str:N #1} }%
    }
  }
}
% Much nicer syntax.  Thanks, egreg!
\labelize {\part}    {breadcrumb:part}
\labelize {\chapter} {breadcrumb:chapter}
\labelize {\section} {breadcrumb:section}

\fancypagestyle{breadcrumb:part:style}{
  \fancyhf{}
}
\fancypagestyle{breadcrumb:chapter:style}{
  \fancyhf{}
  \chead{
    \nameref{breadcrumb:part:\thepart}
  }
}
\fancypagestyle{breadcrumb:section:style}{
  \fancyhf{}
  \chead{
    \nameref{breadcrumb:part:\thepart}~$\rightarrow$~
    \nameref{breadcrumb:chapter:\thechapter}
  }
}
\fancypagestyle{breadcrumb}{
  \fancyhf{}
  \chead{
    \nameref{breadcrumb:part:\thepart}~$\rightarrow$~
    \nameref{breadcrumb:chapter:\thechapter}~$\rightarrow$~
    \nameref{breadcrumb:section:\thesection}
  }
}
\ExplSyntaxOff

test.tex

\documentclass{memoir}

\let\footruleskip\relax

\usepackage{bredcrmb} % Moved code to style file
\usepackage{fancyhdr}
\usepackage[colorlinks]{hyperref}

\pagestyle{breadcrumb}

\usepackage{mwe}
\begin{document}

\part{Part the First}
\chapter{Chapter Primo}
\section{Section A}
\lipsum
\section{Section B}
\lipsum
\chapter{Chapter Secundo}
\section{Section A}
\lipsum
\section{Section B}
\lipsum
\part{Part the Second}
\chapter{Chapter Primo}
\section{Section A}
\lipsum
\section{Section B}
\lipsum
\chapter{Chapter Secundo}
\section{Section A}
\lipsum
\section{Section B}
\lipsum

\end{document}

enter image description here

10
  • 1
    +1 -- but I think the idea is that the breadcrumb navigation only goes as far as you are. That is, you might only be in the 'section' of text between (say) \chapter and \section, in which case, the navigation bar would only have 'part' and 'chapter' in the header. Maybe one solution for this would be to hook explicit page styles into your \chapter and \section (etc.) commands.
    – jon
    Jul 6 '13 at 20:26
  • @jon Ah - So I've missed the use-case of \chapter\lipsum\section and the like? Good idea about page styles; I'll try to incorporate that into an edit here soon. Jul 6 '13 at 20:29
  • 3
    @ScottWilton If you have a decently recent TeX distribution, it should work as-is. LaTeX3's syntax is exposed to LaTeX2e, since LaTeX3 development is a developing like a glacier (unstoppable and awe-inspiring, but slow-moving). This is the main purpose of xparse. Jul 7 '13 at 3:08
  • 1
    There exist \bool_new:c and \bool_set_false:c; you can say \bool_new:c { in_#1 } and \bool_set_false:c { in_#1} avoiding those horrible \expandafter with \cs:w. I fixed them; the conditionals should be global.
    – egreg
    Jul 7 '13 at 9:37
  • 1
    @Scott — shoot! After my sister's wedding I completely forgot! I'll start work up on it again in the next day or so—I was thinking about looking into the Beamer source files for how it handles keeping track of pages. The big block right now is hooking onto pagination Sep 8 '13 at 17:29
13

The following example uses the fact that the anchors for the section commands are set and known before the header line is build. That means with careful timing it is possible to avoid the referencing system with a second LaTeX run.

The example uses class book, where \chaptermark, \sectionmark and \subsectionmark are redefined to include \hyperlink with the current anchor \@currentHref, which is defined quite right before either via \refstepcounter or \phantomsection for unnumbered section commands. Some patching is needed to insert \partmark or to support starred section commands or commands. Also commands like \tableofcontents, which uses \@markboth, needs to be patched to get \chaptermark.

The section titles in the example are taken from the start of the user manual of pgf to get a closer real document feeling.

\documentclass{book}

\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{ragged2e}

\usepackage{etoolbox}

\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyfoot{}
\fancyhead{}
\cfoot{\thepage}
\lhead{%
  \RaggedRight
%  \HeaderFont
  \hypersetup{linkcolor=blue}%
  \rightmark
}

\newcommand*{\HeaderFont}{%
  \usefont{T1}{qhv}{c}{n}%
  \small
}
\makeatletter

\DeclareRobustCommand*{\BreadSep}{%
  \,\textbf{\guilsinglright}\hspace{0pt}\,%
}
\newcount\breadnav@level
\breadnav@level=0 %

\newcommand*{\breadnav@starmark}[2]{%
  \begingroup
    \c@secnumdepth=-9 %
    #1{#2}%
  \endgroup
}

\patchcmd{\@part}{\markboth}{%
  \partmark{#1}\@gobbletwo
}{}{}
\patchcmd{\@spart}{\normalfont}{%
  \breadnav@starmark\partmark{#1}%
  \normalfont
}{}{}
\newcommand*{\partmark}[1]{%
  \protected@xdef\CurrentPart{%
    \protect\hyperlink{\@currentHref}{%
      \ifnum\value{secnumdepth}>-2 %
        \thepart\@~%
      \fi
      #1%
    }%
  }%
  \markright{%
    \CurrentPart
  }%
}
\newcommand*{\CurrentPart}{}

\patchcmd{\@schapterhead}{\normalfont}{%
  \breadnav@starmark\chaptermark{#1}%
  \normalfont
}{}{}
\patchcmd{\tableofcontents}{\@mkboth}{%
  \breadnav@starmark\chaptermark{\contentsname}%
  \@gobbletwo
}
\renewcommand*{\chaptermark}[1]{%
  \protected@xdef\CurrentChapter{%
    \protect\hyperlink{\@currentHref}{%
      \ifnum\value{secnumdepth}>-1 %
        \if@mainmatter
          %\@chapapp~
          \thechapter.\@~%
        \fi
      \fi
      #1%
    }%
  }%
  \markright{%
    \ifx\CurrentPart\@empty
    \else
      \CurrentPart\BreadSep
    \fi
    \CurrentChapter  
  }%
}
\newcommand*{\CurrentChapter}{}

\patchcmd{\@startsection}{\@ifstar}{%
  \edef\CurrentSectionType{#1}%
  \@ifstar
}{%
  \patchcmd{\@ssect}{\@tempskipa}{%
    \expandafter\breadnav@starmark
    \csname\CurrentSectionType mark\endcsname{#5}%
    \@tempskipa
  }{}{}%
}{}
\renewcommand*{\sectionmark}[1]{%
  \protected@xdef\CurrentSection{%
    \protect\hyperlink{%
      \@currentHref
    }{%
      \ifnum\value{secnumdepth}>0 %
        \thesection.\@~%
      \fi
      #1%
    }%
  }%
  \markright{%
    \ifx\CurrentPart\@empty
    \else
      \CurrentPart\BreadSep
    \fi
    \ifx\CurrentChapter\@empty
    \else
      \CurrentChapter\BreadSep
    \fi
    \CurrentSection
  }%
}
\newcommand*{\CurrentSection}{}

\renewcommand*{\subsectionmark}[1]{%
  \protected@xdef\CurrentSubsection{%
    \protect\hyperlink{\@currentHref}{%
      \ifnum\value{secnumdepth}>1 %
        \thesubsection.\@~%
      \fi
      #1%
    }%
  }%
  \markright{%
    \ifx\CurrentPart\@empty
    \else
      \CurrentPart\BreadSep
    \fi
    \ifx\CurrentChapter\@empty
    \else
      \CurrentChapter\BreadSep
    \fi
    \ifx\CurrentSection\@empty
    \else
      \CurrentSection\BreadSep
    \fi
    \CurrentSubsection
  }%
}
\newcommand*{\CurrentSubsection}{}

\let\ps@plain\ps@fancy
\makeatother

\usepackage[colorlinks]{hyperref}
\usepackage{bookmark}
\bookmarksetup{numbered,open}

\usepackage{lipsum}

\addtolength{\textheight}{\headheight}
\setlength{\headheight}{21pt}
\addtolength{\textheight}{-\headheight}

\begin{document}
\title{Test document}
\author{Me}
\date{\today}
\maketitle
\tableofcontents
\chapter{Introduction}
\lipsum
\section{Structure of the System}
\lipsum
\section{Comparison with Other Graphics Packages}
\lipsum
\section{Utility Packages}
\lipsum
\section{How to Read This Manual}
\lipsum
\section{Authors and Acknowledgements}
\lipsum
\section{Getting Help}
\lipsum
\part{Tutorials and Guidelines}
\chapter{Tutorial: A Picture for Karl's Students}
\lipsum
\section{Problem Statement}
\lipsum
\section{Setting up the Environment}
\lipsum
\subsection{Setting up the Environment in \LaTeX}
\lipsum
\subsection{Setting up the Environment in Plain \TeX}
\lipsum
\subsection{Setting up the Environment in Con\TeX t}
\lipsum
\section{Straigt Path Construction}
\lipsum
\section{Curved Path Construction}
\lipsum
\section{Circle Path Construction}
\lipsum
\section{Rectangle Path Construction}
\lipsum
\end{document}

Result, page 8:

Result

and page 25:

Result

Discussion

IMHO the main problem is that breadcrumb headers take a lot of space as seen in the second shapshot, where the header already needs three lines.

The place can be reduced by:

  • Using a shorter separation symbol, the example uses \guilsinglright instead of the larger \rightarrow.

  • Smaller font sizes (\small or \footnotesize).

  • Condensed font. Unfortunately these are not often available for the wanted font family.

The following snapshots show the result for the condensed font of Tex Gyre Hermes in size \small if \HeaderFont is enabled in \lhead. This reduces the three lines to two of the second screen shot above:

Result

Variant

Even more clearly arranged looks the navigation path if the levels are shown in separate lines with indentation. This can be achieved by the following definition of \BreadSep (that does not support overlong titles that needs more than one line):

\DeclareRobustCommand*{\BreadSep}{%
  \\%
  \advance\breadnav@level by 1 %
  \hspace*{\breadnav@level\dimexpr1em\relax}%
  \textbf{\guilsinglright}\,%
}

Screen shot with this definition and using \small as \HeaderFont:

Result

However, this needs as much lines as supported section levels.

It would be nice if smaller headers could give the unneeded space back to the text body. But the page is build before the header and its free space is known. Remember the header size in a label raises the problem that the header can change if the page material is rearranged. Similar to package varioref this can cause documents that never stabilizes.

4
  • Very nice! Would you mind if I cannibalize this solution, at least on GitHub? Your very first point is like gold to what I've been hitting up against. Jul 7 '13 at 12:00
  • In the last example, shouldn't the breadcrumb show section 2.2.1, not 2.2.2? The first lines of text on the page are from section 2.2.1. Jul 12 '13 at 5:52
  • @RichardHansen: It shows the first section title on the page (\leftmark). Otherwise it will become very complicated. Jul 12 '13 at 6:12
  • Yeah, I tried to figure out how to do this in my version of this same question, but ran into some difficulty. I was hoping you were aware of a solution. Jul 12 '13 at 17:22
0

EDIT 1: removed unneeded package from required packages, to prevent annoying warnings in console (wasn't doing anything anyways)


I know this is pretty much a necropost, but I'm doing it anyways in case someone in the future comes across this.


I customized @SeanAllred's code (the accepted answer) with the following features, and felt I should share the code:

  • It only prints the minimum available section level.

    If you're currently in a \part, but haven't started your \chapter or \section yet, then it will print Part Name in the header instead of Part Name → ??? → ???.

    If you have started a \part and \chapter, but not \section, then it will print Part Name → Chapter Name instead of Part Name → Chapter Name → ???.

  • It will print the lowest currently available document section for a page.

    If your page starts with \chapter, then it will print Part Name → Chapter Name at the start, rather than nothing*, like the previous versions did.

  • Made it slightly easier to change the breadcrumb seperator.

  • Doesn't reset the footer before applying the breadcrumbs.

    Previously it reset the header and footer, now it resets only the header.

Since my code is far from perfect, here are some things you should keep in mind when using it:

* Currently this will print the last section on a page. So if your page has two sections, then the one printed is the last one that occurs before the page ends. This could be wanted, or unwanted, behaviour depending on what you want. Keep this in mind if you have multiple \sections on one page.

Also, the pagestyle is automatically applied to all \part, \chapter, and \section commands. (So long as they don't have an asterisk, eg. \chapter*{Some Chapter}) If you only want it to be applied to some of the sectioning commands, then you will have to modify this code.

Lastly, when one of the sectioning commands (\part, \chapter, or \section) is called, it sets the page style for all future pages. If you wish to have the page style reverted to something else afterwards, you must call the \pagestyle macro.

Here is breadcrumb.sty:

%! suppress = EscapeHashOutsideCommand
%! Package = breadcrumb
%! Author = vermiculus (Sean Allred), egreg, solonovamax, Heiko Oberdiek
%! Date = 2021-04-06

% Credits:
%  - vermiculus: Put things together & made shit work
%  - egreg: Contributed logic improvements
%  - solonovamax: added some minor tweaks + doc comments. All comments by solonovamax are signed with ``-s'' at the end.
%  - Heiko Oberdiek: stole their code for the seperator from their SO answer, since I [solonovamax] think it looks nicer.

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{breadcrumb}[2021/04/05]

\RequirePackage{xparse}
\RequirePackage{atbegshi}
\RequirePackage{fancyhdr}

% If you want to change the header style, use this:
%\DeclareRobustCommand*{\BreadSep}{%
%    ~\ensuremath{\rightarrow}~%
%}
% -s
\DeclareRobustCommand*{\BreadSep}{      % This seperator is stolen from Heiko Oberdiek.
    \,{\LARGE\textbf{\guilsinglright}}\hspace{0pt}\,%
}

\let\myhook\AtBeginShipout % playing with the two to see if I can get the desired output
\myhook{\pagestyle{breadcrumb}}

% All credit to egreg for the following generalization: (#122823)
% My [vermiculus's] only additions were the booleans and related stuff.
\ExplSyntaxOn
% Defines macro that allows you to hack the document sectioning commands. -s
\NewDocumentCommand{\labelize}{mm}{\breadcrumbs_labelize:Nn#1{#2}}

\cs_new_protected:Npn\breadcrumbs_labelize:Nn#1#2{
    % LaTeX3-ify
    \bool_new:c{g_breadcrumbs_in_#2}
    \myhook{
        \bool_gset_false:c{g_breadcrumbs_in_#2}
    }
    \cs_set_eq:cN{original_\cs_to_str:N#1}#1
    \RenewDocumentCommand#1{som}{% Put page style stuff here
        \bool_gset_true:c{g_breadcrumbs_in_#2}
%        \thispagestyle{#2:style} % This is redundant now, but didn't do much before, anyways.
%          (It only affected the \section command, since \part and \section print a new page at the start of the macro.)
%          -s
        \IfBooleanTF{##1}{
            \use:c{original_\cs_to_str:N#1}*{##3}
        }{
            \IfNoValueTF{##2}
            {\use:c{original_\cs_to_str:N#1}{##3}}
            {\use:c{original_\cs_to_str:N#1}[##2]{##3}}
%
            \label{#2:\use:c{the\cs_to_str:N#1}}%
            \thispagestyle{#2:style}% Set the style of this page, because SOME things (looking at you, \chapter) reset the style. -s
            \pagestyle{#2:style}% Set the style of all future pages. -s
        %  This means:
        %  All future pages will have the right level
        %  BUT this affects the entire document.
        %  -s
        }

    }
}

% Much nicer syntax.  Thanks, egreg!
% page styles are defined in the syntax: ``breadcrumb:[style name]:style''. You can copy & paste to define any new ones. -s
\fancypagestyle{breadcrumb:part:style}{
    \fancyhead{} % reset header -s
    \chead{\large\nameref{breadcrumb:part:\thepart}}
}%
\fancypagestyle{breadcrumb:chapter:style}{
    \fancyhead{} % reset header -s
    \chead{\large\nameref{breadcrumb:part:\thepart}\BreadSep\nameref{breadcrumb:chapter:\thechapter}}
}%
\fancypagestyle{breadcrumb:section:style}{
    \fancyhead{} % reset header -s
    \chead{\large\nameref{breadcrumb:part:\thepart}\BreadSep\nameref{breadcrumb:chapter:\thechapter}\BreadSep\nameref{breadcrumb:section:\thesection}}
}

\fancypagestyle{breadcrumb}{
    \fancyhead{} % reset header -s
    \chead{\large\nameref{breadcrumb:part:\thepart}\BreadSep\nameref{breadcrumb:chapter:\thechapter}\BreadSep\nameref{breadcrumb:section:\thesection}}
}

% This hacks the \part, \chapter, and \section commands. If you want more levels, define the new page styles and then labelize them.
\labelize{\part}    {breadcrumb:part}
\labelize{\chapter} {breadcrumb:chapter}
\labelize{\section} {breadcrumb:section}

\ExplSyntaxOff

To use it, just include

\usepackage{breadcrumb}

in your document preamble. It will do everything else.

According to the edits on the original answer, an updated version of the code posted on SO can be found on GH, but no matter what I tried, I could not get the code on GH to work properly, so I just stuck with my modified version of the original.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.