5

The following ascii-art representation using a monospaced font shows the result I actually want to get with a proportional font:

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

The idea is that this is a text that's in verse, and because of the visual logic of the unusual way that I'm presenting it (with a geometrically similar set of glosses on the facing right-hand page), I want to do the layout somewhat differently than what is standard. Normally one lays out the text left-justified at the left margin, with the line numbers in the whitespace on the right. What I want to do instead is have some additional whitespace x, say 20 mm, on the left, and I want the line numbers in that whitespace, as shown, rather than on the right.

What I would actually do is to set the value of x by trial and error so that 99% of lines would be short enough to fit. However, there will be 1% of the lines, such as line 23 in the example, that are too long, and in those cases I want the text to scoot over as much as necessary to the left in order to make it fit. This is ugly as hell, but it will happen so rarely that I don't mind. (This is Homer, and the lines are actually quite uniform in length because of the meter.)

Is there any simple way to do this in latex?

There are two problematic cases that I don't really care about much for the purposes of this question:

(1) In the case where the line is really, really long -- too long to fit even if it invades the entire left-hand whitespace -- then I guess it would be a bonus if there were some kind of graceful fallback behavior such as splitting the line, but realistically this just isn't likely to happen with this text. The amount of space I've allowed on the page is very generous, and, e.g., out of the first 500 lines of the text, only two lines have lengths of 53 characters or greater.

(2) There is also the case where there is a line number and the text would overlap with the line number. Ideally the software would just detect this and automatically omit the line number, but again, I don't think this is likely to happen often enough to be worth worrying about.

If I can get a suggestion that works when these edge cases don't occur, I can start playing with that and see how to accommodate those edge cases later.

So maybe ideally I'm looking for a way to define a command to be used like this:

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

I'm currently not using any of the verse-type environments, and I don't think I need or want to use them.

2
  • I assume all the line breaking is manual/explicit ? Jul 14, 2021 at 19:46
  • @DavidCarlisle: Yes.
    – user6853
    Jul 14, 2021 at 23:32

4 Answers 4

5

A simple solution, and using the \homer{20}{...} syntax that you asked for in the question:

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

output

(I numbered line 23 to show that it works fine too, because the very first version of this answer did something incorrect for such lines.)


How it works:

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

