texlive[66848] Master/texmf-dist: lua-typo (14apr23)

commits+karl at tug.org commits+karl at tug.org
Fri Apr 14 22:30:42 CEST 2023


Revision: 66848
          http://tug.org/svn/texlive?view=revision&revision=66848
Author:   karl
Date:     2023-04-14 22:30:42 +0200 (Fri, 14 Apr 2023)
Log Message:
-----------
lua-typo (14apr23)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/lualatex/lua-typo/README.md
    trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf
    trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx
    trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf
    trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx
    trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf
    trunk/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx
    trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty

Added Paths:
-----------
    trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty

Modified: trunk/Master/texmf-dist/doc/lualatex/lua-typo/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/lua-typo/README.md	2023-04-14 20:30:11 UTC (rev 66847)
+++ trunk/Master/texmf-dist/doc/lualatex/lua-typo/README.md	2023-04-14 20:30:42 UTC (rev 66848)
@@ -88,6 +88,10 @@
     on top of next page.
   - code cleaning.
 
+* v.0.70: 
+  - Options handled via `ltkeys` instead of `kvoptions`.
+  - code cleaning, bug fixes.
+  
 --
 Copyright 2020--2023 Daniel Flipo
 E-mail: daniel (dot) flipo (at) free (dot) fr

