texlive[59457] Master/texmf-dist: lua-typo (3jun21)

commits+karl at tug.org commits+karl at tug.org
Thu Jun 3 22:50:26 CEST 2021


Revision: 59457
          http://tug.org/svn/texlive?view=revision&revision=59457
Author:   karl
Date:     2021-06-03 22:50:26 +0200 (Thu, 03 Jun 2021)
Log Message:
-----------
lua-typo (3jun21)

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.cfg
    trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty

Added Paths:
-----------
    trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2021-04-18.sty

Modified: trunk/Master/texmf-dist/doc/lualatex/lua-typo/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/lua-typo/README.md	2021-06-03 20:50:11 UTC (rev 59456)
+++ trunk/Master/texmf-dist/doc/lualatex/lua-typo/README.md	2021-06-03 20:50:26 UTC (rev 59457)
@@ -48,13 +48,27 @@
 Changes
 -------
 
-- First release version: 0.30, March 2021.
+* First release version: 0.30, March 2021.
 
-- v.0.32: bug fixes
-  better protection against nil nodes,
-  new page detection corrected,
-  homeoarchy detection improved.
+* v.0.32: bug fixes
+  - better protection against nil nodes,
+  - new page detection corrected,
+  - homeoarchy detection improved.
 
+* v.0.40 (not released): bug fixes
+  - in some cases parlines count was wrong, fixed;
+  - partial improvement of short pages detection.
+
+* v.0.50: new implementation, May 2021
+  - callback change: `pre_shipout_filter` instead of `pre_output_filter`
+    this change requires latex release 2021-06-01;
+  - rollback enabled in case `pre_shipout_filter` is missing;
+  - footnotes are scanned now;
+  - overfull box detection fixed (works for equations and tt fonts now);
+  - short pages detection fixed;
+  - coloration of faulty lines improved;
+  - all flaws found are now recorded into file "`\jobname`.typo".
+
 --
 Copyright 2020--2021 Daniel Flipo
 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	2021-06-03 20:50:11 UTC (rev 59456)
+++ trunk/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx	2021-06-03 20:50:26 UTC (rev 59457)
@@ -1,3 +1,5 @@
+\RequirePackage{pdfmanagement-testphase}
+\DeclareDocumentMetadata{pdfstandard=A-2b, lang=fr-FR}
 \documentclass[a4paper]{ltxdoc}
 \usepackage[dvipsnames]{xcolor}
 \usepackage{fontspec}

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

