%% %% 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’. %% \ProvidesPackage{lua-typo} [2021/05/13 v.0.50 Daniel Flipo] \ifdefined\DeclareRelease \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty} \DeclareCurrentRelease{}{2021-05-13} \else \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide access\MessageBreak to former versions of the lettrine package.% \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated% \MessageBreak 2020-01-01 or newer; reported} \fi \NeedsTeXFormat{LaTeX2e}[2021/06/01] \ifdefined\directlua \RequirePackage{luatexbase,luacode,luacolor} \RequirePackage{kvoptions,atveryend} \else \PackageError{This package is meant for LuaTeX only! Aborting} {No more information available, sorry!} \fi \newdimen\luatypoLLminWD \newdimen\luatypoBackPI \newdimen\luatypoBackFuzz \newcount\luatypoStretchMax \newcount\luatypoHyphMax \newcount\luatypoPageMin \newcount\luatypoMinFull \newcount\luatypoMinPart \newcount\luatypo@LANGno \newcount\luatypo@options \newtoks\luatypo@single \newtoks\luatypo@double \begin{luacode} luatypo = { } \end{luacode} \SetupKeyvalOptions{ family=luatypo, prefix=LT@, } \DeclareBoolOption[false]{ShowOptions} \DeclareBoolOption[false]{None} \DeclareBoolOption[false]{All} \DeclareBoolOption[false]{BackParindent} \DeclareBoolOption[false]{ShortLines} \DeclareBoolOption[false]{ShortPages} \DeclareBoolOption[false]{OverfullLines} \DeclareBoolOption[false]{UnderfullLines} \DeclareBoolOption[false]{Widows} \DeclareBoolOption[false]{Orphans} \DeclareBoolOption[false]{EOPHyphens} \DeclareBoolOption[false]{RepeatedHyphens} \DeclareBoolOption[false]{ParLastHyphen} \DeclareBoolOption[false]{EOLShortWords} \DeclareBoolOption[false]{FirstWordMatch} \DeclareBoolOption[false]{LastWordMatch} \DeclareBoolOption[false]{FootnoteSplit} \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 } \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@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 \MessageBreak *********************************************% \MessageBreak Lua-typo [ShowOptions] }% \fi \AtBeginDocument{% \directlua{ luatypo.HYPHmax = tex.count.luatypoHyphMax luatypo.PAGEmin = tex.count.luatypoPageMin luatypo.Stretch = tex.count.luatypoStretchMax luatypo.MinFull = tex.count.luatypoMinFull luatypo.MinPart = tex.count.luatypoMinPart luatypo.LLminWD = tex.dimen.luatypoLLminWD luatypo.BackPI = tex.dimen.luatypoBackPI luatypo.BackFuzz = tex.dimen.luatypoBackFuzz }% } \AtVeryEndDocument{% \ifnum\luatypo@options = 0 \LT@Nonetrue \fi \ifLT@None \directlua{ texio.write_nl(' ') texio.write_nl('***********************************') texio.write_nl('*** lua-typo loaded with NO option:') texio.write_nl('*** NO CHECK PERFORMED! ***') texio.write_nl('***********************************') texio.write_nl(' ') }% \else \directlua{ texio.write_nl(' ') texio.write_nl('*************************************') if luatypo.pagelist == "" then texio.write_nl('*** lua-typo: No Typo Flaws found.') else texio.write_nl('*** lua-typo: WARNING *************') texio.write_nl('The following pages need attention: ') 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 = string.char(c) luatypo.single[langno] = luatypo.single[langno] .. s end }% \else \PackageWarning{luatypo}{Unknown language "\luatypo@LANG", \MessageBreak \protect\luatypoOneChar\space command ignored}% \fi} \newcommand*{\luatypoTwoChars}[2]{% \def\luatypo@LANG{#1}\luatypo@double={#2}% \ifcsname l@\luatypo@LANG\endcsname \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax \directlua{ local langno = \the\luatypo@LANGno local string = \the\luatypo@double luatypo.double[langno] = " " for p, c in utf8.codes(string) do local s = string.char(c) luatypo.double[langno] = luatypo.double[langno] .. s end }% \else \PackageWarning{luatypo}{Unknown language "\luatypo@LANG", \MessageBreak \protect\luatypoTwoChars\space command ignored}% \fi} \newcommand*{\luatypoSetColor}[2]{% \begingroup \color{#2}% \directlua{luatypo.colortbl[#1]=\the\LuaCol@Attribute}% \endgroup } \begin{luacode} luatypo.single = { } luatypo.double = { } luatypo.colortbl = { } luatypo.pagelist = "" luatypo.buffer = "List of typographic flaws found for " .. tex.jobname .. ".tex:\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 split_lig = { } split_lig[0xFB00] = "ff" split_lig[0xFB01] = "fi" split_lig[0xFB02] = "fl" split_lig[0xFB03] = "ffi" split_lig[0xFB04] = "ffl" split_lig[0xFB05] = "st" split_lig[0xFB06] = "st" local DISC = node.id("disc") local GLYPH = node.id("glyph") local GLUE = node.id("glue") local KERN = node.id("kern") local 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 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, id = is_glyph(n) if b and not char_to_discard[b] then if b == 0x2019 then b = 0x27 end if b < 0x100 then str = str .. string.char(b) elseif split_lig[b] then local c = split_lig[b] if swap then c = string.reverse(c) end str = str .. c elseif n.subtype == LIGA and b > 0xE000 then local c = string.sub(b,-2) if swap then c = string.reverse(c) end str = str .. c end end elseif n and n.id == DISC then local pre = n.pre local post = n.post local c1 = "" local c2 = "" if pre and pre.char and pre.char ~= HYPH and pre.char < 0x100 then c1 = string.char(pre.char) end if post and post.char then if post.char < 0x100 then c2 = string.char(post.char) elseif split_lig[post.char] then c2 = split_lig[post.char] if swap then c2 = string.reverse(c2) end end end if swap then str = str .. c2 .. c1 else str = str .. c1 .. c2 end end local len = string.len(str) if string.find(str, "_") then len = len - 1 end return len, str end local check_last_word = function (old, node, line, flag) local COLOR = luatypo.colortbl[11] local match = false local new = "" local maxlen = 0 if flag and node then local swap = true local box, go local lastn = node while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC 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 = string.reverse(new) local MinFull = luatypo.MinFull local MinPart = luatypo.MinPart MinFull = math.min(MinPart,MinFull) local k = MinPart local oldlast = string.gsub (old, '.*_', '') local newlast = string.gsub (new, '.*_', '') local i = string.find(new, "_") if i and i > maxlen - MinPart + 1 then k = MinPart + 1 end local oldsub = string.sub(old,-k) local newsub = string.sub(new,-k) local l = string.len(new) if oldsub == newsub and l >= k then match = true elseif oldlast == newlast and string.len(newlast) >= MinFull then match = true oldsub = oldlast newsub = newlast k = string.len(newlast) end if match then local osub = oldsub local nsub = newsub while osub == nsub and k <= maxlen do k = k +1 osub = string.sub(old,-k) nsub = string.sub(new,-k) if osub == nsub then newsub = nsub end end pageflag = true newsub = string.gsub(newsub, '^_', '') local msg = "E.O.L. MATCH=" .. newsub log_flaw(msg, line, colno, footnote) oldsub = string.reverse(newsub) local newsub = "" local n = lastn repeat if n and n.id ~= GLUE then color_node(n, COLOR) 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 return new end local check_first_word = function (old, node, line, flag) local COLOR = luatypo.colortbl[10] local match = false local swap = false local new = "" local maxlen = 0 local 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 oldsub = "" local newsub = "" local oldfirst = string.gsub (old, '_.*', '') local newfirst = string.gsub (new, '_.*', '') local i = string.find(new, "_") if i and i <= MinPart then k = MinPart + 1 end local oldsub = string.sub(old,1,k) local newsub = string.sub(new,1,k) local l = string.len(newsub) if oldsub == newsub and l >= k then match = true elseif oldfirst == newfirst and string.len(newfirst) >= MinFull then match = true oldsub = oldfirst newsub = newfirst k = string.len(newfirst) end if match then local osub = oldsub local nsub = newsub while osub == nsub and k <= maxlen do k = k + 1 osub = string.sub(old,1,k) nsub = string.sub(new,1,k) if osub == nsub then newsub = nsub end end pageflag = true newsub = string.gsub(newsub, '_$', '') --$ local msg = "B.O.L. MATCH=" .. newsub log_flaw(msg, line, colno, footnote) oldsub = newsub local newsub = "" local k = string.len(oldsub) local n = start repeat if n and n.id ~= GLUE then color_node(n, COLOR) l, newsub = signature(n, newsub, swap) elseif n 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 end local check_regexpr = function (glyph, line) local pageno = tex.getcount("c@page") local COLOR = luatypo.colortbl[3] local lang = glyph.lang local match = false local lchar, id = is_glyph(glyph) local previous = glyph.prev if lang and luatypo.single[lang] then if lchar and lchar < 0x100 and previous and previous.id == GLUE then match = string.find(luatypo.single[lang], string.char(lchar)) if match then pageflag = true local msg = "RGX MATCH=" .. string.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 pchar < 0x100 and pprev and pprev.id == GLUE then local pattern = string.char(pchar) .. string.char(lchar) match = string.find(luatypo.double[lang], pattern) if match then pageflag = true 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 pchar < 0x100 and ppprev and ppprev.id == GLUE then local pattern = string.char(pchar) .. string.char(lchar) match = string.find(luatypo.double[lang], pattern) if match then pageflag = true local msg = "RGX MATCH=" .. pattern log_flaw(msg, line, colno, footnote) color_node(pprev,COLOR) color_node(glyph,COLOR) end end end end end end local show_pre_disc = function (disc, color) local n = disc while n and n.id ~= GLUE do color_node(n, color) n = n.prev end return n end 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 local footnoterule_ahead = function (head, debug) local n = head local flag = false if n and n.id == KERN and n.subtype == 1 then local htr = n.kern local ht1, ht2, ht3 n = n.next if n and n.id == RULE and n.subtype == 0 then htr = htr + n.height n = n.next if n and n.id == KERN and n.subtype == 1 then htr = htr + n.kern if htr == 0 then flag = true end end end end return flag end check_vtop = function (head, colno) local PAGEmin = luatypo.PAGEmin local HYPHmax = luatypo.HYPHmax local LLminWD = luatypo.LLminWD local BackPI = luatypo.BackPI local BackFuzz = luatypo.BackFuzz local BackParindent = luatypo.BackParindent local ShortLines = luatypo.ShortLines local ShortPages = luatypo.ShortPages local OverfullLines = luatypo.OverfullLines local UnderfullLines = luatypo.UnderfullLines local Widows = luatypo.Widows local Orphans = luatypo.Orphans local EOPHyphens = luatypo.EOPHyphens local RepeatedHyphens = luatypo.RepeatedHyphens local FirstWordMatch = luatypo.FirstWordMatch local ParLastHyphen = luatypo.ParLastHyphen local EOLShortWords = luatypo.EOLShortWords local LastWordMatch = luatypo.LastWordMatch local FootnoteSplit = luatypo.FootnoteSplit local Stretch = math.max(luatypo.Stretch/100,1) local blskip = tex.getglue("baselineskip") local pageno = tex.getcount("c@page") local vpos_min = PAGEmin * blskip vpos_min = vpos_min * 1.5 local vpos = 0 local pageflag = false local body_bottom = false local page_bottom = false local first_bot = true local footnote = false local ftnsplit = false local orphanflag = false local widowflag = false local lwhyphflag = false local pageshort = false local firstwd = "" local lastwd = "" local hyphcount = 0 local pageline = 0 local ftnline = 0 local line = 0 while head do local nextnode = head.next if not footnote and head.id == KERN and head.subtype == 1 then if footnoterule_ahead(head, true) then footnote = true ftnline = 0 body_bottom = false orphanflag = false lwhyphflag = false hyphcount = 0 firstwd = "" lastwd = "" else vpos = vpos + head.kern end elseif head.id == HLIST and head.subtype == LINE and (head.height > 0 or head.depth > 0) then if footnote then ftnline = ftnline + 1 line = ftnline else pageline = pageline + 1 line = pageline end local first = head.head local hmax = head.width + tex.hfuzz local w,h,d = dimensions(1,2,0, first) if w > hmax and OverfullLines then pageflag = true local wpt = string.format("%.2fpt", (w-head.width)/65536) local msg = "OVERFULL line " .. wpt log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[7] color_line (head, COLOR) elseif head.glue_set > Stretch and head.glue_sign == 1 and head.glue_order == 0 and UnderfullLines then 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 vpos = vpos + head.height + head.depth local n = head.next while n and (n.id == GLUE or n.id == PENALTY or n.id == WHATSIT) do n = n.next end if not n then if footnote then page_bottom = true else page_bottom = true body_bottom = true end elseif footnoterule_ahead(n, false) then body_bottom = true end if footnote and page_bottom then ftnsplit = true end while first.id == MKERN or (first.id == GLUE and first.subtype == LFTSKIP) do first = first.next end local ListItem = false if first.id == LPAR then hyphcount = 0 if not footnote then parline = 1 end if body_bottom then orphanflag = true end local nn = first.next if nn and nn.id == HLIST and nn.subtype == BOX then ListItem = true end elseif not footnote then parline = parline + 1 end if FirstWordMatch then local flag = not ListItem firstwd = check_first_word(firstwd, first, line, flag) end local ln = slide(first) local pn = ln.prev if pn and pn.id == GLUE and pn.subtype == PARFILL then hyphcount = 0 ftnsplit = false orphanflag = false if pageline == 1 and parline > 1 then widowflag = true end local PFskip = effective_glue(pn,head) if ShortLines then local llwd = tex.hsize - PFskip if llwd < LLminWD then pageflag = true local msg = "SHORT LINE: " .. string.format("%.0fpt", llwd/65536) log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[6] local attr = oberdiek.luacolor.getattribute() color_line (head, COLOR) end end if BackParindent and PFskip < BackPI and PFskip > BackFuzz then pageflag = true local msg = "LINE NEARLY FULL: missing " .. string.format("%.1fpt", PFskip/65536) log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[12] local attr = oberdiek.luacolor.getattribute() color_line (head, COLOR) end if LastWordMatch then local flag = textline if PFskip > BackPI then flag = false end lastwd = check_last_word(lastwd, pn, line, flag) end elseif pn and pn.id == DISC then hyphcount = hyphcount + 1 if LastWordMatch then lastwd = check_last_word(lastwd, ln, line, true) end if hyphcount > HYPHmax and RepeatedHyphens then local COLOR = luatypo.colortbl[2] local pg = show_pre_disc (pn,COLOR) pageflag = true local msg = "REPEATED HYPHENS: more than " .. HYPHmax log_flaw(msg, line, colno, footnote) end if (page_bottom or body_bottom) and EOPHyphens then lwhyphflag = true 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 LastWordMatch and pn then lastwd = check_last_word(lastwd, pn, line, true) end if EOLShortWords then while pn and pn.id ~= GLYPH and pn.id ~= HLIST do pn = pn.prev end if pn and pn.id == GLYPH then check_regexpr(pn,line) end end end if widowflag and Widows then pageflag = true widowflag = false local msg = "WIDOW" log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[4] color_line (head, COLOR) end if orphanflag and Orphans then pageflag = true local msg = "ORPHAN" log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[5] color_line (head, COLOR) end if ftnsplit and FootnoteSplit then pageflag = true local msg = "FOOTNOTE SPLIT" log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[13] color_line (head, COLOR) end if lwhyphflag and EOPHyphens then pageflag = true local msg = "LAST WORD SPLIT" log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[1] local pg = show_pre_disc (pn,COLOR) end elseif 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 local n = head.next while n and (n.id == GLUE or n.id == PENALTY or n.id == WHATSIT or n.id == VLIST) do n = n.next end if not n then page_bottom = true body_bottom = true elseif footnoterule_ahead(n, false) then body_bottom = true end elseif head.id == VLIST then vpos = vpos + head.height + head.depth elseif body_bottom and head.id == GLUE and head.subtype == 0 then if first_bot then if pageline > 1 and pageline < PAGEmin and ShortPages then pageshort = true end if pageshort and vpos < vpos_min then pageflag = true local msg = "SHORT PAGE: only " .. pageline .. " lines" log_flaw(msg, line, colno, footnote) local COLOR = luatypo.colortbl[9] local n = head repeat n = n.prev until n.id == HLIST color_line (n, COLOR) end first_bot = false end elseif head.id == GLUE then vpos = vpos + effective_glue(head,body) end head = nextnode end return pageflag end luatypo.check_page = function (head) local pageno = tex.getcount("c@page") local pageflag = false local n2, n3, col, colno local body = get_pagebody(head) local first = body.list if first.id == HLIST and first.subtype == 2 then n2 = first.list colno = 0 for n in traverse_id(HLIST, n2) do if n.id == HLIST and n.subtype == 2 then n3 = n.list col = n3.list colno = colno + 1 pageflag = check_vtop(col,colno) end end elseif body.id == VLIST and body.subtype == 0 then col = body.list pageflag = check_vtop(col,colno) end if pageflag then local pl = luatypo.pagelist local p = tonumber(string.match(pl, "%s(%d+),%s$")) if not p or pageno > p then luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", " end end return true end return luatypo.check_page \end{luacode} \AtEndOfPackage{% \directlua{ if not luatypo.None then luatexbase.add_to_callback ("pre_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{mygrey}{gray}{0.6}% \definecolor{myred}{rgb}{1,0.55,0} \luatypoSetColor0{red}% Paragraph last full line hyphenated \luatypoSetColor1{red}% Page last word hyphenated \luatypoSetColor2{red}% Hyphens on 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}{myred}% First word matches \luatypoSetColor{11}{myred}% Last word matches \luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line \luatypoSetColor{13}{cyan}% Footnote split \luatypoBackPI=1em\relax \luatypoBackFuzz=2pt\relax \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 }% %% %% %% End of file `lua-typo.sty'.