段落回调帮助进行寡妇/孤儿手动调整

段落回调帮助进行寡妇/孤儿手动调整

通常很难判断在\looseness=-1段落中添加内容是否真的会成功减少一行。如何向段落生成器添加回调,以便对每个段落进行注释以显示其作用位置?

我在想类似的事情:对于每个段落,正常构建它,然后使用减少的松散度再次构建它以比较行数。然后输出“正常”段落,但如果减少松散度的段落更短,则最后一行显示为红色。

我问这个问题的原因是:我正在手动调整一本书以摆脱孤行和孤行,我花了很多时间在各个页面之间跳来跳去寻找 +1 或 -1 松散度可以起作用的地方,并思考如何将几个调整结合起来。目前我必须暂时添加\looseness=-1、重新编译并检查结果,因此建议的回调将有很大帮助。

答案1

编辑:代码(更新 2)现在使用不同的颜色来提供更多信息:

  • 红色:段落可以缩小
  • 蓝色:段落可以展开
  • 洋红色:段落可以扩大或缩小
  • 暗青色:可以使用当前紧急拉伸值扩展段落
  • 暗粉色:段落可以通过紧急拉伸进行扩展或缩小

以下是结果示例: 书中的两页带有彩色线条,显示哪些段落可以调整

老的:以下是解决方案的首次尝试:

\documentclass[twocolumn]{article}
\usepackage[margin=2cm]{geometry}
\usepackage{luatexbase}
\usepackage{lipsum}

% This doesn't seem to work when using tex.linebreak
\setlength\parskip{0pt}

\begin{document}
\directlua{
    % Use method aliases for better performance
    local nodeid = node.id
    local nodenew = node.new
    local nodecopy = node.copy
    local nodetail = node.tail
    local nodeinsertbefore = node.insert_before
    local nodeinsertafter = node.insert_after
    local nodetraverseid = node.traverse_id

    % Get node ids from their names
    local HLIST = nodeid("hlist")
    local WHAT = nodeid("whatsit")
    local COL = node.subtype("pdf_colorstack")

    % Make nodes for beginning and end of colored regions
    local color_push = nodenew(WHAT,COL)
    local color_pop = nodenew(WHAT,COL)
    color_push.stack = 0
    color_pop.stack = 0
    color_push.data = "1 0 0 rg" % PDF code for RGB red
    color_push.command = 1
    color_pop.command = 2

    % Function to color the last line in the given list
    local color_last_line = function (n)
        % Get the last hlist in the given list
        local lastLine
        for line in nodetraverseid(HLIST, n) do
            lastLine = line
        end

        % Surround it with color start/stop
        lastLine.head = nodeinsertbefore(lastLine.head, lastLine.head, nodecopy(color_push))
        nodeinsertafter(lastLine.head, nodetail(lastLine.head), nodecopy(color_pop))
    end

    % Callback to color the last line wherever a decreased looseness would work
    local linebreak_filter_test_looseness = function (head, is_display)
        % Build a copy of the paragraph with decreased looseness
        local nM1, iM1 = tex.linebreak(node.copy_list(head), {looseness=tex.looseness-1})

        % Build the paragraph normally
        local n, i = tex.linebreak(head)

        % If decreasing the looseness does decrease the line count, color the last line
        if iM1.prevgraf < i.prevgraf then
            color_last_line(n)
        end

        return n
    end

    % Register callback
    luatexbase.add_to_callback("linebreak_filter", linebreak_filter_test_looseness, "linebreak_filter_test_looseness")
}

\lipsum[1]
\lipsum[6]
\lipsum[71]
\lipsum[3]
\lipsum[35]
\lipsum[76]
\lipsum[7-8]

\end{document}

