summaryrefslogtreecommitdiff
path: root/Master
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-03-04 22:09:16 +0000
committerKarl Berry <karl@freefriends.org>2021-03-04 22:09:16 +0000
commit7df76069beaebbde72a91ad3fd9102eee6857877 (patch)
tree86d6965281030f20bbe7c22ae4967edb9a281d0a /Master
parentb1279daa67e27985c60f1b13293e22c5c596386c (diff)
lua-typo (4mar21)
git-svn-id: svn://tug.org/texlive/trunk@58124 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
-rw-r--r--Master/texmf-dist/doc/lualatex/lua-typo/README.md55
-rw-r--r--Master/texmf-dist/doc/lualatex/lua-typo/demo.pdfbin0 -> 16794 bytes
-rw-r--r--Master/texmf-dist/doc/lualatex/lua-typo/demo.tex89
-rw-r--r--Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx44
-rw-r--r--Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdfbin0 -> 59618 bytes
-rw-r--r--Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx42
-rw-r--r--Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdfbin0 -> 114259 bytes
-rw-r--r--Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx1942
-rw-r--r--Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg48
-rw-r--r--Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty853
-rwxr-xr-xMaster/tlpkg/bin/tlpkg-ctan-check2
-rwxr-xr-xMaster/tlpkg/libexec/ctan2tds2
-rw-r--r--Master/tlpkg/tlpsrc/collection-luatex.tlpsrc1
-rw-r--r--Master/tlpkg/tlpsrc/lua-typo.tlpsrc0
14 files changed, 3077 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/README.md b/Master/texmf-dist/doc/lualatex/lua-typo/README.md
new file mode 100644
index 00000000000..29db098d3b9
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/lua-typo/README.md
@@ -0,0 +1,55 @@
+Lua-typo
+========
+
+Description
+-----------
+This package tracks common typographic flaws in LuaLaTeX documents,
+specially widows, orphans, hyphenated words split over two pages,
+consecutive lines ending with hyphens, paragraphs ending on
+too short lines, etc.
+
+Documentation
+-------------
+
+For the impatient: have a look at files demo.tex and demo.pdf to see
+a short example of how some flaws are highlighted.
+
+Then read the documentation in English (file lua-typo.pdf) or in French
+(file lua-typo-fr.pdf).
+
+License
+-------
+
+Released under the LaTeX Project Public License v1.3 or later
+See http://www.latex-project.org/lppl.txt
+for the details of that license.
+
+Installation
+------------
+
+This bundle is meant to be included in most TeX distributions,
+but if you need to install it by yourself
+1. run "luatex lua-typo.dtx" to strip the comments and create
+ lua-typo.sty, lua-typo.cfg, lua-typo.ltx and lua-typo-fr.ltx;
+2. run "lualatex lua-typo.ltx" to get the full documentation
+ (lua-typo.pdf) in English;
+2. run "lualatex lua-typo-fr.ltx" to get the French documentation
+ (lua-typo-fr.pdf, code not included).
+
+Recommended loactions for installation:
+- TDS:tex/lualatex/lua-typo/lua-typo.sty
+- TDS:tex/lualatex/lua-typo/lua-typo.cfg
+- TDS:doc/lualatex/lua-typo/lua-typo.pdf
+- TDS:doc/lualatex/lua-typo/demo.pdf
+- TDS:doc/lualatex/lua-typo/demo.tex
+- TDS:doc/lualatex/lua-typo/README.md
+- TDS:source/lualatex/lua-typo/lua-typo.dtx
+
+Changes
+-------
+
+- First release version: 0.30, March 2021.
+
+--
+Copyright 2020--2021 Daniel Flipo
+E-mail: daniel (dot) flipo (at) free (dot) fr
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/demo.pdf b/Master/texmf-dist/doc/lualatex/lua-typo/demo.pdf
new file mode 100644
index 00000000000..d7e17c1ab1d
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/lua-typo/demo.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/demo.tex b/Master/texmf-dist/doc/lualatex/lua-typo/demo.tex
new file mode 100644
index 00000000000..e09c822e1df
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/lua-typo/demo.tex
@@ -0,0 +1,89 @@
+\documentclass[a6paper,french,11pt,twoside]{scrartcl}
+\usepackage{scrlayer-scrpage}
+\lohead{Pierre Desproges}
+\lehead{Vivons heureux…}
+\rohead{}
+\rehead{en attendant la mort}
+\usepackage{fourier-otf}
+\usepackage{microtype}
+
+\usepackage{babel}
+\frenchsetup{og=«, fg=»}
+
+\usepackage[All]{lua-typo} % APRÈS babel / polyglossia
+
+% Mots courts (deux lettres max) à éviter en fin de ligne
+\luatypoOneChar{french}{'À à Ô'}
+\luatypoTwoChars{french}{"Je Tu Il On"}
+
+% Pour changer les couleurs :
+%\usepackage[svgnames]{xcolor}
+%\LuaTypoSetColor1{Fuchsia}
+%\LuaTypoSetColor2{ForestGreen}
+%\LuaTypoSetColor5{magenta}
+%\LuaTypoSetColor6{yellow}
+
+% Réglage ligne finale trop courte
+\setlength{\luatypoLLminWD}{3em}
+
+%\luatypoPageMin=5
+%\luatypoHyphMax=2
+
+\parindent=1em
+
+\begin{document}
+
+Le plus effroyablement démuni des pauvres peut toujours espérer
+\footnote{Overfull hbox à la ligne précédente.}
+décrocher un jour le gros lot de la tombola organisée par
+l’association des Pauvres Effroyablement Démunis.
+%
+Le laideron lui, n’a d’autre échappatoire que de
+ronger son frein d’un bec-de-lièvre machinal, de baisser ses yeux quelconques
+aux abords des mirroirs qui l’insultent ou de se foutre à l’eau au risque
+d’effaroucher les murènes.
+
+Quelquefois je trouve que Dieu pousse un peu.
+
+« Les hommes naissent libres et égaux en droit. »
+
+Qu’on me pardonne mais c’est un phrase que j’ai beaucoup de mal à dire sans
+rire. % : « Les hommes naissent libres et égaux en droit. »
+
+Prenons une star, une belle star. Elle est belle.
+
+La beauté. Existe-t-il au monde un privilège plus exorbitant que la beauté ?
+
+Par sa beauté, cette femme n’est-elle pas un peu plus libre
+et un peu plus égale , dans le grand combat pour survivre,
+que l’\textit{Homo sapiens} moyen qui passe sa vie à se courir
+après la queue en attendant la mort ?
+
+Quel profond imbécile aurait l’outrecuidance de soutenir, au nom des grands
+principes révolutionnaires, que l’immonde boudin trapu qui m’a
+%collé une contredanse
+lâchement verbalisé
+tout-à-l’heure
+possède les mêmes armes
+%pour assoir son bonheur terrestre
+que la grande fille féline aux charmes troubles où l’œil se
+pose et chancelle avec une lubricité contenue !
+%(Difficilement contenue.) % Variantes pour mettre 1 ou 2 car. en bout de ligne
+%(Difficilement contenue hein). Je dois le dire.
+%(Difficilement contenue hein). On peut le dire.
+%(Difficilement contenue hein). Il faut l’admettre.
+(Difficilement contenue hein). À voir.
+
+Quand on a vos yeux, madame, quand on a votre bouche, votre grain de peau, la
+légèreté diaphane de votre démarche et la longueur émouvante de vos cuisses,
+c’est une banalité de dire qu’on peut facilement traverser la vie %l’existence
+à l’abri des cabats trop lourds gorgés de poireaux, à l’écart de l’uniforme de
+contractuelle.
+
+\end{document}
+
+%%% Local Variables:
+%%% coding: utf-8-unix
+%%% TeX-master: t
+%%% TeX-engine: luatex
+%%% End:
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx
new file mode 100644
index 00000000000..449dfe7bfd0
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx
@@ -0,0 +1,44 @@
+\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}
+\usepackage[french]{babel}
+\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}
+\OnlyDescription
+\let\FrenchDoc\begingroup\let\endFrenchDoc\endgroup
+\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:
+%%
+%%
+%% End of file `lua-typo-fr.ltx'.
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf
new file mode 100644
index 00000000000..273649ff718
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx
new file mode 100644
index 00000000000..310c7dae04a
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx
@@ -0,0 +1,42 @@
+\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}
+\usepackage[british]{babel}
+\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}
+\let\FrenchDoc\comment\let\endFrenchDoc\endcomment
+\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:
+%%
+%%
+%% End of file `lua-typo.ltx'.
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf
new file mode 100644
index 00000000000..16a7f744c66
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf
Binary files differ
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:
diff --git a/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg
new file mode 100644
index 00000000000..a395e4fe2a5
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg
@@ -0,0 +1,48 @@
+%%% 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 of file `lua-typo.cfg'.
diff --git a/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty
new file mode 100644
index 00000000000..0408dc24a18
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty
@@ -0,0 +1,853 @@
+%%
+%% This is file `lua-typo.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% lua-typo.dtx (with options: `sty')
+%%
+\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+\ProvidesPackage{lua-typo}
+ [2021/03/03 v.0.30 Daniel Flipo]
+\ifdefined\directlua
+ \RequirePackage{luatexbase,luacode,luacolor}
+ \RequirePackage{kvoptions,atveryend}
+\else
+ \PackageError{This package is meant for LuaTeX only! Aborting}
+ {No more information available, sorry!}
+\fi
+\newdimen\luatypoLLminWD
+\newdimen\luatypoBackPI
+\newdimen\luatypoBackFuzz
+\newcount\luatypoStretchMax
+\newcount\luatypoHyphMax
+\newcount\luatypoPageMin
+\newcount\luatypoMinFull
+\newcount\luatypoMinPart
+\newcount\luatypo@LANGno
+\newcount\luatypo@options
+\newtoks\luatypo@single
+\newtoks\luatypo@double
+\begin{luacode}
+luatypo = { }
+\end{luacode}
+\SetupKeyvalOptions{
+ family=luatypo,
+ prefix=LT@,
+}
+\DeclareBoolOption[false]{ShowOptions}
+\DeclareBoolOption[false]{None}
+\DeclareBoolOption[false]{All}
+\DeclareBoolOption[false]{BackParindent}
+\DeclareBoolOption[false]{ShortLines}
+\DeclareBoolOption[false]{ShortPages}
+\DeclareBoolOption[false]{OverfullLines}
+\DeclareBoolOption[false]{UnderfullLines}
+\DeclareBoolOption[false]{Widows}
+\DeclareBoolOption[false]{Orphans}
+\DeclareBoolOption[false]{EOPHyphens}
+\DeclareBoolOption[false]{RepeatedHyphens}
+\DeclareBoolOption[false]{ParLastHyphen}
+\DeclareBoolOption[false]{EOLShortWords}
+\DeclareBoolOption[false]{FirstWordMatch}
+\DeclareBoolOption[false]{LastWordMatch}
+\AddToKeyvalOption{luatypo}{All}{%
+ \LT@ShortLinestrue \LT@ShortPagestrue
+ \LT@OverfullLinestrue \LT@UnderfullLinestrue
+ \LT@Widowstrue \LT@Orphanstrue
+ \LT@EOPHyphenstrue \LT@RepeatedHyphenstrue
+ \LT@ParLastHyphentrue \LT@EOLShortWordstrue
+ \LT@FirstWordMatchtrue \LT@LastWordMatchtrue
+ \LT@BackParindenttrue
+}
+\ProcessKeyvalOptions{luatypo}
+\AtEndOfPackage{%
+ \ifLT@None
+ \directlua{ luatypo.None = true }%
+ \else
+ \directlua{ luatypo.None = false }%
+ \fi
+ \ifLT@BackParindent
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.BackParindent = true }%
+ \else
+ \directlua{ luatypo.BackParindent = false }%
+ \fi
+ \ifLT@ShortLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ShortLines = true }%
+ \else
+ \directlua{ luatypo.ShortLines = false }%
+ \fi
+ \ifLT@ShortPages
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ShortPages = true }%
+ \else
+ \directlua{ luatypo.ShortPages = false }%
+ \fi
+ \ifLT@OverfullLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.OverfullLines = true }%
+ \else
+ \directlua{ luatypo.OverfullLines = false }%
+ \fi
+ \ifLT@UnderfullLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.UnderfullLines = true }%
+ \else
+ \directlua{ luatypo.UnderfullLines = false }%
+ \fi
+ \ifLT@Widows
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.Widows = true }%
+ \else
+ \directlua{ luatypo.Widows = false }%
+ \fi
+ \ifLT@Orphans
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.Orphans = true }%
+ \else
+ \directlua{ luatypo.Orphans = false }%
+ \fi
+ \ifLT@EOPHyphens
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.EOPHyphens = true }%
+ \else
+ \directlua{ luatypo.EOPHyphens = false }%
+ \fi
+ \ifLT@RepeatedHyphens
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.RepeatedHyphens = true }%
+ \else
+ \directlua{ luatypo.RepeatedHyphens = false }%
+ \fi
+ \ifLT@ParLastHyphen
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ParLastHyphen = true }%
+ \else
+ \directlua{ luatypo.ParLastHyphen = false }%
+ \fi
+ \ifLT@EOLShortWords
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.EOLShortWords = true }%
+ \else
+ \directlua{ luatypo.EOLShortWords = false }%
+ \fi
+ \ifLT@FirstWordMatch
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.FirstWordMatch = true }%
+ \else
+ \directlua{ luatypo.FirstWordMatch = false }%
+ \fi
+ \ifLT@LastWordMatch
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.LastWordMatch = true }%
+ \else
+ \directlua{ luatypo.LastWordMatch = false }%
+ \fi
+}
+\ifLT@ShowOptions
+ \GenericWarning{* }{%
+ *** List of possible options for lua-typo ***\MessageBreak
+ [Default values between brackets]%
+ \MessageBreak
+ ShowOptions [false]\MessageBreak
+ None [false]\MessageBreak
+ BackParindent [false]\MessageBreak
+ ShortLines [false]\MessageBreak
+ ShortPages [false]\MessageBreak
+ OverfullLines [false]\MessageBreak
+ UnderfullLines [false]\MessageBreak
+ Widows [false]\MessageBreak
+ Orphans [false]\MessageBreak
+ EOPHyphens [false]\MessageBreak
+ RepeatedHyphens [false]\MessageBreak
+ ParLastHyphen [false]\MessageBreak
+ EOLShortWords [false]\MessageBreak
+ FirstWordMatch [false]\MessageBreak
+ LastWordMatch [false]\MessageBreak
+ \MessageBreak
+ *********************************************%
+ \MessageBreak Lua-typo [ShowOptions]
+ }%
+\fi
+\AtBeginDocument{%
+ \directlua{
+ luatypo.HYPHmax = tex.count.luatypoHyphMax
+ luatypo.PAGEmin = tex.count.luatypoPageMin
+ luatypo.Stretch = tex.count.luatypoStretchMax
+ luatypo.MinFull = tex.count.luatypoMinFull
+ luatypo.MinPart = tex.count.luatypoMinPart
+ luatypo.LLminWD = tex.dimen.luatypoLLminWD
+ luatypo.BackPI = tex.dimen.luatypoBackPI
+ luatypo.BackFuzz = tex.dimen.luatypoBackFuzz
+ }%
+}
+\AtVeryEndDocument{%
+\ifnum\luatypo@options = 0 \LT@Nonetrue \fi
+\ifLT@None
+ \directlua{
+ texio.write_nl(' ')
+ texio.write_nl('***********************************')
+ texio.write_nl('*** lua-typo loaded with NO option:')
+ texio.write_nl('*** NO CHECK PERFORMED! ***')
+ texio.write_nl('***********************************')
+ texio.write_nl(' ')
+ }%
+\else
+ \directlua{
+ texio.write_nl(' ')
+ texio.write_nl('*************************************')
+ if luatypo.pagelist == "" then
+ texio.write_nl('*** lua-typo: No Typo Flaws found.')
+ else
+ texio.write_nl('*** lua-typo: WARNING *************')
+ texio.write_nl('The following pages need attention: '
+ .. luatypo.pagelist)
+ end
+ texio.write_nl('***********************************')
+ texio.write_nl(' ')
+ }%
+\fi}
+\newcommand*{\luatypoOneChar}[2]{%
+ \def\luatypo@LANG{#1}\luatypo@single={#2}%
+ \ifcsname l@\luatypo@LANG\endcsname
+ \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+ \directlua{
+ local langno = \the\luatypo@LANGno
+ local string = \the\luatypo@single
+ luatypo.single[langno] = " "
+ for p, c in utf8.codes(string) do
+ local s = string.char(c)
+ luatypo.single[langno] = luatypo.single[langno] .. s
+ end
+ }%
+ \else
+ \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+ \MessageBreak \protect\luatypoOneChar\space command ignored}%
+ \fi}
+\newcommand*{\luatypoTwoChars}[2]{%
+ \def\luatypo@LANG{#1}\luatypo@double={#2}%
+ \ifcsname l@\luatypo@LANG\endcsname
+ \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+ \directlua{
+ local langno = \the\luatypo@LANGno
+ local string = \the\luatypo@double
+ luatypo.double[langno] = " "
+ for p, c in utf8.codes(string) do
+ local s = string.char(c)
+ luatypo.double[langno] = luatypo.double[langno] .. s
+ end
+ }%
+ \else
+ \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+ \MessageBreak \protect\luatypoTwoChars\space command ignored}%
+ \fi}
+\newcommand*{\luatypoSetColor}[2]{%
+ \begingroup
+ \color{#2}%
+ \directlua{luatypo.colortbl[#1]=\the\LuaCol@Attribute}%
+ \endgroup
+}
+\begin{luacode}
+luatypo.single = { }
+luatypo.double = { }
+luatypo.colortbl = { }
+luatypo.pagelist = ""
+
+local 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")
+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 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
+
+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)
+ end
+ if post then
+ set_attribute(post,attr,color)
+ end
+ if replace then
+ set_attribute(replace,attr,color)
+ end
+ else
+ 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.id == HLIST and n.subtype == BOX then
+ color_hbox(n, color)
+ else
+ color_node(n, color)
+ end
+ end
+end
+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
+ 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
+ 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
+local check_last_word = function (old, node, color, flag)
+ local match = false
+ local new = ""
+ local len = 0
+ if flag then
+ local swap = true
+ local lastn = node
+ while lastn and lastn.id ~= GLYPH and lastn.prev do
+ lastn = lastn.prev
+ end
+ 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)
+ 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
+ match = true
+ elseif oldlast == newlast and len >= MinFull then
+ match = true
+ oldsub = oldlast
+ newsub = newlast
+ k = len
+ 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
+ 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
+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
+ 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
+ match = true
+ k = L
+ elseif oldfirst == newfirst and len >= MinFull then
+ match = true
+ oldsub = oldfirst
+ newsub = newfirst
+ k = len
+ 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
+ if k < MinPart then
+ match =false
+ end
+ end
+ if match then
+ 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
+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
+ 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
+ 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
+ color_node(pprev,COLOR)
+ color_node(glyph,COLOR)
+ end
+ end
+ end
+ end
+ end
+ return match
+end
+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
+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
+ 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
+ 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 = 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
+ 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
+ parlines = 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
+ parlines = parlines + 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
+ 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 pagelines == 1 and parlines > 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 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
+ 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
+ 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
+ elseif pn and pn.id == GLYPH then
+ hyphcount = 0
+ if LastWordMatch then
+ local COLOR = luatypo.colortbl[11]
+ lastwd, match1 = check_last_word(lastwd, pn, COLOR, 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)
+ end
+ if EOLShortWords then
+ match2 = check_regexpr(ppn)
+ end
+ if match1 or match2 then
+ pageflag = true
+ end
+ end
+ else
+ hyphcount = 0
+ 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
+ local COLOR = luatypo.colortbl[1]
+ local pg = show_pre_disc (pn,COLOR)
+ pageflag = true
+ 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 node = head
+ while node.id ~= HLIST or node.subtype ~= LINE do
+ node = node.prev
+ end
+ color_line(node, COLOR)
+ end
+ end
+ head = nextnode
+ end
+ if pageflag then
+ luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", "
+ end
+ return true
+end
+return luatypo.check_page
+\end{luacode}
+\AtEndOfPackage{%
+ \directlua{
+ if not luatypo.None then
+ luatexbase.add_to_callback
+ ("pre_output_filter",luatypo.check_page,"check_page")
+ end
+ }
+}
+
+\InputIfFileExists{lua-typo.cfg}%
+ {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file loaded}}%
+ {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file not found.
+ \MessageBreak Providing default values.}%
+ \definecolor{mygrey}{gray}{0.6}%
+ \definecolor{myred}{rgb}{1,0.55,0}
+ \luatypoSetColor0{red}% Paragraph last full line hyphenated
+ \luatypoSetColor1{red}% Page last word hyphenated
+ \luatypoSetColor2{red}% Hyphens ending two many consecutive lines
+ \luatypoSetColor3{red}% Short word at end of line
+ \luatypoSetColor4{cyan}% Widow
+ \luatypoSetColor5{cyan}% Orphan
+ \luatypoSetColor6{cyan}% Paragraph ending on a short line
+ \luatypoSetColor7{blue}% Overfull lines
+ \luatypoSetColor8{blue}% Underfull lines
+ \luatypoSetColor9{red}% Nearly empty page
+ \luatypoSetColor{10}{myred}% First word matches
+ \luatypoSetColor{11}{myred}% Last word matches
+ \luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+ \luatypoBackPI=1em\relax
+ \luatypoBackFuzz=2pt\relax
+ \luatypoLLminWD=2\parindent\relax
+ \luatypoStretchMax=200\relax
+ \luatypoHyphMax=2\relax
+ \luatypoPageMin=5\relax
+ \luatypoMinFull=4\relax
+ \luatypoMinPART=4\relax
+ }%
+%%
+%%
+%% End of file `lua-typo.sty'.
diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check
index 155fa8723e0..2ca7d79fd45 100755
--- a/Master/tlpkg/bin/tlpkg-ctan-check
+++ b/Master/tlpkg/bin/tlpkg-ctan-check
@@ -472,7 +472,7 @@ my @TLP_working = qw(
lstaddons lstbayes lstfiracode lt3graph ltablex ltabptch ltb2bib
ltxcmds ltxdockit ltxfileinfo ltxguidex ltximg
ltxkeys ltxmisc ltxnew ltxtools
- lua-alt-getopt lua-check-hyphen lua-physical lua-uca lua-ul
+ lua-alt-getopt lua-check-hyphen lua-physical lua-typo lua-uca lua-ul
lua-uni-algos lua-visual-debug
luabibentry luabidi luacode luacolor luahyphenrules
luaimageembed luaindex luainputenc luaintro luakeys
diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds
index 754532046b3..5e9ae580e48 100755
--- a/Master/tlpkg/libexec/ctan2tds
+++ b/Master/tlpkg/libexec/ctan2tds
@@ -792,6 +792,7 @@ chomp (my $ctan_root = `tlpkginfo --ctan-root`);
'ltxkeys', "&MAKEflatten",
'ltxmisc', "die 'skipping, too complicated, sorry'",
'ltxtable', "die 'skipping, use carlisle'",
+ 'lua-typo', "&MAKEflatten",
'lua-uca', "&MAKEflatten",
'lua-visual-debug', "&MAKEflatten",
'lua2dox', "die 'skipping, author request'",
@@ -2934,6 +2935,7 @@ my $core_latex = "$Master/texmf-dist/tex/latex";
'lmake' => 'latex', # requires interaction
'lni' => 'tex --8bit',
'ltxcmds' => 'etex',
+ 'lua-typo' => 'etex',
'lua-ul' => 'etex',
'luaindex' => 'lualatex --shell-escape',
'luatexja' => 'lualatex',
diff --git a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
index eddfda20acf..b6d19b61325 100644
--- a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
@@ -23,6 +23,7 @@ depend enigma
depend innerscript
depend interpreter
depend kanaparser
+depend lua-typo
depend lua-uca
depend lua-uni-algos
depend lua-ul
diff --git a/Master/tlpkg/tlpsrc/lua-typo.tlpsrc b/Master/tlpkg/tlpsrc/lua-typo.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/lua-typo.tlpsrc