summaryrefslogtreecommitdiff
path: root/macros
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2023-04-14 03:01:48 +0000
committerNorbert Preining <norbert@preining.info>2023-04-14 03:01:48 +0000
commitb17717e3829bc08efb71ec2537d9d894c8b82d45 (patch)
tree42351b1c085da8ee5f80387c2fb9ee00b7a7767a /macros
parentc6d2f94d04f69bb5b2143ef62e7ab90bd3b60212 (diff)
CTAN sync 202304140301
Diffstat (limited to 'macros')
-rw-r--r--macros/latex/contrib/siunitx/CHANGELOG.md11
-rw-r--r--macros/latex/contrib/siunitx/siunitx-code.pdfbin642304 -> 642810 bytes
-rw-r--r--macros/latex/contrib/siunitx/siunitx-unit.dtx14
-rw-r--r--macros/latex/contrib/siunitx/siunitx.dtx2
-rw-r--r--macros/latex/contrib/siunitx/siunitx.pdfbin691514 -> 691511 bytes
-rw-r--r--macros/luatex/latex/lua-typo/README.md4
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdfbin16859 -> 16859 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdfbin106404 -> 106400 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo.pdfbin179022 -> 205259 bytes
-rw-r--r--macros/luatex/latex/lua-typo/lua-typo-2023-03-08.sty1345
-rw-r--r--macros/luatex/latex/lua-typo/source/lua-typo.dtx561
11 files changed, 1679 insertions, 258 deletions
diff --git a/macros/latex/contrib/siunitx/CHANGELOG.md b/macros/latex/contrib/siunitx/CHANGELOG.md
index 49a16c2766..8f8d0a1972 100644
--- a/macros/latex/contrib/siunitx/CHANGELOG.md
+++ b/macros/latex/contrib/siunitx/CHANGELOG.md
@@ -7,12 +7,18 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
## [Unreleased]
-## [v3.2.4] - 2023-04-04
+## [v3.2.5] - 2023-04-13
+### Fixed
+- Use of `\mathsf` for sanserif text documents (see issues
+ [\#655](https://github.com/josephwright/siunitx/issues/655))
+
+## [v3.2.4] - 2023-04-04
### Fixed
- Parsing of 'full' uncertainty parts with leading zeros (see issues
[\#652](https://github.com/josephwright/siunitx/issues/652))
+
## [v3.2.3] - 2023-03-14
### Fixed
@@ -1888,7 +1894,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
### Added
- First public testing release (as `si`)
-[Unreleased]: https://github.com/josephwright/siunitx/compare/v3.2.4...HEAD
+[Unreleased]: https://github.com/josephwright/siunitx/compare/v3.2.5...HEAD
+[v3.2.5]: https://github.com/josephwright/siunitx/compare/v3.2.4...v3.2.5
[v3.2.4]: https://github.com/josephwright/siunitx/compare/v3.2.3...v3.2.4
[v3.2.3]: https://github.com/josephwright/siunitx/compare/v3.2.2...v3.2.3
[v3.2.2]: https://github.com/josephwright/siunitx/compare/v3.2.1...v3.2.2
diff --git a/macros/latex/contrib/siunitx/siunitx-code.pdf b/macros/latex/contrib/siunitx/siunitx-code.pdf
index 3fa73c2087..e757f4d116 100644
--- a/macros/latex/contrib/siunitx/siunitx-code.pdf
+++ b/macros/latex/contrib/siunitx/siunitx-code.pdf
Binary files differ
diff --git a/macros/latex/contrib/siunitx/siunitx-unit.dtx b/macros/latex/contrib/siunitx/siunitx-unit.dtx
index eccc0349bb..398eb7187e 100644
--- a/macros/latex/contrib/siunitx/siunitx-unit.dtx
+++ b/macros/latex/contrib/siunitx/siunitx-unit.dtx
@@ -2975,14 +2975,18 @@
}
% \end{macrocode}
%
-% Cover the case where the default font is sanserif.
+% Cover the case where the default font is sanserif with \pkg{beamer}.
% \begin{macrocode}
\AtBeginDocument
{
- \str_if_eq:eeT
- { \exp_not:o { \familydefault } }
- { \exp_not:n { \sfdefault } }
- { \keys_set:nn { siunitx } { unit-font-command = \mathsf } }
+ \@ifclassloaded { beamer }
+ {
+ \str_if_eq:eeT
+ { \exp_not:o { \familydefault } }
+ { \exp_not:n { \sfdefault } }
+ { \keys_set:nn { siunitx } { unit-font-command = \mathsf } }
+ }
+ { }
}
% \end{macrocode}
%
diff --git a/macros/latex/contrib/siunitx/siunitx.dtx b/macros/latex/contrib/siunitx/siunitx.dtx
index 78b5ffa776..84e10ad678 100644
--- a/macros/latex/contrib/siunitx/siunitx.dtx
+++ b/macros/latex/contrib/siunitx/siunitx.dtx
@@ -122,7 +122,7 @@
%
% Identify the package and give the over all version information.
% \begin{macrocode}
-\ProvidesExplPackage {siunitx} {2023-04-04} {3.2.4}
+\ProvidesExplPackage {siunitx} {2023-04-13} {3.2.5}
{A comprehensive (SI) units package}
% \end{macrocode}
%
diff --git a/macros/latex/contrib/siunitx/siunitx.pdf b/macros/latex/contrib/siunitx/siunitx.pdf
index bdff52cf3b..71c7720642 100644
--- a/macros/latex/contrib/siunitx/siunitx.pdf
+++ b/macros/latex/contrib/siunitx/siunitx.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/README.md b/macros/luatex/latex/lua-typo/README.md
index 9577fee1cf..82c1c9b0c4 100644
--- a/macros/luatex/latex/lua-typo/README.md
+++ b/macros/luatex/latex/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/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
index 35fe6bc907..a7b1d9a40f 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
index ed50a38d11..cd64bdaf45 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
index 6990971202..537414c80d 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/lua-typo-2023-03-08.sty b/macros/luatex/latex/lua-typo/lua-typo-2023-03-08.sty
new file mode 100644
index 0000000000..aa876b82fa
--- /dev/null
+++ b/macros/luatex/latex/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/macros/luatex/latex/lua-typo/source/lua-typo.dtx b/macros/luatex/latex/lua-typo/source/lua-typo.dtx
index 41a9966280..dfcc363cd4 100644
--- a/macros/luatex/latex/lua-typo/source/lua-typo.dtx
+++ b/macros/luatex/latex/lua-typo/source/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]
%</dtx|sty|scan>
%<*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))
%<dbg> texio.write_nl("EOLsigold=" .. old)
%<dbg> 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
%<dbg> 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
%<dbg> 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, "^_+", "")
%<dbg> 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
+%<dbg> 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
+%<dbg> 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))
%<dbg> texio.write_nl("BOLsigold=" .. old)
%<dbg> 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
%<dbg> 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
%<dbg> 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, "_+$", "") --$
%<dbg> 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
+%<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}
+%
% \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
-%<dbg> texio.write_nl("=> FOOTNOTE RULE ahead")
-%<dbg> texio.write_nl("check_vtop: last line before footnotes")
-%<dbg> 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}
%<dbg> texio.write_nl("check_vtop: footnotes start")
%<dbg> texio.write_nl(" ")
@@ -2489,10 +2553,9 @@ check_vtop = function (head, colno, vpos)
%<dbg> texio.write(" textheight=" .. tht_pt)
%<dbg> end
%<dbg> 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)
%<dbg> texio.write("-" .. top.subtype)
%<dbg> texio.write(" wd=" .. boxwd .. " ht=" .. boxht)
%<dbg> 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)
%<dbg> texio.write_nl("**MULTICOL type1:")
%<dbg> 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
%<dbg> texio.write_nl("Start of col." .. colno)
%<dbg> 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
%<dbg> texio.write_nl("Start of col." .. colno)
%<dbg> 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
}%