Modified: trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx	2023-04-14 20:30:11 UTC (rev 66847)
+++ trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx	2023-04-14 20:30:42 UTC (rev 66848)
@@ -16,7 +16,7 @@
 \frenchsetup{og=«, fg=»}
 
 \usepackage[ShortPages, OverfullLines, UnderfullLines,
-            Widows, Orphans, EOPHyphens, RepeatedHyphens
+            EOPHyphens, RepeatedHyphens, % Widows, Orphans,
            ]{lua-typo}
 \renewcommand*\descriptionlabel[1]{%
    \hspace{\labelsep}\texttt{#1}}

Modified: trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx	2023-04-14 20:30:11 UTC (rev 66847)
+++ trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx	2023-04-14 20:30:42 UTC (rev 66848)
@@ -15,7 +15,7 @@
 \usepackage{babel,varioref}
 
 \usepackage[ShortPages, OverfullLines, UnderfullLines,
-            Widows, Orphans, EOPHyphens, RepeatedHyphens
+            EOPHyphens, RepeatedHyphens, % Widows, Orphans,
            ]{lua-typo}
 \renewcommand*\descriptionlabel[1]{%
    \hspace{\labelsep}\texttt{#1}}

Modified: trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx
===================================================================
--- trunk/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx	2023-04-14 20:30:11 UTC (rev 66847)
+++ trunk/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx	2023-04-14 20:30:42 UTC (rev 66848)
@@ -72,7 +72,7 @@
 %<+docfr>\frenchsetup{og=«, fg=»}
 
 \usepackage[ShortPages, OverfullLines, UnderfullLines,
-            Widows, Orphans, EOPHyphens, RepeatedHyphens
+            EOPHyphens, RepeatedHyphens, % Widows, Orphans,
            ]{lua-typo}
 \renewcommand*\descriptionlabel[1]{%
    \hspace{\labelsep}\texttt{#1}}
@@ -325,9 +325,8 @@
 %    exactement les réglages internes, il se trouve normalement dans le
 %    répertoire \texdir{texmfdist} des distributions TeXLive, MikTeX, etc.
 %    L’utilisateur a la possibilité de recopier ce fichier soit dans son
-%    répertoire de travail pour un document particulier, soit dans son
-%    répertoire \texdir{texmfhome} ou \texdir{texmflocal} et de le
-%    personnaliser comme il l’entend.
+%    répertoire de travail, soit dans son répertoire \texdir{texmfhome}
+%    ou \texdir{texmflocal} et de le personnaliser comme il l’entend.
 %
 %    Voici la liste complète des paramètres personnalisables avec leur
 %    valeur par défaut, leurs noms sont systématiquement préfixés par
@@ -374,9 +373,11 @@
 %        mot) au début ou à la fin de deux lignes consécutives
 %        déclenchant l’avertissement.
 %        Avec ce réglage (3 et 4), seront détectées deux lignes se
-%        terminant par « cible» et « invincible » (quatre lettres en
-%        commun), ainsi que la présence de « mon » en début ou fin de
-%        deux lignes consécutives (trois lettres en commun).
+%        terminant par « cible » et « irrésistible » ou
+%        « irrésistible-(ment) » (quatre lettres en commun), ainsi que la
+%        présence de « mon » en début ou fin de deux lignes consécutives
+%        (trois lettres en commun), mais « mon » et « mont » en début de
+%        ligne échappent à la détection.
 %
 %    \item[EOLShortWords:] cette option signale la présence en fin de
 %        ligne de mots très courts (une ou deux lettres)
@@ -425,7 +426,7 @@
 % \luatypoSetColor9{red}      % Page presque vide (qq. lignes)
 % \luatypoSetColor{10}{LTred} % Répétitions en début de ligne
 % \luatypoSetColor{11}{LTred} % Répétitions en fin de ligne
-% \luatypoSetColor{12}{LTgrey}% Dernière ligne d’alinéa presque pleine
+% \luatypoSetColor{12}{LTgrey}% Dernière ligne alinéa presque pleine
 % \luatypoSetColor{13}{cyan}  % Note de bas de page éclatée
 % \luatypoSetColor{14}{red}   % Mot de fin de phrase en haut de page
 %    \end{verbatim}
@@ -719,7 +720,7 @@
 %<+scan>\ProvidesPackage{scan-page}
 %<+dtx>\ProvidesFile{lua-typo.dtx}
 %<*dtx|sty|scan>
-                [2023-03-08 v.0.65 Daniel Flipo]
+                [2023-04-12 v.0.70 Daniel Flipo]
 %</dtx|sty|scan>
 %<*sty>
 % \fi
@@ -731,18 +732,13 @@
 %    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.
+%    Rollback version~0.65 is provided for users who would have
+%    a LaTeX kernel older than 2022/06/01.
 %
 %    \begin{macrocode}
-\ifdefined\DeclareRelease
-  \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
-  \DeclareCurrentRelease{}{2023-03-08}
-\else
-  \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide
-    access\MessageBreak to former versions of the lua-typo package.%
-    \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated%
-    \MessageBreak 2020-01-01 or newer; reported}
-\fi
-\NeedsTeXFormat{LaTeX2e}[2021/06/01]
+\DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
+\DeclareRelease{v0.65}{2023-03-08}{lua-typo-2023-03-08.sty}
+\DeclareCurrentRelease{}{2023-04-12}
 %    \end{macrocode}
 %
 %    This package only runs with LuaLaTeX and requires packages
@@ -751,8 +747,7 @@
 %
 %    \begin{macrocode}
 \ifdefined\directlua
-  \RequirePackage{luatexbase,luacode,luacolor}
-  \RequirePackage{kvoptions,atveryend}
+  \RequirePackage{luatexbase,luacode,luacolor,atveryend}
 \else
   \PackageError{This package is meant for LuaTeX only! Aborting}
                {No more information available, sorry!}
@@ -784,46 +779,44 @@
 \end{luacode}
 %    \end{macrocode}
 %
-%    Set up \pkg{kvoptions} initializations.
-%
-%    \begin{macrocode}
-\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}
-\DeclareBoolOption[false]{FootnoteSplit}
-\DeclareBoolOption[false]{ShortFinalWord}
-%    \end{macrocode}
+%    Set up \pkg{ltkeys} initializations.
 %    Option \opt{All} resets all booleans relative to specific
 %    typographic checks to \opt{true}.
+%
+% \changes{v0.70}{2023/04/08}{Package options no longer require
+%    `kvoptions’, they rely on LaTeX `ltkeys’ package.}
+%
 %    \begin{macrocode}
-\AddToKeyvalOption{luatypo}{All}{%
-  \LT at ShortLinestrue     \LT at ShortPagestrue
-  \LT at OverfullLinestrue  \LT at UnderfullLinestrue
-  \LT at Widowstrue         \LT at Orphanstrue
-  \LT at EOPHyphenstrue     \LT at RepeatedHyphenstrue
-  \LT at ParLastHyphentrue  \LT at EOLShortWordstrue
-  \LT at FirstWordMatchtrue \LT at LastWordMatchtrue
-  \LT at BackParindenttrue  \LT at FootnoteSplittrue
-  \LT at ShortFinalWordtrue
-}
-\ProcessKeyvalOptions{luatypo}
+\DeclareKeys[luatypo]
+  {
+   ShowOptions.if     = LT at ShowOptions     ,
+   None.if            = LT at None            ,
+   BackParindent.if   = LT at BackParindent   ,
+   ShortLines.if      = LT at ShortLines      ,
+   ShortPages.if      = LT at ShortPages      ,
+   OverfullLines.if   = LT at OverfullLines   ,
+   UnderfullLines.if  = LT at UnderfullLines  ,
+   Widows.if          = LT at Widows          ,
+   Orphans.if         = LT at Orphans         ,
+   EOPHyphens.if      = LT at EOPHyphens      ,
+   RepeatedHyphens.if = LT at RepeatedHyphens ,
+   ParLastHyphen.if   = LT at ParLastHyphen   ,
+   EOLShortWords.if   = LT at EOLShortWords   ,
+   FirstWordMatch.if  = LT at FirstWordMatch  ,
+   LastWordMatch.if   = LT at LastWordMatch   ,
+   FootnoteSplit.if   = LT at FootnoteSplit   ,
+   ShortFinalWord.if  = LT at ShortFinalWord  ,
+   All.if             = LT at All             ,
+   All.code           = \LT at ShortLinestrue     \LT at ShortPagestrue
+                        \LT at OverfullLinestrue  \LT at UnderfullLinestrue
+                        \LT at Widowstrue         \LT at Orphanstrue
+                        \LT at EOPHyphenstrue     \LT at RepeatedHyphenstrue
+                        \LT at ParLastHyphentrue  \LT at EOLShortWordstrue
+                        \LT at FirstWordMatchtrue \LT at LastWordMatchtrue
+                        \LT at BackParindenttrue  \LT at FootnoteSplittrue
+                        \LT at ShortFinalWordtrue
+  }
+\ProcessKeyOptions[luatypo]
 %    \end{macrocode}
 %
 %     Forward these options to the |luatypo| global table.
@@ -941,6 +934,7 @@
      \MessageBreak
      ShowOptions     [false]\MessageBreak
      None            [false]\MessageBreak
+     All             [false]\MessageBreak
      BackParindent   [false]\MessageBreak
      ShortLines      [false]\MessageBreak
      ShortPages      [false]\MessageBreak
@@ -974,6 +968,10 @@
     luatypo.Stretch = tex.count.luatypoStretchMax
     luatypo.MinFull = tex.count.luatypoMinFull
     luatypo.MinPart = tex.count.luatypoMinPart
+%    \end{macrocode}
+%    Ensure |MinFull|$\leq$|MinPart|.
+%    \begin{macrocode}
+    luatypo.MinFull = math.min(luatypo.MinPart,luatypo.MinFull)
     luatypo.MinLen  = tex.count.luatypoMinLen
     luatypo.LLminWD = tex.dimen.luatypoLLminWD
     luatypo.BackPI  = tex.dimen.luatypoBackPI
@@ -1169,10 +1167,11 @@
 local has_field = node.has_field
 local uses_font = node.uses_font
 local is_glyph  = node.is_glyph
+local utf8_len  = utf8.len
 %    \end{macrocode}
 %    Local definitions from the `unicode.utf8’ library: replacements are
-%    needed for functions |string.gsub()|, |string.find()| and
-%    |string.reverse()| which are meant for one-byte characters only.
+%    needed for functions |string.gsub()|, |string.sub()|, |string.find()|
+%    and |string.reverse()| which are meant for one-byte characters only.
 %
 % \changes{v0.65}{2023/03/02}{Three new functions for utf-8 strings’
 %    manipulations.}
@@ -1192,7 +1191,7 @@
 %    end to beginning) [same as |string.reverse| but for utf-8 strings].
 %    \begin{macrocode}
 local utf8_reverse = function (s)
-  if utf8.len(s) > 1 then
+  if utf8_len(s) > 1 then
      local so = ""
      for p, c in utf8.codes(s) do
          so = utf8.char(c) .. so
@@ -1202,6 +1201,16 @@
   return s
 end
 %    \end{macrocode}
+%    |utf8_sub| returns the substring of s that starts at i and
+%    continues until j (j-i-1 utf8 chars.). \emph{Warning: it requires
+%    $i\ge1$ and $j\ge i$}.
+%    \begin{macrocode}
+local utf8_sub = function (s,i,j)
+    i=utf8.offset(s,i)
+    j=utf8.offset(s,j+1)-1
+    return string.sub(s,i,j)
+end
+%    \end{macrocode}
 %
 %\changes{v0.32}{2021/03/14}{Better protection against unexpected
 %    nil nodes.}
@@ -1316,13 +1325,15 @@
 %    and ligatures, dicretionnaries other than ligatures, kerns
 %    (letterspacing) should be discarded.
 %    For each word to be compared we build a ``signature’’ made of
-%    glyphs and split ligatures.
+%    glyphs, split ligatures and underscores (representing glues).
 %
 % \changes{v0.65}{2023/03/02}{All ligatures are now split using the
 %    node’s `components’ field rather than a table.}
 %
-%    The first function adds a node to a signature of type string.
-%    It returns the augmented string and its length.
+%    The first function adds a (non-nil) node to a signature of type
+%    string, nil nodes are ignored.
+%    It returns the augmented string and its length
+%    (underscores are omitted in the length computation).
 %    The last argument is a boolean needed when building a signature
 %    backwards (see |check_line_last_word|).
 %    \begin{macrocode}
@@ -1331,12 +1342,12 @@
   local str = string
   if n and n.id == GLYPH then
      local b = n.char
-     if b and not char_to_discard[b] then
 %    \end{macrocode}
 %    Punctuation has to be discarded; other glyphs may be ligatures,
 %    then they have a |components| field which holds the list of glyphs
 %    which compose the ligature.
 %    \begin{macrocode}
+     if b and not char_to_discard[b] then
         if n.components then
            local c = ""
            for nn in traverse_id(GLYPH, n.components) do
@@ -1384,14 +1395,14 @@
     else
        str = str .. c1 .. c2
     end
+  elseif n and n.id == GLUE then
+       str = str .. "_"
   end
 %    \end{macrocode}
 %    The returned length is the number of \emph{letters}.
 %    \begin{macrocode}
-  local len = utf8.len(str)
-  if utf8_find(str, "_") then
-     len = len - 1
-  end
+  local s = utf8_gsub(str, "_", "")
+  local len = utf8_len(s)
   return len, str
 end
 %    \end{macrocode}
@@ -1414,6 +1425,9 @@
 % \changes{v0.61}{2023/02/06}{`check\_line\_last\_word’ returns a flag
 %    to set pageflag.}
 %
+% \changes{v0.70}{2023/04/08}{`check\_line\_first\_word’ and
+%    `check\_line\_last\_word’: length of matches corrected.}
+%
 %    \begin{macrocode}
 local check_line_last_word = function (old, node, line, colno, flag)
   local COLOR = luatypo.colortbl[11]
@@ -1420,11 +1434,13 @@
   local match = false
   local new = ""
   local maxlen = 0
+  local MinFull = luatypo.MinFull
+  local MinPart = luatypo.MinPart
   if node then
      local swap = true
      local box, go
 %    \end{macrocode}
-%    Step back to the last glyph or discretionary.
+%    Step back to the last glyph or discretionary or hbox.
 %    \begin{macrocode}
      local lastn = node
      while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC and
@@ -1432,39 +1448,44 @@
        lastn = lastn.prev
      end
 %    \end{macrocode}
-%    A signature is built from the last two words on the current line.
+%    A signature is built from the last two (or more) 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
+     local words = 0
+     while n and (words <= 2 or maxlen < MinPart) do
+%    \end{macrocode}
+%    Go down inside  boxes, read their content from end to beginning,
+%    then step out.
+%    \begin{macrocode}
+       if n and n.id == HLIST then
+          box = n
+          local first = n.head
+          local lastn = slide(first)
+          n = lastn
+          while n do
+            maxlen, new = signature (n, new, swap)
+            n = n.prev
+          end
+          n = box.prev
+          local w = utf8_gsub(new, "_", "")
+          words = words + utf8_len(new) - utf8_len(w) + 1
+       else
+          repeat
+            maxlen, new = signature (n, new, swap)
+            n = n.prev
+          until not n or n.id == GLUE or n.id == HLIST
+          if n and n.id == GLUE then
+             maxlen, new = signature (n, new, swap)
+             words = words + 1
+             n = n.prev
+          end
+       end
      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)
-        until not n or n.id == GLUE
-     end
      new = utf8_reverse(new)
+     new = utf8_gsub(new, "_+$", "")  -- $
+     new = utf8_gsub(new, "^_+", "")
+     maxlen = math.min(utf8_len(old), utf8_len(new))
 %<dbg>     texio.write_nl("EOLsigold=" .. old)
 %<dbg>     texio.write("   EOLsig=" .. new)
 %    \end{macrocode}
@@ -1472,60 +1493,63 @@
 %     the last word’s signature, but doesn’t compare it with the
 %     previous line’s.
 %    \begin{macrocode}
-     if flag then
-        local MinFull = luatypo.MinFull
-        local MinPart = luatypo.MinPart
-        MinFull = math.min(MinPart,MinFull)
-        local k = MinPart
+     if flag and old ~= "" then
+%    \end{macrocode}
+%    |oldlast| and |newlast| hold the last (full) words to be compared
+%    later:
+%    \begin{macrocode}
+        local oldlast = utf8_gsub (old, ".*_", "")
+        local newlast = utf8_gsub (new, ".*_", "")
+%    \end{macrocode}
+%    Let’s look for a partial match: build |oldsub| and |newsub|,
+%    reading (backwards) the last |MinPart| \emph{non-space} characters
+%    of both lines.
+%    \begin{macrocode}
+        local oldsub = ""
+        local newsub = ""
         local dlo = utf8_reverse(old)
         local wen = utf8_reverse(new)
-        local oldlast = utf8_gsub (old, ".*_", "_")
-        local newlast = utf8_gsub (new, ".*_", "_")
-        local i
-        if utf8_find(newlast, "_") then
-           i = utf8.len(newlast)
-        end
-        if i and i > maxlen - MinPart + 1 then
-           k = MinPart + 1
-        end
-        local oldsub = ""
-        local newsub = ""
         for p, c in utf8.codes(dlo) do
-          if utf8.len(oldsub) < k then
+          local s = utf8_gsub(oldsub, "_", "")
+          if utf8_len(s) < MinPart then
              oldsub = utf8.char(c) .. oldsub
           end
         end
         for p, c in utf8.codes(wen) do
-          if utf8.len(newsub) < k then
+          local s = utf8_gsub(newsub, "_", "")
+          if utf8_len(s) < MinPart then
              newsub = utf8.char(c) .. newsub
           end
         end
-        local l = utf8.len(new)
-        if oldsub == newsub and l >= k then
+        if oldsub == newsub then
 %<dbg>           texio.write_nl("EOLnewsub=" .. newsub)
            match = true
-        elseif oldlast == newlast and utf8.len(newlast) > MinFull then
+        end
+        if oldlast == newlast and utf8_len(newlast) >= MinFull then
 %<dbg>           texio.write_nl("EOLnewlast=" .. newlast)
+           if utf8_len(newlast) > MinPart or not match then
+              oldsub = oldlast
+              newsub = newlast
+           end
            match = true
-           oldsub = oldlast
-           newsub = newlast
-           k = utf8.len(newlast)
         end
         if match then
 %    \end{macrocode}
-%    Minimal partial match; any more glyphs matching?
+%    Minimal full or partial match |newsub| of length |k|;
+%    any more glyphs matching?
 %    \begin{macrocode}
-           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)
+           local k = utf8_len(newsub)
+           local osub = utf8_reverse(oldsub)
+           local nsub = utf8_reverse(newsub)
+           while osub == nsub and k < maxlen do
+             k = k + 1
+             osub = utf8_sub(dlo,1,k)
+             nsub = utf8_sub(wen,1,k)
              if osub == nsub then
-                newsub = nsub
+                newsub = utf8_reverse(nsub)
              end
            end
-           newsub = utf8_gsub(newsub, "^_", "")
+           newsub = utf8_gsub(newsub, "^_+", "")
 %<dbg>           texio.write_nl("EOLfullmatch=" .. newsub)
            local msg = "E.O.L. MATCH=" .. newsub
            log_flaw(msg, line, colno, footnote)
@@ -1532,22 +1556,32 @@
 %    \end{macrocode}
 %    Lest’s colour the matching string.
 %    \begin{macrocode}
+           local ns = utf8_gsub(newsub, "_", "")
+           k = utf8_len(ns)
            oldsub = utf8_reverse(newsub)
            local newsub = ""
            local n = lastn
-           repeat
-             if n and n.id ~= GLUE then
+           local l = 0
+           local lo = 0
+           local li = 0
+           while n and newsub ~= oldsub and l < k do
+             if n and n.id == HLIST then
+                local first = n.head
+                for nn in traverse_id(GLYPH, first) do
+                  color_node(nn, COLOR)
+                  local c = nn.char
+                  if not char_to_discard[c] then l = l + 1 end
+                end
+%<dbg>           texio.write_nl("l (box)=" .. l)
+             elseif n then
                 color_node(n, COLOR)
-                l, newsub = signature(n, newsub, swap)
-             elseif n and n.id == GLUE then
-                newsub = newsub .. "_"
-             elseif not n and box then
-                n = box
-             else
-                break
+                li, newsub = signature(n, newsub, swap)
+                l = l + li - lo
+                lo = li
+%<dbg>           texio.write_nl("l=" .. l)
              end
              n = n.prev
-           until newsub == oldsub or l >= k
+           end
         end
      end
   end
@@ -1568,6 +1602,8 @@
   local swap = false
   local new = ""
   local maxlen = 0
+  local MinFull = luatypo.MinFull
+  local MinPart = luatypo.MinPart
   local n = node
   local box, go
   while n and n.id ~= GLYPH and n.id ~= DISC and
@@ -1574,35 +1610,34 @@
         (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
+  start = n
+  local words = 0
+  while n and (words <= 2 or maxlen < MinPart) do
+    if n and n.id == HLIST then
+       box = n
+       n = n.head
+       while n do
+         maxlen, new = signature (n, new, swap)
+         n = n.next
+       end
+       n = box.next
+       local w = utf8_gsub(new, "_", "")
+       words = words + utf8_len(new) - utf8_len(w) + 1
+    else
+       repeat
+         maxlen, new = signature (n, new, swap)
+         n = n.next
+       until not n or n.id == GLUE or n.id == HLIST
+       if n and n.id == GLUE then
+          maxlen, new = signature (n, new, swap)
+          words = words + 1
+          n = n.next
+       end
+    end
   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)
-     until not n or n.id == GLUE
-  end
+  new = utf8_gsub(new, "_+$", "") -- $
+  new = utf8_gsub(new, "^_+", "")
+  maxlen = math.min(utf8_len(old), utf8_len(new))
 %<dbg>  texio.write_nl("BOLsigold=" .. old)
 %<dbg>  texio.write("   BOLsig=" .. new)
 %    \end{macrocode}
@@ -1610,54 +1645,52 @@
 %     the first word’s signature, but doesn’t compare it with the
 %     previous line’s.
 %    \begin{macrocode}
-  if flag then
-     local MinFull = luatypo.MinFull
-     local MinPart = luatypo.MinPart
-     MinFull = math.min(MinPart,MinFull)
-     local k = MinPart
-     local oldfirst = utf8_gsub (old, "_.*", "_")
-     local newfirst = utf8_gsub (new, "_.*", "_")
-     local i
-     if utf8_find(newfirst, "_") then
-        i = utf8.len(newfirst)
-     end
-     if i and i <= MinPart then
-        k = MinPart + 1
-     end
+  if flag and old ~= "" then
+     local oldfirst = utf8_gsub (old, "_.*", "")
+     local newfirst = utf8_gsub (new, "_.*", "")
      local oldsub = ""
      local newsub = ""
      for p, c in utf8.codes(old) do
-       if utf8.len(oldsub) < k then oldsub = oldsub .. utf8.char(c) end
+       local s = utf8_gsub(oldsub, "_", "")
+       if utf8_len(s) < MinPart then
+          oldsub = oldsub .. utf8.char(c)
+       end
      end
      for p, c in utf8.codes(new) do
-       if utf8.len(newsub) < k then newsub = newsub .. utf8.char(c) end
+       local s = utf8_gsub(newsub, "_", "")
+       if utf8_len(s) < MinPart then
+          newsub = newsub .. utf8.char(c)
+       end
      end
-     local l = utf8.len(newsub)
-     if oldsub == newsub and l >= k then
+     if oldsub == newsub then
 %<dbg>        texio.write_nl("BOLnewsub=" .. newsub)
         match = true
-     elseif oldfirst == newfirst  and utf8.len(newfirst) > MinFull then
+     end
+     if oldfirst == newfirst  and utf8_len(newfirst) >= MinFull then
 %<dbg>        texio.write_nl("BOLnewfirst=" .. newfirst)
+        if utf8_len(newfirst) > MinPart or not match then
+           oldsub = oldfirst
+           newsub = newfirst
+        end
         match = true
-        oldsub = oldfirst
-        newsub = newfirst
-        k = utf8.len(newfirst)
      end
      if match then
 %    \end{macrocode}
-%    Minimal partial match; any more glyphs matching?
+%    Minimal full or partial match |newsub| of length |k|;
+%    any more glyphs matching?
 %    \begin{macrocode}
+        local k = utf8_len(newsub)
         local osub = oldsub
         local nsub = newsub
-        while osub == nsub and k <= maxlen do
+        while osub == nsub and k < maxlen do
           k = k + 1
-          osub = string.sub(old,1,k)
-          nsub = string.sub(new,1,k)
+          osub = utf8_sub(old,1,k)
+          nsub = utf8_sub(new,1,k)
           if osub == nsub then
              newsub = nsub
           end
         end
-        newsub = utf8_gsub(newsub, "_$", "")   --$
+        newsub = utf8_gsub(newsub, "_+$", "")   --$
 %<dbg>        texio.write_nl("BOLfullmatch=" .. newsub)
         local msg = "B.O.L. MATCH=" .. newsub
         log_flaw(msg, line, colno, footnote)
@@ -1664,23 +1697,30 @@
 %    \end{macrocode}
 %    Lest’s colour the matching string.
 %    \begin{macrocode}
+        local ns = utf8_gsub(newsub, "_", "")
+        k = utf8_len(ns)
         oldsub = newsub
         local newsub = ""
-        local k = utf8.len(oldsub)
         local n = start
-        repeat
-          if n and n.id ~= GLUE then
+        local l = 0
+        local lo = 0
+        local li = 0
+        while n and newsub ~= oldsub and l < k do
+          if n and n.id == HLIST then
+             local nn = n.head
+             for nnn in traverse(nn) do
+               color_node(nnn, COLOR)
+               local c = nn.char
+               if not char_to_discard[c] then l = l + 1 end
+             end
+          elseif n then
              color_node(n, COLOR)
-             l, newsub = signature(n, newsub, swap)
-          elseif n and n.id == GLUE then
-             newsub = newsub .. "_"
-          elseif not n and box then
-             n = box
-          else
-             break
+             li, newsub = signature(n, newsub, swap)
+             l = l + li - lo
+             lo = li
           end
           n = n.next
-        until newsub == oldsub or l >= k
+        end
      end
   end
   return new, match
@@ -1689,9 +1729,10 @@
 %
 % \changes{v0.65}{2023/03/02}{New `check\_page\_first\_word’ function.}
 %
+% \enlargethispage*{\baselineskip}
 %    The next function checks the first word on a new page: if
-%    it ends a sentence and is short (up to |luatypoMinLen| characters),
-%    the function returns |true| and colors the offending word.
+%    it ends a sentence and is short (up to |\luatypoMinLen| characters),
+%    the function returns |true| and colours the offending word.
 %    Otherwise it just retrurs |false|.
 %    The function requires two arguments: the line’s first node and
 %    a column number (possibly |nil|).
@@ -1702,7 +1743,7 @@
   local match = false
   local swap = false
   local new = ""
-  local maxlen = luatypo.MinLen
+  local minlen = luatypo.MinLen
   local len = 0
   local n = node
   local pn
@@ -1718,7 +1759,7 @@
   repeat
     len, new = signature (n, new, swap)
     n = n.next
-  until len > maxlen or (n and n.id == GLYPH and eow_char[n.char]) or
+  until len > minlen or (n and n.id == GLYPH and eow_char[n.char]) or
         (n and n.id == GLUE) or
         (n and n.id == KERN and n.subtype == 1)
 %    \end{macrocode}
@@ -1729,7 +1770,7 @@
      pn = n
      n = n.next
   end
-  if len <= maxlen and n and n.id == GLYPH and eow_char[n.char] then
+  if len <= minlen and n and n.id == GLYPH and eow_char[n.char] then
      match =true
      if pn and (pn.id == GLUE or pn.id == KERN) then
         new = new .. " "
@@ -1900,6 +1941,33 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{check-EOP}
+%    This function looks ahead of |node| in search of a page end
+%    or a footnote rule and returns the flags |page_bottom| and
+%    |body_bottom| [used in text and display math lines].
+%    \begin{macrocode}
+local check_EOP = function (node)
+  local n = node
+  local page_bot = false
+  local body_bot = false
+  while n and (n.id == GLUE    or n.id == PENALTY or
+               n.id == WHATSIT )    do
+    n = n.next
+  end
+  if not n then
+     page_bot = true
+     body_bot = true
+  elseif footnoterule_ahead(n) then
+     body_bot = true
+%<dbg>     texio.write_nl("=> FOOTNOTE RULE ahead")
+%<dbg>     texio.write_nl("check_vtop: last line before footnotes")
+%<dbg>     texio.write_nl(" ")
+  end
+  return page_bot, body_bot
+end
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{get-pagebody}
 %    The next function scans the \node{vlist}s on the current
 %    page in search of the page body.
@@ -1970,7 +2038,8 @@
 %     if a two columns box starts.}
 %
 %    \begin{macrocode}
-check_vtop = function (head, colno, vpos)
+check_vtop = function (top, colno, vpos)
+  local head = top.list
   local PAGEmin   = luatypo.PAGEmin
   local HYPHmax   = luatypo.HYPHmax
   local LLminWD   = luatypo.LLminWD
@@ -2045,20 +2114,7 @@
 %    This has to be known early in order to set the flags |orphanflag|
 %    and |ftnsplit|.
 %    \begin{macrocode}
-       local n = nextnode
-       while n and (n.id == GLUE    or n.id == PENALTY or
-                    n.id == WHATSIT )    do
-         n = n.next
-       end
-       if not n then
-          page_bottom = true
-          body_bottom = true
-       elseif footnoterule_ahead(n) then
-          body_bottom = true
-%<dbg>          texio.write_nl("=> FOOTNOTE RULE ahead")
-%<dbg>          texio.write_nl("check_vtop: last line before footnotes")
-%<dbg>          texio.write_nl(" ")
-       end
+       page_bottom, body_bottom = check_EOP (nextnode)
 %    \end{macrocode}
 %    Is the current line overfull or underfull?
 % \changes{v0.50}{2021/05/13}{Detection of overfull boxes fixed: the
@@ -2224,7 +2280,7 @@
 %    (except lists)?
 %    \begin{macrocode}
           if FirstWordMatch then
-             local flag = not ListItem
+             local flag = not ListItem and (line > 1)
              firstwd, flag =
                 check_line_first_word(firstwd, first, line, colno, flag)
              if flag then
@@ -2236,11 +2292,12 @@
 %    \begin{macrocode}
           if LastWordMatch then
              local flag = true
-             if PFskip > BackPI then
+             if PFskip > BackPI or line == 1 then
                 flag = false
              end
+             local pnp = pn.prev
              lastwd, flag =
-                check_line_last_word(lastwd, pn, line, colno, flag)
+                check_line_last_word(lastwd, pnp, line, colno, flag)
              if flag then
                 pageflag = true
              end
@@ -2293,7 +2350,7 @@
           if LastWordMatch then
              local flag = true
              lastwd, flag =
-                check_line_last_word(lastwd, ln, line, colno, flag)
+                check_line_last_word(lastwd, pn, line, colno, flag)
              if flag then
                 pageflag = true
              end
@@ -2412,6 +2469,11 @@
           line = pageline
        end
 %    \end{macrocode}
+%    Is this line the last one on the page or before footnotes?
+%    (information needed to set the |pageshort| flag).
+%    \begin{macrocode}
+       page_bottom, body_bottom = check_EOP (nextnode)
+%    \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
@@ -2451,15 +2513,17 @@
           end
        end
     elseif head and head.id == RULE and head.subtype == 0  then
+       vpos = vpos + head.height + head.depth
 %    \end{macrocode}
-%    This is a \node{rule}, possibly a footnote rule.
+%    This is a \node{rule}, possibly a footnote rule. It has most likely
+%    been detected on the previous line (then |body_bottom=true|) but
+%    might have no text before (footnote-only page!).
 %    \begin{macrocode}
-       vpos = vpos + head.height + head.depth
-       if body_bottom then
+       local prev = head.prev
+       if body_bottom or footnoterule_ahead (prev) then
 %    \end{macrocode}
-%    If a |\footnoterule| has been detected on the previous run,
-%    set the |footnote| flag and reset some counters and flags
-%    for the coming footnote lines.
+%    If it is, set the |footnote| flag and reset some counters
+%    and flags for the coming footnote lines.
 %    \begin{macrocode}
 %<dbg>       texio.write_nl("check_vtop: footnotes start")
 %<dbg>       texio.write_nl(" ")
@@ -2489,10 +2553,9 @@
 %<dbg>             texio.write("   textheight=" .. tht_pt)
 %<dbg>          end
 %<dbg>          texio.write_nl(" ")
-          if pageline > 1 and pageline < PAGEmin and ShortPages then
+          if pageline > 1 and pageline < PAGEmin
+             and vpos < vpos_min  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)
@@ -2509,7 +2572,7 @@
 %    \end{macrocode}
 %    Increment |vpos| on other vertical glues.
 %    \begin{macrocode}
-       vpos = vpos + effective_glue(head,body)
+       vpos = vpos + effective_glue(head,top)
     elseif head.id == KERN and head.subtype == 1 then
 %    \end{macrocode}
 %    This is a vertical kern, let’s update |vpos|.
@@ -2614,7 +2677,7 @@
 %<dbg>       texio.write("-" .. top.subtype)
 %<dbg>       texio.write("  wd=" .. boxwd .. "  ht=" .. boxht)
 %<dbg>       texio.write_nl(" ")
-       local next = check_vtop(first,colno,vpos)
+       local next = check_vtop(top,colno,vpos)
        if next then
           top = next
        elseif top then
@@ -2630,9 +2693,8 @@
 %<dbg>           texio.write_nl("**MULTICOL type1:")
 %<dbg>           texio.write_nl(" ")
        colno = 0
-       for n in traverse_id(VLIST, first) do
+       for col in traverse_id(VLIST, first) do
            colno = colno + 1
-           col = n.list
 %<dbg>           texio.write_nl("Start of col." .. colno)
 %<dbg>           texio.write_nl(" ")
            check_vtop(col,colno,vpos)
@@ -2658,9 +2720,8 @@
        colno = 0
        for n in traverse_id(HLIST, first) do
            colno = colno + 1
-           local nn = n.list
-           if nn and nn.list then
-              col = nn.list
+           local col = n.list
+           if col and col.list then
 %<dbg>              texio.write_nl("Start of col." .. colno)
 %<dbg>              texio.write_nl(" ")
               check_vtop(col,colno,vpos)
@@ -2739,7 +2800,7 @@
     \luatypoStretchMax=200\relax
     \luatypoHyphMax=2\relax
     \luatypoPageMin=5\relax
-    \luatypoMinFull=4\relax
+    \luatypoMinFull=3\relax
     \luatypoMinPART=4\relax
     \luatypoMinLen=4\relax
    }%

