9

This is a (somewhat late) follow up to the question How to avoid a linebreak before a comma? where @MostlyHarmless shows that a line break may be introduced after a word ending with a hyphen even if it is followed by a comma.

In the comments section of that question @topskip says:

If you use LuaTeX, we could provide you a solution that forbids a line break before a comma.

So here I’d like to find a solution in Lua(La)TeX.

The MWE to work with is the same:

\documentclass[11pt, a4paper]{scrartcl}
\usepackage[ngerman]{babel}
\begin{document}

aaDas ist ein Absatz. Das ist ein Absatz. Das ist ein Absatz. Es gibt zeit-, temperatur-,  und weggesteuerte Verfahren ein Absatz. Das ist ein Absatz.Das ist ein Absatz. Das ist ein Absatz.Das ist ein Absatz. Das ist ein Absatz.Das ist ein Absatz. Das ist ein Absatz.Das ist ein Absatz. Das ist ein Absatz.

\end{document}

And the output is the same as well: linebreak before comma

  • Excluding typing temperatur"~, I guess. – egreg Sep 21 '15 at 21:37
  • Yes, excluding that because it’s limited to a selection of babel languages only. And also excluding the other solutions given in the original thread. – georgd Sep 21 '15 at 21:46
  • Also relevant: How to prevent a stranded comma on the next line? – Sverre Sep 22 '15 at 9:11
7

You can use simple node processing callback to do that. I've created simpl package containg such callback, hyphencomma.sty:

\ProvidesPackage{hyphencomma}

\RequirePackage{luacode}

\begin{luacode*}
local glyph_id = node.id "glyph"
local penalty_id = node.id "penalty"
local hyphenc  = string.byte "-"
local commac   = string.byte ","
local function prevent_hyphencomma(head)
  for n in node.traverse_id(glyph_id,head) do
    if n.char == hyphenc then
      -- we need to find node after the next node. directly following is automaticaly inserted discretionary
      local next = n.next.next
      if next.id == glyph_id and next.char == commac then
        -- create penalty node
        local penalty = node.new("penalty")
        penalty.penalty = 10000
        -- remove discretionary
        head = node.remove(head,n.next)
        -- insert penalty
        head = node.insert_after(head, n, penalty)
      end
    end
  end
  return head
end


luatexbase.add_to_callback("pre_linebreak_filter", prevent_hyphencomma, "Prevent hyphen comma")

\end{luacode*}

\endinput

The prevent_hypencomma function processes nodes of each paragraph before the linebreak. There are many node types, but most important for are glyph nodes, which contains particular characters. To match nodes of this type, we must know it's node id, which we can find with:

local glyph_id = node.id "glyph"

with node.traverse_id iterator, we process the node list, filtering only nodes with glyph id. Each node has some fields, depending on its type. We can find glyphs character code with char field. It is character number, not string, so we must test for hyphen char code, not -. For ASCII characters, we can get the char code with string.byte function.

After each hyphen glyph, discretionary node is inserted, so we need to skip it to find next node and test it whether it is comma glyph. If it is, we must remove the discretionary node and insert penalty to prevent the linebreak.

The result:

enter image description here

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.