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

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.