Added: trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2021-04-18.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2021-04-18.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2021-04-18.sty	2021-06-03 20:50:26 UTC (rev 59457)
@@ -0,0 +1,843 @@
+%%
+%% This is file `lua-typo.sty' v0.40,
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% lua-typo.dtx  (with options: `sty')
+%%
+%% IMPORTANT NOTICE:
+%% For the copyright see the source file `lua-typo.dtx'.
+%%
+\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+\ProvidesPackage{lua-typo}
+                [2021/04/18 v.0.40 Daniel Flipo]
+\ifdefined\directlua
+  \RequirePackage{luatexbase,luacode,luacolor}
+  \RequirePackage{kvoptions,atveryend}
+\else
+  \PackageError{This package is meant for LuaTeX only! Aborting}
+               {No more information available, sorry!}
+\fi
+\newdimen\luatypoLLminWD
+\newdimen\luatypoBackPI
+\newdimen\luatypoBackFuzz
+\newcount\luatypoStretchMax
+\newcount\luatypoHyphMax
+\newcount\luatypoPageMin
+\newcount\luatypoMinFull
+\newcount\luatypoMinPart
+\newcount\luatypo 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}
+\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
+}
+\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 ShowOptions
+  \GenericWarning{* }{%
+     *** List of possible options for lua-typo ***\MessageBreak
+     [Default values between brackets]%
+     \MessageBreak
+     ShowOptions     [false]\MessageBreak
+     None            [false]\MessageBreak
+     BackParindent   [false]\MessageBreak
+     ShortLines      [false]\MessageBreak
+     ShortPages      [false]\MessageBreak
+     OverfullLines   [false]\MessageBreak
+     UnderfullLines  [false]\MessageBreak
+     Widows          [false]\MessageBreak
+     Orphans         [false]\MessageBreak
+     EOPHyphens      [false]\MessageBreak
+     RepeatedHyphens [false]\MessageBreak
+     ParLastHyphen   [false]\MessageBreak
+     EOLShortWords   [false]\MessageBreak
+     FirstWordMatch  [false]\MessageBreak
+     LastWordMatch   [false]\MessageBreak
+     \MessageBreak
+     *********************************************%
+     \MessageBreak Lua-typo [ShowOptions]
+   }%
+\fi
+\AtBeginDocument{%
+  \directlua{
+    luatypo.HYPHmax = tex.count.luatypoHyphMax
+    luatypo.PAGEmin = tex.count.luatypoPageMin
+    luatypo.Stretch = tex.count.luatypoStretchMax
+    luatypo.MinFull = tex.count.luatypoMinFull
+    luatypo.MinPart = tex.count.luatypoMinPart
+    luatypo.LLminWD = tex.dimen.luatypoLLminWD
+    luatypo.BackPI  = tex.dimen.luatypoBackPI
+    luatypo.BackFuzz  = tex.dimen.luatypoBackFuzz
+   }%
+}
+\AtVeryEndDocument{%
+\ifnum\luatypo 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(' ')
+   }%
+\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 = string.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 = string.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  = ""
+
+local char_to_discard = { }
+char_to_discard[string.byte(",")] = true
+char_to_discard[string.byte(".")] = true
+char_to_discard[string.byte("!")] = true
+char_to_discard[string.byte("?")] = true
+char_to_discard[string.byte(":")] = true
+char_to_discard[string.byte(";")] = true
+char_to_discard[string.byte("-")] = true
+
+local split_lig = { }
+split_lig[0xFB00] = "ff"
+split_lig[0xFB01] = "fi"
+split_lig[0xFB02] = "fl"
+split_lig[0xFB03] = "ffi"
+split_lig[0xFB04] = "ffl"
+split_lig[0xFB05] = "st"
+split_lig[0xFB06] = "st"
+
+local DISC  = node.id("disc")
+local GLYPH = node.id("glyph")
+local GLUE  = node.id("glue")
+local KERN  = node.id("kern")
+local HLIST = node.id("hlist")
+local LPAR  = node.id("local_par")
+local MKERN = node.id("margin_kern")
+local PENALTY = node.id("penalty")
+local USRSKIP  = 0
+local PARSKIP  = 3
+local LFTSKIP  = 8
+local RGTSKIP  = 9
+local TOPSKIP = 10
+local PARFILL = 15
+local LINE    = 1
+local BOX     = 2
+local EQN     = 6
+local USER = 0
+local HYPH = 0x2D
+local LIGA = 0x102
+local parline = 0
+
+local effective_glue = node.effective_glue
+local set_attribute = node.set_attribute
+local slide = node.slide
+local traverse = node.traverse
+local traverse_id = node.traverse_id
+local has_field = node.has_field
+local uses_font = node.uses_font
+local is_glyph  = node.is_glyph
+
+local color_node = function (node, color)
+  local attr = oberdiek.luacolor.getattribute()
+  if node and node.id == DISC then
+     local pre = node.pre
+     local post = node.post
+     local repl = node.replace
+     if pre then
+        set_attribute(pre,attr,color)
+     end
+     if post then
+        set_attribute(post,attr,color)
+     end
+     if repl then
+        set_attribute(repl,attr,color)
+     end
+  elseif node then
+     set_attribute(node,attr,color)
+  end
+end
+local color_hlist = function (head, color)
+  local first = head.head
+  for n in traverse(first) do
+      color_node(n, color)
+  end
+end
+local color_line = function (head, color)
+  local first = head.head
+  for n in traverse(first) do
+      if n and n.id == HLIST then
+         color_hlist(n, color)
+      else
+         color_node(n, color)
+      end
+  end
+end
+local signature = function (node, string, swap)
+  local n = node
+  local str = string
+  if n and n.id == GLYPH then
+    local b, id = is_glyph(n)
+    if b and not char_to_discard[b] then
+       if b == 0x2019 then b = 0x27 end
+       if b < 0x100 then
+          str = str .. string.char(b)
+       elseif split_lig[b] then
+          local c = split_lig[b]
+          if swap then
+             c = string.reverse(c)
+          end
+          str = str .. c
+       elseif n.subtype == LIGA and b > 0xE000 then
+          local c = string.sub(b,-2)
+          if swap then
+             c = string.reverse(c)
+          end
+          str = str .. c
+       end
+    end
+  elseif n and n.id == DISC then
+    local pre = n.pre
+    local post = n.post
+    local c1 = ""
+    local c2 = ""
+    if pre and pre.char and pre.char ~= HYPH and pre.char < 0x100 then
+       c1 = string.char(pre.char)
+    end
+    if post and post.char then
+       if post.char < 0x100 then
+          c2 = string.char(post.char)
+       elseif split_lig[post.char] then
+          c2 = split_lig[post.char]
+          if swap then
+             c2 = string.reverse(c2)
+          end
+       end
+    end
+    if swap then
+       str = str .. c2 .. c1
+    else
+       str = str .. c1 .. c2
+    end
+  end
+  local len = string.len(str)
+  if string.find(str, "_") then
+     len = len - 1
+  end
+  return len, str
+end
+local check_last_word = function (old, node, flag)
+  local COLOR = luatypo.colortbl[11]
+  local match = false
+  local new = ""
+  local maxlen = 0
+  if flag and node then
+     local swap = true
+     local lastn = node
+     while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC do
+       lastn = lastn.prev
+     end
+     local n = lastn
+     while n and n.id ~= GLUE do
+       maxlen, new = signature (n, new, swap)
+       n = n.prev
+     end
+     if n and n.id == GLUE then
+        new = new .. "_"
+        repeat
+          n = n.prev
+          maxlen, new = signature (n, new, swap)
+        until not n or n.id == GLUE
+     end
+     new = string.reverse(new)
+     local MinFull = luatypo.MinFull
+     local MinPart = luatypo.MinPart
+     MinFull = math.min(MinPart,MinFull)
+     local k = MinPart
+     local oldlast = string.gsub (old, '.*_', '')
+     local newlast = string.gsub (new, '.*_', '')
+     local i = string.find(new, "_")
+     if i and i > maxlen - MinPart + 1 then
+        k = MinPart + 1
+     end
+     local oldsub = string.sub(old,-k)
+     local newsub = string.sub(new,-k)
+     local l = string.len(new)
+     if oldsub == newsub and l >= k then
+        match = true
+     elseif oldlast == newlast and string.len(newlast) >= MinFull then
+        match = true
+        oldsub = oldlast
+        newsub = newlast
+        k = string.len(newlast)
+     end
+     if match then
+        local osub = oldsub
+        local nsub = newsub
+        while osub == nsub and k <= maxlen do
+          k = k +1
+          osub = string.sub(old,-k)
+          nsub = string.sub(new,-k)
+          if osub == nsub then
+             newsub = nsub
+          end
+        end
+        pageflag = true
+        newsub = string.gsub(newsub, '^_', '')
+        oldsub = string.reverse(newsub)
+        local newsub = ""
+        local n = lastn
+        repeat
+          if n and n.id ~= GLUE then
+             color_node(n, COLOR)
+             l, newsub = signature(n, newsub, swap)
+          elseif n then
+             newsub = newsub .. "_"
+          end
+          n = n.prev
+        until not n or newsub == oldsub or l >= k
+     end
+  end
+  return new
+end
+local check_first_word = function (old, node, flag)
+  local COLOR = luatypo.colortbl[10]
+  local match = false
+  local swap = false
+  local new = ""
+  local maxlen = 0
+  local start = node
+  local n = start
+  while n and n.id ~= GLYPH and n.id ~= DISC do
+     n = n.next
+  end
+  while n and n.id ~= GLUE do
+    maxlen, new = signature (n, new, swap)
+    n = n.next
+  end
+  if n and n.id == GLUE then
+     new = new .. "_"
+     repeat
+       n = n.next
+       maxlen, new = signature (n, new, swap)
+     until not n or n.id == GLUE
+  end
+  if flag then
+     local MinFull = luatypo.MinFull
+     local MinPart = luatypo.MinPart
+     MinFull = math.min(MinPart,MinFull)
+     local k = MinPart
+     local oldsub = ""
+     local newsub = ""
+     local oldfirst = string.gsub (old, '_.*', '')
+     local newfirst = string.gsub (new, '_.*', '')
+     local i = string.find(new, "_")
+     if i and i <= MinPart then
+        k = MinPart + 1
+     end
+     local oldsub = string.sub(old,1,k)
+     local newsub = string.sub(new,1,k)
+     local l = string.len(newsub)
+     if oldsub == newsub and l >= k then
+        match = true
+     elseif oldfirst == newfirst  and string.len(newfirst) >= MinFull then
+        match = true
+        oldsub = oldfirst
+        newsub = newfirst
+        k = string.len(newfirst)
+     end
+     if match then
+        local osub = oldsub
+        local nsub = newsub
+        while osub == nsub and k <= maxlen do
+          k = k + 1
+          osub = string.sub(old,1,k)
+          nsub = string.sub(new,1,k)
+          if osub == nsub then
+             newsub = nsub
+          end
+        end
+        pageflag = true
+        newsub = string.gsub(newsub, '_$', '')   --$
+        oldsub = newsub
+        local newsub = ""
+        local k = string.len(oldsub)
+        local n = start
+        repeat
+          if n and n.id ~= GLUE then
+             color_node(n, COLOR)
+             l, newsub = signature(n, newsub, swap)
+          elseif n then
+             newsub = newsub .. "_"
+          end
+          n = n.next
+        until not n or newsub == oldsub or l >= k
+     end
+  end
+  return new
+end
+local check_regexpr = function (glyph)
+  local COLOR = luatypo.colortbl[3]
+  local lang = glyph.lang
+  local match = false
+  local lchar, id = is_glyph(glyph)
+  local previous = glyph.prev
+  if lang and luatypo.single[lang] then
+     if lchar and lchar < 0x100 and previous and previous.id == GLUE then
+        match = string.find(luatypo.single[lang], string.char(lchar))
+        if match then
+           pageflag = true
+           color_node(glyph,COLOR)
+        end
+     end
+  end
+  if lang and luatypo.double[lang] then
+     if lchar and previous and previous.id == GLYPH then
+        local pchar, id = is_glyph(previous)
+        local pprev = previous.prev
+        if pchar and pchar < 0x100 and pprev and pprev.id == GLUE then
+           local pattern = string.char(pchar) .. string.char(lchar)
+           match = string.find(luatypo.double[lang], pattern)
+           if match then
+              pageflag = true
+              color_node(previous,COLOR)
+              color_node(glyph,COLOR)
+           end
+        end
+     elseif lchar and previous and previous.id == KERN then
+        local pprev = previous.prev
+        if pprev and pprev.id == GLYPH then
+           local pchar, id = is_glyph(pprev)
+           local ppprev = pprev.prev
+           if pchar and pchar < 0x100 and
+              ppprev and ppprev.id == GLUE then
+              local pattern = string.char(pchar) .. string.char(lchar)
+              match = string.find(luatypo.double[lang], pattern)
+              if match then
+                 pageflag = true
+                 color_node(pprev,COLOR)
+                 color_node(glyph,COLOR)
+              end
+           end
+        end
+     end
+  end
+end
+local show_pre_disc = function (disc, color)
+  local n = disc
+  while n and n.id ~= GLUE do
+    color_node(n, color)
+    n = n.prev
+  end
+  return n
+end
+luatypo.check_page = function (head)
+  local PAGEmin   = luatypo.PAGEmin
+  local HYPHmax   = luatypo.HYPHmax
+  local LLminWD   = luatypo.LLminWD
+  local BackPI    = luatypo.BackPI
+  local BackFuzz  = luatypo.BackFuzz
+  local BackParindent   = luatypo.BackParindent
+  local ShortLines      = luatypo.ShortLines
+  local ShortPages      = luatypo.ShortPages
+  local OverfullLines   = luatypo.OverfullLines
+  local UnderfullLines  = luatypo.UnderfullLines
+  local Widows          = luatypo.Widows
+  local Orphans         = luatypo.Orphans
+  local EOPHyphens      = luatypo.EOPHyphens
+  local RepeatedHyphens = luatypo.RepeatedHyphens
+  local FirstWordMatch  = luatypo.FirstWordMatch
+  local ParLastHyphen   = luatypo.ParLastHyphen
+  local EOLShortWords   = luatypo.EOLShortWords
+  local LastWordMatch   = luatypo.LastWordMatch
+  local pageno   = tex.getcount("c at page")
+  local Stretch  = math.max(luatypo.Stretch/100,1)
+  local blskip   = tex.getglue("baselineskip")
+  local textht   = tex.getdimen("textheight")
+  local vpos_min = (PAGEmin+1) * blskip
+  local vpos = 0
+
+  local pageflag = false
+  local orphanflag = false
+  local widowflag  = false
+  local lwhyphflag = false
+  local pageshort  = false
+  local firstwd = ""
+  local lastwd = ""
+  local hyphcount = 0
+  local pageline = 0
+
+  while head do
+    local nextnode = head.next
+    local prevnode = head.prev
+    local pprevnode = nil
+    if prevnode then
+       pprevnode = prevnode.prev
+    end
+    if head.id == HLIST and head.subtype == LINE then
+       vpos = vpos + head.height + head.depth
+       local first = head.head
+       while first.id == MKERN or
+             (first.id == GLUE and first.subtype == LFTSKIP) do
+          first = first.next
+       end
+       local ListItem = false
+       pageline = pageline + 1
+       if head.glue_set == 1 and head.glue_sign == 2 and
+          head.glue_order == 0 and OverfullLines then
+          pageflag = true
+          local COLOR = luatypo.colortbl[7]
+          color_line (head, COLOR)
+       elseif head.glue_set >= Stretch and head.glue_sign == 1 and
+              head.glue_order == 0  and UnderfullLines then
+          local COLOR = luatypo.colortbl[8]
+          pageflag = true
+          color_line (head, COLOR)
+       end
+       if first.id == LPAR then
+          hyphcount = 0
+          parline = 1
+          if not nextnode then
+             orphanflag = true
+          end
+          local nn = first.next
+          if  nn and  nn.id == HLIST and nn.subtype == BOX then
+              ListItem = true
+          end
+       else
+          parline = parline + 1
+       end
+       if FirstWordMatch then
+          local flag = not ListItem
+          firstwd = check_first_word(firstwd, first, flag)
+       end
+       local ln = slide(first)
+       local pn = ln.prev
+       if pn and pn.id == GLUE and pn.subtype == PARFILL then
+          hyphcount = 0
+          orphanflag = false
+          if pageline == 1 and parline > 1 then
+             widowflag = true
+          end
+          local PFskip = effective_glue(pn,head)
+          if ShortLines then
+             local llwd = tex.hsize - PFskip
+             if llwd < LLminWD then
+                pageflag = true
+                local COLOR  = luatypo.colortbl[6]
+                local attr = oberdiek.luacolor.getattribute()
+                color_line (head, COLOR)
+             end
+          end
+          if BackParindent and PFskip < BackPI and PFskip > BackFuzz then
+             pageflag = true
+             local COLOR  = luatypo.colortbl[12]
+             local attr = oberdiek.luacolor.getattribute()
+             color_line (head, COLOR)
+          end
+          if LastWordMatch then
+             local flag = textline
+             if PFskip > BackPI then
+                flag = false
+             end
+             lastwd = check_last_word(lastwd, pn, flag)
+          end
+       elseif pn and pn.id == DISC then
+          hyphcount = hyphcount + 1
+          if LastWordMatch then
+             lastwd = check_last_word(lastwd, ln, true)
+          end
+          if hyphcount > HYPHmax and RepeatedHyphens then
+             local COLOR = luatypo.colortbl[2]
+             local pg = show_pre_disc (pn,COLOR)
+             pageflag = true
+          end
+          if not nextnode and EOPHyphens then
+             lwhyphflag = true
+          end
+          if nextnode and ParLastHyphen then
+             local nnnode = nextnode.next
+             local nnnnode = nil
+             if nnnode and nnnode.next then
+                nnnnode = nnnode.next
+                if nnnnode and nnnnode.id == HLIST and
+                   nnnnode.subtype == 1 and nnnnode.glue_order == 2 then
+                   local COLOR = luatypo.colortbl[0]
+                   local pg = show_pre_disc (pn,COLOR)
+                   pageflag = true
+                end
+             end
+          end
+       else
+          hyphcount = 0
+          if LastWordMatch and pn then
+             lastwd = check_last_word(lastwd, pn, true)
+          end
+          if EOLShortWords then
+             while pn and pn.id ~= GLYPH and pn.id ~= HLIST do
+               pn = pn.prev
+             end
+             if pn and pn.id == GLYPH then
+                check_regexpr(pn,line)
+             end
+          end
+       end
+       if widowflag and Widows then
+          pageflag = true
+          widowflag = false
+          local COLOR  = luatypo.colortbl[4]
+          color_line (head, COLOR)
+       end
+       if orphanflag and Orphans then
+          pageflag = true
+          local COLOR = luatypo.colortbl[5]
+          color_line (head, COLOR)
+       end
+       if lwhyphflag and EOPHyphens then
+          pageflag = true
+          local COLOR = luatypo.colortbl[1]
+          local pg = show_pre_disc (pn,COLOR)
+       end
+    elseif head.id == GLUE and head.subtype == USRSKIP then
+       vpos = vpos + head.width
+       if not nextnode and ShortPages and
+          pageline > 1 and pageline < PAGEmin then
+          pageshort = true
+       end
+       if pageshort and vpos < vpos_min then
+          pageflag = true
+          local COLOR = luatypo.colortbl[9]
+          local n = head
+          repeat
+            n = n.prev
+          until n.id == HLIST and (n.subtype == LINE or n.subtype == EQN)
+          if n then
+             color_line(n, COLOR)
+          end
+       end
+    elseif head.id == GLUE then
+       vpos = vpos + head.width
+    end
+  head = nextnode
+  end
+  if pageflag then
+     local pl = luatypo.pagelist
+     local p = tonumber(string.match(pl, "%s(%d+),%s$"))
+     if not p or pageno > p then
+        luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", "
+     end
+  end
+  return true
+end
+return luatypo.check_page
+\end{luacode}
+\AtEndOfPackage{%
+  \directlua{
+    if not luatypo.None then
+       luatexbase.add_to_callback
+           ("pre_output_filter",luatypo.check_page,"check_page")
+    end
+  }
+}
+
+\InputIfFileExists{lua-typo.cfg}%
+   {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file loaded}}%
+   {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file not found.
+                               \MessageBreak Providing default values.}%
+    \definecolor{mygrey}{gray}{0.6}%
+    \definecolor{myred}{rgb}{1,0.55,0}
+    \luatypoSetColor0{red}%       Paragraph last full line hyphenated
+    \luatypoSetColor1{red}%       Page last word hyphenated
+    \luatypoSetColor2{red}%       Hyphens ending two many consecutive lines
+    \luatypoSetColor3{red}%       Short word at end of line
+    \luatypoSetColor4{cyan}%      Widow
+    \luatypoSetColor5{cyan}%      Orphan
+    \luatypoSetColor6{cyan}%      Paragraph ending on a short line
+    \luatypoSetColor7{blue}%      Overfull lines
+    \luatypoSetColor8{blue}%      Underfull lines
+    \luatypoSetColor9{red}%       Nearly empty page
+    \luatypoSetColor{10}{myred}%  First word matches
+    \luatypoSetColor{11}{myred}%  Last word matches
+    \luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+    \luatypoBackPI=1em\relax
+    \luatypoBackFuzz=2pt\relax
+    \luatypoLLminWD=2\parindent\relax
+    \luatypoStretchMax=200\relax
+    \luatypoHyphMax=2\relax
+    \luatypoPageMin=5\relax
+    \luatypoMinFull=4\relax
+    \luatypoMinPART=4\relax
+   }%
+%%
+%%
+%% End of file `lua-typo.sty'.


