我知道有一些可用于 Dropped Capitals 的软件包,尤其是用于 LaTeX 的 lettrine.sty 软件包。但是,我正在从基础开始学习 TeX,并决定自己弄清楚如何做。我已经编写了一个可以工作且足够灵活地满足我的需求的实现,现在我想将其转换为宏,但我不确定如何最好地做到这一点。首先,这里有一个示例(我使用的是 csplain,因此您需要使用 运行此 MWE pdftex --fmt=pdfcsplain
):
\input opmac
\input cs-schola
{\dimen0 = 3.67\baselineskip
\divide \dimen0 by 65536
\setbox0 = \vbox{\hbox{\valign{%
#\vfil\cr
\hbox{\typobase\typoscale[\magstep1/]``}\cr
\noalign{\hskip 1pt}
\hbox{\typobase\typosize[\number\dimen0/]W}\cr}}}
\dimen1 = .8\wd0 % base indent
\dimen0 = \hsize \advance\dimen0 by -\dimen1 % base line length
\hbox to \dimen1{\hss\box0}\par % shifts DropCap into left margin slightly
\vskip-3.0\baselineskip % pushes text up next to DropCap
\dimen2 = \dimen1 \advance\dimen2 by 1pt % line 1 indent
\dimen3 = \dimen0 \advance\dimen3 by -1pt % line 1 length
\dimen4 = \dimen1 \advance\dimen4 by -3pt % line 2 indent
\dimen5 = \dimen0 \advance\dimen5 by 3pt % line 2 length
\dimen6 = \dimen1 \advance\dimen6 by -7pt % line 3 indent
\dimen7 = \dimen0 \advance\dimen7 by 7pt % line 3 length
\parshape = 4 \dimen2 \dimen3 \dimen4 \dimen5 \dimen6 \dimen7 0pt \hsize
\noindent{\caps\rm e're here,''} Mother called out, waking me from my nap on the back
seat of the car. When you're a kid who can get carsick just being driven around the
block, you learn to sleep as much as possible on long motor trips. This one was
{\caps\rm 139} curvy miles. Our {\caps\rm 1928} black Buick sedan crunched up the
gravel driveway to the {\em cottage}. Well, that's what summer homes were called then,
even large ones like the Schuneman's. We were in {\em Eagles~Mere,} a Victorian village
in the Pocono Mountains of Pennsylvania $\ldots$\par}
\bye
此实现对于 DropCap 深度 3 行来说相当硬编码,但我可以接受这个限制。大多数代码对于字体和大小都是“通用的”,但 dimen2 到 dimen7 除外,它们总是需要根据字体进行调整。行
\divide \dimen0 by 65536
\hbox{\typobase\typosize[\number\dimen0/]W}
如果我能弄清楚如何让 OPmac 兑现承诺,那么就可以改进,\ptunit
但这只是一个次要问题。
据我所知,我需要以下参数:
- 首字下沉前的可选字符(本例中为左双引号)
- 首字下沉前的字符与首字下沉本身之间的可选 hskip 值
- 首字下沉字符
- “基本缩进” - 这也决定了首字下沉会突出到边距的多少。当前该值是首字下沉宽度的一小部分。(本例中为 .8)
- 第 1 行“移位”值
- 第 2 行“移位”值
- 第 3 行“移位”值
如果我做的事情明显是错误的或不好的,或者有一个更简单更明显的实现方式,请指出。
我并不是要求别人为我编写宏(除非你愿意与我们分享),而是要求一些实际执行的想法。
答案1
当我编写宏时,我首先考虑的是用户界面。为了您的目的,我们建议\Capinsert
在段落之前使用以下宏:
\Capinsert First letter is scaled, this means F in this case.
用户可以插入可选的左侧材料。我们可以\optdef
使用
OPmac 技巧 0067但是可选参数是用括号隔开的[]
,用户可以在这里写\thefontscale[...]
(例如),而且括号[]
不能嵌套。因此,用户可能会感到困惑。这就是我建议用作{}
可选参数的原因。例如:
\Capinsert {material llaped to the left} First letter is scaled ...
这个决定需要更多的宏编程:
\def\Capinsert{\def\leftCapmaterial{}\futurelet\next\CapinsertA}
\def\CapinsertA{\ifx\next\bgroup \expandafter\CapinsertB \else \expandafter\CapinsertC \fi}
\def\CapinsertB #1{\def\leftCapmaterial{#1}\CapinsertC}
\def\CapinsertC #1{...
\Capinsert
检查\futurelet
是否有开括号\bgroup
。如果是,则\CapinsertB
扫描可选参数并将其保存到
\leftCapmaterial
宏中。否则,\CapinsertC
在实现宏的核心处进行处理。
现在,我们建议数据声明的方式。它可能看起来像:
\newdimen\ptem \ptem=.1em
\newdimen\Capsize \Capsize=44\ptem
\newdimen\Capabove \Capabove=8\ptem
\declCap {default} {0;0,0,0}
\declCap A {1;6,2,-2}
...
\declCap W {3;0,4,6}
...
首先,\ptem
这里声明了。使用 10pt 字体时,它是 1pt。但是,如果用户\typosise[12/14]
在数据声明之前声明(例如)并且整个文档被缩放,那么用于字母校正的单位也会被缩放。这是期望的行为。意思\ptem
是“pt 单位依赖于 em”。
声明\Capsize
缩放字母的大小。并\Capabove
声明缩放字母在段落第一条基线上方的升高量。如果它为零,则缩放字母的顶部等于第一条基线。
有关字母的数据按以下方式声明:
\declCap <letter> {protrude left; first line left, second line left, etc.}
{default}
当字母没有自己的声明时使用数据。逗号分隔的列表可以包含任意数量的数字。段落中的相应行数将被移动。数字可以包括小数点,可以是负数,并且以单位\ptem
表示。
宏代码如下。\declCap
仅将数据保存到名为 的宏中\cap:=<letter>
。\sxdef
这里使用了来自 OPmac 的:
\def\declCap #1#2{\sxdef{cap:=#1}{#2}}
读取\CapinsertC
要缩放的字母#1
并执行以下操作:
\def\CapinsertC #1{\par
\isdefined{cap:=#1}\iftrue \edef\tmp{\csname cap:=#1\endcsname}%
\else \edef\tmp{\csname cap:=default\endcsname}\fi
\setbox0=\hbox{{\thefontsize[\expandafter\ignorept\the\Capsize]#1}}%
\expandafter \CapinsertD \tmp,,%
\noindent\kern-\firstlineindent
\rlap{\kern-\protrudeCap\ptem\llap{\leftCapmaterial}%
\vbox to0pt{\kern-\Capabove\box0\vss}}%
\kern\firstlineindent
}
包括\tmp
声明的数据或默认值。包括\box0
缩放后的字母。\thefontsize
参数需要不带的 dimen pt
。因此,\ignorept
OPmac 中的宏会删除由原始pt
扩展的字母 。执行后跟数据和两个逗号。它准备并执行原始并将第一行的缩进保存到。以缩进开头。因此,我们需要返回负 kern,执行并返回。通过实现\the
\Capinsert
\parshape
\firstlineindent
\noindent
\firslineindent
\rlap{scaled letter}
\kern\firstindent
scaled letter
\kern-\protrudeCap\ptem \llap{left material}\vbox{shifted \box0}
最后一件事是扫描表格准备的数据
\CapinsertD protrude left; first line left, second line left, ... ,,%
这是通过以下方式完成的:
\def\CapinsertD #1;{\tmpnum=1 \let\firstlineindent=\undefined
\def\parshapeparams{}\def\protrudeCap{#1}\CapinsertE}
\def\CapinsertE #1,{\ifx,#1,\parshape =\tmpnum \parshapeparams 0pt \hsize
\else
\advance\tmpnum by1
\tmpdim=\wd0 \advance\tmpdim by-#1\ptem \advance\tmpdim by-\protrudeCap\ptem
\edef\parshapeparams{\parshapeparams\the\tmpdim}%
\ifx\firstlineindent\undefined \let\firstlineindent\parshapeparams \fi
\advance\tmpdim by-\hsize \tmpdim=-\tmpdim
\edef\parshapeparams{\parshapeparams\the\tmpdim}%
\expandafter \CapinsertE \fi
}
首先,\CapinsertD
读取protrude left
,将其保存到。 、的\protrudeCap
初始值在这里设置。在循环中读取行缩进。 当为空时,即在附加到数据的最后两个逗号之间,循环结束。counts 缩进的行数加 1。缩进是通过减去数据项减去来计算的。结果加到。接下来,将行宽计算为 减去缩进并将其也加到 parshapeparams 中。 当循环结束时,我们已经为原始数据做好了准备。只有被添加到前面并被附加(这声明了正常行)。\tmpnum
\firstlineindent
\parshapeparams
\CapinsertE
#1
\tmpnum
\wd0
protrude
\parshapeparams
\hsize
\parshapeparams
\parshape
\tmpnum
0pt \hsize
最后,工作示例如下:
\input opmac
\input cs-schola
%\typosize[12/15]
%% macros
\def\declCap #1#2{\sxdef{cap:=#1}{#2}}
\def\Capinsert{\def\leftCapmaterial{}\futurelet\next\CapinsertA}
\def\CapinsertA{\ifx\next\bgroup \expandafter\CapinsertB \else \expandafter\CapinsertC \fi}
\def\CapinsertB #1{\def\leftCapmaterial{#1}\CapinsertC}
\def\CapinsertC #1{\par
\isdefined{cap:=#1}\iftrue \edef\tmp{\csname cap:=#1\endcsname}%
\else \edef\tmp{\csname cap:=default\endcsname}\fi
\setbox0=\hbox{{\thefontsize[\expandafter\ignorept\the\Capsize]\Capprefix#1}\kern\Capafter}%
\expandafter \CapinsertD \tmp,,%
\noindent\kern-\firstlineindent
\rlap{\kern-\protrudeCap\ptem\llap{\leftCapmaterial}%
\vbox to0pt{\kern-\Capabove\box0\vss}}%
\kern\firstlineindent
}
\def\CapinsertD #1;{\tmpnum=1 \let\firstlineindent=\undefined
\def\parshapeparams{}\def\protrudeCap{#1}\CapinsertE}
\def\CapinsertE #1,{\ifx,#1,\parshape =\tmpnum \parshapeparams 0pt \hsize
\else
\advance\tmpnum by1
\tmpdim=\wd0 \advance\tmpdim by-#1\ptem \advance\tmpdim by-\protrudeCap\ptem
\edef\parshapeparams{\parshapeparams\the\tmpdim}%
\ifx\firstlineindent\undefined \let\firstlineindent\parshapeparams \fi
\advance\tmpdim by-\hsize \tmpdim=-\tmpdim
\edef\parshapeparams{\parshapeparams\the\tmpdim}%
\expandafter \CapinsertE \fi
}
\def\hboxshift#1#2{\vbox to0pt{\vss\hbox{#2}\kern-#1}}
%% data declarations:
\newdimen\ptem \ptem=.1em
\newdimen\Capsize \Capsize=44\ptem
\newdimen\Capabove \Capabove=8\ptem
\newdimen\Capafter \Capafter=1\ptem
\def\Capprefix{\localcolor\Red}
\declCap {default} {0;0,0,0}
\declCap W {3;0,4,6}
\declCap A {1;6,2,-2}
\declCap L {0;9,0,0}
% \declCap B ... etc.
%% document:
\Capinsert {\thefontscale[\magstep3]\localcolor\Grey\hboxshift{4pt}{``}\kern.1em}
W{\caps\rm e're here,''}
Mother called out, waking me from my nap on the back seat of the car. When
you're a kid who can get carsick just being driven around the block, you
learn to sleep as much as possible on long motor trips. This one was
{\caps\rm 139} curvy miles. Our {\caps\rm 1928} black Buick sedan crunched
up the gravel driveway to the {\em cottage}. Well, that's what summer homes
were called then, even large ones like the Schuneman's. We were in {\em
Eagles~Mere,} a Victorian village in the Pocono Mountains of Pennsylvania
$\ldots$
\Capinsert Another example is here.
Mother called out, waking me from my nap on the back seat of the car. When
you're a kid who can get carsick just being driven around the block, you
learn to sleep as much as possible on long motor trips. This one was
{\caps\rm 139} curvy miles. Our {\caps\rm 1928} black Buick sedan crunched
up the gravel driveway to the {\em cottage}. Well, that's what summer homes
were called then, even large ones like the Schuneman's. We were in {\em
Eagles~Mere,} a Victorian village in the Pocono Mountains of Pennsylvania
$\ldots$
\Capinsert Lettrine package does something similar.
Mother called out, waking me from my nap on the back seat of the car. When
you're a kid who can get carsick just being driven around the block, you
learn to sleep as much as possible on long motor trips. This one was
{\caps\rm 139} curvy miles. Our {\caps\rm 1928} black Buick sedan crunched
up the gravel driveway to the {\em cottage}. Well, that's what summer homes
were called then, even large ones like the Schuneman's. We were in {\em
Eagles~Mere,} a Victorian village in the Pocono Mountains of Pennsylvania
$\ldots$
\bye
编辑:我添加了全局参数\Capafter
,\Capprefix
如下图: