使用 `multicol` 自动生成列标题

使用 `multicol` 自动生成列标题

我正在尝试用 Latex 重现一本旧字典。它的格式如下:

  • 三栏布局
  • 的前三个字母第一的在其顶部的列条目

字典

以下是我开始编写的代码:

\documentclass[8pt]{amsbook}

\usepackage{multicol}
\usepackage{microtype}
\usepackage{lipsum}
\usepackage[margin=1cm]{geometry}

\setlength{\columnseprule}{1pt}

\begin{document}
    \bfseries
    \begin{multicols}{3}
        \begin{description}
            \item[lorem] \lipsum[1]
            \item[ipsum] \lipsum[2]
            \item[dolor] \lipsum[3]
            \item[sit] \lipsum[4]
            \item[amet] \lipsum[5]
        \end{description}
    \end{multicols}
\end{document}

我的问题是列线不在中央。此外,我不知道如何添加列标题。

试图

答案1

这是一个解决方案,它生成一个三列布局,其标题与给定图像中的标题类似。该代码适用于和类bookamsbook它使用multicol包来创建列,使用fancyhdr包来创建列标题,并提供了一个非常简单的用户界面来打印字典:

\begin{dict}
\dictentry{foo} Description text of foo.
\dictentry{bar} ...
\end{dict}

使用该multicol包使得从单个条目创建标题变得稍微复杂一些,因为它在将结果拆分到多个列和页面之前会处理整个文本。因此,我们不知道当前在哪一列或哪一页处理条目。因此,我们必须将信息转移到列标题的过程分为三步。

在第一遍中,对列进行排版,并将有关各个条目所在的列的信息写入 .aux 文件。幸运的是,multicol提供了一个包选项colaction,它通过提供一个命令\docolaction来执行条件代码(具体取决于处理此命令的列),从而实现了这一点。

在第二遍中,我们知道了条目出现的相应列号(以调用的形式\dict@entry@page{<entry number>}{<page number>))。由此,我们现在可以通过在从第三列切换到第一列时计算页码来确定每个条目的页码。同样,此信息与条目名称一起写入 .aux 文件。

最后,在第三次编译时,我们可以使用 .aux 文件中的信息,通过以下形式的宏调用来确定每页上每列的第一个和最后一个条目\dict@entry@name{<page number>}{<column number>}{<entry name>}。第三次编译运行后,标题将完全更新。

提供了两个宏\dict@name@<page>@<column>@first\dict@name@<page>@<column>@last,它们可以在列标题中使用,分别获取每列和每页的第一个和最后一个条目。标题的排版显示在中\dict@header。出于演示目的,它使用列的第一个和最后一个条目,并剪辑到三个首字母(\dict@initials)。

完整示例词典(示例文本取自这里):

\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}

输出

第 1 页的输出

第 2 页的输出

第 3 页的输出

从输出结果可以看出,仍有优化空间。例如,列首和列末的间距似乎不是最佳的。此外,第二次运行后没有出现“标签可能已更改”警告。


编辑:感谢 Frank Mittelbach 的评论,由于\docolactions 出现在项目文本的实际开始之前而导致列标题错误的问题得以修复。标题的另一个问题已修复,如果单个条目跨越多个列,就会出现该问题。

答案2

我不太了解amsbookclass,但 book class 可以自动解决您的居中问题。现在对于您的标题,我推荐使用fancyhdrpackage。我也觉得标题很大,因此我重新定义了所有标题。请参阅下面的代码。

\documentclass[8pt]{book}
\usepackage{fancyhdr}
\usepackage{multicol}
\usepackage{microtype}
\usepackage{lipsum}
\usepackage[margin=1cm]{geometry}
\newcommand{\llhead}[1]{\lhead{\LARGE#1}} % You can adjust the size by changing LARGE to any desired size.
\newcommand{\lrhead}[1]{\rhead{\LARGE#1}}
\newcommand{\lchead}[1]{\chead{\LARGE#1}}
\newcommand{\markall}[3]{\llhead{#1}\lchead{#2}\lrhead{#3}}
\setlength{\columnseprule}{1pt}

\begin{document}
\bfseries
\thispagestyle{fancy}
\markall{left head}{center head}{right head}
    \begin{multicols}{3}
        \begin{description}
            \item[lorem] \lipsum[1]
            \item[ipsum] \lipsum[2]
            \item[dolor] \lipsum[3]
            \item[sit] \lipsum[4]
            \item[amet] \lipsum[5]
        \end{description}
    \end{multicols}
\end{document}

相关内容