summaryrefslogtreecommitdiff
path: root/Master
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2025-02-18 21:07:08 +0000
committerKarl Berry <karl@freefriends.org>2025-02-18 21:07:08 +0000
commit539853db9caf41acc43c6c0bf38ec442aa23888b (patch)
tree331880e9f52c443bd6258f790ec83b07e69b8ac0 /Master
parent35b2da2ebcb559a1ac09ec2cfe69625ed5cd3879 (diff)
marginalia (18feb25)
git-svn-id: svn://tug.org/texlive/trunk@74103 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
-rw-r--r--Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-example.tex1
-rw-r--r--Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-ysep-explanation.pdfbin436691 -> 436690 bytes
-rw-r--r--Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc.pdfbin845420 -> 798476 bytes
-rw-r--r--Master/texmf-dist/source/lualatex/marginalia/marginalia.dtx1340
-rw-r--r--Master/texmf-dist/tex/lualatex/marginalia/marginalia.lua807
-rw-r--r--Master/texmf-dist/tex/lualatex/marginalia/marginalia.sty3
6 files changed, 2145 insertions, 6 deletions
diff --git a/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-example.tex b/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-example.tex
index 96208628def..07d39926978 100644
--- a/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-example.tex
+++ b/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-example.tex
@@ -14,4 +14,3 @@ width=35mm]{This note is placed on the left side of the page, wider, in sans
serif, ragged left, and bottom-aligned.}
\end{document}
-LUACODE
diff --git a/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-ysep-explanation.pdf b/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-ysep-explanation.pdf
index f667b1f5d6e..0b87405f39d 100644
--- a/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-ysep-explanation.pdf
+++ b/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc-ysep-explanation.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc.pdf b/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc.pdf
index de4e9c33a4f..52b18c9c54b 100644
--- a/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc.pdf
+++ b/Master/texmf-dist/doc/lualatex/marginalia/marginalia-doc.pdf
Binary files differ
diff --git a/Master/texmf-dist/source/lualatex/marginalia/marginalia.dtx b/Master/texmf-dist/source/lualatex/marginalia/marginalia.dtx
index 7d7bc9846ff..867a3492e20 100644
--- a/Master/texmf-dist/source/lualatex/marginalia/marginalia.dtx
+++ b/Master/texmf-dist/source/lualatex/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}
%
%
%
diff --git a/Master/texmf-dist/tex/lualatex/marginalia/marginalia.lua b/Master/texmf-dist/tex/lualatex/marginalia/marginalia.lua
index 9498ce2083f..bf53468a87d 100644
--- a/Master/texmf-dist/tex/lualatex/marginalia/marginalia.lua
+++ b/Master/texmf-dist/tex/lualatex/marginalia/marginalia.lua
@@ -20,4 +20,809 @@
-- and version 1.3c or later is part of all distributions of
-- LaTeX version 2008-05-04 or later.
--
-LUACODE
+local PAGE_DATA_MAIN_TABLE = {}
+local ITEM_DATA_MAIN_TABLE = {}
+local PROBLEM_REPORT_TABLE = {}
+local PAGE_CHANGE_REPORT_TABLE = {}
+local ITEM_CHANGE_REPORT_TABLE = {}
+local PROBLEM_REPORT_MAX_LENGTH = 40
+local PAGE_CHANGE_REPORT_MAX_LENGTH = 10
+local ITEM_CHANGE_REPORT_MAX_LENGTH = 10
+local TYPE_NORMAL = 1
+local TYPE_FIXED = 2
+local TYPE_OPTFIXED = 3
+local POS_AUTO = 1
+local POS_REVERSE = 2
+local POS_LEFT = 3
+local POS_RIGHT = 4
+local POS_NEAREST = 5
+local KEY_ABSPAGENO = 'abspageno'
+local KEY_CHECKED = 'checked'
+local KEY_PAGEDATANO = 'pagedatano'
+local KEY_HOFFSETORIGIN = 'hoffsetorigin'
+local KEY_VOFFSETORIGIN = 'voffsetorigin'
+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'
+local KEY_COLUMNCOUNT = 'columncount'
+local KEY_TWOSIDE = 'twoside'
+local KEY_ITEMNO = 'itemno'
+local KEY_PAGENO = 'pageno'
+local KEY_XPOS = 'xpos'
+local KEY_YPOS = 'ypos'
+local KEY_HEIGHT = 'height'
+local KEY_DEPTH = 'depth'
+local KEY_TYPE = 'type'
+local KEY_POS = 'pos'
+local KEY_COLUMN = 'column'
+local KEY_YSHIFT = 'yshift'
+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'
+local KEY_COLNO_COMPUTED = 'colno computed'
+local KEY_XSHIFT_COMPUTED = 'xshift computed'
+local KEY_YSHIFT_COMPUTED = 'yshift computed'
+local KEY_SIDE_COMPUTED = 'side computed'
+local KEY_MARGINNO_COMPUTED = 'marginno computed'
+local KEY_ENABLED_COMPUTED = 'enabled computed'
+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
+local function toboolean(s)
+ return s == "true"
+end
+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
+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
+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
+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
+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,
+}
+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
+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
+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
+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,
+}
+
+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
+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
+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
+local function write_problem_report()
+
+ write_report(PROBLEM_REPORT_TABLE,PROBLEM_REPORT_MAX_LENGTH)
+
+end
+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
+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
+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,
+ },
+ },
+}
+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,
+ },
+ },
+}
+local function compute_items_horizontal(item_data_list,page_data)
+ if #item_data_list == 0 then
+ return
+ end
+ 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]
+ local x_textleft = {}
+ local x_textright = {}
+ local x_textmiddle = {}
+ 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
+ for i=1,#item_data_list do
+ item_data_list[i][KEY_COLNO_COMPUTED] = 0
+ end
+ else
+ 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
+
+ local left_column_x_limit = (
+ x_textright[1]
+ + .5*page_data[KEY_COLUMNSEP]
+ )
+ 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
+ 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
+ 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
+local function get_y_item_top(item_data)
+ return item_data[KEY_YPOS]
+ + item_data[KEY_YSHIFT_COMPUTED]
+ + item_data[KEY_HEIGHT]
+end
+local function get_y_item_bottom(item_data)
+ return item_data[KEY_YPOS]
+ - item_data[KEY_DEPTH]
+ + item_data[KEY_YSHIFT_COMPUTED]
+end
+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
+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
+local function compute_items_vertical_adjustment(item_data_list,page_data)
+ if #item_data_list == 0 then
+ return
+ end
+
+ local ysep_list = get_ysep_list(item_data_list)
+ 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
+ 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
+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',
+}
+local function check_items_vertical(item_data_list,page_data)
+ if (#item_data_list) == 0 then
+ return
+ end
+
+ local ysep_list = get_ysep_list(item_data_list)
+
+ local item_data
+
+ 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
+ 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
+local function compute_items_vertical(item_data_list,page_data)
+ for i=1,#item_data_list do
+ local item_data = item_data_list[i]
+
+ item_data[KEY_YSHIFT_COMPUTED] = item_data[KEY_YSHIFT]
+ end
+ compute_items_vertical_optfixed_enabled(item_data_list)
+ list_filter(item_data_list,function(item_data)
+ return item_data[KEY_ENABLED_COMPUTED]
+ end)
+ 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
+local function compute_items()
+ local max_abspageno = 0
+
+ for k,v in pairs(ITEM_DATA_MAIN_TABLE) do
+ max_abspageno = math.max(v[KEY_ABSPAGENO],max_abspageno)
+ end
+ local per_abspage_item_data_list = {}
+ 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
+ local per_abspage_page_data_list = {}
+ local pagedatano = 0
+ for abspageno = 1,max_abspageno do
+ 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
+ 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]
+ compute_items_horizontal(current_page_item_data_list,current_page_data)
+ 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
+ for i=0,5 do
+ compute_items_vertical(
+ current_page_item_data_sublists[i],
+ current_page_data
+ )
+ end
+ end
+end
+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
+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,
+}
diff --git a/Master/texmf-dist/tex/lualatex/marginalia/marginalia.sty b/Master/texmf-dist/tex/lualatex/marginalia/marginalia.sty
index 0561a905b22..5a1643151bb 100644
--- a/Master/texmf-dist/tex/lualatex/marginalia/marginalia.sty
+++ b/Master/texmf-dist/tex/lualatex/marginalia/marginalia.sty
@@ -21,7 +21,7 @@
%% LaTeX version 2008-05-04 or later.
%%
\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}
\sys_if_engine_luatex:F
{
@@ -512,4 +512,3 @@
{
\__marginalia_write_page_data:
}
-LUACODE