summaryrefslogtreecommitdiff
path: root/macros/luatex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2021-06-03 03:02:31 +0000
committerNorbert Preining <norbert@preining.info>2021-06-03 03:02:31 +0000
commitd231c6331f53a048d29c2f847254a9f56d5ee2dd (patch)
treec8b2637a679254e40e8d4da5c03fda92cfaa7974 /macros/luatex
parent138d0a6cfd5f2187973fb6b83ad2fb0dcf5a8348 (diff)
CTAN sync 202106030302
Diffstat (limited to 'macros/luatex')
-rw-r--r--macros/luatex/latex/lua-typo/README.md24
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdfbin16794 -> 16794 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdfbin59619 -> 64171 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo.pdfbin115609 -> 138442 bytes
-rw-r--r--macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty843
-rw-r--r--macros/luatex/latex/lua-typo/source/lua-typo.dtx1177
6 files changed, 1697 insertions, 347 deletions
diff --git a/macros/luatex/latex/lua-typo/README.md b/macros/luatex/latex/lua-typo/README.md
index 5e88fafb62..64373e6913 100644
--- a/macros/luatex/latex/lua-typo/README.md
+++ b/macros/luatex/latex/lua-typo/README.md
@@ -48,12 +48,26 @@ Recommended loactions for installation:
Changes
-------
-- First release version: 0.30, March 2021.
+* First release version: 0.30, March 2021.
-- v.0.32: bug fixes
- better protection against nil nodes,
- new page detection corrected,
- homeoarchy detection improved.
+* v.0.32: bug fixes
+ - better protection against nil nodes,
+ - new page detection corrected,
+ - homeoarchy detection improved.
+
+* v.0.40 (not released): bug fixes
+ - in some cases parlines count was wrong, fixed;
+ - partial improvement of short pages detection.
+
+* v.0.50: new implementation, May 2021
+ - callback change: `pre_shipout_filter` instead of `pre_output_filter`
+ this change requires latex release 2021-06-01;
+ - rollback enabled in case `pre_shipout_filter` is missing;
+ - footnotes are scanned now;
+ - overfull box detection fixed (works for equations and tt fonts now);
+ - short pages detection fixed;
+ - coloration of faulty lines improved;
+ - all flaws found are now recorded into file "`\jobname`.typo".
--
Copyright 2020--2021 Daniel Flipo
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
index f5c82e7015..89951bdf86 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
index 5b2c788bd0..315c8306e1 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
index 764e6374a4..5b2b2dcbdf 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty b/macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty
new file mode 100644
index 0000000000..1580bcb147
--- /dev/null
+++ b/macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty
@@ -0,0 +1,843 @@
+%%
+%% This is file `lua-typo.sty' v0.40,
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% lua-typo.dtx (with options: `sty')
+%%
+%% IMPORTANT NOTICE:
+%% For the copyright see the source file `lua-typo.dtx'.
+%%
+\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+\ProvidesPackage{lua-typo}
+ [2021/04/18 v.0.40 Daniel Flipo]
+\ifdefined\directlua
+ \RequirePackage{luatexbase,luacode,luacolor}
+ \RequirePackage{kvoptions,atveryend}
+\else
+ \PackageError{This package is meant for LuaTeX only! Aborting}
+ {No more information available, sorry!}
+\fi
+\newdimen\luatypoLLminWD
+\newdimen\luatypoBackPI
+\newdimen\luatypoBackFuzz
+\newcount\luatypoStretchMax
+\newcount\luatypoHyphMax
+\newcount\luatypoPageMin
+\newcount\luatypoMinFull
+\newcount\luatypoMinPart
+\newcount\luatypo@LANGno
+\newcount\luatypo@options
+\newtoks\luatypo@single
+\newtoks\luatypo@double
+
+\begin{luacode}
+luatypo = { }
+\end{luacode}
+\SetupKeyvalOptions{
+ family=luatypo,
+ prefix=LT@,
+}
+\DeclareBoolOption[false]{ShowOptions}
+\DeclareBoolOption[false]{None}
+\DeclareBoolOption[false]{All}
+\DeclareBoolOption[false]{BackParindent}
+\DeclareBoolOption[false]{ShortLines}
+\DeclareBoolOption[false]{ShortPages}
+\DeclareBoolOption[false]{OverfullLines}
+\DeclareBoolOption[false]{UnderfullLines}
+\DeclareBoolOption[false]{Widows}
+\DeclareBoolOption[false]{Orphans}
+\DeclareBoolOption[false]{EOPHyphens}
+\DeclareBoolOption[false]{RepeatedHyphens}
+\DeclareBoolOption[false]{ParLastHyphen}
+\DeclareBoolOption[false]{EOLShortWords}
+\DeclareBoolOption[false]{FirstWordMatch}
+\DeclareBoolOption[false]{LastWordMatch}
+\AddToKeyvalOption{luatypo}{All}{%
+ \LT@ShortLinestrue \LT@ShortPagestrue
+ \LT@OverfullLinestrue \LT@UnderfullLinestrue
+ \LT@Widowstrue \LT@Orphanstrue
+ \LT@EOPHyphenstrue \LT@RepeatedHyphenstrue
+ \LT@ParLastHyphentrue \LT@EOLShortWordstrue
+ \LT@FirstWordMatchtrue \LT@LastWordMatchtrue
+ \LT@BackParindenttrue
+}
+\ProcessKeyvalOptions{luatypo}
+\AtEndOfPackage{%
+ \ifLT@None
+ \directlua{ luatypo.None = true }%
+ \else
+ \directlua{ luatypo.None = false }%
+ \fi
+ \ifLT@BackParindent
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.BackParindent = true }%
+ \else
+ \directlua{ luatypo.BackParindent = false }%
+ \fi
+ \ifLT@ShortLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ShortLines = true }%
+ \else
+ \directlua{ luatypo.ShortLines = false }%
+ \fi
+ \ifLT@ShortPages
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ShortPages = true }%
+ \else
+ \directlua{ luatypo.ShortPages = false }%
+ \fi
+ \ifLT@OverfullLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.OverfullLines = true }%
+ \else
+ \directlua{ luatypo.OverfullLines = false }%
+ \fi
+ \ifLT@UnderfullLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.UnderfullLines = true }%
+ \else
+ \directlua{ luatypo.UnderfullLines = false }%
+ \fi
+ \ifLT@Widows
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.Widows = true }%
+ \else
+ \directlua{ luatypo.Widows = false }%
+ \fi
+ \ifLT@Orphans
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.Orphans = true }%
+ \else
+ \directlua{ luatypo.Orphans = false }%
+ \fi
+ \ifLT@EOPHyphens
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.EOPHyphens = true }%
+ \else
+ \directlua{ luatypo.EOPHyphens = false }%
+ \fi
+ \ifLT@RepeatedHyphens
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.RepeatedHyphens = true }%
+ \else
+ \directlua{ luatypo.RepeatedHyphens = false }%
+ \fi
+ \ifLT@ParLastHyphen
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ParLastHyphen = true }%
+ \else
+ \directlua{ luatypo.ParLastHyphen = false }%
+ \fi
+ \ifLT@EOLShortWords
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.EOLShortWords = true }%
+ \else
+ \directlua{ luatypo.EOLShortWords = false }%
+ \fi
+ \ifLT@FirstWordMatch
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.FirstWordMatch = true }%
+ \else
+ \directlua{ luatypo.FirstWordMatch = false }%
+ \fi
+ \ifLT@LastWordMatch
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.LastWordMatch = true }%
+ \else
+ \directlua{ luatypo.LastWordMatch = false }%
+ \fi
+}
+\ifLT@ShowOptions
+ \GenericWarning{* }{%
+ *** List of possible options for lua-typo ***\MessageBreak
+ [Default values between brackets]%
+ \MessageBreak
+ ShowOptions [false]\MessageBreak
+ None [false]\MessageBreak
+ BackParindent [false]\MessageBreak
+ ShortLines [false]\MessageBreak
+ ShortPages [false]\MessageBreak
+ OverfullLines [false]\MessageBreak
+ UnderfullLines [false]\MessageBreak
+ Widows [false]\MessageBreak
+ Orphans [false]\MessageBreak
+ EOPHyphens [false]\MessageBreak
+ RepeatedHyphens [false]\MessageBreak
+ ParLastHyphen [false]\MessageBreak
+ EOLShortWords [false]\MessageBreak
+ FirstWordMatch [false]\MessageBreak
+ LastWordMatch [false]\MessageBreak
+ \MessageBreak
+ *********************************************%
+ \MessageBreak Lua-typo [ShowOptions]
+ }%
+\fi
+\AtBeginDocument{%
+ \directlua{
+ luatypo.HYPHmax = tex.count.luatypoHyphMax
+ luatypo.PAGEmin = tex.count.luatypoPageMin
+ luatypo.Stretch = tex.count.luatypoStretchMax
+ luatypo.MinFull = tex.count.luatypoMinFull
+ luatypo.MinPart = tex.count.luatypoMinPart
+ luatypo.LLminWD = tex.dimen.luatypoLLminWD
+ luatypo.BackPI = tex.dimen.luatypoBackPI
+ luatypo.BackFuzz = tex.dimen.luatypoBackFuzz
+ }%
+}
+\AtVeryEndDocument{%
+\ifnum\luatypo@options = 0 \LT@Nonetrue \fi
+\ifLT@None
+ \directlua{
+ texio.write_nl(' ')
+ texio.write_nl('***********************************')
+ texio.write_nl('*** lua-typo loaded with NO option:')
+ texio.write_nl('*** NO CHECK PERFORMED! ***')
+ texio.write_nl('***********************************')
+ texio.write_nl(' ')
+ }%
+\else
+ \directlua{
+ texio.write_nl(' ')
+ texio.write_nl('*************************************')
+ if luatypo.pagelist == "" then
+ texio.write_nl('*** lua-typo: No Typo Flaws found.')
+ else
+ texio.write_nl('*** lua-typo: WARNING *************')
+ texio.write_nl('The following pages need attention: ')
+ texio.write(luatypo.pagelist)
+ end
+ texio.write_nl('***********************************')
+ texio.write_nl(' ')
+ }%
+\fi}
+\newcommand*{\luatypoOneChar}[2]{%
+ \def\luatypo@LANG{#1}\luatypo@single={#2}%
+ \ifcsname l@\luatypo@LANG\endcsname
+ \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+ \directlua{
+ local langno = \the\luatypo@LANGno
+ local string = \the\luatypo@single
+ luatypo.single[langno] = " "
+ for p, c in utf8.codes(string) do
+ local s = string.char(c)
+ luatypo.single[langno] = luatypo.single[langno] .. s
+ end
+ }%
+ \else
+ \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+ \MessageBreak \protect\luatypoOneChar\space command ignored}%
+ \fi}
+\newcommand*{\luatypoTwoChars}[2]{%
+ \def\luatypo@LANG{#1}\luatypo@double={#2}%
+ \ifcsname l@\luatypo@LANG\endcsname
+ \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+ \directlua{
+ local langno = \the\luatypo@LANGno
+ local string = \the\luatypo@double
+ luatypo.double[langno] = " "
+ for p, c in utf8.codes(string) do
+ local s = string.char(c)
+ luatypo.double[langno] = luatypo.double[langno] .. s
+ end
+ }%
+ \else
+ \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+ \MessageBreak \protect\luatypoTwoChars\space command ignored}%
+ \fi}
+\newcommand*{\luatypoSetColor}[2]{%
+ \begingroup
+ \color{#2}%
+ \directlua{luatypo.colortbl[#1]=\the\LuaCol@Attribute}%
+ \endgroup
+}
+\begin{luacode}
+luatypo.single = { }
+luatypo.double = { }
+luatypo.colortbl = { }
+luatypo.pagelist = ""
+
+local char_to_discard = { }
+char_to_discard[string.byte(",")] = true
+char_to_discard[string.byte(".")] = true
+char_to_discard[string.byte("!")] = true
+char_to_discard[string.byte("?")] = true
+char_to_discard[string.byte(":")] = true
+char_to_discard[string.byte(";")] = true
+char_to_discard[string.byte("-")] = true
+
+local split_lig = { }
+split_lig[0xFB00] = "ff"
+split_lig[0xFB01] = "fi"
+split_lig[0xFB02] = "fl"
+split_lig[0xFB03] = "ffi"
+split_lig[0xFB04] = "ffl"
+split_lig[0xFB05] = "st"
+split_lig[0xFB06] = "st"
+
+local DISC = node.id("disc")
+local GLYPH = node.id("glyph")
+local GLUE = node.id("glue")
+local KERN = node.id("kern")
+local HLIST = node.id("hlist")
+local LPAR = node.id("local_par")
+local MKERN = node.id("margin_kern")
+local PENALTY = node.id("penalty")
+local USRSKIP = 0
+local PARSKIP = 3
+local LFTSKIP = 8
+local RGTSKIP = 9
+local TOPSKIP = 10
+local PARFILL = 15
+local LINE = 1
+local BOX = 2
+local EQN = 6
+local USER = 0
+local HYPH = 0x2D
+local LIGA = 0x102
+local parline = 0
+
+local effective_glue = node.effective_glue
+local set_attribute = node.set_attribute
+local slide = node.slide
+local traverse = node.traverse
+local traverse_id = node.traverse_id
+local has_field = node.has_field
+local uses_font = node.uses_font
+local is_glyph = node.is_glyph
+
+local color_node = function (node, color)
+ local attr = oberdiek.luacolor.getattribute()
+ if node and node.id == DISC then
+ local pre = node.pre
+ local post = node.post
+ local repl = node.replace
+ if pre then
+ set_attribute(pre,attr,color)
+ end
+ if post then
+ set_attribute(post,attr,color)
+ end
+ if repl then
+ set_attribute(repl,attr,color)
+ end
+ elseif node then
+ set_attribute(node,attr,color)
+ end
+end
+local color_hlist = function (head, color)
+ local first = head.head
+ for n in traverse(first) do
+ color_node(n, color)
+ end
+end
+local color_line = function (head, color)
+ local first = head.head
+ for n in traverse(first) do
+ if n and n.id == HLIST then
+ color_hlist(n, color)
+ else
+ color_node(n, color)
+ end
+ end
+end
+local signature = function (node, string, swap)
+ local n = node
+ local str = string
+ if n and n.id == GLYPH then
+ local b, id = is_glyph(n)
+ if b and not char_to_discard[b] then
+ if b == 0x2019 then b = 0x27 end
+ if b < 0x100 then
+ str = str .. string.char(b)
+ elseif split_lig[b] then
+ local c = split_lig[b]
+ if swap then
+ c = string.reverse(c)
+ end
+ str = str .. c
+ elseif n.subtype == LIGA and b > 0xE000 then
+ local c = string.sub(b,-2)
+ if swap then
+ c = string.reverse(c)
+ end
+ str = str .. c
+ end
+ end
+ elseif n and n.id == DISC then
+ local pre = n.pre
+ local post = n.post
+ local c1 = ""
+ local c2 = ""
+ if pre and pre.char and pre.char ~= HYPH and pre.char < 0x100 then
+ c1 = string.char(pre.char)
+ end
+ if post and post.char then
+ if post.char < 0x100 then
+ c2 = string.char(post.char)
+ elseif split_lig[post.char] then
+ c2 = split_lig[post.char]
+ if swap then
+ c2 = string.reverse(c2)
+ end
+ end
+ end
+ if swap then
+ str = str .. c2 .. c1
+ else
+ str = str .. c1 .. c2
+ end
+ end
+ local len = string.len(str)
+ if string.find(str, "_") then
+ len = len - 1
+ end
+ return len, str
+end
+local check_last_word = function (old, node, flag)
+ local COLOR = luatypo.colortbl[11]
+ local match = false
+ local new = ""
+ local maxlen = 0
+ if flag and node then
+ local swap = true
+ local lastn = node
+ while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC do
+ lastn = lastn.prev
+ end
+ local n = lastn
+ while n and n.id ~= GLUE do
+ maxlen, new = signature (n, new, swap)
+ n = n.prev
+ end
+ if n and n.id == GLUE then
+ new = new .. "_"
+ repeat
+ n = n.prev
+ maxlen, new = signature (n, new, swap)
+ until not n or n.id == GLUE
+ end
+ new = string.reverse(new)
+ local MinFull = luatypo.MinFull
+ local MinPart = luatypo.MinPart
+ MinFull = math.min(MinPart,MinFull)
+ local k = MinPart
+ local oldlast = string.gsub (old, '.*_', '')
+ local newlast = string.gsub (new, '.*_', '')
+ local i = string.find(new, "_")
+ if i and i > maxlen - MinPart + 1 then
+ k = MinPart + 1
+ end
+ local oldsub = string.sub(old,-k)
+ local newsub = string.sub(new,-k)
+ local l = string.len(new)
+ if oldsub == newsub and l >= k then
+ match = true
+ elseif oldlast == newlast and string.len(newlast) >= MinFull then
+ match = true
+ oldsub = oldlast
+ newsub = newlast
+ k = string.len(newlast)
+ end
+ if match then
+ local osub = oldsub
+ local nsub = newsub
+ while osub == nsub and k <= maxlen do
+ k = k +1
+ osub = string.sub(old,-k)
+ nsub = string.sub(new,-k)
+ if osub == nsub then
+ newsub = nsub
+ end
+ end
+ pageflag = true
+ newsub = string.gsub(newsub, '^_', '')
+ oldsub = string.reverse(newsub)
+ local newsub = ""
+ local n = lastn
+ repeat
+ if n and n.id ~= GLUE then
+ color_node(n, COLOR)
+ l, newsub = signature(n, newsub, swap)
+ elseif n then
+ newsub = newsub .. "_"
+ end
+ n = n.prev
+ until not n or newsub == oldsub or l >= k
+ end
+ end
+ return new
+end
+local check_first_word = function (old, node, flag)
+ local COLOR = luatypo.colortbl[10]
+ local match = false
+ local swap = false
+ local new = ""
+ local maxlen = 0
+ local start = node
+ local n = start
+ while n and n.id ~= GLYPH and n.id ~= DISC do
+ n = n.next
+ end
+ while n and n.id ~= GLUE do
+ maxlen, new = signature (n, new, swap)
+ n = n.next
+ end
+ if n and n.id == GLUE then
+ new = new .. "_"
+ repeat
+ n = n.next
+ maxlen, new = signature (n, new, swap)
+ until not n or n.id == GLUE
+ end
+ if flag then
+ local MinFull = luatypo.MinFull
+ local MinPart = luatypo.MinPart
+ MinFull = math.min(MinPart,MinFull)
+ local k = MinPart
+ local oldsub = ""
+ local newsub = ""
+ local oldfirst = string.gsub (old, '_.*', '')
+ local newfirst = string.gsub (new, '_.*', '')
+ local i = string.find(new, "_")
+ if i and i <= MinPart then
+ k = MinPart + 1
+ end
+ local oldsub = string.sub(old,1,k)
+ local newsub = string.sub(new,1,k)
+ local l = string.len(newsub)
+ if oldsub == newsub and l >= k then
+ match = true
+ elseif oldfirst == newfirst and string.len(newfirst) >= MinFull then
+ match = true
+ oldsub = oldfirst
+ newsub = newfirst
+ k = string.len(newfirst)
+ end
+ if match then
+ local osub = oldsub
+ local nsub = newsub
+ while osub == nsub and k <= maxlen do
+ k = k + 1
+ osub = string.sub(old,1,k)
+ nsub = string.sub(new,1,k)
+ if osub == nsub then
+ newsub = nsub
+ end
+ end
+ pageflag = true
+ newsub = string.gsub(newsub, '_$', '') --$
+ oldsub = newsub
+ local newsub = ""
+ local k = string.len(oldsub)
+ local n = start
+ repeat
+ if n and n.id ~= GLUE then
+ color_node(n, COLOR)
+ l, newsub = signature(n, newsub, swap)
+ elseif n then
+ newsub = newsub .. "_"
+ end
+ n = n.next
+ until not n or newsub == oldsub or l >= k
+ end
+ end
+ return new
+end
+local check_regexpr = function (glyph)
+ local COLOR = luatypo.colortbl[3]
+ local lang = glyph.lang
+ local match = false
+ local lchar, id = is_glyph(glyph)
+ local previous = glyph.prev
+ if lang and luatypo.single[lang] then
+ if lchar and lchar < 0x100 and previous and previous.id == GLUE then
+ match = string.find(luatypo.single[lang], string.char(lchar))
+ if match then
+ pageflag = true
+ color_node(glyph,COLOR)
+ end
+ end
+ end
+ if lang and luatypo.double[lang] then
+ if lchar and previous and previous.id == GLYPH then
+ local pchar, id = is_glyph(previous)
+ local pprev = previous.prev
+ if pchar and pchar < 0x100 and pprev and pprev.id == GLUE then
+ local pattern = string.char(pchar) .. string.char(lchar)
+ match = string.find(luatypo.double[lang], pattern)
+ if match then
+ pageflag = true
+ color_node(previous,COLOR)
+ color_node(glyph,COLOR)
+ end
+ end
+ elseif lchar and previous and previous.id == KERN then
+ local pprev = previous.prev
+ if pprev and pprev.id == GLYPH then
+ local pchar, id = is_glyph(pprev)
+ local ppprev = pprev.prev
+ if pchar and pchar < 0x100 and
+ ppprev and ppprev.id == GLUE then
+ local pattern = string.char(pchar) .. string.char(lchar)
+ match = string.find(luatypo.double[lang], pattern)
+ if match then
+ pageflag = true
+ color_node(pprev,COLOR)
+ color_node(glyph,COLOR)
+ end
+ end
+ end
+ end
+ end
+end
+local show_pre_disc = function (disc, color)
+ local n = disc
+ while n and n.id ~= GLUE do
+ color_node(n, color)
+ n = n.prev
+ end
+ return n
+end
+luatypo.check_page = function (head)
+ local PAGEmin = luatypo.PAGEmin
+ local HYPHmax = luatypo.HYPHmax
+ local LLminWD = luatypo.LLminWD
+ local BackPI = luatypo.BackPI
+ local BackFuzz = luatypo.BackFuzz
+ local BackParindent = luatypo.BackParindent
+ local ShortLines = luatypo.ShortLines
+ local ShortPages = luatypo.ShortPages
+ local OverfullLines = luatypo.OverfullLines
+ local UnderfullLines = luatypo.UnderfullLines
+ local Widows = luatypo.Widows
+ local Orphans = luatypo.Orphans
+ local EOPHyphens = luatypo.EOPHyphens
+ local RepeatedHyphens = luatypo.RepeatedHyphens
+ local FirstWordMatch = luatypo.FirstWordMatch
+ local ParLastHyphen = luatypo.ParLastHyphen
+ local EOLShortWords = luatypo.EOLShortWords
+ local LastWordMatch = luatypo.LastWordMatch
+ local pageno = tex.getcount("c@page")
+ local Stretch = math.max(luatypo.Stretch/100,1)
+ local blskip = tex.getglue("baselineskip")
+ local textht = tex.getdimen("textheight")
+ local vpos_min = (PAGEmin+1) * blskip
+ local vpos = 0
+
+ local pageflag = false
+ local orphanflag = false
+ local widowflag = false
+ local lwhyphflag = false
+ local pageshort = false
+ local firstwd = ""
+ local lastwd = ""
+ local hyphcount = 0
+ local pageline = 0
+
+ while head do
+ local nextnode = head.next
+ local prevnode = head.prev
+ local pprevnode = nil
+ if prevnode then
+ pprevnode = prevnode.prev
+ end
+ if head.id == HLIST and head.subtype == LINE then
+ vpos = vpos + head.height + head.depth
+ local first = head.head
+ while first.id == MKERN or
+ (first.id == GLUE and first.subtype == LFTSKIP) do
+ first = first.next
+ end
+ local ListItem = false
+ pageline = pageline + 1
+ if head.glue_set == 1 and head.glue_sign == 2 and
+ head.glue_order == 0 and OverfullLines then
+ pageflag = true
+ local COLOR = luatypo.colortbl[7]
+ color_line (head, COLOR)
+ elseif head.glue_set >= Stretch and head.glue_sign == 1 and
+ head.glue_order == 0 and UnderfullLines then
+ local COLOR = luatypo.colortbl[8]
+ pageflag = true
+ color_line (head, COLOR)
+ end
+ if first.id == LPAR then
+ hyphcount = 0
+ parline = 1
+ if not nextnode then
+ orphanflag = true
+ end
+ local nn = first.next
+ if nn and nn.id == HLIST and nn.subtype == BOX then
+ ListItem = true
+ end
+ else
+ parline = parline + 1
+ end
+ if FirstWordMatch then
+ local flag = not ListItem
+ firstwd = check_first_word(firstwd, first, flag)
+ end
+ local ln = slide(first)
+ local pn = ln.prev
+ if pn and pn.id == GLUE and pn.subtype == PARFILL then
+ hyphcount = 0
+ orphanflag = false
+ if pageline == 1 and parline > 1 then
+ widowflag = true
+ end
+ local PFskip = effective_glue(pn,head)
+ if ShortLines then
+ local llwd = tex.hsize - PFskip
+ if llwd < LLminWD then
+ pageflag = true
+ local COLOR = luatypo.colortbl[6]
+ local attr = oberdiek.luacolor.getattribute()
+ color_line (head, COLOR)
+ end
+ end
+ if BackParindent and PFskip < BackPI and PFskip > BackFuzz then
+ pageflag = true
+ local COLOR = luatypo.colortbl[12]
+ local attr = oberdiek.luacolor.getattribute()
+ color_line (head, COLOR)
+ end
+ if LastWordMatch then
+ local flag = textline
+ if PFskip > BackPI then
+ flag = false
+ end
+ lastwd = check_last_word(lastwd, pn, flag)
+ end
+ elseif pn and pn.id == DISC then
+ hyphcount = hyphcount + 1
+ if LastWordMatch then
+ lastwd = check_last_word(lastwd, ln, true)
+ end
+ if hyphcount > HYPHmax and RepeatedHyphens then
+ local COLOR = luatypo.colortbl[2]
+ local pg = show_pre_disc (pn,COLOR)
+ pageflag = true
+ end
+ if not nextnode and EOPHyphens then
+ lwhyphflag = true
+ end
+ if nextnode and ParLastHyphen then
+ local nnnode = nextnode.next
+ local nnnnode = nil
+ if nnnode and nnnode.next then
+ nnnnode = nnnode.next
+ if nnnnode and nnnnode.id == HLIST and
+ nnnnode.subtype == 1 and nnnnode.glue_order == 2 then
+ local COLOR = luatypo.colortbl[0]
+ local pg = show_pre_disc (pn,COLOR)
+ pageflag = true
+ end
+ end
+ end
+ else
+ hyphcount = 0
+ if LastWordMatch and pn then
+ lastwd = check_last_word(lastwd, pn, true)
+ end
+ if EOLShortWords then
+ while pn and pn.id ~= GLYPH and pn.id ~= HLIST do
+ pn = pn.prev
+ end
+ if pn and pn.id == GLYPH then
+ check_regexpr(pn,line)
+ end
+ end
+ end
+ if widowflag and Widows then
+ pageflag = true
+ widowflag = false
+ local COLOR = luatypo.colortbl[4]
+ color_line (head, COLOR)
+ end
+ if orphanflag and Orphans then
+ pageflag = true
+ local COLOR = luatypo.colortbl[5]
+ color_line (head, COLOR)
+ end
+ if lwhyphflag and EOPHyphens then
+ pageflag = true
+ local COLOR = luatypo.colortbl[1]
+ local pg = show_pre_disc (pn,COLOR)
+ end
+ elseif head.id == GLUE and head.subtype == USRSKIP then
+ vpos = vpos + head.width
+ if not nextnode and ShortPages and
+ pageline > 1 and pageline < PAGEmin then
+ pageshort = true
+ end
+ if pageshort and vpos < vpos_min then
+ pageflag = true
+ local COLOR = luatypo.colortbl[9]
+ local n = head
+ repeat
+ n = n.prev
+ until n.id == HLIST and (n.subtype == LINE or n.subtype == EQN)
+ if n then
+ color_line(n, COLOR)
+ end
+ end
+ elseif head.id == GLUE then
+ vpos = vpos + head.width
+ end
+ head = nextnode
+ end
+ if pageflag then
+ local pl = luatypo.pagelist
+ local p = tonumber(string.match(pl, "%s(%d+),%s$"))
+ if not p or pageno > p then
+ luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", "
+ end
+ end
+ return true
+end
+return luatypo.check_page
+\end{luacode}
+\AtEndOfPackage{%
+ \directlua{
+ if not luatypo.None then
+ luatexbase.add_to_callback
+ ("pre_output_filter",luatypo.check_page,"check_page")
+ end
+ }
+}
+
+\InputIfFileExists{lua-typo.cfg}%
+ {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file loaded}}%
+ {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file not found.
+ \MessageBreak Providing default values.}%
+ \definecolor{mygrey}{gray}{0.6}%
+ \definecolor{myred}{rgb}{1,0.55,0}
+ \luatypoSetColor0{red}% Paragraph last full line hyphenated
+ \luatypoSetColor1{red}% Page last word hyphenated
+ \luatypoSetColor2{red}% Hyphens ending two many consecutive lines
+ \luatypoSetColor3{red}% Short word at end of line
+ \luatypoSetColor4{cyan}% Widow
+ \luatypoSetColor5{cyan}% Orphan
+ \luatypoSetColor6{cyan}% Paragraph ending on a short line
+ \luatypoSetColor7{blue}% Overfull lines
+ \luatypoSetColor8{blue}% Underfull lines
+ \luatypoSetColor9{red}% Nearly empty page
+ \luatypoSetColor{10}{myred}% First word matches
+ \luatypoSetColor{11}{myred}% Last word matches
+ \luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+ \luatypoBackPI=1em\relax
+ \luatypoBackFuzz=2pt\relax
+ \luatypoLLminWD=2\parindent\relax
+ \luatypoStretchMax=200\relax
+ \luatypoHyphMax=2\relax
+ \luatypoPageMin=5\relax
+ \luatypoMinFull=4\relax
+ \luatypoMinPART=4\relax
+ }%
+%%
+%%
+%% End of file `lua-typo.sty'.
diff --git a/macros/luatex/latex/lua-typo/source/lua-typo.dtx b/macros/luatex/latex/lua-typo/source/lua-typo.dtx
index f783033fdd..ebcd727a67 100644
--- a/macros/luatex/latex/lua-typo/source/lua-typo.dtx
+++ b/macros/luatex/latex/lua-typo/source/lua-typo.dtx
@@ -1,4 +1,4 @@
-% \CheckSum{336}
+% \CheckSum{373}
%
% \iffalse meta-comment
%
@@ -34,8 +34,9 @@
\postamble
\endpostamble
\let\MetaPrefix\DoubleperCent
+\askforoverwritefalse
\generate{%
- \file{lua-typo.sty}{\from{lua-typo.dtx}{sty}}% {sty,msg,dbg}
+ \file{lua-typo.sty}{\from{lua-typo.dtx}{sty}}% {sty,dbg}
\nopreamble
\file{lua-typo.cfg}{\from{lua-typo.dtx}{cfg}}%
\file{lua-typo-fr.ltx}{\from{lua-typo.dtx}{driver,docfr}}%
@@ -49,6 +50,9 @@
\csname fi\endcsname
%</gobble>
%<*driver>
+\RequirePackage{pdfmanagement-testphase}
+%<-docfr>\DeclareDocumentMetadata{pdfstandard=A-2b, lang=en-GB}
+%<-doc>\DeclareDocumentMetadata{pdfstandard=A-2b, lang=fr-FR}
\documentclass[a4paper]{ltxdoc}
\usepackage[dvipsnames]{xcolor}
\usepackage{fontspec}
@@ -79,14 +83,15 @@
%<-doc>\let\FrenchDoc\begingroup\let\endFrenchDoc\endgroup
%
%<-docfr>\let\FrenchDoc\comment\let\endFrenchDoc\endcomment
-%<-docfr|doc>\RecordChanges
-%<-docfr|doc>\AtEndDocument{%
-%<-docfr|doc> \clearpage
-%<-docfr|doc> \section{Change History}%
-%<-docfr|doc> \GlossaryPrologue{}%
-%<-docfr|doc> Changes are listed in reverse order (latest first) from version~0.1
-%<-docfr|doc> \PrintChanges
-%<-docfr|doc>}
+%<-docfr>\RecordChanges
+%<-docfr>\AtEndDocument{%
+%<-docfr> \clearpage
+%<-docfr> \section{Change History}%
+%<-docfr> \GlossaryPrologue{}%
+%<-docfr> Changes are listed in reverse order (latest first)
+%<-docfr> from version~0.30.
+%<-docfr> \PrintChanges
+%<-docfr>}
%
\newcommand*\file[1]{\texttt{#1}}
\newcommand*\pkg[1]{\texttt{#1}}
@@ -124,8 +129,8 @@
% L’extension \pkg{lua-typo} décrite ci-dessous%
% \footnote{Version \fileversion, mise à jour le \filedate.}
% permet de mettre en lumière par un changement de couleur, les
-% lignes typographiquement imparfaites d'un fichier PDF produit par
-% LuaLaTex. Une liste des pages concernées est affichée à la fin du
+% lignes typographiquement imparfaites d’un fichier PDF produit par
+% LuaLaTeX. Une liste des pages concernées est affichée à la fin du
% fichier \file{.log}, permettant un accès rapide aux pages
% potentiellement défectueuses.
%
@@ -137,7 +142,7 @@
% lignes consécutives, des dernières lignes d’alinéa trop courtes ou
% presque pleines, des pages quasi vides.
% La répétition d’un même mot ou partie de mot au début ou à la fin
-% de deux lignes consécutives est détectée.
+% de deux lignes consécutives est aussi détectée.
% La présence en fin de ligne de certains mots très courts (une ou
% deux lettres, liste dépendant de la langue) peut également être
% recherchée.
@@ -145,9 +150,9 @@
% \pkg{lua-typo}, ne fonctionne qu’avec LuaLaTeX et
% \emph{ne corrige aucun des défauts relevés} mais se contente de
% \emph{signaler} au relecteur les points qui peuvent nécessiter une
-% correction. Seul un œil humain entrainé peut décider si une ligne
+% correction. Seul un humain entrainé peut décider si une ligne
% légèrement lavée est acceptable ou non, ou si la suppression
-% d’une césure malvenue ne va pas provoquer des désordres plus
+% d’une coupure malvenue ne va pas provoquer des désordres plus
% graves encore.
%
% Je conseille de n’appliquer \pkg{lua-typo} que sur des textes
@@ -158,6 +163,13 @@
% \pkg{lua-typo}, il suffit d’ajouter dans le préambule la ligne\\
% |\usepackage[All]{lua-typo}|
%
+% La version courante (0.50) nécessite un noyau LaTeX très récent,
+% 2021/06/01 ou ultérieur. Ceux qui ne disposent que d’un noyau plus
+% ancient reçoivent un message d’avertissement et un message d’erreur
+% «\texttt{Unable to register callback}» ; une version «rollback »
+% est prévue à leur intention, elle se charge par la commande
+% |\usepackage[All]{lua-typo}[=v0.4]|.
+%
% Les fichiers \file{demo.tex} et \file{demo.pdf} fournissent un
% exemple du traitement opéré par \pkg{lua-typo}.
%
@@ -182,7 +194,7 @@
% Le tableau suivant donne le nom des options et le type des
% vérifications proposées :\\[12pt]
% \begin{tabular}{>{\ttfamily}ll}
-% \multicolumn{1}{l}{Nom} & Imperfection à signaler\\ \hline
+% \multicolumn{1}{l}{Nom} & Imperfection à signaler\\ \hline
% All & Active toutes les options ci-dessous\\
% ShortLines & Dernière ligne d’alinéa trop courte ?\\
% BackParindent & Dernière ligne d’alinéa \emph{presque} pleine ?\\
@@ -190,15 +202,16 @@
% OverfullLines & Ligne trop pleine ?\\
% UnderfullLines & Ligne lavée ? \\
% Widows & Veuve (haut de page) ?\\
-% Orphans & Orpheline (bas de page)\\
+% Orphans & Orpheline (bas de page) ?\\
% EOPHyphens & Mot coupé en bas de page ?\\
-% RepeatedHyphens & Césures sur trop de lignes consécutives ?\\
-% ParLastHyphen & Césure à l’avant-dernière ligne d’un alinéa ?\\
+% RepeatedHyphens & Coupures sur trop de lignes consécutives ?\\
+% ParLastHyphen & Coupure à l’avant-dernière ligne d’un alinéa ?\\
% EOLShortWords & Mots courts (1 or 2 lettres) en fin de ligne ?\\
% FirstWordMatch & Même (partie de) mot en début de lignes
% consécutives ?\\
% LastWordMatch & Même (partie de) mot en fin de lignes
% consécutives ?\\
+% FootnoteSplit & Fin de note de bas de page sur page suivante?\\
% \hline
% \end{tabular}\\[12pt]
% Par exemple, pour limiter les vérifications aux lignes trop pleines
@@ -238,28 +251,31 @@
% n’est jamais signalé comme répétition, même s’il est identique
% au mot de la ligne du dessus.
% De même, s’il est identique au dernier mot de la ligne
-% suivante, la répéttion ne sera pas signalée non plus.
+% suivante, la répéttion ne sera pas signalée non plus.
% \item[ShortPages :] lorsque le nombre de lignes d’une page est jugé
% insuffisant (voir ci-dessous), seule la dernière ligne de celle-ci
% est mise en couleur.
% \enlargethispage*{\baselineskip}
% \item[RepeatedHyphens :] de même, lorsque le nombre de lignes
-% consécutives affectées par des césures dépasse le seuil fixé
+% consécutives affectées par des coupures dépasse le seuil fixé
% (voir ci-dessous), ne sont coloriées que les coupures en excès.
% \end{description}
%
-% Enfin, le contenu des notes de bas de pages n’est pas vérifié
-% par \pkg{lua-typo} car celles-ci passent pour l’instant en dessous
-% de nos radars… il faut le savoir.
+% À partir de la version~0.50, le contenu des notes de bas de pages
+% est également vérifié, les notes trop longues pour se terminer sur
+% la page de leur appel sont signalées (option \opt{FootnoteSplit}).
+% Un fichier de suffixe \file{.typo} est créé, il contient la liste
+% complète des défauts repérés avec leur localisation dans le fichier
+% de sortie.
%
% \section{Paramétrage personnalisé}
%
% Pour certaines vérifications faites par \pkg{lua-typo} un
% paramétrage est nécessaire : à partir de quelle limite une dernière
% ligne d’alinéa est-elle considérée comme trop courte ?
-% Combien de césures consécutives en bout de ligne sont-elles
-% acceptables ? Ces réglages dépendent évidemment du contexte, un
-% correcteur de romans aura des exigences plus strictes qu’un auteur
+% Combien de coupures consécutives en bout de ligne sont-elles
+% acceptables ? Ces réglages dépendent évidemment du contexte, un
+% correcteur de romans aura des exigences plus strictes qu’un auteur
% de documentation technique par exemple…
%
% \pkg{lua-typo} permet de modifier le réglage des curseurs soit dans
@@ -278,23 +294,27 @@
%
% Voici la liste complète des paramètres personnalisables avec leur
% valeur par défaut, leurs noms sont systématiquement préfixés par
-% |luatypo| afin d’éviter de possible conflits avec d’autres
+% |luatypo| afin d’éviter de possibles conflits avec d’autres
% extensions.
% \begin{description}
-% \item[BackParindent :] la dernière ligne d’un alinéa fixe
+% \item[BackParindent :] la dernière ligne d’un alinéa fixe
% devrait, soit être pleine ---~en fait se terminer à moins de
% |\luatypoBackFuzz=2pt| de la marge droite~---, soit
% s’en éloigner d’au moins de |\luatypoBackPI=1em|.
%
-% \item[ShortLines :] |\luatypoLLminWD=2\parindent| fixe la
-% longueur minimale acceptable pour la dernière ligne d’un alinéa.
+% \item[ShortLines :] |\luatypoLLminWD=2\parindent|%
+% \footnote{Ou \texttt{20pt} si \cs{parindent=0pt}.}
+% fixe la longueur minimale acceptable pour la dernière ligne
+% d’un alinéa.
%
% \item[ShortPages :] |\luatypoPageMin=5| fixe le nombre minimal de
-% lignes d’une page pour que celle-ci ne pas être
-% signalée comme trop courte.
+% lignes d’une page pour que celle-ci ne soit pas déclarée trop
+% courte. En fait, la position de la dernière ligne est prise en
+% compte afin que les pages de titre ou celles contenant une
+% image ne soient pas signalées comme fautives.
%
% \item[RepeatedHyphens :] |\luatypoHyphMax=2| fixe le nombre maximal
-% acceptable de lignes consécutives terminées par une césure.
+% acceptable de lignes consécutives terminées par un mot coupé.
%
% \item[UnderfullLines :] |\luatypoStretchMax=200| fixe le
% pourcentage maximal acceptable pour l’étirement des
@@ -307,7 +327,7 @@
% réglages par défaut (|\tolerance=200|, |\hbadness=1000|),
% considère comme \emph{Underfull hbox}.
%
-% \item[First/LastWordMatch:] |\luatypoMinFull=3|\hfil et\hfil
+% \item[First/LastWordMatch:] |\luatypoMinFull=3|\hfil et\hfil
% |\luatypoMinPart=4|\hfil nombres\linebreak[4] minimaux de lettres
% identiques (resp. pour un mot complet ou pour une partie de
% mot) au début ou à la fin de deux lignes consécutives
@@ -321,11 +341,11 @@
% ligne de mots très courts (une ou deux lettres)
% qui sont répertoriés dans une des listes
% suivantes (elles dépendent de la langue courante) :\\
-% |\luatypoOneChar{|\meta{language}|}{'|\meta{list of words}|'}|\\
-% |\luatypoTwoChars{|\meta{language}|}{'|\meta{list of words}|'}|
+% |\luatypoOneChar{|\meta{langue}|}{'|\meta{liste de mots}|'}|\\
+% |\luatypoTwoChars{|\meta{langue}|}{'|\meta{liste de mots}|'}|
%
% Lorsque les listes correspondant à la langue du document sont
-% vides, aucune vérification n’est effectuée. Pour l’instant,
+% vides, aucune vérification n’est effectuée. Pour l’instant,
% il y a deux lignes (non actives) prévues pour le français :\\
% |\luatypoOneChar{french}{'À à Ô'}|\\
% |\luatypoTwoChars{french}{'Je Tu Il On'}|
@@ -352,10 +372,10 @@
% \begin{verbatim}
% \definecolor{mygrey}{gray}{0.6}
% \definecolor{myred}{rgb}{1,0.55,0}
-% \luatypoSetColor0{red} % Césure à l’avant-dernière ligne d’un alinéa
-% \luatypoSetColor1{red} % Césure en bas de page
-% \luatypoSetColor2{red} % Césures sur trop de lignes consécutives
-% \luatypoSetColor3{red} % Mot d’une ou deux lettres en fin de ligne
+% \luatypoSetColor0{red} % Coupure à l’avant-dernière ligne
+% \luatypoSetColor1{red} % Coupure en bas de page
+% \luatypoSetColor2{red} % Coupures consécutives
+% \luatypoSetColor3{red} % Mot court en fin de ligne
% \luatypoSetColor4{cyan} % Veuve
% \luatypoSetColor5{cyan} % Orpheline
% \luatypoSetColor6{cyan} % Dernière ligne d’alinéa trop courte
@@ -365,6 +385,7 @@
% \luatypoSetColor{10}{myred} % Répétitions en début de ligne
% \luatypoSetColor{11}{myred} % Répétitions en fin de ligne
% \luatypoSetColor{12}{mygrey}% Dernière ligne d’alinéa presque pleine
+% \luatypoSetColor{13}{cyan} % Note de bas de page éclatée
% \end{verbatim}
% \pkg{lua-typo} charge l’extension graphique \pkg{color}.
% Seules les couleurs portant un nom (\emph{named colors}) peuvent
@@ -412,6 +433,12 @@
% made: if some flaws remain, getting them printed in colour in
% the final document would be a shame!
%
+% This version (0.50) requires the latest LaTeX kernel (dated
+% 2021/06/01). Users running an older kernel will get a warning
+% and an error message ``\texttt{Unable to register callback}’’;
+% for them, a ``rollback’’ version of \pkg{lua-typo} is provided,
+% it can be loaded this way: |\usepackage[All]{lua-typo}[=v0.4]|.
+%
% See files \file{demo.tex} and \file{demo.pdf} for a short example
% (in French).
%
@@ -445,14 +472,15 @@
% OverfullLines & overfull lines?\\
% UnderfullLines & underfull lines?\\
% Widows & widows (top of page)?\\
-% Orphans & orphans (bottom of page)\\
+% Orphans & orphans (bottom of page)?\\
% EOPHyphens & hyphenated word split across two pages?\\
% RepeatedHyphens & too many consecutive hyphens?\\
% ParLastHyphen & paragraph’s last full line hyphenated?\\
% EOLShortWords & short words (1 or 2 chars) at end of line?\\
% FirstWordMatch & same (part of) word starting two consecutive lines?\\
% LastWordMatch & same (part of) word ending two consecutive lines?\\
-% \hline
+% FootnoteSplit & footnotes spread over two pages or more?\\
+% \hline
% \end{tabular}\\[12pt]
% For example, if you want \pkg{lua-typo} to only warn about overfull
% and underfull lines, you can load \pkg{lua-typo} like this:\\
@@ -464,13 +492,13 @@
% taken into account as they are read \emph{i.e.} from left to right.
%
% The list of all available options is printed to the \file{.log}
-% file when option \opt{ShowOptions} is passed to \pkg{lua-typo}, an
-% easy way to get their names without having to look into the
-% documentation.
+% file when option \opt{ShowOptions} is passed to \pkg{lua-typo},
+% this option provides an easy way to get their names without having
+% to look into the documentation.
%
% With option \opt{None}, \pkg{lua-typo} \emph{does absolutely
% nothing}, all checks are disabled as the main function is not added
-% to any LuaTeX callback. It not quite equivalent to commenting out
+% to any LuaTeX callback. It not quite equivalent to commenting out
% the |\usepackage{lua-typo}| line though, as user defined commands
% related to \pkg{lua-typo} are still defined and will not print
% any error message.
@@ -480,22 +508,24 @@
% \item[FirstWordMatch:] the first word of consecutive list items
% is not highlighted, as these repetitions result of the author’s
% choice.
-% \item[LastWordMatch:] a paragraphs’ last word ending ``too far’’
+% \item[LastWordMatch:] a paragraphs’ last word ending ``too far’’
% from the right margin (\emph{i.e.} more than
% |\luatypoBackPI| --default=1em-- away) is never highlighted
% even if it matches the one on the previous line.
% Similarly, if it matches the one on the next line, the latter
% will not be highlighted either.
% \item[ShortPages:] if a page is considered too short, its last
-% line only is highlighted, not the whole page.
+% line only is highlighted, not the whole page.
% \item[RepeatedHyphens:] ditto, when the number of consecutives
% hyphenated lines is too high, only the hyphenated words in
% excess (the last ones) are hightlighted.
% \end{description}
-%
-% Finally, please note that the footnotes’ contents are not checked
-% by \pkg{lua-typo}, I have currently no clue of how to do that,
-% hints are welcome!
+% \enlargethispage*{\baselineskip}
+% Starting with version~0.50, the footnotes’ contents are checked as
+% well by \pkg{lua-typo} and footnotes too long to end on the current
+% page are mentionned as a flaw (option \opt{FootnoteSplit}).
+% The list of all flaws found is written to a specific log-file whose
+% name is suffixed by \file{.typo}.
%
% \section{Customisation}
%
@@ -507,7 +537,7 @@
%
% A default configuration file \file{lua-typo.cfg} is provided
% with all parameters set to their defaults; it is located under
-% the \textsc{texmfdist} directory. It is up to the users to copy
+% the \textsc{texmfdist} directory. It is up to the users to copy
% this file into their working directory (or \textsc{texmfhome} or
% \textsc{texmflocal}) and tune the defaults according to their own
% taste.
@@ -521,17 +551,21 @@
% Here are the parameters names (all prefixed by |luatypo| in order
% to avoid conflicts with other packages) and their default values:
% \begin{description}
-% \item[BackParindent :] paragraphs’ last line should either
+% \item[BackParindent :] paragraphs’ last line should either
% touch the right margin (actually end at less than
-% |\luatypoBackFuzz|, default |2pt|, from it) or leave at least
+% |\luatypoBackFuzz|, default |2pt|, from it) or leave at least
% |\luatypoBackPI|, default |1em|, between its end and the right
% margin.
%
-% \item[ShortLines:] |\luatypoLLminWD=2\parindent| sets the
-% minimum acceptable length for paragraphs’ last lines.
+% \item[ShortLines:] |\luatypoLLminWD=2\parindent|%
+% \footnote{Or \texttt{20pt} if \cs{parindent=0pt}.}
+% sets the minimum acceptable length for paragraphs’ last lines.
%
-% \item[ShortPages:] |\luatypoPageMin=5| sets the minimum acceptable
-% number of lines on a page (chapters’ last page for instance).
+% \item[ShortPages:] |\luatypoPageMin=5| sets the minimum
+% acceptable number of lines on a page (chapters’ last page
+% for instance). Actually, the last line’s vertical position on
+% the page is taken into account so that f.i.\ title pages or
+% pages ending on a picture are not pointed out.
%
% \item[RepeatedHyphens:] |\luatypoHyphMax=2| sets the maximum
% acceptable number of consecutive hyphenated lines.
@@ -546,14 +580,14 @@
% ``Underfull hbox’’ message (when |\tolerance=200| and
% |\hbadness=1000|).
%
-% \item[First/LastWordMatch:] |\luatypoMinFull=3| and
+% \item[First/LastWordMatch:] |\luatypoMinFull=3| and
% |\luatypoMinPart=4| set the minimum number of characters
-% required for a match to be pointed out. With this setting (3
+% required for a match to be pointed out. With this setting (3
% and 4), two occurrences of the word `out’ at the beginning or
% end of two consecutive lines will be highlighted (three chars,
% `in’ wouldn’t match), whereas a line ending with ``full’’ or
% ``overfull’’ followed by one ending with ``underfull’’ will
-% match (four chars): the second occurence of ``full’’ or
+% match (four chars): the second occurence of ``full’’ or
% ``erfull’’ will be highlighted.
%
% \item[EOLShortWords:] this check deals with lines ending with
@@ -598,12 +632,13 @@
% \luatypoSetColor{10}{myred} % First word matches
% \luatypoSetColor{11}{myred} % Last word matches
% \luatypoSetColor{12}{mygrey}% paragraph’s last line nearly full
+% \luatypoSetColor{13}{cyan} % footnotes spread over two pages
% \end{verbatim}
%
% \pkg{lua-typo} loads the \pkg{color} package from the LaTeX graphic
-% bundle. Only named colours can be used by \pkg{lua-typo}, so you can
-% either use the |\definecolor| from \pkg{color} package to define
-% yours (as done in the config file for `mygrey’) or load the
+% bundle. Only named colours can be used by \pkg{lua-typo}, so you
+% can either use the |\definecolor| from \pkg{color} package to
+% define yours (as done in the config file for `mygrey’) or load the
% \pkg{xcolor} package which provides a bunch of named colours.
%
% \clearpage
@@ -611,18 +646,41 @@
%
% \iffalse
%<*sty>
-\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+%% IMPORTANT NOTICE:
+%% For the copyright see the source file `lua-typo.dtx’.
+%%
\ProvidesPackage{lua-typo}
%</sty>
%<*dtx>
\ProvidesFile{lua-typo.dtx}
%</dtx>
%<*dtx|sty>
- [2021/03/14 v.0.32 Daniel Flipo]
+ [2021/05/13 v.0.50 Daniel Flipo]
%</dtx|sty>
%<*sty>
% \fi
%
+% \changes{v0.50}{2021/05/05}{Rollback mechanism used for recovering
+% older versions.}
+%
+% Starting with version~0.50, this package uses the rollback
+% mechanism to provide easier backward compatibility.
+% Rollback version~0.40 is provided for users who would have
+% a LaTeX kernel older than 2021/06/01.
+%
+% \begin{macrocode}
+\ifdefined\DeclareRelease
+ \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
+ \DeclareCurrentRelease{}{2021-05-13}
+\else
+ \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide
+ access\MessageBreak to former versions of the lettrine package.%
+ \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated%
+ \MessageBreak 2020-01-01 or newer; reported}
+\fi
+\NeedsTeXFormat{LaTeX2e}[2021/06/01]
+% \end{macrocode}
+%
% This package only runs with LuaLaTeX and requires packages
% \pkg{luatexbase}, \pkg{luacode}, \pkg{luacolor} and
% \pkg{atveryend}.
@@ -684,6 +742,7 @@ luatypo = { }
\DeclareBoolOption[false]{EOLShortWords}
\DeclareBoolOption[false]{FirstWordMatch}
\DeclareBoolOption[false]{LastWordMatch}
+\DeclareBoolOption[false]{FootnoteSplit}
% \end{macrocode}
% Option \opt{All} resets all booleans relative to specific
% typographic checks to \opt{true}.
@@ -695,7 +754,7 @@ luatypo = { }
\LT@EOPHyphenstrue \LT@RepeatedHyphenstrue
\LT@ParLastHyphentrue \LT@EOLShortWordstrue
\LT@FirstWordMatchtrue \LT@LastWordMatchtrue
- \LT@BackParindenttrue
+ \LT@BackParindenttrue \LT@FootnoteSplittrue
}
\ProcessKeyvalOptions{luatypo}
% \end{macrocode}
@@ -790,6 +849,12 @@ luatypo = { }
\else
\directlua{ luatypo.LastWordMatch = false }%
\fi
+ \ifLT@FootnoteSplit
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.FootnoteSplit = true }%
+ \else
+ \directlua{ luatypo.FootnoteSplit = false }%
+ \fi
}
% \end{macrocode}
%
@@ -816,6 +881,7 @@ luatypo = { }
EOLShortWords [false]\MessageBreak
FirstWordMatch [false]\MessageBreak
LastWordMatch [false]\MessageBreak
+ FootnoteSplit [false]\MessageBreak
\MessageBreak
*********************************************%
\MessageBreak Lua-typo [ShowOptions]
@@ -842,7 +908,8 @@ luatypo = { }
% \end{macrocode}
%
% Print the summary of offending pages ---if any--- at the
-% (very) end of document unless option |None| has been selected.
+% (very) end of document and write the report file on disc,
+% unless option |None| has been selected.
%
% \begin{macrocode}
\AtVeryEndDocument{%
@@ -869,6 +936,10 @@ luatypo = { }
end
texio.write_nl('***********************************')
texio.write_nl(' ')
+ local fileout= tex.jobname .. ".typo"
+ local out=io.open(fileout,"w+")
+ out:write(luatypo.buffer)
+ io.close(out)
}%
\fi}
% \end{macrocode}
@@ -927,7 +998,7 @@ luatypo = { }
% \vspace*{\baselineskip}
% \begin{macro}{\luatypoSetColor}
% This is a user-level command to customise the colours highlighting
-% the nine types of possible typographic flaws.
+% the fourteen types of possible typographic flaws.
% The first argument is a number (flaw type), the second the named
% colour associated to it.
% The colour support is based on the \pkg{luacolor} package (color
@@ -950,13 +1021,8 @@ luatypo.single = { }
luatypo.double = { }
luatypo.colortbl = { }
luatypo.pagelist = ""
-
-local hyphcount = 0
-local parlines = 0
-local pagelines = 0
-local pageno = 0
-local prevno = 0
-local pageflag = false
+luatypo.buffer = "List of typographic flaws found for "
+ .. tex.jobname .. ".tex:\string\n\string\n"
local char_to_discard = { }
char_to_discard[string.byte(",")] = true
@@ -980,11 +1046,14 @@ local DISC = node.id("disc")
local GLYPH = node.id("glyph")
local GLUE = node.id("glue")
local KERN = node.id("kern")
+local RULE = node.id("rule")
local HLIST = node.id("hlist")
+local VLIST = node.id("vlist")
local LPAR = node.id("local_par")
local MKERN = node.id("margin_kern")
local PENALTY = node.id("penalty")
-% \end{macrocode}
+local WHATSIT = node.id("whatsit")
+% \end{macrocode}
% Glue subtypes:
% \begin{macrocode}
local USRSKIP = 0
@@ -993,11 +1062,14 @@ local LFTSKIP = 8
local RGTSKIP = 9
local TOPSKIP = 10
local PARFILL = 15
-% \end{macrocode}
+% \end{macrocode}
% Hlist subtypes:
% \begin{macrocode}
local LINE = 1
local BOX = 2
+local INDENT = 3
+local ALIGN = 4
+local EQN = 6
% \end{macrocode}
% Penalty subtypes:
% \begin{macrocode}
@@ -1007,7 +1079,13 @@ local HYPH = 0x2D
% Glyph subtypes:
% \begin{macrocode}
local LIGA = 0x102
+% \end{macrocode}
+% |parline| (current paragraph) must not be reset on every new page!
+% \begin{macrocode}
+local parline = 0
+local dimensions = node.dimensions
+local rangedimensions = node.rangedimensions
local effective_glue = node.effective_glue
local set_attribute = node.set_attribute
local slide = node.slide
@@ -1019,6 +1097,9 @@ local is_glyph = node.is_glyph
% \end{macrocode}
%
+% \changes{v0.32}{2021/03/14}{Better protection against unexpected
+% nil nodes.}
+%
% This auxillary function colours glyphs and discretionaries.
% It requires two arguments: a node and a (named) colour.
%
@@ -1035,55 +1116,119 @@ local color_node = function (node, color)
end
if post then
set_attribute(post,attr,color)
-%<dbg> texio.write_nl('POST=' .. tostring(post.char))
+%<dbg> if pre then
+%<dbg> texio.write(' POST=' .. tostring(post.char))
+%<dbg> else
+%<dbg> texio.write_nl('POST=' .. tostring(post.char))
+%<dbg> end
end
if repl then
set_attribute(repl,attr,color)
-%<dbg> texio.write_nl('REPL=' .. tostring(repl.char))
+%<dbg> if pre or post then
+%<dbg> texio.write(' REPL=' .. tostring(repl.char))
+%<dbg> else
+%<dbg> texio.write_nl('REPL=' .. tostring(repl.char))
+%<dbg> end
end
+%<dbg> if pre or post or repl then
%<dbg> texio.write_nl(' ')
+%<dbg> end
elseif node then
set_attribute(node,attr,color)
end
end
% \end{macrocode}
%
-% This auxillary function colours the content of an |\hbox|.
-% It requires two arguments: a node (the box) and a (named) colour.
+% This auxillary function colours a whole line. It requires two
+% arguments: a line’s node and a (named) colour.\par
+% Digging into nested hlists and vlists is needed f.i.\ to colour
+% aligned equations.
+%
+% \changes{v0.50}{2021/05/05}{Go down deeper into hlists and vlists to
+% colour nodes.}
%
% \begin{macrocode}
-local color_hbox = function (head, color)
+local color_line = function (head, color)
local first = head.head
for n in traverse(first) do
- color_node(n, color)
+ if n.id == HLIST or n.id == VLIST then
+ local ff = n.head
+ for nn in traverse(ff) do
+ if nn.id == HLIST or nn.id == VLIST then
+ local f3 = nn.head
+ for n3 in traverse(f3) do
+ if n3.id == HLIST or n3.id == VLIST then
+ local f4 = n3.head
+ for n4 in traverse(f4) do
+ if n4.id == HLIST or n4.id == VLIST then
+ local f5 = n4.head
+ for n5 in traverse(f5) do
+ if n5.id == HLIST or n5.id == VLIST then
+ local f6 = n5.head
+ for n6 in traverse(f6) do
+ color_node(n6, color)
+ end
+ else
+ color_node(n5, color)
+ end
+ end
+ else
+ color_node(n4, color)
+ end
+ end
+ else
+ color_node(n3, color)
+ end
+ end
+ else
+ color_node(nn, color)
+ end
+ end
+ else
+ color_node(n, color)
+ end
end
end
% \end{macrocode}
%
-% This auxillary function colours a whole line. It requires two
-% arguments: a line’s node and a (named) colour.
+% This function appends a line to a buffer which will be written
+% to file `\cs{jobname.typo}’; it takes four arguments:
+% a string, two numbers (which can be \texttt{nil}) and a flag.
+%
+% \changes{v0.50}{2021/05/13}{Summary of flaws written to file
+% `\cs{jobname.typo}’.}
%
% \begin{macrocode}
-local color_line = function (head, color)
- local first = head.head
- for n in traverse(first) do
- if n and n.id == HLIST and n.subtype == BOX then
- color_hbox(n, color)
- else
- color_node(n, color)
- end
+log_flaw= function (msg, line, colno, footnote)
+ local pageno = tex.getcount("c@page")
+ local prt ="p. " .. pageno
+ if colno then
+ prt = prt .. ", col." .. colno
+ end
+ if line then
+ local l = string.format("%2d, ", line)
+ if footnote then
+ prt = prt .. ", (ftn.) line " .. l
+ else
+ prt = prt .. ", line " .. l
+ end
end
+ prt = prt .. msg
+ luatypo.buffer = luatypo.buffer .. prt .. "\string\n"
end
% \end{macrocode}
%
-% The next three functions deal with ``homeoarchy’’, \emph{i.e.}
+% The next three functions deal with ``homeoarchy'', \emph{i.e.}
% lines beginning or ending with the same (part of) word.
% While comparing two words, the only significant nodes are glyphs
% and ligatures, dicretionnaries other than ligatures, kerns
% (letterspacing) should be discarded.
-% For each word to be compared we build a ``signature’’ made of
+% For each word to be compared we build a ``signature'' made of
% glyphs and split ligatures.
%
+% \changes{v0.32}{2021/03/14}{Experimental code to deal with non
+% standard ligatures.}
+%
% The first function adds a node to a signature of type string.
% It returns the augmented string and its length.
% The last argument is a boolean needed when building a signature
@@ -1096,8 +1241,9 @@ local signature = function (node, string, swap)
local b, id = is_glyph(n)
if b and not char_to_discard[b] then
% \end{macrocode}
-% Punctuation has to be discarded; the French apostrophe (right quote
-% U+2019) has a char code ``out of range’’, we replace it with U+0027;
+% Punctuation has to be discarded; the French apostrophe
+% (right quote U+2019) has a char code ``out of range'',
+% we replace it with U+0027;
% Other glyphs should have char codes less than 0x100 (or 0x180?) or
% be ligatures… standard ones (U+FB00 to U+FB06) are converted using
% table |split_lig|.
@@ -1126,7 +1272,7 @@ local signature = function (node, string, swap)
elseif n and n.id == DISC then
% \end{macrocode}
% Ligatures are split into |pre| and |post| and both parts are
-% stored. In case of \emph{ffl, ffi}, the post part is also
+% stored. In case of \emph{ffl, ffi}, the post part is also
% a ligature…
% \begin{macrocode}
local pre = n.pre
@@ -1163,37 +1309,65 @@ local signature = function (node, string, swap)
end
% \end{macrocode}
%
-% This auxillary function looks for lines ending with the same letters.
-% It requires four arguments: a string (previous line’s signature),
-% a node (the last one on the current line), a (named) colour and a
-% boolean to cancel the coloration in some cases (end of paragraphs).
+% This auxillary function looks for consecutive lines ending with the
+% same letters.
+% It requires four arguments: a string (previous line’s signature),
+% a node (the last one on the current line), a line number and
+% a boolean to cancel checking in some cases (end of paragraphs).
% It prints the matching part at end of linewith with the supplied
% colour and returns the current line’s last word and a boolean (match).
%
+% \changes{v0.32}{2021/03/14}{Functions `check\_last\_word’ and
+% `check\_last\_word’ rewritten.}
+%
+% \changes{v0.50}{2021/05/13}{Homeoarchy detection added for lines
+% starting or ending on \cs{mbox}.}
+%
% \begin{macrocode}
-local check_last_word = function (old, node, color, flag)
+local check_last_word = function (old, node, line, flag)
+ local COLOR = luatypo.colortbl[11]
local match = false
local new = ""
local maxlen = 0
if flag and node then
local swap = true
+ local box, go
% \end{macrocode}
% Step back to the last glyph or discretionary.
% \begin{macrocode}
local lastn = node
- while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC do
+ while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC and
+ lastn.id ~= HLIST do
lastn = lastn.prev
end
% \end{macrocode}
% A signature is built from the last two words on the current line.
% \begin{macrocode}
local n = lastn
+ if n and n.id == HLIST then
+ box = n
+ prev = n.prev
+ lastn = slide(n.head)
+ n = lastn
+ end
while n and n.id ~= GLUE do
maxlen, new = signature (n, new, swap)
n = n.prev
end
if n and n.id == GLUE then
new = new .. "_"
+ go = true
+ elseif box and not n then
+ local p = box.prev
+ if p.id == GLUE then
+ new = new .. "_"
+ n = p
+ else
+ n = box
+ end
+ go = true
+ end
+ if go then
repeat
n = n.prev
maxlen, new = signature (n, new, swap)
@@ -1239,11 +1413,11 @@ local check_last_word = function (old, node, color, flag)
newsub = nsub
end
end
+ pageflag = true
newsub = string.gsub(newsub, '^_', '')
-%<dbg> texio.write_nl('EOLfullmatch=' .. newsub)
-%<msg> texio.write_nl('***EOLMATCH=' .. newsub ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
+%<dbg> texio.write_nl('EOLfullmatch=' .. newsub)
+ local msg = "E.O.L. MATCH=" .. newsub
+ log_flaw(msg, line, colno, footnote)
% \end{macrocode}
% Lest's colour the matching string.
% \begin{macrocode}
@@ -1252,39 +1426,63 @@ local check_last_word = function (old, node, color, flag)
local n = lastn
repeat
if n and n.id ~= GLUE then
- color_node(n, color)
+ color_node(n, COLOR)
l, newsub = signature(n, newsub, swap)
- elseif n then
+ elseif n and n.id == GLUE then
newsub = newsub .. "_"
+ elseif not n and box then
+ n = box
+ else
+ break
end
n = n.prev
- until not n or newsub == oldsub or l >= k
+ until newsub == oldsub or l >= k
end
end
- return new, match
+ return new
end
% \end{macrocode}
%
-% Same thing for beginning of lines: check the first two words and
-% compare their signature with the previous line’s.
+% Same thing for beginning of lines: check the first two words
+% and compare their signature with the previous line’s.
%
% \begin{macrocode}
-local check_first_word = function (old, node, color, flag)
+local check_first_word = function (old, node, line, flag)
+ local COLOR = luatypo.colortbl[10]
local match = false
local swap = false
local new = ""
local maxlen = 0
- local start = node
- local n = start
- while n and n.id ~= GLYPH and n.id ~= DISC do
+ local n = node
+ local box, go
+ while n and n.id ~= GLYPH and n.id ~= DISC and
+ (n.id ~= HLIST or n.subtype == INDENT) do
n = n.next
end
+ local start = n
+ if n and n.id == HLIST then
+ box = n
+ start = n.head
+ n = n.head
+ end
while n and n.id ~= GLUE do
maxlen, new = signature (n, new, swap)
n = n.next
end
if n and n.id == GLUE then
new = new .. "_"
+ go = true
+ elseif box and not n then
+ local bn = box.next
+ if bn.id == GLUE then
+ new = new .. "_"
+ n = bn
+ else
+ n = box
+ end
+ go = true
+ end
+ if go then
repeat
n = n.next
maxlen, new = signature (n, new, swap)
@@ -1337,13 +1535,13 @@ local check_first_word = function (old, node, color, flag)
newsub = nsub
end
end
+ pageflag = true
newsub = string.gsub(newsub, '_$', '') --$
%<dbg> texio.write_nl('BOLfullmatch=' .. newsub)
-%<msg> texio.write_nl('***BOLMATCH=' .. newsub ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
+ local msg = "B.O.L. MATCH=" .. newsub
+ log_flaw(msg, line, colno, footnote)
% \end{macrocode}
-% Lest's colour the matching string.
+% Lest’s colour the matching string.
% \begin{macrocode}
oldsub = newsub
local newsub = ""
@@ -1351,27 +1549,32 @@ local check_first_word = function (old, node, color, flag)
local n = start
repeat
if n and n.id ~= GLUE then
- color_node(n, color)
+ color_node(n, COLOR)
l, newsub = signature(n, newsub, swap)
- elseif n then
+ elseif n and n.id == GLUE then
newsub = newsub .. "_"
+ elseif not n and box then
+ n = box
+ else
+ break
end
n = n.next
- until not n or newsub == oldsub or l >= k
+ until newsub == oldsub or l >= k
end
end
- return new, match
+ return new
end
% \end{macrocode}
%
% This auxillary function looks for a short word (one or two chars)
% at end of lines, compares it to a given list and colours it if
-% matches. The argument must be a node of type GLYPH, usually
-% the last line’s node.\\
+% matches. The first argument must be a node of type |GLYPH|,
+% usually the last line’s node, the second one is the line number.\\
% TODO: where does ``out of range’’ starts? U+0100? U+0180?
%
% \begin{macrocode}
-local check_regexpr = function (glyph)
+local check_regexpr = function (glyph, line)
+ local pageno = tex.getcount("c@page")
local COLOR = luatypo.colortbl[3]
local lang = glyph.lang
local match = false
@@ -1387,10 +1590,10 @@ local check_regexpr = function (glyph)
if lchar and lchar < 0x100 and previous and previous.id == GLUE then
match = string.find(luatypo.single[lang], string.char(lchar))
if match then
+ pageflag = true
+ local msg = "RGX MATCH=" .. string.char(lchar)
+ log_flaw(msg, line, colno, footnote)
color_node(glyph,COLOR)
-%<msg> texio.write_nl('***RGXMATCH=' .. string.char(lchar) ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
end
end
end
@@ -1408,11 +1611,11 @@ local check_regexpr = function (glyph)
local pattern = string.char(pchar) .. string.char(lchar)
match = string.find(luatypo.double[lang], pattern)
if match then
+ pageflag = true
+ local msg = "RGX MATCH=" .. pattern
+ log_flaw(msg, line, colno, footnote)
color_node(previous,COLOR)
color_node(glyph,COLOR)
-%<msg> texio.write_nl('***RGXMATCH=' .. pattern ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
end
end
% \end{macrocode}
@@ -1423,27 +1626,27 @@ local check_regexpr = function (glyph)
if pprev and pprev.id == GLYPH then
local pchar, id = is_glyph(pprev)
local ppprev = pprev.prev
- if pchar and pchar < 0x100 and ppprev and ppprev.id == GLUE then
+ if pchar and pchar < 0x100 and
+ ppprev and ppprev.id == GLUE then
local pattern = string.char(pchar) .. string.char(lchar)
match = string.find(luatypo.double[lang], pattern)
if match then
+ pageflag = true
+ local msg = "RGX MATCH=" .. pattern
+ log_flaw(msg, line, colno, footnote)
color_node(pprev,COLOR)
color_node(glyph,COLOR)
-%<msg> texio.write_nl('***RGXMATCH=' .. pattern ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
end
end
end
end
end
- return match
end
% \end{macrocode}
%
% This auxillary function prints the first part of an hyphenated word
% up to the discretionary, with a supplied colour.
-% It requires two arguments: a DISC node and a (named) colour.
+% It requires two arguments: a |DISC| node and a (named) colour.
%
% \begin{macrocode}
local show_pre_disc = function (disc, color)
@@ -1453,149 +1656,297 @@ local show_pre_disc = function (disc, color)
n = n.prev
end
return n
+ end
+% \end{macrocode}
+%
+% This auxillary function scans the `vlists’ in search of the page body.
+% It returns the corresponding node or nil in case of failure.
+%
+% \changes{v0.50}{2021/05/02}{New function `get\_pagebody’ required for
+% callback `pre\_shipout\_filter’.}
+%
+% \begin{macrocode}
+local get_pagebody = function (head)
+ local textht = tex.getdimen("textheight")
+ local fn = head.list
+ local body = nil
+ repeat
+ fn = fn.next
+ until fn.id == VLIST and fn.height > 0
+%<dbg> texio.write_nl(' ht=' .. fn.height/65536 .. 'pt')
+%<dbg> texio.write(' dp=' .. fn.depth/65536 .. 'pt')
+ first = fn.list
+ for n in traverse_id(VLIST,first) do
+ if n.subtype == 0 and n.height == textht then
+%<dbg> texio.write_nl(' BODY: ' .. n.height/65536 .. 'pt')
+ body = n
+ break
+ else
+%<dbg> texio.write_nl(' ht=' .. n.height/65536 .. 'pt')
+%<dbg> texio.write_nl(' dp=' .. n.depth/65536 .. 'pt')
+ first = n.list
+ for n in traverse_id(VLIST,first) do
+ if n.subtype == 0 and n.height == textht then
+%<dbg> texio.write_nl(' BODY: ' .. n.height/65536 .. 'pt')
+ body = n
+ break
+ end
+ end
+ end
+ end
+ if not body then
+ texio.write_nl('***lua-typo ERROR: PAGE BODY *NOT* FOUND!***')
+ end
+ return body
end
% \end{macrocode}
%
-% This is the main function which will be added to the
-% |"pre_output_filter"| callback unless option |None| is selected.
+% This auxillary function scans the current `vlist’ in search
+% of a |\footnoterule| (kern, rule, kern, totalheight=0).
+% It returns |true| if found, false othewise.
+%
+% \changes{v0.50}{2021/05/02}{New function `footnoterule\_ahead’.}
%
% \begin{macrocode}
-luatypo.check_page = function (head)
+local footnoterule_ahead = function (head, debug)
+ local n = head
+ local flag = false
+ if n and n.id == KERN and n.subtype == 1 then
+ local htr = n.kern
+ local ht1, ht2, ht3
+%<dbg> if debug then
+%<dbg> ht1 = string.format("%.2fpt", n.kern/65536)
+%<dbg> end
+ n = n.next
+ if n and n.id == RULE and n.subtype == 0 then
+ htr = htr + n.height
+%<dbg> if debug then
+%<dbg> ht2 = string.format("%.2fpt", n.height/65536)
+%<dbg> end
+ n = n.next
+ if n and n.id == KERN and n.subtype == 1 then
+ htr = htr + n.kern
+%<dbg> if debug then
+%<dbg> ht3 = string.format("%.2fpt", n.kern/65536)
+%<dbg> texio.write_nl(' ')
+%<dbg> texio.write_nl('KERN height: ' .. ht1)
+%<dbg> texio.write(' RULE height: ' .. ht2)
+%<dbg> texio.write(' KERN height: ' .. ht3)
+%<dbg> texio.write_nl('TOTAL height: ' .. htr .. 'sp')
+%<dbg> end
+ if htr == 0 then
+ flag = true
+%<dbg> if debug then
+%<dbg> texio.write(' => footnoterule found!')
+%<dbg> end
+ end
+ end
+ end
+ end
+ return flag
+end
+% \end{macrocode}
+%
+% This function scans the page body (or each column) in search of
+% typographical flaws.
+%
+% \changes{v0.40}{2021/04/18}{Title pages, pages with figures and/or
+% tables may not be empty pages: check `vpos’ last line’s position.}
+%
+% \begin{macrocode}
+check_vtop = function (head, colno)
local PAGEmin = luatypo.PAGEmin
local HYPHmax = luatypo.HYPHmax
local LLminWD = luatypo.LLminWD
local BackPI = luatypo.BackPI
local BackFuzz = luatypo.BackFuzz
- local BackParindent = luatypo.BackParindent
- local ShortLines = luatypo.ShortLines
- local ShortPages = luatypo.ShortPages
- local OverfullLines = luatypo.OverfullLines
- local UnderfullLines = luatypo.UnderfullLines
- local Widows = luatypo.Widows
- local Orphans = luatypo.Orphans
- local EOPHyphens = luatypo.EOPHyphens
+ local BackParindent = luatypo.BackParindent
+ local ShortLines = luatypo.ShortLines
+ local ShortPages = luatypo.ShortPages
+ local OverfullLines = luatypo.OverfullLines
+ local UnderfullLines = luatypo.UnderfullLines
+ local Widows = luatypo.Widows
+ local Orphans = luatypo.Orphans
+ local EOPHyphens = luatypo.EOPHyphens
local RepeatedHyphens = luatypo.RepeatedHyphens
- local FirstWordMatch = luatypo.FirstWordMatch
- local ParLastHyphen = luatypo.ParLastHyphen
- local EOLShortWords = luatypo.EOLShortWords
- local LastWordMatch = luatypo.LastWordMatch
- local Stretch = math.max(luatypo.Stretch/100,1)
-
+ local FirstWordMatch = luatypo.FirstWordMatch
+ local ParLastHyphen = luatypo.ParLastHyphen
+ local EOLShortWords = luatypo.EOLShortWords
+ local LastWordMatch = luatypo.LastWordMatch
+ local FootnoteSplit = luatypo.FootnoteSplit
+ local Stretch = math.max(luatypo.Stretch/100,1)
+ local blskip = tex.getglue("baselineskip")
+ local pageno = tex.getcount("c@page")
+ local vpos_min = PAGEmin * blskip
+ vpos_min = vpos_min * 1.5
+ local vpos = 0
+ local pageflag = false
+ local body_bottom = false
+ local page_bottom = false
+ local first_bot = true
+ local footnote = false
+ local ftnsplit = false
local orphanflag = false
local widowflag = false
local lwhyphflag = false
- local match1 = false
- local match2 = false
+ local pageshort = false
local firstwd = ""
local lastwd = ""
+ local hyphcount = 0
+ local pageline = 0
+ local ftnline = 0
+ local line = 0
+% \end{macrocode}
+% The main loop scans the content of the |\vtop| holding the page
+% (or column) body, footnotes included.
+% The vertical position of the current node is stored in the |vpos|
+% dimension (integer in `sp’ units).
+% \begin{macrocode}
while head do
local nextnode = head.next
- local prevnode = head.prev
- local pprevnode = nil
- if prevnode then
- pprevnode = prevnode.prev
- end
% \end{macrocode}
-% If the current node is a glue of type topskip, we are starting new
-% page, let’s reset some counters and flags : |pageflag| will be set
-% to |true| if a possible typographic flaw is found on this page,
-% and trigger the addition of this page number to the summary list.
-% |hyphcount| hold the number the consecutive hyphenated lines.
-% \begin{macrocode}
- if head.id == GLUE and head.subtype == TOPSKIP then
- pageno = tex.getcount("c@page")
- hyphcount = 0
- if pageno > prevno then
- pageflag = false
- pagelines = 0
- match1 = false
+% If a |\footnoterule| is found, set the |footnote| flag and reset
+% some counters and flags for the coming footnotes.
+% \begin{macrocode}
+ if not footnote and head.id == KERN and head.subtype == 1 then
+ if footnoterule_ahead(head, true) then
+ footnote = true
+ ftnline = 0
+ body_bottom = false
+ orphanflag = false
+ lwhyphflag = false
+ hyphcount = 0
firstwd = ""
lastwd = ""
- prevno = pageno
- end
- elseif head.id == HLIST and head.subtype == LINE then
-% \end{macrocode}
-% The current node is a line, |first| is the line’s first node.
-% Skip margin kern or leftskip if any.
-% \begin{macrocode}
- local first = head.head
- if first.id == MKERN or
- (first.id == GLUE and first.subtype == LFTSKIP) then
- first = first.next
+ else
+ vpos = vpos + head.kern
end
- pagelines = pagelines + 1
- local ListItem = false
+ elseif head.id == HLIST and head.subtype == LINE and
+ (head.height > 0 or head.depth > 0) then
% \end{macrocode}
-% Is this line really a text line (one glyph at least)?
+% This is a text line, increment counters |pageline| or
+% |ftnline| and |line| (for |log_flaw|).
% \begin{macrocode}
- local textline = false
- if first.id == GLYPH then
- textline = true
+ if footnote then
+ ftnline = ftnline + 1
+ line = ftnline
else
- local n = first
- repeat
- n = n.next
- if n and n.id == GLYPH then
- textline = true
- break
- end
- until not n or (n.id == GLUE and n.subtype == RGTSKIP)
+ pageline = pageline + 1
+ line = pageline
end
% \end{macrocode}
-% Is this line overfull or underfull?
+% Is it overfull or underfull?
+% \changes{v0.50}{2021/05/13}{Detection of overfull boxes fixed: the
+% former code didn’t work for typewriter fonts.}
% \begin{macrocode}
- if head.glue_set == 1 and head.glue_sign == 2 and
- head.glue_order == 0 and OverfullLines then
-%<msg> texio.write_nl('***OVERFULL line on page ' .. pageno)
-%<msg> texio.write_nl(' ')
+ local first = head.head
+ local hmax = head.width + tex.hfuzz
+ local w,h,d = dimensions(1,2,0, first)
+ if w > hmax and OverfullLines then
pageflag = true
+ local wpt = string.format("%.2fpt", (w-head.width)/65536)
+ local msg = "OVERFULL line " .. wpt
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[7]
color_line (head, COLOR)
- elseif head.glue_set >= Stretch and head.glue_sign == 1 and
- head.glue_order == 0 and UnderfullLines then
-%<msg> texio.write_nl('***UNDERFULL line on page ' ..
-%<msg> tex.getcount("c@page"))
-%<msg> texio.write_nl(' ')
- local COLOR = luatypo.colortbl[8]
+ elseif head.glue_set > Stretch and head.glue_sign == 1 and
+ head.glue_order == 0 and UnderfullLines then
pageflag = true
+ local s = string.format("%.0f%s", 100*head.glue_set, "%")
+ local msg = "UNDERFULL line stretch=" .. s
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[8]
color_line (head, COLOR)
end
% \end{macrocode}
+% Let’s update |vpos| and check if the current line is the last one
+% of the page body; this requires to look ahead \emph{now} for the
+% next nodes in the `vlist’ as this information is needed to decide
+% about orphans, last page’s word hyphenated, etc.
+% \begin{macrocode}
+ vpos = vpos + head.height + head.depth
+ local n = head.next
+ while n and
+ (n.id == GLUE or n.id == PENALTY or n.id == WHATSIT) do
+ n = n.next
+ end
+% \end{macrocode}
+% Is this line the last one on the current page? …
+% \begin{macrocode}
+ if not n then
+ if footnote then
+ page_bottom = true
+ else
+ page_bottom = true
+ body_bottom = true
+ end
+% \end{macrocode}
+% or the last one before |\footnoterule|?
+% \begin{macrocode}
+ elseif footnoterule_ahead(n, false) then
+ body_bottom = true
+ end
+% \end{macrocode}
+% Set flag |ftnsplit| to |true| on every page’s last line.
+% This flag will be reset to false if the current line ends a
+% paragraph.
+% \begin{macrocode}
+ if footnote and page_bottom then
+ ftnsplit = true
+ end
+% \end{macrocode}
+% The current node is a line, |first| is the line’s first node.
+% Skip margin kern and/or leftskip if any.
+%
+% \changes{v0.40}{2021/04/18}{Both MKERN and LFTSKIP may occur on
+% the same line.}
+%
+% \changes{v0.40}{2021/04/18}{All hlists of subtype LINE now
+% count as a pageline.}
+%
+% \begin{macrocode}
+ while first.id == MKERN or
+ (first.id == GLUE and first.subtype == LFTSKIP) do
+ first = first.next
+ end
+ local ListItem = false
+% \end{macrocode}
% Now let’s analyse the beginning of the current line.
% \begin{macrocode}
if first.id == LPAR then
% \end{macrocode}
-% It starts a paragraph…
+% It starts a paragraph… Reset |parline| except in footnotes
+% (|parline| and |pageline| counts are for ``body'' \emph{only},
+% they are frozen in footnotes).
% \begin{macrocode}
hyphcount = 0
- parlines = 1
- if not nextnode then
+ if not footnote then
+ parline = 1
+ end
+ if body_bottom then
% \end{macrocode}
-% No more nodes: we are at the page bottom, this ligne is
-% an orphan (unless it is the unique line of the paragraph)… see below.
+% We are at the page bottom (footnotes excluded), this ligne is
+% an orphan (unless it is the unique line of the paragraph, this
+% will be checked later when scanning the end of line).
% \begin{macrocode}
orphanflag = true
end
% \end{macrocode}
-% List items begin with LPAR followed by an hbox.
+% List items begin with |LPAR| followed by an hbox.
% \begin{macrocode}
local nn = first.next
- if nn and nn.id == HLIST and nn.subtype == BOX then
- ListItem = true
+ if nn and nn.id == HLIST and nn.subtype == BOX then
+ ListItem = true
end
- else
- parlines = parlines + 1
+ elseif not footnote then
+ parline = parline + 1
end
% \end{macrocode}
% Let’s track lines beginning with the same word (except lists).
% \begin{macrocode}
if FirstWordMatch then
local flag = not ListItem
- local COLOR = luatypo.colortbl[10]
- firstwd, match1 = check_first_word(firstwd, first, COLOR, flag)
- if match1 then
- pageflag = true
- end
+ firstwd = check_first_word(firstwd, first, line, flag)
end
% \end{macrocode}
% Let’s check the end of line: |ln| (usually a rightskip) and |pn|
@@ -1605,15 +1956,17 @@ luatypo.check_page = function (head)
local pn = ln.prev
if pn and pn.id == GLUE and pn.subtype == PARFILL then
% \end{macrocode}
-% The paragraph ends with this line, it is not an orphan then…
+% CASE 1: this line ends the paragraph, reset |ftnsplit| and |orphan|
+% flags to false…
% \begin{macrocode}
hyphcount = 0
+ ftnsplit = false
orphanflag = false
% \end{macrocode}
% but it is a widow if it is the page’s first line and it does’nt
% start a new paragraph.\\ Orphans and widows will be colored later.
% \begin{macrocode}
- if pagelines == 1 and parlines > 1 then
+ if pageline == 1 and parline > 1 then
widowflag = true
end
% \end{macrocode}
@@ -1622,18 +1975,19 @@ luatypo.check_page = function (head)
local PFskip = effective_glue(pn,head)
if ShortLines then
local llwd = tex.hsize - PFskip
-%<dbg> local PFskip_pt = PFskip/65536
-%<dbg> local llwd_pt = llwd/65536
-%<dbg> texio.write_nl('PFskip= ' .. PFskip_pt .. ' pt')
-%<dbg> texio.write_nl('llwd= ' .. llwd_pt .. ' pt')
+%<dbg> local PFskip_pt = string.format("%.1fpt", PFskip/65536)
+%<dbg> local llwd_pt = string.format("%.1fpt", llwd/65536)
+%<dbg> texio.write_nl('PFskip= ' .. PFskip_pt)
+%<dbg> texio.write(' llwd= ' .. llwd_pt)
% \end{macrocode}
% |llwd| is the line’s length. Is it too short?
% \begin{macrocode}
if llwd < LLminWD then
-%<msg> texio.write_nl('***Last line too short, page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
- local COLOR = luatypo.colortbl[6]
+ local msg = "SHORT LINE: " ..
+ string.format("%.0fpt", llwd/65536)
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[6]
local attr = oberdiek.luacolor.getattribute()
% \end{macrocode}
% let’s colour the whole line.
@@ -1645,10 +1999,11 @@ luatypo.check_page = function (head)
% Is this line nearly full? (ending too close to the right margin)
% \begin{macrocode}
if BackParindent and PFskip < BackPI and PFskip > BackFuzz then
-%<msg> texio.write_nl('***Last line nearly full, page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
- local COLOR = luatypo.colortbl[12]
+ local msg = "LINE NEARLY FULL: missing " ..
+ string.format("%.1fpt", PFskip/65536)
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[12]
local attr = oberdiek.luacolor.getattribute()
color_line (head, COLOR)
end
@@ -1656,38 +2011,30 @@ luatypo.check_page = function (head)
% Does the last word and the one on the previous line match?
% \begin{macrocode}
if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
local flag = textline
if PFskip > BackPI then
flag = false
end
- lastwd, match1 = check_last_word(lastwd, pn, COLOR, flag)
- if match1 then
- pageflag = true
- end
+ lastwd = check_last_word(lastwd, pn, line, flag)
end
elseif pn and pn.id == DISC then
% \end{macrocode}
-% The current line ends with an hyphen.
+% CASE 2: the current line ends with an hyphen.
% \begin{macrocode}
hyphcount = hyphcount + 1
if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
- lastwd, match1 = check_last_word(lastwd, ln, COLOR, true)
- if match1 then
- pageflag = true
- end
+ lastwd = check_last_word(lastwd, ln, line, true)
end
if hyphcount > HYPHmax and RepeatedHyphens then
local COLOR = luatypo.colortbl[2]
local pg = show_pre_disc (pn,COLOR)
pageflag = true
-%<msg> texio.write_nl('***HYPH issue page ' .. pageno)
-%<msg> texio.write_nl(' ')
+ local msg = "REPEATED HYPHENS: more than " .. HYPHmax
+ log_flaw(msg, line, colno, footnote)
end
- if not nextnode and EOPHyphens then
+ if (page_bottom or body_bottom) and EOPHyphens then
% \end{macrocode}
-% No more nodes: this hyphen occurs on the page last line.
+% This hyphen occurs on the page’s last line (body or footnote).
% \begin{macrocode}
lwhyphflag = true
end
@@ -1697,64 +2044,38 @@ luatypo.check_page = function (head)
% a `linebreak penalty’, the next one is a `baseline skip’ and the
% node after a `hlist of subtype line’ with |glue_order=2|.
% \begin{macrocode}
- local nnnode = nextnode.next
- local nnnnode = nil
- if nnnode and nnnode.next then
- nnnnode = nnnode.next
- if nnnnode and nnnnode.id == HLIST and
- nnnnode.subtype == 1 and nnnnode.glue_order == 2 then
+ local nn = nextnode.next
+ local nnn = nil
+ if nn and nn.next then
+ nnn = nn.next
+ if nnn.id == HLIST and nnn.subtype == LINE and
+ nnn.glue_order == 2 then
+ pageflag = true
+ local msg = "HYPHEN on next to last line"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[0]
local pg = show_pre_disc (pn,COLOR)
- pageflag = true
end
end
end
- elseif pn and pn.id == GLYPH then
% \end{macrocode}
-% \enlargethispage*{\baselineskip}
-% The current line ends with a character, reset |hyphcount| and
-% compare the end of line’s word with the one on previous line.
+% CASE 3: the current line ends with anything else (|MKERN|,
+% |GLYPH|, |HLIST|, etc.), reset |hyphcount|, perform checks
+% for `LastWordMatch’ and for `EOLShortWords’.
% \begin{macrocode}
+ else
hyphcount = 0
- if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
- lastwd, match1 = check_last_word(lastwd, pn, COLOR, true)
+ if LastWordMatch and pn then
+ lastwd = check_last_word(lastwd, pn, line, true)
end
-% \end{macrocode}
-% Look for a short unwanted short word at the end of the current
-% line.
-% \begin{macrocode}
if EOLShortWords then
- match2 = check_regexpr(pn)
- end
- if match1 or match2 then
- pageflag = true
- end
-% \end{macrocode}
-% Microtype sometimes adds a margin kern at the right margin,
-% |check_regexpr| has to operate on the previous node then.
-% \begin{macrocode}
- elseif pn and pn.id == MKERN then
- hyphcount = 0
- local ppn = pn.prev
- if ppn and ppn.id == GLYPH then
- if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
- lastwd, match1 = check_last_word(lastwd, pn, COLOR, true)
+ while pn and pn.id ~= GLYPH and pn.id ~= HLIST do
+ pn = pn.prev
end
- if EOLShortWords then
- match2 = check_regexpr(ppn)
- end
- if match1 or match2 then
- pageflag = true
+ if pn and pn.id == GLYPH then
+ check_regexpr(pn,line)
end
end
-% \end{macrocode}
-% If the current line ends with anything else (no hyphen),
-% reset |hyphcount|.
-% \begin{macrocode}
- else
- hyphcount = 0
end
% \end{macrocode}
% Colour the whole line if is is a widow.
@@ -1762,59 +2083,221 @@ luatypo.check_page = function (head)
if widowflag and Widows then
pageflag = true
widowflag = false
-%<msg> texio.write_nl('***WIDOW on top of page ' .. pageno)
-%<msg> texio.write_nl(' ')
+ local msg = "WIDOW"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[4]
color_line (head, COLOR)
end
% \end{macrocode}
-% Colour the whole line if is is a orphan.
+% Colour the whole line if is is a orphan or footenote continuing
+% on the next page.
% \begin{macrocode}
if orphanflag and Orphans then
-%<msg> texio.write_nl('***ORPHAN at bottom of page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
+ local msg = "ORPHAN"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[5]
color_line (head, COLOR)
end
+ if ftnsplit and FootnoteSplit then
+ pageflag = true
+ local msg = "FOOTNOTE SPLIT"
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[13]
+ color_line (head, COLOR)
+ end
% \end{macrocode}
% Colour (differently) the last word if hyphenated.
% \begin{macrocode}
if lwhyphflag and EOPHyphens then
-%<msg> texio.write_nl('***LAST WORD SPLIT page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
+ local msg = "LAST WORD SPLIT"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[1]
local pg = show_pre_disc (pn,COLOR)
end
+ elseif head.id == HLIST and
+ (head.subtype == EQN or head.subtype == ALIGN) and
+ (head.height > 0 or head.depth > 0) then
% \end{macrocode}
-% Track empty pages: check the number of lines at end of page.
-% Widows are already detected; get the last line and colour it.
-% NOTE: there are usually two consecutive nodes of type 12-0 at
-% end of pages…
+%
+% \changes{v0.50}{2021/05/05}{Consider displayed and aligned equations
+% too for overfull boxes.}
+%
+% This line is a displayed or aligned equation.
+% Let’s update |vpos| and the line number.
% \begin{macrocode}
- elseif not nextnode and head.id == GLUE and
- head.subtype == USRSKIP then
- if pagelines > 1 and pagelines < PAGEmin and ShortPages then
- pageflag = true
- local COLOR = luatypo.colortbl[9]
-%<msg> texio.write_nl('***Only ' .. pagelines ..
-%<msg> ' lines on page ' .. pageno)
-%<msg> texio.write_nl(' ')
- local n = head
- while n and (n.id ~= HLIST or n.subtype ~= LINE) do
- n = n.prev
+ vpos = vpos + head.height + head.depth
+ if footnote then
+ ftnline = ftnline + 1
+ line = ftnline
+ else
+ pageline = pageline + 1
+ line = pageline
+ end
+% \end{macrocode}
+% Let’s check for an ``Overfull box''. For a displayed equation
+% it is straightforward. A set of aligned equations all have the
+% same (maximal) width; in order to avoid highlighting the whole
+% set, we have to look for glues at the end of embedded `hlists’.
+% \begin{macrocode}
+ local fl = true
+ local wd = 0
+ local hmax = 0
+ if head.subtype == EQN then
+ local f = head.list
+ wd = rangedimensions(head,f)
+ hmax = head.width + tex.hfuzz
+ else
+ wd = head.width
+ hmax = tex.getdimen("linewidth") + tex.hfuzz
+ end
+ if wd > hmax and OverfullLines then
+ if head.subtype == ALIGN then
+ local first = head.list
+ for n in traverse_id(HLIST, first) do
+ local last = slide(n.list)
+ if last.id == GLUE and last.subtype == USER then
+ wd = wd - effective_glue(last,n)
+ if wd <= hmax then fl = false end
+ end
+ end
end
- if n then
- color_line(n, COLOR)
+ if fl then
+ pageflag = true
+ local w = wd - hmax + tex.hfuzz
+ local wpt = string.format("%.2fpt", w/65536)
+ local msg = "OVERFULL equation " .. wpt
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[7]
+ color_line (head, COLOR)
end
end
+% \end{macrocode}
+% We also need to set flag |body_bottom| and to increment the
+% |pageline| counter to track empty pages.
+% \begin{macrocode}
+ local n = head.next
+ while n and (n.id == GLUE or n.id == PENALTY or
+ n.id == WHATSIT or n.id == VLIST) do
+ n = n.next
+ end
+ if not n then
+ page_bottom = true
+ body_bottom = true
+ elseif footnoterule_ahead(n, false) then
+ body_bottom = true
+ end
+% \end{macrocode}
+% This is a |\vbox|, let’s update |vpos|.
+% \begin{macrocode}
+ elseif head.id == VLIST then
+ vpos = vpos + head.height + head.depth
+% \end{macrocode}
+% Track empty pages: check the number of lines at end of page,
+% in case this number is low, \emph{and} |vpos| is less than
+% |vpos_min|, fetch the last line and colour it.\\
+% NOTE1: |effective_glue| requires a `parent’ node, as pointed out by
+% Marcel Krüger on S.E., this implies using |pre_shipout_filter|
+% instead of |pre_output_filter|.\\
+% NOTE2: Widows are already detected, skip them here; there are
+% usually two consecutive nodes of type 12-0 at end of pages…
+% \begin{macrocode}
+ elseif body_bottom and head.id == GLUE and head.subtype == 0 then
+ if first_bot then
+%<dbg> local vpos_pt = string.format("%.1fpt", vpos/65536)
+%<dbg> local vmin_pt = string.format("%.1fpt", vpos_min/65536)
+%<dbg> texio.write_nl('pageline=' .. pageline)
+%<dbg> texio.write_nl('vpos=' .. vpos_pt)
+%<dbg> texio.write(' vpos_min=' .. vmin_pt)
+%<dbg> if page_bottom then
+%<dbg> local tht = tex.getdimen("textheight")
+%<dbg> local tht_pt = string.format("%.1fpt", tht/65536)
+%<dbg> texio.write(' textheight=' .. tht_pt)
+%<dbg> end
+%<dbg> texio.write_nl(' ')
+ if pageline > 1 and pageline < PAGEmin and ShortPages then
+ pageshort = true
+ end
+ if pageshort and vpos < vpos_min then
+ pageflag = true
+ local msg = "SHORT PAGE: only " .. pageline .. " lines"
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[9]
+ local n = head
+ repeat
+ n = n.prev
+ until n.id == HLIST
+ color_line (n, COLOR)
+ end
+ first_bot = false
+ end
+% \end{macrocode}
+% Increment |vpos| on other vertical glues.
+% \begin{macrocode}
+ elseif head.id == GLUE then
+ vpos = vpos + effective_glue(head,body)
end
head = nextnode
end
+ return pageflag
+end
+% \end{macrocode}
+%
+% This is the main function which will be added to the
+% |pre_shipout_filter| callback unless option |None| is selected.
+% It executes |get_pagebody|, then scans the page body for possible
+% columns (multi column page).
+%
+% \begin{macrocode}
+luatypo.check_page = function (head)
+ local pageno = tex.getcount("c@page")
+ local pageflag = false
+ local n2, n3, col, colno
+ local body = get_pagebody(head)
+ local first = body.list
+%<dbg> texio.write_nl('body.id=' .. tostring(node.type(body.id)))
+%<dbg> texio.write('-' .. body.subtype)
+%<dbg> texio.write_nl(' ')
+%<dbg> texio.write_nl('first.id=' .. tostring(node.type(first.id)))
+%<dbg> texio.write('-' .. first.subtype)
+%<dbg> texio.write_nl(' ')
+ if first.id == HLIST and first.subtype == 2 then
+% \end{macrocode}
+% Two or more columns, each one is boxed in an hlist.
+% Run |check_vtop| on every column.
+% \begin{macrocode}
+ n2 = first.list
+ colno = 0
+ for n in traverse_id(HLIST, n2) do
+%<dbg> texio.write_nl('n.id=' .. tostring(node.type(n.id)))
+%<dbg> texio.write('-' .. n.subtype)
+%<dbg> texio.write(' ht=' .. n.height)
+%<dbg> texio.write_nl(' ')
+ if n.id == HLIST and n.subtype == 2 then
+ n3 = n.list
+%<dbg> texio.write_nl('n3.id=' .. tostring(node.type(n3.id)))
+%<dbg> texio.write('-' .. n3.subtype)
+%<dbg> texio.write(' ht=' .. n3.height)
+%<dbg> texio.write_nl(' ')
+ col = n3.list
+ colno = colno + 1
+ pageflag = check_vtop(col,colno)
+ end
+ end
+ elseif body.id == VLIST and body.subtype == 0 then
+% \end{macrocode}
+% Single column, run |check_vtop| on the top vlist.
+% \begin{macrocode}
+ col = body.list
+ pageflag = check_vtop(col,colno)
+ end
% \end{macrocode}
% Add this page number to the summary if any flaw has been found on it.
% Skip duplicates.
+%
+% \changes{v0.32}{2021/03/14}{Remove duplicates in the summary of pages.}
+%
% \begin{macrocode}
if pageflag then
local pl = luatypo.pagelist
@@ -1829,22 +2312,29 @@ return luatypo.check_page
\end{luacode}
% \end{macrocode}
%
-% Add the |luatypo.check_page| function to the |pre_output_filter|
-% callback, unless option |None| is selected ; remember that the
-% |None| boolean’s value is forwarded to Lua `AtEndOfPackage’…
+% Add the |luatypo.check_page| function to the |pre_shipout_filter|
+% callback (with priority 1 for color attributes to be effective),
+% unless option |None| is selected ; remember that the |None|
+% boolean’s value is forwarded to Lua `AtEndOfPackage’…
+%
+% \changes{v0.50}{2021/05/02}{Callback `pre\_output\_filter’ replaced
+% by `pre\_shipout\_filter’, in the later the material is not boxed
+% yet and footnotes are not visible.}
+%
% \begin{macrocode}
\AtEndOfPackage{%
\directlua{
if not luatypo.None then
luatexbase.add_to_callback
- ("pre_output_filter",luatypo.check_page,"check_page")
+ ("pre_shipout_filter",luatypo.check_page,"check_page",1)
end
}
}
% \end{macrocode}
%
-% Load a local config file if present in LaTeX’s search path.
+% Load a local config file if present in LaTeX’s search path.\\
% Otherwise, set reasonnable defaults.
+% \enlargethispage*{\baselineskip}
% \begin{macrocode}
\InputIfFileExists{lua-typo.cfg}%
@@ -1855,7 +2345,7 @@ return luatypo.check_page
\definecolor{myred}{rgb}{1,0.55,0}
\luatypoSetColor0{red}% Paragraph last full line hyphenated
\luatypoSetColor1{red}% Page last word hyphenated
- \luatypoSetColor2{red}% Hyphens ending two many consecutive lines
+ \luatypoSetColor2{red}% Hyphens on to many consecutive lines
\luatypoSetColor3{red}% Short word at end of line
\luatypoSetColor4{cyan}% Widow
\luatypoSetColor5{cyan}% Orphan
@@ -1866,9 +2356,11 @@ return luatypo.check_page
\luatypoSetColor{10}{myred}% First word matches
\luatypoSetColor{11}{myred}% Last word matches
\luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+ \luatypoSetColor{13}{cyan}% Footnote split
\luatypoBackPI=1em\relax
\luatypoBackFuzz=2pt\relax
- \luatypoLLminWD=2\parindent\relax
+ \ifdim\parindent=0pt \luatypoLLminWD=20pt\relax
+ \else\luatypoLLminWD=2\parindent\relax\fi
\luatypoStretchMax=200\relax
\luatypoHyphMax=2\relax
\luatypoPageMin=5\relax
@@ -1879,8 +2371,7 @@ return luatypo.check_page
% \iffalse
%</sty>
% \fi
-%
-% \clearpage
+% \clearpage
% \section{Configuration file}
%
% \iffalse
@@ -1897,7 +2388,9 @@ return luatypo.check_page
\luatypoBackFuzz=2pt\relax
%% Minimum length of paragraphs’ last lines
-\luatypoLLminWD=2\parindent\relax
+\ifdim\parindent=0pt \luatypoLLminWD=20pt\relax
+\else \luatypoLLminWD=2\parindent\relax
+\fi
%% Maximum number of consecutive hyphenated lines
\luatypoHyphMax=2\relax
@@ -1917,7 +2410,7 @@ return luatypo.check_page
\definecolor{myred}{rgb}{1,0.55,0}
\luatypoSetColor0{red} % Paragraph last full line hyphenated
\luatypoSetColor1{red} % Page last word hyphenated
-\luatypoSetColor2{red} % Hyphens ending two many consecutive lines
+\luatypoSetColor2{red} % Hyphens on to many consecutive lines
\luatypoSetColor3{red} % Short word at end of line
\luatypoSetColor4{cyan} % Widow
\luatypoSetColor5{cyan} % Orphan
@@ -1928,10 +2421,10 @@ return luatypo.check_page
\luatypoSetColor{10}{myred} % First word matches
\luatypoSetColor{11}{myred} % Last word matches
\luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+\luatypoSetColor{13}{cyan} % Footnote split
-%% Language specific settings (example for French only currently):
+%% Language specific settings (example for French):
%% short words (two letters max) to be avoided at end of lines.
-%% French:
%%\luatypoOneChar{french}{'À à Ô'}
%%\luatypoTwoChars{french}{'Je Tu Il On'}
% \end{macrocode}