用左对齐的文本填充一个框,但如果不适合,则溢出到左侧

用左对齐的文本填充一个框,但如果不适合,则溢出到左侧

以下使用等宽字体的 ascii-art 表示显示了我实际上想要使用比例字体获得的结果:

20      «Ἀτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί,
        ὑμῖν μὲν θεοὶ δοῖεν Ὀλύμπια δώματ᾽ ἔχοντες
        ἐκπέρσαι Πριάμοιο πόλιν, εὖ δ᾽ οἴκαδ᾽ ἱκέσθαι·
      παῖδα δ᾽ ἐμοὶ λύσαιτε φίλην, τὰ δ᾽ ἄποινα δέχεσθαι,
        ἁζόμενοι Διὸς υἱὸν ἑκηβόλον Ἀπόλλωνα.»
25      Ἔνθ᾽ ἄλλοι μὲν πάντες ἐπευφήμησαν Ἀχαιοὶ
        αἰδεῖσθαί θ᾽ ἱερῆα καὶ ἀγλαὰ δέχθαι ἄποινα·
        ἀλλ᾽ οὐκ Ἀτρεΐδῃ Ἀγαμέμνονι ἥνδανε θυμῷ,
        ἀλλὰ κακῶς ἀφίει, κρατερὸν δ᾽ ἐπὶ μῦθον ἔτελλε·

这个想法是,这是一篇以诗歌形式呈现的文本,并且由于我以一种不寻常的方式呈现它(在右页上有一组几何形状相似的注释),我希望布局与标准布局略有不同。通常,人们会将文本左对齐地放在左边距,行号放在右侧的空白处。我想要做的是在左侧留出一些额外的空白 x,比如 20 毫米,并且我希望行号放在该空白处,如图所示,而不是在右侧。

我实际上会通过反复试验来设置 x 的值,以便 99% 的行都足够短以适合。但是,会有 1% 的行(例如示例中的第 23 行)太长,在这种情况下,我希望文本尽可能向左移动以使其适合。这非常难看,但这种情况很少发生,所以我不介意。(这是 Homer,由于米制,这些行的长度实际上相当均匀。)

有没有什么简单的方法可以在乳胶中做到这一点?

就这个问题而言,有两个有问题的情况我不太关心:

(1) 如果行数非常非常长(即使占据了整个左侧空白,也太长了,无法容纳),那么我猜如果有某种优雅的后备行为(例如拆分行),那将是一个加分项,但实际上,这种文本不太可能出现这种情况。我在页面上留出的空间非常大,例如,在文本的前 500 行中,只有两行的长度为 53 个字符或更长。

(2) 还存在这样的情况:文本中有行号,但文本会与行号重叠。理想情况下,软件会检测到这种情况并自动忽略行号,但同样,我认为这种情况不太可能经常发生,不值得担心。

如果我能得到一个在这些边缘情况不发生时有效的建议,我就可以开始尝试它并看看以后如何适应这些边缘情况。

因此,理想情况下,我正在寻找一种方法来定义要使用的命令:

\homer{20}{«Ἀτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί,}
...
\homer{}{παῖδα δ᾽ ἐμοὶ λύσαιτε φίλην, τὰ δ᾽ ἄποινα δέχεσθαι,}

我目前没有使用任何诗歌类型环境,并且我认为我不需要或不想使用它们。

答案1

一个简单的解决方案,并使用\homer{20}{...}您在问题中要求的语法:

\documentclass{article}
\usepackage[greek]{babel}

\begin{document}

