diff options
Diffstat (limited to 'Master')
-rw-r--r-- | Master/texmf-dist/doc/lualatex/lua-typo/README.md | 7 | ||||
-rw-r--r-- | Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf | bin | 16859 -> 16859 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf | bin | 106625 -> 107474 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf | bin | 208526 -> 217771 bytes | |||
-rw-r--r-- | Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx | 588 | ||||
-rw-r--r-- | Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg | 9 | ||||
-rw-r--r-- | Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty | 226 |
7 files changed, 590 insertions, 240 deletions
diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/README.md b/Master/texmf-dist/doc/lualatex/lua-typo/README.md index 526538afb05..8f974434faa 100644 --- a/Master/texmf-dist/doc/lualatex/lua-typo/README.md +++ b/Master/texmf-dist/doc/lualatex/lua-typo/README.md @@ -90,7 +90,7 @@ Changes * v.0.70: - Options handled via `ltkeys` instead of `kvoptions`. - - code cleaning, bug fixes. + - Code cleaning, bug fixes. * v.0.80: - Config file `lua-typo.cfg` changed (new colours added, all colour @@ -99,6 +99,11 @@ Changes added for lines affected by multiple flaws (f.i. widow+overfull). - Bug fix (file `.typo`): in footnotes, line numbers for homeoarchy were not reset. + +* v.0.85: + - A warning is issued if some pages fail to be checked properly. + - Margin notes checking added (over/underfull lines, position). + - Code cleaning, bug fixes. -- Copyright 2020--2023 Daniel Flipo 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 Binary files differindex 6606b65d368..e82b355c627 100644 --- a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf +++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-demo.pdf 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 Binary files differindex 88dea3e4dee..b117378ac8a 100644 --- a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf +++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo-fr.pdf diff --git a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf Binary files differindex b43a1f75fe4..be36b78a7e3 100644 --- a/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf +++ b/Master/texmf-dist/doc/lualatex/lua-typo/lua-typo.pdf 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 4ec480c9211..7809a013774 100644 --- a/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx +++ b/Master/texmf-dist/source/lualatex/lua-typo/lua-typo.dtx @@ -204,7 +204,7 @@ % \pkg{lua-typo}, il suffit d’ajouter dans le préambule la ligne %\\ % |\usepackage[All]{lua-typo}| % -% La version courante (0.80) nécessite un noyau LaTeX récent, +% La version courante (0.85) nécessite un noyau LaTeX récent, % 2022/06/01 ou ultérieur. Ceux qui ne disposent que d’un noyau % antérieur à 2021/06/01 reçoivent un message d’erreur % «\texttt{Unable to register callback}» ; une version «rollback » @@ -269,6 +269,7 @@ % consécutives ?\\ % FootnoteSplit & Fin de note de bas de page sur page suivante?\\ % ShortFinalWord & Mot de fin de phrase court en haut de page\\ +% MarginparPos & Note marginale se terminant trop bas\\ % \hline % \end{tabular} % \end{table} @@ -363,7 +364,7 @@ % 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 +% une kyrielle de lignes 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}. @@ -380,7 +381,7 @@ % (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 +% \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) :\\ @@ -405,6 +406,12 @@ % 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. +% +% \item[\cs{luatypoMarginparTol}] est une \emph{dimension} qui vaut +% |\baselineskip| par défaut; les notes marginales qui se +% terminent à plus de |\luatypoMarginparTol| en dessous de la +% dernière ligne de la page sont déclarées fautives. +% % \end{description} % % À chacune des vérifications faites par \pkg{lua-typo} peut être @@ -432,6 +439,7 @@ % \luatypoSetColor{14}{cyan} % Note de bas de page éclatée % \luatypoSetColor{15}{red} % Mot de fin de phrase en haut de page % \luatypoSetColor{16}{LTline}% Ligne présentant plusieurs "défauts" +% \luatypoSetColor{17}{red} % Note marginale se terminant trop bas % \end{verbatim} % \pkg{lua-typo} charge les extensions \pkg{luacolor} et donc % \pkg{color}. Seules les couleurs portant un nom (\emph{named @@ -512,7 +520,8 @@ % for them, a ``rollback’’ version of \pkg{lua-typo} is provided, % it can be loaded this way: |\usepackage[All]{lua-typo}[=v0.4]|. % -% Version 0.80 requires a LaTeX kernel dated 2022/06/01 or later. +% \enlargethispage*{\baselineskip} +% Version 0.85 requires a LaTeX kernel dated 2022/06/01 or later. % Another ``rollback’’ version |[=v0.65]| has been added for those % who run an older kernel. % @@ -558,6 +567,7 @@ % LastWordMatch & same (part of) word ending two consecutive lines?\\ % FootnoteSplit & footnotes spread over two pages or more?\\ % ShortFinalWord & Short word ending a sentence on the next page\\ +% MarginparPos & Margin note ending too low on the page\\ % \hline % \end{tabular}\\[12pt] % For example, if you want \pkg{lua-typo} to only warn about overfull @@ -574,6 +584,7 @@ % this option provides an easy way to get their names without having % to look into the documentation. % +% \newpage % 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 @@ -594,7 +605,8 @@ % \item[ShortFinalWord :] the first word on a page is highlighted % if it ends a sentence and is short (up to |\luatypoMinLen=4| % letters). -% \end{description} +% +% \end{description} % % \section{Customisation} % @@ -682,7 +694,12 @@ % (\emph{i.e.} surrounded by single or double \textsc{ascii} % quotes) made of your words separated by spaces. % -% \end{description} +% \item[\cs{luatypoMarginparTol}] is a \emph{dimension} which +% defaults to |\baselineskip|; marginal notes trigger a flaw +% if they end lower than |\luatypoMarginparTol| under the page’s +% last line. +% +% \end{description} % % It is possible to define a specific colour for each % typographic flaws that \pkg{lua-typo} deals with. @@ -706,8 +723,8 @@ % \luatypoSetColor{13}{LTgrey}% Paragraph’s last line nearly full % \luatypoSetColor{14}{cyan} % Footnotes spread over two pages % \luatypoSetColor{15}{red} % Short final word on top of the page -% \luatypoSetColor{16}{LTline}% Line color for multiple flaws\end{verbatim} -% +% \luatypoSetColor{16}{LTline}% Line color for multiple flaws +% \luatypoSetColor{17}{red} % Margin note ending too low\end{verbatim} % \pkg{lua-typo} loads the \pkg{luacolor} package which loads the % \pkg{color} package from the LaTeX graphic bundle. % |\luatypoSetColor| requires named colours, so you can either use @@ -728,7 +745,7 @@ %<+scan>\ProvidesPackage{scan-page} %<+dtx>\ProvidesFile{lua-typo.dtx} %<*dtx|sty|scan> - [2023-04-28 v.0.80 Daniel Flipo] + [2023-09-13 v.0.85 Daniel Flipo] %</dtx|sty|scan> %<*sty> % \fi @@ -746,7 +763,7 @@ % \begin{macrocode} \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} +\DeclareCurrentRelease{}{2023-09-13} % \end{macrocode} % % This package only runs with LuaLaTeX and requires packages @@ -769,6 +786,7 @@ \newdimen\luatypoLLminWD \newdimen\luatypoBackPI \newdimen\luatypoBackFuzz +\newdimen\luatypoMarginparTol \newcount\luatypoStretchMax \newcount\luatypoHyphMax \newcount\luatypoPageMin @@ -814,6 +832,7 @@ luatypo = { } LastWordMatch.if = LT@LastWordMatch , FootnoteSplit.if = LT@FootnoteSplit , ShortFinalWord.if = LT@ShortFinalWord , + MarginparPos.if = LT@MarginparPos , All.if = LT@All , All.code = \LT@ShortLinestrue \LT@ShortPagestrue \LT@OverfullLinestrue \LT@UnderfullLinestrue @@ -822,7 +841,7 @@ luatypo = { } \LT@ParLastHyphentrue \LT@EOLShortWordstrue \LT@FirstWordMatchtrue \LT@LastWordMatchtrue \LT@BackParindenttrue \LT@FootnoteSplittrue - \LT@ShortFinalWordtrue + \LT@ShortFinalWordtrue \LT@MarginparPostrue } \ProcessKeyOptions[luatypo] % \end{macrocode} @@ -929,6 +948,12 @@ luatypo = { } \else \directlua{ luatypo.ShortFinalWord = false }% \fi + \ifLT@MarginparPos + \advance\luatypo@options by 1 + \directlua{ luatypo.MarginparPos = true }% + \else + \directlua{ luatypo.MarginparPos = false }% + \fi } % \end{macrocode} % @@ -958,6 +983,7 @@ luatypo = { } LastWordMatch [false]\MessageBreak FootnoteSplit [false]\MessageBreak ShortFinalWord [false]\MessageBreak + MarginparPos [false]\MessageBreak \MessageBreak *********************************************% \MessageBreak Lua-typo [ShowOptions] @@ -979,11 +1005,12 @@ luatypo = { } % \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 - luatypo.BackFuzz = tex.dimen.luatypoBackFuzz + luatypo.MinFull = math.min(luatypo.MinPart,luatypo.MinFull) + luatypo.MinLen = tex.count.luatypoMinLen + luatypo.LLminWD = tex.dimen.luatypoLLminWD + luatypo.BackPI = tex.dimen.luatypoBackPI + luatypo.BackFuzz = tex.dimen.luatypoBackFuzz + luatypo.MParTol = tex.dimen.luatypoMarginparTol % \end{macrocode} % \changes{0.80}{2023/04/28}{New table `luatypo.map’ for colours.} % @@ -1006,6 +1033,16 @@ luatypo = { } % (very) end of document and write the report file on disc, % unless option |None| has been selected. % +% On every page, at least one line of text should be found. +% Otherwise, \pkg{lua-typo} presumes something went wrong and +% writes the page number to a |failedlist| list. +% In case |pagelist| is empty \emph{and} |failedlist| \emph{is not}, +% a warning is issued instead of the \texttt{No Typo Flaws found.} +% message (new to version~0.85). +% +% \changes{0.85}{2023/09/07}{Warn in case some pages failed to +% be checked properly.} +% % \begin{macrocode} \AtVeryEndDocument{% \ifnum\luatypo@options = 0 \LT@Nonetrue \fi @@ -1023,14 +1060,27 @@ luatypo = { } texio.write_nl(' ') texio.write_nl('*************************************') if luatypo.pagelist == " " then - texio.write_nl('*** lua-typo: No Typo Flaws found.') + if luatypo.failedlist == " " then + texio.write_nl('*** lua-typo: No Typo Flaws found.') + else + texio.write_nl('*** WARNING: ') + texio.write('lua-typo failed to scan these pages:') + texio.write_nl('***' .. luatypo.failedlist) + texio.write_nl('*** Please report to the maintainer.') + end 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('*************************************') texio.write_nl(' ') + if luatypo.failedlist == " " then + else + local prt = "WARNING: lua-typo failed to scan pages " .. + luatypo.failedlist .. "\string\n\string\n" + luatypo.buffer = prt .. luatypo.buffer + end local fileout= tex.jobname .. ".typo" local out=io.open(fileout,"w+") out:write(luatypo.buffer) @@ -1060,7 +1110,7 @@ luatypo = { } local s = utf8.char(c) luatypo.single[langno] = luatypo.single[langno] .. s end -%<dbg> texio.write_nl("SINGLE=" .. luatypo.single[langno]) +%<dbg> texio.write_nl('SINGLE=' .. luatypo.single[langno]) %<dbg> texio.write_nl(' ') }% \else @@ -1079,7 +1129,7 @@ luatypo = { } local s = utf8.char(c) luatypo.double[langno] = luatypo.double[langno] .. s end -%<dbg> texio.write_nl("DOUBLE=" .. luatypo.double[langno]) +%<dbg> texio.write_nl('DOUBLE=' .. luatypo.double[langno]) %<dbg> texio.write_nl(' ') }% \else @@ -1113,13 +1163,14 @@ luatypo = { } % % \begin{macrocode} \begin{luacode} -luatypo.colortbl = { } -luatypo.map = { } -luatypo.single = { } -luatypo.double = { } -luatypo.pagelist = " " -luatypo.buffer = "List of typographic flaws found for " - .. tex.jobname .. ".pdf:\string\n\string\n" +luatypo.colortbl = { } +luatypo.map = { } +luatypo.single = { } +luatypo.double = { } +luatypo.pagelist = " " +luatypo.failedlist = " " +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 @@ -1535,12 +1586,12 @@ local check_line_last_word = new = utf8_gsub(new, "_+$", "") -- $ new = utf8_gsub(new, "^_+", "") maxlen = math.min(utf8_len(old), utf8_len(new)) -%<dbg> texio.write_nl("EOLsigold=" .. old) -%<dbg> texio.write(" EOLsig=" .. new) +%<dbg> texio.write_nl('EOLsigold=' .. old) +%<dbg> texio.write(' EOLsig=' .. new) % \end{macrocode} -% When called with flag |false|, |check_line_last_word| returns -% the last word’s signature, but doesn’t compare it with the -% previous line’s. +% When called with flag |false|, |check_line_last_word| doesn’t +% compare it with the previous line’s, but just returns the +% last word’s signature. % \begin{macrocode} if flag and old ~= "" then % \end{macrocode} @@ -1571,11 +1622,11 @@ local check_line_last_word = end end if oldsub == newsub then -%<dbg> texio.write_nl("EOLnewsub=" .. newsub) +%<dbg> texio.write_nl('EOLnewsub=' .. newsub) match = true end if oldlast == newlast and utf8_len(newlast) >= MinFull then -%<dbg> texio.write_nl("EOLnewlast=" .. newlast) +%<dbg> texio.write_nl('EOLnewlast=' .. newlast) if utf8_len(newlast) > MinPart or not match then oldsub = oldlast newsub = newlast @@ -1621,13 +1672,13 @@ local check_line_last_word = local c = nn.char if not char_to_discard[c] then l = l + 1 end end -%<dbg> texio.write_nl("l (box)=" .. l) +%<dbg> texio.write_nl('l (box)=' .. l) elseif n then color_node(n, COLOR) li, newsub = signature(n, newsub, swap) l = l + li - lo lo = li -%<dbg> texio.write_nl("l=" .. l) +%<dbg> texio.write_nl('l=' .. l) end n = n.prev end @@ -1688,12 +1739,12 @@ local check_line_first_word = new = utf8_gsub(new, "_+$", "") -- $ new = utf8_gsub(new, "^_+", "") maxlen = math.min(utf8_len(old), utf8_len(new)) -%<dbg> texio.write_nl("BOLsigold=" .. old) -%<dbg> texio.write(" BOLsig=" .. new) +%<dbg> texio.write_nl('BOLsigold=' .. old) +%<dbg> texio.write(' BOLsig=' .. new) % \end{macrocode} -% When called with flag |false|, |check_line_first_word| returns -% the first word’s signature, but doesn’t compare it with the -% previous line’s. +% When called with flag |false|, |check_line_first_word| doesn’t +% compare it with the previous line’s, but returns +% the first word’s signature. % \begin{macrocode} if flag and old ~= "" then local oldfirst = utf8_gsub (old, "_.*", "") @@ -1713,11 +1764,11 @@ local check_line_first_word = end end if oldsub == newsub then -%<dbg> texio.write_nl("BOLnewsub=" .. newsub) +%<dbg> texio.write_nl('BOLnewsub=' .. newsub) match = true end if oldfirst == newfirst and utf8_len(newfirst) >= MinFull then -%<dbg> texio.write_nl("BOLnewfirst=" .. newfirst) +%<dbg> texio.write_nl('BOLnewfirst=' .. newfirst) if utf8_len(newfirst) > MinPart or not match then oldsub = oldfirst newsub = newfirst @@ -1741,7 +1792,7 @@ local check_line_first_word = end end newsub = utf8_gsub(newsub, "_+$", "") --$ -%<dbg> texio.write_nl("BOLfullmatch=" .. newsub) +%<dbg> texio.write_nl('BOLfullmatch=' .. newsub) local msg = "B.O.L. MATCH=" .. newsub log_flaw(msg, line, colno, footnote) % \end{macrocode} @@ -1832,7 +1883,7 @@ local check_page_first_word = function (node, colno, footnote) match = true end end -%<dbg> texio.write_nl("FinalWord=" .. new) +%<dbg> texio.write_nl('FinalWord=' .. new) if match then local msg = "ShortFinalWord=" .. new log_flaw(msg, 1, colno, footnote) @@ -1981,11 +2032,11 @@ local footnoterule_ahead = function (head) if totalht == 0 or totalht == ruleht then flag = true else -%<dbg> texio.write_nl(" ") -%<dbg> texio.write_nl("Not a footnoterule:") -%<dbg> texio.write(" KERN height=" .. ht1) -%<dbg> texio.write(" RULE height=" .. ht2) -%<dbg> texio.write(" KERN height=" .. ht3) +%<dbg> texio.write_nl(' ') +%<dbg> texio.write_nl('Not a footnoterule:') +%<dbg> texio.write(' KERN height=' .. ht1) +%<dbg> texio.write(' RULE height=' .. ht2) +%<dbg> texio.write(' KERN height=' .. ht3) end end end @@ -2013,15 +2064,108 @@ local check_EOP = function (node) body_bot = true elseif footnoterule_ahead(n) then body_bot = true -%<dbg> texio.write_nl("=> FOOTNOTE RULE ahead") -%<dbg> texio.write_nl("check_vtop: last line before footnotes") -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('=> FOOTNOTE RULE ahead') +%<dbg> texio.write_nl('check_vtop: last line before footnotes') +%<dbg> texio.write_nl(' ') end return page_bot, body_bot end % \end{macrocode} % \end{macro} % +% \changes{v0.85}{2023/09/07}{New function `check\_marginnote’.} +% +% \begin{macro}{check-marginnote} +% This function checks margin notes for overfull/underfull lines; +% It also warns if a margin note ends too low under the last line +% of text. +% \begin{macrocode} +local check_marginnote = function (head, line, colno, vpos, bpmn) + local OverfullLines = luatypo.OverfullLines + local UnderfullLines = luatypo.UnderfullLines + local MarginparPos = luatypo.MarginparPos + local margintol = luatypo.MParTol + local marginpp = tex.getdimen("marginparpush") + local pflag = false + local ofirst = true + local ufirst = true + local n = head.head + local bottom = vpos + if vpos <= bpmn then + bottom = bpmn + marginpp + end +%<dbg> texio.write_nl('*** Margin note? ***') + repeat + if n and (n.id == GLUE or n.id == PENALTY) then +%<dbg> texio.write_nl(' Found GLUE or PENALTY') + n = n.next + elseif n and n.id == VLIST then +%<dbg> texio.write_nl(' Found VLIST') + n = n.head + end + until not n or (n.id == HLIST and n.subtype == LINE) + local head = n + if head then +%<dbg> texio.write_nl(' Found HLIST') + else +%<dbg> texio.write_nl(' No text line found.') + end +%<dbg> local l = 0 + local last = head + while head do + local next = head.next + if head.id == HLIST and head.subtype == LINE then +%<dbg> l = l + 1 +%<dbg> texio.write_nl(' Checking line ' .. l) + bottom = bottom + head.height + head.depth + local first = head.head + local linewd = head.width + local hmax = linewd + tex.hfuzz + local w,h,d = dimensions(1,2,0, first) + local Stretch = math.max(luatypo.Stretch/100,1) + if w > hmax and OverfullLines then +%<dbg> texio.write(': Overfull!') + pflag = true + local COLOR = luatypo.colortbl[8] + color_line (head, COLOR) + if ofirst then + local msg = "OVERFULL line(s) in margin note" + log_flaw(msg, line, colno, false) + ofirst = false + end + elseif head.glue_set > Stretch and head.glue_sign == 1 and + head.glue_order == 0 and UnderfullLines then +%<dbg> texio.write(': Underfull!') + pflag = true + local COLOR = luatypo.colortbl[9] + color_line (head, COLOR) + if ufirst then + local msg = "UNDERFULL line(s) in margin note" + log_flaw(msg, line, colno, false) + ufirst = false + end + end + end + last = head + head = next + end + local textht = tex.getdimen("textheight") +%<dbg> local tht = string.format("%.1fpt", textht/65536) +%<dbg> local bott = string.format("%.1fpt", bottom/65536) +%<dbg> texio.write_nl(' Bottom=' .. bott) +%<dbg> texio.write(' TextBottom=' ..tht) + if bottom > textht + margintol and MarginparPos then + pflag = true + local COLOR = luatypo.colortbl[17] + color_line (last, COLOR) + local msg = "Margin note too low" + log_flaw(msg, line, colno, false) + end + return bottom, pflag +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. @@ -2038,29 +2182,29 @@ local get_pagebody = function (head) repeat fn = fn.next until fn.id == VLIST and fn.height > 0 -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl(' ') %<dbg> local ht = string.format("%.1fpt", fn.height/65536) %<dbg> local dp = string.format("%.1fpt", fn.depth/65536) -%<dbg> texio.write_nl("get_pagebody: TOP VLIST") -%<dbg> texio.write(" ht=" .. ht .. " dp=" .. dp) +%<dbg> texio.write_nl('get_pagebody: TOP VLIST') +%<dbg> texio.write(' ht=' .. ht .. ' dp=' .. dp) first = fn.list for n in traverse_id(VLIST,first) do if n.subtype == 0 and n.height == textht then %<dbg> local ht = string.format("%.1fpt", n.height/65536) -%<dbg> texio.write_nl("BODY found: ht=" .. ht) -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('BODY found: ht=' .. ht) +%<dbg> texio.write_nl(' ') body = n break else -%<dbg> texio.write_nl("Skip short VLIST:") +%<dbg> texio.write_nl('Skip short VLIST:') %<dbg> local ht = string.format("%.1fpt", n.height/65536) %<dbg> local dp = string.format("%.1fpt", n.depth/65536) -%<dbg> texio.write(" ht=" .. ht .. " dp=" .. dp) +%<dbg> texio.write(' ht=' .. ht .. ' dp=' .. dp) first = n.list for n in traverse_id(VLIST,first) do if n.subtype == 0 and n.height == textht then %<dbg> local ht = string.format("%.1fpt", n.height/65536) -%<dbg> texio.write_nl(" BODY: ht=" .. ht) +%<dbg> texio.write_nl(' BODY: ht=' .. ht) body = n break end @@ -2068,7 +2212,7 @@ local get_pagebody = function (head) end end if not body then - texio.write_nl("***lua-typo ERROR: PAGE BODY *NOT* FOUND!***") + texio.write_nl('***lua-typo ERROR: PAGE BODY *NOT* FOUND!***') end return body end @@ -2120,6 +2264,7 @@ check_vtop = function (top, colno, vpos) vpos_min = vpos_min * 1.5 local linewd = tex.getdimen("textwidth") local first_bot = true + local done = false local footnote = false local ftnsplit = false local orphanflag = false @@ -2135,6 +2280,7 @@ check_vtop = function (top, colno, vpos) local pageline = 0 local ftnline = 0 local line = 0 + local bpmn = 0 local body_bottom = false local page_bottom = false local pageflag = false @@ -2154,9 +2300,11 @@ check_vtop = function (top, colno, vpos) % \end{macrocode} % This is a text line, store its width, increment counters % |pageline| or |ftnline| and |line| (for |log_flaw|). -% Let’s update |vpos| (vertical position in `sp’ units) too. +% Let’s update |vpos| (vertical position in `sp’ units) and +% set flag |done| to |true|. % \begin{macrocode} vpos = vpos + head.height + head.depth + done = true local linewd = head.width local first = head.head local ListItem = false @@ -2256,15 +2404,15 @@ check_vtop = function (top, colno, vpos) % Does the first word and the one on the previous line match % (except lists)? % \begin{macrocode} - if FirstWordMatch then - local flag = not ListItem and (line > 1) - firstwd, flag = - check_line_first_word(firstwd, first, line, colno, - flag, footnote) - if flag then - pageflag = true - end + if FirstWordMatch then + local flag = not ListItem and (line > 1) + firstwd, flag = + check_line_first_word(firstwd, first, line, colno, + flag, footnote) + if flag then + pageflag = true end + end % \end{macrocode} % Check the page’s first word (end of sentence?). % \begin{macrocode} @@ -2277,12 +2425,19 @@ check_vtop = function (top, colno, vpos) % and |pn| are the last two nodes. % \begin{macrocode} local ln = slide(first) +% \end{macrocode} +% Skip a possible RULE pointing an overfull line. +% \begin{macrocode} + if ln.id == RULE and ln.subtype == 0 then + ln = ln.prev + end local pn = ln.prev if pn and pn.id == GLUE and pn.subtype == PARFILL then % \end{macrocode} % CASE 1: this line ends the paragraph, reset |ftnsplit| and |orphan| % flags to false… % \begin{macrocode} +%<dbg> texio.write_nl('EOL CASE 1: end of paragraph') hyphcount = 0 ftnsplit = false orphanflag = false @@ -2302,8 +2457,8 @@ check_vtop = function (top, colno, vpos) local llwd = linewd - PFskip %<dbg> local PFskip_pt = string.format("%.1fpt", PFskip/65536) %<dbg> local llwd_pt = string.format("%.1fpt", llwd/65536) -%<dbg> texio.write_nl("PFskip= " .. PFskip_pt) -%<dbg> texio.write(" llwd= " .. llwd_pt) +%<dbg> texio.write_nl('PFskip= ' .. PFskip_pt) +%<dbg> texio.write(' llwd= ' .. llwd_pt) % \end{macrocode} % |llwd| is the line’s length. Is it too short? % \begin{macrocode} @@ -2347,6 +2502,7 @@ check_vtop = function (top, colno, vpos) % \end{macrocode} % CASE 2: the current line ends with an hyphen. % \begin{macrocode} +%<dbg> texio.write_nl('EOL CASE 2: hyphen') hyphcount = hyphcount + 1 if hyphcount > HYPHmax and RepeatedHyphens then local COLOR = luatypo.colortbl[3] @@ -2400,10 +2556,11 @@ check_vtop = function (top, colno, vpos) end % \end{macrocode} % CASE 3: the current line ends with anything else (\node{glyph}, -% \node{mkern}, \node{hlist}, etc.), reset |hyphcount|, check for -% `LastWordMatch’ and `EOLShortWords’. +% \node{mkern}, \node{hlist}, etc.), then reset |hyphcount| and +% check for `LastWordMatch’ and `EOLShortWords’. % \begin{macrocode} else +%<dbg> texio.write_nl('EOL CASE 3') hyphcount = 0 % \end{macrocode} % Track matching words at end of line and short words. @@ -2485,6 +2642,36 @@ check_vtop = function (top, colno, vpos) color_line (head, COLOR) backpar = false end + elseif head and head.id == HLIST and head.subtype == BOX and + head.width > 0 then + if head.height == 0 then +% \end{macrocode} +% This is a possible margin note. +% \begin{macrocode} + bpmn, pflag = check_marginnote(head, line, colno, vpos, bpmn) + if pflag then pageflag = true end + else +% \end{macrocode} +% Leave |check_vtop| if a two columns box starts. +% \begin{macrocode} + local hf = head.list + if hf and hf.id == VLIST and hf.subtype == 0 then +%<dbg> texio.write_nl('check_vtop: BREAK => multicol') +%<dbg> texio.write_nl(' ') + break + else +% \end{macrocode} +% \changes{v0.80}{2023/04/18}{hlist-2: added detection of page bottom +% and increment line number and vpos.} +% This is an |\hbox| (f.i.\ centred), let’s update |vpos|, line +% and check for page bottom +% \begin{macrocode} + vpos = vpos + head.height + head.depth + pageline = pageline + 1 + line = pageline + page_bottom, body_bottom = check_EOP (nextnode) + end + end elseif head.id == HLIST and (head.subtype == EQN or head.subtype == ALIGN) and (head.height > 0 or head.depth > 0) then @@ -2561,8 +2748,8 @@ check_vtop = function (top, colno, vpos) % If it is, set the |footnote| flag and reset some counters % and flags for the coming footnote lines. % \begin{macrocode} -%<dbg> texio.write_nl("check_vtop: footnotes start") -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('check_vtop: footnotes start') +%<dbg> texio.write_nl(' ') footnote = true ftnline = 0 body_bottom = false @@ -2580,15 +2767,15 @@ check_vtop = function (top, colno, vpos) if first_bot then %<dbg> local vpos_pt = string.format("%.1fpt", vpos/65536) %<dbg> local vmin_pt = string.format("%.1fpt", vpos_min/65536) -%<dbg> texio.write_nl("pageline=" .. pageline) -%<dbg> texio.write_nl("vpos=" .. vpos_pt) -%<dbg> texio.write(" vpos_min=" .. vmin_pt) +%<dbg> texio.write_nl('pageline=' .. pageline) +%<dbg> texio.write_nl('vpos=' .. vpos_pt) +%<dbg> texio.write(' vpos_min=' .. vmin_pt) %<dbg> if page_bottom then %<dbg> local tht = tex.getdimen("textheight") %<dbg> local tht_pt = string.format("%.1fpt", tht/65536) -%<dbg> texio.write(" textheight=" .. tht_pt) +%<dbg> texio.write(' textheight=' .. tht_pt) %<dbg> end -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl(' ') if pageline > 1 and pageline < PAGEmin and vpos < vpos_min and ShortPages then pageshort = true @@ -2619,38 +2806,20 @@ check_vtop = function (top, colno, vpos) % This is a |\vbox|, let’s update |vpos|. % \begin{macrocode} vpos = vpos + head.height + head.depth - elseif head.id == HLIST and head.subtype == BOX then -% \end{macrocode} -% \changes{v0.80}{2023/04/18}{hlist-2: added detection of page bottom -% and increment line number and vpos.} -% -% This is an |\hbox| (f.i.\ centred), let’s update |vpos|, line -% and check for page bottom -% \begin{macrocode} - vpos = vpos + head.height + head.depth - pageline = pageline + 1 - line = pageline - page_bottom, body_bottom = check_EOP (nextnode) - local hf = head.list -% \end{macrocode} -% Leave |check_vtop| if a two columns box starts. -% \begin{macrocode} - if hf and hf.id == VLIST and hf.subtype == 0 then -%<dbg> texio.write_nl("check_vtop: BREAK => multicol") -%<dbg> texio.write_nl(" ") - break - end +%<dbg> local tht = head.height + head.depth +%<dbg> local tht_pt = string.format("%.1fpt", tht/65536) +%<dbg> texio.write(' vbox: height=' .. tht_pt) end head = nextnode end %<dbg> if nextnode then -%<dbg> texio.write("Exit check_vtop, next=") +%<dbg> texio.write('Exit check_vtop, next=') %<dbg> texio.write(tostring(node.type(nextnode.id))) -%<dbg> texio.write("-".. nextnode.subtype) +%<dbg> texio.write('-'.. nextnode.subtype) %<dbg> else -%<dbg> texio.write_nl("Exit check_vtop, next=nil") +%<dbg> texio.write_nl('Exit check_vtop, next=nil') %<dbg> end -%<dbg> texio.write_nl("") +%<dbg> texio.write_nl('') % \end{macrocode} % Update the list of flagged pages avoiding duplicates: % \begin{macrocode} @@ -2661,9 +2830,10 @@ check_vtop = function (top, colno, vpos) luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", " end end - return head + return head, done % \end{macrocode} % |head| is nil unless |check_vtop| exited on a two column start. +% |done| is true unless |check_vtop| found no text line. % \begin{macrocode} end % \end{macrocode} @@ -2686,49 +2856,71 @@ end % \begin{macrocode} luatypo.check_page = function (head) local textwd = tex.getdimen("textwidth") - local vpos = 0 - local n2, n3, col, colno + local textht = tex.getdimen("textheight") + local checked, boxed, n2, n3, col, colno local body = get_pagebody(head) + local pageno = tex.getcount("c@page") + local vpos = 0 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 + local next + local count = 0 +%<dbg> texio.write_nl('Body=' .. tostring(node.type(top.id))) +%<dbg> texio.write('-' .. tostring(top.subtype)) +%<dbg> texio.write('; First=' .. tostring(node.type(first.id))) +%<dbg> texio.write('-' .. tostring(first.subtype)) +%<dbg> texio.write_nl(' ') + if ((first and first.id == HLIST and first.subtype == BOX) or + (first and first.id == VLIST and first.subtype == 0)) and + (first.width == textwd and first.height > 0 and not boxed) then % \end{macrocode} % Some classes (\cls{memoir}, \cls{tugboat} …) use one more level -% of bowing, let’s step down one level. +% of bowing for two columns, let’s step down one level. % \begin{macrocode} %<dbg> local boxwd = string.format("%.1fpt", first.width/65536) -%<dbg> texio.write_nl("One step down: boxwd=" .. boxwd) -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('One step down: boxwd=' .. boxwd) +%<dbg> texio.write_nl(' ') top = body.list - first = top.list +% \end{macrocode} +% A float on top of a page is a VLIST-0 included in a VLIST-0 (body), +% it should not trigger this step down. Workaround: the body will be +% read again. +% \begin{macrocode} + if first.id == VLIST then + boxed = body + end end % \end{macrocode} % Main loop: % \begin{macrocode} while top do first = top.list -%<dbg> texio.write_nl("Page loop: top=" .. tostring(node.type(top.id))) -%<dbg> texio.write("-" .. top.subtype) -%<dbg> texio.write_nl(" ") + next = top.next +%<dbg> count = count + 1 +%<dbg> texio.write_nl('Page loop' .. count) +%<dbg> texio.write(': top=' .. tostring(node.type(top.id))) +%<dbg> texio.write('-' .. tostring(top.subtype)) +%<dbg> if first then +%<dbg> texio.write(' first=' .. tostring(node.type(first.id))) +%<dbg> texio.write('-' .. tostring(first.subtype)) +%<dbg> end if top and top.id == VLIST and top.subtype == 0 and - top.width > textwd/2 then + top.width > textwd/2 then % \end{macrocode} % Single column, run |check_vtop| on the top vlist. % \begin{macrocode} %<dbg> local boxht = string.format("%.1fpt", top.height/65536) %<dbg> local boxwd = string.format("%.1fpt", top.width/65536) -%<dbg> texio.write_nl("**VLIST: ") +%<dbg> texio.write_nl('**VLIST: ') %<dbg> texio.write(tostring(node.type(top.id))) -%<dbg> texio.write("-" .. top.subtype) -%<dbg> texio.write(" wd=" .. boxwd .. " ht=" .. boxht) -%<dbg> texio.write_nl(" ") - local next = check_vtop(top,colno,vpos) - if next then - top = next - elseif top then - top = top.next +%<dbg> texio.write('-' .. tostring(top.subtype)) +%<dbg> texio.write(' wd=' .. boxwd .. ' ht=' .. boxht) +%<dbg> texio.write_nl(' ') + local n, ok = check_vtop(top,colno,vpos) + if ok then checked = true end + if n then + next = n end elseif (top and top.id == HLIST and top.subtype == BOX) and (first and first.id == VLIST and first.subtype == 0) and @@ -2737,23 +2929,24 @@ luatypo.check_page = function (head) % Two or more columns, each one is boxed in a vlist.\par % Run |check_vtop| on every column. % \begin{macrocode} -%<dbg> texio.write_nl("**MULTICOL type1:") -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('**MULTICOL type1:') +%<dbg> texio.write_nl(' ') colno = 0 for col in traverse_id(VLIST, first) do colno = colno + 1 -%<dbg> texio.write_nl("Start of col." .. colno) -%<dbg> texio.write_nl(" ") - check_vtop(col,colno,vpos) -%<dbg> texio.write_nl("End of col." .. colno) -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('Start of col.' .. colno) +%<dbg> texio.write_nl(' ') + local n, ok = check_vtop(col,colno,vpos) + if ok then checked = true end +%<dbg> texio.write_nl('End of col.' .. colno) +%<dbg> texio.write_nl(' ') end colno = nil top = top.next -%<dbg> texio.write_nl("MULTICOL type1 END: next=") +%<dbg> texio.write_nl('MULTICOL type1 END: next=') %<dbg> texio.write(tostring(node.type(top.id))) -%<dbg> texio.write("-" .. top.subtype) -%<dbg> texio.write_nl(" ") +%<dbg> texio.write('-' .. tostring(top.subtype)) +%<dbg> texio.write_nl(' ') 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 @@ -2762,25 +2955,38 @@ luatypo.check_page = function (head) % holds a vlist.\par % Run |check_vtop| on every column. % \begin{macrocode} -%<dbg> texio.write_nl("**MULTICOL type2:") -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('**MULTICOL type2:') +%<dbg> texio.write_nl(' ') colno = 0 for n in traverse_id(HLIST, first) do colno = colno + 1 local col = n.list if col and col.list then -%<dbg> texio.write_nl("Start of col." .. colno) -%<dbg> texio.write_nl(" ") - check_vtop(col,colno,vpos) -%<dbg> texio.write_nl("End of col." .. colno) -%<dbg> texio.write_nl(" ") +%<dbg> texio.write_nl('Start of col.' .. colno) +%<dbg> texio.write_nl(' ') + local n, ok = check_vtop(col,colno,vpos) + if ok then checked = true end +%<dbg> texio.write_nl('End of col.' .. colno) +%<dbg> texio.write_nl(' ') end end colno = nil - top = top.next - else - top = top.next end +% \end{macrocode} +% Workaround for top floats: check the whole body again. +% \begin{macrocode} + if boxed and not next then + next = boxed + boxed = nil + end + top = next + end + if not checked then + luatypo.failedlist = luatypo.failedlist .. tostring(pageno) .. ", " +%<dbg> texio.write_nl(' ') +%<dbg> texio.write_nl('WARNING: no text line found on page ') +%<dbg> texio.write(tostring(pageno)) +%<dbg> texio.write_nl(' ') end return true end @@ -2842,6 +3048,7 @@ return luatypo.check_page \luatypoSetColor{14}{cyan}% Footnote split \luatypoSetColor{15}{red}% Too short first (final) word on the page \luatypoSetColor{16}{LTline}% Line color for multiple flaws + \luatypoSetColor{17}{red}% Margin note ending too low \luatypoBackPI=1em\relax \luatypoBackFuzz=2pt\relax \ifdim\parindent=0pt \luatypoLLminWD=20pt\relax @@ -2852,12 +3059,13 @@ return luatypo.check_page \luatypoMinFull=3\relax \luatypoMinPart=4\relax \luatypoMinLen=4\relax + \luatypoMarginparTol=\baselineskip }% % \end{macrocode} % \iffalse %</sty> % \fi -% \clearpage +% % \section{Configuration file} % % \iffalse @@ -2892,8 +3100,12 @@ return luatypo.check_page \luatypoMinPart=4\relax %% Minimum number of characters for the first word on a page if it ends -%% a sentence. -\luatypoMinLen=4\relax +%% a sentence (version >= 0.65). +\ifdefined\luatypoMinLen \luatypoMinLen=4\relax\fi + +%% Acceptable marginpars must end at |\luatypoMarginparTol| under +%% the page’s last line or above (version >= 0.85). +\ifdefined\luatypoMarginparTol \luatypoMarginparTol=\baselineskip \fi %% Default colours = red, cyan, blue, LTgrey, LTred, LTline. \definecolor{LTgrey}{gray}{0.6} @@ -2915,6 +3127,7 @@ return luatypo.check_page \luatypoSetColor{14}{cyan}% Footnote split \luatypoSetColor{15}{red}% Too short first (final) word on the page \luatypoSetColor{16}{LTline}% Line color for multiple flaws +\luatypoSetColor{17}{red}% Margin note ending too low %% Language specific settings (example for French): %% short words (two letters max) to be avoided at end of lines. @@ -2984,12 +3197,12 @@ return luatypo.check_page \AtVeryEndDocument{% \directlua{ - texio.write_nl(" ") - texio.write_nl("***************************") - texio.write_nl("*** PAGE SCANNING ONLY: ***") - texio.write_nl("*** NO CHECK PERFORMED! ***") - texio.write_nl("***************************") - texio.write_nl(" ") + texio.write_nl(' ') + texio.write_nl('***************************') + texio.write_nl('*** PAGE SCANNING ONLY: ***') + texio.write_nl('*** NO CHECK PERFORMED! ***') + texio.write_nl('***************************') + texio.write_nl(' ') }% } @@ -3003,6 +3216,7 @@ luatypo.single = { } luatypo.double = { } luatypo.colortbl = { } luatypo.pagelist = " " +luatypo.failedlist = " " local hyphcount = 0 local pageno = 0 @@ -3055,20 +3269,20 @@ local get_pagebody = function (head) for n in traverse_id(VLIST,first) do if n.subtype == 0 and n.height == textht then local ht = string.format("%.1fpt", n.height/65536) - texio.write_nl(" BODY: " .. ht) - texio.write_nl(" ") + texio.write_nl(' BODY: ' .. ht) + texio.write_nl(' ') body = n break else local ht = string.format("%.1fpt", n.height/65536) local dp = string.format("%.1fpt", n.depth/65536) - texio.write_nl(" SKIP vlist: ht=" .. ht .. " dp=" .. dp) + texio.write_nl(' SKIP vlist: ht=' .. ht .. ' dp=' .. dp) first = n.list for n in traverse_id(VLIST,first) do if n.subtype == 0 and n.height == textht then ht = string.format("%.1fpt", n.height/65536) - texio.write_nl(" BODY: " .. ht) - texio.write_nl(" ") + texio.write_nl(' BODY: ' .. ht) + texio.write_nl(' ') body = n break end @@ -3076,7 +3290,7 @@ local get_pagebody = function (head) end end if not body then - texio.write_nl("***lua-typo ERROR: PAGE BODY *NOT* FOUND!***") + texio.write_nl('***lua-typo ERROR: PAGE BODY *NOT* FOUND!***') end return body end @@ -3088,7 +3302,7 @@ local print_subtype = function (node,parent) elseif n.id == GLUE then texio.write(tostring(n.subtype)) real_pt = string.format("%.1fpt", effective_glue(n,parent)/65536) - texio.write(" realwd=" .. real_pt) + texio.write(' realwd=' .. real_pt) elseif n.id == DISC then local c = "" if n.replace then @@ -3102,24 +3316,24 @@ local print_subtype = function (node,parent) for nn in traverse_id(GLYPH, n.pre) do c = c .. utf8.char(nn.char) end - texio.write(" pre=" .. c) + texio.write(' pre=' .. c) end if n.post then c = "" for nn in traverse_id(GLYPH, n.post) do c = c .. utf8.char(nn.char) end - texio.write(" post=" .. c) + texio.write(' post=' .. c) end elseif n.subtype and n.subtype < 256 then texio.write(tostring(n.subtype)) if n.height and n.depth then ht_pt = string.format("%.1fpt", n.height/65536) dp_pt = string.format("%.1fpt", n.depth/65536) - texio.write(" ht=" .. ht_pt .. " dp=" .. dp_pt) + texio.write(' ht=' .. ht_pt .. ' dp=' .. dp_pt) if n.width then wd_pt = string.format("%.1fpt", n.width/65536) - texio.write(" wd=" .. wd_pt) + texio.write(' wd=' .. wd_pt) end end end @@ -3133,58 +3347,62 @@ local scanvlist = function (head) local textht = tex.getdimen("textheight") local textwd = tex.getdimen("textwidth") local pageno = tex.getcount("c@page") - texio.write_nl("PAGE " .. pageno) - texio.write(" textheight=".. string.format("%.1fpt", textht/65536)) - texio.write(" textwidth=" .. string.format("%.1fpt", textwd/65536)) + texio.write_nl('PAGE ' .. pageno) + texio.write(' textheight='.. string.format("%.1fpt", textht/65536)) + texio.write(' textwidth=' .. string.format("%.1fpt", textwd/65536)) local linecount = 0 local body, parent, ht_pt, dp_pt, real_pt body = get_pagebody(head) if body then parent = body head = body.head + texio.write_nl('Enter outer vbox (body) of type ') + texio.write(tostring(node.type(body.id))) + texio.write('-') + texio.write(tostring(body.subtype)) end - while (head) do + while head do texio.write_nl(tostring(node.type(head.id))) - texio.write("-") + texio.write('-') print_subtype(head,body) if head.kern then ht_pt = string.format("%.1fpt", head.kern/65536) - texio.write(" KERN ht/wd=" .. ht_pt) + texio.write(' KERN ht/wd=' .. ht_pt) end local next = head.next local first = head.head for n in traverse(first) do parent = head - texio.write_nl(" " .. tostring(node.type(n.id))) - texio.write("-") + texio.write_nl(' ' .. tostring(node.type(n.id))) + texio.write('-') print_subtype(n,head) if n.id == VLIST or n.id == HLIST then local ff = n.head for nn in traverse(ff) do parent = n - texio.write_nl(" " .. tostring(node.type(nn.id))) - texio.write("-") + texio.write_nl(' ' .. tostring(node.type(nn.id))) + texio.write('-') print_subtype(nn,n) if nn.id == VLIST or nn.id == HLIST then local f3 = nn.head for n3 in traverse(f3) do parent = nn - texio.write_nl(" " .. tostring(node.type(n3.id))) - texio.write("-") + texio.write_nl(' ' .. tostring(node.type(n3.id))) + texio.write('-') print_subtype(n3,nn) if n3.id == VLIST or n3.id == HLIST then local f4 = n3.head for n4 in traverse(f4) do parent = n3 - texio.write_nl(" " .. tostring(node.type(n4.id))) - texio.write("-") + texio.write_nl(' ' .. tostring(node.type(n4.id))) + texio.write('-') print_subtype(n4,n3) if n4.id == VLIST or n4.id == HLIST then local f5 = n4.head for n5 in traverse(f5) do parent = n4 - texio.write_nl(" " .. tostring(node.type(n4.id))) - texio.write("-") + texio.write_nl(' ' .. tostring(node.type(n5.id))) + texio.write('-') print_subtype(n5,n4) end 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 index fe9b2438312..32f6ade6cf8 100644 --- a/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg +++ b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.cfg @@ -24,8 +24,12 @@ \luatypoMinPart=4\relax %% Minimum number of characters for the first word on a page if it ends -%% a sentence. -\luatypoMinLen=4\relax +%% a sentence (version >= 0.65). +\ifdefined\luatypoMinLen \luatypoMinLen=4\relax\fi + +%% Acceptable marginpars must end at |\luatypoMarginparTol| under +%% the page’s last line or above (version >= 0.85). +\ifdefined\luatypoMarginparTol \luatypoMarginparTol=\baselineskip \fi %% Default colours = red, cyan, blue, LTgrey, LTred, LTline. \definecolor{LTgrey}{gray}{0.6} @@ -47,6 +51,7 @@ \luatypoSetColor{14}{cyan}% Footnote split \luatypoSetColor{15}{red}% Too short first (final) word on the page \luatypoSetColor{16}{LTline}% Line color for multiple flaws +\luatypoSetColor{17}{red}% Margin note ending too low %% Language specific settings (example for French): %% short words (two letters max) to be avoided at end of lines. 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 ef9040c4013..92f1c05ba18 100644 --- a/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty +++ b/Master/texmf-dist/tex/lualatex/lua-typo/lua-typo.sty @@ -11,10 +11,10 @@ %% \NeedsTeXFormat{LaTeX2e}[2021/06/01] \ProvidesPackage{lua-typo} - [2023-04-28 v.0.80 Daniel Flipo] + [2023-09-13 v.0.85 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} +\DeclareCurrentRelease{}{2023-09-13} \ifdefined\directlua \RequirePackage{luatexbase,luacode,luacolor,atveryend} \else @@ -24,6 +24,7 @@ \newdimen\luatypoLLminWD \newdimen\luatypoBackPI \newdimen\luatypoBackFuzz +\newdimen\luatypoMarginparTol \newcount\luatypoStretchMax \newcount\luatypoHyphMax \newcount\luatypoPageMin @@ -56,6 +57,7 @@ luatypo = { } LastWordMatch.if = LT@LastWordMatch , FootnoteSplit.if = LT@FootnoteSplit , ShortFinalWord.if = LT@ShortFinalWord , + MarginparPos.if = LT@MarginparPos , All.if = LT@All , All.code = \LT@ShortLinestrue \LT@ShortPagestrue \LT@OverfullLinestrue \LT@UnderfullLinestrue @@ -64,7 +66,7 @@ luatypo = { } \LT@ParLastHyphentrue \LT@EOLShortWordstrue \LT@FirstWordMatchtrue \LT@LastWordMatchtrue \LT@BackParindenttrue \LT@FootnoteSplittrue - \LT@ShortFinalWordtrue + \LT@ShortFinalWordtrue \LT@MarginparPostrue } \ProcessKeyOptions[luatypo] \AtEndOfPackage{% @@ -163,6 +165,12 @@ luatypo = { } \else \directlua{ luatypo.ShortFinalWord = false }% \fi + \ifLT@MarginparPos + \advance\luatypo@options by 1 + \directlua{ luatypo.MarginparPos = true }% + \else + \directlua{ luatypo.MarginparPos = false }% + \fi } \ifLT@ShowOptions \GenericWarning{* }{% @@ -187,6 +195,7 @@ luatypo = { } LastWordMatch [false]\MessageBreak FootnoteSplit [false]\MessageBreak ShortFinalWord [false]\MessageBreak + MarginparPos [false]\MessageBreak \MessageBreak *********************************************% \MessageBreak Lua-typo [ShowOptions] @@ -199,11 +208,12 @@ 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 - luatypo.BackFuzz = tex.dimen.luatypoBackFuzz + luatypo.MinFull = math.min(luatypo.MinPart,luatypo.MinFull) + luatypo.MinLen = tex.count.luatypoMinLen + luatypo.LLminWD = tex.dimen.luatypoLLminWD + luatypo.BackPI = tex.dimen.luatypoBackPI + luatypo.BackFuzz = tex.dimen.luatypoBackFuzz + luatypo.MParTol = tex.dimen.luatypoMarginparTol local tbl = luatypo.colortbl local map = { } for i,v in ipairs (luatypo.colortbl) do @@ -230,14 +240,27 @@ luatypo = { } texio.write_nl(' ') texio.write_nl('*************************************') if luatypo.pagelist == " " then - texio.write_nl('*** lua-typo: No Typo Flaws found.') + if luatypo.failedlist == " " then + texio.write_nl('*** lua-typo: No Typo Flaws found.') + else + texio.write_nl('*** WARNING: ') + texio.write('lua-typo failed to scan these pages:') + texio.write_nl('***' .. luatypo.failedlist) + texio.write_nl('*** Please report to the maintainer.') + end 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('*************************************') texio.write_nl(' ') + if luatypo.failedlist == " " then + else + local prt = "WARNING: lua-typo failed to scan pages " .. + luatypo.failedlist .. "\string\n\string\n" + luatypo.buffer = prt .. luatypo.buffer + end local fileout= tex.jobname .. ".typo" local out=io.open(fileout,"w+") out:write(luatypo.buffer) @@ -285,13 +308,14 @@ luatypo = { } \endgroup } \begin{luacode} -luatypo.colortbl = { } -luatypo.map = { } -luatypo.single = { } -luatypo.double = { } -luatypo.pagelist = " " -luatypo.buffer = "List of typographic flaws found for " - .. tex.jobname .. ".pdf:\string\n\string\n" +luatypo.colortbl = { } +luatypo.map = { } +luatypo.single = { } +luatypo.double = { } +luatypo.pagelist = " " +luatypo.failedlist = " " +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 @@ -894,6 +918,75 @@ local check_EOP = function (node) end return page_bot, body_bot end +local check_marginnote = function (head, line, colno, vpos, bpmn) + local OverfullLines = luatypo.OverfullLines + local UnderfullLines = luatypo.UnderfullLines + local MarginparPos = luatypo.MarginparPos + local margintol = luatypo.MParTol + local marginpp = tex.getdimen("marginparpush") + local pflag = false + local ofirst = true + local ufirst = true + local n = head.head + local bottom = vpos + if vpos <= bpmn then + bottom = bpmn + marginpp + end + repeat + if n and (n.id == GLUE or n.id == PENALTY) then + n = n.next + elseif n and n.id == VLIST then + n = n.head + end + until not n or (n.id == HLIST and n.subtype == LINE) + local head = n + if head then + else + end + local last = head + while head do + local next = head.next + if head.id == HLIST and head.subtype == LINE then + bottom = bottom + head.height + head.depth + local first = head.head + local linewd = head.width + local hmax = linewd + tex.hfuzz + local w,h,d = dimensions(1,2,0, first) + local Stretch = math.max(luatypo.Stretch/100,1) + if w > hmax and OverfullLines then + pflag = true + local COLOR = luatypo.colortbl[8] + color_line (head, COLOR) + if ofirst then + local msg = "OVERFULL line(s) in margin note" + log_flaw(msg, line, colno, false) + ofirst = false + end + elseif head.glue_set > Stretch and head.glue_sign == 1 and + head.glue_order == 0 and UnderfullLines then + pflag = true + local COLOR = luatypo.colortbl[9] + color_line (head, COLOR) + if ufirst then + local msg = "UNDERFULL line(s) in margin note" + log_flaw(msg, line, colno, false) + ufirst = false + end + end + end + last = head + head = next + end + local textht = tex.getdimen("textheight") + if bottom > textht + margintol and MarginparPos then + pflag = true + local COLOR = luatypo.colortbl[17] + color_line (last, COLOR) + local msg = "Margin note too low" + log_flaw(msg, line, colno, false) + end + return bottom, pflag +end local get_pagebody = function (head) local textht = tex.getdimen("textheight") local fn = head.list @@ -917,7 +1010,7 @@ local get_pagebody = function (head) end end if not body then - texio.write_nl("***lua-typo ERROR: PAGE BODY *NOT* FOUND!***") + texio.write_nl('***lua-typo ERROR: PAGE BODY *NOT* FOUND!***') end return body end @@ -949,6 +1042,7 @@ check_vtop = function (top, colno, vpos) vpos_min = vpos_min * 1.5 local linewd = tex.getdimen("textwidth") local first_bot = true + local done = false local footnote = false local ftnsplit = false local orphanflag = false @@ -964,6 +1058,7 @@ check_vtop = function (top, colno, vpos) local pageline = 0 local ftnline = 0 local line = 0 + local bpmn = 0 local body_bottom = false local page_bottom = false local pageflag = false @@ -973,6 +1068,7 @@ check_vtop = function (top, colno, vpos) if head.id == HLIST and head.subtype == LINE and (head.height > 0 or head.depth > 0) then vpos = vpos + head.height + head.depth + done = true local linewd = head.width local first = head.head local ListItem = false @@ -1024,20 +1120,23 @@ check_vtop = function (top, colno, vpos) elseif not footnote then parline = parline + 1 end - if FirstWordMatch then - local flag = not ListItem and (line > 1) - firstwd, flag = - check_line_first_word(firstwd, first, line, colno, - flag, footnote) - if flag then - pageflag = true - end + if FirstWordMatch then + local flag = not ListItem and (line > 1) + firstwd, flag = + check_line_first_word(firstwd, first, line, colno, + flag, footnote) + if flag then + pageflag = true end + end if ShortFinalWord and pageline == 1 and parline > 1 and check_page_first_word(first, colno, footnote) then pageflag = true end local ln = slide(first) + if ln.id == RULE and ln.subtype == 0 then + ln = ln.prev + end local pn = ln.prev if pn and pn.id == GLUE and pn.subtype == PARFILL then hyphcount = 0 @@ -1189,6 +1288,22 @@ check_vtop = function (top, colno, vpos) color_line (head, COLOR) backpar = false end + elseif head and head.id == HLIST and head.subtype == BOX and + head.width > 0 then + if head.height == 0 then + bpmn, pflag = check_marginnote(head, line, colno, vpos, bpmn) + if pflag then pageflag = true end + else + local hf = head.list + if hf and hf.id == VLIST and hf.subtype == 0 then + break + else + vpos = vpos + head.height + head.depth + pageline = pageline + 1 + line = pageline + page_bottom, body_bottom = check_EOP (nextnode) + end + end elseif head.id == HLIST and (head.subtype == EQN or head.subtype == ALIGN) and (head.height > 0 or head.depth > 0) then @@ -1268,15 +1383,6 @@ check_vtop = function (top, colno, vpos) vpos = vpos + head.kern elseif head.id == VLIST then vpos = vpos + head.height + head.depth - elseif head.id == HLIST and head.subtype == BOX then - vpos = vpos + head.height + head.depth - pageline = pageline + 1 - line = pageline - page_bottom, body_bottom = check_EOP (nextnode) - local hf = head.list - if hf and hf.id == VLIST and hf.subtype == 0 then - break - end end head = nextnode end @@ -1287,30 +1393,37 @@ check_vtop = function (top, colno, vpos) luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", " end end - return head + return head, done end luatypo.check_page = function (head) local textwd = tex.getdimen("textwidth") - local vpos = 0 - local n2, n3, col, colno + local textht = tex.getdimen("textheight") + local checked, boxed, n2, n3, col, colno local body = get_pagebody(head) + local pageno = tex.getcount("c@page") + local vpos = 0 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 + local next + local count = 0 + if ((first and first.id == HLIST and first.subtype == BOX) or + (first and first.id == VLIST and first.subtype == 0)) and + (first.width == textwd and first.height > 0 and not boxed) then top = body.list - first = top.list + if first.id == VLIST then + boxed = body + end end while top do first = top.list + next = top.next if top and top.id == VLIST and top.subtype == 0 and - top.width > textwd/2 then - local next = check_vtop(top,colno,vpos) - if next then - top = next - elseif top then - top = top.next + top.width > textwd/2 then + local n, ok = check_vtop(top,colno,vpos) + if ok then checked = true end + if n then + next = n end elseif (top and top.id == HLIST and top.subtype == BOX) and (first and first.id == VLIST and first.subtype == 0) and @@ -1318,7 +1431,8 @@ luatypo.check_page = function (head) colno = 0 for col in traverse_id(VLIST, first) do colno = colno + 1 - check_vtop(col,colno,vpos) + local n, ok = check_vtop(col,colno,vpos) + if ok then checked = true end end colno = nil top = top.next @@ -1330,14 +1444,20 @@ luatypo.check_page = function (head) colno = colno + 1 local col = n.list if col and col.list then - check_vtop(col,colno,vpos) + local n, ok = check_vtop(col,colno,vpos) + if ok then checked = true end end end colno = nil - top = top.next - else - top = top.next end + if boxed and not next then + next = boxed + boxed = nil + end + top = next + end + if not checked then + luatypo.failedlist = luatypo.failedlist .. tostring(pageno) .. ", " end return true end @@ -1374,6 +1494,7 @@ return luatypo.check_page \luatypoSetColor{14}{cyan}% Footnote split \luatypoSetColor{15}{red}% Too short first (final) word on the page \luatypoSetColor{16}{LTline}% Line color for multiple flaws + \luatypoSetColor{17}{red}% Margin note ending too low \luatypoBackPI=1em\relax \luatypoBackFuzz=2pt\relax \ifdim\parindent=0pt \luatypoLLminWD=20pt\relax @@ -1384,6 +1505,7 @@ return luatypo.check_page \luatypoMinFull=3\relax \luatypoMinPart=4\relax \luatypoMinLen=4\relax + \luatypoMarginparTol=\baselineskip }% %% %% |