diff options
author | Norbert Preining <norbert@preining.info> | 2025-02-19 03:04:01 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2025-02-19 03:04:01 +0000 |
commit | facfc58cb00299a25dbc22cea25d4ea12c1cd902 (patch) | |
tree | 9ddeb30522441a50d5acc02e8f6c045680429ea4 /macros/luatex | |
parent | db3bad71b584503efcbe1a9c0d4309826f93722f (diff) |
CTAN sync 202502190303
Diffstat (limited to 'macros/luatex')
-rw-r--r-- | macros/luatex/generic/luamplib/NEWS | 9 | ||||
-rw-r--r-- | macros/luatex/generic/luamplib/luamplib.dtx | 67 | ||||
-rw-r--r-- | macros/luatex/generic/luamplib/luamplib.pdf | bin | 300502 -> 301954 bytes | |||
-rw-r--r-- | macros/luatex/generic/luamplib/test-luamplib-latex.tex | 12 | ||||
-rw-r--r-- | macros/luatex/latex/marginalia/marginalia-doc-ysep-explanation.pdf | bin | 436691 -> 436690 bytes | |||
-rw-r--r-- | macros/luatex/latex/marginalia/marginalia-doc.pdf | bin | 845420 -> 798476 bytes | |||
-rw-r--r-- | macros/luatex/latex/marginalia/marginalia.dtx | 1340 |
7 files changed, 1411 insertions, 17 deletions
diff --git a/macros/luatex/generic/luamplib/NEWS b/macros/luatex/generic/luamplib/NEWS index 9158d1ef92..eca51b064f 100644 --- a/macros/luatex/generic/luamplib/NEWS +++ b/macros/luatex/generic/luamplib/NEWS @@ -1,5 +1,14 @@ History of the luamplib package +2025/02/18 2.37.1 + * A new operator 'mplibuclength <string>' returns the number of unicode + grapheme clusters in the string. + + * A new operator 'mplibucsubstring <pair> of <string>' returns the part of + the string indexed by unicode grapheme clusters. + + * These two operators require lua-uni-algos package. + 2025/02/06 2.37.0 * introduce a new operator 'mpliblength <string>', a unicode-aware version of the 'length' primitive diff --git a/macros/luatex/generic/luamplib/luamplib.dtx b/macros/luatex/generic/luamplib/luamplib.dtx index c08d07f029..99414a7ef1 100644 --- a/macros/luatex/generic/luamplib/luamplib.dtx +++ b/macros/luatex/generic/luamplib/luamplib.dtx @@ -85,7 +85,7 @@ See source file '\inFileName' for licencing and contact information. %<*driver> \NeedsTeXFormat{LaTeX2e} \ProvidesFile{luamplib.drv}% - [2025/02/06 v2.37.0 Interface for using the mplib library]% + [2025/02/18 v2.37.1 Interface for using the mplib library]% \documentclass{ltxdoc} \usepackage{metalogo,multicol,xspace} \usepackage[x11names]{xcolor} @@ -155,7 +155,7 @@ See source file '\inFileName' for licencing and contact information. % \author{Hans Hagen, Taco Hoekwater, Elie Roux, Philipp Gesang and Kim Dohyun\\ % Current Maintainer: Kim Dohyun\\ % Support: \url{https://github.com/lualatex/luamplib}} -% \date{2025/02/06 v2.37.0} +% \date{2025/02/18 v2.37.1} % % \maketitle % @@ -1084,7 +1084,7 @@ See source file '\inFileName' for licencing and contact information. % \item[\texttt{withshadingcenter} \meta{pair}] % Values for shifting starting center. % For instance, -% |(0,0)| means that center of starting circle is |center p|; +% |(0,0)| means that the center of starting circle is |center p|; % |(1,1)| means |urcorner p|. % \item[\texttt{withshadingtransform} \meta{string}] % where \meta{string} shall be |"yes"| (respect transform) or |"no"| (ignore transform). @@ -1103,18 +1103,34 @@ See source file '\inFileName' for licencing and contact information. % Default value: |(white,black)| % \end{description} % -% \subsubsection{\texttt{mpliblength ...}} +% \subsubsection{\texttt{mpliblength ...}, \texttt{mplibuclength ...}} % |mpliblength| \meta{string} returns the number of unicode characters in the string. % This is a unicode-aware version equivalent to the \metapost primitive |length|, but % accepts only a string-type argument. % For instance, |mpliblength| |"abçdéf"| returns |6|, not |8|. % -% \subsubsection{\texttt{mplibsubstring ... of ...}} +% On the other hand, +% |mplibuclength| \meta{string} returns the number of unicode grapheme clusters in the string. +% For instance, |mplibuclength| |"Äpfel"|, +% where |Ä| is encoded using two codepoints (U+0041 and U+0308), +% returns |5|, not |6| or |7|. +% This operator requires \pkg{lua-uni-algos} package. +% +% \subsubsection{\texttt{mplibsubstring ... of ...}, \texttt{mplibucsubstring ... of ...}} % |mplibsubstring| \meta{pair} |of| \meta{string} is a unicode-aware version equivalent to the -% \metapost's |substring ... of ...| primitive. The syntax is the same as the latter. +% \metapost's |substring ... of ...| primitive. The syntax is the same as the latter, +% but the string is indexed by unicode characters. % For instance, |mplibsubstring| |(2,5)| |of| |"abçdéf"| returns |"çdé"|, % and |mplibsubstring| |(5,2)| |of| |"abçdéf"| returns |"édç"|. % +% On the other hand, +% |mplibucsubstring| \meta{pair} |of| \meta{string} returns the part of the string indexed +% by unicode grapheme clusters. +% For instance, |mplibucsubstring| |(0,1)| |of| |"Äpfel"|, +% where |Ä| is encoded using two codepoints (U+0041 and U+0308), +% returns |"Ä"|, not |"A"|. +% This operator requires \pkg{lua-uni-algos} package. +% % \subsection{Lua} % % \subsubsection{\texttt{runscript ...}} @@ -1200,8 +1216,8 @@ See source file '\inFileName' for licencing and contact information. luatexbase.provides_module { name = "luamplib", - version = "2.37.0", - date = "2025/02/06", + version = "2.37.1", + date = "2025/02/18", description = "Lua package to typeset Metapost with LuaTeX's MPLib.", } @@ -2644,12 +2660,25 @@ end % \end{macrocode} % -% lua function for |mplibsubstring ... of ...| +% lua functions for |mplib(uc)substring ... of ...| % \begin{macrocode} -function luamplib.utf8substring (s,b,e) - local t, tt, step = { }, { } - for _, c in utf8.codes(s) do - table.insert(t, utf8.char(c)) +function luamplib.getunicodegraphemes (s) + local t = { } + local graphemes = require'lua-uni-graphemes' + for _, _, c in graphemes.graphemes(s) do + table.insert(t, c) + end + return t +end +function luamplib.unicodesubstring (s,b,e,grph) + local tt, t, step = { } + if grph then + t = luamplib.getunicodegraphemes(s) + else + t = { } + for _, c in utf8.codes(s) do + table.insert(t, utf8.char(c)) + end end if b <= e then b, step = b+1, 1 @@ -3091,10 +3120,18 @@ def mpliblength primary t = runscript("return utf8.len[===[" & t & "]===]") enddef; def mplibsubstring expr p of t = - runscript("return luamplib.utf8substring([===[" & t & "]===]," + runscript("return luamplib.unicodesubstring([===[" & t & "]===]," & decimal xpart p & "," & decimal ypart p & ")") enddef; +def mplibuclength primary t = + runscript("return #luamplib.getunicodegraphemes[===[" & t & "]===]") +enddef; +def mplibucsubstring expr p of t = + runscript("return luamplib.unicodesubstring([===[" & t & "]===]," + & decimal xpart p & "," + & decimal ypart p & ",true)") +enddef; ]], legacyverbatimtex = [[ def specialVerbatimTeX (text t) = runscript("luamplibprefig{"&t&"}") enddef; @@ -4564,7 +4601,7 @@ end % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luamplib} - [2025/02/06 v2.37.0 mplib package for LuaTeX] + [2025/02/18 v2.37.1 mplib package for LuaTeX] \fi \ifdefined\newluafunction\else \input ltluatex diff --git a/macros/luatex/generic/luamplib/luamplib.pdf b/macros/luatex/generic/luamplib/luamplib.pdf Binary files differindex 95965a4f20..2033bb205d 100644 --- a/macros/luatex/generic/luamplib/luamplib.pdf +++ b/macros/luatex/generic/luamplib/luamplib.pdf diff --git a/macros/luatex/generic/luamplib/test-luamplib-latex.tex b/macros/luatex/generic/luamplib/test-luamplib-latex.tex index f983d96b17..6156a00f66 100644 --- a/macros/luatex/generic/luamplib/test-luamplib-latex.tex +++ b/macros/luatex/generic/luamplib/test-luamplib-latex.tex @@ -6,6 +6,7 @@ \fi \usepackage{fontspec} \setmainfont{latin modern roman} +\newfontfamily\hangulfont{NotoSansCJKKR}[Script=Hangul,Language=Korean] \usepackage{luamplib} \usepackage{graphicx,xcolor} \everymplib{ beginfig(0); } @@ -353,12 +354,23 @@ draw fullcircle scaled 100 withpattern "mypatt" withpen pencircle scaled 1 withshadingcolors (red,blue) ; \endmpfig +\leavevmode \mpfig string Test; Test="abçdéf"; for k=0 upto mpliblength(Test)-1: draw TEX(mplibsubstring (k,k+1) of Test) scaled 2 shifted (20k,0); endfor \endmpfig +\qquad +\mpliblegacybehavior{false}% +\mpfig + verbatimtex \hangulfont etex; + string Test; Test="나랏말ᄊᆞ미"; + for k=0 upto mplibuclength(Test)-1: + draw TEX(mplibucsubstring (k,k+1) of Test) scaled 1.5 shifted (20k,0); + endfor +\endmpfig +\mpliblegacybehavior{true}% \tracingcommands0 \vskip 2\baselineskip diff --git a/macros/luatex/latex/marginalia/marginalia-doc-ysep-explanation.pdf b/macros/luatex/latex/marginalia/marginalia-doc-ysep-explanation.pdf Binary files differindex f667b1f5d6..0b87405f39 100644 --- a/macros/luatex/latex/marginalia/marginalia-doc-ysep-explanation.pdf +++ b/macros/luatex/latex/marginalia/marginalia-doc-ysep-explanation.pdf diff --git a/macros/luatex/latex/marginalia/marginalia-doc.pdf b/macros/luatex/latex/marginalia/marginalia-doc.pdf Binary files differindex de4e9c33a4..52b18c9c54 100644 --- a/macros/luatex/latex/marginalia/marginalia-doc.pdf +++ b/macros/luatex/latex/marginalia/marginalia-doc.pdf diff --git a/macros/luatex/latex/marginalia/marginalia.dtx b/macros/luatex/latex/marginalia/marginalia.dtx index 7d7bc9846f..867a3492e2 100644 --- a/macros/luatex/latex/marginalia/marginalia.dtx +++ b/macros/luatex/latex/marginalia/marginalia.dtx @@ -973,7 +973,7 @@ width=35mm]{This note is placed on the left side of the page, wider, in sans % Package identification/version information. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e}[2020-02-02] -\ProvidesExplPackage{marginalia}{2025-02-17}{0.80.1} +\ProvidesExplPackage{marginalia}{2025-02-18}{0.80.2} {Non-floating marginal content for LuaLaTeX} % \end{macrocode} % Check that Lua\TeX\ is in use. @@ -1923,7 +1923,1343 @@ width=35mm]{This note is placed on the left side of the page, wider, in sans % % % -LUACODE +% \section{Implementation (Lua backend)} +% +% \begin{macrocode} +%<*lua> +% \end{macrocode} +% +% +% +% \subsection{Global variables} +% +% Global tables for page_data and item_data. +% \begin{macrocode} +local PAGE_DATA_MAIN_TABLE = {} +local ITEM_DATA_MAIN_TABLE = {} +% \end{macrocode} +% Global tables for compiling reports. +% \begin{macrocode} +local PROBLEM_REPORT_TABLE = {} +local PAGE_CHANGE_REPORT_TABLE = {} +local ITEM_CHANGE_REPORT_TABLE = {} +% \end{macrocode} +% Global configuration for reports. +% \begin{macrocode} +local PROBLEM_REPORT_MAX_LENGTH = 40 +local PAGE_CHANGE_REPORT_MAX_LENGTH = 10 +local ITEM_CHANGE_REPORT_MAX_LENGTH = 10 +% \end{macrocode} +% +% +% +% \subsection{Constants} +% +% Type constants. These match the possible values for the type key. +% \begin{macrocode} +local TYPE_NORMAL = 1 +local TYPE_FIXED = 2 +local TYPE_OPTFIXED = 3 +% \end{macrocode} +% Position constants. These match the possible values for the pos key. +% \begin{macrocode} +local POS_AUTO = 1 +local POS_REVERSE = 2 +local POS_LEFT = 3 +local POS_RIGHT = 4 +local POS_NEAREST = 5 +% \end{macrocode} +% +% +% +% \subsection{Keys for tables} +% +% The strings listed in this subsection are constants used to index the tables. Also listed are the types of +% values that are indexed by each key. Note that values listed below as "dimensions" are actually integers, giving the +% dimension in TeX scaled points (sp) +% +% +% +% \subsubsection{Keys for both page and item data tables} +% +% Integer: Absolute page number in output file (not on-page number), used in both page_data and item_data tables +% \begin{macrocode} +local KEY_ABSPAGENO = 'abspageno' +% \end{macrocode} +% Boolean: Used to mark page_data or item_data as checked when the .aux file is read back at the end of the document +% \begin{macrocode} +local KEY_CHECKED = 'checked' +% \end{macrocode} +% +% +% +% \subsubsection{Keys for page data tables, layout etc.} +% +% Integer: Used only to distinguish instances of data written to .aux file +% \begin{macrocode} +local KEY_PAGEDATANO = 'pagedatano' +% \end{macrocode} +% Dimensions: Value of next two will always be equivalent of \qty{1}{\inch}, but it is simpler to keep all geometry +% data together. +% \begin{macrocode} +local KEY_HOFFSETORIGIN = 'hoffsetorigin' +local KEY_VOFFSETORIGIN = 'voffsetorigin' +% \end{macrocode} +% Dimensions: corresponding to obvious LaTeX dimensions +% \begin{macrocode} +local KEY_HOFFSET = 'hoffset' +local KEY_VOFFSET = 'voffset' +local KEY_PAPERHEIGHT = 'paperheight' +local KEY_ODDSIDEMARGIN = 'oddsidemargin' +local KEY_EVENSIDEMARGIN = 'evensidemargin' +local KEY_TEXTWIDTH = 'textwidth' +local KEY_COLUMNWIDTH = 'columnwidth' +local KEY_COLUMNSEP = 'columnsep' +% \end{macrocode} +% Integer: either \(1\) or \(2\), depending on whether LaTeX was in one- or two-column mode +% \begin{macrocode} +local KEY_COLUMNCOUNT = 'columncount' +% \end{macrocode} +% Boolean: true iff LaTeX is in twoside mode +% \begin{macrocode} +local KEY_TWOSIDE = 'twoside' +% \end{macrocode} +% +% +% +% \subsubsection{Keys for item data tables} +% +% Integer: Used to identify data with item +% \begin{macrocode} +local KEY_ITEMNO = 'itemno' +% \end{macrocode} +% Integer: On-page number +% \begin{macrocode} +local KEY_PAGENO = 'pageno' +% \end{macrocode} +% Dimensions: \(x\) and \(y\) positions of call to \cs{marginalia} +% \begin{macrocode} +local KEY_XPOS = 'xpos' +local KEY_YPOS = 'ypos' +% \end{macrocode} +% Dimensions: Height and depth of typeset item +% \begin{macrocode} +local KEY_HEIGHT = 'height' +local KEY_DEPTH = 'depth' +% \end{macrocode} +% Integer: Specified type, following \luavar{TYPE_*} +% \begin{macrocode} +local KEY_TYPE = 'type' +% \end{macrocode} +% Integer: corresponds to value of \key{pos} key: \(0 = \texttt{auto}\), \(1 = \texttt{reverse}\), \(2 = \texttt{left}\), +% \(3 = \texttt{right}\), \(4 = \texttt{nearest}\) +% \begin{macrocode} +local KEY_POS = 'pos' +% \end{macrocode} +% Integer: corresponds to value of \key{column} key: \(-1 = \texttt{auto}\), \(0 = \texttt{one}\), \(1 = \texttt{left}\), +% \(2 = \texttt{right}\) +% \begin{macrocode} +local KEY_COLUMN = 'column' +% \end{macrocode} +% Dimension: specified vertical shift +% \begin{macrocode} +local KEY_YSHIFT = 'yshift' +% \end{macrocode} +% Dimensions: specified vertical separations +% \begin{macrocode} +local KEY_YSEP_ABOVE = 'ysep above' +local KEY_YSEP_BELOW = 'ysep below' +local KEY_YSEP_PAGE_TOP = 'ysep page top' +local KEY_YSEP_PAGE_BOTTOM = 'ysep page bottom' +% \end{macrocode} +% +% \medskip\noindent +% The preceding keys refer to values that will be supplied from \LaTeX. The remaining values will be computed in Lua +% and passed back to \LaTeX. +% +% \medskip\noindent +% Integer: column in which the call to \cs{marginalia} was located: \(0 = \textrm{one-column}\), +% \(1 = \textrm{left}\), \(2 = \textrm{right}\) +% \begin{macrocode} +local KEY_COLNO_COMPUTED = 'colno computed' +% \end{macrocode} +% Dimension: Horizontal shift between the call to \cs{marginalia} and the margin in which the item should be located +% \begin{macrocode} +local KEY_XSHIFT_COMPUTED = 'xshift computed' +% \end{macrocode} +% Dimension: Computed vertical shift +% \begin{macrocode} +local KEY_YSHIFT_COMPUTED = 'yshift computed' +% \end{macrocode} +% Integer: Side of text on which the item will appear: \(0 = \textrm{right}\), \(1 = \textrm{left}\) +% \begin{macrocode} +local KEY_SIDE_COMPUTED = 'side computed' +% \end{macrocode} +% Integer: Number of margin in which the item will appear, \(0 = \textrm{recto outer}\), \(1 = \textrm{recto inner}\), +% \(2 = \textrm{verso outer}\), \(3 = \textrm{verso inner}\), \(4 = \textrm{ right between}\), +% \(5 = \textrm{left between}\) +% \begin{macrocode} +local KEY_MARGINNO_COMPUTED = 'marginno computed' +% \end{macrocode} +% Boolean: Whether the item will actually appear on the page +% \begin{macrocode} +local KEY_ENABLED_COMPUTED = 'enabled computed' +% \end{macrocode} +% +% +% +% \subsection{Utility functions} +% +% \begin{macro}[int]{list_filter} +% Take a list \luavar{t} and remove from it any elements for which the function +% \luavar{f} does not return true. (The index \luavar{j} is always the destination index to which a `keep' element +% is moved.)\sidenote{Code adapted from \url{https://stackoverflow.com/a/53038524/8990243}.} +% \begin{macrocode} +local function list_filter(t, f) + local j = 1 + local n = #t + + for i=1,n do + if (f(t[i])) then + if (i ~= j) then + t[j] = t[i] + t[i] = nil + end + j = j + 1 + else + t[i] = nil + end + end + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{list_filter} +% Return boolean true iff \luavar{s} is exactly the string `\luavar{true}'. +% \begin{macrocode} +local function toboolean(s) + return s == "true" +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{get_data_page_number} +% Take a item or page data and return a human-readable string indicating the page to which the data pertains. +% \begin{macrocode} +local function get_data_page_number(data) + local pageno = data[KEY_PAGENO] + if pageno ~= nil then + return 'p' .. pageno .. ' (' .. data[KEY_ABSPAGENO] .. ')' + else + return data[KEY_ABSPAGENO] + end +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Generic page/item data functions} +% +% \begin{macro}[int]{parse_data} +% Parse \luavar{keyvalue_string} and return the corresponding data as a table. The \luavar{keyvalue_string} is +% expected to be of precisely the kind written to the \file{.aux} file as the parameter of \cs{marginalia@pagedata} or +% \cs{marginalia@notedata}. +% +% Ignore any keys in \luavar{keyvalue_string} that are not listed in \luavar{conversion_table}. Fill in any missing +% value with values from \luavar{defaults_table}. +% +% \luavar{conversion_table} is indexed by possible keys, with values equal to functions to convert the corresponding +% value string to the value that should appear in the returned table. +% +% \luavar{defaults_table} is indexed by keys that \emph{will} appear in the returned table, using the corresponding +% value unless it was given in \luavar{keyvalue_string} and the key appeared in \luavar{conversion_table}. +% \begin{macrocode} +local function parse_data(keyvalue_string,conversion_table,defaults_table) + + local key + local value + local result = {} + + for s in string.gmatch(keyvalue_string,'([^,]+)') do + + key,value = string.match(s,'^(.+)=(.+)$') + local conv = conversion_table[key] + if conv ~= nil then + result[key] = conv(value) + end + + end + + for key,value in pairs(defaults_table) do + if not(result[key] ~= nil) then + result[key] = value + end + end + + return result + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{check_data} +% Check \luavar{keyvalue_string} against stored data. If it is new or has changed, append a report to +% \luavar{report_table}. Set the \luavar{KEY_CHECKED} of the data item to true. +% +% The \luavar{keyvalue_string} is processed using \luavar{conversion_table} and \luavar{defaults_table} as per the +% \luavar{parse_data} function. The resulting table is compared to the table in \luavar{data_table} with the same value +% whose key is \luavar{data_table_key}. The tables are compared using the fields indexed by keys in +% \luavar{conversion_table}. +% \begin{macrocode} +local function check_data(keyvalue_string,conversion_table,defaults_table, + data_table,data_table_key_field,report_table) + + local new_data = parse_data(keyvalue_string, + conversion_table,defaults_table) + + local data_table_key = new_data[data_table_key_field] + + local stored_data = data_table[data_table_key] + if stored_data == nil then + table.insert( + report_table, + get_data_page_number(new_data) .. ' New' + ) + else + local change_report = '' + for k,_ in pairs(conversion_table) do + if stored_data[k] ~= new_data[k] then + change_report = change_report + .. ' ' .. k .. ':' .. + tostring(stored_data[k]) .. '->' .. tostring(new_data[k]) + end + end + if change_report ~= '' then + table.insert( + report_table, + get_data_page_number(new_data) .. ' ' .. change_report + ) + end + stored_data[KEY_CHECKED] = true + end + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{check_removed_data} +% Check whether data have been removed from \luavar{data_table}, which corresponds to some entry having the value +% of \luavar{KEY_CHECKED} being false. In this case, append a report to \luavar{report_table}. +% \begin{macrocode} +local function check_removed_data(data_table,report_table) + for _,data in pairs(data_table) do + if not data[KEY_CHECKED] then + table.insert( + report_table, + ' Removed' + ) + break + end + end +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Processing of page data from \texorpdfstring{\file{.aux}}{.aux} file} +% +% Conversion and default tables. +% \begin{macrocode} +local PAGE_DATA_CONVERSION_TABLE = { + [KEY_PAGEDATANO] = tonumber, + [KEY_ABSPAGENO] = tonumber, + [KEY_HOFFSETORIGIN] = tonumber, + [KEY_VOFFSETORIGIN] = tonumber, + [KEY_HOFFSET] = tonumber, + [KEY_VOFFSET] = tonumber, + [KEY_PAPERHEIGHT] = tonumber, + [KEY_ODDSIDEMARGIN] = tonumber, + [KEY_EVENSIDEMARGIN] = tonumber, + [KEY_COLUMNCOUNT] = tonumber, + [KEY_COLUMNWIDTH] = tonumber, + [KEY_COLUMNSEP] = tonumber, + [KEY_TEXTWIDTH] = tonumber, + [KEY_TWOSIDE] = toboolean, +} +local PAGE_DATA_DEFAULT_TABLE = { + [KEY_PAGEDATANO] = 0, + [KEY_ABSPAGENO] = 0, + [KEY_HOFFSETORIGIN] = tex.sp('1in'), + [KEY_VOFFSETORIGIN] = tex.sp('1in'), + [KEY_HOFFSET] = tex.dimen['hoffset'], + [KEY_VOFFSET] = tex.dimen['voffset'], + [KEY_PAPERHEIGHT] = tex.dimen['paperheight'], + [KEY_ODDSIDEMARGIN] = tex.dimen['oddsidemargin'], + [KEY_EVENSIDEMARGIN] = tex.dimen['evensidemargin'], + [KEY_TEXTWIDTH] = tex.dimen['textwidth'], + [KEY_COLUMNWIDTH] = tex.dimen['columnwidth'], + [KEY_COLUMNSEP] = tex.dimen['columnsep'], + [KEY_COLUMNCOUNT] = 1, + [KEY_TWOSIDE] = false, + [KEY_CHECKED] = false, +} +% \end{macrocode} +% +% \begin{macro}[int]{store_page_data} +% Store page data supplied by \luavar{keyvalue_string} in \luavar{PAGE_DATA_MAIN_TABLE}. +% \begin{macrocode} +local function store_page_data(keyvalue_string) + + local page_data = parse_data(keyvalue_string, + PAGE_DATA_CONVERSION_TABLE, + PAGE_DATA_DEFAULT_TABLE) + + PAGE_DATA_MAIN_TABLE[page_data[KEY_PAGEDATANO]] = page_data + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{store_default_page_data} +% Store default page data in \luavar{PAGE_DATA_MAIN_TABLE}, so that there is some data to work with when +% computing item positions, even on a first run, when no page data has been written to the \file{.aux} file. +% \begin{macrocode} +local function store_default_page_data() + + default_page_data = parse_data('', + PAGE_DATA_CONVERSION_TABLE, + PAGE_DATA_DEFAULT_TABLE) + + default_page_data[KEY_ABSPAGENO] = 1 + default_page_data[KEY_CHECKED] = true + + PAGE_DATA_MAIN_TABLE[0] = default_page_data + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{check_page_data} +% Check whether page_data supplied by keyvalue_string differs from that in \luavar{PAGE_DATA_MAIN_TABLE}, appending +% reports to \luavar{PAGE_CHANGE_REPORT_TABLE} if so. +% \begin{macrocode} +local function check_page_data(keyvalue_string) + + check_data(keyvalue_string, + PAGE_DATA_CONVERSION_TABLE,PAGE_DATA_DEFAULT_TABLE, + PAGE_DATA_MAIN_TABLE,KEY_PAGEDATANO, + PAGE_CHANGE_REPORT_TABLE) + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Processing of item data from \texorpdfstring{\file{.aux}}{.aux} file} +% +% Conversion and default tables. +% \begin{macrocode} +local ITEM_DATA_CONVERSIONS = { + [KEY_ITEMNO] = tonumber, + [KEY_ABSPAGENO] = tonumber, + [KEY_PAGENO] = tonumber, + [KEY_XPOS] = tonumber, + [KEY_YPOS] = tonumber, + [KEY_HEIGHT] = tonumber, + [KEY_DEPTH] = tonumber, + [KEY_TYPE] = tonumber, + [KEY_POS] = tonumber, + [KEY_COLUMN] = tonumber, + [KEY_YSHIFT] = tonumber, + [KEY_YSEP_ABOVE] = tonumber, + [KEY_YSEP_BELOW] = tonumber, + [KEY_YSEP_PAGE_TOP] = tonumber, + [KEY_YSEP_PAGE_BOTTOM] = tonumber, + [KEY_CHECKED] = toboolean, +} +local ITEM_DATA_DEFAULTS = { + [KEY_ITEMNO] = 0, + [KEY_ABSPAGENO] = 1, + [KEY_PAGENO] = 1, + [KEY_XPOS] = 0, + [KEY_YPOS] = 0, + [KEY_HEIGHT] = 0, + [KEY_DEPTH] = 0, + [KEY_TYPE] = 0, + [KEY_POS] = 0, + [KEY_COLUMN] = -1, + [KEY_YSHIFT] = 0, + [KEY_YSEP_ABOVE] = tex.dimen['marginparpush'], + [KEY_YSEP_BELOW] = tex.dimen['marginparpush'], + [KEY_YSEP_PAGE_TOP] = tex.dimen['marginparpush'], + [KEY_YSEP_PAGE_BOTTOM] = tex.dimen['marginparpush'], + [KEY_COLNO_COMPUTED] = 0, + [KEY_XSHIFT_COMPUTED] = 0, + [KEY_YSHIFT_COMPUTED] = 0, + [KEY_SIDE_COMPUTED] = 0, + [KEY_MARGINNO_COMPUTED] = 0, + [KEY_ENABLED_COMPUTED] = true, + [KEY_CHECKED] = false, +} +% \end{macrocode} +% \luavar{ITEM_DATA_DEFAULTS} is also used by \luafunc{load_item_data} when no stored item data is found in +% \luavar{ITEM_DATA_MAIN_TABLE}. + +% \begin{macro}[int]{store_item_data} +% Store item_data supplied by \luavar{keyvalue_string} in \luavar{ITEM_DATA_MAIN_TABLE}. +% \begin{macrocode} +local function store_item_data(keyvalue_string) + + local item = parse_data(keyvalue_string, + ITEM_DATA_CONVERSIONS, + ITEM_DATA_DEFAULTS) + + ITEM_DATA_MAIN_TABLE[item[KEY_ITEMNO]] = item + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{check_item_data} +% Check whether item_data supplied by \luavar{keyvalue_string} differs from that in \luavar{ITEM_DATA_MAIN_TABLE}, +% appending reports to \luavar{ITEM_CHANGE_REPORT_TABLE} if so. +% \begin{macrocode} +local function check_item_data(keyvalue_string) + + check_data(keyvalue_string, + ITEM_DATA_CONVERSIONS,ITEM_DATA_DEFAULTS, + ITEM_DATA_MAIN_TABLE,KEY_ITEMNO, + ITEM_CHANGE_REPORT_TABLE) + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Writing reports} +% +% \begin{macro}[int]{write_report} +% Write the data contained in \luavar{report_table} to \TeX\ in a format suitable for a package warning. The written +% text will contain at most \luavar{max_length} items. +% \begin{macrocode} +local function write_report(report_table,max_length) + + if #report_table > 0 then + local report_text + local report_length + + if #report_table <= max_length then + report_length = #report_table + report_text = ' Here they are:\n' + else + report_length = max_length + report_text = ' Here are the first ' .. report_length .. ':\n' + end + + for i=1,report_length do + report_text = report_text .. report_table[i] + if i < report_length then + report_text = report_text .. '\n' + end + end + + tex.print(report_text) + end + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{write_problem_report} +% Write a report about placement problems to \TeX\ in a format suitable for a package warning. +% \begin{macrocode} +local function write_problem_report() + + write_report(PROBLEM_REPORT_TABLE,PROBLEM_REPORT_MAX_LENGTH) + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{write_item_change_report} +% Write a report about changes in item data to \TeX\ in a format suitable for a package warning. +% \begin{macrocode} +local function write_item_change_report() + + check_removed_data(ITEM_DATA_MAIN_TABLE,ITEM_CHANGE_REPORT_TABLE) + write_report(ITEM_CHANGE_REPORT_TABLE,ITEM_CHANGE_REPORT_MAX_LENGTH) + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{write_page_change_report} +% Write a report about changes in page data to \TeX\ in a format suitable for a package warning. +% \begin{macrocode} +local function write_page_change_report() + + check_removed_data(PAGE_DATA_MAIN_TABLE,PAGE_CHANGE_REPORT_TABLE) + write_report(PAGE_CHANGE_REPORT_TABLE,PAGE_CHANGE_REPORT_MAX_LENGTH) + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Computing horizontal positions} +% +% It is necessary to determine whether an item should be placed on the right or left of the text block, and in which +% column it lies. The following lookup tables are used. +% +% The value found in \luavar{RIGHTSIDE_LOOKUP_TABLE} is either \luavar{true} (right) or \luavar{false} (left). It is +% indexed by whether the item is on a recto page (\luavar{true}/\luavar{false}), whether it pertains to single-column +% text, the left column, or the right colum (\luavar{0}/\luavar{1}/\luavar{2}), and the value of \key{pos} being +% either \val{auto} or \val{reverse}. +% \begin{macrocode} +local RIGHTSIDE_LOOKUP_TABLE = { + [true] = { + [0] = { + [POS_AUTO] = true, + [POS_REVERSE] = false, + }, + [1] = { + [POS_AUTO] = false, + [POS_REVERSE] = true, + }, + [2] = { + [POS_AUTO] = true, + [POS_REVERSE] = false, + }, + }, + [false] = { + [0] = { + [POS_AUTO] = false, + [POS_REVERSE] = true, + }, + [1] = { + [POS_AUTO] = true, + [POS_REVERSE] = false, + }, + [2] = { + [POS_AUTO] = false, + [POS_REVERSE] = true, + }, + }, +} +% \end{macrocode} +% The value found in \luavar{MARGINNO_LOOKUP_TABLE} ranges from \luavar{0} to \luavar{5} (see +% \luavar{KEY_MARGINNO_COMPUTED} for the meaning of these values). It is indexed by whether the item is on a recto +% page (\luavar{true}/\luavar{false}), whether it pertains to single-column text, the left column, or the right colum +% (\luavar{0}/\luavar{1}/\luavar{2}), and whether it is to be placed on the right of the text block +% (\luavar{true}/\luavar{false}). +% \begin{macrocode} +local MARGINNO_LOOKUP_TABLE = { + [true] = { + [0] = { + [false] = 1, + [true] = 0, + }, + [1] = { + [false] = 1, + [true] = 5, + }, + [2] = { + [false] = 4, + [true] = 0, + }, + }, + [false] = { + [0] = { + [false] = 2, + [true] = 3, + }, + [1] = { + [false] = 2, + [true] = 5, + }, + [2] = { + [false] = 4, + [true] = 3, + }, + }, +} +% \end{macrocode} +% +% \begin{macro}[int]{compute_items_horizontal} +% For every \luavar{item_data} in \luavar{item_data_list}, compute the fields relevant to horizontal positioning, +% namely \luavar{KEY_COLNO_COMPUTED}, \luavar{KEY_XSHIFT_COMPUTED}, \luavar{KEY_SIDE_COMPUTED}, based on the layout +% information in page_data. Every item described in \luavar{item_data_list} is assumed to be on the same page. +% \begin{macrocode} +local function compute_items_horizontal(item_data_list,page_data) +% \end{macrocode} +% Immediately return if \luavar{item_data_list} is empty, to avoid edge cases. +% \begin{macrocode} + if #item_data_list == 0 then + return + end +% \end{macrocode} +% Information used frequently and which is the same for every item. +% \begin{macrocode} + local pageno = item_data_list[1][KEY_PAGENO] + local twoside = page_data[KEY_TWOSIDE] + local recto = ((pageno % 2) == 1) or (not twoside) + local columncount = page_data[KEY_COLUMNCOUNT] +% \end{macrocode} +% Tables to contain the \(x\)-coordinates of left edge, right edge, and middle of the current text, whether a single +% column (index 0), the left column (index 1), or the right column (index 2). +% \begin{macrocode} + local x_textleft = {} + local x_textright = {} + local x_textmiddle = {} +% \end{macrocode} +% First, compute necessary dimensions for single-column text, since most of these calculations would be used anyway +% for two-column text. The terms used in calculating \luavar{x_textleft[0]} respectively take one to the origin of +% \cs{hoffset}, to the origin of \cs{oddsidemargin} and \cs{evensidemargin}, and to the left-hand side of the text +% block. +% \begin{macrocode} + if recto then + x_textleft[0] = ( + page_data[KEY_HOFFSETORIGIN] + + page_data[KEY_HOFFSET] + + page_data[KEY_ODDSIDEMARGIN] + ) + x_textright[0] = ( + x_textleft[0] + + page_data[KEY_TEXTWIDTH] + ) + else + x_textleft[0] = ( + page_data[KEY_HOFFSETORIGIN] + + page_data[KEY_HOFFSET] + + page_data[KEY_EVENSIDEMARGIN] + ) + x_textright[0] = ( + x_textleft[0] + + page_data[KEY_TEXTWIDTH] + ) + end + x_textmiddle[0] = (x_textleft[0] + x_textright[0])/2 + + + if columncount == 1 then +% \end{macrocode} +% If the page is one-column, the field \luavar{KEY_COLNO_COMPUTED} can be set immediately for every item_data. +% \begin{macrocode} + for i=1,#item_data_list do + item_data_list[i][KEY_COLNO_COMPUTED] = 0 + end + else +% \end{macrocode} +% If the page is two-column, calculate the \(x\)-coordinates of the left and right edges and the mid-point of each +% column. +% \begin{macrocode} + x_textleft[1] = x_textleft[0] + x_textright[1] = ( + x_textleft[1] + + page_data[KEY_COLUMNWIDTH] + ) + x_textmiddle[1] = (x_textleft[1] + x_textright[1])/2 + + x_textleft[2] = ( + x_textright[1] + + page_data[KEY_COLUMNSEP] + ) + x_textright[2] = ( + x_textleft[2] + + page_data[KEY_COLUMNWIDTH] + ) + x_textmiddle[2] = (x_textleft[2] + x_textright[2])/2 + +% \end{macrocode} +% Calculate the cut-off (mid-way between the columns) that distinguishes items from left and right columns. +% \begin{macrocode} + local left_column_x_limit = ( + x_textright[1] + + .5*page_data[KEY_COLUMNSEP] + ) +% \end{macrocode} +% Now set the field \luavar{KEY_COLNO_COMPUTED} for each item. +% \begin{macrocode} + for i=1,#item_data_list do + local item_data = item_data_list[i] + + if item_data[KEY_COLUMN] >= 0 then + item_data[KEY_COLNO_COMPUTED] = item_data[KEY_COLUMN] + else + if item_data[KEY_XPOS] <= left_column_x_limit then + item_data[KEY_COLNO_COMPUTED] = 1 + else + item_data[KEY_COLNO_COMPUTED] = 2 + end + end + end + + end +% \end{macrocode} +% For every item_data in item_data_list, compute and set the fields \luavar{KEY_SIDE_COMPUTED}, +% \luavar{KEY_XSHIFT_COMPUTED}, and \luavar{KEY_MARGINNO_COMPUTED}. +% \begin{macrocode} + for i=1,#item_data_list do + local item = item_data_list[i] + + local pos = item[KEY_POS] + local colnocomputed = item[KEY_COLNO_COMPUTED] + + if pos == POS_LEFT then + rightside = false + elseif pos == POS_RIGHT then + rightside = true + elseif pos == POS_NEAREST then + rightside = (item[KEY_XPOS] >= x_textmiddle[colnocomputed]) + else +% \end{macrocode} +% \luavar{pos} must be POS_AUTO or POS_REVERSE +% \begin{macrocode} + rightside = RIGHTSIDE_LOOKUP_TABLE[recto][colnocomputed][pos] + end + + local marginno = MARGINNO_LOOKUP_TABLE[recto][colnocomputed][rightside] + + if rightside then + item[KEY_SIDE_COMPUTED] = 0 + item[KEY_XSHIFT_COMPUTED] = -item[KEY_XPOS] + + x_textright[colnocomputed] + else + item[KEY_SIDE_COMPUTED] = 1 + item[KEY_XSHIFT_COMPUTED] = -item[KEY_XPOS] + + x_textleft[colnocomputed] + end + item[KEY_MARGINNO_COMPUTED] = marginno + + end + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{get_y_item_top} +% Return the \(y\)-coordinate of the top of the item described by \luavar{item_data}. +% \begin{macrocode} +local function get_y_item_top(item_data) + return item_data[KEY_YPOS] + + item_data[KEY_YSHIFT_COMPUTED] + + item_data[KEY_HEIGHT] +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{get_y_item_bottom} +% Return the \(y\)-coordinate of the bottom of the item described by \luavar{item_data}. +% \begin{macrocode} +local function get_y_item_bottom(item_data) + return item_data[KEY_YPOS] + - item_data[KEY_DEPTH] + + item_data[KEY_YSHIFT_COMPUTED] +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{get_ysep_list} +% Calculate the separation to be used between adjacent marginal content items as described in +% \luavar{item_data_list}. The list is assumed to be sorted so that items are in the order they should appear on the +% page, top to bottom. +% +% The idea is that we have the following arrangement for \(i = 1,\ldots,\luavar{\#item_data_list}\): +% { +% \null~~~~~~\(\vdots\)\\ +% \null~~~~\luavar{item_data_list[i]}\\ +% \null~~~~~~\luavar{ysep_list[i]}\\ +% \null~~~~\luavar{item_data_list[i+1]}\\ +% \null~~~~~~\(\vdots\)\\ +% } +% Also set \luavar{ysep_list[0]} and \luavar{ysep_list[\#item_data_list]} to 0, to avoid checking when these values +% are accessed (although they are not used). +% \begin{macrocode} +local function get_ysep_list(item_data_list) + + local ysep_list = {} + + ysep_list[0] = 0 + for i=1,#item_data_list-1 do + ysep_list[i] = math.max( + item_data_list[i][KEY_YSEP_BELOW], + item_data_list[i+1][KEY_YSEP_ABOVE] + ) + end + ysep_list[#item_data_list] = 0 + + return ysep_list + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Computing vertical positions} +% +% +% +% \subsubsection{Computing \val{optfixed} enabled} +% +% \begin{macro}[int]{compute_items_vertical_optfixed_enabled} +% For every \luavar{item_data} in \luavar{item_data_list} describing an item of type \luavar{TYPE_OPTFIXED}, check +% for a clash with an item of type \luavar{TYPE_FIXED}. If so, set \luavar{item_data[KEY_ENABLED_COMPUTED]} to +% \luavar{false}. Every item described in \luavar{item_data_list} is assumed to be on the same page and to have +% \luavar{KEY_YSHIFT} set to the default. +% \begin{macrocode} +local function compute_items_vertical_optfixed_enabled(item_data_list) + + local optfixed_item_data_list = {} + local fixed_item_data_list = {} + + for _,item_data in pairs(item_data_list) do + if item_data[KEY_TYPE] == TYPE_OPTFIXED then + optfixed_item_data_list[#optfixed_item_data_list+1] = item_data + elseif item_data[KEY_TYPE] == TYPE_FIXED then + fixed_item_data_list[#fixed_item_data_list+1] = item_data + end + end + + for _,optfixed_item_data in pairs(optfixed_item_data_list) do + local optfixed_y_item_top = get_y_item_top(optfixed_item_data) + local optfixed_y_item_bottom = get_y_item_bottom(optfixed_item_data) + + for _,fixed_item_data in pairs(fixed_item_data_list) do + local fixed_y_item_top = get_y_item_top(fixed_item_data) + local fixed_y_item_bottom = get_y_item_bottom(fixed_item_data) + + if ( + ( + (fixed_y_item_bottom - optfixed_y_item_top) + < + math.max( + fixed_item_data[KEY_YSEP_BELOW], + optfixed_item_data[KEY_YSEP_ABOVE] + ) + ) + and + ( + (optfixed_y_item_bottom - fixed_y_item_top) + < + math.max( + optfixed_item_data[KEY_YSEP_BELOW], + fixed_item_data[KEY_YSEP_ABOVE] + ) + ) + ) then + optfixed_item_data[KEY_ENABLED_COMPUTED] = false + break + end + end + end + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Computing vertical adjustment} +% +% \begin{macro}[int]{compute_items_vertical_adjustment} +% For every \luavar{item_data} in \luavar{item_data_list}, compute the field relevant to vertical positioning, +% namely \luavar{KEY_YSHIFT_COMPUTED}, based on the layout information in \luavar{page_data}. Every item described +% in \luavar{item_data_list} is assumed to be on the same page and to have \luavar{KEY_YSHIFT} set to the default, +% and the list is assumed to be sorted so that items are in the order they should appear on the page, top to bottom. +% \begin{macrocode} +local function compute_items_vertical_adjustment(item_data_list,page_data) +% \end{macrocode} +% Immediately return if \luavar{item_data_list} is empty, to avoid edge cases +% \begin{macrocode} + if #item_data_list == 0 then + return + end + + local ysep_list = get_ysep_list(item_data_list) +% \end{macrocode} +% \textit{First pass of computation (downward).} \luavar{y_limit_above} will always be the highest \(y\)-coordinate +% at which the top of next item below can appear. +% \begin{macrocode} + local y_limit_above = ( + page_data[KEY_VOFFSET] + + page_data[KEY_PAPERHEIGHT] + - item_data_list[1][KEY_YSEP_PAGE_TOP] + ) + + for i=1,#item_data_list do + local item_data = item_data_list[i] + + local y_item_top = get_y_item_top(item_data) + + if y_item_top > y_limit_above then + if item_data[KEY_TYPE] == TYPE_NORMAL then + item_data[KEY_YSHIFT_COMPUTED] = item_data[KEY_YSHIFT_COMPUTED] + + (y_limit_above - y_item_top) + end + end + + y_limit_above = get_y_item_bottom(item_data) - ysep_list[i] + end +% \end{macrocode} +% \textit{Second pass of computation (upward)}. \luavar{y_limit_below} will always be the lowest \(y\)-coordinate at +% which the bottom of next item above can appear. +% \begin{macrocode} + local y_limit_below = ( + page_data[KEY_VOFFSET] + + item_data_list[#item_data_list][KEY_YSEP_PAGE_BOTTOM] + ) + + for i=#item_data_list,1,-1 do + local item_data = item_data_list[i] + + local y_item_bottom = get_y_item_bottom(item_data) + + if y_item_bottom < y_limit_below then + if item_data[KEY_TYPE] == TYPE_NORMAL then + item_data[KEY_YSHIFT_COMPUTED] = item_data[KEY_YSHIFT_COMPUTED] + + (y_limit_below - y_item_bottom) + end + end + + y_limit_below = get_y_item_top(item_data) + ysep_list[i-1] + end + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Checking vertical adjustment} +% +% Messages to use when checking results of vertical adjustment. +% \begin{macrocode} +local ITEM_PASSED_YSEP_PAGE_TOP_MESSAGES = { + [TYPE_NORMAL] = 'Moveable item > ysep page top', + [TYPE_FIXED] = 'Topmost fixed item > ysep page top', + [TYPE_OPTFIXED] = 'Topmost optfixed item > ysep page top', +} +local ITEM_CLASH_MESSAGES = { + [TYPE_NORMAL] = { + [TYPE_NORMAL] = 'moveable items' + .. ' (this shouldn\'t happen)', + [TYPE_FIXED] = 'moveable item above fixed item', + [TYPE_OPTFIXED] = 'moveable item above optfixed item', + }, + [TYPE_FIXED] = { + [TYPE_NORMAL] = 'moveable item below fixed item', + [TYPE_FIXED] = 'fixed items', + [TYPE_OPTFIXED] = 'fixed item above optfixed item ' + .. '(this shouldn\'t happen)', + }, + [TYPE_OPTFIXED] = { + [TYPE_NORMAL] = 'moveable items below optfixed item', + [TYPE_FIXED] = 'fixed item below optfixed item ' + .. '(this shouldn\'t happen)', + [TYPE_OPTFIXED] = 'optfixed items ' + .. '(this shouldn\'t happen)', + }, +} +local ITEM_PASSED_YSEP_PAGE_BOTTOM_MESSAGE = { + [TYPE_NORMAL] = 'Moveable item < ysep page bottom', + [TYPE_FIXED] = 'Bottommost fixed item < ysep page bottom', + [TYPE_OPTFIXED] = 'Bottommost optfixed item < ysep page bottom', +} +% \end{macrocode} +% +% \begin{macro}[int]{check_items_vertical} +% For the items described by the item_data in \luavar{item_data_list}, check whether any clash or fail to obey +% \key{ysep page top} or \key{ysep page bottom}. If so, write messages to \luavar{PROBLEM_REPORT_TABLE}. +% \begin{macrocode} +local function check_items_vertical(item_data_list,page_data) +% \end{macrocode} +% Immediately return if item_data_list is empty, to avoid edge cases +% \begin{macrocode} + if (#item_data_list) == 0 then + return + end + + local ysep_list = get_ysep_list(item_data_list) + + local item_data + +% \end{macrocode} +% If any item fails to obey \key{ysep page top}, the first one in the list does. +% \begin{macrocode} + item_data = item_data_list[1] + if ( + get_y_item_top(item_data) > page_data[KEY_VOFFSET] + + page_data[KEY_PAPERHEIGHT] + - item_data[KEY_YSEP_PAGE_TOP] + ) then + table.insert( + PROBLEM_REPORT_TABLE, + get_data_page_number(item_data) + .. ' ' .. ITEM_PASSED_YSEP_PAGE_TOP_MESSAGES[item_data[KEY_TYPE]] + ) + end + + for i=2,#item_data_list do + local item_data = item_data_list[i] + local prev_item_data = item_data_list[i-1] + if ( + get_y_item_top(item_data) > get_y_item_bottom(prev_item_data) + - ysep_list[i-1] + ) then + table.insert( + PROBLEM_REPORT_TABLE, + get_data_page_number(item_data) + .. ' Clash: ' .. + ITEM_CLASH_MESSAGES[prev_item_data[KEY_TYPE]][item_data[KEY_TYPE]] + ) + end + end +% \end{macrocode} +% If any item fails to obey \key{ysep page bottom}, the last one in the list does. +% \begin{macrocode} + item_data = item_data_list[#item_data_list] + if ( + get_y_item_bottom(item_data) < page_data[KEY_VOFFSET] + + item_data[KEY_YSEP_PAGE_BOTTOM] + ) then + table.insert( + PROBLEM_REPORT_TABLE, + get_data_page_number(item_data) + .. ' ' .. ITEM_PASSED_YSEP_PAGE_BOTTOM_MESSAGE[item_data[KEY_TYPE]] + ) + end + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsubsection{Core vertical position computation} +% +% \begin{macro}[int]{compute_items_vertical} +% For every \luavar{item_data} in \luavar{item_data_list}, compute the field relevant to vertical positioning, +% namely \luavar{KEY_YSHIFT_COMPUTED}, based on the layout information in \luavar{page_data}. This may involve +% setting the field \luavar{KEY_ENABLED_COMPUTED} to false. In such a case, the relevant item_data is removed from +% \luavar{item_data_list}. +% \begin{macrocode} +local function compute_items_vertical(item_data_list,page_data) +% \end{macrocode} +% Set \luavar{KEY_YSHIFT_COMPUTED} of each \luavar{item_data} to the user-supplied value. +% \begin{macrocode} + for i=1,#item_data_list do + local item_data = item_data_list[i] + + item_data[KEY_YSHIFT_COMPUTED] = item_data[KEY_YSHIFT] + end +% \end{macrocode} +% Decide which items of type \luavar{ITEM_DATA_OPTFIXED} are to be disabled. +% \begin{macrocode} + compute_items_vertical_optfixed_enabled(item_data_list) +% \end{macrocode} +% Strip any \luavar{item_data} with \luavar{KEY_ENABLED_COMPUTED} set to false from \luavar{item_data_list}. +% \begin{macrocode} + list_filter(item_data_list,function(item_data) + return item_data[KEY_ENABLED_COMPUTED] + end) +% \end{macrocode} +% Sort \luavar{item_data_list} according to the stored position from top to bottom and left to right on the page, +% resolving ties using \luavar{KEY_ITEMNO}. +% \begin{macrocode} + table.sort( + item_data_list, + function(left,right) + local y_diff = left[KEY_YPOS] - right[KEY_YPOS] + + if y_diff > 0 then + return true + elseif y_diff < 0 then + return false + end + + local x_diff = left[KEY_XPOS] - right[KEY_XPOS] + + if x_diff < 0 then + return true + elseif x_diff > 0 then + return false + end + + return (left[KEY_ITEMNO] < right[KEY_ITEMNO]) + end + ) + + compute_items_vertical_adjustment(item_data_list,page_data) + + check_items_vertical(item_data_list,page_data) + +end +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{compute_items} +% For every item represented in \luavar{ITEM_DATA_MAIN_TABLE}, use the \luavar{page_data} stored in +% \luavar{PAGE_DATA_MAIN_TABLE} to compute the item_data values necessary to place the item correctly on the page, +% namely those indexed by: \luavar{KEY_COLNO_COMPUTED}, \luavar{KEY_XSHIFT_COMPUTED}, \luavar{KEY_YSHIFT_COMPUTED}, +% \luavar{KEY_SIDE_COMPUTED}, \luavar{KEY_ENABLED_COMPUTED}. +% \begin{macrocode} +local function compute_items() +% \end{macrocode} +% Compute the maximum abspageno, which will be the last page of the document on which a item appears. +% \begin{macrocode} + local max_abspageno = 0 + + for k,v in pairs(ITEM_DATA_MAIN_TABLE) do + max_abspageno = math.max(v[KEY_ABSPAGENO],max_abspageno) + end +% \end{macrocode} +% \luavar{list per_abspage_item_data_list} will be a list indexed by absolute page numbers. Each entry will be a +% list (possibly empty) of \luavar{item_data} describing the items that appear on the corresponding page. +% \begin{macrocode} + local per_abspage_item_data_list = {} +% \end{macrocode} +% Prepare \luavar{per_abspage_item_data_list} by making each entry an empty list, then fill it from +% \luavar{ITEM_DATA_MAIN_TABLE}. +% \begin{macrocode} + for i=1,max_abspageno do + per_abspage_item_data_list[i] = {} + end + for _,item_data in pairs(ITEM_DATA_MAIN_TABLE) do + local temp_table = per_abspage_item_data_list[item_data[KEY_ABSPAGENO]] + temp_table[#temp_table+1] = item_data + end +% \end{macrocode} +% \luavar{per_abspage_item_data_list} will be a list indexed by abssolute page numbers. Each entry will be a +% \luavar{page_data} describing the corresponding page. Usually multiple entries will be the same +% \luavar{page_data}: in the loop, \luavar{pagedatano} will be the index of the last entry in +% \luavar{PAGE_DATA_MAIN_TABLE} with \luavar{KEY_ABSPAGENO} value less than or equal to \luavar{abspageno}. (There +% may be several such entries in \luavar{PAGE_DATA_MAIN_TABLE} because \cs{marginalianewgeometry} may have been +% called multiple times on the same page.) Note that \luavar{PAGE_DATA_MAIN_TABLE[0]} is available even if there was +% no data in the \file{.aux} file, because the defaults were stored by \luafunc{store_default_page_data}. +% \begin{macrocode} + local per_abspage_page_data_list = {} +% \end{macrocode} +% \begin{macrocode} + local pagedatano = 0 + for abspageno = 1,max_abspageno do +% \end{macrocode} +% \begin{macrocode} + while ( + PAGE_DATA_MAIN_TABLE[pagedatano+1] ~= nil + and + PAGE_DATA_MAIN_TABLE[pagedatano+1][KEY_ABSPAGENO] == abspageno + ) do + pagedatano = pagedatano+1 + end + per_abspage_page_data_list[abspageno] = PAGE_DATA_MAIN_TABLE[pagedatano] + end +% \end{macrocode} +% Iterate through all pages and perform the necessary computations. +% \begin{macrocode} + for abspageno=1,#per_abspage_item_data_list do + local current_page_data = per_abspage_page_data_list[abspageno] + local current_page_item_data_list = per_abspage_item_data_list[abspageno] +% \end{macrocode} +% First, compute the horizontal positions, which includes sorting items into columns in two-column mode. +% \begin{macrocode} + compute_items_horizontal(current_page_item_data_list,current_page_data) +% \end{macrocode} +% Sort the items into sublists corresponding to the margins in which they are located. +% \begin{macrocode} + local current_page_item_data_sublists = {} + + for i=0,5 do + current_page_item_data_sublists[i] = {} + end + + for _,item_data in pairs(current_page_item_data_list) do + table.insert( + current_page_item_data_sublists[item_data[KEY_MARGINNO_COMPUTED]], + item_data + ) + end +% \end{macrocode} +% Compute vertical positons for each sublist. +% \begin{macrocode} + for i=0,5 do + compute_items_vertical( + current_page_item_data_sublists[i], + current_page_data + ) + end + end +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Passing item_data back to \LaTeX} +% +% \begin{macro}[int]{load_item_data} +% Set the relevant \LaTeX\ counter and dimension variables to the values computed for \luavar{itemno}. +% \begin{macrocode} +local function load_item_data(itemno) + + item = ITEM_DATA_MAIN_TABLE[tonumber(itemno)] + if item == nil then + item = ITEM_DATA_DEFAULTS + end + + tex.count['l__marginalia_page_int'] = item[KEY_PAGENO] + tex.count['l__marginalia_column_computed_int'] = item[KEY_COLNO_COMPUTED] + tex.dimen['l__marginalia_xshift_computed_dim'] = item[KEY_XSHIFT_COMPUTED] + tex.dimen['l__marginalia_yshift_computed_dim'] = item[KEY_YSHIFT_COMPUTED] + tex.count['l__marginalia_side_computed_int'] = item[KEY_SIDE_COMPUTED] + tex.count['l__marginalia_marginno_computed_int'] + = item[KEY_MARGINNO_COMPUTED] + if item[KEY_ENABLED_COMPUTED] then + tex.count['l__marginalia_enabled_computed_int'] = 1 + else + tex.count['l__marginalia_enabled_computed_int'] = 0 + end + +end +% \end{macrocode} +% \end{macro} +% +% +% +% \subsection{Export public functions} +% +% Finally, make available the functions that will be called from \LaTeX\ using \cs{lua_now:n} and \cs{lua_now:e}. +% \begin{macrocode} +return { + store_default_page_data = store_default_page_data, + store_page_data = store_page_data, + check_page_data = check_page_data, + + store_item_data = store_item_data, + check_item_data = check_item_data, + + compute_items = compute_items, + + load_item_data = load_item_data, + + write_problem_report = write_problem_report, + + write_page_change_report = write_page_change_report, + write_item_change_report = write_item_change_report, +} +% \end{macrocode} +% +% +% +% \begin{macrocode} +%</lua> +% \end{macrocode} % % % |