Here's a solution that produces a three-column layout with headers similiar to the one in the given image. The code works with both the book
and amsbook
classes, it uses the multicol
package for creating the columns and the fancyhdr
package for the column headers, and provides a quite simple user interface for printing the dictionary:
\begin{dict}
\dictentry{foo} Description text of foo.
\dictentry{bar} ...
\end{dict}
Using the multicol
package makes creating the headers from the individual entries a bit more complicated because it processes the whole text before it splits the result across several columns and pages. Because of that, we do neither know in what column nor on what page an entry is processed at the moment. We thus have to make the shifting of information to the column headers a three-pass process.
On the first pass the columns are typeset and information about the columns the individual entries occur in is written to the .aux file. Fortunately, multicol
provides a package option colaction
which faciliates exactly that by providing a command \docolaction
to execute conditional code depending on the column this command is processed in.
On the second pass we know the corresponding column numbers (the start of) an entry occurs in (in form of \dict@entry@page{<entry number>}{<page number>)
calls). From that we can now determine the page number of each entry by counting page numbers up whenever a switch from column three to column one occurs. Again, this information is written to the .aux file along with the entry name.
Finally, on the third pass we can use the information from the .aux file to determine the first and last entry in each column on each page from macro calls of the form \dict@entry@name{<page number>}{<column number>}{<entry name>}
. After the third compilation run the headers are fully updated.
Two macros \dict@name@<page>@<column>@first
and \dict@name@<page>@<column>@last
are provided that can be used in the column header to get the first and last entries of each column and page, respectively. The typesetting of the headers appears in \dict@header
. For demonstration purpose it uses both the first and last entry of a column, clipped to the three initial letters (\dict@initials
).
Full example dictionary (sample text taken from here):
\documentclass{amsbook}
\usepackage[colaction]{multicol}
\usepackage{fancyhdr}
\usepackage[margin=1cm,bottom=2cm,a6paper,landscape]{geometry}
\makeatletter
\newcounter{dict@page}
\newcounter{dict@entry}
\newcounter{dict@lastcol}
% Called from .aux file to link page number with each entry number
\newcommand\dict@entry@page[2]{%
\expandafter\gdef\csname dict@#1@page\endcsname{#2}%
}
% Called from .aux file to link entry names with each page and column number
\newcommand\dict@entry@name[3]{%
\global\let\dict@ifrerunrequired=\iffalse
\unless\ifcsname dict@name@#1@#2@first\endcsname
\expandafter\gdef\csname dict@name@#1@#2@first\endcsname{#3}%
\fi
\expandafter\gdef\csname dict@name@#1@#2@last\endcsname{#3}%
}
\newcommand\dict@curname@first{???}
\newcommand\dict@curname@last{???}
% Create column header output for column number #1
\newcommand\dict@header[1]{%
% If name is defined for column, use it ...
\ifcsname dict@name@\arabic{page}@#1@first\endcsname
\edef\dict@temp{\csname dict@name@\arabic{page}@#1@first\endcsname}%
\edef\dict@temp{\expandafter\dict@initials\expandafter{\dict@temp}}%
\xdef\dict@curname@first{\noexpand\MakeUppercase{\dict@temp}}%
% ... otherwise use name of previous column
\else
\expandafter\gdef\expandafter\dict@curname@first\expandafter{\dict@curname@last}%
\fi
\ifcsname dict@name@\arabic{page}@#1@last\endcsname
\edef\dict@temp{\csname dict@name@\arabic{page}@#1@last\endcsname}%
\edef\dict@temp{\expandafter\dict@initials\expandafter{\dict@temp}}%
\xdef\dict@curname@last{\noexpand\MakeUppercase{\dict@temp}}%
\else
\expandafter\gdef\expandafter\dict@curname@last\expandafter{\dict@curname@first}%
\fi
% Output the column header box
\makebox[\dimexpr(\textwidth-2\columnsep)/3][c]{%
\textbf{\dict@curname@first--\dict@curname@last}%
}%
}
% Get first three letters/tokens of entry name
\newcommand\dict@initials[1]{\dict@initials@#1\relax\relax\relax\@end}
\def\dict@initials@#1#2#3#4\@end{#1#2#3}
% Header setup
\fancyhead[L]{\dict@header1}
\fancyhead[C]{\dict@header2}
\fancyhead[R]{\dict@header3}
\renewcommand\headrulewidth{1.2pt}
\headsep=8pt
\headheight=20pt
\thispagestyle{fancy}
\pagestyle{fancy}
% Column setup
\columnsep=12pt
\columnseprule=1.2pt
% User interface
\newenvironment{dict}{%
\setcounter{dict@lastcol}{0}%
\setcounter{dict@page}{\value{page}}%
\begin{multicols}{3}
\begin{list}{}{%
\labelwidth=0pt
\leftmargin=1em
\itemindent=-\leftmargin
\let\makelabel=\descriptionlabel
\parsep=0pt
}
\leftmargin=1.5em
\footnotesize
}{%
\end{list}
\end{multicols}
}
\newcommand\dictentry[1]{%
\stepcounter{dict@entry}%
\item[#1]%
\leavevmode
% Figure out page breaks from column numbers
\docolaction{%
\ifnum\value{dict@lastcol}=3
\stepcounter{dict@page}
\fi
\setcounter{dict@lastcol}{1}%
}{\setcounter{dict@lastcol}{2}}{\setcounter{dict@lastcol}{3}}%
% Write page number for each entry
\immediate\write\@auxout{%
\string\dict@entry@page{\arabic{dict@entry}}{\arabic{dict@page}}%
}%
% If page number is available, write entry name for each page and column number
\ifcsname dict@\arabic{dict@entry}@page\endcsname
\immediate\write\@auxout{%
\string\dict@entry@name{\arabic{dict@page}}{\arabic{dict@lastcol}}{#1}%
}%
\fi
\ignorespaces
}
\makeatother
\begin{document}
\begin{dict}
\dictentry{a} Creates a link around SVG elements
\dictentry{altGlyph} Provides control over the glyphs used to render particular character data
\dictentry{altGlyphDef} Defines a substitution set for glyphs
\dictentry{altGlyphItem} Defines a candidate set of glyph substitutions
\dictentry{animate} Defines how an attribute of an element changes over time
\dictentry{animateMotion} Causes a referenced element to move along a motion path
\dictentry{animateTransform} Animates a transformation attribute on a target element, thereby allowing animations to control translation, scaling, rotation and/or skewing
\dictentry{circle} Defines a circle
\dictentry{clipPath} Clipping is about hiding what normally would be drawn. The stencil which defines what is and what isn't drawn is called a clipping path
color-profile Specifies a color profile description (when the document is styled using CSS)
\dictentry{cursor} Defines a platform-independent custom cursor
\dictentry{defs} A container for referenced elements
\dictentry{desc} A text-only description for container elements or graphic elements in SVG (user agents may display the text as a tooltip)
\dictentry{ellipse} Defines an ellipse
\dictentry{feBlend} Composes two objects together according to a certain blending mode
\dictentry{filter} Container for filter effects
\dictentry{font} Defines a font
\dictentry{g} Used to group together elements
\dictentry{glyph} Defines the graphics for a given glyph
\dictentry{glyphRef} Defines a possible glyph to use
\dictentry{image} Defines an image
\dictentry{line} Defines a line
\dictentry{linearGradient} Defines a linear gradient. Linear gradients fill the object by using a vector, and can be defined as horizontal, vertical or angular gradients.
\dictentry{marker} Markers can be placed on the vertices of lines, polylines, polygons and paths. These elements can use the marker attributes "marker-start", "marker-mid" and "marker-end"' which inherit by default or can be set to 'none' or the URI of a defined marker. You must first define the marker before you can reference it via its URI. Any kind of shape can be put inside marker. They are drawn on top of the element they are attached to
\dictentry{mask} Masking is a combination of opacity values and clipping. Like clipping you can use shapes, text or paths to define sections of the mask. The default state of a mask is fully transparent which is the opposite of clipping plane. The graphics in a mask sets how opaque portions of the mask are
\dictentry{metadata} Specifies metadata
\dictentry{path} Defines a path
\dictentry{pattern} Defines the coordinates you want the view to show and the size of the view. Then you add shapes into your pattern. The pattern repeats when an edge of the view box (viewing area) is hit
\dictentry{polygon} Defines a graphic that contains at least three sides
\dictentry{polyline} Defines any shape that consists of only straight lines
\dictentry{radialGradient} Defines a radial gradient. Radial gradients are created by taking a circle and smoothly changing values between gradient stops from the focus point to the outside radius.
\dictentry{rect} Defines a rectangle
\dictentry{script} Container for scripts (e.g., ECMAScript)
\dictentry{set} Sets the value of an attribute for a specified duration
\dictentry{stop} The stops for a gradient
\dictentry{style} Allows style sheets to be embedded directly within SVG content
\dictentry{svg} Creates an SVG document fragment
\dictentry{text} Defines a text
\dictentry{tref} References any text element in the SVG document and reuse it
\dictentry{tspan} Identical to the text element but can be nested inside text tags and inside itself
\dictentry{use} Uses a URI to reference a g, svg or other graphical element with a unique id attribute and replicate it. The copy is only a reference to the original so only the original exists in the document. Any change to the original affects all copies.
\end{dict}
\end{document}
outputs



As you can see from the output, there's still room for optimizations. For example, the spacing at the begin and end of the columns doesn't seem optimal. Also, there's no "Label(s) may have changed" warning after the second pass.
EDIT: Thanks to Frank Mittelbach's comment the problem of wrong column headers due to \docolaction
s happening before the actual start of an item's text could be fixed. Another issue with the headers was fixed, which occured if a single entry spanned more than one column.
description
environment? – siracusa Nov 2 '19 at 9:31description
was just an idea. – user152057 Nov 2 '19 at 9:46