Property changes on: trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2021-04-18.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.cfg
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg	2021-06-03 20:50:11 UTC (rev 59456)
+++ trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg	2021-06-03 20:50:26 UTC (rev 59457)
@@ -6,7 +6,9 @@
 \luatypoBackFuzz=2pt\relax
 
 %% Minimum length of paragraphs’ last lines
-\luatypoLLminWD=2\parindent\relax
+\ifdim\parindent=0pt \luatypoLLminWD=20pt\relax
+\else \luatypoLLminWD=2\parindent\relax
+\fi
 
 %% Maximum number of consecutive hyphenated lines
 \luatypoHyphMax=2\relax
@@ -26,7 +28,7 @@
 \definecolor{myred}{rgb}{1,0.55,0}
 \luatypoSetColor0{red}      % Paragraph last full line hyphenated
 \luatypoSetColor1{red}      % Page last word hyphenated
-\luatypoSetColor2{red}      % Hyphens ending two many consecutive lines
+\luatypoSetColor2{red}      % Hyphens on to many consecutive lines
 \luatypoSetColor3{red}      % Short word at end of line
 \luatypoSetColor4{cyan}     % Widow
 \luatypoSetColor5{cyan}     % Orphan
@@ -37,10 +39,10 @@
 \luatypoSetColor{10}{myred} % First word matches
 \luatypoSetColor{11}{myred} % Last word matches
 \luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+\luatypoSetColor{13}{cyan}  % Footnote split
 
-%% Language specific settings (example for French only currently):
+%% Language specific settings (example for French):
 %% short words (two letters max) to be avoided at end of lines.
-%% French:
 %%\luatypoOneChar{french}{'À à Ô'}
 %%\luatypoTwoChars{french}{'Je Tu Il On'}
 %% 

Modified: trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty	2021-06-03 20:50:11 UTC (rev 59456)
+++ trunk/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty	2021-06-03 20:50:26 UTC (rev 59457)
@@ -6,9 +6,21 @@
 %%
 %% lua-typo.dtx  (with options: `sty')
 %% 
-\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+%% IMPORTANT NOTICE:
+%% For the copyright see the source file `lua-typo.dtx’.
+%%
 \ProvidesPackage{lua-typo}
-                [2021/03/14 v.0.32 Daniel Flipo]
+                [2021/05/13 v.0.50 Daniel Flipo]
+\ifdefined\DeclareRelease
+  \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
+  \DeclareCurrentRelease{}{2021-05-13}
+\else
+  \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide
+    access\MessageBreak to former versions of the lettrine package.%
+    \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated%
+    \MessageBreak 2020-01-01 or newer; reported}
+\fi
+\NeedsTeXFormat{LaTeX2e}[2021/06/01]
 \ifdefined\directlua
   \RequirePackage{luatexbase,luacode,luacolor}
   \RequirePackage{kvoptions,atveryend}
