From db8bc0b6f5708407c17023d40e21370b354fc185 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 14 Apr 2023 20:30:42 +0000 Subject: lua-typo (14apr23) git-svn-id: svn://tug.org/texlive/trunk@66848 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/lualatex/lua-typo/README.md | 4 + .../doc/lualatex/lua-typo/lua-typo-demo.pdf | Bin 16859 -> 16859 bytes .../doc/lualatex/lua-typo/lua-typo-fr.ltx | 2 +- .../doc/lualatex/lua-typo/lua-typo-fr.pdf | Bin 106404 -> 106400 bytes .../texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx | 2 +- .../texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf | Bin 179022 -> 205259 bytes .../source/lualatex/lua-typo/lua-typo.dtx | 561 ++++---- .../tex/lualatex/lua-typo/lua-typo-2023-03-08.sty | 1345 ++++++++++++++++++++ .../texmf-dist/tex/lualatex/lua-typo/lua-typo.sty | 446 +++---- 9 files changed, 1891 insertions(+), 469 deletions(-) create mode 100644 Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/README.md b/Master/texmf-dist/doc/lualatex/lua-typo/README.md index 9577fee1cf0..82c1c9b0c49 100644 --- a/Master/texmf-dist/doc/lualatex/lua-typo/README.md +++ b/Master/texmf-dist/doc/lualatex/lua-typo/README.md @@ -88,6 +88,10 @@ Changes on top of next page. - code cleaning. +* v.0.70: + - Options handled via `ltkeys` instead of `kvoptions`. + - code cleaning, bug fixes. + -- Copyright 2020--2023 Daniel Flipo E-mail: daniel (dot) flipo (at) free (dot) fr diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf index 35fe6bc9070..a7b1d9a40f2 100644 Binary files a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf and b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf differ 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 index 6a4abf1f8a9..e20f2b6df4f 100644 --- a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx +++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.ltx @@ -16,7 +16,7 @@ \frenchsetup{og=«, fg=»} \usepackage[ShortPages, OverfullLines, UnderfullLines, - Widows, Orphans, EOPHyphens, RepeatedHyphens + EOPHyphens, RepeatedHyphens, % Widows, Orphans, ]{lua-typo} \renewcommand*\descriptionlabel[1]{% \hspace{\labelsep}\texttt{#1}} 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 index ed50a38d11e..cd64bdaf454 100644 Binary files a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf and b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf 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 index 94d5ad0b56e..041d844c9f4 100644 --- a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx +++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.ltx @@ -15,7 +15,7 @@ \usepackage{babel,varioref} \usepackage[ShortPages, OverfullLines, UnderfullLines, - Widows, Orphans, EOPHyphens, RepeatedHyphens + EOPHyphens, RepeatedHyphens, % Widows, Orphans, ]{lua-typo} \renewcommand*\descriptionlabel[1]{% \hspace{\labelsep}\texttt{#1}} diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf index 6990971202e..537414c80de 100644 Binary files a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf and b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf 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 index 41a9966280c..dfcc363cd4e 100644 --- a/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx +++ b/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx @@ -72,7 +72,7 @@ %<+docfr>\frenchsetup{og=«, fg=»} \usepackage[ShortPages, OverfullLines, UnderfullLines, - Widows, Orphans, EOPHyphens, RepeatedHyphens + EOPHyphens, RepeatedHyphens, % Widows, Orphans, ]{lua-typo} \renewcommand*\descriptionlabel[1]{% \hspace{\labelsep}\texttt{#1}} @@ -325,9 +325,8 @@ % exactement les réglages internes, il se trouve normalement dans le % répertoire \texdir{texmfdist} des distributions TeXLive, MikTeX, etc. % L’utilisateur a la possibilité de recopier ce fichier soit dans son -% répertoire de travail pour un document particulier, soit dans son -% répertoire \texdir{texmfhome} ou \texdir{texmflocal} et de le -% personnaliser comme il l’entend. +% répertoire de travail, soit dans son répertoire \texdir{texmfhome} +% ou \texdir{texmflocal} et de le personnaliser comme il l’entend. % % Voici la liste complète des paramètres personnalisables avec leur % valeur par défaut, leurs noms sont systématiquement préfixés par @@ -374,9 +373,11 @@ % mot) au début ou à la fin de deux lignes consécutives % déclenchant l’avertissement. % Avec ce réglage (3 et 4), seront détectées deux lignes se -% terminant par « cible» et « invincible » (quatre lettres en -% commun), ainsi que la présence de « mon » en début ou fin de -% deux lignes consécutives (trois lettres en commun). +% terminant par « cible » et « irrésistible » ou +% « irrésistible-(ment) » (quatre lettres en commun), ainsi que la +% présence de « mon » en début ou fin de deux lignes consécutives +% (trois lettres en commun), mais « mon » et « mont » en début de +% ligne échappent à la détection. % % \item[EOLShortWords:] cette option signale la présence en fin de % ligne de mots très courts (une ou deux lettres) @@ -425,7 +426,7 @@ % \luatypoSetColor9{red} % Page presque vide (qq. lignes) % \luatypoSetColor{10}{LTred} % Répétitions en début de ligne % \luatypoSetColor{11}{LTred} % Répétitions en fin de ligne -% \luatypoSetColor{12}{LTgrey}% Dernière ligne d’alinéa presque pleine +% \luatypoSetColor{12}{LTgrey}% Dernière ligne alinéa presque pleine % \luatypoSetColor{13}{cyan} % Note de bas de page éclatée % \luatypoSetColor{14}{red} % Mot de fin de phrase en haut de page % \end{verbatim} @@ -719,7 +720,7 @@ %<+scan>\ProvidesPackage{scan-page} %<+dtx>\ProvidesFile{lua-typo.dtx} %<*dtx|sty|scan> - [2023-03-08 v.0.65 Daniel Flipo] + [2023-04-12 v.0.70 Daniel Flipo] % %<*sty> % \fi @@ -731,18 +732,13 @@ % mechanism to provide easier backward compatibility. % Rollback version~0.40 is provided for users who would have % a LaTeX kernel older than 2021/06/01. +% Rollback version~0.65 is provided for users who would have +% a LaTeX kernel older than 2022/06/01. % % \begin{macrocode} -\ifdefined\DeclareRelease - \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty} - \DeclareCurrentRelease{}{2023-03-08} -\else - \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide - access\MessageBreak to former versions of the lua-typo package.% - \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated% - \MessageBreak 2020-01-01 or newer; reported} -\fi -\NeedsTeXFormat{LaTeX2e}[2021/06/01] +\DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty} +\DeclareRelease{v0.65}{2023-03-08}{lua-typo-2023-03-08.sty} +\DeclareCurrentRelease{}{2023-04-12} % \end{macrocode} % % This package only runs with LuaLaTeX and requires packages @@ -751,8 +747,7 @@ % % \begin{macrocode} \ifdefined\directlua - \RequirePackage{luatexbase,luacode,luacolor} - \RequirePackage{kvoptions,atveryend} + \RequirePackage{luatexbase,luacode,luacolor,atveryend} \else \PackageError{This package is meant for LuaTeX only! Aborting} {No more information available, sorry!} @@ -784,46 +779,44 @@ 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} -\DeclareBoolOption[false]{FootnoteSplit} -\DeclareBoolOption[false]{ShortFinalWord} -% \end{macrocode} +% Set up \pkg{ltkeys} initializations. % Option \opt{All} resets all booleans relative to specific % typographic checks to \opt{true}. -% \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 \LT@FootnoteSplittrue - \LT@ShortFinalWordtrue -} -\ProcessKeyvalOptions{luatypo} +% +% \changes{v0.70}{2023/04/08}{Package options no longer require +% `kvoptions’, they rely on LaTeX `ltkeys’ package.} +% +% \begin{macrocode} +\DeclareKeys[luatypo] + { + ShowOptions.if = LT@ShowOptions , + None.if = LT@None , + BackParindent.if = LT@BackParindent , + ShortLines.if = LT@ShortLines , + ShortPages.if = LT@ShortPages , + OverfullLines.if = LT@OverfullLines , + UnderfullLines.if = LT@UnderfullLines , + Widows.if = LT@Widows , + Orphans.if = LT@Orphans , + EOPHyphens.if = LT@EOPHyphens , + RepeatedHyphens.if = LT@RepeatedHyphens , + ParLastHyphen.if = LT@ParLastHyphen , + EOLShortWords.if = LT@EOLShortWords , + FirstWordMatch.if = LT@FirstWordMatch , + LastWordMatch.if = LT@LastWordMatch , + FootnoteSplit.if = LT@FootnoteSplit , + ShortFinalWord.if = LT@ShortFinalWord , + All.if = LT@All , + All.code = \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 \LT@FootnoteSplittrue + \LT@ShortFinalWordtrue + } +\ProcessKeyOptions[luatypo] % \end{macrocode} % % Forward these options to the |luatypo| global table. @@ -941,6 +934,7 @@ luatypo = { } \MessageBreak ShowOptions [false]\MessageBreak None [false]\MessageBreak + All [false]\MessageBreak BackParindent [false]\MessageBreak ShortLines [false]\MessageBreak ShortPages [false]\MessageBreak @@ -974,6 +968,10 @@ luatypo = { } luatypo.Stretch = tex.count.luatypoStretchMax luatypo.MinFull = tex.count.luatypoMinFull luatypo.MinPart = tex.count.luatypoMinPart +% \end{macrocode} +% Ensure |MinFull|$\leq$|MinPart|. +% \begin{macrocode} + luatypo.MinFull = math.min(luatypo.MinPart,luatypo.MinFull) luatypo.MinLen = tex.count.luatypoMinLen luatypo.LLminWD = tex.dimen.luatypoLLminWD luatypo.BackPI = tex.dimen.luatypoBackPI @@ -1169,10 +1167,11 @@ local traverse_id = node.traverse_id local has_field = node.has_field local uses_font = node.uses_font local is_glyph = node.is_glyph +local utf8_len = utf8.len % \end{macrocode} % Local definitions from the `unicode.utf8’ library: replacements are -% needed for functions |string.gsub()|, |string.find()| and -% |string.reverse()| which are meant for one-byte characters only. +% needed for functions |string.gsub()|, |string.sub()|, |string.find()| +% and |string.reverse()| which are meant for one-byte characters only. % % \changes{v0.65}{2023/03/02}{Three new functions for utf-8 strings’ % manipulations.} @@ -1192,7 +1191,7 @@ local utf8_gsub = unicode.utf8.gsub % end to beginning) [same as |string.reverse| but for utf-8 strings]. % \begin{macrocode} local utf8_reverse = function (s) - if utf8.len(s) > 1 then + if utf8_len(s) > 1 then local so = "" for p, c in utf8.codes(s) do so = utf8.char(c) .. so @@ -1202,6 +1201,16 @@ local utf8_reverse = function (s) return s end % \end{macrocode} +% |utf8_sub| returns the substring of s that starts at i and +% continues until j (j-i-1 utf8 chars.). \emph{Warning: it requires +% $i\ge1$ and $j\ge i$}. +% \begin{macrocode} +local utf8_sub = function (s,i,j) + i=utf8.offset(s,i) + j=utf8.offset(s,j+1)-1 + return string.sub(s,i,j) +end +% \end{macrocode} % %\changes{v0.32}{2021/03/14}{Better protection against unexpected % nil nodes.} @@ -1316,13 +1325,15 @@ end % and ligatures, dicretionnaries other than ligatures, kerns % (letterspacing) should be discarded. % For each word to be compared we build a ``signature’’ made of -% glyphs and split ligatures. +% glyphs, split ligatures and underscores (representing glues). % % \changes{v0.65}{2023/03/02}{All ligatures are now split using the % node’s `components’ field rather than a table.} % -% The first function adds a node to a signature of type string. -% It returns the augmented string and its length. +% The first function adds a (non-nil) node to a signature of type +% string, nil nodes are ignored. +% It returns the augmented string and its length +% (underscores are omitted in the length computation). % The last argument is a boolean needed when building a signature % backwards (see |check_line_last_word|). % \begin{macrocode} @@ -1331,12 +1342,12 @@ local signature = function (node, string, swap) local str = string if n and n.id == GLYPH then local b = n.char - if b and not char_to_discard[b] then % \end{macrocode} % Punctuation has to be discarded; other glyphs may be ligatures, % then they have a |components| field which holds the list of glyphs % which compose the ligature. % \begin{macrocode} + if b and not char_to_discard[b] then if n.components then local c = "" for nn in traverse_id(GLYPH, n.components) do @@ -1384,14 +1395,14 @@ local signature = function (node, string, swap) else str = str .. c1 .. c2 end + elseif n and n.id == GLUE then + str = str .. "_" end % \end{macrocode} % The returned length is the number of \emph{letters}. % \begin{macrocode} - local len = utf8.len(str) - if utf8_find(str, "_") then - len = len - 1 - end + local s = utf8_gsub(str, "_", "") + local len = utf8_len(s) return len, str end % \end{macrocode} @@ -1414,17 +1425,22 @@ end % \changes{v0.61}{2023/02/06}{`check\_line\_last\_word’ returns a flag % to set pageflag.} % +% \changes{v0.70}{2023/04/08}{`check\_line\_first\_word’ and +% `check\_line\_last\_word’: length of matches corrected.} +% % \begin{macrocode} local check_line_last_word = function (old, node, line, colno, flag) local COLOR = luatypo.colortbl[11] local match = false local new = "" local maxlen = 0 + local MinFull = luatypo.MinFull + local MinPart = luatypo.MinPart if node then local swap = true local box, go % \end{macrocode} -% Step back to the last glyph or discretionary. +% Step back to the last glyph or discretionary or hbox. % \begin{macrocode} local lastn = node while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC and @@ -1432,39 +1448,44 @@ local check_line_last_word = function (old, node, line, colno, flag) lastn = lastn.prev end % \end{macrocode} -% A signature is built from the last two words on the current line. +% A signature is built from the last two (or more) words +% on the current line. % \begin{macrocode} local n = lastn - if n and n.id == HLIST then - box = n - prev = n.prev - lastn = slide(n.head) - n = lastn - end - while n and n.id ~= GLUE do - maxlen, new = signature (n, new, swap) - n = n.prev - end - if n and n.id == GLUE then - new = new .. "_" - go = true - elseif box and not n then - local p = box.prev - if p.id == GLUE then - new = new .. "_" - n = p - else - n = box - end - go = true - end - if go then - repeat - n = n.prev - maxlen, new = signature (n, new, swap) - until not n or n.id == GLUE + local words = 0 + while n and (words <= 2 or maxlen < MinPart) do +% \end{macrocode} +% Go down inside boxes, read their content from end to beginning, +% then step out. +% \begin{macrocode} + if n and n.id == HLIST then + box = n + local first = n.head + local lastn = slide(first) + n = lastn + while n do + maxlen, new = signature (n, new, swap) + n = n.prev + end + n = box.prev + local w = utf8_gsub(new, "_", "") + words = words + utf8_len(new) - utf8_len(w) + 1 + else + repeat + maxlen, new = signature (n, new, swap) + n = n.prev + until not n or n.id == GLUE or n.id == HLIST + if n and n.id == GLUE then + maxlen, new = signature (n, new, swap) + words = words + 1 + n = n.prev + end + end end new = utf8_reverse(new) + new = utf8_gsub(new, "_+$", "") -- $ + new = utf8_gsub(new, "^_+", "") + maxlen = math.min(utf8_len(old), utf8_len(new)) % texio.write_nl("EOLsigold=" .. old) % texio.write(" EOLsig=" .. new) % \end{macrocode} @@ -1472,82 +1493,95 @@ local check_line_last_word = function (old, node, line, colno, flag) % the last word’s signature, but doesn’t compare it with the % previous line’s. % \begin{macrocode} - if flag then - local MinFull = luatypo.MinFull - local MinPart = luatypo.MinPart - MinFull = math.min(MinPart,MinFull) - local k = MinPart - local dlo = utf8_reverse(old) - local wen = utf8_reverse(new) - local oldlast = utf8_gsub (old, ".*_", "_") - local newlast = utf8_gsub (new, ".*_", "_") - local i - if utf8_find(newlast, "_") then - i = utf8.len(newlast) - end - if i and i > maxlen - MinPart + 1 then - k = MinPart + 1 - end + if flag and old ~= "" then +% \end{macrocode} +% |oldlast| and |newlast| hold the last (full) words to be compared +% later: +% \begin{macrocode} + local oldlast = utf8_gsub (old, ".*_", "") + local newlast = utf8_gsub (new, ".*_", "") +% \end{macrocode} +% Let’s look for a partial match: build |oldsub| and |newsub|, +% reading (backwards) the last |MinPart| \emph{non-space} characters +% of both lines. +% \begin{macrocode} local oldsub = "" local newsub = "" + local dlo = utf8_reverse(old) + local wen = utf8_reverse(new) for p, c in utf8.codes(dlo) do - if utf8.len(oldsub) < k then + local s = utf8_gsub(oldsub, "_", "") + if utf8_len(s) < MinPart then oldsub = utf8.char(c) .. oldsub end end for p, c in utf8.codes(wen) do - if utf8.len(newsub) < k then + local s = utf8_gsub(newsub, "_", "") + if utf8_len(s) < MinPart then newsub = utf8.char(c) .. newsub end end - local l = utf8.len(new) - if oldsub == newsub and l >= k then + if oldsub == newsub then % texio.write_nl("EOLnewsub=" .. newsub) match = true - elseif oldlast == newlast and utf8.len(newlast) > MinFull then + end + if oldlast == newlast and utf8_len(newlast) >= MinFull then % texio.write_nl("EOLnewlast=" .. newlast) + if utf8_len(newlast) > MinPart or not match then + oldsub = oldlast + newsub = newlast + end match = true - oldsub = oldlast - newsub = newlast - k = utf8.len(newlast) end if match then % \end{macrocode} -% Minimal partial match; any more glyphs matching? +% Minimal full or partial match |newsub| of length |k|; +% any more glyphs matching? % \begin{macrocode} - local osub = oldsub - local nsub = newsub - while osub == nsub and k <= maxlen do - k = k +1 - osub = string.sub(old,-k) - nsub = string.sub(new,-k) + local k = utf8_len(newsub) + local osub = utf8_reverse(oldsub) + local nsub = utf8_reverse(newsub) + while osub == nsub and k < maxlen do + k = k + 1 + osub = utf8_sub(dlo,1,k) + nsub = utf8_sub(wen,1,k) if osub == nsub then - newsub = nsub + newsub = utf8_reverse(nsub) end end - newsub = utf8_gsub(newsub, "^_", "") + newsub = utf8_gsub(newsub, "^_+", "") % texio.write_nl("EOLfullmatch=" .. newsub) local msg = "E.O.L. MATCH=" .. newsub log_flaw(msg, line, colno, footnote) % \end{macrocode} % Lest’s colour the matching string. % \begin{macrocode} + local ns = utf8_gsub(newsub, "_", "") + k = utf8_len(ns) oldsub = utf8_reverse(newsub) local newsub = "" local n = lastn - repeat - if n and n.id ~= GLUE then + local l = 0 + local lo = 0 + local li = 0 + while n and newsub ~= oldsub and l < k do + if n and n.id == HLIST then + local first = n.head + for nn in traverse_id(GLYPH, first) do + color_node(nn, COLOR) + local c = nn.char + if not char_to_discard[c] then l = l + 1 end + end +% texio.write_nl("l (box)=" .. l) + elseif n then color_node(n, COLOR) - l, newsub = signature(n, newsub, swap) - elseif n and n.id == GLUE then - newsub = newsub .. "_" - elseif not n and box then - n = box - else - break + li, newsub = signature(n, newsub, swap) + l = l + li - lo + lo = li +% texio.write_nl("l=" .. l) end n = n.prev - until newsub == oldsub or l >= k + end end end end @@ -1568,41 +1602,42 @@ local check_line_first_word = function (old, node, line, colno, flag) local swap = false local new = "" local maxlen = 0 + local MinFull = luatypo.MinFull + local MinPart = luatypo.MinPart local n = node local box, go while n and n.id ~= GLYPH and n.id ~= DISC and (n.id ~= HLIST or n.subtype == INDENT) do n = n.next end - local start = n - if n and n.id == HLIST then - box = n - start = n.head - n = n.head - end - while n and n.id ~= GLUE do - maxlen, new = signature (n, new, swap) - n = n.next - end - if n and n.id == GLUE then - new = new .. "_" - go = true - elseif box and not n then - local bn = box.next - if bn.id == GLUE then - new = new .. "_" - n = bn - else - n = box - end - go = true - end - if go then - repeat - n = n.next - maxlen, new = signature (n, new, swap) - until not n or n.id == GLUE + start = n + local words = 0 + while n and (words <= 2 or maxlen < MinPart) do + if n and n.id == HLIST then + box = n + n = n.head + while n do + maxlen, new = signature (n, new, swap) + n = n.next + end + n = box.next + local w = utf8_gsub(new, "_", "") + words = words + utf8_len(new) - utf8_len(w) + 1 + else + repeat + maxlen, new = signature (n, new, swap) + n = n.next + until not n or n.id == GLUE or n.id == HLIST + if n and n.id == GLUE then + maxlen, new = signature (n, new, swap) + words = words + 1 + n = n.next + end + end end + new = utf8_gsub(new, "_+$", "") -- $ + new = utf8_gsub(new, "^_+", "") + maxlen = math.min(utf8_len(old), utf8_len(new)) % texio.write_nl("BOLsigold=" .. old) % texio.write(" BOLsig=" .. new) % \end{macrocode} @@ -1610,77 +1645,82 @@ local check_line_first_word = function (old, node, line, colno, flag) % the first word’s signature, but doesn’t compare it with the % previous line’s. % \begin{macrocode} - if flag then - local MinFull = luatypo.MinFull - local MinPart = luatypo.MinPart - MinFull = math.min(MinPart,MinFull) - local k = MinPart - local oldfirst = utf8_gsub (old, "_.*", "_") - local newfirst = utf8_gsub (new, "_.*", "_") - local i - if utf8_find(newfirst, "_") then - i = utf8.len(newfirst) - end - if i and i <= MinPart then - k = MinPart + 1 - end + if flag and old ~= "" then + local oldfirst = utf8_gsub (old, "_.*", "") + local newfirst = utf8_gsub (new, "_.*", "") local oldsub = "" local newsub = "" for p, c in utf8.codes(old) do - if utf8.len(oldsub) < k then oldsub = oldsub .. utf8.char(c) end + local s = utf8_gsub(oldsub, "_", "") + if utf8_len(s) < MinPart then + oldsub = oldsub .. utf8.char(c) + end end for p, c in utf8.codes(new) do - if utf8.len(newsub) < k then newsub = newsub .. utf8.char(c) end + local s = utf8_gsub(newsub, "_", "") + if utf8_len(s) < MinPart then + newsub = newsub .. utf8.char(c) + end end - local l = utf8.len(newsub) - if oldsub == newsub and l >= k then + if oldsub == newsub then % texio.write_nl("BOLnewsub=" .. newsub) match = true - elseif oldfirst == newfirst and utf8.len(newfirst) > MinFull then + end + if oldfirst == newfirst and utf8_len(newfirst) >= MinFull then % texio.write_nl("BOLnewfirst=" .. newfirst) + if utf8_len(newfirst) > MinPart or not match then + oldsub = oldfirst + newsub = newfirst + end match = true - oldsub = oldfirst - newsub = newfirst - k = utf8.len(newfirst) end if match then % \end{macrocode} -% Minimal partial match; any more glyphs matching? +% Minimal full or partial match |newsub| of length |k|; +% any more glyphs matching? % \begin{macrocode} + local k = utf8_len(newsub) local osub = oldsub local nsub = newsub - while osub == nsub and k <= maxlen do + while osub == nsub and k < maxlen do k = k + 1 - osub = string.sub(old,1,k) - nsub = string.sub(new,1,k) + osub = utf8_sub(old,1,k) + nsub = utf8_sub(new,1,k) if osub == nsub then newsub = nsub end end - newsub = utf8_gsub(newsub, "_$", "") --$ + newsub = utf8_gsub(newsub, "_+$", "") --$ % texio.write_nl("BOLfullmatch=" .. newsub) local msg = "B.O.L. MATCH=" .. newsub log_flaw(msg, line, colno, footnote) % \end{macrocode} % Lest’s colour the matching string. % \begin{macrocode} + local ns = utf8_gsub(newsub, "_", "") + k = utf8_len(ns) oldsub = newsub local newsub = "" - local k = utf8.len(oldsub) local n = start - repeat - if n and n.id ~= GLUE then + local l = 0 + local lo = 0 + local li = 0 + while n and newsub ~= oldsub and l < k do + if n and n.id == HLIST then + local nn = n.head + for nnn in traverse(nn) do + color_node(nnn, COLOR) + local c = nn.char + if not char_to_discard[c] then l = l + 1 end + end + elseif n then color_node(n, COLOR) - l, newsub = signature(n, newsub, swap) - elseif n and n.id == GLUE then - newsub = newsub .. "_" - elseif not n and box then - n = box - else - break + li, newsub = signature(n, newsub, swap) + l = l + li - lo + lo = li end n = n.next - until newsub == oldsub or l >= k + end end end return new, match @@ -1689,9 +1729,10 @@ end % % \changes{v0.65}{2023/03/02}{New `check\_page\_first\_word’ function.} % +% \enlargethispage*{\baselineskip} % The next function checks the first word on a new page: if -% it ends a sentence and is short (up to |luatypoMinLen| characters), -% the function returns |true| and colors the offending word. +% it ends a sentence and is short (up to |\luatypoMinLen| characters), +% the function returns |true| and colours the offending word. % Otherwise it just retrurs |false|. % The function requires two arguments: the line’s first node and % a column number (possibly |nil|). @@ -1702,7 +1743,7 @@ local check_page_first_word = function (node, colno) local match = false local swap = false local new = "" - local maxlen = luatypo.MinLen + local minlen = luatypo.MinLen local len = 0 local n = node local pn @@ -1718,7 +1759,7 @@ local check_page_first_word = function (node, colno) repeat len, new = signature (n, new, swap) n = n.next - until len > maxlen or (n and n.id == GLYPH and eow_char[n.char]) or + until len > minlen or (n and n.id == GLYPH and eow_char[n.char]) or (n and n.id == GLUE) or (n and n.id == KERN and n.subtype == 1) % \end{macrocode} @@ -1729,7 +1770,7 @@ local check_page_first_word = function (node, colno) pn = n n = n.next end - if len <= maxlen and n and n.id == GLYPH and eow_char[n.char] then + if len <= minlen and n and n.id == GLYPH and eow_char[n.char] then match =true if pn and (pn.id == GLUE or pn.id == KERN) then new = new .. " " @@ -1900,6 +1941,33 @@ end % \end{macrocode} % \end{macro} % +% \begin{macro}{check-EOP} +% This function looks ahead of |node| in search of a page end +% or a footnote rule and returns the flags |page_bottom| and +% |body_bottom| [used in text and display math lines]. +% \begin{macrocode} +local check_EOP = function (node) + local n = node + local page_bot = false + local body_bot = false + while n and (n.id == GLUE or n.id == PENALTY or + n.id == WHATSIT ) do + n = n.next + end + if not n then + page_bot = true + body_bot = true + elseif footnoterule_ahead(n) then + body_bot = true +% texio.write_nl("=> FOOTNOTE RULE ahead") +% texio.write_nl("check_vtop: last line before footnotes") +% texio.write_nl(" ") + end + return page_bot, body_bot +end +% \end{macrocode} +% \end{macro} +% % \begin{macro}{get-pagebody} % The next function scans the \node{vlist}s on the current % page in search of the page body. @@ -1970,7 +2038,8 @@ end % if a two columns box starts.} % % \begin{macrocode} -check_vtop = function (head, colno, vpos) +check_vtop = function (top, colno, vpos) + local head = top.list local PAGEmin = luatypo.PAGEmin local HYPHmax = luatypo.HYPHmax local LLminWD = luatypo.LLminWD @@ -2045,20 +2114,7 @@ check_vtop = function (head, colno, vpos) % This has to be known early in order to set the flags |orphanflag| % and |ftnsplit|. % \begin{macrocode} - local n = nextnode - while n and (n.id == GLUE or n.id == PENALTY or - n.id == WHATSIT ) do - n = n.next - end - if not n then - page_bottom = true - body_bottom = true - elseif footnoterule_ahead(n) then - body_bottom = true -% texio.write_nl("=> FOOTNOTE RULE ahead") -% texio.write_nl("check_vtop: last line before footnotes") -% texio.write_nl(" ") - end + page_bottom, body_bottom = check_EOP (nextnode) % \end{macrocode} % Is the current line overfull or underfull? % \changes{v0.50}{2021/05/13}{Detection of overfull boxes fixed: the @@ -2224,7 +2280,7 @@ check_vtop = function (head, colno, vpos) % (except lists)? % \begin{macrocode} if FirstWordMatch then - local flag = not ListItem + local flag = not ListItem and (line > 1) firstwd, flag = check_line_first_word(firstwd, first, line, colno, flag) if flag then @@ -2236,11 +2292,12 @@ check_vtop = function (head, colno, vpos) % \begin{macrocode} if LastWordMatch then local flag = true - if PFskip > BackPI then + if PFskip > BackPI or line == 1 then flag = false end + local pnp = pn.prev lastwd, flag = - check_line_last_word(lastwd, pn, line, colno, flag) + check_line_last_word(lastwd, pnp, line, colno, flag) if flag then pageflag = true end @@ -2293,7 +2350,7 @@ check_vtop = function (head, colno, vpos) if LastWordMatch then local flag = true lastwd, flag = - check_line_last_word(lastwd, ln, line, colno, flag) + check_line_last_word(lastwd, pn, line, colno, flag) if flag then pageflag = true end @@ -2412,6 +2469,11 @@ check_vtop = function (head, colno, vpos) line = pageline end % \end{macrocode} +% Is this line the last one on the page or before footnotes? +% (information needed to set the |pageshort| flag). +% \begin{macrocode} + page_bottom, body_bottom = check_EOP (nextnode) +% \end{macrocode} % Let’s check for an ``Overfull box’’. For a displayed equation % it is straightforward. A set of aligned equations all have the % same (maximal) width; in order to avoid highlighting the whole @@ -2451,15 +2513,17 @@ check_vtop = function (head, colno, vpos) end end elseif head and head.id == RULE and head.subtype == 0 then + vpos = vpos + head.height + head.depth % \end{macrocode} -% This is a \node{rule}, possibly a footnote rule. +% This is a \node{rule}, possibly a footnote rule. It has most likely +% been detected on the previous line (then |body_bottom=true|) but +% might have no text before (footnote-only page!). % \begin{macrocode} - vpos = vpos + head.height + head.depth - if body_bottom then + local prev = head.prev + if body_bottom or footnoterule_ahead (prev) then % \end{macrocode} -% If a |\footnoterule| has been detected on the previous run, -% set the |footnote| flag and reset some counters and flags -% for the coming footnote lines. +% If it is, set the |footnote| flag and reset some counters +% and flags for the coming footnote lines. % \begin{macrocode} % texio.write_nl("check_vtop: footnotes start") % texio.write_nl(" ") @@ -2489,10 +2553,9 @@ check_vtop = function (head, colno, vpos) % texio.write(" textheight=" .. tht_pt) % end % texio.write_nl(" ") - if pageline > 1 and pageline < PAGEmin and ShortPages then + if pageline > 1 and pageline < PAGEmin + and vpos < vpos_min and ShortPages then pageshort = true - end - if pageshort and vpos < vpos_min then pageflag = true local msg = "SHORT PAGE: only " .. pageline .. " lines" log_flaw(msg, line, colno, footnote) @@ -2509,7 +2572,7 @@ check_vtop = function (head, colno, vpos) % \end{macrocode} % Increment |vpos| on other vertical glues. % \begin{macrocode} - vpos = vpos + effective_glue(head,body) + vpos = vpos + effective_glue(head,top) elseif head.id == KERN and head.subtype == 1 then % \end{macrocode} % This is a vertical kern, let’s update |vpos|. @@ -2614,7 +2677,7 @@ luatypo.check_page = function (head) % texio.write("-" .. top.subtype) % texio.write(" wd=" .. boxwd .. " ht=" .. boxht) % texio.write_nl(" ") - local next = check_vtop(first,colno,vpos) + local next = check_vtop(top,colno,vpos) if next then top = next elseif top then @@ -2630,9 +2693,8 @@ luatypo.check_page = function (head) % texio.write_nl("**MULTICOL type1:") % texio.write_nl(" ") colno = 0 - for n in traverse_id(VLIST, first) do + for col in traverse_id(VLIST, first) do colno = colno + 1 - col = n.list % texio.write_nl("Start of col." .. colno) % texio.write_nl(" ") check_vtop(col,colno,vpos) @@ -2658,9 +2720,8 @@ luatypo.check_page = function (head) colno = 0 for n in traverse_id(HLIST, first) do colno = colno + 1 - local nn = n.list - if nn and nn.list then - col = nn.list + local col = n.list + if col and col.list then % texio.write_nl("Start of col." .. colno) % texio.write_nl(" ") check_vtop(col,colno,vpos) @@ -2739,7 +2800,7 @@ return luatypo.check_page \luatypoStretchMax=200\relax \luatypoHyphMax=2\relax \luatypoPageMin=5\relax - \luatypoMinFull=4\relax + \luatypoMinFull=3\relax \luatypoMinPART=4\relax \luatypoMinLen=4\relax }% diff --git a/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty new file mode 100644 index 00000000000..aa876b82fa9 --- /dev/null +++ b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo-2023-03-08.sty @@ -0,0 +1,1345 @@ +%% +%% This is file `lua-typo.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% lua-typo.dtx (with options: `sty') +%% +%% IMPORTANT NOTICE: +%% For the copyright see the source file `lua-typo.dtx’. +%% +\NeedsTeXFormat{LaTeX2e}[2021/06/01] +\ProvidesPackage{lua-typo} + [2023-03-08 v.0.65 Daniel Flipo] +\ifdefined\DeclareRelease + \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty} + \DeclareCurrentRelease{}{2023-03-08} +\else + \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide + access\MessageBreak to former versions of the lua-typo package.% + \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated% + \MessageBreak 2020-01-01 or newer; reported} +\fi +\NeedsTeXFormat{LaTeX2e}[2021/06/01] +\ifdefined\directlua + \RequirePackage{luatexbase,luacode,luacolor} + \RequirePackage{kvoptions,atveryend} +\else + \PackageError{This package is meant for LuaTeX only! Aborting} + {No more information available, sorry!} +\fi +\newdimen\luatypoLLminWD +\newdimen\luatypoBackPI +\newdimen\luatypoBackFuzz +\newcount\luatypoStretchMax +\newcount\luatypoHyphMax +\newcount\luatypoPageMin +\newcount\luatypoMinFull +\newcount\luatypoMinPart +\newcount\luatypoMinLen +\newcount\luatypo@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} +\DeclareBoolOption[false]{FootnoteSplit} +\DeclareBoolOption[false]{ShortFinalWord} +\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 \LT@FootnoteSplittrue + \LT@ShortFinalWordtrue +} +\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@FootnoteSplit + \advance\luatypo@options by 1 + \directlua{ luatypo.FootnoteSplit = true }% + \else + \directlua{ luatypo.FootnoteSplit = false }% + \fi + \ifLT@ShortFinalWord + \advance\luatypo@options by 1 + \directlua{ luatypo.ShortFinalWord = true }% + \else + \directlua{ luatypo.ShortFinalWord = 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 + FootnoteSplit [false]\MessageBreak + ShortFinalWord [false]\MessageBreak + \MessageBreak + *********************************************% + \MessageBreak Lua-typo [ShowOptions] + }% +\fi +\AtBeginDocument{% + \directlua{ + luatypo.HYPHmax = tex.count.luatypoHyphMax + luatypo.PAGEmin = tex.count.luatypoPageMin + luatypo.Stretch = tex.count.luatypoStretchMax + luatypo.MinFull = tex.count.luatypoMinFull + luatypo.MinPart = tex.count.luatypoMinPart + luatypo.MinLen = tex.count.luatypoMinLen + luatypo.LLminWD = tex.dimen.luatypoLLminWD + luatypo.BackPI = tex.dimen.luatypoBackPI + luatypo.BackFuzz = tex.dimen.luatypoBackFuzz + }% +} +\AtVeryEndDocument{% +\ifnum\luatypo@options = 0 \LT@Nonetrue \fi +\ifLT@None + \directlua{ + texio.write_nl(' ') + texio.write_nl('***********************************') + texio.write_nl('*** lua-typo loaded with NO option:') + texio.write_nl('*** NO CHECK PERFORMED! ***') + texio.write_nl('***********************************') + texio.write_nl(' ') + }% +\else + \directlua{ + texio.write_nl(' ') + texio.write_nl('*************************************') + if luatypo.pagelist == " " then + texio.write_nl('*** lua-typo: No Typo Flaws found.') + else + texio.write_nl('*** lua-typo: WARNING *************') + texio.write_nl('The following pages need attention:') + texio.write(luatypo.pagelist) + end + texio.write_nl('***********************************') + texio.write_nl(' ') + local fileout= tex.jobname .. ".typo" + local out=io.open(fileout,"w+") + out:write(luatypo.buffer) + io.close(out) + }% +\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 = utf8.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 = utf8.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 = " " +luatypo.buffer = "List of typographic flaws found for " + .. tex.jobname .. ".pdf:\string\n\string\n" + +local char_to_discard = { } +char_to_discard[string.byte(",")] = true +char_to_discard[string.byte(".")] = true +char_to_discard[string.byte("!")] = true +char_to_discard[string.byte("?")] = true +char_to_discard[string.byte(":")] = true +char_to_discard[string.byte(";")] = true +char_to_discard[string.byte("-")] = true + +local eow_char = { } +eow_char[string.byte(".")] = true +eow_char[string.byte("!")] = true +eow_char[string.byte("?")] = true +eow_char[utf8.codepoint("…")] = true + +local DISC = node.id("disc") +local GLYPH = node.id("glyph") +local GLUE = node.id("glue") +local KERN = node.id("kern") +local RULE = node.id("rule") +local HLIST = node.id("hlist") +local VLIST = node.id("vlist") +local LPAR = node.id("local_par") +local MKERN = node.id("margin_kern") +local PENALTY = node.id("penalty") +local WHATSIT = node.id("whatsit") +local USRSKIP = 0 +local PARSKIP = 3 +local LFTSKIP = 8 +local RGTSKIP = 9 +local TOPSKIP = 10 +local PARFILL = 15 +local LINE = 1 +local BOX = 2 +local INDENT = 3 +local ALIGN = 4 +local EQN = 6 +local USER = 0 +local HYPH = 0x2D +local LIGA = 0x102 +local parline = 0 +local dimensions = node.dimensions +local rangedimensions = node.rangedimensions +local effective_glue = node.effective_glue +local set_attribute = node.set_attribute +local slide = node.slide +local traverse = node.traverse +local traverse_id = node.traverse_id +local has_field = node.has_field +local uses_font = node.uses_font +local is_glyph = node.is_glyph +local utf8_find = unicode.utf8.find +local utf8_gsub = unicode.utf8.gsub +local utf8_reverse = function (s) + if utf8.len(s) > 1 then + local so = "" + for p, c in utf8.codes(s) do + so = utf8.char(c) .. so + end + s = so + end + return s +end +local color_node = function (node, color) + local attr = oberdiek.luacolor.getattribute() + if node and node.id == DISC then + local pre = node.pre + local post = node.post + local repl = node.replace + if pre then + set_attribute(pre,attr,color) + end + if post then + set_attribute(post,attr,color) + end + if repl then + set_attribute(repl,attr,color) + end + elseif node then + set_attribute(node,attr,color) + end +end +local color_line = function (head, color) + local first = head.head + for n in traverse(first) do + if n.id == HLIST or n.id == VLIST then + local ff = n.head + for nn in traverse(ff) do + if nn.id == HLIST or nn.id == VLIST then + local f3 = nn.head + for n3 in traverse(f3) do + if n3.id == HLIST or n3.id == VLIST then + local f4 = n3.head + for n4 in traverse(f4) do + if n4.id == HLIST or n4.id == VLIST then + local f5 = n4.head + for n5 in traverse(f5) do + if n5.id == HLIST or n5.id == VLIST then + local f6 = n5.head + for n6 in traverse(f6) do + color_node(n6, color) + end + else + color_node(n5, color) + end + end + else + color_node(n4, color) + end + end + else + color_node(n3, color) + end + end + else + color_node(nn, color) + end + end + else + color_node(n, color) + end + end +end +log_flaw= function (msg, line, colno, footnote) + local pageno = tex.getcount("c@page") + local prt ="p. " .. pageno + if colno then + prt = prt .. ", col." .. colno + end + if line then + local l = string.format("%2d, ", line) + if footnote then + prt = prt .. ", (ftn.) line " .. l + else + prt = prt .. ", line " .. l + end + end + prt = prt .. msg + luatypo.buffer = luatypo.buffer .. prt .. "\string\n" +end +local signature = function (node, string, swap) + local n = node + local str = string + if n and n.id == GLYPH then + local b = n.char + if b and not char_to_discard[b] then + if n.components then + local c = "" + for nn in traverse_id(GLYPH, n.components) do + c = c .. utf8.char(nn.char) + end + if swap then + str = str .. utf8_reverse(c) + else + str = str .. c + end + else + str = str .. utf8.char(b) + end + end + elseif n and n.id == DISC then + local pre = n.pre + local post = n.post + local c1 = "" + local c2 = "" + if pre and pre.char then + if pre.components then + for nn in traverse_id(GLYPH, post.components) do + c1 = c1 .. utf8.char(nn.char) + end + else + c1 = utf8.char(pre.char) + end + c1 = utf8_gsub(c1, "-", "") + end + if post and post.char then + if post.components then + for nn in traverse_id(GLYPH, post.components) do + c2 = c2 .. utf8.char(nn.char) + end + else + c2 = utf8.char(post.char) + end + end + if swap then + str = str .. utf8_reverse(c2) .. c1 + else + str = str .. c1 .. c2 + end + end + local len = utf8.len(str) + if utf8_find(str, "_") then + len = len - 1 + end + return len, str +end +local check_line_last_word = function (old, node, line, colno, flag) + local COLOR = luatypo.colortbl[11] + local match = false + local new = "" + local maxlen = 0 + if node then + local swap = true + local box, go + local lastn = node + while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC and + lastn.id ~= HLIST do + lastn = lastn.prev + end + local n = lastn + if n and n.id == HLIST then + box = n + prev = n.prev + lastn = slide(n.head) + n = lastn + end + while n and n.id ~= GLUE do + maxlen, new = signature (n, new, swap) + n = n.prev + end + if n and n.id == GLUE then + new = new .. "_" + go = true + elseif box and not n then + local p = box.prev + if p.id == GLUE then + new = new .. "_" + n = p + else + n = box + end + go = true + end + if go then + repeat + n = n.prev + maxlen, new = signature (n, new, swap) + until not n or n.id == GLUE + end + new = utf8_reverse(new) + if flag then + local MinFull = luatypo.MinFull + local MinPart = luatypo.MinPart + MinFull = math.min(MinPart,MinFull) + local k = MinPart + local dlo = utf8_reverse(old) + local wen = utf8_reverse(new) + local oldlast = utf8_gsub (old, ".*_", "_") + local newlast = utf8_gsub (new, ".*_", "_") + local i + if utf8_find(newlast, "_") then + i = utf8.len(newlast) + end + if i and i > maxlen - MinPart + 1 then + k = MinPart + 1 + end + local oldsub = "" + local newsub = "" + for p, c in utf8.codes(dlo) do + if utf8.len(oldsub) < k then + oldsub = utf8.char(c) .. oldsub + end + end + for p, c in utf8.codes(wen) do + if utf8.len(newsub) < k then + newsub = utf8.char(c) .. newsub + end + end + local l = utf8.len(new) + if oldsub == newsub and l >= k then + match = true + elseif oldlast == newlast and utf8.len(newlast) > MinFull then + match = true + oldsub = oldlast + newsub = newlast + k = utf8.len(newlast) + end + if match then + local osub = oldsub + local nsub = newsub + while osub == nsub and k <= maxlen do + k = k +1 + osub = string.sub(old,-k) + nsub = string.sub(new,-k) + if osub == nsub then + newsub = nsub + end + end + newsub = utf8_gsub(newsub, "^_", "") + local msg = "E.O.L. MATCH=" .. newsub + log_flaw(msg, line, colno, footnote) + oldsub = utf8_reverse(newsub) + local newsub = "" + local n = lastn + repeat + if n and n.id ~= GLUE then + color_node(n, COLOR) + l, newsub = signature(n, newsub, swap) + elseif n and n.id == GLUE then + newsub = newsub .. "_" + elseif not n and box then + n = box + else + break + end + n = n.prev + until newsub == oldsub or l >= k + end + end + end + return new, match +end +local check_line_first_word = function (old, node, line, colno, flag) + local COLOR = luatypo.colortbl[10] + local match = false + local swap = false + local new = "" + local maxlen = 0 + local n = node + local box, go + while n and n.id ~= GLYPH and n.id ~= DISC and + (n.id ~= HLIST or n.subtype == INDENT) do + n = n.next + end + local start = n + if n and n.id == HLIST then + box = n + start = n.head + n = n.head + end + while n and n.id ~= GLUE do + maxlen, new = signature (n, new, swap) + n = n.next + end + if n and n.id == GLUE then + new = new .. "_" + go = true + elseif box and not n then + local bn = box.next + if bn.id == GLUE then + new = new .. "_" + n = bn + else + n = box + end + go = true + end + if go then + repeat + n = n.next + maxlen, new = signature (n, new, swap) + until not n or n.id == GLUE + end + if flag then + local MinFull = luatypo.MinFull + local MinPart = luatypo.MinPart + MinFull = math.min(MinPart,MinFull) + local k = MinPart + local oldfirst = utf8_gsub (old, "_.*", "_") + local newfirst = utf8_gsub (new, "_.*", "_") + local i + if utf8_find(newfirst, "_") then + i = utf8.len(newfirst) + end + if i and i <= MinPart then + k = MinPart + 1 + end + local oldsub = "" + local newsub = "" + for p, c in utf8.codes(old) do + if utf8.len(oldsub) < k then oldsub = oldsub .. utf8.char(c) end + end + for p, c in utf8.codes(new) do + if utf8.len(newsub) < k then newsub = newsub .. utf8.char(c) end + end + local l = utf8.len(newsub) + if oldsub == newsub and l >= k then + match = true + elseif oldfirst == newfirst and utf8.len(newfirst) > MinFull then + match = true + oldsub = oldfirst + newsub = newfirst + k = utf8.len(newfirst) + end + if match then + local osub = oldsub + local nsub = newsub + while osub == nsub and k <= maxlen do + k = k + 1 + osub = string.sub(old,1,k) + nsub = string.sub(new,1,k) + if osub == nsub then + newsub = nsub + end + end + newsub = utf8_gsub(newsub, "_$", "") --$ + local msg = "B.O.L. MATCH=" .. newsub + log_flaw(msg, line, colno, footnote) + oldsub = newsub + local newsub = "" + local k = utf8.len(oldsub) + local n = start + repeat + if n and n.id ~= GLUE then + color_node(n, COLOR) + l, newsub = signature(n, newsub, swap) + elseif n and n.id == GLUE then + newsub = newsub .. "_" + elseif not n and box then + n = box + else + break + end + n = n.next + until newsub == oldsub or l >= k + end + end + return new, match +end +local check_page_first_word = function (node, colno) + local COLOR = luatypo.colortbl[14] + local match = false + local swap = false + local new = "" + local maxlen = luatypo.MinLen + local len = 0 + local n = node + local pn + while n and n.id ~= GLYPH and n.id ~= DISC and + (n.id ~= HLIST or n.subtype == INDENT) do + n = n.next + end + local start = n + if n and n.id == HLIST then + start = n.head + n = n.head + end + repeat + len, new = signature (n, new, swap) + n = n.next + until len > maxlen or (n and n.id == GLYPH and eow_char[n.char]) or + (n and n.id == GLUE) or + (n and n.id == KERN and n.subtype == 1) + if n and (n.id == GLUE or n.id == KERN) then + pn = n + n = n.next + end + if len <= maxlen and n and n.id == GLYPH and eow_char[n.char] then + match =true + if pn and (pn.id == GLUE or pn.id == KERN) then + new = new .. " " + len = len + 1 + end + len = len + 1 + end + if match then + local msg = "ShortFinalWord=" .. new + log_flaw(msg, 1, colno, false) + local n = start + repeat + color_node(n, COLOR) + n = n.next + until eow_char[n.char] + color_node(n, COLOR) + end + return match +end +local check_regexpr = function (glyph, line, colno) + local COLOR = luatypo.colortbl[3] + local lang = glyph.lang + local match = false + local retflag = false + local lchar, id = is_glyph(glyph) + local previous = glyph.prev + if lang and luatypo.single[lang] then + if lchar and previous and previous.id == GLUE then + match = utf8_find(luatypo.single[lang], utf8.char(lchar)) + if match then + retflag = true + local msg = "RGX MATCH=" .. utf8.char(lchar) + log_flaw(msg, line, colno, footnote) + color_node(glyph,COLOR) + end + end + end + if lang and luatypo.double[lang] then + if lchar and previous and previous.id == GLYPH then + local pchar, id = is_glyph(previous) + local pprev = previous.prev + if pchar and pprev and pprev.id == GLUE then + local pattern = utf8.char(pchar) .. utf8.char(lchar) + match = utf8_find(luatypo.double[lang], pattern) + if match then + retflag = true + local msg = "RGX MATCH=" .. pattern + log_flaw(msg, line, colno, footnote) + color_node(previous,COLOR) + color_node(glyph,COLOR) + end + end + elseif lchar and previous and previous.id == KERN then + local pprev = previous.prev + if pprev and pprev.id == GLYPH then + local pchar, id = is_glyph(pprev) + local ppprev = pprev.prev + if pchar and ppprev and ppprev.id == GLUE then + local pattern = utf8.char(pchar) .. utf8.char(lchar) + match = utf8_find(luatypo.double[lang], pattern) + if match then + retflag = true + local msg = "REGEXP MATCH=" .. pattern + log_flaw(msg, line, colno, footnote) + color_node(pprev,COLOR) + color_node(glyph,COLOR) + end + end + end + end + end +return retflag +end +local show_pre_disc = function (disc, color) + local n = disc + while n and n.id ~= GLUE do + color_node(n, color) + n = n.prev + end + return n + end +local footnoterule_ahead = function (head) + local n = head + local flag = false + local totalht, ruleht, ht1, ht2, ht3 + if n and n.id == KERN and n.subtype == 1 then + totalht = n.kern + n = n.next + while n and n.id == GLUE do n = n.next end + if n and n.id == RULE and n.subtype == 0 then + ruleht = n.height + totalht = totalht + ruleht + n = n.next + if n and n.id == KERN and n.subtype == 1 then + totalht = totalht + n.kern + if totalht == 0 or totalht == ruleht then + flag = true + else + end + end + end + end + return flag +end +local get_pagebody = function (head) + local textht = tex.getdimen("textheight") + local fn = head.list + local body = nil + repeat + fn = fn.next + until fn.id == VLIST and fn.height > 0 + first = fn.list + for n in traverse_id(VLIST,first) do + if n.subtype == 0 and n.height == textht then + body = n + break + else + first = n.list + for n in traverse_id(VLIST,first) do + if n.subtype == 0 and n.height == textht then + body = n + break + end + end + end + end + if not body then + texio.write_nl("***lua-typo ERROR: PAGE BODY *NOT* FOUND!***") + end + return body +end +check_vtop = function (head, colno, vpos) + local PAGEmin = luatypo.PAGEmin + local HYPHmax = luatypo.HYPHmax + local LLminWD = luatypo.LLminWD + local BackPI = luatypo.BackPI + local BackFuzz = luatypo.BackFuzz + local BackParindent = luatypo.BackParindent + local ShortLines = luatypo.ShortLines + local ShortPages = luatypo.ShortPages + local OverfullLines = luatypo.OverfullLines + local UnderfullLines = luatypo.UnderfullLines + local Widows = luatypo.Widows + local Orphans = luatypo.Orphans + local EOPHyphens = luatypo.EOPHyphens + local RepeatedHyphens = luatypo.RepeatedHyphens + local FirstWordMatch = luatypo.FirstWordMatch + local ParLastHyphen = luatypo.ParLastHyphen + local EOLShortWords = luatypo.EOLShortWords + local LastWordMatch = luatypo.LastWordMatch + local FootnoteSplit = luatypo.FootnoteSplit + local ShortFinalWord = luatypo.ShortFinalWord + local Stretch = math.max(luatypo.Stretch/100,1) + local blskip = tex.getglue("baselineskip") + local vpos_min = PAGEmin * blskip + vpos_min = vpos_min * 1.5 + local linewd = tex.getdimen("textwidth") + local first_bot = true + local footnote = false + local ftnsplit = false + local orphanflag = false + local widowflag = false + local pageshort = false + local firstwd = "" + local lastwd = "" + local hyphcount = 0 + local pageline = 0 + local ftnline = 0 + local line = 0 + local body_bottom = false + local page_bottom = false + local pageflag = false + local pageno = tex.getcount("c@page") + while head do + local nextnode = head.next + if head.id == HLIST and head.subtype == LINE and + (head.height > 0 or head.depth > 0) then + vpos = vpos + head.height + head.depth + local linewd = head.width + local first = head.head + local ListItem = false + if footnote then + ftnline = ftnline + 1 + line = ftnline + else + pageline = pageline + 1 + line = pageline + end + local n = nextnode + while n and (n.id == GLUE or n.id == PENALTY or + n.id == WHATSIT ) do + n = n.next + end + if not n then + page_bottom = true + body_bottom = true + elseif footnoterule_ahead(n) then + body_bottom = true + end + local hmax = linewd + tex.hfuzz + local w,h,d = dimensions(1,2,0, first) + if w > hmax and OverfullLines then + pageflag = true + local wpt = string.format("%.2fpt", (w-head.width)/65536) + local msg = "OVERFULL line " .. wpt + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[7] + color_line (head, COLOR) + elseif head.glue_set > Stretch and head.glue_sign == 1 and + head.glue_order == 0 and UnderfullLines then + pageflag = true + local s = string.format("%.0f%s", 100*head.glue_set, "%") + local msg = "UNDERFULL line stretch=" .. s + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[8] + color_line (head, COLOR) + end + if footnote and page_bottom then + ftnsplit = true + end + while first.id == MKERN or + (first.id == GLUE and first.subtype == LFTSKIP) do + first = first.next + end + if first.id == LPAR then + hyphcount = 0 + firstwd = "" + lastwd = "" + if not footnote then + parline = 1 + if body_bottom then + orphanflag = true + end + end + local nn = first.next + if nn and nn.id == HLIST and nn.subtype == BOX then + ListItem = true + end + elseif not footnote then + parline = parline + 1 + end + local ln = slide(first) + local pn = ln.prev + if pn and pn.id == GLUE and pn.subtype == PARFILL then + hyphcount = 0 + ftnsplit = false + orphanflag = false + if pageline == 1 and parline > 1 then + widowflag = true + end + local PFskip = effective_glue(pn,head) + if ShortLines then + local llwd = linewd - PFskip + if llwd < LLminWD then + pageflag = true + local msg = "SHORT LINE: length=" .. + string.format("%.0fpt", llwd/65536) + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[6] + local attr = oberdiek.luacolor.getattribute() + color_line (head, COLOR) + end + end + if BackParindent and PFskip < BackPI and + PFskip >= BackFuzz and parline > 1 then + pageflag = true + local msg = "NEARLY FULL line: backskip=" .. + string.format("%.1fpt", PFskip/65536) + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[12] + local attr = oberdiek.luacolor.getattribute() + color_line (head, COLOR) + end + if Widows and widowflag then + pageflag = true + local msg = "WIDOW" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[4] + color_line (head, COLOR) + widowflag = false + end + if FirstWordMatch then + local flag = not ListItem + firstwd, flag = + check_line_first_word(firstwd, first, line, colno, flag) + if flag then + pageflag = true + end + end + if LastWordMatch then + local flag = true + if PFskip > BackPI then + flag = false + end + lastwd, flag = + check_line_last_word(lastwd, pn, line, colno, flag) + if flag then + pageflag = true + end + end + elseif pn and pn.id == DISC then + hyphcount = hyphcount + 1 + if orphanflag and Orphans then + pageflag = true + local msg = "ORPHAN" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[5] + color_line (head, COLOR) + end + if ftnsplit and FootnoteSplit then + pageflag = true + local msg = "FOOTNOTE SPLIT" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[13] + color_line (head, COLOR) + end + if (page_bottom or body_bottom) and EOPHyphens then + pageflag = true + local msg = "LAST WORD SPLIT" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[1] + local pg = show_pre_disc (pn,COLOR) + end + if FirstWordMatch then + local flag = not ListItem + firstwd, flag = + check_line_first_word(firstwd, first, line, colno, flag) + if flag then + pageflag = true + end + end + if LastWordMatch then + local flag = true + lastwd, flag = + check_line_last_word(lastwd, ln, line, colno, flag) + if flag then + pageflag = true + end + end + if hyphcount > HYPHmax and RepeatedHyphens then + local COLOR = luatypo.colortbl[2] + local pg = show_pre_disc (pn,COLOR) + pageflag = true + local msg = "REPEATED HYPHENS: more than " .. HYPHmax + log_flaw(msg, line, colno, footnote) + end + if nextnode and ParLastHyphen then + local nn = nextnode.next + local nnn = nil + if nn and nn.next then + nnn = nn.next + if nnn.id == HLIST and nnn.subtype == LINE and + nnn.glue_order == 2 then + pageflag = true + local msg = "HYPHEN on next to last line" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[0] + local pg = show_pre_disc (pn,COLOR) + end + end + end + else + hyphcount = 0 + if orphanflag and Orphans then + pageflag = true + local msg = "ORPHAN" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[5] + color_line (head, COLOR) + end + if ftnsplit and FootnoteSplit then + pageflag = true + local msg = "FOOTNOTE SPLIT" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[13] + color_line (head, COLOR) + end + if FirstWordMatch then + local flag = not ListItem + firstwd, flag = + check_line_first_word(firstwd, first, line, colno, flag) + if flag then + pageflag = true + end + end + if LastWordMatch and pn then + local flag = true + lastwd, flag = + check_line_last_word(lastwd, pn, line, colno, flag) + if flag then + pageflag = true + end + end + if EOLShortWords then + while pn and pn.id ~= GLYPH and pn.id ~= HLIST do + pn = pn.prev + end + if pn and pn.id == GLYPH then + if check_regexpr(pn, line, colno) then + pageflag = true + end + end + end + end + if ShortFinalWord and pageline == 1 and parline > 1 and + check_page_first_word(first,colno) then + pageflag = true + end + elseif head.id == HLIST and + (head.subtype == EQN or head.subtype == ALIGN) and + (head.height > 0 or head.depth > 0) then + vpos = vpos + head.height + head.depth + if footnote then + ftnline = ftnline + 1 + line = ftnline + else + pageline = pageline + 1 + line = pageline + end + local fl = true + local wd = 0 + local hmax = 0 + if head.subtype == EQN then + local f = head.list + wd = rangedimensions(head,f) + hmax = head.width + tex.hfuzz + else + wd = head.width + hmax = tex.getdimen("linewidth") + tex.hfuzz + end + if wd > hmax and OverfullLines then + if head.subtype == ALIGN then + local first = head.list + for n in traverse_id(HLIST, first) do + local last = slide(n.list) + if last.id == GLUE and last.subtype == USER then + wd = wd - effective_glue(last,n) + if wd <= hmax then fl = false end + end + end + end + if fl then + pageflag = true + local w = wd - hmax + tex.hfuzz + local wpt = string.format("%.2fpt", w/65536) + local msg = "OVERFULL equation " .. wpt + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[7] + color_line (head, COLOR) + end + end + elseif head and head.id == RULE and head.subtype == 0 then + vpos = vpos + head.height + head.depth + if body_bottom then + footnote = true + ftnline = 0 + body_bottom = false + orphanflag = false + hyphcount = 0 + firstwd = "" + lastwd = "" + end + elseif body_bottom and head.id == GLUE and head.subtype == 0 then + if first_bot then + if pageline > 1 and pageline < PAGEmin and ShortPages then + pageshort = true + end + if pageshort and vpos < vpos_min then + pageflag = true + local msg = "SHORT PAGE: only " .. pageline .. " lines" + log_flaw(msg, line, colno, footnote) + local COLOR = luatypo.colortbl[9] + local n = head + repeat + n = n.prev + until n.id == HLIST + color_line (n, COLOR) + end + first_bot = false + end + elseif head.id == GLUE then + vpos = vpos + effective_glue(head,body) + elseif head.id == KERN and head.subtype == 1 then + vpos = vpos + head.kern + elseif head.id == VLIST then + vpos = vpos + head.height + head.depth + elseif head.id == HLIST and head.subtype == BOX then + local hf = head.list + if hf and hf.id == VLIST and hf.subtype == 0 then + break + end + end + head = nextnode + end + if pageflag then + local plist = luatypo.pagelist + local lastp = tonumber(string.match(plist, "%s(%d+),%s$")) + if not lastp or pageno > lastp then + luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", " + end + end + return head +end +luatypo.check_page = function (head) + local textwd = tex.getdimen("textwidth") + local vpos = 0 + local n2, n3, col, colno + local body = get_pagebody(head) + local footnote = false + local top = body + local first = body.list + if (first and first.id == HLIST and first.subtype == BOX) or + (first and first.id == VLIST and first.subtype == 0) then + top = body.list + first = top.list + end + while top do + first = top.list + if top and top.id == VLIST and top.subtype == 0 and + top.width > textwd/2 then + local next = check_vtop(first,colno,vpos) + if next then + top = next + elseif top then + top = top.next + end + elseif (top and top.id == HLIST and top.subtype == BOX) and + (first and first.id == VLIST and first.subtype == 0) and + (first.height > 0 and first.width > 0) then + colno = 0 + for n in traverse_id(VLIST, first) do + colno = colno + 1 + col = n.list + check_vtop(col,colno,vpos) + end + colno = nil + top = top.next + elseif (top and top.id == HLIST and top.subtype == BOX) and + (first and first.id == HLIST and first.subtype == BOX) and + (first.height > 0 and first.width > 0) then + colno = 0 + for n in traverse_id(HLIST, first) do + colno = colno + 1 + local nn = n.list + if nn and nn.list then + col = nn.list + check_vtop(col,colno,vpos) + end + end + colno = nil + top = top.next + else + top = top.next + end + end + return true +end +return luatypo.check_page +\end{luacode} +\AtEndOfPackage{% + \directlua{ + if not luatypo.None then + luatexbase.add_to_callback + ("pre_shipout_filter",luatypo.check_page,"check_page",1) + end + }% +} +\InputIfFileExists{lua-typo.cfg}% + {\PackageInfo{lua-typo.sty}{"lua-typo.cfg" file loaded}}% + {\PackageInfo{lua-typo.sty}{"lua-typo.cfg" file not found. + \MessageBreak Providing default values.}% + \definecolor{LTgrey}{gray}{0.6}% + \definecolor{LTred}{rgb}{1,0.55,0} + \luatypoSetColor0{red}% Paragraph last full line hyphenated + \luatypoSetColor1{red}% Page last word hyphenated + \luatypoSetColor2{red}% Hyphens on to many consecutive lines + \luatypoSetColor3{red}% Short word at end of line + \luatypoSetColor4{cyan}% Widow + \luatypoSetColor5{cyan}% Orphan + \luatypoSetColor6{cyan}% Paragraph ending on a short line + \luatypoSetColor7{blue}% Overfull lines + \luatypoSetColor8{blue}% Underfull lines + \luatypoSetColor9{red}% Nearly empty page + \luatypoSetColor{10}{LTred}% First word matches + \luatypoSetColor{11}{LTred}% Last word matches + \luatypoSetColor{12}{LTgrey}% Paragraph ending on a nearly full line + \luatypoSetColor{13}{cyan}% Footnote split + \luatypoSetColor{14}{red}% Too short first (final) word on the page + \luatypoBackPI=1em\relax + \luatypoBackFuzz=2pt\relax + \ifdim\parindent=0pt \luatypoLLminWD=20pt\relax + \else\luatypoLLminWD=2\parindent\relax\fi + \luatypoStretchMax=200\relax + \luatypoHyphMax=2\relax + \luatypoPageMin=5\relax + \luatypoMinFull=4\relax + \luatypoMinPART=4\relax + \luatypoMinLen=4\relax + }% +%% +%% +%% End of file `lua-typo.sty'. diff --git a/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty index aa876b82fa9..891d4076862 100644 --- a/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty +++ b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty @@ -11,20 +11,12 @@ %% \NeedsTeXFormat{LaTeX2e}[2021/06/01] \ProvidesPackage{lua-typo} - [2023-03-08 v.0.65 Daniel Flipo] -\ifdefined\DeclareRelease - \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty} - \DeclareCurrentRelease{}{2023-03-08} -\else - \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide - access\MessageBreak to former versions of the lua-typo package.% - \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated% - \MessageBreak 2020-01-01 or newer; reported} -\fi -\NeedsTeXFormat{LaTeX2e}[2021/06/01] + [2023-04-12 v.0.70 Daniel Flipo] +\DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty} +\DeclareRelease{v0.65}{2023-03-08}{lua-typo-2023-03-08.sty} +\DeclareCurrentRelease{}{2023-04-12} \ifdefined\directlua - \RequirePackage{luatexbase,luacode,luacolor} - \RequirePackage{kvoptions,atveryend} + \RequirePackage{luatexbase,luacode,luacolor,atveryend} \else \PackageError{This package is meant for LuaTeX only! Aborting} {No more information available, sorry!} @@ -45,39 +37,36 @@ \begin{luacode} luatypo = { } \end{luacode} -\SetupKeyvalOptions{ - family=luatypo, - prefix=LT@, -} -\DeclareBoolOption[false]{ShowOptions} -\DeclareBoolOption[false]{None} -\DeclareBoolOption[false]{All} -\DeclareBoolOption[false]{BackParindent} -\DeclareBoolOption[false]{ShortLines} -\DeclareBoolOption[false]{ShortPages} -\DeclareBoolOption[false]{OverfullLines} -\DeclareBoolOption[false]{UnderfullLines} -\DeclareBoolOption[false]{Widows} -\DeclareBoolOption[false]{Orphans} -\DeclareBoolOption[false]{EOPHyphens} -\DeclareBoolOption[false]{RepeatedHyphens} -\DeclareBoolOption[false]{ParLastHyphen} -\DeclareBoolOption[false]{EOLShortWords} -\DeclareBoolOption[false]{FirstWordMatch} -\DeclareBoolOption[false]{LastWordMatch} -\DeclareBoolOption[false]{FootnoteSplit} -\DeclareBoolOption[false]{ShortFinalWord} -\AddToKeyvalOption{luatypo}{All}{% - \LT@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 \LT@FootnoteSplittrue - \LT@ShortFinalWordtrue -} -\ProcessKeyvalOptions{luatypo} +\DeclareKeys[luatypo] + { + ShowOptions.if = LT@ShowOptions , + None.if = LT@None , + BackParindent.if = LT@BackParindent , + ShortLines.if = LT@ShortLines , + ShortPages.if = LT@ShortPages , + OverfullLines.if = LT@OverfullLines , + UnderfullLines.if = LT@UnderfullLines , + Widows.if = LT@Widows , + Orphans.if = LT@Orphans , + EOPHyphens.if = LT@EOPHyphens , + RepeatedHyphens.if = LT@RepeatedHyphens , + ParLastHyphen.if = LT@ParLastHyphen , + EOLShortWords.if = LT@EOLShortWords , + FirstWordMatch.if = LT@FirstWordMatch , + LastWordMatch.if = LT@LastWordMatch , + FootnoteSplit.if = LT@FootnoteSplit , + ShortFinalWord.if = LT@ShortFinalWord , + All.if = LT@All , + All.code = \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 \LT@FootnoteSplittrue + \LT@ShortFinalWordtrue + } +\ProcessKeyOptions[luatypo] \AtEndOfPackage{% \ifLT@None \directlua{ luatypo.None = true }% @@ -182,6 +171,7 @@ luatypo = { } \MessageBreak ShowOptions [false]\MessageBreak None [false]\MessageBreak + All [false]\MessageBreak BackParindent [false]\MessageBreak ShortLines [false]\MessageBreak ShortPages [false]\MessageBreak @@ -209,6 +199,7 @@ luatypo = { } luatypo.Stretch = tex.count.luatypoStretchMax luatypo.MinFull = tex.count.luatypoMinFull luatypo.MinPart = tex.count.luatypoMinPart + luatypo.MinFull = math.min(luatypo.MinPart,luatypo.MinFull) luatypo.MinLen = tex.count.luatypoMinLen luatypo.LLminWD = tex.dimen.luatypoLLminWD luatypo.BackPI = tex.dimen.luatypoBackPI @@ -344,10 +335,11 @@ local traverse_id = node.traverse_id local has_field = node.has_field local uses_font = node.uses_font local is_glyph = node.is_glyph +local utf8_len = utf8.len local utf8_find = unicode.utf8.find local utf8_gsub = unicode.utf8.gsub local utf8_reverse = function (s) - if utf8.len(s) > 1 then + if utf8_len(s) > 1 then local so = "" for p, c in utf8.codes(s) do so = utf8.char(c) .. so @@ -356,6 +348,11 @@ local utf8_reverse = function (s) end return s end +local utf8_sub = function (s,i,j) + i=utf8.offset(s,i) + j=utf8.offset(s,j+1)-1 + return string.sub(s,i,j) +end local color_node = function (node, color) local attr = oberdiek.luacolor.getattribute() if node and node.id == DISC then @@ -482,11 +479,11 @@ local signature = function (node, string, swap) else str = str .. c1 .. c2 end + elseif n and n.id == GLUE then + str = str .. "_" end - local len = utf8.len(str) - if utf8_find(str, "_") then - len = len - 1 - end + local s = utf8_gsub(str, "_", "") + local len = utf8_len(s) return len, str end local check_line_last_word = function (old, node, line, colno, flag) @@ -494,6 +491,8 @@ local check_line_last_word = function (old, node, line, colno, flag) local match = false local new = "" local maxlen = 0 + local MinFull = luatypo.MinFull + local MinPart = luatypo.MinPart if node then local swap = true local box, go @@ -503,103 +502,104 @@ local check_line_last_word = function (old, node, line, colno, flag) lastn = lastn.prev end local n = lastn - if n and n.id == HLIST then - box = n - prev = n.prev - lastn = slide(n.head) - n = lastn - end - while n and n.id ~= GLUE do - maxlen, new = signature (n, new, swap) - n = n.prev - end - if n and n.id == GLUE then - new = new .. "_" - go = true - elseif box and not n then - local p = box.prev - if p.id == GLUE then - new = new .. "_" - n = p - else - n = box - end - go = true - end - if go then - repeat - n = n.prev - maxlen, new = signature (n, new, swap) - until not n or n.id == GLUE + local words = 0 + while n and (words <= 2 or maxlen < MinPart) do + if n and n.id == HLIST then + box = n + local first = n.head + local lastn = slide(first) + n = lastn + while n do + maxlen, new = signature (n, new, swap) + n = n.prev + end + n = box.prev + local w = utf8_gsub(new, "_", "") + words = words + utf8_len(new) - utf8_len(w) + 1 + else + repeat + maxlen, new = signature (n, new, swap) + n = n.prev + until not n or n.id == GLUE or n.id == HLIST + if n and n.id == GLUE then + maxlen, new = signature (n, new, swap) + words = words + 1 + n = n.prev + end + end end new = utf8_reverse(new) - if flag then - local MinFull = luatypo.MinFull - local MinPart = luatypo.MinPart - MinFull = math.min(MinPart,MinFull) - local k = MinPart - local dlo = utf8_reverse(old) - local wen = utf8_reverse(new) - local oldlast = utf8_gsub (old, ".*_", "_") - local newlast = utf8_gsub (new, ".*_", "_") - local i - if utf8_find(newlast, "_") then - i = utf8.len(newlast) - end - if i and i > maxlen - MinPart + 1 then - k = MinPart + 1 - end + new = utf8_gsub(new, "_+$", "") -- $ + new = utf8_gsub(new, "^_+", "") + maxlen = math.min(utf8_len(old), utf8_len(new)) + if flag and old ~= "" then + local oldlast = utf8_gsub (old, ".*_", "") + local newlast = utf8_gsub (new, ".*_", "") local oldsub = "" local newsub = "" + local dlo = utf8_reverse(old) + local wen = utf8_reverse(new) for p, c in utf8.codes(dlo) do - if utf8.len(oldsub) < k then + local s = utf8_gsub(oldsub, "_", "") + if utf8_len(s) < MinPart then oldsub = utf8.char(c) .. oldsub end end for p, c in utf8.codes(wen) do - if utf8.len(newsub) < k then + local s = utf8_gsub(newsub, "_", "") + if utf8_len(s) < MinPart then newsub = utf8.char(c) .. newsub end end - local l = utf8.len(new) - if oldsub == newsub and l >= k then + if oldsub == newsub then match = true - elseif oldlast == newlast and utf8.len(newlast) > MinFull then + end + if oldlast == newlast and utf8_len(newlast) >= MinFull then + if utf8_len(newlast) > MinPart or not match then + oldsub = oldlast + newsub = newlast + end match = true - oldsub = oldlast - newsub = newlast - k = utf8.len(newlast) end if match then - local osub = oldsub - local nsub = newsub - while osub == nsub and k <= maxlen do - k = k +1 - osub = string.sub(old,-k) - nsub = string.sub(new,-k) + local k = utf8_len(newsub) + local osub = utf8_reverse(oldsub) + local nsub = utf8_reverse(newsub) + while osub == nsub and k < maxlen do + k = k + 1 + osub = utf8_sub(dlo,1,k) + nsub = utf8_sub(wen,1,k) if osub == nsub then - newsub = nsub + newsub = utf8_reverse(nsub) end end - newsub = utf8_gsub(newsub, "^_", "") + newsub = utf8_gsub(newsub, "^_+", "") local msg = "E.O.L. MATCH=" .. newsub log_flaw(msg, line, colno, footnote) + local ns = utf8_gsub(newsub, "_", "") + k = utf8_len(ns) oldsub = utf8_reverse(newsub) local newsub = "" local n = lastn - repeat - if n and n.id ~= GLUE then + local l = 0 + local lo = 0 + local li = 0 + while n and newsub ~= oldsub and l < k do + if n and n.id == HLIST then + local first = n.head + for nn in traverse_id(GLYPH, first) do + color_node(nn, COLOR) + local c = nn.char + if not char_to_discard[c] then l = l + 1 end + end + elseif n then color_node(n, COLOR) - l, newsub = signature(n, newsub, swap) - elseif n and n.id == GLUE then - newsub = newsub .. "_" - elseif not n and box then - n = box - else - break + li, newsub = signature(n, newsub, swap) + l = l + li - lo + lo = li end n = n.prev - until newsub == oldsub or l >= k + end end end end @@ -611,103 +611,108 @@ local check_line_first_word = function (old, node, line, colno, flag) local swap = false local new = "" local maxlen = 0 + local MinFull = luatypo.MinFull + local MinPart = luatypo.MinPart local n = node local box, go while n and n.id ~= GLYPH and n.id ~= DISC and (n.id ~= HLIST or n.subtype == INDENT) do n = n.next end - local start = n - if n and n.id == HLIST then - box = n - start = n.head - n = n.head - end - while n and n.id ~= GLUE do - maxlen, new = signature (n, new, swap) - n = n.next - end - if n and n.id == GLUE then - new = new .. "_" - go = true - elseif box and not n then - local bn = box.next - if bn.id == GLUE then - new = new .. "_" - n = bn - else - n = box - end - go = true - end - if go then - repeat - n = n.next - maxlen, new = signature (n, new, swap) - until not n or n.id == GLUE + start = n + local words = 0 + while n and (words <= 2 or maxlen < MinPart) do + if n and n.id == HLIST then + box = n + n = n.head + while n do + maxlen, new = signature (n, new, swap) + n = n.next + end + n = box.next + local w = utf8_gsub(new, "_", "") + words = words + utf8_len(new) - utf8_len(w) + 1 + else + repeat + maxlen, new = signature (n, new, swap) + n = n.next + until not n or n.id == GLUE or n.id == HLIST + if n and n.id == GLUE then + maxlen, new = signature (n, new, swap) + words = words + 1 + n = n.next + end + end end - if flag then - local MinFull = luatypo.MinFull - local MinPart = luatypo.MinPart - MinFull = math.min(MinPart,MinFull) - local k = MinPart - local oldfirst = utf8_gsub (old, "_.*", "_") - local newfirst = utf8_gsub (new, "_.*", "_") - local i - if utf8_find(newfirst, "_") then - i = utf8.len(newfirst) - end - if i and i <= MinPart then - k = MinPart + 1 - end + new = utf8_gsub(new, "_+$", "") -- $ + new = utf8_gsub(new, "^_+", "") + maxlen = math.min(utf8_len(old), utf8_len(new)) + if flag and old ~= "" then + local oldfirst = utf8_gsub (old, "_.*", "") + local newfirst = utf8_gsub (new, "_.*", "") local oldsub = "" local newsub = "" for p, c in utf8.codes(old) do - if utf8.len(oldsub) < k then oldsub = oldsub .. utf8.char(c) end + local s = utf8_gsub(oldsub, "_", "") + if utf8_len(s) < MinPart then + oldsub = oldsub .. utf8.char(c) + end end for p, c in utf8.codes(new) do - if utf8.len(newsub) < k then newsub = newsub .. utf8.char(c) end + local s = utf8_gsub(newsub, "_", "") + if utf8_len(s) < MinPart then + newsub = newsub .. utf8.char(c) + end end - local l = utf8.len(newsub) - if oldsub == newsub and l >= k then + if oldsub == newsub then match = true - elseif oldfirst == newfirst and utf8.len(newfirst) > MinFull then + end + if oldfirst == newfirst and utf8_len(newfirst) >= MinFull then + if utf8_len(newfirst) > MinPart or not match then + oldsub = oldfirst + newsub = newfirst + end match = true - oldsub = oldfirst - newsub = newfirst - k = utf8.len(newfirst) end if match then + local k = utf8_len(newsub) local osub = oldsub local nsub = newsub - while osub == nsub and k <= maxlen do + while osub == nsub and k < maxlen do k = k + 1 - osub = string.sub(old,1,k) - nsub = string.sub(new,1,k) + osub = utf8_sub(old,1,k) + nsub = utf8_sub(new,1,k) if osub == nsub then newsub = nsub end end - newsub = utf8_gsub(newsub, "_$", "") --$ + newsub = utf8_gsub(newsub, "_+$", "") --$ local msg = "B.O.L. MATCH=" .. newsub log_flaw(msg, line, colno, footnote) + local ns = utf8_gsub(newsub, "_", "") + k = utf8_len(ns) oldsub = newsub local newsub = "" - local k = utf8.len(oldsub) local n = start - repeat - if n and n.id ~= GLUE then + local l = 0 + local lo = 0 + local li = 0 + while n and newsub ~= oldsub and l < k do + if n and n.id == HLIST then + local nn = n.head + for nnn in traverse(nn) do + color_node(nnn, COLOR) + local c = nn.char + if not char_to_discard[c] then l = l + 1 end + end + elseif n then color_node(n, COLOR) - l, newsub = signature(n, newsub, swap) - elseif n and n.id == GLUE then - newsub = newsub .. "_" - elseif not n and box then - n = box - else - break + li, newsub = signature(n, newsub, swap) + l = l + li - lo + lo = li end n = n.next - until newsub == oldsub or l >= k + end end end return new, match @@ -717,7 +722,7 @@ local check_page_first_word = function (node, colno) local match = false local swap = false local new = "" - local maxlen = luatypo.MinLen + local minlen = luatypo.MinLen local len = 0 local n = node local pn @@ -733,14 +738,14 @@ local check_page_first_word = function (node, colno) repeat len, new = signature (n, new, swap) n = n.next - until len > maxlen or (n and n.id == GLYPH and eow_char[n.char]) or + until len > minlen or (n and n.id == GLYPH and eow_char[n.char]) or (n and n.id == GLUE) or (n and n.id == KERN and n.subtype == 1) if n and (n.id == GLUE or n.id == KERN) then pn = n n = n.next end - if len <= maxlen and n and n.id == GLYPH and eow_char[n.char] then + if len <= minlen and n and n.id == GLYPH and eow_char[n.char] then match =true if pn and (pn.id == GLUE or pn.id == KERN) then new = new .. " " @@ -845,6 +850,22 @@ local footnoterule_ahead = function (head) end return flag end +local check_EOP = function (node) + local n = node + local page_bot = false + local body_bot = false + while n and (n.id == GLUE or n.id == PENALTY or + n.id == WHATSIT ) do + n = n.next + end + if not n then + page_bot = true + body_bot = true + elseif footnoterule_ahead(n) then + body_bot = true + end + return page_bot, body_bot +end local get_pagebody = function (head) local textht = tex.getdimen("textheight") local fn = head.list @@ -872,7 +893,8 @@ local get_pagebody = function (head) end return body end -check_vtop = function (head, colno, vpos) +check_vtop = function (top, colno, vpos) + local head = top.list local PAGEmin = luatypo.PAGEmin local HYPHmax = luatypo.HYPHmax local LLminWD = luatypo.LLminWD @@ -929,17 +951,7 @@ check_vtop = function (head, colno, vpos) pageline = pageline + 1 line = pageline end - local n = nextnode - while n and (n.id == GLUE or n.id == PENALTY or - n.id == WHATSIT ) do - n = n.next - end - if not n then - page_bottom = true - body_bottom = true - elseif footnoterule_ahead(n) then - body_bottom = true - end + page_bottom, body_bottom = check_EOP (nextnode) local hmax = linewd + tex.hfuzz local w,h,d = dimensions(1,2,0, first) if w > hmax and OverfullLines then @@ -1023,7 +1035,7 @@ check_vtop = function (head, colno, vpos) widowflag = false end if FirstWordMatch then - local flag = not ListItem + local flag = not ListItem and (line > 1) firstwd, flag = check_line_first_word(firstwd, first, line, colno, flag) if flag then @@ -1032,11 +1044,12 @@ check_vtop = function (head, colno, vpos) end if LastWordMatch then local flag = true - if PFskip > BackPI then + if PFskip > BackPI or line == 1 then flag = false end + local pnp = pn.prev lastwd, flag = - check_line_last_word(lastwd, pn, line, colno, flag) + check_line_last_word(lastwd, pnp, line, colno, flag) if flag then pageflag = true end @@ -1075,7 +1088,7 @@ check_vtop = function (head, colno, vpos) if LastWordMatch then local flag = true lastwd, flag = - check_line_last_word(lastwd, ln, line, colno, flag) + check_line_last_word(lastwd, pn, line, colno, flag) if flag then pageflag = true end @@ -1160,6 +1173,7 @@ check_vtop = function (head, colno, vpos) pageline = pageline + 1 line = pageline end + page_bottom, body_bottom = check_EOP (nextnode) local fl = true local wd = 0 local hmax = 0 @@ -1194,7 +1208,8 @@ check_vtop = function (head, colno, vpos) end elseif head and head.id == RULE and head.subtype == 0 then vpos = vpos + head.height + head.depth - if body_bottom then + local prev = head.prev + if body_bottom or footnoterule_ahead (prev) then footnote = true ftnline = 0 body_bottom = false @@ -1205,10 +1220,9 @@ check_vtop = function (head, colno, vpos) end elseif body_bottom and head.id == GLUE and head.subtype == 0 then if first_bot then - if pageline > 1 and pageline < PAGEmin and ShortPages then + if pageline > 1 and pageline < PAGEmin + and vpos < vpos_min and ShortPages then pageshort = true - end - if pageshort and vpos < vpos_min then pageflag = true local msg = "SHORT PAGE: only " .. pageline .. " lines" log_flaw(msg, line, colno, footnote) @@ -1222,7 +1236,7 @@ check_vtop = function (head, colno, vpos) first_bot = false end elseif head.id == GLUE then - vpos = vpos + effective_glue(head,body) + vpos = vpos + effective_glue(head,top) elseif head.id == KERN and head.subtype == 1 then vpos = vpos + head.kern elseif head.id == VLIST then @@ -1261,7 +1275,7 @@ luatypo.check_page = function (head) first = top.list if top and top.id == VLIST and top.subtype == 0 and top.width > textwd/2 then - local next = check_vtop(first,colno,vpos) + local next = check_vtop(top,colno,vpos) if next then top = next elseif top then @@ -1271,9 +1285,8 @@ luatypo.check_page = function (head) (first and first.id == VLIST and first.subtype == 0) and (first.height > 0 and first.width > 0) then colno = 0 - for n in traverse_id(VLIST, first) do + for col in traverse_id(VLIST, first) do colno = colno + 1 - col = n.list check_vtop(col,colno,vpos) end colno = nil @@ -1284,9 +1297,8 @@ luatypo.check_page = function (head) colno = 0 for n in traverse_id(HLIST, first) do colno = colno + 1 - local nn = n.list - if nn and nn.list then - col = nn.list + local col = n.list + if col and col.list then check_vtop(col,colno,vpos) end end @@ -1336,7 +1348,7 @@ return luatypo.check_page \luatypoStretchMax=200\relax \luatypoHyphMax=2\relax \luatypoPageMin=5\relax - \luatypoMinFull=4\relax + \luatypoMinFull=3\relax \luatypoMinPART=4\relax \luatypoMinLen=4\relax }% -- cgit v1.2.3