1

This question "has been asked before" and answered:

Minimum length of last line of a paragraph

Ensure minimal length of last line

But I do not want lualatex (or any tex) to automatically stretch lines, or re-break the paragraph. What I want to do is typeout a message (to Terminal and log file) that directs the editor's attention there. Pseudo-code:

\HookShortLastLine{\typeout{Short last line in paragraph, page \thepage.}}

What I have tried so far: Manually hacking code from the impnattypo package. Since I do not understand Lua well, my results are too embarassing to show here.

Rationale: As has already been noted in comments to the above linked pages, automated final line break is likely to produce stange results.

I work with original fiction, where the text is easily edited to improve its appearance. So, I do not need TeX to change things that I can change myself. But it does help to have it noted, so I know where to look.

EDIT: Here is the simplified section of lua code, from impnattypo:

local glue_id  = node.id 'glue'
function lastline_length(head)
  while head do
    local _w,_h,_d = node.dimensions(head)
    if head.id == glue_id and head.subtype ~= 15 and (_w < 2 * tex.parindent)
    then
-- I know that the following commented-out routine will work,
-- but it is not what I need:
--      local p = node.new('penalty')
--      p.penalty = 10000
--      node.insert_after(head,head.prev,p}
-- The following line is my own code, which does not work:
      tex.print('\typeout{Short last line of paragraph, page \thepage.}')
    end
    head = head.next
  end
  return true
end
luatexbase.add_to_callback('pre_linebreak_filter',lastline_length,'lastline')

2 Answers 2

1

Since you don't want to actually modify any line-breaking decisions made by TeX, but just have a better visual of paragraphs with short last line, you can inquire the width of parfillskip at the post_linebreak_filter callback.

The following test if parfillskip is of width of at least half a line (i.e. the last line of the paragraph is shorter than half of a line), and if so it will fill the end of the paragraph with a red rectengle, and write the page number and coordinates of the end of the "bad" paragraph to a file named shortlines.txt (you can write to the .log file or terminal if you prefer).

\documentclass{article}
\directlua{
 local file = io.open("shortlines.txt", "w")
 local function write_shortlines_data()
    file:write("Short line detected on page " .. 
                        tex.count['c@page'] .. 
                        (". Coordinates: (\csstring\%i, \csstring\%i)"):format(pdf.getpos()) 
                        .. "\string\n")
 end
 local function lastline_length(head,c)
    for line in node.traverse(head) do
        for n in node.traverse(line.list) do
            if n.id == node.id('glue') and n.subtype == 15 then
                local glue_width = node.effective_glue(n,line,true)
                if glue_width >= tex.hsize / 2 then --[[you can choose a different length...]]
                    local normalized_glue_width = glue_width / 65781
                    local colorbar = node.new("whatsit","pdf_literal")
                    colorbar.data = "q 1 0 0 rg 2 0 " .. 
                        normalized_glue_width .. " 5 re f Q"
                    line.head = node.insert_before(line.list,n,colorbar)
                    local shortlines_data = node.new("whatsit","late_lua")
                    shortlines_data.data = write_shortlines_data
                    line.head = node.insert_before(line.list, colorbar, shortlines_data)
                end
                return head
            end
        end
    end
 end
 luatexbase.add_to_callback('post_linebreak_filter',lastline_length,'lastline')
}
\begin{document}
Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 

Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 

\newpage
 Test test test test
\end{document}

This is the first page

enter image description here

The file shortlines.txt contains

Short line detected on page 1. Coordinates: (17686202, 45729504)
Short line detected on page 2. Coordinates: (15601501, 46515936)

The coordinates does not really add anything in this example, but after reading your answer I wanted to show a way to fetch the coordinates of the end of a paragraph.

If you will prefer to use the x coordinate of an end of a paragraph rather than inquiring parfillskip to portray a bad paragraph, you can modify write_shortlines_data accordingly (the name might not make sense...), and maybe remove redundant things from lastline_length.

For example