@@ -51,6 +63,7 @@
 \DeclareBoolOption[false]{EOLShortWords}
 \DeclareBoolOption[false]{FirstWordMatch}
 \DeclareBoolOption[false]{LastWordMatch}
+\DeclareBoolOption[false]{FootnoteSplit}
 \AddToKeyvalOption{luatypo}{All}{%
   \LT at ShortLinestrue     \LT at ShortPagestrue
   \LT at OverfullLinestrue  \LT at UnderfullLinestrue
@@ -58,7 +71,7 @@
   \LT at EOPHyphenstrue     \LT at RepeatedHyphenstrue
   \LT at ParLastHyphentrue  \LT at EOLShortWordstrue
   \LT at FirstWordMatchtrue \LT at LastWordMatchtrue
-  \LT at BackParindenttrue
+  \LT at BackParindenttrue  \LT at FootnoteSplittrue
 }
 \ProcessKeyvalOptions{luatypo}
 \AtEndOfPackage{%
@@ -145,6 +158,12 @@
   \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 ShowOptions
   \GenericWarning{* }{%
@@ -166,6 +185,7 @@
      EOLShortWords   [false]\MessageBreak
      FirstWordMatch  [false]\MessageBreak
      LastWordMatch   [false]\MessageBreak
+     FootnoteSplit   [false]\MessageBreak
      \MessageBreak
      *********************************************%
      \MessageBreak Lua-typo [ShowOptions]
@@ -207,6 +227,10 @@
     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]{%
@@ -254,14 +278,9 @@
 luatypo.double = { }
 luatypo.colortbl  = { }
 luatypo.pagelist  = ""
+luatypo.buffer    = "List of typographic flaws found for "
+                    .. tex.jobname .. ".tex:\string\n\string\n"
 
-local hyphcount = 0
-local parlines = 0
-local pagelines = 0
-local pageno = 0
-local prevno = 0
-local pageflag = false
-
 local char_to_discard = { }
 char_to_discard[string.byte(",")] = true
 char_to_discard[string.byte(".")] = true
@@ -284,10 +303,13 @@
 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
@@ -296,10 +318,16 @@
 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
@@ -328,22 +356,64 @@
      set_attribute(node,attr,color)
   end
 end
-local color_hbox = function (head, color)
-  local first = head.head
-  for n in traverse(first) do
-      color_node(n, color)
-  end
-end
 local color_line = function (head, color)
   local first = head.head
   for n in traverse(first) do
-      if n and n.id == HLIST and n.subtype == BOX then
-         color_hbox(n, color)
+      if n.id == HLIST or n.id == VLIST then
+         local ff = n.head
+         for nn in traverse(ff) do
+           if nn.id == HLIST or nn.id == VLIST then
+              local f3 = nn.head
+              for n3 in traverse(f3) do
+                if n3.id == HLIST or n3.id == VLIST then
+                   local f4 = n3.head
+                   for n4 in traverse(f4) do
+                     if n4.id == HLIST or n4.id == VLIST then
+                        local f5 = n4.head
+                        for n5 in traverse(f5) do
+                          if n5.id == HLIST or n5.id == VLIST then
+                             local f6 = n5.head
+                             for n6 in traverse(f6) do
+                               color_node(n6, color)
+                             end
+                          else
+                             color_node(n5, color)
+                          end
+                        end
+                     else
+                        color_node(n4, color)
+                     end
+                   end
+                else
+                   color_node(n3, color)
+                end
+              end
+           else
+              color_node(nn, color)
+           end
+         end
       else
          color_node(n, color)
       end
   end
 end
+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
@@ -397,17 +467,26 @@
   end
   return len, str
 end
-local check_last_word = function (old, node, color, flag)
+local check_last_word = function (old, node, line, flag)
+  local COLOR = luatypo.colortbl[11]
   local match = false
   local new = ""
   local maxlen = 0
   if flag and node then
      local swap = true
+     local box, go
      local lastn = node
-     while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC do
+     while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC and
+           lastn.id ~= HLIST do
        lastn = lastn.prev
      end
      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
@@ -414,6 +493,18 @@
      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)
@@ -452,33 +543,48 @@
              newsub = nsub
           end
         end
+        pageflag = true
         newsub = string.gsub(newsub, '^_', '')
+        local msg = "E.O.L. MATCH=" .. newsub
+        log_flaw(msg, line, colno, footnote)
         oldsub = string.reverse(newsub)
         local newsub = ""
         local n = lastn
         repeat
           if n and n.id ~= GLUE then
-             color_node(n, color)
+             color_node(n, COLOR)
              l, newsub = signature(n, newsub, swap)
-          elseif n then
+          elseif n and n.id == GLUE then
              newsub = newsub .. "_"
+          elseif not n and box then
+             n = box
+          else
+             break
           end
           n = n.prev
-        until not n or newsub == oldsub or l >= k
+        until newsub == oldsub or l >= k
      end
   end
-  return new, match
+  return new
 end
-local check_first_word = function (old, node, color, flag)
+local check_first_word = function (old, node, line, flag)
+  local COLOR = luatypo.colortbl[10]
   local match = false
   local swap = false
   local new = ""
   local maxlen = 0
-  local start = node
-  local n = start
-  while n and n.id ~= GLYPH and n.id ~= DISC do
+  local n = node
+  local box, go
+  while n and n.id ~= GLYPH and n.id ~= DISC and
+        (n.id ~= HLIST or n.subtype == INDENT) do
      n = n.next
   end
+  local start = n
+  if n and n.id == HLIST then
+     box = n
+     start = n.head
+     n = n.head
+  end
   while n and n.id ~= GLUE do
     maxlen, new = signature (n, new, swap)
     n = n.next
@@ -485,6 +591,18 @@
   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)