Added: trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty	2023-04-14 20:30:42 UTC (rev 66848)
@@ -0,0 +1,1345 @@
+%%
+%% This is file `lua-typo.sty',
+%% 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}[2021/06/01]
+\ProvidesPackage{lua-typo}
+                [2023-03-08 v.0.65 Daniel Flipo]
+\ifdefined\DeclareRelease
+  \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
+  \DeclareCurrentRelease{}{2023-03-08}
+\else
+  \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide
+    access\MessageBreak to former versions of the lua-typo package.%
+    \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated%
+    \MessageBreak 2020-01-01 or newer; reported}
+\fi
+\NeedsTeXFormat{LaTeX2e}[2021/06/01]
+\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\luatypoMinLen
+\newcount\luatypo at LANGno
+\newcount\luatypo at options
+\newtoks\luatypo at single
+\newtoks\luatypo at 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}
+\DeclareBoolOption[false]{FootnoteSplit}
+\DeclareBoolOption[false]{ShortFinalWord}
+\AddToKeyvalOption{luatypo}{All}{%
+  \LT at ShortLinestrue     \LT at ShortPagestrue
+  \LT at OverfullLinestrue  \LT at UnderfullLinestrue
+  \LT at Widowstrue         \LT at Orphanstrue
+  \LT at EOPHyphenstrue     \LT at RepeatedHyphenstrue
+  \LT at ParLastHyphentrue  \LT at EOLShortWordstrue
+  \LT at FirstWordMatchtrue \LT at LastWordMatchtrue
+  \LT at BackParindenttrue  \LT at FootnoteSplittrue
+  \LT at ShortFinalWordtrue
+}
+\ProcessKeyvalOptions{luatypo}
+\AtEndOfPackage{%
+  \ifLT at None
+    \directlua{ luatypo.None = true }%
+  \else
+    \directlua{ luatypo.None = false }%
+  \fi
+  \ifLT at BackParindent
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.BackParindent = true }%
+  \else
+    \directlua{ luatypo.BackParindent = false }%
+  \fi
+  \ifLT at ShortLines
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.ShortLines = true }%
+  \else
+    \directlua{ luatypo.ShortLines = false }%
+  \fi
+  \ifLT at ShortPages
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.ShortPages = true }%
+  \else
+    \directlua{ luatypo.ShortPages = false }%
+  \fi
+  \ifLT at OverfullLines
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.OverfullLines = true }%
+  \else
+    \directlua{ luatypo.OverfullLines = false }%
+  \fi
+  \ifLT at UnderfullLines
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.UnderfullLines = true }%
+  \else
+    \directlua{ luatypo.UnderfullLines = false }%
+  \fi
+  \ifLT at Widows
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.Widows = true }%
+  \else
+    \directlua{ luatypo.Widows = false }%
+  \fi
+  \ifLT at Orphans
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.Orphans = true }%
+  \else
+    \directlua{ luatypo.Orphans = false }%
+  \fi
+  \ifLT at EOPHyphens
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.EOPHyphens = true }%
+  \else
+    \directlua{ luatypo.EOPHyphens = false }%
+  \fi
+  \ifLT at RepeatedHyphens
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.RepeatedHyphens = true }%
+  \else
+    \directlua{ luatypo.RepeatedHyphens = false }%
+  \fi
+  \ifLT at ParLastHyphen
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.ParLastHyphen = true }%
+  \else
+    \directlua{ luatypo.ParLastHyphen = false }%
+  \fi
+  \ifLT at EOLShortWords
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.EOLShortWords = true }%
+  \else
+    \directlua{ luatypo.EOLShortWords = false }%
+  \fi
+  \ifLT at FirstWordMatch
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.FirstWordMatch = true }%
+  \else
+    \directlua{ luatypo.FirstWordMatch = false }%
+  \fi
+  \ifLT at LastWordMatch
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.LastWordMatch = true }%
+  \else
+    \directlua{ luatypo.LastWordMatch = false }%
+  \fi
+  \ifLT at FootnoteSplit
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.FootnoteSplit = true }%
+  \else
+    \directlua{ luatypo.FootnoteSplit = false }%
+  \fi
+  \ifLT at ShortFinalWord
+    \advance\luatypo at options by 1
+    \directlua{ luatypo.ShortFinalWord = true }%
+  \else
+    \directlua{ luatypo.ShortFinalWord = false }%
+  \fi
+}
+\ifLT at 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
+     FootnoteSplit   [false]\MessageBreak
+     ShortFinalWord  [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.MinLen  = tex.count.luatypoMinLen
+    luatypo.LLminWD = tex.dimen.luatypoLLminWD
+    luatypo.BackPI  = tex.dimen.luatypoBackPI
+    luatypo.BackFuzz  = tex.dimen.luatypoBackFuzz
+   }%
+}
+\AtVeryEndDocument{%
+\ifnum\luatypo at options = 0 \LT at Nonetrue \fi
+\ifLT at 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(' ')
+    local fileout= tex.jobname .. ".typo"
+    local out=io.open(fileout,"w+")
+    out:write(luatypo.buffer)
+    io.close(out)
+   }%
+\fi}
+\newcommand*{\luatypoOneChar}[2]{%
+  \def\luatypo at LANG{#1}\luatypo at single={#2}%
+  \ifcsname l@\luatypo at LANG\endcsname
+    \luatypo at LANGno=\the\csname l@\luatypo at LANG\endcsname \relax
+    \directlua{
+      local langno = \the\luatypo at LANGno
+      local string = \the\luatypo at single
+      luatypo.single[langno] = " "
+      for p, c in utf8.codes(string) do
+        local s = utf8.char(c)
+        luatypo.single[langno] = luatypo.single[langno] .. s
+      end
+     }%
+  \else
+    \PackageWarning{luatypo}{Unknown language "\luatypo at LANG",
+       \MessageBreak \protect\luatypoOneChar\space command ignored}%
+  \fi}
+\newcommand*{\luatypoTwoChars}[2]{%
+  \def\luatypo at LANG{#1}\luatypo at double={#2}%
+  \ifcsname l@\luatypo at LANG\endcsname
+    \luatypo at LANGno=\the\csname l@\luatypo at LANG\endcsname \relax
+    \directlua{
+      local langno = \the\luatypo at LANGno
+      local string = \the\luatypo at double
+      luatypo.double[langno] = " "
+      for p, c in utf8.codes(string) do
+        local s = utf8.char(c)
+        luatypo.double[langno] = luatypo.double[langno] .. s
+      end
+    }%
+  \else
+    \PackageWarning{luatypo}{Unknown language "\luatypo at LANG",
+       \MessageBreak \protect\luatypoTwoChars\space command ignored}%
+  \fi}
+\newcommand*{\luatypoSetColor}[2]{%
+  \begingroup
+    \color{#2}%
+    \directlua{luatypo.colortbl[#1]=\the\LuaCol at Attribute}%
+  \endgroup
+}
+\begin{luacode}
+luatypo.single = { }
+luatypo.double = { }
+luatypo.colortbl  = { }
+luatypo.pagelist  = " "
+luatypo.buffer    = "List of typographic flaws found for "
+                    .. tex.jobname .. ".pdf:\string\n\string\n"
+
+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 eow_char = { }
+eow_char[string.byte(".")] = true
+eow_char[string.byte("!")] = true
+eow_char[string.byte("?")] = true
+eow_char[utf8.codepoint("…")] = true
+
+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")
+local WHATSIT = node.id("whatsit")
+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 INDENT  = 3
+local ALIGN   = 4
+local EQN     = 6
+local USER = 0
+local HYPH = 0x2D
+local LIGA = 0x102
+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
+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 utf8_find = unicode.utf8.find
+local utf8_gsub = unicode.utf8.gsub
+local utf8_reverse = function (s)
+  if utf8.len(s) > 1 then
+     local so = ""
+     for p, c in utf8.codes(s) do
+         so = utf8.char(c) .. so
+     end
+     s = so
+  end
+  return s
+end
+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_line = function (head, color)
+  local first = head.head
+  for n in traverse(first) do
+      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
+log_flaw= function (msg, line, colno, footnote)
+  local pageno = tex.getcount("c at 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
+local signature = function (node, string, swap)
+  local n = node
+  local str = string
+  if n and n.id == GLYPH then
+     local b = n.char
+     if b and not char_to_discard[b] then
+        if n.components then
+           local c = ""
+           for nn in traverse_id(GLYPH, n.components) do
+             c = c .. utf8.char(nn.char)
+           end
+           if swap then
+              str = str .. utf8_reverse(c)
+           else
+              str = str .. c
+           end
+        else
+           str = str .. utf8.char(b)
+        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 then
+       if pre.components then
+          for nn in traverse_id(GLYPH, post.components) do
+            c1 = c1 .. utf8.char(nn.char)
+          end
+       else
+          c1 = utf8.char(pre.char)
+       end
+       c1 = utf8_gsub(c1, "-", "")
+    end
+    if post and post.char then
+       if post.components then
+          for nn in traverse_id(GLYPH, post.components) do
+            c2 = c2 .. utf8.char(nn.char)
+          end
+       else
+          c2 = utf8.char(post.char)
+       end
+    end
+    if swap then
+       str = str .. utf8_reverse(c2) .. c1
+    else
+       str = str .. c1 .. c2
+    end
+  end
+  local len = utf8.len(str)
+  if utf8_find(str, "_") then
+     len = len - 1
+  end
+  return len, str
+end
+local check_line_last_word = function (old, node, line, colno, flag)
+  local COLOR = luatypo.colortbl[11]
+  local match = false
+  local new = ""
+  local maxlen = 0
+  if node then
+     local swap = true
+     local box, go
+     local lastn = node
+     while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC and
+           lastn.id ~= HLIST do
+       lastn = lastn.prev
+     end
+     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)
+        until not n or n.id == GLUE
+     end
+     new = utf8_reverse(new)
+     if flag then
+        local MinFull = luatypo.MinFull
+        local MinPart = luatypo.MinPart
+        MinFull = math.min(MinPart,MinFull)
+        local k = MinPart
+        local dlo = utf8_reverse(old)
+        local wen = utf8_reverse(new)
+        local oldlast = utf8_gsub (old, ".*_", "_")
+        local newlast = utf8_gsub (new, ".*_", "_")
+        local i
+        if utf8_find(newlast, "_") then
+           i = utf8.len(newlast)
+        end
+        if i and i > maxlen - MinPart + 1 then
+           k = MinPart + 1
+        end
+        local oldsub = ""
+        local newsub = ""
+        for p, c in utf8.codes(dlo) do
+          if utf8.len(oldsub) < k then
+             oldsub = utf8.char(c) .. oldsub
+          end
+        end
+        for p, c in utf8.codes(wen) do
+          if utf8.len(newsub) < k then
+             newsub = utf8.char(c) .. newsub
+          end
+        end
+        local l = utf8.len(new)
+        if oldsub == newsub and l >= k then
+           match = true
+        elseif oldlast == newlast and utf8.len(newlast) > MinFull then
+           match = true
+           oldsub = oldlast
+           newsub = newlast
+           k = utf8.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
+           newsub = utf8_gsub(newsub, "^_", "")
+           local msg = "E.O.L. MATCH=" .. newsub
+           log_flaw(msg, line, colno, footnote)
+           oldsub = utf8_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 and n.id == GLUE then
+                newsub = newsub .. "_"
+             elseif not n and box then
+                n = box
+             else
+                break
+             end
+             n = n.prev
+           until newsub == oldsub or l >= k
+        end
+     end
+  end
+  return new, match
+end
+local check_line_first_word = function (old, node, line, colno, flag)
+  local COLOR = luatypo.colortbl[10]
+  local match = false
+  local swap = false
+  local new = ""
+  local maxlen = 0
+  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)
+     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 oldfirst = utf8_gsub (old, "_.*", "_")
+     local newfirst = utf8_gsub (new, "_.*", "_")
+     local i
+     if utf8_find(newfirst, "_") then
+        i = utf8.len(newfirst)
+     end
+     if i and i <= MinPart then
+        k = MinPart + 1
+     end
+     local oldsub = ""
+     local newsub = ""
+     for p, c in utf8.codes(old) do
+       if utf8.len(oldsub) < k then oldsub = oldsub .. utf8.char(c) end
+     end
+     for p, c in utf8.codes(new) do
+       if utf8.len(newsub) < k then newsub = newsub .. utf8.char(c) end
+     end
+     local l = utf8.len(newsub)
+     if oldsub == newsub and l >= k then
+        match = true
+     elseif oldfirst == newfirst  and utf8.len(newfirst) > MinFull then
+        match = true
+        oldsub = oldfirst
+        newsub = newfirst
+        k = utf8.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
+        newsub = utf8_gsub(newsub, "_$", "")   --$
+        local msg = "B.O.L. MATCH=" .. newsub
+        log_flaw(msg, line, colno, footnote)
+        oldsub = newsub
+        local newsub = ""
+        local k = utf8.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 and n.id == GLUE then
+             newsub = newsub .. "_"
+          elseif not n and box then
+             n = box
+          else
+             break
+          end
+          n = n.next
+        until newsub == oldsub or l >= k
+     end
+  end
+  return new, match
+end
+local check_page_first_word = function (node, colno)
+  local COLOR = luatypo.colortbl[14]
+  local match = false
+  local swap = false
+  local new = ""
+  local maxlen = luatypo.MinLen
+  local len = 0
+  local n = node
+  local pn
+  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
+     start = n.head
+     n = n.head
+  end
+  repeat
+    len, new = signature (n, new, swap)
+    n = n.next
+  until len > maxlen or (n and n.id == GLYPH and eow_char[n.char]) or
+        (n and n.id == GLUE) or
+        (n and n.id == KERN and n.subtype == 1)
+  if n and (n.id == GLUE or n.id == KERN) then
+     pn = n
+     n = n.next
+  end
+  if len <= maxlen and n and n.id == GLYPH and eow_char[n.char] then
+     match =true
+     if pn and (pn.id == GLUE or pn.id == KERN) then
+        new = new .. " "
+        len = len + 1
+     end
+     len = len + 1
+  end
+   if match then
+     local msg = "ShortFinalWord=" .. new
+     log_flaw(msg, 1, colno, false)
+     local n = start
+     repeat
+       color_node(n, COLOR)
+       n = n.next
+     until eow_char[n.char]
+     color_node(n, COLOR)
+  end
+  return match
+end
+local check_regexpr = function (glyph, line, colno)
+  local COLOR = luatypo.colortbl[3]
+  local lang = glyph.lang
+  local match = false
+  local retflag = false
+  local lchar, id = is_glyph(glyph)
+  local previous = glyph.prev
+  if lang and luatypo.single[lang] then
+     if lchar and previous and previous.id == GLUE then
+        match = utf8_find(luatypo.single[lang], utf8.char(lchar))
+        if match then
+           retflag = true
+           local msg = "RGX MATCH=" .. utf8.char(lchar)
+           log_flaw(msg, line, colno, footnote)
+           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 pprev and pprev.id == GLUE then
+           local pattern = utf8.char(pchar) .. utf8.char(lchar)
+           match = utf8_find(luatypo.double[lang], pattern)
+           if match then
+              retflag = true
+              local msg = "RGX MATCH=" .. pattern
+              log_flaw(msg, line, colno, footnote)
+              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 ppprev and ppprev.id == GLUE then
+              local pattern = utf8.char(pchar) .. utf8.char(lchar)
+              match = utf8_find(luatypo.double[lang], pattern)
+              if match then
+                 retflag = true
+                 local msg = "REGEXP MATCH=" .. pattern
+                 log_flaw(msg, line, colno, footnote)
+                 color_node(pprev,COLOR)
+                 color_node(glyph,COLOR)
+              end
+           end
+        end
+     end
+  end
+return retflag
+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
+local footnoterule_ahead = function (head)
+  local n = head
+  local flag = false
+  local totalht, ruleht, ht1, ht2, ht3
+  if n and n.id == KERN and n.subtype == 1 then
+     totalht = n.kern
+     n = n.next
+     while n and n.id == GLUE do n = n.next end
+     if n and n.id == RULE and n.subtype == 0 then
+        ruleht = n.height
+        totalht = totalht + ruleht
+        n = n.next
+        if n and n.id == KERN and n.subtype == 1 then
+           totalht = totalht + n.kern
+           if totalht == 0 or totalht == ruleht then
+              flag = true
+           else
+           end
+         end
+     end
+  end
+  return flag
+end
+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
+  first = fn.list
+  for n in traverse_id(VLIST,first) do
+      if n.subtype == 0 and n.height == textht then
+         body = n
+         break
+      else
+         first = n.list
+         for n in traverse_id(VLIST,first) do
+             if n.subtype == 0 and n.height == textht then
+                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
+check_vtop = function (head, colno, vpos)
+  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 FootnoteSplit   = luatypo.FootnoteSplit
+  local ShortFinalWord  = luatypo.ShortFinalWord
+  local Stretch  = math.max(luatypo.Stretch/100,1)
+  local blskip   = tex.getglue("baselineskip")
+  local vpos_min = PAGEmin * blskip
+  vpos_min = vpos_min * 1.5
+  local linewd = tex.getdimen("textwidth")
+  local first_bot = true
+  local footnote = false
+  local ftnsplit = false
+  local orphanflag = false
+  local widowflag = false
+  local pageshort  = false
+  local firstwd = ""
+  local lastwd = ""
+  local hyphcount = 0
+  local pageline = 0
+  local ftnline = 0
+  local line = 0
+  local body_bottom = false
+  local page_bottom = false
+  local pageflag = false
+  local pageno = tex.getcount("c at page")
+  while head do
+    local nextnode = head.next
+    if head.id == HLIST and head.subtype == LINE and
+          (head.height > 0 or head.depth > 0) then
+       vpos = vpos + head.height + head.depth
+       local linewd = head.width
+       local first = head.head
+       local ListItem = false
+       if footnote then
+          ftnline = ftnline + 1
+          line = ftnline
+       else
+          pageline = pageline + 1
+          line = pageline
+       end
+       local n = nextnode
+       while n and (n.id == GLUE    or n.id == PENALTY or
+                    n.id == WHATSIT )    do
+         n = n.next
+       end
+       if not n then
+          page_bottom = true
+          body_bottom = true
+       elseif footnoterule_ahead(n) then
+          body_bottom = true
+       end
+       local hmax = linewd + 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
+          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
+       if footnote and page_bottom then
+          ftnsplit = true
+       end
+       while first.id == MKERN or
+             (first.id == GLUE and first.subtype == LFTSKIP) do
+          first = first.next
+       end
+       if first.id == LPAR then
+          hyphcount = 0
+          firstwd = ""
+          lastwd = ""
+          if not footnote then
+             parline = 1
+             if body_bottom then
+                orphanflag = true
+             end
+          end
+          local nn = first.next
+          if nn and nn.id == HLIST and nn.subtype == BOX then
+             ListItem = true
+          end
+       elseif not footnote then
+          parline = parline + 1
+       end
+       local ln = slide(first)
+       local pn = ln.prev
+       if pn and pn.id == GLUE and pn.subtype == PARFILL then
+          hyphcount = 0
+          ftnsplit = false
+          orphanflag = false
+          if pageline == 1 and parline > 1 then
+             widowflag = true
+          end
+          local PFskip = effective_glue(pn,head)
+          if ShortLines then
+             local llwd = linewd - PFskip
+             if llwd < LLminWD then
+                pageflag = true
+                local msg = "SHORT LINE: length=" ..
+                            string.format("%.0fpt", llwd/65536)
+                log_flaw(msg, line, colno, footnote)
+                local COLOR = luatypo.colortbl[6]
+                local attr = oberdiek.luacolor.getattribute()
+                color_line (head, COLOR)
+             end
+          end
+          if BackParindent and PFskip < BackPI and
+             PFskip >= BackFuzz and parline > 1 then
+             pageflag = true
+             local msg = "NEARLY FULL line: backskip=" ..
+                         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
+          if Widows and widowflag then
+             pageflag = true
+             local msg = "WIDOW"
+             log_flaw(msg, line, colno, footnote)
+             local COLOR  = luatypo.colortbl[4]
+             color_line (head, COLOR)
+             widowflag = false
+          end
+          if FirstWordMatch then
+             local flag = not ListItem
+             firstwd, flag =
+                check_line_first_word(firstwd, first, line, colno, flag)
+             if flag then
+                pageflag = true
+             end
+          end
+          if LastWordMatch then
+             local flag = true
+             if PFskip > BackPI then
+                flag = false
+             end
+             lastwd, flag =
+                check_line_last_word(lastwd, pn, line, colno, flag)
+             if flag then
+                pageflag = true
+             end
+          end
+       elseif pn and pn.id == DISC then
+          hyphcount = hyphcount + 1
+          if orphanflag and Orphans then
+             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
+          if (page_bottom or body_bottom) and EOPHyphens then
+             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
+          if FirstWordMatch then
+             local flag = not ListItem
+             firstwd, flag =
+                check_line_first_word(firstwd, first, line, colno, flag)
+             if flag then
+                pageflag = true
+             end
+          end
+          if LastWordMatch then
+             local flag = true
+             lastwd, flag =
+                check_line_last_word(lastwd, ln, line, colno, flag)
+             if flag then
+                pageflag = true
+             end
+          end
+          if hyphcount > HYPHmax and RepeatedHyphens then
+             local COLOR = luatypo.colortbl[2]
+             local pg = show_pre_disc (pn,COLOR)
+             pageflag = true
+             local msg = "REPEATED HYPHENS: more than " .. HYPHmax
+             log_flaw(msg, line, colno, footnote)
+          end
+          if nextnode and ParLastHyphen 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)
+                end
+             end
+          end
+       else
+          hyphcount = 0
+          if orphanflag and Orphans then
+             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
+          if FirstWordMatch then
+             local flag = not ListItem
+             firstwd, flag =
+                check_line_first_word(firstwd, first, line, colno, flag)
+             if flag then
+                pageflag = true
+             end
+          end
+          if LastWordMatch and pn then
+             local flag = true
+             lastwd, flag =
+                check_line_last_word(lastwd, pn, line, colno, flag)
+             if flag then
+                pageflag = true
+             end
+          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
+                if check_regexpr(pn, line, colno) then
+                   pageflag = true
+                end
+             end
+          end
+       end
+       if ShortFinalWord and pageline == 1 and parline > 1 and
+          check_page_first_word(first,colno) then
+          pageflag = true
+       end
+    elseif head.id == HLIST and
+          (head.subtype == EQN or head.subtype == ALIGN) and
+          (head.height > 0 or head.depth > 0) then
+       vpos = vpos + head.height + head.depth
+       if footnote then
+          ftnline = ftnline + 1
+          line = ftnline
+       else
+          pageline = pageline + 1
+          line = pageline
+       end
+       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 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
+    elseif head and head.id == RULE and head.subtype == 0  then
+       vpos = vpos + head.height + head.depth
+       if body_bottom then
+          footnote = true
+          ftnline = 0
+          body_bottom = false
+          orphanflag = false
+          hyphcount = 0
+          firstwd = ""
+          lastwd = ""
+       end
+    elseif body_bottom and head.id == GLUE and head.subtype == 0 then
+       if first_bot then
+          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
+    elseif head.id == GLUE then
+       vpos = vpos + effective_glue(head,body)
+    elseif head.id == KERN and head.subtype == 1 then
+       vpos = vpos + head.kern
+    elseif head.id == VLIST then
+       vpos = vpos + head.height + head.depth
+    elseif head.id == HLIST and head.subtype == BOX then
+       local hf = head.list
+       if hf and hf.id == VLIST and hf.subtype == 0 then
+          break
+       end
+    end
+  head = nextnode
+  end
+  if pageflag then
+     local plist = luatypo.pagelist
+     local lastp = tonumber(string.match(plist, "%s(%d+),%s$"))
+     if not lastp or pageno > lastp then
+        luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", "
+     end
+  end
+  return head
+end
+luatypo.check_page = function (head)
+  local textwd = tex.getdimen("textwidth")
+  local vpos = 0
+  local n2, n3, col, colno
+  local body = get_pagebody(head)
+  local footnote = false
+  local top = body
+  local first = body.list
+  if (first and first.id == HLIST and first.subtype == BOX) or
+     (first and first.id == VLIST and first.subtype == 0) then
+     top = body.list
+     first = top.list
+  end
+  while top do
+    first = top.list
+    if top and top.id == VLIST and top.subtype == 0 and
+       top.width > textwd/2                              then
+       local next = check_vtop(first,colno,vpos)
+       if next then
+          top = next
+       elseif top then
+          top = top.next
+       end
+    elseif (top and top.id == HLIST and top.subtype == BOX) and
+           (first and first.id == VLIST and first.subtype == 0) and
+           (first.height > 0 and first.width > 0) then
+       colno = 0
+       for n in traverse_id(VLIST, first) do
+           colno = colno + 1
+           col = n.list
+           check_vtop(col,colno,vpos)
+       end
+       colno = nil
+       top = top.next
+    elseif (top and top.id == HLIST and top.subtype == BOX) and
+           (first and first.id == HLIST and first.subtype == BOX) and
+           (first.height > 0 and first.width > 0) then
+       colno = 0
+       for n in traverse_id(HLIST, first) do
+           colno = colno + 1
+           local nn = n.list
+           if nn and nn.list then
+              col = nn.list
+              check_vtop(col,colno,vpos)
+           end
+       end
+       colno = nil
+       top = top.next
+    else
+       top = top.next
+    end
+  end
+  return true
+end
+return luatypo.check_page
+\end{luacode}
+\AtEndOfPackage{%
+  \directlua{
+    if not luatypo.None then
+       luatexbase.add_to_callback
+           ("pre_shipout_filter",luatypo.check_page,"check_page",1)
+    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{LTgrey}{gray}{0.6}%
+    \definecolor{LTred}{rgb}{1,0.55,0}
+    \luatypoSetColor0{red}%       Paragraph last full line hyphenated
+    \luatypoSetColor1{red}%       Page last word hyphenated
+    \luatypoSetColor2{red}%       Hyphens on to 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}{LTred}%  First word matches
+    \luatypoSetColor{11}{LTred}%  Last word matches
+    \luatypoSetColor{12}{LTgrey}% Paragraph ending on a nearly full line
+    \luatypoSetColor{13}{cyan}%   Footnote split
+    \luatypoSetColor{14}{red}%    Too short first (final) word on the page
+    \luatypoBackPI=1em\relax
+    \luatypoBackFuzz=2pt\relax
+    \ifdim\parindent=0pt \luatypoLLminWD=20pt\relax
+    \else\luatypoLLminWD=2\parindent\relax\fi
+    \luatypoStretchMax=200\relax
+    \luatypoHyphMax=2\relax
+    \luatypoPageMin=5\relax
+    \luatypoMinFull=4\relax
+    \luatypoMinPART=4\relax
+    \luatypoMinLen=4\relax
+   }%
+%% 
+%%
+%% End of file `lua-typo.sty'.


Property changes on: trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty	2023-04-14 20:30:11 UTC (rev 66847)
+++ trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty	2023-04-14 20:30:42 UTC (rev 66848)
@@ -11,20 +11,12 @@
 %%
 \NeedsTeXFormat{LaTeX2e}[2021/06/01]
 \ProvidesPackage{lua-typo}
-                [2023-03-08 v.0.65 Daniel Flipo]
-\ifdefined\DeclareRelease
-  \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
-  \DeclareCurrentRelease{}{2023-03-08}
-\else
-  \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide
-    access\MessageBreak to former versions of the lua-typo package.%
-    \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated%
-    \MessageBreak 2020-01-01 or newer; reported}
-\fi
-\NeedsTeXFormat{LaTeX2e}[2021/06/01]
+                [2023-04-12 v.0.70 Daniel Flipo]
+\DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
+\DeclareRelease{v0.65}{2023-03-08}{lua-typo-2023-03-08.sty}
+\DeclareCurrentRelease{}{2023-04-12}
 \ifdefined\directlua
-  \RequirePackage{luatexbase,luacode,luacolor}
-  \RequirePackage{kvoptions,atveryend}
+  \RequirePackage{luatexbase,luacode,luacolor,atveryend}
 \else
   \PackageError{This package is meant for LuaTeX only! Aborting}
                {No more information available, sorry!}
@@ -45,39 +37,36 @@
 \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}
-\DeclareBoolOption[false]{FootnoteSplit}
-\DeclareBoolOption[false]{ShortFinalWord}
-\AddToKeyvalOption{luatypo}{All}{%
-  \LT at ShortLinestrue     \LT at ShortPagestrue
-  \LT at OverfullLinestrue  \LT at UnderfullLinestrue
-  \LT at Widowstrue         \LT at Orphanstrue
-  \LT at EOPHyphenstrue     \LT at RepeatedHyphenstrue
-  \LT at ParLastHyphentrue  \LT at EOLShortWordstrue
-  \LT at FirstWordMatchtrue \LT at LastWordMatchtrue
-  \LT at BackParindenttrue  \LT at FootnoteSplittrue
-  \LT at ShortFinalWordtrue
-}
-\ProcessKeyvalOptions{luatypo}
+\DeclareKeys[luatypo]
+  {
+   ShowOptions.if     = LT at ShowOptions     ,
+   None.if            = LT at None            ,
+   BackParindent.if   = LT at BackParindent   ,
+   ShortLines.if      = LT at ShortLines      ,
+   ShortPages.if      = LT at ShortPages      ,
+   OverfullLines.if   = LT at OverfullLines   ,
+   UnderfullLines.if  = LT at UnderfullLines  ,
+   Widows.if          = LT at Widows          ,
+   Orphans.if         = LT at Orphans         ,
+   EOPHyphens.if      = LT at EOPHyphens      ,
+   RepeatedHyphens.if = LT at RepeatedHyphens ,
+   ParLastHyphen.if   = LT at ParLastHyphen   ,
+   EOLShortWords.if   = LT at EOLShortWords   ,
+   FirstWordMatch.if  = LT at FirstWordMatch  ,
+   LastWordMatch.if   = LT at LastWordMatch   ,
+   FootnoteSplit.if   = LT at FootnoteSplit   ,
+   ShortFinalWord.if  = LT at ShortFinalWord  ,
+   All.if             = LT at All             ,
+   All.code           = \LT at ShortLinestrue     \LT at ShortPagestrue
+                        \LT at OverfullLinestrue  \LT at UnderfullLinestrue
+                        \LT at Widowstrue         \LT at Orphanstrue
+                        \LT at EOPHyphenstrue     \LT at RepeatedHyphenstrue
+                        \LT at ParLastHyphentrue  \LT at EOLShortWordstrue
+                        \LT at FirstWordMatchtrue \LT at LastWordMatchtrue
+                        \LT at BackParindenttrue  \LT at FootnoteSplittrue
+                        \LT at ShortFinalWordtrue
+  }
+\ProcessKeyOptions[luatypo]
 \AtEndOfPackage{%
   \ifLT at None
     \directlua{ luatypo.None = true }%
@@ -182,6 +171,7 @@
      \MessageBreak
      ShowOptions     [false]\MessageBreak
      None            [false]\MessageBreak
+     All             [false]\MessageBreak
      BackParindent   [false]\MessageBreak
      ShortLines      [false]\MessageBreak
      ShortPages      [false]\MessageBreak
@@ -209,6 +199,7 @@
     luatypo.Stretch = tex.count.luatypoStretchMax
     luatypo.MinFull = tex.count.luatypoMinFull
     luatypo.MinPart = tex.count.luatypoMinPart
+    luatypo.MinFull = math.min(luatypo.MinPart,luatypo.MinFull)
     luatypo.MinLen  = tex.count.luatypoMinLen
     luatypo.LLminWD = tex.dimen.luatypoLLminWD
     luatypo.BackPI  = tex.dimen.luatypoBackPI
@@ -344,10 +335,11 @@
 local has_field = node.has_field
 local uses_font = node.uses_font
 local is_glyph  = node.is_glyph
+local utf8_len  = utf8.len
 local utf8_find = unicode.utf8.find
 local utf8_gsub = unicode.utf8.gsub
 local utf8_reverse = function (s)
-  if utf8.len(s) > 1 then
+  if utf8_len(s) > 1 then
      local so = ""
      for p, c in utf8.codes(s) do
          so = utf8.char(c) .. so
@@ -356,6 +348,11 @@
   end
   return s
 end
+local utf8_sub = function (s,i,j)
+    i=utf8.offset(s,i)
+    j=utf8.offset(s,j+1)-1
+    return string.sub(s,i,j)
+end
 local color_node = function (node, color)
   local attr = oberdiek.luacolor.getattribute()
   if node and node.id == DISC then
@@ -482,11 +479,11 @@
     else
        str = str .. c1 .. c2
     end
+  elseif n and n.id == GLUE then
+       str = str .. "_"
   end
-  local len = utf8.len(str)
-  if utf8_find(str, "_") then
-     len = len - 1
-  end
+  local s = utf8_gsub(str, "_", "")
+  local len = utf8_len(s)
   return len, str
 end
 local check_line_last_word = function (old, node, line, colno, flag)
@@ -494,6 +491,8 @@
   local match = false
   local new = ""
   local maxlen = 0
+  local MinFull = luatypo.MinFull
+  local MinPart = luatypo.MinPart
   if node then
      local swap = true
      local box, go
@@ -503,103 +502,104 @@
        lastn = lastn.prev
      end
      local n = lastn
-     if n and n.id == HLIST then
-        box = n
-        prev = n.prev
-        lastn = slide(n.head)
-        n = lastn
+     local words = 0
+     while n and (words <= 2 or maxlen < MinPart) do
+       if n and n.id == HLIST then
+          box = n
+          local first = n.head
+          local lastn = slide(first)
+          n = lastn
+          while n do
+            maxlen, new = signature (n, new, swap)
+            n = n.prev
+          end
+          n = box.prev
+          local w = utf8_gsub(new, "_", "")
+          words = words + utf8_len(new) - utf8_len(w) + 1
+       else
+          repeat
+            maxlen, new = signature (n, new, swap)
+            n = n.prev
+          until not n or n.id == GLUE or n.id == HLIST
+          if n and n.id == GLUE then
+             maxlen, new = signature (n, new, swap)
+             words = words + 1
+             n = n.prev
+          end
+       end
      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)
-        until not n or n.id == GLUE
-     end
      new = utf8_reverse(new)
-     if flag then
-        local MinFull = luatypo.MinFull
-        local MinPart = luatypo.MinPart
-        MinFull = math.min(MinPart,MinFull)
-        local k = MinPart
+     new = utf8_gsub(new, "_+$", "")  -- $
+     new = utf8_gsub(new, "^_+", "")
+     maxlen = math.min(utf8_len(old), utf8_len(new))
+     if flag and old ~= "" then
+        local oldlast = utf8_gsub (old, ".*_", "")
+        local newlast = utf8_gsub (new, ".*_", "")
+        local oldsub = ""
+        local newsub = ""
         local dlo = utf8_reverse(old)
         local wen = utf8_reverse(new)
-        local oldlast = utf8_gsub (old, ".*_", "_")
-        local newlast = utf8_gsub (new, ".*_", "_")
-        local i
-        if utf8_find(newlast, "_") then
-           i = utf8.len(newlast)
-        end
-        if i and i > maxlen - MinPart + 1 then
-           k = MinPart + 1
-        end
-        local oldsub = ""
-        local newsub = ""
         for p, c in utf8.codes(dlo) do
-          if utf8.len(oldsub) < k then
+          local s = utf8_gsub(oldsub, "_", "")
+          if utf8_len(s) < MinPart then
              oldsub = utf8.char(c) .. oldsub
           end
         end
         for p, c in utf8.codes(wen) do
-          if utf8.len(newsub) < k then
+          local s = utf8_gsub(newsub, "_", "")
+          if utf8_len(s) < MinPart then
              newsub = utf8.char(c) .. newsub
           end
         end
-        local l = utf8.len(new)
-        if oldsub == newsub and l >= k then
+        if oldsub == newsub then
            match = true
-        elseif oldlast == newlast and utf8.len(newlast) > MinFull then
+        end
+        if oldlast == newlast and utf8_len(newlast) >= MinFull then
+           if utf8_len(newlast) > MinPart or not match then
+              oldsub = oldlast
+              newsub = newlast
+           end
            match = true
-           oldsub = oldlast
-           newsub = newlast
-           k = utf8.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)
+           local k = utf8_len(newsub)
+           local osub = utf8_reverse(oldsub)
+           local nsub = utf8_reverse(newsub)
+           while osub == nsub and k < maxlen do
+             k = k + 1
+             osub = utf8_sub(dlo,1,k)
+             nsub = utf8_sub(wen,1,k)
              if osub == nsub then
-                newsub = nsub
+                newsub = utf8_reverse(nsub)
              end
            end
-           newsub = utf8_gsub(newsub, "^_", "")
+           newsub = utf8_gsub(newsub, "^_+", "")
            local msg = "E.O.L. MATCH=" .. newsub
            log_flaw(msg, line, colno, footnote)
+           local ns = utf8_gsub(newsub, "_", "")
+           k = utf8_len(ns)
            oldsub = utf8_reverse(newsub)
            local newsub = ""
            local n = lastn
-           repeat
-             if n and n.id ~= GLUE then
+           local l = 0
+           local lo = 0
+           local li = 0
+           while n and newsub ~= oldsub and l < k do
+             if n and n.id == HLIST then
+                local first = n.head
+                for nn in traverse_id(GLYPH, first) do
+                  color_node(nn, COLOR)
+                  local c = nn.char
+                  if not char_to_discard[c] then l = l + 1 end
+                end
+             elseif n then
                 color_node(n, COLOR)
-                l, newsub = signature(n, newsub, swap)
-             elseif n and n.id == GLUE then
-                newsub = newsub .. "_"
-             elseif not n and box then
-                n = box
-             else
-                break
+                li, newsub = signature(n, newsub, swap)
+                l = l + li - lo
+                lo = li
              end
              n = n.prev
-           until newsub == oldsub or l >= k
+           end
         end
      end
   end
@@ -611,6 +611,8 @@
   local swap = false
   local new = ""
   local maxlen = 0
+  local MinFull = luatypo.MinFull
+  local MinPart = luatypo.MinPart
   local n = node
   local box, go
   while n and n.id ~= GLYPH and n.id ~= DISC and
@@ -617,97 +619,100 @@
         (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
+  start = n
+  local words = 0
+  while n and (words <= 2 or maxlen < MinPart) do
+    if n and n.id == HLIST then
+       box = n
+       n = n.head
+       while n do
+         maxlen, new = signature (n, new, swap)
+         n = n.next
+       end
+       n = box.next
+       local w = utf8_gsub(new, "_", "")
+       words = words + utf8_len(new) - utf8_len(w) + 1
+    else
+       repeat
+         maxlen, new = signature (n, new, swap)
+         n = n.next
+       until not n or n.id == GLUE or n.id == HLIST
+       if n and n.id == GLUE then
+          maxlen, new = signature (n, new, swap)
+          words = words + 1
+          n = n.next
+       end
+    end
   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)
-     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 oldfirst = utf8_gsub (old, "_.*", "_")
-     local newfirst = utf8_gsub (new, "_.*", "_")
-     local i
-     if utf8_find(newfirst, "_") then
-        i = utf8.len(newfirst)
-     end
-     if i and i <= MinPart then
-        k = MinPart + 1
-     end
+  new = utf8_gsub(new, "_+$", "") -- $
+  new = utf8_gsub(new, "^_+", "")
+  maxlen = math.min(utf8_len(old), utf8_len(new))
+  if flag and old ~= "" then
+     local oldfirst = utf8_gsub (old, "_.*", "")
+     local newfirst = utf8_gsub (new, "_.*", "")
      local oldsub = ""
      local newsub = ""
      for p, c in utf8.codes(old) do
-       if utf8.len(oldsub) < k then oldsub = oldsub .. utf8.char(c) end
+       local s = utf8_gsub(oldsub, "_", "")
+       if utf8_len(s) < MinPart then
+          oldsub = oldsub .. utf8.char(c)
+       end
      end
      for p, c in utf8.codes(new) do
-       if utf8.len(newsub) < k then newsub = newsub .. utf8.char(c) end
+       local s = utf8_gsub(newsub, "_", "")
+       if utf8_len(s) < MinPart then
+          newsub = newsub .. utf8.char(c)
+       end
      end
-     local l = utf8.len(newsub)
-     if oldsub == newsub and l >= k then
+     if oldsub == newsub then
         match = true
-     elseif oldfirst == newfirst  and utf8.len(newfirst) > MinFull then
+     end
+     if oldfirst == newfirst  and utf8_len(newfirst) >= MinFull then
+        if utf8_len(newfirst) > MinPart or not match then
+           oldsub = oldfirst
+           newsub = newfirst
+        end
         match = true
-        oldsub = oldfirst
-        newsub = newfirst
-        k = utf8.len(newfirst)
      end
      if match then
+        local k = utf8_len(newsub)
         local osub = oldsub
         local nsub = newsub
-        while osub == nsub and k <= maxlen do
+        while osub == nsub and k < maxlen do
           k = k + 1
-          osub = string.sub(old,1,k)
-          nsub = string.sub(new,1,k)
+          osub = utf8_sub(old,1,k)
+          nsub = utf8_sub(new,1,k)
           if osub == nsub then
              newsub = nsub
           end
         end
-        newsub = utf8_gsub(newsub, "_$", "")   --$
+        newsub = utf8_gsub(newsub, "_+$", "")   --$
         local msg = "B.O.L. MATCH=" .. newsub
         log_flaw(msg, line, colno, footnote)
+        local ns = utf8_gsub(newsub, "_", "")
+        k = utf8_len(ns)
         oldsub = newsub
         local newsub = ""
-        local k = utf8.len(oldsub)
         local n = start
-        repeat
-          if n and n.id ~= GLUE then
+        local l = 0
+        local lo = 0
+        local li = 0
+        while n and newsub ~= oldsub and l < k do
+          if n and n.id == HLIST then
+             local nn = n.head
+             for nnn in traverse(nn) do
+               color_node(nnn, COLOR)
+               local c = nn.char
+               if not char_to_discard[c] then l = l + 1 end
+             end
+          elseif n then
              color_node(n, COLOR)
-             l, newsub = signature(n, newsub, swap)
-          elseif n and n.id == GLUE then
-             newsub = newsub .. "_"
-          elseif not n and box then
-             n = box
-          else
-             break
+             li, newsub = signature(n, newsub, swap)
+             l = l + li - lo
+             lo = li
           end
           n = n.next
-        until newsub == oldsub or l >= k
+        end
      end
   end
   return new, match
@@ -717,7 +722,7 @@
   local match = false
   local swap = false
   local new = ""
-  local maxlen = luatypo.MinLen
+  local minlen = luatypo.MinLen
   local len = 0
   local n = node
   local pn
@@ -733,7 +738,7 @@
   repeat
     len, new = signature (n, new, swap)
     n = n.next
-  until len > maxlen or (n and n.id == GLYPH and eow_char[n.char]) or
+  until len > minlen or (n and n.id == GLYPH and eow_char[n.char]) or
         (n and n.id == GLUE) or
         (n and n.id == KERN and n.subtype == 1)
   if n and (n.id == GLUE or n.id == KERN) then
@@ -740,7 +745,7 @@
      pn = n
      n = n.next
   end
-  if len <= maxlen and n and n.id == GLYPH and eow_char[n.char] then
+  if len <= minlen and n and n.id == GLYPH and eow_char[n.char] then
      match =true
      if pn and (pn.id == GLUE or pn.id == KERN) then
         new = new .. " "
@@ -845,6 +850,22 @@
   end
   return flag
 end
+local check_EOP = function (node)
+  local n = node
+  local page_bot = false
+  local body_bot = false
+  while n and (n.id == GLUE    or n.id == PENALTY or
+               n.id == WHATSIT )    do
+    n = n.next
+  end
+  if not n then
+     page_bot = true
+     body_bot = true
+  elseif footnoterule_ahead(n) then
+     body_bot = true
+  end
+  return page_bot, body_bot
+end
 local get_pagebody = function (head)
   local textht = tex.getdimen("textheight")
   local fn = head.list
@@ -872,7 +893,8 @@
   end
   return body
 end
-check_vtop = function (head, colno, vpos)
+check_vtop = function (top, colno, vpos)
+  local head = top.list
   local PAGEmin   = luatypo.PAGEmin
   local HYPHmax   = luatypo.HYPHmax
   local LLminWD   = luatypo.LLminWD
@@ -929,17 +951,7 @@
           pageline = pageline + 1
           line = pageline
        end
-       local n = nextnode
-       while n and (n.id == GLUE    or n.id == PENALTY or
-                    n.id == WHATSIT )    do
-         n = n.next
-       end
-       if not n then
-          page_bottom = true
-          body_bottom = true
-       elseif footnoterule_ahead(n) then
-          body_bottom = true
-       end
+       page_bottom, body_bottom = check_EOP (nextnode)
        local hmax = linewd + tex.hfuzz
        local w,h,d = dimensions(1,2,0, first)
        if w > hmax and OverfullLines then
@@ -1023,7 +1035,7 @@
              widowflag = false
           end
           if FirstWordMatch then
-             local flag = not ListItem
+             local flag = not ListItem and (line > 1)
              firstwd, flag =
                 check_line_first_word(firstwd, first, line, colno, flag)
              if flag then
@@ -1032,11 +1044,12 @@
           end
           if LastWordMatch then
              local flag = true
-             if PFskip > BackPI then
+             if PFskip > BackPI or line == 1 then
                 flag = false
              end
+             local pnp = pn.prev
              lastwd, flag =
-                check_line_last_word(lastwd, pn, line, colno, flag)
+                check_line_last_word(lastwd, pnp, line, colno, flag)
              if flag then
                 pageflag = true
              end
@@ -1075,7 +1088,7 @@
           if LastWordMatch then
              local flag = true
              lastwd, flag =
-                check_line_last_word(lastwd, ln, line, colno, flag)
+                check_line_last_word(lastwd, pn, line, colno, flag)
              if flag then
                 pageflag = true
              end
@@ -1160,6 +1173,7 @@
           pageline = pageline + 1
           line = pageline
        end
+       page_bottom, body_bottom = check_EOP (nextnode)
        local fl = true
        local wd = 0
        local hmax = 0
@@ -1194,7 +1208,8 @@
        end
     elseif head and head.id == RULE and head.subtype == 0  then
        vpos = vpos + head.height + head.depth
-       if body_bottom then
+       local prev = head.prev
+       if body_bottom or footnoterule_ahead (prev) then
           footnote = true
           ftnline = 0
           body_bottom = false
@@ -1205,10 +1220,9 @@
        end
     elseif body_bottom and head.id == GLUE and head.subtype == 0 then
        if first_bot then
-          if pageline > 1 and pageline < PAGEmin and ShortPages then
+          if pageline > 1 and pageline < PAGEmin
+             and vpos < vpos_min  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)
@@ -1222,7 +1236,7 @@
           first_bot = false
        end
     elseif head.id == GLUE then
-       vpos = vpos + effective_glue(head,body)
+       vpos = vpos + effective_glue(head,top)
     elseif head.id == KERN and head.subtype == 1 then
        vpos = vpos + head.kern
     elseif head.id == VLIST then
@@ -1261,7 +1275,7 @@
     first = top.list
     if top and top.id == VLIST and top.subtype == 0 and
        top.width > textwd/2                              then
-       local next = check_vtop(first,colno,vpos)
+       local next = check_vtop(top,colno,vpos)
        if next then
           top = next
        elseif top then
@@ -1271,9 +1285,8 @@
            (first and first.id == VLIST and first.subtype == 0) and
            (first.height > 0 and first.width > 0) then
        colno = 0
-       for n in traverse_id(VLIST, first) do
+       for col in traverse_id(VLIST, first) do
            colno = colno + 1
-           col = n.list
            check_vtop(col,colno,vpos)
        end
        colno = nil
@@ -1284,9 +1297,8 @@
        colno = 0
        for n in traverse_id(HLIST, first) do
            colno = colno + 1
-           local nn = n.list
-           if nn and nn.list then
-              col = nn.list
+           local col = n.list
+           if col and col.list then
               check_vtop(col,colno,vpos)
            end
        end
@@ -1336,7 +1348,7 @@
     \luatypoStretchMax=200\relax
     \luatypoHyphMax=2\relax
     \luatypoPageMin=5\relax
-    \luatypoMinFull=4\relax
+    \luatypoMinFull=3\relax
     \luatypoMinPART=4\relax
     \luatypoMinLen=4\relax
    }%



More information about the tex-live-commits mailing list.