This—note that \rlap{#1} is the same as \hbox to 0pt{#1\hss}—defines each \homer{…}{…} to be a paragraph containing:

  • the optional line number (#1) in a box that pretends to have zero width,
  • a horizontal skip that is 20mm by default, but can shrink,
  • a hbox which is your line of text.

When the last box is long enough that the total length (20mm + your line of text) exceeds the line width, then the shrink allowed in the \hskip comes into play, and decreases the skip by as much as necessary.

This can be made to fail reasonably gracefully in the two problematic edge cases you mentioned:

  • (2) To prevent the line from silently overlapping the number, you can make the maximum shrink smaller. For example, changing \hskip 20mm minus 20mm to \hskip 20mm minus 16mm would make sure the line number has at least 4mm. (This happens to be more than enough for two digits in this font; perhaps 4 is the widest digit in some other font.)

  • (1) In the unlikely case that a line is too long even after the \hskip has shrunk by its maximum amount (either the full 20mm or a smaller amount like 16mm, whichever you chose), you get an overfull box warning, so you can manually deal with it.

2
  • 1
    This worked great for me, and I like it because it's simple, so it's easy to understand. If I want to modify it later, that's doable because I understand it. Ευχαριστώ! (That means thanks in Greek.) I was able to handle problematic case #2 from the question simply by counting characters in my ruby script that generates the latex code. If there are 53 or more characters in the line, then it omits the line number. This means that I can just do 20mm minus 20mm rather than guessing the maximum width of a line number.
    – user6853
    Jul 15, 2021 at 0:18
  • @BenCrowell That's great! So I changed the answer back to just say 20mm minus 20mm in the main text for even more simplicity, leaving the 16mm trick to a note. :-) Jul 15, 2021 at 0:36
5

Is the following code close to your request?

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

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

\end{document}

enter image description here

3
  • The OP wants the line numbers on the left, and for long lines (like line numbered 23 above) to intrude into that space. This solution (as of now) seems to do neither of those. Jul 14, 2021 at 20:26
  • @ShreevatsaR, thanks for the explanation.
    – citsahcots
    Jul 14, 2021 at 20:43
  • 2
    Ah great, I think the output is as desired now (per my understanding), but I think the OP also preferred the overflow-to-the-left on line 23 to happen automatically (rather than manually specifying \> instead of \>\> for such lines). Jul 14, 2021 at 20:52
3

EDITED to address not counting blank lines of text. This answer also focuses on the margination desired by the OP.

The justifly environment uses \makeboxes to achieve the margination, and redefines \\ to continue the process. Rather than a mere \end{justifly}, one also needs to end the process with a double \\\\, to stop the self-referencing iteration.

\buffwd is the default indent asked for (set currently to 20mm), while \linewd is the default allowed width for a normal-sized line.

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

enter image description here

And just to make David more happy, a rendition in Greek (with different margin widths)

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

enter image description here

4
  • 1
    Your text is true poetry, worthy of comparison with Homer. Jul 14, 2021 at 21:14
  • @DavidCarlisle Thank you, but I'm sure it looks even better in Greek. And, BTW, would that be Homer Simpson to whom you refer? D'oh!! Jul 14, 2021 at 21:21
  • 2
    I translated the first line of your poem into Greek for you: ααα.
    – user6853
    Jul 15, 2021 at 0:10
  • @BenCrowell And didn't I say that it would look better in Greek? Huh? Tell me I wasn't right! Dayum! Jul 15, 2021 at 8:58
2

So the simple, naïve approach that might do the job for you is to define the line with¹

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

I'm using \NewDocumentCommand to define because then it becomes a simple matter of changing the first m to O{} to enable you to write

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

and not have to put empty braces on every line without a number.

So the approach here is to manually create a box for each line. LaTeX's \mbox has a bit of magic that I want to avoid so I'm using the TeX primitive \hbox and putting in a check to make sure that we start in vertical mode and not horizontal mode ❶.

We leave space on the left for the number and use an \hfil to fill that space ❷ then we tell LaTeX that after that space we have 0pt of space that can shrink by the margin width ❸ so that the text will go into the left margin if it needs to. Otherwise it sits neatly even on the left margin.

But that was easy. Let's make this more fun. You wanted to dump the line number if it didn't fit and wrap the line if it was absolutely necessary.

So we'll want to measure the width of the line that we want to set.²

To do this, we can save the contents of argument 2 with LaTeX's \sbox mechanism. We're also going to go inside a group so all our changes are local.

{%
  \sbox0{#2}%
  

We'll also save the width of the number of the line

  \sbox1{#1}%

Now we have three cases to consider:

  1. The line fits and there's room for the number.
  2. The line fits in the text width but we need to drop the number
  3. The line doesn't fit and we need to wrap the line.

We can discriminate between 3 and 1/2 by comparing \wd0 (the width of the box we saved with \sbox0 with \linewidth If \wd0>\linewidth we're in case 3.

  \ifdim\wd0 > \linewidth

In this case, we'll re-set the argument from #2 with some slight formatting changes (we'll not go into the left margin and we'll indent continuation lines by an em, plus make everything ragged right). \hbox to\linewidth{% \hbox to\homermargin{#1\hfil}% \vtop{% ❹ \hsize\linewidth \addtolength{\hsize}{-\homermargin}% ❺ \parindent-10pt \leftskip 10pt \rightskip 0pt plus1fil #2\strut % ❻ }% }

A \vtop ❹ is a special form of a \vbox that aligns on the first baseline of its contents³. We'll adjust the hsize to include enough space for the margin at the left. ❺ We need the \strut ❻ at the end because, since we're throwing something with a greater than normal depth into the vertical list, LaTeX won't know that we actually want a little more space if the last line doesn't include descenders and the spacing will be off. The \strut puts an invisible rule with sufficient height and depth in place to make sure our line spacing stays consistent.

Now we can consider the other possibilities. We'll add up the width of the box with the line and the box with the line number.

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

The line marked ❼ is optional, but it makes sure that there's at least a little white space between the text and the number, I went with a ¼em which is the standard width of a space among the fine typography practitioners working in the wake of Robert Morris.⁴ We can then compare \dimen0 to \linewidth to see if we fit:

    \ifdim\dimen0 < \linewidth

If we do fit, we can set our line with no worries. We'll re-use the boxes that we created above to set the output

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

If we don't fit, we'll do nearly the same thing but not set the number. We can also drop that whole first box and just use a wider \hskip for the left indent:

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

We have to do two \hskips ❽ because while we can use a length after the minus to indicate shrinkability, if we try to add a minus after \hskip\homermargin TeX will not realize that it was meant to modify the length of \homermargin. We would have to do some glue arithmetic to combine the fixed width and shrinkability and frankly it's just easier to write two \hskips here.

Finally, we wrap up all our ifs and the group we started.

    \fi
  \fi
 }

I was considering adding an additional modification to auto-number the lines, but I'll leave that as an exercise for the reader.


  1. You don't specify, so I'm assuming you're using LaTeX. If not, let me know in a comment and I'll adjust to TeX-primitive definitions. Most of this is defined using TeX primitives already since there's not really enough complexity to get simplification from expl3 or even some of the LaTeX private commands.

  2. All of this that follows replaces everything between the lines marked ❖ including those lines.

  3. It's a bit more involved than that, but that's what we're using it for here.

  4. Computer Modern uses a ⅓em space which is rather wide, but conforms to the spacing standards established in the nineteenth century when compositors were paid by the line and felt justified⁵ in padding out their work with wide spaces.

  5. So to speak.

2
  • Surely it's an oversight and I didn't try the "more fun" solution, but I think your simple approach doesn't actually work BTW: it doesn't seem to overflow on the left, but on the right (but without an overfull box warning). Because \hskip 0pt minus \homermargin will never skip to the left; it will only pretend that shrink is happening. (I had a similar bug in the first version of my answer.) Jul 14, 2021 at 22:52
  • Hmm, I’ll have to check that. I think I know what’s going on there as I think about it and a variation of the more fun answer would do the trick. I suspect it’s because of the positive stretch on the right side. I usually write these off the top of my head so this sort of thing can happen.
    – Don Hosek
    Jul 14, 2021 at 23:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy