调整字体大小以适合 vbox 中的长单词

调整字体大小以适合 vbox 中的长单词

目标:
在 ConTeXt 中排版简约的书籍封面,标题采用尽可能最大的字体大小但仍保留在框中。

问题:
长单词或长行不尊重框的右侧并且会延伸到页面外。

最小示例:
我发现这是 Wolfgang 的代码在邮件列表中,感谢这个答案来自@aditya。(抱歉,标题有点傻,但我想找一个我知道会延续到页面之外的词!)

\newbox\cover
\newdimen\coverheight \coverheight= 4in
\newdimen\coverwidth  \coverwidth = 7in
\newdimen\coverfont   \coverfont  = 12pt
\newdimen\fontstep    \fontstep   = 1pt

\def\startcover
  {\dostartbuffer[cover][startcover][stopcover]}

\def\stopcover
  {\setups[cover:place]}

\def\boxsize
  {\setbox\cover\vbox
     {\hsize\coverwidth
      \definedfont[Serif at \the\coverfont]\setupinterlinespace
      \setupalign[nothyphenated]
      \emergencystretch\maxdimen
      \getbuffer[cover]}}

\def\covertext
  {\boxsize\doloop
     {\boxsize
      \ifdim\ht\cover>\coverheight
        \global\advance\coverfont-\fontstep
        \exitloop
      \else
        \global\advance\coverfont\fontstep
      \fi}}

\startsetups[cover:place]
  \covertext
  \boxsize
  \vbox to\coverheight{\box\cover}
\stopsetups

\starttext

\startcover
\strut Mary Poppins Sings Supercalifragilisticexpialidocious
\stopcover

\stoptext

编译为:
在此处输入图片描述 笔记:
在上下文中(无双关语!)书籍封面,我更关心的是水平方向上文本的最大尺寸,而不是填充垂直空间。理想的做法是设置文本的最大高度和绝对宽度,并将文本放入其中,与顶部对齐。如果文本不占用最大垂直空间,那就没问题。

答案1

在这种情况下,设置会稍微复杂一些,因为我们必须想出一个全新的算法。您发布的算法将内容排版在一个框中,并循环字体大小(增加),直到达到一定的高度。在这里,我们想将内容排版在一个框中,检查它是否太宽,然后减少字体大小。这意味着我们从一个非常大的字体大小开始(我选择了 100pt)。

现在的问题是,我们在一个固定宽度的 vbox 中设置内容(\textwidth为了简单起见)。我们如何知道一行是否太宽?Victor Eijkhout 在他的书中介绍了一种方法。TeX 按主题分类他称之为\eatlines。我对此进行了修改,以测量线的宽度,并根据宽度是否满足设置布尔值。剩下要做的就是循环遍历字体大小(这里以 1pt 为步长,逐渐减小)。我还处理了字体大小减小到 0pt 以下的情​​况,在这种情况下,内容永远无法放入框中(不应该发生这种情况),但如果忽略这种情况,则会导致无限循环。

\newdimen\coverwidth  \coverwidth = \textwidth
\newdimen\coverfont   \coverfont  = 100pt
\newdimen\fontstep    \fontstep   = 1pt
\newconditional\widthmet

\def\startcover
  {\dostartbuffer[cover][startcover][stopcover]}

\def\stopcover
  {\setups[cover:place]}

\def\eatlines{%
  \global\settrue\widthmet
  \setbox\scratchboxone=\lastbox
  \ifvoid\scratchboxone\else
    \unskip\unpenalty
    {\eatlines}%
    \copy\scratchboxone
    \setbox\scratchboxtwo=\hbox{\unhbox\scratchboxone}%
    \ifdim\wd\scratchboxtwo>\coverwidth
      \global\setfalse\widthmet
    \fi
  \fi
}

\startsetups cover:place
  \start\dontcomplain
  \doloop{%
    \setbox\scratchbox=\vbox{\hsize=\coverwidth
      \definedfont[Serif at \the\coverfont]\setupinterlinespace
      \setupalign[nothyphenated]\emergencystretch\maxdimen
      \getbuffer[cover]%
      \par\eatlines
    }%
    \ifconditional\widthmet
      \box\scratchbox
      \exitloop
    \else
      \global\advance\coverfont by -\fontstep
    \fi
    \ifdim\coverfont<0pt
      \exitloop
    \fi
  }
  \stop
\stopsetups

\starttext

\startcover
Mary Poppins Sings Supercalifragilisticexpialidocious
\stopcover

\stoptext

输出(带有\showboxes):

在此处输入图片描述

ConTeXt 可以很容易地为其添加一个全面的键值接口,因此您可以使用类似

\startcover[width=2in]
  Mary Poppins Sings Supercalifragilisticexpialidocious
\stopcover

\definecover[whatever][maxsize=200pt]
\setupcover[whatever][stepsize=5pt]
\startwhatever[width=4in]
  Mary Poppins Sings Supercalifragilisticexpialidocious
\stopwhatever

这可能看起来有点夸张,但人们甚至可以更进一步将这些东西放入模块中,所以你只需\usemodule[cover]要这样做就可以了。将以下内容保存t-cover.mkiv为工作目录中的内容。

\startmodule[cover]
\unprotect

\installcorenamespace{cover}
\installcommandhandler \??cover {cover} \??cover

\startinterface all
  \setinterfaceconstant {maxsize}  {maxsize}
  \setinterfaceconstant {stepsize} {stepsize}
\stopinterface

\appendtoks
  \setuevalue{\e!start\currentcover}{\cover_start[\currentcover]}%
  \setuvalue {\e!stop\currentcover}{\cover_process}%
\to \everydefinecover

\setupcover[
  \c!width=\textwidth,
  \c!maxsize=100pt,
  \c!stepsize=1pt,
]

\unexpanded\def\cover_start
  {\bgroup\obeylines\dodoubleargument\cover_start_indeed}

\starttexdefinition cover_start_indeed [#1][#2]
  \egroup
  \edef\currentcover{#1}
  \setupcover[#1][#2]
  \grabbufferdata[coverbuffer][start#1][stop#1]
\stoptexdefinition

\unexpanded\def\cover_process{%
  \start\dontcomplain
  \scratchdimen=\coverparameter\c!maxsize
  \doloop{%
    \setbox\scratchbox=\vbox{\hsize=\coverparameter\c!width
      \definedfont[Serif at \the\scratchdimen]\setupinterlinespace
      \setupalign[nothyphenated]\emergencystretch\maxdimen
      \getbuffer[coverbuffer]%
      \par\eatlines
    }%
    \ifconditional\scratchcounter
      \box\scratchbox
      \exitloop
    \else
      \global\advance\scratchdimen by -\coverparameter\c!stepsize
    \fi
    \ifdim\scratchdimen<0pt
      \exitloop
    \fi
  }%
  \stop
}

\def\eatlines{%
  \global\settrue\scratchcounter
  \setbox\scratchboxone=\lastbox
  \ifvoid\scratchboxone\else
    \unskip\unpenalty
    {\eatlines}%
    \copy\scratchboxone
    \setbox\scratchboxtwo=\hbox{\unhbox\scratchboxone}%
    \ifdim\wd\scratchboxtwo>\coverparameter\c!width
      \global\setfalse\scratchcounter
    \fi
  \fi
}

\definecover[cover]

\protect
\stopmodule

然后你的主文件将会读取

\usemodule[cover]

\starttext

\startcover
  Mary Poppins Sings Supercalifragilisticexpialidocious
\stopcover

\stoptext

答案2

如果您不介意手动换行(对于封面来说应该不是什么大问题),并且不介意使用错误的光学尺寸(很少有字体会根据字体大小更改字形),您可以简单地排版框并适当缩放。例如:

\define[1]\FitBox
    {\scale[width=10cm,height=5cm,factor=max]{\framed[align=normal]{#1}}}


\starttext
\FitBox{Mary \\ Poppins Sings \\ Supercalifragilisticexpialidocious}

\FitBox{A \\ B \\ C \\ D \\ E \\ F \\ G \\ H \\ I \\ J \\ K \\ L \\ M \\ N}

\stoptext

相关内容