[Tex/LaTex] one-letter word at the end of line

line-breakingtypography

This question led to a new feature in a package:
impnattypo

I come from Poland and I'm writing some text in my native language. One of our typography standards is that we do not leave one-letter words at the end of a line. For example:

Mietek poszedl do sklepu i
kupil jabola

is illegal, and correctly it should be as follows:

Mietek poszedl do sklepu 
i kupil jabola

I don't know if there is such a rule in US/English typography also, but I cannot find any latex setting that would fix such errors.
The only solution I've found so far is to add the "~" character, which seems to work as whitespace, but also prevents to break connected words between lines.
So I have to write:

Mietek poszedl do sklepu i~kupil jabola

Is there any other way to do so? I've tried with

\widowpenalty10000  
\clubpenalty10000 

which prevents orphans, but it seems that 'orphans' for us (Polish) is something different than for Americans 😉 In the US orphan is the first line of a paragraph on the last line of a page (at least I think so), but for us, 'orphan' is such a single-letter word at the end of a line.

Best Answer

This is a LuaLaTeX solution. It is a function that gets called just before TeX breaks the text into lines. It inserts ties ~ (only the penalty of 10000, the glue is already there) after the single letter word. Words will still hyphenate (see example below) - as far as I can see (after the w).

[Edit: I have added a check in the code that only letters (L* unicode character class) will be taken into account when preventing a line break after the glyph.]

\documentclass{article}
\usepackage{polski}
\usepackage[polish]{babel}
\usepackage{fontspec}
\usepackage{luatexbase}\usepackage{luacode}

\begin{luacode}

local prevent_single_letter = function (head)
  while head do
    if head.id == 37 and unicode.utf8.match(unicode.utf8.char(head.char),"%a") then -- a letter
      if head.prev.id == 10 and head.next.id == 10 then    -- only if we are at a one letter word

        local p = node.new("penalty")
        p.penalty = 10000

        -- This is for debugging only, but then you have to
        -- remove the last node.insert_after line:
        -- local w = node.new("whatsit","pdf_literal")
        -- w.data = "q 1 0 1 RG 1 0 1 rg 0 0 m 0 5 l 2 5 l 2 0 l b Q"
        -- node.insert_after(head,head,w)
        -- node.insert_after(head,w,p)

        node.insert_after(head,head,p)

      end
    end
    head = head.next
  end
  return true
end

luatexbase.add_to_callback("pre_linebreak_filter",prevent_single_letter,"active~")
\end{luacode}


\begin{document}
\hsize 2.7in

Noc była sierpniowa, ciepła i słodka, Księżyc oświecał srebrnem światłem wgłębienie, tak,
że twarze małego rycerza i Basi były skąpane w blasku.
Poniżej, na podwórzu zamkowem, widać było uśpione kupy żołnierzy, a także i ciała zabitych
podczas dziennej strzelaniny, bo nie znaleziono dotÄ…d czasu na ich pogrzebanie.

\end{document}

example output

BTW: the small hyphenation marks are made with the package showhyphens.

Related Question