\documentclass{article}
\directlua{
 local file = io.open("shortlines.txt", "w")
 local function write_shortlines_data()
    local x,_ = pdf.getpos()
    if x < 17700000 then --[[ choose a different number if you want ]]
        file:write("Short line detected on page " .. 
                            tex.count['c@page'] .. 
                            (". Coordinates: (\csstring\%i, \csstring\%i)"):format(pdf.getpos()) 
                            .. "\string\n")
    end
 end
 local function lastline_length(head,c)
    for line in node.traverse(head) do
        for n in node.traverse(line.list) do
            if n.id == node.id('glue') and n.subtype == 15 then
                local shortlines_data = node.new("whatsit","late_lua")
                shortlines_data.data = write_shortlines_data
                line.head = node.insert_before(line.list, n, shortlines_data)
                return head
            end
        end
    end
 end
 luatexbase.add_to_callback('post_linebreak_filter',lastline_length,'lastline')
}
\begin{document}
Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 

Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 
Test test test test test test Test test test test test test 

\newpage
 Test test test test
\end{document}

This approach produces the same text file with this example, but I did not add the red rules, I'll leave it to you.

7
  • I will have a detailed look at that, and report back. Where I live, a holiday is about to happen, so you might not hear from me for a couple of days.
    – rallg
    Nov 22 at 17:03
  • 1
    @rallg Happy holiday!
    – Udi Fogiel
    Nov 22 at 17:13
  • Accepted! Also see my edit to my non-answer. Improves your code (for my usage) by eliminating the flag on blank lines.
    – rallg
    Nov 23 at 20:22
  • @rallg Happy it helped. I'm not sure what do you mean by "blank lines", Can you give an example?
    – Udi Fogiel
    Nov 23 at 20:29
  • Blank line: \space\par and similar code. Maybe even variations on the theme of \null and \hbox. My usage requires precision layout of text, so I often do that sort of thing. Maybe not the best TeX approach, but it is low-math and low-thinking. :-)
    – rallg
    Nov 23 at 20:32
1

EDIT2: When I asked the question, I was unaware of package lua-typo, which appears to do what I originally had in mind: Highlight or flag problems (fix them yourself, by re-write) instead of auto-correct.

EDIT: I had almost given up hope, but then Udi (see accepted answer) came to the rescue. Improvement over that code: As written (unless modified) it flags short lines, but also flags lines that are intentionally blank (I use them for layout spacing). This modification does not flag the blank lines. The numbers are specific to my text width, so change them as needed. They are measured in TeX sp. The 1000 seems to be needed for absorbing some kind of tiny math error.

if glue_width >= tex.hsize - 2300000
and glue_width < tex.hsize - 1000

I wrote this as an "answer" for the benefit of others who find this question.

LaTeX (and LualaTeX has \savepos and related commands, that are intended to store the x and y page coordinates where they are written. The numbers can be retrieved later. BUT: Apparently the request to save position does not immediately store coordinates. because TeX does not actually know them at that point. The numbers are not final until the time when the page is shipped. Alas, it does not seem possible to sequentially save information for each paragraph, and retrieve them together (perhaps comma-separated), because only the information at the last save request is honored.

I can understand why the y coordinate is not available until shipout, due to vertical glues. But in the case of a single-column page, I would expect that the x coordinate is available at the end of every paragraph, even before shipout.

4
  • 3
    Alas, it does not seem possible to sequentially save information for each paragraph, and retrieve them together (perhaps comma-separated), because only the information at the last save request is honored. that is not entirely true. Since \write is executed at shipout time (unless prefixed by \immediate) you can write the coordinate to an external file and retrieve it on a second run. see for example the zref-savepos and marginnote packages, or this answer
    – Udi Fogiel
    Nov 22 at 1:00
  • @UdiFogiel My workflow does not use *aux or second run that way. Should have mentioned it. But others might benefit from that approach, so your comment is appreciated.
    – rallg
    Nov 22 at 17:02
  • Hmmm, I doubt that you never compile your document more than once. If you have table of contents in your book, you already use auxiliary files, and this question is about how to correct the layout manually...
    – Udi Fogiel
    Nov 22 at 17:12
  • @UdiFogiel No table of contents. No bibliography. No TikZ. No tables. No math! Just fiction text. Compiling more than once is only for editing. I have been doing without *aux for about 6 years now.
    – rallg
    Nov 22 at 18:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .