summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/lualatex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2022-04-19 20:10:27 +0000
committerKarl Berry <karl@freefriends.org>2022-04-19 20:10:27 +0000
commitb12479292b3e2468c564619ee6b60dbc13eeb1be (patch)
tree01f444779592389c9650c7cfecfcd301ca6198db /Master/texmf-dist/tex/lualatex
parent3aef115c3b639b7f73a9866cda39c98e9bc8649c (diff)
luamathalign (19apr22)
git-svn-id: svn://tug.org/texlive/trunk@63081 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/lualatex')
-rw-r--r--Master/texmf-dist/tex/lualatex/luamathalign/build.lua21
-rw-r--r--Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luacmd.lua19
-rw-r--r--Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luaprop.lua42
-rw-r--r--Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.lua375
-rw-r--r--Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.sty98
5 files changed, 555 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/lualatex/luamathalign/build.lua b/Master/texmf-dist/tex/lualatex/luamathalign/build.lua
new file mode 100644
index 00000000000..3ce83c670a6
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luamathalign/build.lua
@@ -0,0 +1,21 @@
+module = "luamathalign"
+typesetexe = "lualatex"
+tdsroot = "lualatex"
+checkengines = {"luatex"}
+unpackfiles = {"*.dtx"}
+installfiles = {"*.sty", "*.lua"}
+sourcefiles = {"*.dtx", "*.lua"}
+
+uploadconfig = {
+ pkg = "luamathalign",
+ version = "v0.1",
+ author = "Marcel Krüger",
+ license = "lppl1.3c",
+ summary = "More flexible alignment in amsmath environments",
+ ctanPath = "/macros/luatex/latex/luamathalign/",
+ -- repository = "https://github.com/zauguin/luamathalign/"
+ description = "Allow aligning mathematical expressions on points where \z
+ where direcly using &amp; is not possible, especially in nested macros or environments.",
+ topic = {'maths', 'luatex'},
+ -- update = true,
+}
diff --git a/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luacmd.lua b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luacmd.lua
new file mode 100644
index 00000000000..a2ff4ed0679
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luacmd.lua
@@ -0,0 +1,19 @@
+----Copyright (C) 2019--2022 by Marcel Krueger
+---
+--- This file may be distributed and/or modified under the
+--- conditions of the LaTeX Project Public License, either
+--- version 1.3c of this license or (at your option) any later
+--- version. The latest version of this license is in:
+---
+--- http://www.latex-project.org/lppl.txt
+---
+--- and version 1.3 or later is part of all distributions of
+--- LaTeX version 2005/12/01 or later.
+local func_tbl = lua.get_functions_table()
+local set_lua = token.set_lua
+local new_luafunction = luatexbase.new_luafunction
+return function(name, func, ...)
+ local idx = new_luafunction(name)
+ set_lua(name, idx, ...)
+ func_tbl[idx] = func
+end
diff --git a/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luaprop.lua b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luaprop.lua
new file mode 100644
index 00000000000..3e10f0fc454
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign-luaprop.lua
@@ -0,0 +1,42 @@
+----Copyright (C) 2019--2022 by Marcel Krueger
+---
+--- This file may be distributed and/or modified under the
+--- conditions of the LaTeX Project Public License, either
+--- version 1.3c of this license or (at your option) any later
+--- version. The latest version of this license is in:
+---
+--- http://www.latex-project.org/lppl.txt
+---
+--- and version 1.3 or later is part of all distributions of
+--- LaTeX version 2005/12/01 or later.
+return function(namespace)
+ return {
+ query = function(n)
+ local p = node.getproperty(n)
+ return p and p[namespace]
+ end,
+ get = function(n, ...)
+ local p = node.getproperty(n)
+ if not p then
+ p = {}
+ node.setproperty(n, p)
+ end
+ local n = p[namespace]
+ if nil == p[namespace] then
+ local d = select('#', ...) ~= 0 and ... or {}
+ n, p[namespace] = d, d
+ end
+ return n
+ end,
+ set = function(n, v)
+ local p = node.getproperty(n)
+ if not p then
+ p = {}
+ node.setproperty(n, p)
+ end
+ local old = p[namespace]
+ p[namespace] = v
+ return old
+ end,
+ }
+end
diff --git a/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.lua b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.lua
new file mode 100644
index 00000000000..aa8476b0553
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.lua
@@ -0,0 +1,375 @@
+--
+-- This is file `luamathalign.lua',
+-- generated with the docstrip utility.
+--
+-- The original source files were:
+--
+-- luamathalign.dtx (with options: `lua')
+--
+-- Copyright (C) 2019--2022 by Marcel Krueger
+--
+-- This file may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either
+-- version 1.3c of this license or (at your option) any later
+-- version. The latest version of this license is in:
+--
+-- http://www.latex-project.org/lppl.txt
+--
+-- and version 1.3 or later is part of all distributions of
+-- LaTeX version 2005/12/01 or later.
+local luacmd = require'luamathalign-luacmd'
+local luaprop = require'luamathalign-luaprop'('mathalign')
+local hlist = node.id'hlist'
+local vlist = node.id'vlist'
+local whatsit = node.id'whatsit'
+local kern = node.id'kern'
+local user_defined = node.subtype'user_defined'
+local whatsit_id = luatexbase.new_whatsit'mathalign'
+local node_cmd = token.command_id'node'
+local ampersand = token.new(38, 4)
+-- We might want to add y later
+local function is_marked(mark, list)
+ for n in node.traverse(list) do
+ local id = n.id
+ if id == hlist or id == vlist then
+ if is_marked(mark, n.head) then return true end
+ elseif id == whatsit and n.subtype == user_defined
+ and n.user_id == whatsit_id and n.value == mark then
+ return true
+ end
+ end
+ return false
+end
+local function assert_unmarked(mark, list, ...)
+ local marked = is_marked(mark, list)
+ if marked then
+ tex.error("Multiple alignment marks", "I found multiple alignment marks \z
+ of type " .. mark .. " in an alignment where I already had an \z
+ alignment mark of that type. You should look at both of them and \z
+ decide which one is right. I will continue with the first one for now.")
+ end
+ return ...
+end
+local measure do
+ local vmeasure
+ local function hmeasure(mark, list)
+ local x, last = 0, list.head
+ for n in node.traverse(last) do
+ local id = n.id
+ if id == hlist then
+ local w, h, d = node.rangedimensions(list, last, n)
+ x, last = x + w, n
+ local dx = hmeasure(mark, n)
+ if dx then return assert_unmarked(mark, n.next, dx + x) end
+ elseif id == vlist then
+ local w, h, d = node.rangedimensions(list, last, n)
+ x, last = x + w, n
+ local dx = vmeasure(mark, n)
+ if dx then return assert_unmarked(mark, n.next, dx + x) end
+ elseif id == whatsit and n.subtype == user_defined
+ and n.user_id == whatsit_id and n.value == mark then
+ local w, h, d = node.rangedimensions(list, last, n)
+ local after
+ list.head, after = node.remove(list.head, n)
+ return assert_unmarked(mark, after, x + w)
+ end
+ end
+ end
+ function vmeasure(mark, list)
+ for n in node.traverse(list.head) do
+ local id = n.id
+ if id == hlist then
+ local dx = hmeasure(mark, n)
+ if dx then return assert_unmarked(mark, n.next, dx + n.shift) end
+ elseif id == vlist then
+ local dx = vmeasure(mark, n)
+ if dx then return assert_unmarked(mark, n.next, dx + n.shift) end
+ elseif id == whatsit and n.subtype == user_defined
+ and n.user_id == whatsit_id and n.value == mark then
+ local after
+ list.head, after = node.remove(list.head, n)
+ return assert_unmarked(mark, after, 0)
+ end
+ end
+ end
+ function measure(mark, head)
+ local x, last = 0, head
+ for n in node.traverse(last) do
+ local id = n.id
+ if id == hlist then
+ local w, h, d = node.dimensions(last, n)
+ x, last = x + w, n
+ local dx = hmeasure(mark, n)
+ if dx then return assert_unmarked(mark, n.next, head, dx + x) end
+ elseif id == vlist then
+ local w, h, d = node.dimensions(last, n)
+ x, last = x + w, n
+ local dx = vmeasure(mark, n)
+ if dx then return assert_unmarked(mark, n.next, head, dx + x) end
+ elseif id == whatsit and n.subtype == user_defined
+ and n.user_id == whatsit_id and n.value == mark then
+ local w, h, d = node.dimensions(last, n)
+ local after
+ head, after = node.remove(head, n)
+ return assert_unmarked(mark, after, head, x + w)
+ end
+ end
+ return head
+ end
+end
+
+local isolate do
+ local visolate
+ local function hisolate(list, offset)
+ local x, last = 0, list.head
+ local newhead, newtail = nil, nil
+ local n = last
+ while n do
+ local id = n.id
+ if id == hlist then
+ local w, h, d = node.rangedimensions(list, last, n)
+ x, last = x + w, n
+ local inner_head, inner_tail, new_offset = hisolate(n, offset - x)
+ if inner_head then
+ if newhead then
+ newtail.next, inner_head.prev = inner_head, newtail
+ else
+ newhead = inner_head
+ end
+ newtail = inner_tail
+ offset = x + new_offset
+ end
+ n = n.next
+ elseif id == vlist then
+ local w, h, d = node.rangedimensions(list, last, n)
+ x, last = x + w, n
+ local inner_head, inner_tail, new_offset = visolate(n, offset - x)
+ if inner_head then
+ if newhead then
+ newtail.next, inner_head.prev = inner_head, newtail
+ else
+ newhead = inner_head
+ end
+ newtail = inner_tail
+ offset = x + new_offset
+ end
+ n = n.next
+ elseif id == whatsit and n.subtype == user_defined
+ and n.user_id == whatsit_id then
+ local w, h, d = node.rangedimensions(list, last, n)
+ x = x + w
+ list.head, last = node.remove(list.head, n)
+ if x ~= offset then
+ local k = node.new(kern)
+ k.kern, offset = x - offset, x
+ newhead, newtail = node.insert_after(newhead, newtail, k)
+ end
+ newhead, newtail = node.insert_after(newhead, newtail, n)
+ n = last
+ else
+ n = n.next
+ end
+ end
+ return newhead, newtail, offset
+ end
+ function visolate(list, offset)
+ local newhead, newtail = nil, nil
+ local n = list.head
+ while n do
+ local id = n.id
+ if id == hlist then
+ if dx then return assert_unmarked(mark, n.next, dx + n.shift) end
+ local inner_head, inner_tail, new_offset = hisolate(n, offset)
+ if inner_head then
+ if newhead then
+ newtail.next, inner_head.prev = inner_head, newtail
+ else
+ newhead = inner_head
+ end
+ newtail = inner_tail
+ offset = new_offset
+ end
+ n = n.next
+ elseif id == vlist then
+ if dx then return assert_unmarked(mark, n.next, dx + n.shift) end
+ local inner_head, inner_tail, new_offset = visolate(n, offset)
+ if inner_head then
+ if newhead then
+ newtail.next, inner_head.prev = inner_head, newtail
+ else
+ newhead = inner_head
+ end
+ newtail = inner_tail
+ offset = new_offset
+ end
+ n = n.next
+ elseif id == whatsit and n.subtype == user_defined
+ and n.user_id == whatsit_id then
+ local after
+ list.head, after = node.remove(list.head, n)
+ if 0 ~= offset then
+ local k = node.new(kern)
+ k.kern, offset = -offset, 0
+ newhead, newtail = node.insert_after(newhead, newtail, k)
+ end
+ newhead, newtail = node.insert_after(newhead, newtail, n)
+ n = last
+ else
+ n = n.next
+ end
+ end
+ return newhead, newtail, offset
+ end
+ function isolate(head)
+ local x, last = 0, head
+ local newhead, newtail, offset = nil, nil, 0
+ local n = last
+ while n do
+ local id = n.id
+ if id == hlist then
+ local w, h, d = node.dimensions(last, n)
+ x, last = x + w, n
+ local inner_head, inner_tail, new_offset = hisolate(n, offset - x)
+ if inner_head then
+ if newhead then
+ newtail.next, inner_head.prev = inner_head, newtail
+ else
+ newhead = inner_head
+ end
+ newtail = inner_tail
+ offset = x + new_offset
+ end
+ n = n.next
+ elseif id == vlist then
+ local w, h, d = node.dimensions(last, n)
+ x, last = x + w, n
+ local inner_head, inner_tail, new_offset = visolate(n, offset - x)
+ if inner_head then
+ if newhead then
+ newtail.next, inner_head.prev = inner_head, newtail
+ else
+ newhead = inner_head
+ end
+ newtail = inner_tail
+ offset = x + new_offset
+ end
+ n = n.next
+ elseif id == whatsit and n.subtype == user_defined
+ and n.user_id == whatsit_id then
+ local w, h, d = node.dimensions(last, n)
+ x = x + w
+ head, last = node.remove(head, n)
+ if x ~= offset then
+ local k = node.new(kern)
+ k.kern, offset = x - offset, x
+ newhead, newtail = node.insert_after(newhead, newtail, k)
+ end
+ newhead, newtail = node.insert_after(newhead, newtail, n)
+ n = last
+ else
+ n = n.next
+ end
+ end
+ return head, newhead
+ end
+end
+
+local mark, afterkern
+luatexbase.add_to_callback('post_mlist_to_hlist_filter', function(n)
+ if mark then
+ local x
+ n, x = measure(mark, n)
+ local k = node.new'kern'
+ local off = x - n.width
+ k.kern, afterkern.kern = off, -off
+ node.insert_after(n.head, nil, k)
+ n.width = x
+ mark, afterkern = nil, nil
+ end
+ return n
+end, 'luamathalign')
+
+local function get_kerntoken(newmark)
+ assert(not mark)
+ mark, afterkern = newmark, node.new'kern'
+ return token.new(node.direct.todirect(afterkern), node_cmd)
+end
+
+local function insert_whatsit(mark)
+ local n = node.new(whatsit, user_defined)
+ n.user_id, n.type, n.value = whatsit_id, string.byte'd', mark
+ node.write(n)
+end
+luacmd("SetAlignmentPoint", function()
+ local mark = token.scan_int()
+ if mark < 0 then
+ for i=tex.nest.ptr,0,-1 do
+ local t = tex.nest[i].head
+ if luaprop.query(t) ~= nil then
+ mark = mark + 1
+ if mark == 0 then
+ luaprop.set(t, true)
+ return insert_whatsit(-i)
+ end
+ end
+ end
+ tex.error('No compatible alignment environment found',
+ 'This either means that \\SetAlignmentPoint was used outside\n\z
+ of an alignment or the used alignment is not setup for use with\n\z
+ luamathalign. In the latter case you might want to look at\n\z
+ non-negative alignment marks.')
+ else
+ return insert_whatsit(mark)
+ end
+end, "protected")
+
+function handle_whatsit(mark)
+ token.put_next(ampersand, get_kerntoken(mark))
+end
+luacmd("ExecuteAlignment", function()
+ return handle_whatsit(token.scan_int())
+end, "protected")
+
+luacmd("LuaMathAlign@begin", function()
+ local nest = tex.nest.top
+ luaprop.set(nest.head, false)
+end, "protected")
+luacmd("LuaMathAlign@end@early", function()
+ local t = tex.nest.top.head
+ if luaprop.set(t, nil) == true then
+ handle_whatsit(-tex.nest.ptr)
+ end
+end, "protected")
+local delayed
+luacmd("LuaMathAlign@end", function()
+ local nest = tex.nest.top
+ local t = nest.head
+ if luaprop.set(t, nil) == true then
+ assert(not delayed)
+ delayed = {get_kerntoken(-tex.nest.ptr), ampersand}
+ end
+end, "protected")
+luatexbase.add_to_callback("hpack_filter", function(head, groupcode)
+ if delayed and groupcode == "align_set" then
+-- HACK: token.put_next puts the tokens into the input stream after the cell
+-- is fully read, before the next starts. This will act as if the content was
+-- written as the first element of the next field.
+ token.put_next(delayed)
+ delayed = nil
+ end
+ return true
+end, "luamathalign.delayed")
+
+luacmd("LuaMathAlign@IsolateAlignmentPoints", function()
+ local main = token.scan_int()
+ if not token.scan_keyword 'into' then
+ tex.error'Expected "into"'
+ end
+ local marks = token.scan_int()
+ local head, newhead = isolate(tex.box[main])
+ tex.box[marks] = node.direct.tonode(node.direct.hpack(
+ newhead and node.direct.todirect(newhead) or 0))
+end, "protected")
+--
+--
+-- End of file `luamathalign.lua'.
diff --git a/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.sty b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.sty
new file mode 100644
index 00000000000..6a735b40120
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/luamathalign/luamathalign.sty
@@ -0,0 +1,98 @@
+%%
+%% This is file `luamathalign.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% luamathalign.dtx (with options: `package')
+%%
+%% Copyright (C) 2019--2022 by Marcel Krueger
+%%
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.3 or later is part of all distributions of
+%% LaTeX version 2005/12/01 or later.
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage
+ {luamathalign}
+ [2022-04-18 v0.1 additional math alignment tricks using Lua]
+\directlua{require'luamathalign'}
+\IfPackageLoadedTF{amsmath}{%
+ \@firstofone
+}{%
+ \AddToHook{package/amsmath/after}
+}
+{%
+ \def\align@preamble{%
+ &\hfil
+ \strut@
+ \setboxz@h{\@lign$\m@th\displaystyle{%
+ \LuaMathAlign@begin##\LuaMathAlign@end}$}%
+ \ifmeasuring@\savefieldlength@\fi
+ \set@field
+ \tabskip\z@skip
+ &\setboxz@h{\@lign$\m@th\displaystyle{{}##}$}%
+ \ifmeasuring@\savefieldlength@\fi
+ \set@field
+ \hfil
+ \tabskip\alignsep@
+ }
+ \renewcommand{\start@aligned}[2]{%
+ \RIfM@\else
+ \nonmatherr@{\begin{\@currenvir}}%
+ \fi
+ \savecolumn@ % Assumption: called inside a group
+ \alignedspace@left
+ \if #1t\vtop \else \if#1b \vbox \else \vcenter \fi \fi \bgroup
+ \maxfields@#2\relax
+ \ifnum\maxfields@>\m@ne
+ \multiply\maxfields@\tw@
+ \let\math@cr@@@\math@cr@@@alignedat
+ \alignsep@\z@skip
+ \else
+ \let\math@cr@@@\math@cr@@@aligned
+ \alignsep@\minalignsep
+ \fi
+ \Let@ \chardef\dspbrk@context\@ne
+ \default@tag
+ \spread@equation % no-op if already called
+ \global\column@\z@
+ \ialign\bgroup
+ &\column@plus
+ \hfil
+ \strut@
+ $\m@th\displaystyle{\LuaMathAlign@begin##\LuaMathAlign@end}$%
+ \tabskip\z@skip
+ &\column@plus
+ $\m@th\displaystyle{{}##}$%
+ \hfil
+ \tabskip\alignsep@
+ \crcr
+ }
+ \edef\math@cr@@@alignedat{\LuaMathAlign@end@early
+ \unexpanded\expandafter{\math@cr@@@alignedat}}
+ \edef\math@cr{\LuaMathAlign@end@early
+ \unexpanded\expandafter{\math@cr}}
+ \edef\endaligned{\LuaMathAlign@end@early
+ \unexpanded\expandafter{\endaligned}}
+}
+\protected\def\AlignHere{\SetAlignmentPoint\m@ne}
+\begingroup
+ \def\patch@finph@nt\setbox\tw@\null{%
+ \LuaMathAlign@IsolateAlignmentPoints\z@ into \tw@
+ }%
+\expanded{\endgroup%
+\protected\def\noexpand\finph@nt{%
+ \unexpanded\expandafter\expandafter\expandafter{%
+ \expandafter\patch@finph@nt\finph@nt
+ }%
+}}
+\ExplSyntaxOff
+\endinput
+%%
+%% End of file `luamathalign.sty'.