\newcommand{\homer}[2]{\noindent\rlap{#1}\hskip 20mm minus 20mm \hbox{#2}\par}

\hsize=265pt  % Setting a smaller width for illustration.

% Just to show the allowed text width
\noindent \rule{\hsize}{0.4pt}

\homer{20}{«Ἀτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί,}
\homer{}{ὑμῖν μὲν θεοὶ δοῖεν Ὀλύμπια δώματ᾽ ἔχοντες}
\homer{}{ἐκπέρσαι Πριάμοιο πόλιν, εὖ δ᾽ οἴκαδ᾽ ἱκέσθαι·}
\homer{23}{παῖδα δ᾽ ἐμοὶ λύσαιτε φίλην, τὰ δ᾽ ἄποινα δέχεσθαι,}
\homer{}{ἁζόμενοι Διὸς υἱὸν ἑκηβόλον Ἀπόλλωνα.»}
\homer{25}{Ἔνθ᾽ ἄλλοι μὲν πάντες ἐπευφήμησαν Ἀχαιοὶ}
\homer{}{αἰδεῖσθαί θ᾽ ἱερῆα καὶ ἀγλαὰ δέχθαι ἄποινα·}
\homer{}{ἀλλ᾽ οὐκ Ἀτρεΐδῃ Ἀγαμέμνονι ἥνδανε θυμῷ,}
\homer{}{ἀλλὰ κακῶς ἀφίει, κρατερὸν δ᾽ ἐπὶ μῦθον ἔτελλε·}

\noindent \rule{\hsize}{0.4pt}

\end{document}

输出

(我给第 23 行编号以表明它也能正常工作,因为这个答案的第一个版本对于这样的行做了一些不正确的事情。)


怎么运行的:

\newcommand{\homer}[2]{\noindent\rlap{#1}\hskip 20mm minus 20mm \hbox{#2}\par}

注意,这与将 each 定义为包含以下内容的段落\rlap{#1}相同:\hbox to 0pt{#1\hss}\homer{…}{…}

  • #1假装宽度为零的框中的可选行号( ),
  • 水平跳跃默认为 20 毫米,但可以缩小,
  • ahbox是您的文本行。

当最后一个框足够长以至于总长度(20 毫米 + 您的文本行)超出线宽时,允许的收缩就会\hskip发挥作用,并根据需要减少跳过。

在您提到的两个有问题的边缘情况下,这可以合理地失败:

  • (2) 为了防止行号与数字重叠,可以将最大收缩量调小。例如,改为可确保行号至少有 4mm。(对于此字体中的两位数字\hskip 20mm minus 20mm来说\hskip 20mm minus 16mm,这正好足够了;也许4 是最宽的数字使用其他字体。)

  • (1) 在极少数情况下,即使\hskip已缩小到其最大量(无论是满量20mm还是较小的量,如16mm,无论您选择哪种),一行仍然太长,您会收到过满框警告,因此您可以手动处理它。

答案2

以下代码是否接近您的要求?

\documentclass{article}
\usepackage[greek]{babel}

\begin{document}
\begin{tabbing}
20\=\quad\=«Ἀτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί,\\
  \>\>ὑμῖν μὲν θεοὶ δοῖεν Ὀλύμπια δώματ᾽ ἔχοντες\\
  \>\>ἐκπέρσαι Πριάμοιο πόλιν, εὖ δ᾽ οἴκαδ᾽ ἱκέσθαι·\\
  \>παῖδα δ᾽ ἐμοὶ λύσαιτε φίλην, τὰ δ᾽ ἄποινα δέχεσθαι,\\
  \>\>ἁζόμενοι Διὸς υἱὸν ἑκηβόλον Ἀπόλλωνα.»\\
25\>\>Ἔνθ᾽ ἄλλοι μὲν πάντες ἐπευφήμησαν Ἀχαιοὶ\\
  \>\>αἰδεῖσθαί θ᾽ ἱερῆα καὶ ἀγλαὰ δέχθαι ἄποινα·\\
  \>\>ἀλλ᾽ οὐκ Ἀτρεΐδῃ Ἀγαμέμνονι ἥνδανε θυμῷ,\\
  \>\>ἀλλὰ κακῶς ἀφίει, κρατερὸν δ᾽ ἐπὶ μῦθον ἔτελλε·
\end{tabbing}

\end{document}

在此处输入图片描述

答案3

已编辑以解决不计算文本空白行的问题。此答案还重点关注了 OP 所希望的边距。

环境justifly使用\makeboxes 实现边缘化,并重新定义\\以继续该过程。除了简单的\end{justifly},还需要使用双 , 来结束该过程\\\\,以停止自引用迭代。

\buffwd是要求的默认缩进(当前设置为 20mm),而\linewd是正常尺寸行的默认允许宽度。

\documentclass{article}

\newcounter{stanzaline}
\newlength\linewd
\setlength\linewd{50mm}
\newlength\buffwd
\setlength\buffwd{20mm}
\let\endtest\empty
\makeatletter
\newcommand\fitline[1]{%
  \ifnum\testblank#1\endtest\endtest=0\relax
    \makebox[\buffwd][l]{}%
  \else
    \stepcounter{stanzaline}%
    \makebox[\buffwd][l]{\tiny\thestanzaline}%
  \fi
  \sbox0{#1}%
  \ifdim\wd0>\linewd\relax
    \makebox[\linewd][r]{\ignorespaces#1}%
  \else
    \makebox[\linewd][l]{\ignorespaces#1}%
  \fi}
\makeatother
\def\testblank#1#2\endtest{\ifx\endtest#10\else1\fi}
\let\svbbs\\
\newenvironment{justifly}
{\setcounter{stanzaline}{0}%
  \bgroup\def\\##1\\{\ifx\relax##1\relax\else\mbox{}\svbbs
  \fitline{##1}\expandafter\\\fi}\\}{\egroup}
\begin{document}
Here is normal text\\
Now for the poetry margins:

\noindent\rule{\buffwd}{1pt}\rule{\linewd}{3pt}

\begin{justifly}
aaa\\
This is a test\\
This is a longer test than the margin\\
\\
New verse\\
This is a test\\
This is a longer test that exceeds margin\\\\
\end{justifly}

\end{document}

在此处输入图片描述

为了让大卫更高兴,我们提供了希腊语版本(边距不同)

\documentclass{article}
\usepackage[english,greek]{babel}

\newcounter{stanzaline}
\newlength\linewd
\setlength\linewd{70mm}
\newlength\buffwd
\setlength\buffwd{30mm}
\let\endtest\empty
\makeatletter
\newcommand\fitline[1]{%
  \ifnum\testblank#1\endtest\endtest=0\relax
    \makebox[\buffwd][l]{}%
  \else
    \stepcounter{stanzaline}%
    \makebox[\buffwd][l]{\tiny\thestanzaline}%
  \fi
  \sbox0{#1}%
  \ifdim\wd0>\linewd\relax
    \makebox[\linewd][r]{\ignorespaces#1}%
  \else
    \makebox[\linewd][l]{\ignorespaces#1}%
  \fi}
\makeatother
\def\testblank#1#2\endtest{\ifx\endtest#10\else1\fi}
\let\svbbs\\
\newenvironment{justifly}
{\setcounter{stanzaline}{0}%
  \bgroup\def\\##1\\{\ifx\relax##1\relax\else\mbox{}\svbbs
  \fitline{##1}\expandafter\\\fi}\\}{\egroup}
\begin{document}
\begin{otherlanguage}{english}
Here is normal text\\
Now for the poetry margins:
\end{otherlanguage}

\noindent\rule{\buffwd}{1pt}\rule{\linewd}{3pt}

\begin{justifly}
        «Ἀτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί,\\
        ὑμῖν μὲν θεοὶ δοῖεν Ὀλύμπια δώματ᾽ ἔχοντες\\
        ἐκπέρσαι Πριάμοιο πόλιν, εὖ δ᾽ οἴκαδ᾽ ἱκέσθαι·\\
      παῖδα δ᾽ ἐμοὶ λύσαιτε φίλην, τὰ δ᾽ ἄποινα δέχεσθαι,\\
        ἁζόμενοι Διὸς υἱὸν ἑκηβόλον Ἀπόλλωνα.»\\
        Ἔνθ᾽ ἄλλοι μὲν πάντες ἐπευφήμησαν Ἀχαιοὶ\\
        αἰδεῖσθαί θ᾽ ἱερῆα καὶ ἀγλαὰ δέχθαι ἄποινα·\\
        ἀλλ᾽ οὐκ Ἀτρεΐδῃ Ἀγαμέμνονι ἥνδανε θυμῷ,\\
        ἀλλὰ κακῶς ἀφίει, κρατερὸν δ᾽ ἐπὶ μῦθον ἔτελλε·\\\\
\end{justifly}

\end{document}

在此处输入图片描述

答案4

因此,可能对你有用的简单、幼稚的方法就是用¹定义这条线

\newlength{\homermargin}
\setlength{\homermargin}{20mm}
\NewDocumentCommand{\homer}{ m m }{% ❖
   \ifvmode\else\endgraf\fi % ❶
   \hbox to\linewidth{%
      \hbox to\homermargin{#1\hfil}% ❷
      \hskip 0pt minus \homermargin % ❸
      #2\strut\hfil 
   } % ❖
}

我使用来定义,因为这样就变成了一个简单的问题,只需\NewDocumentCommand将第一个改为,就可以让你写mO{}

\homer[20]{«Ἀτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί,}
\homer{παῖδα δ᾽ ἐμοὶ λύσαιτε φίλην, τὰ δ᾽ ἄποινα δέχεσθαι,}

而不必在每一行没有数字的地方都放置空括号。

因此,这里的方法是手动为每一行创建一个框。LaTeX\mbox有一些我想要避免的魔法,所以我使用 TeX 原语\hbox并进行检查以确保我们以垂直模式而不是水平模式开始❶。

我们在左侧留出空间用于数字,并使用\hfil填充该空间 ❷ 然后我们告诉 LaTeX,在该空间之后,我们有 0pt 的空间可以缩小边距宽度 ❸ ,这样文本就可以在需要时进入左边距。否则,它会整齐地放在左边距上。

但这很简单。让我们让它更有趣。如果行号不合适,则需要转储行号;如果绝对必要,则需要换行。

所以我们要测量我们想要设置的线条的宽度。²

为此,我们可以使用 LaTeX 的\sbox机制保存参数 2 的内容。我们还将进入一个组,这样我们所有的更改都是本地的。

{%
  \sbox0{#2}%
  

我们还将保存行号的宽度

  \sbox1{#1}%

现在我们需要考虑三种情况:

  1. 线路合适,并且有空间容纳号码。
  2. 该行适合文本宽度,但我们需要减少数字
  3. 线不太合适,我们需要将其换行。

\wd0我们可以通过比较(我们保存的框的宽度\sbox0\linewidth情况\wd0>\linewidth3 的宽度)来区分 3 和 1/2 。

  \ifdim\wd0 > \linewidth

在这种情况下,我们将重新设置参数并#2进行一些轻微的格式更改(我们不会进入左边距,我们将通过 em 缩进连续行,并使所有内容都正确排列)。 \hbox to\linewidth{% \hbox to\homermargin{#1\hfil}% \vtop{% ❹ \hsize\linewidth \addtolength{\hsize}{-\homermargin}% ❺ \parindent-10pt \leftskip 10pt \rightskip 0pt plus1fil #2\strut % ❻ }% }

\vtop是 的一种特殊形式,\vbox它与内容的第一条基线对齐³。我们将调整 hsize 以包含足够的空间用于左侧边距。❺ 我们需要\strut在末尾添加 ❻,因为由于我们将比正常深度更大的内容放入垂直列表中,如果最后一行不包含降部,LaTeX 将不知道我们实际上需要更多空间,并且间距会偏离。放置\strut一个具有足够高度和深度的隐形规则,以确保我们的行距保持一致。

现在我们可以考虑其他的可能性。我们将框的宽度与线条相加,框的宽度与线条号相加。

  \else
    \dimen0=\wd0
    \advance\dimen0\wd1
    \advance\dimen0 0.25em % ❼

标记为 ❼ 的行是可选的,但它确保文本和数字之间至少有一点空白,我选择了 ¼em,这是罗伯特·莫里斯 (Robert Morris) 之后的精细排版从业人员使用的标准空格宽度。⁴ 然后我们可以比较\dimen0一下\linewidth是否合适:

    \ifdim\dimen0 < \linewidth

如果确实合适,我们就可以放心地设置我们的线路。我们将重新使用上面创建的框来设置输出

      \hbox to \linewidth{%
        \hbox to \homermargin{%
           \box1\hfil
        }%
        \hskip 0pt minus\homermargin
        \box0\hfil
      }

如果不适合,我们会做几乎相同的事情,但不设置数字。我们也可以删除整个第一个框,只使用更宽的\hskip左缩进:

   \else
      \hbox to \linewidth{%
        \hskip \homermargin 
        \hskip 0pt minus\homermargin % ❽
      } 

我们必须写两个\hskips ❽,因为虽然我们可以在 后面使用长度minus来表示可收缩性,但如果我们试图minus在 后面添加\hskip\homermargin,TeX 将无法意识到它是为了修改 的长度\homermargin。我们必须做一些粘合算术来结合固定宽度和可收缩性,坦率地说,在这里写两个 s 更容易\hskip

最后,我们结束了所有的如果和我们开始的小组。

    \fi
  \fi
 }

我正在考虑添加额外的修改来自动编号行,但我将其留给读者作为练习。


  1. 您没有指定,所以我假设您使用的是 LaTeX。如果不是,请在评论中告诉我,我会调整为 TeX 基元定义。其中大部分已经使用 TeX 基元定义,因为实际上并没有足够的复杂性来从 expl3 甚至一些 LaTeX 私有命令中获得简化。

  2. 以下所有内容将替换标有 ❖ 的行之间的所有内容,包括这些行。

  3. 事实上,这要复杂一些,但这就是我们在这里使用它的原因。

  4. Computer Modern 使用 ⅓em 间距,这种间距相当宽,但符合 19 世纪建立的间距标准,当时排字员是按行获得报酬的,因此他们认为用宽间距填充他们的作品是合理的。

  5. 可以这么说。

相关内容