@@ -525,7 +643,10 @@
              newsub = nsub
           end
         end
+        pageflag = true
         newsub = string.gsub(newsub, '_$', '')   --$
+        local msg = "B.O.L. MATCH=" .. newsub
+        log_flaw(msg, line, colno, footnote)
         oldsub = newsub
         local newsub = ""
         local k = string.len(oldsub)
@@ -532,18 +653,23 @@
         local n = start
         repeat
           if n and n.id ~= GLUE then
-             color_node(n, color)
+             color_node(n, COLOR)
              l, newsub = signature(n, newsub, swap)
-          elseif n then
+          elseif n and n.id == GLUE then
              newsub = newsub .. "_"
+          elseif not n and box then
+             n = box
+          else
+             break
           end
           n = n.next
-        until not n or newsub == oldsub or l >= k
+        until newsub == oldsub or l >= k
      end
   end
-  return new, match
+  return new
 end
-local check_regexpr = function (glyph)
+local check_regexpr = function (glyph, line)
+  local pageno = tex.getcount("c at page")
   local COLOR = luatypo.colortbl[3]
   local lang = glyph.lang
   local match = false
@@ -553,6 +679,9 @@
      if lchar and lchar < 0x100 and previous and previous.id == GLUE then
         match = string.find(luatypo.single[lang], string.char(lchar))
         if match then
+           pageflag = true
+           local msg = "RGX MATCH=" .. string.char(lchar)
+           log_flaw(msg, line, colno, footnote)
            color_node(glyph,COLOR)
         end
      end
@@ -565,6 +694,9 @@
            local pattern = string.char(pchar) .. string.char(lchar)
            match = string.find(luatypo.double[lang], pattern)
            if match then
+              pageflag = true
+              local msg = "RGX MATCH=" .. pattern
+              log_flaw(msg, line, colno, footnote)
               color_node(previous,COLOR)
               color_node(glyph,COLOR)
            end
@@ -574,10 +706,14 @@
         if pprev and pprev.id == GLYPH then
            local pchar, id = is_glyph(pprev)
            local ppprev = pprev.prev
-           if pchar and pchar < 0x100 and ppprev and ppprev.id == GLUE then
+           if pchar and pchar < 0x100 and
+              ppprev and ppprev.id == GLUE then
               local pattern = string.char(pchar) .. string.char(lchar)
               match = string.find(luatypo.double[lang], pattern)
               if match then
+                 pageflag = true
+                 local msg = "RGX MATCH=" .. pattern
+                 log_flaw(msg, line, colno, footnote)
                  color_node(pprev,COLOR)
                  color_node(glyph,COLOR)
               end
@@ -585,7 +721,6 @@
         end
      end
   end
-  return  match
 end
 local show_pre_disc = function (disc, color)
   local n = disc
@@ -594,112 +729,189 @@
     n = n.prev
   end
   return n
+  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
-luatypo.check_page = function (head)
+local footnoterule_ahead = function (head, debug)
+  local n = head
+  local flag = false
+  if n and n.id == KERN and n.subtype == 1 then
+     local htr = n.kern
+     local ht1, ht2, ht3
+     n = n.next
+     if n and n.id == RULE and n.subtype == 0 then
+        htr = htr + n.height
+        n = n.next
+        if n and n.id == KERN and n.subtype == 1 then
+           htr = htr + n.kern
+           if htr == 0 then
+              flag = true
+           end
+         end
+     end
+  end
+  return flag
+end
+check_vtop = function (head, colno)
   local PAGEmin   = luatypo.PAGEmin
   local HYPHmax   = luatypo.HYPHmax
   local LLminWD   = luatypo.LLminWD
   local BackPI    = luatypo.BackPI
   local BackFuzz  = luatypo.BackFuzz
-  local BackParindent  = luatypo.BackParindent
-  local ShortLines     = luatypo.ShortLines
-  local ShortPages     = luatypo.ShortPages
-  local OverfullLines  = luatypo.OverfullLines
-  local UnderfullLines = luatypo.UnderfullLines
-  local Widows         = luatypo.Widows
-  local Orphans        = luatypo.Orphans
-  local EOPHyphens     = luatypo.EOPHyphens
+  local BackParindent   = luatypo.BackParindent
+  local ShortLines      = luatypo.ShortLines
+  local ShortPages      = luatypo.ShortPages
+  local OverfullLines   = luatypo.OverfullLines
+  local UnderfullLines  = luatypo.UnderfullLines
+  local Widows          = luatypo.Widows
+  local Orphans         = luatypo.Orphans
+  local EOPHyphens      = luatypo.EOPHyphens
   local RepeatedHyphens = luatypo.RepeatedHyphens
-  local FirstWordMatch = luatypo.FirstWordMatch
-  local ParLastHyphen  = luatypo.ParLastHyphen
-  local EOLShortWords  = luatypo.EOLShortWords
-  local LastWordMatch  = luatypo.LastWordMatch
-  local Stretch = math.max(luatypo.Stretch/100,1)
-
+  local FirstWordMatch  = luatypo.FirstWordMatch
+  local ParLastHyphen   = luatypo.ParLastHyphen
+  local EOLShortWords   = luatypo.EOLShortWords
+  local LastWordMatch   = luatypo.LastWordMatch
+  local FootnoteSplit   = luatypo.FootnoteSplit
+  local Stretch  = math.max(luatypo.Stretch/100,1)
+  local blskip   = tex.getglue("baselineskip")
+  local pageno   = tex.getcount("c at page")
+  local vpos_min = PAGEmin * blskip
+  vpos_min = vpos_min * 1.5
+  local vpos = 0
+  local pageflag = false
+  local body_bottom = false
+  local page_bottom = false
+  local first_bot = true
+  local footnote = false
+  local ftnsplit = false
   local orphanflag = false
   local widowflag  = false
   local lwhyphflag = false
