关于将此 DropCap/Lettrine 代码转换为宏的思考

关于将此 DropCap/Lettrine 代码转换为宏的思考

我知道有一些可用于 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但这只是一个次要问题。

据我所知,我需要以下参数:

  1. 首字下沉前的可选字符(本例中为左双引号)
  2. 首字下沉前的字符与首字下沉本身之间的可选 hskip 值
  3. 首字下沉字符
  4. “基本缩进” - 这也决定了首字下沉会突出到边距的多少。当前该值是首字下沉宽度的一小部分。(本例中为 .8)
  5. 第 1 行“移位”值
  6. 第 2 行“移位”值
  7. 第 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。因此,\ignoreptOPmac 中的宏会删除由原始pt扩展的字母 。执行后跟数据和两个逗号。它准备并执行原始并将第一行的缩进保存到。以缩进开头。因此,我们需要返回负 kern,执行并返回。通过实现\the\Capinsert\parshape\firstlineindent\noindent\firslineindent\rlap{scaled letter}\kern\firstindentscaled 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\wd0protrude\parshapeparams\hsize\parshapeparams\parshape\tmpnum0pt \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如下图:

沃尔

相关内容