(谢谢这条信息鸡化文档。

你可以看到结果这里

问题是 parskip 设置不再起作用。有关比较,请参阅这里对于不使用回调时的输出:左右列的行完全对齐。

这似乎是使用回调调用 tex.linebreak 构建段落时的一个普遍问题……只需这样做\directlua{ luatexbase.add_to_callback("linebreak_filter", tex.linebreak, "myfilter") }即可重现该问题。

更新:这是解决该问题的方法tex.linebreak:换行前过滤器测试是否\looseness=-1可以工作,并设置一个变量,由换行后过滤器检查该变量以进行着色。通过外部变量传达信息有点不方便,但这样我就让 LuaTeX 在内部构建段落,这样tex.linebreak就不会有机会弄乱基线跳过粘连。

\documentclass[twocolumn]{article}
\usepackage[margin=2cm]{geometry}
\usepackage{luatexbase}
\usepackage{lipsum}

\usepackage[callback=]{nodetree}

% This doesn't seem to work when using tex.linebreak
\setlength\parskip{0pt}

\begin{document}
\directlua{
    % Use method aliases for better performance
    local nodeid = node.id
    local nodenew = node.new
    local nodecopy = node.copy
    local nodetail = node.tail
    local nodeinsertbefore = node.insert_before
    local nodeinsertafter = node.insert_after
    local nodetraverseid = node.traverse_id

    % Get node ids from their names
    local HLIST = nodeid("hlist")
    local WHAT = nodeid("whatsit")
    local COL = node.subtype("pdf_colorstack")

    % Make nodes for beginning and end of colored regions
    local color_push = nodenew(WHAT,COL)
    local color_pop = nodenew(WHAT,COL)
    color_push.stack = 0
    color_pop.stack = 0
    color_push.data = "1 0 0 rg" % PDF code for RGB red
    color_push.command = 1
    color_pop.command = 2

    % Set to true when the next post-linebreak filter should color the last line
    local ColorLastLine = false

    % Function to color the last line in the given list
    local color_last_line = function (n)
        % Get the last hlist in the given list
        local lastLine
        for line in nodetraverseid(HLIST, n) do
            lastLine = line
        end

        % Surround it with color start/stop
        lastLine.head = nodeinsertbefore(lastLine.head, lastLine.head, nodecopy(color_push))
        nodeinsertafter(lastLine.head, nodetail(lastLine.head), nodecopy(color_pop))
    end

    % Callback to check if decreasing the looseness would decrease the line count
    local pre_linebreak_test_looseness = function (head, groupeCode)
        % Build a copy of the paragraph with decreased looseness
        local nM1, iM1 = tex.linebreak(node.copy_list(head), {looseness=tex.looseness-1})

        % Build a copy of the paragraph normally
        local n, i = tex.linebreak(node.copy_list(head))

        % Store whether decreasing the looseness does decrease the line count, for "post" callback
        ColorLastLine = iM1.prevgraf < i.prevgraf

        return true
    end

    % Callback to colorize the last line of the paragraph when ColorLastLine is true
    local post_linebreak_color_last_line = function (head, groupcode)
        if ColorLastLine then
            color_last_line(head)
        end
        return true
    end

    % Register callbacks
    luatexbase.add_to_callback("pre_linebreak_filter", pre_linebreak_test_looseness, "pre_linebreak_test_looseness")
    luatexbase.add_to_callback("post_linebreak_filter", post_linebreak_color_last_line, "post_linebreak_color_last_line")
}

\lipsum[1]
\lipsum[6]
\lipsum[71]
\lipsum[3]
\lipsum[35]
\lipsum[76]
\lipsum[7-8]

\end{document}

更新 2:这是一个独立的 Lua 文件,它对段落的最后一行使用不同的颜色,取决于它是否可以收缩、扩展或两者兼而有之(如本答案顶部所述)。

文件widow-assist.lua

-- Use method aliases for better performance
local nodecopy = node.copy
local nodecopylist = node.copy_list
local nodetail = node.tail
local nodeinsertbefore = node.insert_before
local nodeinsertafter = node.insert_after
local nodetraverseid = node.traverse_id

-- Get node ids from their names
local HLIST = node.id("hlist")
local WHAT = node.id("whatsit")
local COL = node.subtype("pdf_colorstack")

-- Make nodes for beginning and end of colored regions
local color_p1 = node.new(WHAT,COL)
local color_p1s = node.new(WHAT,COL)
local color_m1 = node.new(WHAT,COL)
local color_pm1 = node.new(WHAT,COL)
local color_pm1s = node.new(WHAT,COL)
local color_pop = node.new(WHAT,COL)
color_p1.stack = 0
color_p1.command = 1
color_p1.data = "0 0 1 rg" -- PDF code for RGB blue
color_p1s.stack = 0
color_p1s.command = 1
color_p1s.data = "0 0.7 0.7 rg" -- PDF code for RGB dark cyan
color_m1.stack = 0
color_m1.command = 1
color_m1.data = "1 0 0 rg" -- PDF code for RGB red
color_pm1.stack = 0
color_pm1.command = 1
color_pm1.data = "1 0 1 rg" -- PDF code for RGB magenta
color_pm1s.stack = 0
color_pm1s.command = 1
color_pm1s.data = "1 .5 .5 rg" -- PDF code for RGB pink
color_pop.stack = 0
color_pop.command = 2

-- Color to use for last line in the next post-linebreak filter call (nil = no color)
local LastLineColor = nil

-- Function to color the last line in the given list
local color_last_line = function (n)
    -- Get the last hlist in the given list
    local lastLine
    for line in nodetraverseid(HLIST, n) do
        lastLine = line
    end

    -- Surround it with color start/stop
    lastLine.head = nodeinsertbefore(lastLine.head, lastLine.head, nodecopy(LastLineColor))
    nodeinsertafter(lastLine.head, nodetail(lastLine.head), nodecopy(color_pop))
end

-- Callback to check if changing the looseness by +-1 would affect the line count
local pre_linebreak_test_looseness = function (head, groupeCode)
    -- Disable underfull and overfull boxes reporting
    luatexbase.add_to_callback("hpack_quality", function() end, "hpqfilter")
    luatexbase.add_to_callback("vpack_quality", function() end, "vpqfilter")

    -- Build a copy of the paragraph normally
    local n, i = tex.linebreak(nodecopylist(head))

    -- Build a copy of the paragraph with increased looseness and default emergency stretch
    local nP1s, iP1s = tex.linebreak(nodecopylist(head), {looseness=tex.looseness+1})

    local nP1, iP1
    if iP1s.prevgraf > i.prevgraf then
        -- It worked with the default emergency stretch, let's try without
        nP1, iP1 = tex.linebreak(nodecopylist(head), {looseness=tex.looseness+1, emergencystretch=0})
    else
        -- Didn't work with emergency stretch, no point to try without
        nP1, iP1 = n, i
    end

    -- Build a copy of the paragraph with decreased looseness
    local nM1, iM1 = tex.linebreak(nodecopylist(head), {looseness=tex.looseness-1})

    -- Reenable underfull and overfull boxes reporting
    luatexbase.remove_from_callback("hpack_quality", "hpqfilter")
    luatexbase.remove_from_callback("vpack_quality", "vpqfilter")

    -- Set color to use in the post-linebreak callback
    if iP1.prevgraf > i.prevgraf and iM1.prevgraf < i.prevgraf then
        -- Both +1 and -1 looseness would work
        LastLineColor = color_pm1
    elseif iP1s.prevgraf > i.prevgraf and iM1.prevgraf < i.prevgraf then
        -- Both +1 and -1 looseness would work, but +1 only with emergency stretch
        LastLineColor = color_pm1s
    elseif iP1.prevgraf > i.prevgraf then
        -- Only +1 looseness would work
        LastLineColor = color_p1
    elseif iP1s.prevgraf > i.prevgraf then
        -- Only +1 looseness would work and only thanks to the emergency stretch
        LastLineColor = color_p1s
    elseif iM1.prevgraf < i.prevgraf then
        -- Only -1 looseness would work
        LastLineColor = color_m1
    else
        LastLineColor = nil
    end

    return true
end

-- Callback to colorize the last line of the paragraph when ColorLastLine is true
local post_linebreak_color_last_line = function (head, groupcode)
    if LastLineColor then
        color_last_line(head)
    end
    return true
end

-- Register callbacks
luatexbase.add_to_callback("pre_linebreak_filter", pre_linebreak_test_looseness, "pre_linebreak_test_looseness")
luatexbase.add_to_callback("post_linebreak_filter", post_linebreak_color_last_line, "post_linebreak_color_last_line")

使用方法如下:

\documentclass{article}
\usepackage{lipsum}
\usepackage{luatexbase}
\directlua{dofile("widow-assist.lua")}
\begin{document}
\lipsum[1-5]
\end{document}

相关内容