-  local match1 = false
-  local match2 = false
+  local pageshort  = false
   local firstwd = ""
   local lastwd = ""
+  local hyphcount = 0
+  local pageline = 0
+  local ftnline = 0
+  local line = 0
   while head do
     local nextnode = head.next
-    local prevnode = head.prev
-    local pprevnode = nil
-    if prevnode then
-       pprevnode = prevnode.prev
-    end
-    if head.id == GLUE and head.subtype == TOPSKIP then
-       pageno = tex.getcount("c at page")
-       hyphcount = 0
-       if pageno > prevno then
-          pageflag = false
-          pagelines = 0
-          match1 = false
+    if not footnote and head.id == KERN and head.subtype == 1  then
+       if footnoterule_ahead(head, true) then
+          footnote = true
+          ftnline = 0
+          body_bottom = false
+          orphanflag = false
+          lwhyphflag = false
+          hyphcount = 0
           firstwd = ""
           lastwd = ""
-          prevno = pageno
+       else
+          vpos = vpos + head.kern
        end
-    elseif head.id == HLIST and head.subtype == LINE then
-       local first = head.head
-       if first.id == MKERN or
-         (first.id == GLUE and first.subtype == LFTSKIP) then
-          first = first.next
-       end
-       pagelines = pagelines + 1
-       local ListItem = false
-       local textline = false
-       if first.id == GLYPH then
-          textline = true
+    elseif head.id == HLIST and head.subtype == LINE and
+          (head.height > 0 or head.depth > 0) then
+       if footnote then
+          ftnline = ftnline + 1
+          line = ftnline
        else
-          local n = first
-          repeat
-            n = n.next
-            if n and n.id == GLYPH then
-               textline = true
-               break
-            end
-          until not n or (n.id == GLUE and n.subtype == RGTSKIP)
+          pageline = pageline + 1
+          line = pageline
        end
-       if head.glue_set == 1 and head.glue_sign == 2 and
-          head.glue_order == 0 and OverfullLines then
+       local first = head.head
+       local hmax = head.width + tex.hfuzz
+       local w,h,d = dimensions(1,2,0, first)
+       if w > hmax and OverfullLines then
           pageflag = true
+          local wpt = string.format("%.2fpt", (w-head.width)/65536)
+          local msg = "OVERFULL line " .. wpt
+          log_flaw(msg, line, colno, footnote)
           local COLOR = luatypo.colortbl[7]
           color_line (head, COLOR)
-       elseif head.glue_set >= Stretch and head.glue_sign == 1 and
-              head.glue_order == 0  and UnderfullLines then
+       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]
-          pageflag = true
           color_line (head, COLOR)
        end
+       vpos = vpos + head.height + head.depth
+       local n = head.next
+       while n and
+            (n.id == GLUE or n.id == PENALTY or n.id == WHATSIT) do
+         n = n.next
+       end
+       if not n then
+          if footnote then
+             page_bottom = true
+          else
+             page_bottom = true
+             body_bottom = true
+          end
+       elseif footnoterule_ahead(n, false) then
+          body_bottom = true
+       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
+       local ListItem = false
        if first.id == LPAR then
           hyphcount = 0
-          parlines = 1
-          if not nextnode then
+          if not footnote then
+             parline = 1
+          end
+          if body_bottom then
              orphanflag = true
           end
           local nn = first.next
-          if  nn and  nn.id == HLIST and nn.subtype == BOX then
-              ListItem = true
+          if nn and nn.id == HLIST and nn.subtype == BOX then
+             ListItem = true
           end
-       else
-          parlines = parlines + 1
+       elseif not footnote then
+          parline = parline + 1
        end
        if FirstWordMatch then
           local flag = not ListItem
-          local COLOR = luatypo.colortbl[10]
-          firstwd, match1 = check_first_word(firstwd, first, COLOR, flag)
-          if match1 then
-             pageflag = true
-          end
+          firstwd = check_first_word(firstwd, first, line, flag)
        end
        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 pagelines == 1 and parlines > 1 then
+          if pageline == 1 and parline > 1 then
              widowflag = true
           end
           local PFskip = effective_glue(pn,head)
@@ -707,7 +919,10 @@
              local llwd = tex.hsize - PFskip
              if llwd < LLminWD then
                 pageflag = true
-                local COLOR  = luatypo.colortbl[6]
+                local msg = "SHORT LINE: " ..
+                            string.format("%.0fpt", llwd/65536)
+                log_flaw(msg, line, colno, footnote)
+                local COLOR = luatypo.colortbl[6]
                 local attr = oberdiek.luacolor.getattribute()
                 color_line (head, COLOR)
              end
@@ -714,113 +929,195 @@
           end
           if BackParindent and PFskip < BackPI and PFskip > BackFuzz then
              pageflag = true
-             local COLOR  = luatypo.colortbl[12]
+             local msg = "LINE NEARLY FULL: missing " ..
+                         string.format("%.1fpt", PFskip/65536)
+             log_flaw(msg, line, colno, footnote)
+             local COLOR = luatypo.colortbl[12]
              local attr = oberdiek.luacolor.getattribute()
              color_line (head, COLOR)
           end
           if LastWordMatch then
-             local COLOR = luatypo.colortbl[11]
              local flag = textline
              if PFskip > BackPI then
                 flag = false
              end
-             lastwd, match1 = check_last_word(lastwd, pn, COLOR, flag)
-             if match1 then
-                pageflag = true
-             end
+             lastwd = check_last_word(lastwd, pn, line, flag)
           end
        elseif pn and pn.id == DISC then
           hyphcount = hyphcount + 1
           if LastWordMatch then
-             local COLOR = luatypo.colortbl[11]
-             lastwd, match1 = check_last_word(lastwd, ln, COLOR, true)
-             if match1 then
-                pageflag = true
-             end
+             lastwd = check_last_word(lastwd, ln, line, true)
           end
           if hyphcount > HYPHmax and RepeatedHyphens then
              local COLOR = luatypo.colortbl[2]
              local pg = show_pre_disc (pn,COLOR)
              pageflag = true
