From 7df76069beaebbde72a91ad3fd9102eee6857877 Mon Sep 17 00:00:00 2001
From: Karl Berry <karl@freefriends.org>
Date: Thu, 4 Mar 2021 22:09:16 +0000
Subject: lua-typo (4mar21)

git-svn-id: svn://tug.org/texlive/trunk@58124 c570f23f-e606-0410-a88d-b1316a301751
---
 .../source/lualatex/lua-typo/lua-typo.dtx          | 1942 ++++++++++++++++++++
 1 file changed, 1942 insertions(+)
 create mode 100644 Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx

(limited to 'Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx')

diff --git a/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx b/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx
new file mode 100644
index 00000000000..d1a4b0068a8
--- /dev/null
+++ b/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx
@@ -0,0 +1,1942 @@
+% \CheckSum{337}
+%
+% \iffalse meta-comment
+%
+% Copyright © 2020-2021 Daniel Flipo.
+%
+% This program can be distributed and/or modified under the terms
+% of the LaTeX Project Public License either version 1.3c of this
+% license or (at your option) any later version.
+% The latest version of this license is in
+%    http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This file has the LPPL maintenance status "maintained".
+%
+%    Lua-Typo package for LaTeX version 2e
+%
+%    Copyright © 2020-2021 by Daniel Flipo
+%
+%    Please report errors to: daniel (dot) flipo (at) free (dot) fr
+%
+%<*batch>
+%<*gobble>
+\ifx\jobname\relax\let\documentclass\undefined\fi
+\ifx\documentclass\undefined
+\csname fi\endcsname
+%</gobble>
+\input docstrip.tex
+\keepsilent
+\let\MetaPrefix\relax
+\preamble
+\endpreamble
+\postamble
+\endpostamble
+\let\MetaPrefix\DoubleperCent
+\generate{%
+   \file{lua-typo.sty}{\from{lua-typo.dtx}{sty}}% {sty,msg,dbg}
+   \nopreamble
+   \file{lua-typo.cfg}{\from{lua-typo.dtx}{cfg}}%
+   \file{lua-typo-fr.ltx}{\from{lua-typo.dtx}{driver,docfr}}%
+   \file{lua-typo.ltx}{\from{lua-typo.dtx}{driver,doc}}%
+}
+\endbatchfile
+%</batch>
+%<*gobble>
+\fi
+\expandafter\ifx\csname @currname\endcsname\empty
+\csname fi\endcsname
+%</gobble>
+%<*driver>
+\documentclass[a4paper]{ltxdoc}
+\usepackage[dvipsnames]{xcolor}
+\usepackage{fontspec}
+\setmainfont{erewhon}
+\setsansfont{Cabin}[Scale=MatchLowercase]
+\setmonofont{VeraMono.ttf}[
+  ItalicFont        = VeraMoIt.ttf,
+  BoldFont          = VeraMoBd.ttf,
+  BoldItalicFont    = VeraMoBI.ttf,
+  Scale = MatchLowercase,
+  HyphenChar=None,   Color=Sepia,
+  ]
+\usepackage[expansion=true, protrusion=true]{microtype}
+%<-docfr>\usepackage[british]{babel}
+%<-doc>\usepackage[french]{babel}
+%<-doc>\frenchsetup{og=«, fg=»}
+\usepackage[ShortPages, OverfullLines, UnderfullLines,
+            Widows, Orphans, EOPHyphens, RepeatedHyphens
+           ]{lua-typo}
+\luatypoLLminWD=3em
+\renewcommand*\descriptionlabel[1]{%
+   \hspace{\labelsep}\texttt{#1}}
+\usepackage{array,url,verbatim}
+\usepackage[numbered]{hypdoc}
+\hypersetup{colorlinks,urlcolor=blue,unicode}
+%
+%<-doc>\OnlyDescription
+%<-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>}
+%
+\newcommand*\file[1]{\texttt{#1}}
+\newcommand*\pkg[1]{\texttt{#1}}
+\newcommand*\opt[1]{\texttt{#1}}
+\renewcommand\meta[1]{\texttt{\textsl{\color{Sepia}<#1>}}}
+%
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{.3\baselineskip plus 0.3pt minus 0.3pt}
+\begin{document}
+\GetFileInfo{lua-typo.sty}
+\DocInput{lua-typo.dtx}
+\end{document}
+
+%%% Local Variables:
+%%% coding: utf-8
+%%% TeX-engine: luatex
+%%% End:
+%</driver>
+%<*gobble>
+\fi
+%</gobble>
+% \fi
+%
+% \begin{FrenchDoc}
+% \begin{center}
+%   \textbf{\Large Recherche d’imperfections typographiques\\[3pt]
+%           avec LuaLaTeX}
+%   \\[.5\baselineskip]^^A\]
+%   {\large Daniel Flipo}\\
+%   \texttt{daniel.flipo@free.fr}
+% \end{center}
+%
+% \section{De quoi s’agit-il ?}
+%
+%    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
+%    fichier \file{.log}, permettant un accès rapide aux pages
+%    potentiellement défectueuses.
+%
+%    Normalement, c’est-à-dire lorsque la justification n’est pas trop
+%    étroite, (Lua)TeX fait du bon travail, mais il peut rester des
+%    points à vérifier, notamment des lignes trop pleines ou lavées
+%    (\emph{Overfull, Underfull box}), des veuves et des orphelines,
+%    des mots coupés en fin de page ou d’alinéa ou sur plusieurs
+%    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.
+%    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.
+%
+%    \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
+%    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
+%    graves encore.
+%
+%    Je conseille de n’appliquer \pkg{lua-typo} que sur des textes
+%    « presque au point », d’améliorer ce qui peut l’être puis de
+%    \emph{supprimer} l’appel à \pkg{lua-typo} afin de ne pas risquer
+%    de mettre en lumière les imperfections que l’on aura renoncé
+%    à corriger. Pour appliquer toutes les vérifications prévues par
+%    \pkg{lua-typo}, il suffit d’ajouter dans le préambule la ligne\\
+%    |\usepackage[All]{lua-typo}|
+%
+%    Les fichiers \file{demo.tex} et \file{demo.pdf} fournissent un
+%    exemple du traitement opéré par \pkg{lua-typo}.
+%
+%    Un grand merci à Jacques André et Thomas Savary pour avoir accepté
+%    de tester les pré-versions et pour leurs retours riches et toujours
+%    pertinents ; leurs suggestions et leurs encouragements ont
+%    grandement contribué à améliorer la première version mise en ligne.
+%
+%  \section{Utilisation}
+%
+%    Comme indiqué plus haut la vérification la plus complète s’obtient
+%    par :\\
+%    |\usepackage[All]{lua-typo}|
+%
+%    Il est possible de choisir les tests à activer de deux
+%    manières, soit « tout sauf … » soit « seulement ceci et cela ».
+%    Pour tout activer sauf les options \meta{OptX} et \meta{OptY} :\\
+%    |\usepackage[All, |\meta{OptX}|=false, |\meta{OptY}|=false]{lua-typo}|\\
+%    ou pour se limiter aux tests \meta{OptX} et \meta{OptY} :\\
+%    |\usepackage[|\meta{OptX}|, |\meta{OptY}|, |\meta{OptZ}|]{lua-typo}|
+%
+%    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
+%      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 ?\\
+%      ShortPages      & Page quasi vide (quelques lines) ?\\
+%      OverfullLines   & Ligne trop pleine ?\\
+%      UnderfullLines  & Ligne lavée ? \\
+%      Widows          & Veuve (haut 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 ?\\
+%      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 ?\\
+%      \hline
+%    \end{tabular}\\[12pt]
+%    Par exemple, pour limiter les vérifications aux lignes trop pleines
+%    ou creuses, il suffit de coder :\\
+%    |\usepackage[OverfullLines, UnderfullLines]{lua-typo}|\\
+%    Pour tout vérifier sauf les coupures répétées en fin de ligne on
+%    codera :\\
+%    |\usepackage[All, RepeatedHyphens=false]{lua-typo}|\\
+%    Notez que l’option that \opt{All} doit être la première de la
+%    liste, les suivantes étant rétirées de la liste complète définie
+%    par~\opt{All}.
+%
+%    Le nom des différentes options n’étant pas facile à mémoriser, il
+%    est possible de les retrouver sans devoir consulter la
+%    documentation ; l’option \opt{ShowOptions} affiche la liste
+%    complète dans le fichier \file{.log} :
+%    |\usepackage[ShowOptions]{lua-typo}|
+%
+%    L’option \opt{None}, empêche toute vérification :
+%    |\usepackage[None]{lua-typo}|
+%    a pour effet de supprimer complètement tout ajout de code LuaTeX
+%    (aucune fonction n’est ajoutée aux \emph{callbacks} de LuaTeX).
+%    Cette option peut-être utile lors de la toute dernière compilation,
+%    elle n’est pas tout-à-fait équivalente à la mise en commentaire de la
+%    ligne car les variables utilisées par \pkg{luatypo} restent
+%    définies ; si certaines ont été modifiées dans le préambule aucun
+%    message d’erreur du type ``\emph{Undefined Control Sequence}’’ ne
+%    sera émis à leur sujet.
+%
+%    Terminons par quelques précisions sur ces options.
+%    \begin{description}
+%    \item[FirstWordMatch :] les répétitions en début de ligne dans les
+%      listes ne sont pas signalées. Ceci est voulu car elles résultent
+%      d’un choix délibéré de l’auteur.
+%      \item[LastWordMatch:] le dernier mot d’un alinéa qui se termine
+%        à plus de |1em| (en fait |\luatypoBackPI|) de la marge droite
+%        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.
+%    \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é
+%      (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.
+%
+%    \section{Paramétrage personnalisé}
+%
+%    Pour certaines vérifications faites par \pkg{lua-typo} un
+%    paramétrage est nécessaire : à partir de quelle limite une dernière
+%    ligne d’alinéa est-elle considérée comme trop courte ?
+%    Combien de césures consécutives en bout de ligne sont-elles
+%    acceptables ?  Ces réglages dépendent évidemment du contexte, un
+%    correcteur de romans aura des exigences  plus strictes qu’un auteur
+%    de documentation technique par exemple…
+%
+%    \pkg{lua-typo} permet de modifier le réglage des curseurs soit dans
+%    le fichier \file{lua-typo.cfg} soit dans le préambule après l’appel
+%    de \pkg{lua-typo} ; les réglages placés dans le préambule prévalent
+%    sur ceux du fichier \file{lua-typo.cfg} qui eux-mêmes prévalent sur
+%    les réglages internes de l’extension.
+%
+%    Le fichier \file{lua-typo.cfg} fourni avec la distribution reprend
+%    exactement les réglages internes, il se trouve normalement dans le
+%    répertoire \textsc{texmfdist} des distributions TeXLive, MikTeX, etc.
+%    L’utilisateur a la possibilité de recopier ce fichier soit dans son
+%    répertoire de travail pour un document particulier, soit dans son
+%    répertoire \textsc{texmfhome} ou \textsc{texmflocal} et de le
+%    personnaliser comme il l’entend.
+%
+%    Voici la liste complète des paramètres personnalisables avec leur
+%    valeur par défaut, leurs noms sont systématiquement préfixés par
+%    |luatypo| afin d’éviter de possible conflits avec d’autres
+%    extensions.
+%    \begin{description}
+%      \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[ShortPages :] |\luatypoPageMin=5| fixe le nombre minimal de
+%        lignes d’une page pour que celle-ci ne pas être
+%        signalée comme trop courte.
+%
+%      \item[RepeatedHyphens :] |\luatypoHyphMax=2| fixe le nombre maximal
+%        acceptable de lignes consécutives terminées par une césure.
+%
+%      \item[UnderfullLines :] |\luatypoStretchMax=200| fixe le
+%        pourcentage maximal acceptable pour l’étirement des
+%        espaces-mots, au-delà la ligne est déclarée lavée.
+%        La valeur donnée doit être un entier supérieur ou égal à~100,
+%        cette valeur 100 correspond à l’étirement maximal prévu par la
+%        fonte (|\fontdimen3|) ; avec ce réglage attendez-vous à trouver
+%        une kyrielle de lignes les creuses ! En fait la valeur par défaut
+%        (200) correspond approximativement à ce que TeX, avec les
+%        réglages par défaut (|\tolerance=200|, |\hbadness=1000|),
+%        considère comme \emph{Underfull hbox}.
+%
+%      \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
+%        déclenchant l’avertissement.
+%        Avec ce réglage (3 et 4), seront détectées deux lignes se
+%        terminant par « cible» et « invincible » (quatre lettres en
+%        commun), ainsi que la présence de « mon » en début ou fin de
+%        deux lignes consécutives (trois lettres en commun).
+%
+%      \item[EOLShortWords:] cette option signale la présence en fin de
+%        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}|'}|
+%
+%        Lorsque les listes correspondant à la langue du document sont
+%        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'}|
+%
+%        Deux contraintes sont à respecter lorsqu’on veut
+%        personnaliser ces listes :\\
+%        a) le premier argument (langue) \emph{doit être connu de}
+%        \pkg{babel}, aussi les commandes |\luatypoOneChar| et
+%        |\luatypoTwoChars|, si elles sont utilisées, doivent l’être
+%        \emph{après} le chargement de \pkg{babel}, une bonne habitude
+%        à prendre est donc de toujours charger \pkg{lua-typo}
+%        \emph{après} \pkg{babel} ;
+%        b) le second argument \emph{doit être une chaîne de
+%        caractères}, donc entourée de simples ou doubles
+%        \emph{quotes} \textsc{ascii} et composées de mots séparés
+%        par des espaces comme dans les exemples ci-dessus.
+%    \end{description}
+%
+%    À chacune des vérifications faites par \pkg{lua-typo} peut être
+%    attachée une couleur spécifique pour mettre en évidence les
+%    imperfections détectées.
+%    Actuellement, seulement cinq couleurs sont utilisées par défaut,
+%    voici leur définition dans \file{lua-typo.cfg} :
+%    \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
+% \luatypoSetColor4{cyan}     % Veuve
+% \luatypoSetColor5{cyan}     % Orpheline
+% \luatypoSetColor6{cyan}     % Dernière ligne d’alinéa trop courte
+% \luatypoSetColor7{mygrey}   % Ligne trop pleine
+% \luatypoSetColor8{mygrey}   % Ligne creuse
+% \luatypoSetColor9{red}      % Page presque vide (qq. lignes)
+% \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
+%    \end{verbatim}
+%    \pkg{lua-typo} charge l’extension graphique \pkg{color}.
+%    Seules les couleurs portant un nom (\emph{named colors}) peuvent
+%    être utilisées ; pour en définir de nouvelles, il faut donc soit
+%    utiliser la commande |\definecolor| de l’extension \pkg{color}
+%    (comme ci-dessus pour |mygrey| ou |myred|), soit charger l’extension
+%    \pkg{xcolor} package qui donne accès à une kyrielle de noms de
+%    couleurs.
+% \end{FrenchDoc}
+%
+% \StopEventually{}
+%
+% \begin{center}
+%   \textbf{\Large Highlighting Typographical Flaws with LuaLaTeX}
+%   \\[.5\baselineskip]^^A\]
+%   {\large Daniel Flipo}\\
+%   \texttt{daniel.flipo@free.fr}
+% \end{center}
+%
+% \section{What is it about?}
+%
+%    The file \file{\filename}\footnote{The file described in this
+%    section has version number \fileversion\ and was last revised on
+%    \filedate.}, is meant for careful writers and proofreaders who do
+%    not feel totally satisfied with LaTeX output, the most frequent
+%    issues being widows and orphans, hyphenated words split across two
+%    pages, consecutive lines ending with hyphens, paragraphs ending
+%    on too short or nearly full lines, homeoarchy, etc.
+%
+%    This package, which works with LuaLaTeX only,
+%    \emph{does not try to correct anything} but just highlights
+%    potential issues (the offending lines or end of lines are printed
+%    in colour) and provides at the end of the \file{.log} file a summary
+%    of pages to be checked and manually corrected if possible.
+%    My understanding is that automatic correction often introduces new
+%    issues (underflow/overfull lines) when fixing one of the flaws
+%    mentionned above, human correction providing much better results.
+%    For completeness, overfull and underfull lines are also coloured
+%    (in grey by default) and mentionned in the summary provided at the
+%    end of the \file{.log} file.
+%
+%    I suggest to add a call |\usepackage[All]{lua-typo}| to the
+%    preamble of a document which is ``nearly finished’’
+%    \emph{and to remove it} once all possible corrections have been
+%    made:  if some flaws remain, getting them printed in colour in
+%    the final document would be a shame!
+%
+%    See files \file{demo.tex} and \file{demo.pdf} for a short example
+%    (in French).
+%
+%    I am very grateful to Jacques André and Thomas Savary, who kindly
+%    tested my beta versions, providing much valuable feedback and
+%    suggesting many improvements for the first released version.
+%    Special thanks to both of them!
+%
+%  \section{Usage}
+%
+%    The easiest way to trigger all checks perfomed by \pkg{lua-typo}
+%    is:\\
+%    |\usepackage[All]{lua-typo}|
+%
+%    It is possible to enable or disable some checks through boolean
+%    options passed to \pkg{lua-typo};
+%    you may want to perform all checks except a few, then
+%    \pkg{lua-typo} should be loaded this way:\\
+%    |\usepackage[All, |\meta{OptX}|=false, |\meta{OptY}|=false]{lua-typo}|\\
+%    or to enable just a few checks, then do it this way:\\
+%    |\usepackage[|\meta{OptX}|, |\meta{OptY}|, |\meta{OptZ}|]{lua-typo}|
+%
+%    \pagebreak
+%    Here is the full list of possible checks (name and purpose):\\[12pt]
+%    \begin{tabular}{>{\ttfamily}ll}
+%      \multicolumn{1}{l}{Name}  & Glitch to highlight\\ \hline
+%      All             & Turns all options to \opt{true}\\
+%      BackParindent   & paragraph’s last line \emph{nearly} full?\\
+%      ShortLines      & paragraph’s last line too short?\\
+%      ShortPages      & nearly empty page (just a few lines)?\\
+%      OverfullLines   & overfull lines?\\
+%      UnderfullLines  & underfull lines?\\
+%      Widows          & widows (top of page)?\\
+%      Orphans         & orphans (bottom of page)\\
+%      EOPHyphens      & hyphenated word split across two pages?\\
+%      RepeatedHyphens & too many consecutive hyphens?\\
+%      ParLastHyphen   & paragraph’s last full line hyphenated?\\
+%      EOLShortWords   & short words (1 or 2 chars) at end of line?\\
+%      FirstWordMatch  & same (part of) word starting two consecutive lines?\\
+%      LastWordMatch   & same (part of) word ending two consecutive lines?\\
+%      \hline
+%    \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:\\
+%    |\usepackage[OverfullLines, UnderfullLines]{lua-typo}|\\
+%    If you want everything to be checked except paragraphs ending on
+%    a short line try:\\
+%    |\usepackage[All, ShortLines=false]{lua-typo}|\\
+%    please note that \opt{All} has to be the first one, as options are
+%    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.
+%
+%    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
+%    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.
+%
+%    Please be aware of the following features:
+%    \begin{description}
+%      \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’’
+%        from the right margin (\emph{i.e.} more than
+%        |\luatypoBackPI| --default=1em-- away) is never highlighted
+%        even if it matches the one on the previous line.
+%        Similarly, if it matches the one on the next line, the latter
+%        will not be highlighted either.
+%      \item[ShortPages:] if a page is considered too short, its last
+%        line only is  highlighted, not the whole page.
+%      \item[RepeatedHyphens:] ditto, when the number of consecutives
+%        hyphenated lines is too high, only the hyphenated words in
+%        excess (the last ones) are hightlighted.
+%    \end{description}
+%
+%    Finally, please note that the footnotes’ contents are not checked
+%    by \pkg{lua-typo}, I have currently no clue of how to do that,
+%    hints are welcome!
+%
+%    \section{Customisation}
+%
+%    Some of the checks mentionned above require tuning, for
+%    instance, when is a last paragraph’s length called too short?
+%    how many hyphens ending consecutive lines are acceptable?
+%    \pkg{lua-typo} provides user customisable parameters to set
+%    what is regarded as acceptable or not.
+%
+%    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
+%    this file into their working directory (or \textsc{texmfhome} or
+%    \textsc{texmflocal}) and tune the defaults according to their own
+%    taste.
+%
+%    It is also possible to provide defaults directly in the
+%    document’s preamble (this overwrites the corresponding settings
+%    done in the configuration file found on TeX’s search path: current
+%    directory, then \textsc{texmfhome}, \textsc{texmflocal} and
+%    finally \textsc{texmfdist}.
+%
+%    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
+%        touch the right margin (actually end at less than
+%        |\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[ShortPages:] |\luatypoPageMin=5| sets the minimum acceptable
+%        number of lines on a page (chapters’ last page for instance).
+%
+%      \item[RepeatedHyphens:] |\luatypoHyphMax=2| sets the maximum
+%        acceptable number of consecutive hyphenated lines.
+%
+%      \item[UnderfullLines:] |\luatypoStretchMax=200| sets the maximum
+%        acceptable percentage of stretch acceptable before a line is
+%        tagged by \pkg{lua-typo} as underfull; it must be an integer
+%        over 100, 100 means that the slightest stretch exceeding the
+%        font tolerance (|\fontdimen3|) will be warned about (be
+%        prepared for a lot of ``underfull lines’’ with this setting),
+%        the default value 200 is just below what triggers TeX’s
+%        ``Underfull hbox’’ message (when |\tolerance=200| and
+%        |\hbadness=1000|).
+%
+%      \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
+%        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
+%        ``erfull’’ will be highlighted.
+%
+%      \item[EOLShortWords:] this check deals with lines ending with
+%        very short words (one or two characters), not all of them but
+%        a user selected list depending on the current language.\\
+%        |\luatypoOneChar{|\meta{language}|}{'|\meta{list of words}|'}|\\
+%        |\luatypoTwoChars{|\meta{language}|}{'|\meta{list of words}|'}|
+%
+%        Currently, defaults (commented out) are suggested for the French
+%        language only:\\
+%        |\luatypoOneChar{french}{'À à Ô'}|\\
+%        |\luatypoTwoChars{french}{'Je Tu Il On'}|
+%
+%        Feel free to customise these lists for French or to add your
+%        own shorts words for other languages but remember that
+%        a) the first argument (language name) \emph{must be known by}
+%        \pkg{babel}, so if you add |\luatypoOneChar| or
+%        |\luatypoTwoChars| commands, please make sure that
+%        \pkg{lua-typo} is loaded \emph{after} \pkg{babel};
+%        b) the second argument \emph{must be a string}
+%        (\emph{i.e.} surrounded by single or double \textsc{ascii}
+%        quotes) made of your words separated by spaces.
+%
+%    \end{description}
+%
+%    It is possible to define a specific colour for each
+%    typographic flaws that \pkg{lua-typo} deals with.
+%    Currently, only five colours are used in \file{lua-typo.cfg}:
+%    \begin{verbatim}
+% \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 on 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{mygrey}   % Overfull lines
+% \luatypoSetColor8{mygrey}   % Underfull lines
+% \luatypoSetColor9{red}      % Nearly empty page (a few lines)
+% \luatypoSetColor{10}{myred} % First word matches
+% \luatypoSetColor{11}{myred} % Last word matches
+% \luatypoSetColor{12}{mygrey}% paragraph’s last line nearly full
+%    \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
+%    \pkg{xcolor} package which provides a bunch of named colours.
+%
+% \clearpage
+%    \section{\TeX{}nical details}
+%
+% \iffalse
+%<*sty>
+\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+\ProvidesPackage{lua-typo}
+%</sty>
+%<*dtx>
+\ProvidesFile{lua-typo.dtx}
+%</dtx>
+%<*dtx|sty>
+                [2021/03/03 v.0.30 Daniel Flipo]
+%</dtx|sty>
+%<*sty>
+% \fi
+%
+%    This package only runs with LuaLaTeX and requires packages
+%    \pkg{luatexbase}, \pkg{luacode}, \pkg{luacolor} and
+%    \pkg{atveryend}.
+%
+%    \begin{macrocode}
+\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
+%    \end{macrocode}
+%
+%    Let’s define the necessary internal counters, dimens, token
+%    registers and commands…
+%
+%    \begin{macrocode}
+\newdimen\luatypoLLminWD
+\newdimen\luatypoBackPI
+\newdimen\luatypoBackFuzz
+\newcount\luatypoStretchMax
+\newcount\luatypoHyphMax
+\newcount\luatypoPageMin
+\newcount\luatypoMinFull
+\newcount\luatypoMinPart
+\newcount\luatypo@LANGno
+\newcount\luatypo@options
+\newtoks\luatypo@single
+\newtoks\luatypo@double
+%    \end{macrocode}
+%    … and define a global table for this package.
+%    \begin{macrocode}
+\begin{luacode}
+luatypo = { }
+\end{luacode}
+%    \end{macrocode}
+%
+%    Set up \pkg{kvoptions} initializations.
+%
+%    \begin{macrocode}
+\SetupKeyvalOptions{
+   family=luatypo,
+   prefix=LT@,
+}
+\DeclareBoolOption[false]{ShowOptions}
+\DeclareBoolOption[false]{None}
+\DeclareBoolOption[false]{All}
+\DeclareBoolOption[false]{BackParindent}
+\DeclareBoolOption[false]{ShortLines}
+\DeclareBoolOption[false]{ShortPages}
+\DeclareBoolOption[false]{OverfullLines}
+\DeclareBoolOption[false]{UnderfullLines}
+\DeclareBoolOption[false]{Widows}
+\DeclareBoolOption[false]{Orphans}
+\DeclareBoolOption[false]{EOPHyphens}
+\DeclareBoolOption[false]{RepeatedHyphens}
+\DeclareBoolOption[false]{ParLastHyphen}
+\DeclareBoolOption[false]{EOLShortWords}
+\DeclareBoolOption[false]{FirstWordMatch}
+\DeclareBoolOption[false]{LastWordMatch}
+%    \end{macrocode}
+%    Option \opt{All} resets all booleans relative to specific
+%    typographic checks to \opt{true}.
+%    \begin{macrocode}
+\AddToKeyvalOption{luatypo}{All}{%
+  \LT@ShortLinestrue     \LT@ShortPagestrue
+  \LT@OverfullLinestrue  \LT@UnderfullLinestrue
+  \LT@Widowstrue         \LT@Orphanstrue
+  \LT@EOPHyphenstrue     \LT@RepeatedHyphenstrue
+  \LT@ParLastHyphentrue  \LT@EOLShortWordstrue
+  \LT@FirstWordMatchtrue \LT@LastWordMatchtrue
+  \LT@BackParindenttrue
+}
+\ProcessKeyvalOptions{luatypo}
+%    \end{macrocode}
+%
+%     Forward these options to the |luatypo| global table.
+%     Wait until the config file \file{lua-typo.cfg} has been read
+%     in order to give it a chance of overruling the boolean options.
+%     This enables the user to permanently change the defaults.
+%
+%    \begin{macrocode}
+\AtEndOfPackage{%
+  \ifLT@None
+    \directlua{ luatypo.None = true }%
+  \else
+    \directlua{ luatypo.None = false }%
+  \fi
+  \ifLT@BackParindent
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.BackParindent = true }%
+  \else
+    \directlua{ luatypo.BackParindent = false }%
+  \fi
+  \ifLT@ShortLines
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.ShortLines = true }%
+  \else
+    \directlua{ luatypo.ShortLines = false }%
+  \fi
+  \ifLT@ShortPages
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.ShortPages = true }%
+  \else
+    \directlua{ luatypo.ShortPages = false }%
+  \fi
+  \ifLT@OverfullLines
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.OverfullLines = true }%
+  \else
+    \directlua{ luatypo.OverfullLines = false }%
+  \fi
+  \ifLT@UnderfullLines
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.UnderfullLines = true }%
+  \else
+    \directlua{ luatypo.UnderfullLines = false }%
+  \fi
+  \ifLT@Widows
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.Widows = true }%
+  \else
+    \directlua{ luatypo.Widows = false }%
+  \fi
+  \ifLT@Orphans
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.Orphans = true }%
+  \else
+    \directlua{ luatypo.Orphans = false }%
+  \fi
+  \ifLT@EOPHyphens
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.EOPHyphens = true }%
+  \else
+    \directlua{ luatypo.EOPHyphens = false }%
+  \fi
+  \ifLT@RepeatedHyphens
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.RepeatedHyphens = true }%
+  \else
+    \directlua{ luatypo.RepeatedHyphens = false }%
+  \fi
+  \ifLT@ParLastHyphen
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.ParLastHyphen = true }%
+  \else
+    \directlua{ luatypo.ParLastHyphen = false }%
+  \fi
+  \ifLT@EOLShortWords
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.EOLShortWords = true }%
+  \else
+    \directlua{ luatypo.EOLShortWords = false }%
+  \fi
+  \ifLT@FirstWordMatch
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.FirstWordMatch = true }%
+  \else
+    \directlua{ luatypo.FirstWordMatch = false }%
+  \fi
+  \ifLT@LastWordMatch
+    \advance\luatypo@options by 1
+    \directlua{ luatypo.LastWordMatch = true }%
+  \else
+    \directlua{ luatypo.LastWordMatch = false }%
+  \fi
+}
+%    \end{macrocode}
+%
+%    |ShowOptions| is specific:
+%
+%    \begin{macrocode}
+\ifLT@ShowOptions
+  \GenericWarning{* }{%
+     *** List of possible options for lua-typo ***\MessageBreak
+     [Default values between brackets]%
+     \MessageBreak
+     ShowOptions     [false]\MessageBreak
+     None            [false]\MessageBreak
+     BackParindent   [false]\MessageBreak
+     ShortLines      [false]\MessageBreak
+     ShortPages      [false]\MessageBreak
+     OverfullLines   [false]\MessageBreak
+     UnderfullLines  [false]\MessageBreak
+     Widows          [false]\MessageBreak
+     Orphans         [false]\MessageBreak
+     EOPHyphens      [false]\MessageBreak
+     RepeatedHyphens [false]\MessageBreak
+     ParLastHyphen   [false]\MessageBreak
+     EOLShortWords   [false]\MessageBreak
+     FirstWordMatch  [false]\MessageBreak
+     LastWordMatch   [false]\MessageBreak
+     \MessageBreak
+     *********************************************%
+     \MessageBreak Lua-typo [ShowOptions]
+   }%
+\fi
+%    \end{macrocode}
+%
+%   Some defaut values which can be customised in the preamble
+%   are forwarded to Lua AtBeginDocument.
+%
+%    \begin{macrocode}
+\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
+   }%
+}
+%    \end{macrocode}
+%
+%    Print the summary of offending pages ---if any-- at the
+%    (very) end of document unless option |None| has been selected.
+%
+%    \begin{macrocode}
+\AtVeryEndDocument{%
+\ifnum\luatypo@options = 0 \LT@Nonetrue \fi
+\ifLT@None
+  \directlua{
+    texio.write_nl(' ')
+    texio.write_nl('***********************************')
+    texio.write_nl('*** lua-typo loaded with NO option:')
+    texio.write_nl('*** NO CHECK PERFORMED! ***')
+    texio.write_nl('***********************************')
+    texio.write_nl(' ')
+   }%
+\else
+  \directlua{
+    texio.write_nl(' ')
+    texio.write_nl('*************************************')
+    if luatypo.pagelist == "" then
+       texio.write_nl('*** lua-typo: No Typo Flaws found.')
+    else
+       texio.write_nl('*** lua-typo: WARNING *************')
+       texio.write_nl('The following pages need attention: '
+                      .. luatypo.pagelist)
+    end
+    texio.write_nl('***********************************')
+    texio.write_nl(' ')
+   }%
+\fi}
+%    \end{macrocode}
+%
+% \begin{macro}{\luatypoOneChar}
+% \begin{macro}{\luatypoTwoChars}
+%    These commands set which short words should be avoided at end of
+%    lines.  The first argument is a language name, say \opt{french},
+%    which is turned into a command |\l@french| expanding to a number
+%    known by luatex, otherwise an error message occurs.
+%    The UTF8 string entered as second argument has to be converted
+%    into the font internal coding.
+%    \begin{macrocode}
+\newcommand*{\luatypoOneChar}[2]{%
+  \def\luatypo@LANG{#1}\luatypo@single={#2}%
+  \ifcsname l@\luatypo@LANG\endcsname
+    \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+    \directlua{
+      local langno = \the\luatypo@LANGno
+      local string = \the\luatypo@single
+      luatypo.single[langno] = " "
+      for p, c in utf8.codes(string) do
+        local s = string.char(c)
+        luatypo.single[langno] = luatypo.single[langno] .. s
+      end
+%<dbg>     texio.write_nl("SINGLE=" .. luatypo.single[langno])
+%<dbg>     texio.write_nl(' ')
+     }%
+  \else
+    \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+       \MessageBreak \protect\luatypoOneChar\space command ignored}%
+  \fi}
+\newcommand*{\luatypoTwoChars}[2]{%
+  \def\luatypo@LANG{#1}\luatypo@double={#2}%
+  \ifcsname l@\luatypo@LANG\endcsname
+    \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+    \directlua{
+      local langno = \the\luatypo@LANGno
+      local string = \the\luatypo@double
+      luatypo.double[langno] = " "
+      for p, c in utf8.codes(string) do
+        local s = string.char(c)
+        luatypo.double[langno] = luatypo.double[langno] .. s
+      end
+%<dbg>     texio.write_nl("DOUBLE=" .. luatypo.double[langno])
+%<dbg>     texio.write_nl(' ')
+    }%
+  \else
+    \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+       \MessageBreak \protect\luatypoTwoChars\space command ignored}%
+  \fi}
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \vspace*{\baselineskip}
+% \begin{macro}{\luatypoSetColor}
+%    This is a user-level command to customise the colours highlighting
+%    the nine 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
+%    attributes).
+%    \begin{macrocode}
+\newcommand*{\luatypoSetColor}[2]{%
+  \begingroup
+    \color{#2}%
+    \directlua{luatypo.colortbl[#1]=\the\LuaCol@Attribute}%
+  \endgroup
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%    The Lua code now, initialisations.
+%
+%    \begin{macrocode}
+\begin{luacode}
+luatypo.single = { }
+luatypo.double = { }
+luatypo.colortbl  = { }
+luatypo.pagelist  = ""
+
+local hyphcount = 0
+local parlines = 0
+local pagelines = 0
+local pageno = 0
+local pageflag = false
+
+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"
+
+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")
+% \end{macrocode}
+%    GLUE subtypes:
+%    \begin{macrocode}
+local USRSKIP  = 0
+local PARSKIP  = 3
+local LFTSKIP  = 8
+local RGTSKIP  = 9
+local TOPSKIP = 10
+local PARFILL = 15
+% \end{macrocode}
+%    HLIST subtypes:
+%    \begin{macrocode}
+local LINE    = 1
+local BOX     = 2
+%    \end{macrocode}
+%    Penalty subtypes:
+%    \begin{macrocode}
+local USER = 0
+local HYPH = 0x2D
+
+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
+
+%    \end{macrocode}
+%
+%    This auxillary function colours glyphs and discretionaries.
+%    It requires two arguments: a node and a (named) colour.
+%
+%    \begin{macrocode}
+local color_node = function (node, color)
+  local attr = oberdiek.luacolor.getattribute()
+  if node.id == DISC then
+     local pre = node.pre
+     local post = node.post
+     local replace = node.replace
+     if pre then
+        set_attribute(pre,attr,color)
+%<dbg>     texio.write_nl('PRE=' .. tostring(pre.char))
+     end
+     if post then
+        set_attribute(post,attr,color)
+%<dbg>     texio.write_nl('POST=' .. tostring(post.char))
+     end
+     if replace then
+        set_attribute(replace,attr,color)
+%<dbg>     texio.write_nl('REPL=' .. tostring(replace.char))
+     end
+%<dbg>     texio.write_nl(' ')
+  else
+     set_attribute(node,attr,color)
+  end
+end
+%    \end{macrocode}
+%
+%    This auxillary function colours the content of an |\hbox|.
+%    It requires two arguments: a node (the box) and a (named) colour.
+%
+%    \begin{macrocode}
+local color_hbox = function (head, color)
+  local first = head.head
+  for n in traverse(first) do
+      color_node(n, color)
+  end
+end
+%
+%    This auxillary function colours a whole line. It requires two
+%    arguments: a line’s node and a (named) colour.
+%
+%    \begin{macrocode}
+local color_line = function (head, color)
+  local first = head.head
+  for n in traverse(first) do
+      if n.id == HLIST and n.subtype == BOX then
+         color_hbox(n, color)
+      else
+         color_node(n, color)
+      end
+  end
+end
+%    \end{macrocode}
+%
+%    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
+%    glyphs and split 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
+%    backwards (see |check_last_word|).
+%    \begin{macrocode}
+local signature = function (node, string, swap)
+  local n = node
+  local str = string
+  if n.id == GLYPH then
+    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;
+%    Other glyphs should have char codes less than 0x100 (or 0x180?) or
+%    be ligatures… standard ones (U+FB00 to U+FB04) are converted using
+%    table |split_lig|.
+%    \begin{macrocode}
+       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
+       end
+    end
+  elseif 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
+%    a ligature…
+%    \begin{macrocode}
+    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)
+  return len, str
+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).
+%    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).
+%
+%    \begin{macrocode}
+local check_last_word = function (old, node, color, flag)
+  local match = false
+  local new = ""
+  local len = 0
+  if flag then
+%    \end{macrocode}
+%    Get the last glyph one the line, skipping discretionaries,
+%    margin kerns, etc., unless |flag| cancels the whole process.
+%    \begin{macrocode}
+     local swap = true
+     local lastn = node
+     while lastn and lastn.id ~= GLYPH and lastn.prev do
+       lastn = lastn.prev
+     end
+%    \end{macrocode}
+%    A signature is built from the last two words on the current line.
+%    \begin{macrocode}
+     local n = lastn
+     repeat
+       len, new = signature (n, new, swap)
+       n = n.prev
+     until not n or n.id == GLUE
+     if n and n.id == GLUE then
+        new = new .. "_"
+        len = len + 1
+        repeat
+          n = n.prev
+          len, new = signature (n, new, swap)
+        until n.id == GLUE or not n.prev
+     end
+     new = string.reverse(new)
+%<dbg>  texio.write_nl('EOLsigold=' .. old)
+%<dbg>  texio.write('   EOLsig=' .. new)
+     local MinFull = luatypo.MinFull
+     local MinPart = luatypo.MinPart
+     MinFull = math.min(MinPart,MinFull)
+     local k = MinPart
+     local oldlast = string.gsub (old, '%w+_', '')
+     local newlast = string.gsub (new, '%w+_', '')
+     len = string.len(newlast)
+     if len < MinPart then
+        k = MinPart + 1
+     end
+     local oldsub = string.sub(old,-k)
+     local newsub = string.sub(new,-k)
+     local maxlen = string.len(new)
+     if oldsub == newsub then
+%<dbg>  texio.write_nl('EOLnewsub=' .. newsub)
+        match = true
+     elseif oldlast == newlast and len >= MinFull then
+%<dbg>  texio.write_nl('EOLnewlast=' .. newlast)
+        match = true
+        oldsub = oldlast
+        newsub = newlast
+        k = len
+     end
+     if match then
+%    \end{macrocode}
+%    Minimal partial match; any more glyphs matching?
+%    \begin{macrocode}
+        local osub = oldsub
+        local nsub = newsub
+        while osub == nsub and k < maxlen do
+          k = k +1
+          osub = string.sub(old,-k)
+          nsub = string.sub(new,-k)
+          if osub == nsub then
+             newsub = nsub
+          end
+        end
+%<dbg>     texio.write_nl('EOLfullmatch=' .. newsub)
+%<msg>     texio.write_nl('***MATCH=' .. newsub ..
+%<msg>                              " on page " .. pageno)
+%<msg>     texio.write_nl(' ')
+%    \end{macrocode}
+%    Lest's colour the matching string.
+%    \begin{macrocode}
+        oldsub = string.reverse(newsub)
+        local newsub = ""
+        local k = string.len(oldsub)
+        local n = lastn
+        repeat
+          if n and n.id ~= GLUE then
+             color_node(n, color)
+             len, newsub = signature(n, newsub, swap)
+          elseif n then
+             newsub = newsub .. "_"
+             len = len + 1
+          end
+          n = n.prev
+        until not n or newsub == oldsub or len >= k
+     end
+  end
+  return new, match
+end
+%    \end{macrocode}
+%
+%    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 match = false
+  local swap = false
+  local new = ""
+  local len = 0
+  local start = node
+  local n = start
+  while n and n.id ~= GLUE do
+    len, new = signature (n, new, swap)
+    n = n.next
+  end
+  n = n.next
+  new = new .. "_"
+  while n and n.id ~= GLUE do
+    len, new = signature (n, new, swap)
+    n = n.next
+  end
+%<dbg>  texio.write_nl('BOLsigold=' .. old)
+%<dbg>  texio.write('   BOLsig=' .. new)
+%    \end{macrocode}
+%     When called with flag |false|, |check_first_word| returns
+%     the first word’s signature, but doesn’t compare it with the
+%     previous line’s.
+%    \begin{macrocode}
+  if flag then
+     local MinFull = luatypo.MinFull
+     local MinPart = luatypo.MinPart
+     MinFull = math.min(MinPart,MinFull)
+     local k = MinPart
+     local L = MinPart -1
+     local oldsub = ""
+     local newsub = ""
+     local oldfirst = string.gsub (old, '_%w+', '')
+     local newfirst = string.gsub (new, '_%w+', '')
+     len = string.len(newfirst)
+     if len < MinPart then
+        k = MinPart + 1
+     end
+     local maxlen = string.len(new)
+     local oldsub = string.sub(old,1,L)
+     local newsub = string.sub(new,1,L)
+     if oldsub == newsub then
+%<dbg>  texio.write_nl('BOLnewsub=' .. newsub)
+        match = true
+        k = L
+     elseif oldfirst == newfirst  and len >= MinFull then
+%<dbg>  texio.write_nl('BOLnewfirst=' .. newfirst)
+        match = true
+        oldsub = oldfirst
+        newsub = newfirst
+        k = len
+     end
+     if match then
+%    \end{macrocode}
+%    Minimal partial match; any more glyphs matching?
+%    \begin{macrocode}
+        local osub = oldsub
+        local nsub = newsub
+        while osub == nsub and k < maxlen do
+          k = k + 1
+          osub = string.sub(old,1,k)
+          nsub = string.sub(new,1,k)
+          if osub == nsub then
+             newsub = nsub
+          end
+        end
+        if k < MinPart then
+           match =false
+        end
+     end
+     if match then
+%<dbg>     texio.write_nl('BOLfullmatch=' .. newsub)
+%<msg>     texio.write_nl('***MATCH=' .. newsub ..
+%<msg>                              " on page " .. pageno)
+%<msg>     texio.write_nl(' ')
+%    \end{macrocode}
+%    Lest's colour the matching string.
+%    \begin{macrocode}
+        oldsub = newsub
+        local newsub = ""
+        local k = string.len(oldsub)
+        local n = start
+        repeat
+          if n and n.id ~= GLUE then
+             color_node(n, color)
+             len, newsub = signature(n, newsub, swap)
+          elseif n then
+             newsub = newsub .. "_"
+             len = len + 1
+          end
+          n = n.next
+        until not n or newsub == oldsub or len >= k
+     end
+  end
+  return new, match
+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.\\
+%    TODO: where does ``out of range’’ starts? U+0100? U+0180?
+%
+%    \begin{macrocode}
+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
+%    \end{macrocode}
+%    First look for single chars unless the list of words is empty.
+%    \begin{macrocode}
+  if lang and luatypo.single[lang] then
+%    \end{macrocode}
+%    For single char words, the previous node is a glue.
+%    \begin{macrocode}
+     if lchar and lchar < 0x100 and previous and previous.id == GLUE then
+        match = string.find(luatypo.single[lang], string.char(lchar))
+        if match then
+           color_node(glyph,COLOR)
+%<msg>   texio.write_nl('***MATCH=' .. string.char(lchar) ..
+%<msg>                            " on page " .. pageno)
+%<msg>   texio.write_nl(' ')
+        end
+     end
+  end
+%    \end{macrocode}
+%    Look for two chars words unless the list of words is empty.
+%    \begin{macrocode}
+  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
+%    \end{macrocode}
+%    For two chars words, the previous node is a glue…
+%    \begin{macrocode}
+        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
+              color_node(previous,COLOR)
+              color_node(glyph,COLOR)
+%<msg>        texio.write_nl('***MATCH=' .. pattern ..
+%<msg>                                 " on page " .. pageno)
+%<msg>        texio.write_nl(' ')
+           end
+        end
+%    \end{macrocode}
+%    …unless a kern is found between the two chars.
+%    \begin{macrocode}
+     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
+                 color_node(pprev,COLOR)
+                 color_node(glyph,COLOR)
+%<msg>           texio.write_nl('***MATCH=' .. 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.
+%
+%    \begin{macrocode}
+local show_pre_disc = function (disc, color)
+  local n = disc
+  while n.id ~= GLUE do
+    color_node(n, color)
+    n = n.prev
+  end
+  return n
+end
+%    \end{macrocode}
+%
+%    This is the main function which will be added to the
+%    |"pre_output_filter"| callback unless option |None| is selected.
+%
+%    \begin{macrocode}
+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 Stretch = math.max(luatypo.Stretch/100,1)
+
+  local orphanflag = false
+  local widowflag  = false
+  local lwhyphflag = false
+  local match1 = false
+  local match2 = false
+  local firstwd = ""
+  local lastwd = ""
+  while head do
+    local nextnode = head.next
+    local prevnode = head.prev
+    local pprevnode = nil
+    if prevnode then
+       pprevnode = prevnode.prev
+    end
+%    \end{macrocode}
+%    If the current node is a glue of type topskip, we are starting new
+%    page, let’s reset some counters and flags : |pageflag| will be set
+%    to |true| if a possible typographic flaw is found on this page,
+%    and trigger the addition of this page number to the summary list.
+%    |hyphcount| hold the number the consecutive hyphenated lines.
+%    \begin{macrocode}
+    if head.id == GLUE and head.subtype == TOPSKIP then
+       pageflag = false
+       match1 = false
+       pageno = tex.getcount("c@page")
+       hyphcount = 0
+       pagelines = 0
+       firstwd = ""
+       lastwd = ""
+    elseif head.id == HLIST and head.subtype == LINE then
+%    \end{macrocode}
+%    The current node is a line, |first| is the line’s first node.
+%    Skip margin kern or leftskip if any.
+%    \begin{macrocode}
+       local first = head.head
+       if first.id == MKERN or
+         (first.id == GLUE and first.subtype == LFTSKIP) then
+          first = first.next
+       end
+       pagelines = pagelines + 1
+       local ListItem = false
+%    \end{macrocode}
+%    Is this line really a text line (one glyph at least)?
+%    \begin{macrocode}
+       local textline = true
+       local n = first
+       while (n and n.id ~= GLYPH) and
+             not (n.id == GLUE and n.subtype == RGTSKIP) do
+         n = n.next
+       end
+       if n.id == GLUE then
+          textline = false
+       end
+%    \end{macrocode}
+%    Is this line overfull or underfull?
+%    \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(' ')
+          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
+%<msg>  texio.write_nl('***UNDERFULL line on page ' ..
+%<msg>                                    tex.getcount("c@page"))
+%<msg>  texio.write_nl(' ')
+          local COLOR = luatypo.colortbl[8]
+          pageflag = true
+          color_line (head, COLOR)
+       end
+%    \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,
+%    \begin{macrocode}
+          hyphcount = 0
+          parlines = 1
+          if not nextnode 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.
+%    \begin{macrocode}
+             orphanflag = true
+          end
+%    \end{macrocode}
+%    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
+          end
+       else
+          parlines = parlines + 1
+       end
+%    \end{macrocode}
+%    Let’s track lines beginning with the same word (except lists).
+%    \begin{macrocode}
+       if FirstWordMatch then
+          local flag = not ListItem
+          local COLOR = luatypo.colortbl[10]
+          firstwd, match1 = check_first_word(firstwd, first, COLOR, flag)
+          if match1 then
+             pageflag = true
+          end
+       end
+%    \end{macrocode}
+%    Let’s check the end of line: |ln| (usually a rightskip) and |pn|
+%    are the last two nodes.
+%    \begin{macrocode}
+       local ln = slide(first)
+       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…
+%    \begin{macrocode}
+          hyphcount = 0
+          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
+             widowflag = true
+          end
+%    \end{macrocode}
+%    |PFskip| is the rubber length (in sp) added to complete the line.
+%    \begin{macrocode}
+          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')
+%    \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 attr = oberdiek.luacolor.getattribute()
+%    \end{macrocode}
+%    let’s colour the whole line.
+%    \begin{macrocode}
+                color_line (head, COLOR)
+             end
+          end
+%    \end{macrocode}
+%    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 attr = oberdiek.luacolor.getattribute()
+             color_line (head, COLOR)
+          end
+%    \end{macrocode}
+%    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
+          end
+       elseif pn and pn.id == DISC then
+%    \end{macrocode}
+%    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
+          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(' ')
+          end
+          if not nextnode and EOPHyphens then
+%    \end{macrocode}
+%    No more nodes: this hyphen occurs on the page last line.
+%    \begin{macrocode}
+             lwhyphflag = true
+          end
+          if nextnode and ParLastHyphen then
+%    \end{macrocode}
+%    Does the next line end the current paragraph? If so, |nextnode| is
+%    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 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.
+%    \begin{macrocode}
+          hyphcount = 0
+          if LastWordMatch then
+             local COLOR = luatypo.colortbl[11]
+             lastwd, match1 = check_last_word(lastwd, pn, COLOR, 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)
+             end
+             if EOLShortWords then
+                match2 = check_regexpr(ppn)
+             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.
+%    \begin{macrocode}
+       if widowflag and Widows then
+          pageflag = true
+          widowflag = false
+%<msg>    texio.write_nl('***WIDOW on top of page ' .. pageno)
+%<msg>    texio.write_nl(' ')
+          local COLOR  = luatypo.colortbl[4]
+          color_line (head, COLOR)
+       end
+%    \end{macrocode}
+%    Colour the whole line if is is a orphan.
+%    \begin{macrocode}
+       if orphanflag and Orphans then
+%<msg>    texio.write_nl('***ORPHAN at bottom of page ' .. pageno)
+%<msg>    texio.write_nl(' ')
+          pageflag = true
+          local COLOR = luatypo.colortbl[5]
+          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(' ')
+          local COLOR = luatypo.colortbl[1]
+          local pg = show_pre_disc (pn,COLOR)
+          pageflag = true
+       end
+%    \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…
+%    \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 node = head
+          while node.id ~= HLIST or node.subtype ~= LINE do
+             node = node.prev
+          end
+          color_line(node, COLOR)
+       end
+    end
+  head = nextnode
+  end
+%    \end{macrocode}
+%    Add this page number to the summary if any flaw has been found on it.
+%    \begin{macrocode}
+  if pageflag then
+     luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", "
+  end
+  return true
+end
+return luatypo.check_page
+\end{luacode}
+%    \end{macrocode}
+%
+%    Add the |luatypo.check_page| function to the |pre_output_filter|
+%    callback, unless option |None| is selected ; remember that the
+%    |None| boolean’s value is forwarded to Lua `AtEndOfPackage’…
+%    \begin{macrocode}
+\AtEndOfPackage{%
+  \directlua{
+    if not luatypo.None then
+       luatexbase.add_to_callback
+           ("pre_output_filter",luatypo.check_page,"check_page")
+    end
+  }
+}
+%    \end{macrocode}
+%
+%    Load a local config file if present in LaTeX’s search path.
+%    Otherwise, set reasonnable defaults.
+%    \begin{macrocode}
+
+\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{macrocode}
+% \iffalse
+%</sty>
+% \fi
+%
+% \clearpage
+%  \section{Configuration file}
+%
+% \iffalse
+%<*cfg>
+% \fi
+%
+% \let\theCodelineNo\relax
+%    \begin{macrocode}
+%%% Configuration file for lua-typo.sty
+%%% These settings can also be overruled in the preamble.
+
+%% Minimum gap between end of paragraphs’ last lines and the right margin
+\luatypoBackPI=1em\relax
+\luatypoBackFuzz=2pt\relax
+
+%% Minimum length of paragraphs’ last lines
+\luatypoLLminWD=2\parindent\relax
+
+%% Maximum number of consecutive hyphenated lines
+\luatypoHyphMax=2\relax
+
+%% Nearly empty pages: minimum number of lines
+\luatypoPageMin=5\relax
+
+%% Maximum acceptable stretch before a line is tagged as Underfull
+\luatypoStretchMax=200\relax
+
+%% Minimum number of matching characters for words at begin/end of line
+\luatypoMinFull=3\relax
+\luatypoMinPart=4\relax
+
+%% Default colours = red, cyan, mygrey
+\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 (just a few lines)
+\luatypoSetColor{10}{myred} % First word matches
+\luatypoSetColor{11}{myred} % Last word matches
+\luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+
+%% Language specific settings (example for French only currently):
+%% short words (two letters max) to be avoided at end of lines.
+%% French:
+%%\luatypoOneChar{french}{'À à Ô'}
+%%\luatypoTwoChars{french}{'Je Tu Il On'}
+%    \end{macrocode}
+%
+%\iffalse
+%</cfg>
+% \fi
+%
+% \iffalse
+%<*dtx>
+% \fi
+%%
+%% \CharacterTable
+%%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+%%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+%%   Digits        \0\1\2\3\4\5\6\7\8\9
+%%   Exclamation   \!     Double quote  \"     Hash (number) \#
+%%   Dollar        \$     Percent       \%     Ampersand     \&
+%%   Acute accent  \'     Left paren    \(     Right paren   \)
+%%   Asterisk      \*     Plus          \+     Comma         \,
+%%   Minus         \-     Point         \.     Solidus       \/
+%%   Colon         \:     Semicolon     \;     Less than     \<
+%%   Equals        \=     Greater than  \>     Question mark \?
+%%   Commercial at \@     Left bracket  \[     Backslash     \\
+%%   Right bracket \]     Circumflex    \^     Underscore    \_
+%%   Grave accent  \`     Left brace    \{     Vertical bar  \|
+%%   Right brace   \}     Tilde         \~}
+%%
+% \iffalse
+%</dtx>
+% \fi
+%
+% \Finale
+\endinput
+
+%%% Local Variables:
+%%% fill-column: 72
+%%% coding: utf-8
+%%% TeX-engine: luatex
+%%% End:
-- 
cgit v1.2.3