+             local msg = "REPEATED HYPHENS: more than " .. HYPHmax
+             log_flaw(msg, line, colno, footnote)
           end
-          if not nextnode and EOPHyphens then
+          if (page_bottom or body_bottom) and EOPHyphens then
              lwhyphflag = true
           end
           if nextnode and ParLastHyphen then
-             local nnnode = nextnode.next
-             local nnnnode = nil
-             if nnnode and nnnode.next then
-                nnnnode = nnnode.next
-                if nnnnode and nnnnode.id == HLIST and
-                   nnnnode.subtype == 1 and nnnnode.glue_order == 2 then
+             local nn = nextnode.next
+             local nnn = nil
+             if nn and nn.next then
+                nnn = nn.next
+                if nnn.id == HLIST and nnn.subtype == LINE and
+                   nnn.glue_order == 2 then
+                   pageflag = true
+                   local msg = "HYPHEN on next to last line"
+                   log_flaw(msg, line, colno, footnote)
                    local COLOR = luatypo.colortbl[0]
                    local pg = show_pre_disc (pn,COLOR)
-                   pageflag = true
                 end
              end
           end
-       elseif pn and pn.id == GLYPH then
+       else
           hyphcount = 0
-          if LastWordMatch then
-             local COLOR = luatypo.colortbl[11]
-             lastwd, match1 = check_last_word(lastwd, pn, COLOR, true)
+          if LastWordMatch and pn then
+             lastwd = check_last_word(lastwd, pn, line, true)
           end
           if EOLShortWords then
-             match2 = check_regexpr(pn)
-          end
-          if match1 or match2 then
-             pageflag = true
-          end
-       elseif pn and pn.id == MKERN then
-          hyphcount = 0
-          local ppn = pn.prev
-          if ppn and ppn.id == GLYPH then
-             if LastWordMatch then
-                local COLOR = luatypo.colortbl[11]
-                lastwd, match1 = check_last_word(lastwd, pn, COLOR, true)
+             while pn and pn.id ~= GLYPH and pn.id ~= HLIST do
+               pn = pn.prev
              end
-             if EOLShortWords then
-                match2 = check_regexpr(ppn)
+             if pn and pn.id == GLYPH then
+                check_regexpr(pn,line)
              end
-             if match1 or match2 then
-                pageflag = true
-             end
           end
-       else
-          hyphcount = 0
        end
        if widowflag and Widows then
           pageflag = true
           widowflag = false
+          local msg = "WIDOW"
+          log_flaw(msg, line, colno, footnote)
           local COLOR  = luatypo.colortbl[4]
           color_line (head, COLOR)
        end
        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 lwhyphflag 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
-    elseif not nextnode and head.id == GLUE and
-                            head.subtype == USRSKIP then
-       if pagelines > 1 and pagelines < PAGEmin and ShortPages then
-          pageflag = true
-          local COLOR = luatypo.colortbl[9]
-          local n = head
-          while n and (n.id ~= HLIST or n.subtype ~= LINE) do
-             n = n.prev
+    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 n then
-             color_line(n, COLOR)
+          if fl then
+             pageflag = true
+             local w = wd - hmax + tex.hfuzz
+             local wpt = string.format("%.2fpt", w/65536)
+             local msg = "OVERFULL equation " .. wpt
+             log_flaw(msg, line, colno, footnote)
+             local COLOR = luatypo.colortbl[7]
+             color_line (head, COLOR)
           end
        end
+       local n = head.next
+       while n and (n.id == GLUE    or n.id == PENALTY or
+                    n.id == WHATSIT or n.id == VLIST)     do
+         n = n.next
+       end
+       if not n then
+          page_bottom = true
+          body_bottom = true
+       elseif footnoterule_ahead(n, false) then
+          body_bottom = true
+       end
+    elseif head.id == VLIST then
+       vpos = vpos + head.height + head.depth
+    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)
     end
   head = nextnode
   end
+  return pageflag
+end
+luatypo.check_page = function (head)
+  local pageno = tex.getcount("c at page")
+  local pageflag = false
+  local n2, n3, col, colno
+  local body = get_pagebody(head)
+  local first = body.list
+  if first.id == HLIST and first.subtype == 2 then
+     n2 = first.list
+     colno = 0
+     for n in traverse_id(HLIST, n2) do
+         if n.id == HLIST and n.subtype == 2 then
+            n3 = n.list
+            col = n3.list
+            colno = colno + 1
+            pageflag = check_vtop(col,colno)
+         end
+     end
+  elseif body.id == VLIST and body.subtype == 0 then
+     col = body.list
+     pageflag = check_vtop(col,colno)
+  end
   if pageflag then
      local pl = luatypo.pagelist
      local p = tonumber(string.match(pl, "%s(%d+),%s$"))
@@ -836,7 +1133,7 @@
   \directlua{
     if not luatypo.None then
        luatexbase.add_to_callback
-           ("pre_output_filter",luatypo.check_page,"check_page")
+           ("pre_shipout_filter",luatypo.check_page,"check_page",1)
     end
   }
 }
@@ -849,7 +1146,7 @@
     \definecolor{myred}{rgb}{1,0.55,0}
     \luatypoSetColor0{red}%       Paragraph last full line hyphenated
     \luatypoSetColor1{red}%       Page last word hyphenated
-    \luatypoSetColor2{red}%       Hyphens ending two many consecutive lines
+    \luatypoSetColor2{red}%       Hyphens on to many consecutive lines
     \luatypoSetColor3{red}%       Short word at end of line
     \luatypoSetColor4{cyan}%      Widow
     \luatypoSetColor5{cyan}%      Orphan
@@ -860,9 +1157,11 @@
     \luatypoSetColor{10}{myred}%  First word matches
     \luatypoSetColor{11}{myred}%  Last word matches
     \luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+    \luatypoSetColor{13}{cyan}%   Footnote split
     \luatypoBackPI=1em\relax
     \luatypoBackFuzz=2pt\relax
-    \luatypoLLminWD=2\parindent\relax
+    \ifdim\parindent=0pt \luatypoLLminWD=20pt\relax
+    \else\luatypoLLminWD=2\parindent\relax\fi
     \luatypoStretchMax=200\relax
     \luatypoHyphMax=2\relax
     \luatypoPageMin=5\relax



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