summaryrefslogtreecommitdiff
path: root/macros/luatex/generic
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic')
-rw-r--r--macros/luatex/generic/luaaddplot/README.md2
-rw-r--r--macros/luatex/generic/luaaddplot/luaaddplot.dtx11
-rw-r--r--macros/luatex/generic/luaaddplot/luaaddplot.pdfbin258501 -> 258865 bytes
-rw-r--r--macros/luatex/generic/luakeys/luakeys-debug.sty2
-rw-r--r--macros/luatex/generic/luakeys/luakeys-doc.tex2
-rw-r--r--macros/luatex/generic/luakeys/luakeys.lua4
-rw-r--r--macros/luatex/generic/luakeys/luakeys.pdfbin426656 -> 427014 bytes
-rw-r--r--macros/luatex/generic/luakeys/luakeys.sty2
-rw-r--r--macros/luatex/generic/luamplib/Makefile1
-rw-r--r--macros/luatex/generic/luamplib/NEWS61
-rw-r--r--macros/luatex/generic/luamplib/luamplib.dtx2680
-rw-r--r--macros/luatex/generic/luamplib/luamplib.pdfbin214279 -> 274674 bytes
-rw-r--r--macros/luatex/generic/luamplib/test-luamplib-latex.tex108
-rw-r--r--macros/luatex/generic/luamplib/test-luamplib-plain.tex68
-rw-r--r--macros/luatex/generic/luatexja/README6
-rw-r--r--macros/luatex/generic/luatexja/doc/ltjsclasses.pdfbin460413 -> 461972 bytes
-rw-r--r--macros/luatex/generic/luatexja/doc/luatexja-en.pdfbin699725 -> 700002 bytes
-rw-r--r--macros/luatex/generic/luatexja/doc/luatexja-ja.pdfbin1143479 -> 1144831 bytes
-rw-r--r--macros/luatex/generic/luatexja/doc/luatexja-ruby.pdfbin391680 -> 416321 bytes
-rw-r--r--macros/luatex/generic/luatexja/doc/luatexja-ruby.tex477
-rw-r--r--macros/luatex/generic/luatexja/doc/luatexja.dtx53
-rw-r--r--macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-27c.sty16
-rw-r--r--macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29c.sty16
-rw-r--r--macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29e.sty16
-rw-r--r--macros/luatex/generic/luatexja/src/addons/luatexja-ruby.sty18
-rw-r--r--macros/luatex/generic/luatexja/src/lltjext.sty128
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-adjust.lua34
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-ivd_aj1.lua7
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-jfmglue.lua10
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-jfont.lua18
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-latex.sty103
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-pretreat.lua7
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-rmlgbm.lua22
-rw-r--r--macros/luatex/generic/luatexja/src/ltj-ruby.lua368
-rw-r--r--macros/luatex/generic/luatexja/src/ltjsclasses.dtx9
-rw-r--r--macros/luatex/generic/luatexja/src/luatexja-core.sty5
-rw-r--r--macros/luatex/generic/luatexja/src/luatexja.lua7
-rw-r--r--macros/luatex/generic/luatexja/src/luatexja.sty2
-rw-r--r--macros/luatex/generic/luatexja/src/patches/lltjcore.sty9
-rw-r--r--macros/luatex/generic/luatexja/src/patches/lltjdefs.sty51
-rw-r--r--macros/luatex/generic/luatexja/src/patches/lltjfont.sty319
-rw-r--r--macros/luatex/generic/luatexja/src/patches/lltjp-array.sty25
-rw-r--r--macros/luatex/generic/luatexja/src/patches/lltjp-footmisc.sty62
-rw-r--r--macros/luatex/generic/luatexja/src/patches/lltjp-geometry.sty24
-rw-r--r--macros/luatex/generic/luatexja/src/patches/lltjp-siunitx.sty113
-rw-r--r--macros/luatex/generic/luaxml/README2
-rw-r--r--macros/luatex/generic/luaxml/luaxml-cssquery.lua14
-rw-r--r--macros/luatex/generic/luaxml/luaxml-domobject.lua186
-rw-r--r--macros/luatex/generic/luaxml/luaxml-encodings.lua89
-rw-r--r--macros/luatex/generic/luaxml/luaxml-mod-html.lua227
-rw-r--r--macros/luatex/generic/luaxml/luaxml-sty.lua155
-rw-r--r--macros/luatex/generic/luaxml/luaxml-transform.lua20
-rw-r--r--macros/luatex/generic/luaxml/luaxml.pdfbin119150 -> 139791 bytes
-rw-r--r--macros/luatex/generic/luaxml/luaxml.sty123
-rw-r--r--macros/luatex/generic/luaxml/luaxml.tex340
-rw-r--r--macros/luatex/generic/nodetree/README.md6
-rw-r--r--macros/luatex/generic/nodetree/nodetree-doc.tex2
-rw-r--r--macros/luatex/generic/nodetree/nodetree.dtx6
-rw-r--r--macros/luatex/generic/nodetree/nodetree.ins4
-rw-r--r--macros/luatex/generic/nodetree/nodetree.lua33
-rw-r--r--macros/luatex/generic/nodetree/nodetree.pdfbin290864 -> 284593 bytes
-rw-r--r--macros/luatex/generic/penlight/README.md2
-rw-r--r--macros/luatex/generic/penlight/penlight.lua355
-rw-r--r--macros/luatex/generic/penlight/penlight.pdfbin26787 -> 27431 bytes
-rw-r--r--macros/luatex/generic/penlight/penlight.sty6
-rw-r--r--macros/luatex/generic/penlight/penlight.tex9
-rw-r--r--macros/luatex/generic/penlightplus/penlightplus.lua250
-rw-r--r--macros/luatex/generic/penlightplus/penlightplus.pdfbin81225 -> 87726 bytes
-rw-r--r--macros/luatex/generic/penlightplus/penlightplus.sty71
-rw-r--r--macros/luatex/generic/penlightplus/penlightplus.tex256
70 files changed, 4740 insertions, 2282 deletions
diff --git a/macros/luatex/generic/luaaddplot/README.md b/macros/luatex/generic/luaaddplot/README.md
index ce4f4448e3..f89acae2ae 100644
--- a/macros/luatex/generic/luaaddplot/README.md
+++ b/macros/luatex/generic/luaaddplot/README.md
@@ -3,7 +3,7 @@ luaaddeplot
Version
-------
-1.0
+1.0a
Author
------
diff --git a/macros/luatex/generic/luaaddplot/luaaddplot.dtx b/macros/luatex/generic/luaaddplot/luaaddplot.dtx
index 7d3e4bf8c2..d6387dfbef 100644
--- a/macros/luatex/generic/luaaddplot/luaaddplot.dtx
+++ b/macros/luatex/generic/luaaddplot/luaaddplot.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-% $Id: luaaddplot.dtx 3 2022-03-19 23:12:51Z reinhard $
+% $Id: luaaddplot.dtx 6 2024-09-22 00:02:45Z reinhard $
%
%<*driver>
\documentclass[a4paper,11pt]{ltxdoc}
@@ -17,6 +17,7 @@
\author{Reinhard Kotucha}
\title{\textsf{luaaddplot}}
+\date{Version 1.0a\\[.5ex]\today}
\begin{document}
\maketitle
\vspace*{1cm}
@@ -70,8 +71,8 @@ ignored.
Different devices use different column delimiters. Therefore tabs,
colons, semicolons, and commas are replaced by a space. With other
-words, various datafile formats are converted to a format specified by
-\textsc{Matlab}.
+words, various datafile formats are converted to a to a \textsc{Matlab}
+compatible format.
\subsection{\texorpdfstring{$\lambda$}{lambda}-Expressions}
@@ -118,7 +119,7 @@ Frequencies are in \si{\hertz} but for the plot we prefer
\si{\mega\hertz}. The solution is
\begin{verbatim}
\luaaddplot[blue] file "spectrum.data", function (col)
- return col[1]/1e6, col2
+ return col[1]/1e6, col[2]
end;
\end{verbatim}
@@ -203,7 +204,7 @@ The solution is
% \end{macrocode}
% \subsection{luaaddplot.sty}
% \begin{macrocode}
-%<*sty> \ProvidesPackage{luaaddplot}[2022/03/18/ v1.0]
+%<*sty> \ProvidesPackage{luaaddplot}[2024/09/22 v1.0a]
\input luaaddplot.tex
%</sty>
% \end{macrocode}
diff --git a/macros/luatex/generic/luaaddplot/luaaddplot.pdf b/macros/luatex/generic/luaaddplot/luaaddplot.pdf
index 875fac1157..732135e6d8 100644
--- a/macros/luatex/generic/luaaddplot/luaaddplot.pdf
+++ b/macros/luatex/generic/luaaddplot/luaaddplot.pdf
Binary files differ
diff --git a/macros/luatex/generic/luakeys/luakeys-debug.sty b/macros/luatex/generic/luakeys/luakeys-debug.sty
index 57e1bae7f8..c52171cbe8 100644
--- a/macros/luatex/generic/luakeys/luakeys-debug.sty
+++ b/macros/luatex/generic/luakeys/luakeys-debug.sty
@@ -17,6 +17,6 @@
% luakeys-debug.sty and luakeys-debug.tex.
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luakeys-debug}[2024/04/09 v0.14.0 Debug package for luakeys.]
+\ProvidesPackage{luakeys-debug}[2024/09/29 v0.15.0 Debug package for luakeys.]
\input luakeys-debug.tex
diff --git a/macros/luatex/generic/luakeys/luakeys-doc.tex b/macros/luatex/generic/luakeys/luakeys-doc.tex
index e14569b459..0d5bba1aad 100644
--- a/macros/luatex/generic/luakeys/luakeys-doc.tex
+++ b/macros/luatex/generic/luakeys/luakeys-doc.tex
@@ -64,7 +64,7 @@ The default value of the option “\texttt{#1}” is:
\href{https://github.com/Josef-Friedrich/luakeys}
{github.com/Josef-Friedrich/luakeys}%
}
-\date{v0.14.0 from 2024/04/09}
+\date{v0.15.0 from 2024/09/29}
\maketitle
diff --git a/macros/luatex/generic/luakeys/luakeys.lua b/macros/luatex/generic/luakeys/luakeys.lua
index 224c77ed72..b47d2d47f2 100644
--- a/macros/luatex/generic/luakeys/luakeys.lua
+++ b/macros/luatex/generic/luakeys/luakeys.lua
@@ -1621,6 +1621,8 @@ local function main()
local picked_value = nil
if is[pick_type](v) then
picked_value = v
+ elseif pick_type == 'string' and is.number(v) then
+ picked_value = tostring(v)
end
if picked_value ~= nil then
@@ -2061,7 +2063,7 @@ local function main()
return {
new = main,
- version = { 0, 14, 0 },
+ version = { 0, 15, 0 },
parse = parse,
diff --git a/macros/luatex/generic/luakeys/luakeys.pdf b/macros/luatex/generic/luakeys/luakeys.pdf
index 2ab59e561c..7f10f1f50d 100644
--- a/macros/luatex/generic/luakeys/luakeys.pdf
+++ b/macros/luatex/generic/luakeys/luakeys.pdf
Binary files differ
diff --git a/macros/luatex/generic/luakeys/luakeys.sty b/macros/luatex/generic/luakeys/luakeys.sty
index 6369cff49f..69a44f1261 100644
--- a/macros/luatex/generic/luakeys/luakeys.sty
+++ b/macros/luatex/generic/luakeys/luakeys.sty
@@ -17,7 +17,7 @@
% luakeys-debug.sty and luakeys-debug.tex.
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luakeys}[2024/04/09 v0.14.0 Parsing key-value options using Lua.]
+\ProvidesPackage{luakeys}[2024/09/29 v0.15.0 Parsing key-value options using Lua.]
\directlua{
if luakeys == nil then
luakeys = require('luakeys')()
diff --git a/macros/luatex/generic/luamplib/Makefile b/macros/luatex/generic/luamplib/Makefile
index 87f04f727e..a0a2282675 100644
--- a/macros/luatex/generic/luamplib/Makefile
+++ b/macros/luatex/generic/luamplib/Makefile
@@ -40,6 +40,7 @@ world: all ctan
%.pdf: %.dtx
@$(DOLATEX)
@if( grep rerunfilecheck $(NAME).log |grep 'has changed' > /dev/null ); then $(DOLATEX); fi
+ @if( grep 'Rerun to get' $(NAME).log > /dev/null ); then $(DOLATEX); fi
$(UNPACKED): $(DTX)
luatex -interaction=batchmode $< >/dev/null
diff --git a/macros/luatex/generic/luamplib/NEWS b/macros/luatex/generic/luamplib/NEWS
index 50e7c625a9..f53e2b3602 100644
--- a/macros/luatex/generic/luamplib/NEWS
+++ b/macros/luatex/generic/luamplib/NEWS
@@ -1,5 +1,66 @@
History of the luamplib package
+2024/11/12 2.35.0
+ * even under \mplibtextextlabel{true}, the original 'infont' operator will
+ be used when the character slot of the text argument is less than 32, or is
+ equal to 35, 36, 37, 38, 92, 94, 95, 123, 125, 126 or 127.
+
+ * \mplibforcehmode will have effects on \usemplibgroup as well as ordinary
+ metapost figures.
+
+ * support tagged PDF when tagpdf package is activated. The related code is
+ currently in experimental stage. See the documentation for details.
+
+2024/08/03 2.34.5
+ * provide 'withgroupbbox' macro for transparency group to enable users to
+ control the bounding box
+
+ * write down the width/height/depth values of mplibgroup to the log file
+
+2024/07/31 2.34.4
+ * 'withpattern' operator accepts a <textual picture> as well as a <path>
+ for its operand. Thus users can give pattern effect to the result of btex
+ command or infont operator.
+
+ * fix regarding line width in mplibgraphictext
+
+2024/07/27 2.34.3
+ * in DVI mode, use LaTeX's shipout hooks to generate new XObjects
+ or to put resources to pageresources. (in plain, load atbegshi.sty)
+
+ * fix shading to allow the even-odd rule
+
+2024/07/24 2.34.2
+ * With the newly introduced macros, \mplibgroup ... \endmplibgroup, users
+ can define a transparency group or a normal form XObject from TeX side.
+ The LaTeX environment 'mplibgroup' is identical to this pair of macros.
+ See the document for details.
+
+ * document: be more precise about the color effect of a transparency group
+
+ * remove trailing zeros from numeric objects to get smaller PDF
+
+2024/07/19 2.34.1
+ * transparency group is available with plain format as well.
+ * transparency group once used is reusable in the TeX code or
+ in other MetaPost code chunks. see the document for details.
+
+2024/07/17 2.34.0
+ * support transparency group with metafun format
+ * fix regarding default value in circular fading mode
+ * fix regarging the scope of graphics state
+
+2024/07/14 2.33.1
+ * refactor the manual, including some minor items undocumented so far
+ * improve fading routine to get possibly smaller pdf file
+
+2024/07/08 2.33.0
+ * provide a new metapost operator 'withfademethod' and related macros,
+ which make the color of an object gradiently transparent. see luamplib
+ document for details.
+
+ * fix spot-color shading routine to support user-defined colorspace
+
2024/07/03 2.32.4
* make the variable 'patterns' local
* 'math.randomseed' on plain TeX
diff --git a/macros/luatex/generic/luamplib/luamplib.dtx b/macros/luatex/generic/luamplib/luamplib.dtx
index c050955a39..0510b15120 100644
--- a/macros/luatex/generic/luamplib/luamplib.dtx
+++ b/macros/luatex/generic/luamplib/luamplib.dtx
@@ -2,8 +2,8 @@
%
% Copyright (C) 2008-2024 by Hans Hagen, Taco Hoekwater, Elie Roux,
% Manuel Pégourié-Gonnard, Philipp Gesang and Kim Dohyun.
-% Currently maintained by the LuaLaTeX development team.
-% Support: <lualatex-dev@tug.org>
+% Currently maintained by Kim Dohyun.
+% Support: <https://github.com/lualatex/luamplib>
%
% This work is under the GPL v2.0 license.
%
@@ -85,9 +85,9 @@ See source file '\inFileName' for licencing and contact information.
%<*driver>
\NeedsTeXFormat{LaTeX2e}
\ProvidesFile{luamplib.drv}%
- [2024/07/03 v2.32.4 Interface for using the mplib library]%
+ [2024/11/12 v2.35.0 Interface for using the mplib library]%
\documentclass{ltxdoc}
-\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace}
+\usepackage{metalogo,multicol,xspace}
\usepackage[x11names]{xcolor}
%
\def\primarycolor{DodgerBlue4} %%-> rgb 16 78 139 | #104e8b
@@ -114,7 +114,7 @@ See source file '\inFileName' for licencing and contact information.
]{Linux Libertine O}
\setmonofont[Scale=MatchLowercase]{InconsolataN}
%setsansfont[Ligatures=TeX]{Linux Biolinum O}
-\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium}
+\setsansfont[UprightFont=*Medium,BoldFont=*Heavy,Ligatures=TeX,Scale=MatchLowercase]{Iwona}
%setmathfont{XITS Math}
\usepackage{hologo}
@@ -123,7 +123,9 @@ See source file '\inFileName' for licencing and contact information.
\newcommand*\email [1] {<\href{mailto:#1}{#1}>}
\newcommand \file {\nolinkurl}
-\newcommand \pk {\textsf}
+
+\newcommand*\metapost {\textsc{metapost}\xspace}
+\newcommand*\mplib {\pkg{mplib}\xspace}
\begin{document}
\DocInput{luamplib.dtx}%
@@ -149,125 +151,164 @@ See source file '\inFileName' for licencing and contact information.
% Grave accent \` Left brace \{ Vertical bar \|
% Right brace \} Tilde \~}
%
-% \title{The \textsf{luamplib} package}
+% \title{The \pkg{luamplib} package}
% \author{Hans Hagen, Taco Hoekwater, Elie Roux, Philipp Gesang and Kim Dohyun\\
-% Maintainer: LuaLaTeX Maintainers ---
-% Support: \email{lualatex-dev@tug.org}}
-% \date{2024/07/03 v2.32.4}
+% Current Maintainer: Kim Dohyun\\
+% Support: \url{https://github.com/lualatex/luamplib}}
+% \date{2024/11/12 v2.35.0}
%
% \maketitle
%
% \begin{abstract}
-% Package to have metapost code typeset directly in a document with \LuaTeX.
+% Package to have \metapost code typeset directly in a document with \LuaTeX.
% \end{abstract}
%
% \section{Documentation}
%
-% This packages aims at providing a simple way to typeset directly metapost
-% code in a document with \LuaTeX. \LuaTeX\ is built with the lua
-% \texttt{mplib} library, that runs metapost code. This package is basically a
-% wrapper (in Lua) for the Lua \texttt{mplib} functions and some \TeX\
-% functions to have the output of the \texttt{mplib} functions in the pdf.
-%
-% In the past,
-% the package required PDF mode in order to output something.
-% Starting with version 2.7 it works in DVI mode as well, though
-% DVIPDFMx is the only DVI tool currently supported.
-%
-% The metapost figures are put in a \TeX\ \texttt{hbox} with dimensions
-% adjusted to the metapost code.
+% This package aims at providing a simple way to typeset directly \metapost
+% code in a document with \LuaTeX. \LuaTeX\ is built with the Lua
+% \mplib library, that runs \metapost code. This package is basically a
+% wrapper for the Lua \mplib functions and some \TeX\
+% functions to have the output of the \mplib functions in the pdf.
%
-% Using this package is easy: in Plain, type your metapost code between the
+% Using this package is easy: in Plain, type your \metapost code between the
% macros \cs{mplibcode} and \cs{endmplibcode}, and in \LaTeX\ in the
-% \texttt{mplibcode} environment.
+% |mplibcode| environment.
%
-% The code is from the \texttt{luatex-mplib.lua} and \texttt{luatex-mplib.tex} files
-% from \ConTeXt, they have been adapted to \LaTeX\ and Plain by Elie Roux and
-% Philipp Gesang, new functionalities have been added by Kim Dohyun.
-% The changes are:
+% The resulting \metapost figures are put in a \TeX\ |hbox| with dimensions
+% adjusted to the \metapost code.
+%
+% The code of luamplib is basically from the |luatex-mplib.lua| and |luatex-mplib.tex| files
+% from \ConTeXt. They have been adapted to \LaTeX\ and Plain by Elie Roux and
+% Philipp Gesang and new functionalities have been added by Kim Dohyun.
+% The most notable changes are:
%
% \begin{itemize}
-% \item a \LaTeX\ environment
-% \item all \TeX\ macros start by |mplib|
-% \item use of our own function for errors, warnings and informations
% \item possibility to use |btex ... etex| to typeset \TeX\ code.
-% |textext()| is a more versatile macro equivalent to |TEX()| from TEX.mp.
-% |TEX()| is also allowed and is a synomym of |textext()|.\par\smallskip
-% \textsc{n.b.} Since v2.5, |btex ... etex| input from external |mp| files
-% will also be processed by \textsf{luamplib}.\par\smallskip
-% \textsc{n.b.} Since v2.20, |verbatimtex ... etex| from external |mp| files
-% will be also processed by \textsf{luamplib}. Warning: This is a change
-% from previous version.
+% |textext()| is a more versatile macro equivalent to |TEX()| from \pkg{TEX.mp}.
+% |TEX()| is also allowed and is a synonym of |textext()|.
+% The argument of
+% \mplib's primitive |maketext| will also be processed by the same routine.
+%
+% \item possibility to use |verbatimtex ... etex|, though it's behavior cannot
+% be the same as the stand-alone \pkg{mpost}.
+% Of course you cannot include \cs{documentclass}, \cs{usepackage} etc.
+% When these \TeX\ commands are found in |verbatimtex ... etex|, the entire code will be
+% ignored.
+% The treatment of |verbatimtex| command has changed a lot since v2.20:
+% see \hyperlink{mpliblegacybehavior}{below \S\,1.1}.
+%
+% \item in the past,
+% the package required PDF mode in order to have some output.
+% Starting with version 2.7 it works in DVI mode as well, though
+% DVIPDFMx is the only DVI tool currently supported.
% \end{itemize}
%
-% Some more changes and cautions are:
+% It seems to be convenient to divide the explanations of some more changes and cautions
+% into three parts: \TeX, \textsc{MetaPost}, and Lua interfaces.
+%
+% \subsection{\TeX}
%
% \paragraph{\cs{mplibforcehmode}}
-% When this macro is declared, every mplibcode figure box will be
+% When this macro is declared, every \metapost figure box will be
% typeset in horizontal mode, so \cs{centering}, \cs{raggedleft} etc
-% will have effects. |\mplibnoforcehmode|, being default, reverts this
-% setting. (Actually these commands redefine |\prependtomplibbox|. You
-% can define this command with anything suitable before a box.)
+% will have effects. \cs{mplibnoforcehmode}, being default, reverts this
+% setting. (Actually these commands redefine \cs{prependtomplibbox}; you
+% can redefine this command with anything suitable before a box.)
%
-% \paragraph{\cs{mpfig} \ldots\ \cs{endmpfig}}
-% Since v2.29 we provide unexpandable \TeX\ macros |\mpfig ... \endmpfig| and its starred version
-% |\mpfig* ... \endmpfig| to save typing toil.
-% The first is roughly the same as follows:
-% \begin{verbatim}
-% \begin{mplibcode}[@mpfig]
-% beginfig(0)
-% token list declared by \everymplib[@mpfig]
-% ...
-% token list declared by \everyendmplib[@mpfig]
-% endfig;
-% \end{mplibcode}
-% \end{verbatim}
-% and the starred version is roughly the same as follows:
-% \begin{verbatim}
-% \begin{mplibcode}[@mpfig]
-% ...
+% \paragraph{\cs{everymplib\{...\}}, \cs{everyendmplib\{...\}}}
+% \cs{everymplib} and \cs{everyendmplib} redefine
+% the lua table containing \metapost code
+% which will
+% be automatically inserted at the beginning and ending of each \metapost code chunk.
+%\begin{verbatim}
+% \everymplib{ beginfig(0); }
+% \everyendmplib{ endfig; }
+% \begin{mplibcode}
+% % beginfig/endfig not needed
+% draw fullcircle scaled 1cm;
% \end{mplibcode}
-% \end{verbatim}
-% In these macros |\mpliblegacybehavior{disable}| (see below)
-% is forcibly declared.
-% And as both share the same instance name, metapost codes are inherited among them.
-% A simple example:
-% \begin{verbatim}
-% \mpfig* input boxes \endmpfig
-% \everymplib[@mpfig]{ drawoptions(withcolor .5[red,white]); }
-% \mpfig circleit.a(btex Box 1 etex); drawboxed(a); \endmpfig
-% \end{verbatim}
-% The instance name (default: |@mpfig|) can be changed by redefining
-% \cs{mpfiginstancename}, after which a new MPlib instance will start and
-% code inheritance too will begin anew. |\let\mpfiginstancename\empty| will
-% prevent code inheritance if |\mplibcodeinherit{true}| (see below) is not declared.\footnote{%
-% As for user setting values, |enable|, |true|, |yes| are identical, and
-% |disable|, |false|, |no| are identical.}
-%
-% \paragraph{\cs{mpliblegacybehavior\{enable\}}}
-% By default, |\mpliblegacybehavior{enable}| is already declared,
-% in which case
-% a |verbatimtex ... etex| that comes just before |beginfig()|
-% is not ignored, but the \TeX\ code will be inserted before the
-% following mplib hbox. Using this command,
-% each mplib box can be freely moved horizontally and/or vertically.
-% Also, a box number might be assigned to mplib box, allowing it to be
-% reused later (see test files).
-% \begin{verbatim}
+%\end{verbatim}
+%
+% \paragraph{\cs{mplibsetformat\{plain\textbar metafun\}}}
+% There are (basically) two formats for \metapost: \emph{plain} and
+% \emph{metafun}. By default, the \emph{plain} format is used, but you can set
+% the format to be used by future figures at any time using
+% \cs{mplibsetformat\{<format name>\}}.
+%
+% \textsc{n.b.} As \emph{metafun} is such a complicated format,
+% we cannot support all the functionalities producing special effects provided by \emph{metafun}.
+% At least, however, transparency (actually opacity), shading (gradient colors) and transparency group
+% are fully supported,
+% and outlinetext is supported by our own alternative |mpliboutlinetext|
+% (see \hyperlink{mpliboutlinetext}{below \S\,1.2}).
+%
+% \leavevmode\llap{\textcolor{red}{☞}\kern1.2\parindent}\relax
+% Among these, transparency (|texdoc metafun| \S\,8.2) is so simple that you can apply it to an object,
+% with \emph{plain} format as well,
+% just by appending |withprescript| |"tr_transparency=|\hskip0pt\relax|<number>"| to the sentence.
+% ($0 \le \hbox{\emph{<number>}} \le 1$)
+%
+% One thing worth mentioning about shading (|texdoc metafun| \S\,8.3) is:
+% when a color expression is given in string type,
+% it is regarded by luamplib as
+% a color expression of \TeX\ side.
+% For instance, when |withshadecolors("orange", 2/3red)| is given, the first color |"orange"| will be
+% interpreted as a \pkg{color}, \pkg{xcolor} or \pkg{l3color}'s expression.
+%
+% \hypertarget{metafunformat}{}\relax
+% As for transparency group, the current \emph{metafun} document \S\,8.8 is not correct.
+% The true syntax is:
+%\begin{verbatim}
+% draw <picture>|<path> asgroup <string>
+%\end{verbatim}
+% where \emph{<string>} should be |""| (empty), |"isolated"|, |"knockout"|, or |"isolated,knockout"|.
+% Beware that currently many of the PDF rendering applications, except Adobe Acrobat Reader,
+% cannot properly render the isolated or knockout effect.
+% Transparency group is available with \emph{plain} format as well, with extended functionality.
+% See \hyperlink{transparencygroup}{below \S\,1.2}.
+%
+% \paragraph{\cs{mplibnumbersystem\{scaled\textbar double\textbar decimal\}}}
+% Users can choose |numbersystem| option.
+% The default value is |scaled|, which can be changed
+% by declaring \cs{mplibnumbersystem\{double\}} or
+% \cs{mplibnumbersystem\{decimal\}}.
+%
+% \paragraph{\cs{mplibshowlog\{enable\textbar disable\}}}
+% Default: |disable|.
+% When \cs{mplibshowlog\{enable\}}\footnote{As for user's setting,
+% |enable|, |true| and |yes| are identical;
+% |disable|, |false| and |no| are identical.} is declared, log messages returned by
+% the \metapost process will be printed to the |.log| file.
+% This is the \TeX{} side interface for |luamplib.showlog|.
+%
+% \paragraph{\cs{mpliblegacybehavior\{enable\textbar disable\}}}
+% \hypertarget{mpliblegacybehavior}{}\relax
+% By default, \cs{mpliblegacybehavior\{enable\}}
+% is already declared for backward compatibility,
+% in which case \TeX\ code in
+% |verbatimtex| |...| |etex| that comes just before |beginfig()|
+% will be inserted before the
+% following \metapost figure box. In this way,
+% each figure box can be freely moved horizontally or vertically.
+% Also, a box number can be assigned to a figure box, allowing it to be
+% reused later.
+%\begin{verbatim}
% \mplibcode
% verbatimtex \moveright 3cm etex; beginfig(0); ... endfig;
% verbatimtex \leavevmode etex; beginfig(1); ... endfig;
% verbatimtex \leavevmode\lower 1ex etex; beginfig(2); ... endfig;
% verbatimtex \endgraf\moveright 1cm etex; beginfig(3); ... endfig;
% \endmplibcode
-% \end{verbatim}
+%\end{verbatim}
% \textsc{n.b.} \cs{endgraf} should be used instead of \cs{par} inside
% |verbatimtex ... etex|.
%
-% By contrast, \TeX\ code in |VerbatimTeX(...)| or |verbatimtex ... etex|
+% On the other hand, \TeX\ code in |verbatimtex ... etex|
% between |beginfig()| and |endfig| will be inserted
-% after flushing out the mplib figure.
-% \begin{verbatim}
+% after flushing out the \metapost figure.
+% As shown in the example below, |VerbatimTeX()| is a synonym of |verbatimtex| |...| |etex|.
+%\begin{verbatim}
% \mplibcode
% D := sqrt(2)**7;
% beginfig(0);
@@ -276,15 +317,15 @@ See source file '\inFileName' for licencing and contact information.
% endfig;
% \endmplibcode
% diameter: \Dia bp.
-% \end{verbatim}
+%\end{verbatim}
%
-% \paragraph{\cs{mpliblegacybehavior\{disable\}}}
-% If |\mpliblegacybehavior{disabled}| is declared by user, any
-% |verbatimtex ... etex| will be executed, along with |btex ... etex|,
+% By contrast,
+% when \cs{mpliblegacybehavior\{disable\}} is declared, any
+% |verbatimtex| |...| |etex| will be executed, along with |btex| |...| |etex|,
% sequentially one by one.
% So, some \TeX\ code in |verbatimtex ... etex| will have effects on
-% |btex ... etex| codes that follows.
-% \begin{verbatim}
+% following |btex| |...| |etex| codes.
+%\begin{verbatim}
% \begin{mplibcode}
% beginfig(0);
% draw btex ABC etex;
@@ -293,114 +334,80 @@ See source file '\inFileName' for licencing and contact information.
% draw btex GHI etex shifted (2cm,0); % bold face
% endfig;
% \end{mplibcode}
-% \end{verbatim}
-%
-% \paragraph{\cs{everymplib}, \cs{everyendmplib}}
-% Since v2.3, new macros \cs{everymplib} and \cs{everyendmplib} redefine
-% the lua table containing MetaPost code
-% which will
-% be automatically inserted at the beginning and ending of each |mplibcode|.
-% \begin{verbatim}
-% \everymplib{ beginfig(0); }
-% \everyendmplib{ endfig; }
-% \mplibcode % beginfig/endfig not needed
-% draw fullcircle scaled 1cm;
-% \endmplibcode
-% \end{verbatim}
-%
-% \paragraph{\cs{mpdim}}
-% Since v2.3, \cs{mpdim} and other raw \TeX\ commands are allowed
-% inside mplib code. This feature is inpired by gmp.sty authored by
-% Enrico Gregorio. Please refer the manual of gmp package for details.
-% \begin{verbatim}
-% \begin{mplibcode}
-% draw origin--(.6\mpdim{\linewidth},0) withpen pencircle scaled 4
-% dashed evenly scaled 4 withcolor \mpcolor{orange};
-% \end{mplibcode}
-% \end{verbatim}
-% \textsc{n.b.} Users should not use the protected variant of
-% |btex ... etex| as provided by gmp package. As \textsf{luamplib}
-% automatically protects \TeX\ code inbetween, \cs{btex} is not supported
-% here.
-%
-% \paragraph{\cs{mpcolor}}
-% With \cs{mpcolor} command, color names or expressions of
-% \textsf{color}/\textsf{xcolor} packages can be used inside mplibcode
-% enviroment (after |withcolor| operator),
-% though \textsf{luamplib} does not automatically load these
-% packages. See the example code above. For spot colors,
-% \textsf{colorspace}, \textsf{spotcolor}
-% (in PDF mode) and \textsf{xespotcolor} (in DVI mode) packages are supported
-% as well.
-%
-% From v2.26.1, \textsf{l3color} is also supported by the command
-% \cs{mpcolor\{color expression\}}, including spot colors.
+%\end{verbatim}
%
-% \paragraph{\cs{mplibnumbersystem}}
-% Users can choose |numbersystem| option since v2.4.
-% The default value |scaled| can be changed to |double| or |decimal|
-% by declaring |\mplibnumbersystem{double}| or |\mplibnumbersystem{decimal}|.
-% For details see
-% \url{http://github.com/lualatex/luamplib/issues/21}.
-%
-% \paragraph{\cs{mplibtextextlabel}}
-% Starting with v2.6, |\mplibtextextlabel{enable}| enables
-% string labels typeset via |textext()| instead of |infont| operator.
+% \paragraph{\cs{mplibtextextlabel\{enable\textbar disable\}}}
+% Default: |disable|.
+% \cs{mplibtextextlabel\{enable\}} enables
+% the labels typeset via |textext| instead of |infont| operator.
% So, |label("my text",origin)| thereafter is exactly the same as
-% |label(textext("my text"),origin)|. \textsc{n.b.} In the background,
-% \textsf{luamplib} redefines |infont| operator so that the right side
-% argument (the font part) is totally ignored. Every string label
-% therefore will be typeset with current \TeX\ font.
-% Also take care of |char| operator in the left side argument,
+% |label(textext("my text"),origin)|.
+%
+% \textsc{n.b.} In the background,
+% luamplib redefines |infont| operator so that the right side
+% argument (the font part) is totally ignored. Therefore the left side arguemnt (the text part)
+% will be typeset with the current \TeX\ font.
+%
+% From v2.35, however, the redefinition of |infont| operator has been revised:
+% when the character slot of the text argument is less than $32$~(control characters),
+% or is equal to $35$~(|#|), $36$~(|$|), \iffalse |$|\fi
+% $37$~(|%|), $38$~(|&|), $92$~(|\|), $94$~(|^|),
+% $95$~(|_|), $123$~(|{|), $125$~(|}|), $126$~(|~|) or $127$~(DEL),
+% the original |infont| operator will be used instead of |textext| operator
+% so that the font part will be honored.
+% Despite the revision, please take care of |char| operator in the text argument,
% as this might bring unpermitted characters into \TeX.
%
-% \paragraph{\cs{mplibcodeinherit}}
-% Starting with v2.9, |\mplibcodeinherit{enable}| enables the inheritance
-% of variables, constants, and macros defined by previous |mplibcode| chunks.
-% On the contrary, the default value |\mplibcodeinherit{disable}| will make
-% each code chunks being treated as an independent instance, and never
+% \paragraph{\cs{mplibcodeinherit\{enable\textbar disable\}}}
+% Default: |disable|.
+% \cs{mplibcodeinherit\{enable\}} enables the inheritance
+% of variables, constants, and macros defined by previous \metapost code chunks.
+% On the contrary, \cs{mplibcodeinherit\{disable\}} will make
+% each code chunk being treated as an independent instance, never
% affected by previous code chunks.
%
-% \paragraph{Separate instances for \LaTeX{} and plain \TeX}
-% v2.22 has added the support for several named MetaPost instances
+% \paragraph{Separate \metapost instances}
+% luamplib v2.22 has added the support for several named \metapost instances
% in \LaTeX{} |mplibcode| environment.
-% (And since v2.29 plain \TeX\ users can use this functionality as well.)
-% Syntax is like so:
-% \begin{verbatim}
+% Plain \TeX\ users also can use this functionality.
+% The syntax for \LaTeX\ is:
+%\begin{verbatim}
% \begin{mplibcode}[instanceName]
% % some mp code
% \end{mplibcode}
-% \end{verbatim}
-% Behaviour is as follows.
+%\end{verbatim}
+% The behavior is as follows.
% \begin{itemize}
% \item All the variables and functions are shared
% only among all the environments belonging to the same instance.
-% \item |\mplibcodeinherit| only affects environments
+% \item \cs{mplibcodeinherit} only affects environments
% with no instance name set (since if a name is set,
% the code is intended to be reused at some point).
-% \item From v2.27, |btex ... etex| boxes are also shared and do not
-% require |\mplibglobaltextext|.
+% \item |btex ... etex| boxes are also shared and do not
+% require \cs{mplibglobaltextext}.
% \item When an instance names is set,
-% respective |\currentmpinstancename| is set.
+% respective \cs{currentmpinstancename} is set as well.
% \end{itemize}
-% In parellel with this functionality, v2.23 and after supports
+% In parellel with this functionality, we support
% optional argument of instance name for \cs{everymplib} and
% \cs{everyendmplib}, affecting only those |mplibcode| environments
% of the same name.
% Unnamed \cs{everymplib} affects not only those instances with no name,
% but also those with name but with no corresponding \cs{everymplib}.
-% Syntax is:
-% \begin{verbatim}
+% The syntax is:
+%\begin{verbatim}
% \everymplib[instanceName]{...}
% \everyendmplib[instanceName]{...}
-% \end{verbatim}
+%\end{verbatim}
%
-% \paragraph{\cs{mplibglobaltextext}}
-% Formerly, to inherit |btex ... etex| boxes as well as metapost variables,
+% \paragraph{\cs{mplibglobaltextext\{enable\textbar disable\}}}
+% Default: |disable|.
+% Formerly, to inherit |btex| |...| |etex| boxes as well as other \metapost macros, variables and constants,
% it was necessary to declare \cs{mplibglobaltextext\{enable\}} in advance.
% But from v2.27, this is implicitly enabled when \cs{mplibcodeinherit}
-% is |true|.
-% \begin{verbatim}
+% is enabled.
+% This optional command still remains mostly for backward compatibility.
+%\begin{verbatim}
% \mplibcodeinherit{enable}
% %\mplibglobaltextext{enable}
% \everymplib{ beginfig(0);} \everyendmplib{ endfig;}
@@ -412,38 +419,92 @@ See source file '\inFileName' for licencing and contact information.
% \mplibcode
% currentpicture := pic scaled 2;
% \endmplibcode
-% \end{verbatim}
-% Generally speaking, it is recommended to turn |mplibglobaltextext|
-% always on, because it has the advantage of reusing metapost pictures
-% among code chunks.
-% But everything has its downside: it will waste more memory resources.
-%
-% \paragraph{\cs{mplibverbatim}}
-% Starting with v2.11, users can issue |\mplibverbatim{enable}|, after which
+%\end{verbatim}
+%
+% \paragraph{\cs{mplibverbatim\{enable\textbar disable\}}}
+% Default: |disable|.
+% Users can issue \cs{mplibverbatim\{enable\}}, after which
% the contents of mplibcode environment will be read verbatim. As a result,
-% except for |\mpdim| and |\mpcolor|, all other \TeX\ commands outside
-% |btex ... etex| or |verbatimtex ... etex| are not expanded and will be fed
-% literally into the mplib process.
+% except for \cs{mpdim} and \cs{mpcolor} (see \hyperlink{mpdim}{below}),
+% all other \TeX\ commands outside of the
+% |btex| or |verbatimtex| |...| |etex| are not expanded and will be fed
+% literally to the \mplib library.
+%
+% \paragraph{\cs{mpdim\{...\}}}
+% \hypertarget{mpdim}{}\relax
+% Besides other \TeX\ commands, \cs{mpdim} is specially allowed
+% in the mplibcode environment. This feature is inpired by \pkg{gmp} package authored by
+% Enrico Gregorio. Please refer to the manual of \pkg{gmp} package for details.
+%\begin{verbatim}
+% \begin{mplibcode}
+% beginfig(1)
+% draw origin--(.6\mpdim{\linewidth},0) withpen pencircle scaled 4
+% dashed evenly scaled 4 withcolor \mpcolor{orange};
+% endfig;
+% \end{mplibcode}
+%\end{verbatim}
%
-% \paragraph{\cs{mplibshowlog}}
-% When |\mplibshowlog{enable}| is declared, log messages returned by
-% |mplib| instance will be printed into the |.log| file.
-% |\mplibshowlog{disable}| will revert this functionality.
-% This is a \TeX{} side interface for |luamplib.showlog|. (v2.20.8)
+% \paragraph{\cs{mpcolor[...]\{...\}}}
+% With \cs{mpcolor} command, color names or expressions of
+% \pkg{color}, \pkg{xcolor} and \pkg{l3color} module/packages can be used in the mplibcode
+% environment (after |withcolor| operator).
+% See the example \hyperlink{mpdim}{above}.
+% The optional |[...]| denotes the option of \pkg{xcolor}'s \cs{color} command.
+% For spot colors, \pkg{l3color} (in PDF/DVI mode),
+% \pkg{colorspace}, \pkg{spotcolor}
+% (in PDF mode) and \pkg{xespotcolor} (in DVI mode) packages are supported
+% as well.
+%
+% \paragraph{\cs{mpfig} \ldots\ \cs{endmpfig}}
+% Besides the |mplibcode| environment (for \LaTeX) and
+% \cs{mplibcode ...} \cs{endmplibcode} (for Plain),
+% we also provide unexpandable \TeX\ macros \cs{mpfig} |...| \cs{endmpfig} and its starred version
+% \cs{mpfig*} |...| \cs{endmpfig} to save typing toil.
+% The former is roughly the same as follows:
+%\begin{verbatim}
+% \begin{mplibcode}[@mpfig]
+% beginfig(0)
+% token list declared by \everymplib[@mpfig]
+% ...
+% token list declared by \everyendmplib[@mpfig]
+% endfig;
+% \end{mplibcode}
+%\end{verbatim}
+% and the starred version is roughly the same as follows:
+%\begin{verbatim}
+% \begin{mplibcode}[@mpfig]
+% ...
+% \end{mplibcode}
+%\end{verbatim}
+% In these macros \cs{mpliblegacybehavior\{disable\}}
+% is forcibly declared.
+% Again, as both share the same instance name, \metapost codes are inherited among them.
+% A simple example:
+%\begin{verbatim}
+% \everymplib[@mpfig]{ drawoptions(withcolor .5[red,white]); }
+% \mpfig* input boxes \endmpfig
+% \mpfig
+% circleit.a(btex Box 1 etex); drawboxed(a);
+% \endmpfig
+%\end{verbatim}
+% The instance name (default: |@mpfig|) can be changed by redefining
+% \cs{mpfiginstancename}, after which a new \mplib instance will start and
+% code inheritance too will begin anew. \cs{let}\cs{mpfiginstancename}\cs{empty} will
+% prevent code inheritance if \cs{mplibcodeinherit\{true\}} is not declared.
%
% \paragraph{About cache files}
-% To support |btex ... etex| in external |.mp| files, \textsf{luamplib}
-% inspects the content of each and every |.mp| input files and makes caches
-% if nececcsary, before returning their paths to \LuaTeX's mplib library.
-% This would make the compilation time longer wastefully, as most |.mp| files
-% do not contain |btex ... etex| command. So \textsf{luamplib} provides
-% macros as follows, so that users can give instruction about files
+% To support |btex ... etex| in external |.mp| files, luamplib
+% inspects the content of each and every |.mp| file and makes caches
+% if nececcsary, before returning their paths to \LuaTeX's \mplib library.
+% This could waste the compilation time, as most |.mp| files
+% do not contain |btex ... etex| commands. So luamplib provides
+% macros as follows, so that users can give instructions about files
% that do not require this functionality.
% \begin{itemize}
-% \item |\mplibmakenocache{<filename>[,<filename>,...]}|
-% \item |\mplibcancelnocache{<filename>[,<filename>,...]}|
+% \item \cs{mplibmakenocache\{<filename>[,<filename>,...]\}}
+% \item \cs{mplibcancelnocache\{<filename>[,<filename>,...]\}}
% \end{itemize}
-% where |<filename>| is a file name excluding |.mp| extension.
+% where |<filename>| is a filename excluding |.mp| extension.
% Note that |.mp| files under |$TEXMFMAIN/metapost/base| and
% |$TEXMFMAIN/metapost/context/base| are already registered by default.
%
@@ -451,64 +512,152 @@ See source file '\inFileName' for licencing and contact information.
% if it's not available (mostly not writable),
% in the directory where output files are saved:
% to be specific, |$TEXMF_OUTPUT_DIRECTORY/luamplib_cache|,
-% |./luamplib_cache|, |$TEXMFOUTPUT/luamplib_cache|, and |.| in this order.
-% (|$TEXMF_OUTPUT_DIRECTORY| is normally the value of |--output-directory|
-% command-line option.)
-% This behavior however can be changed by the command
-% |\mplibcachedir{<directory path>}|, where tilde (|~|) is interpreted
+% |./luamplib_cache|, |$TEXMFOUTPUT/luamplib_cache|, and |.|, in this order.
+% |$TEXMF_OUTPUT_DIRECTORY| is normally the value of |--output-directory|
+% command-line option.
+%
+% Users can change this behavior by the command
+% \cs{mplibcachedir\{<directory path>\}}, where tilde (|~|) is interpreted
% as the user's home directory (on a windows machine as well).
-% As backslashes (|\|) should be escaped by users, it would be easier to use
+% As backslashes (\cs{}) should be escaped by users, it would be easier to use
% slashes (|/|) instead.
%
-% \paragraph{\texttt{mplibtexcolor}, \texttt{mplibrgbtexcolor}}
-% |mplibtexcolor| is a metapost operator that converts a \TeX\ color expression
-% to a MetaPost color expression. For instance:
-% \begin{verbatim}
+% \paragraph{About figure box metric}
+% Notice that, after each figure is processed, the macro \cs{MPwidth} stores
+% the width value of the latest figure; \cs{MPheight}, the height value.
+% Incidentally, also note that \cs{MPllx}, \cs{MPlly}, \cs{MPurx}, and
+% \cs{MPury} store the bounding box information of the latest figure
+% without the unit |bp|.
+%
+% \paragraph{luamplib.cfg}
+% At the end of package loading, luamplib searches
+% |luamplib.cfg| and, if found, reads the file in automatically.
+% Frequently used settings such as \cs{everymplib}, \cs{mplibforcehmode}
+% or \cs{mplibcodeinherit} are suitable for going into this file.
+%
+% \paragraph{Tagged PDF}
+% When \pkg{tagpdf} package is loaded and activated, |mplibcode| environment accepts additional options for tagged PDF\@.
+% The code related to this functionality is currently in experimental stage, not guaranteeing backward compatibility.
+% Like the \LaTeX's |picture| environment,
+% available optional keys are |tag|, |alt|, |actualtext|, |artifact|, |debug| and |correct-BBox|
+% (|texdoc| |latex-lab-graphic|).
+% Additionally, luamplib provides its own |text| key.
+% \begin{description}
+% \item[|tag=...|] You can choose a tag name, default value being |Figure|.
+% BBox info will be added automatically to the PDF
+% unless the value is |artifact|, |text|, or |false|.
+% When the value is |false|, tagging is deactivated.
+% \item[|debug|] draws bounding box of the figure for checking, which you can correct
+% by |correct-BBox| key with space-separated four dimen values.
+% \item[|alt=...|] sets an alternative text of the figure as given.
+% This key is needed for ordinary \metapost figures.
+% You can give alternative text within \metapost code as well:
+% |verbatimtex| |\mplibalttext{...}| |etex;|
+% \item[|artifact|] starts an |artifact| MC (marked content).
+% BBox info will not be added.
+% This key is intended for decorative figures which have no semantic quality.
+% \item[|actualtext=...|] starts a |Span| tag implicitly and sets an actual text as given.
+% Horizontal mode is forced by \cs{noindent} command. BBox info will not be added.
+% This key is intended for figures which can be represented by a character or
+% a small sequence of characters.
+% You can give actual text within \metapost code as well:
+% |verbatimtex| |\mplibactualtext{...}| |etex;|
+% \item[|text|] starts an |artifact| MC and enables tagging on textext
+% (the same as |btex| |...| |etex|) boxes.
+% Horizontal mode is forced by \cs{noindent} command. BBox info will not be added.
+% This key is intended for figures made mostly of textext boxes.
+% Inside text-keyed figures, reusing textext boxes is strongly discouraged.
+% \end{description}
+% These keys are provided also for \cs{mpfig} and \cs{usemplibgroup} (see \hyperlink{usemplibgroup}{below}) commands.
+%\begin{verbatim}
+% \begin{mplibcode}[myInstanceName, alt=figure drawing a circle]
+% ...
+% \end{mplibcode}
+%
+% \mpfig[alt=figure drawing a square box]
+% ...
+% \endmpfig
+%
+% \usemplibgroup[alt=figure drawing a triangle]{...}
+%
+% \mppattern{...} % see below
+% \mpfig[tag=false] % do not tag this figure
+% ...
+% \endmpfig
+% \endmppattern
+%\end{verbatim}
+% As for the instance name of |mplibcode| environment,
+% |instance=...| or |instancename=...| is also allowed
+% in addition to the raw instance name as shown above.
+%
+%
+% \subsection{\textsc{MetaPost}}
+%
+% \paragraph{\texttt{mplibdimen(...)}, \texttt{mplibcolor(...)}}
+% These are \metapost interfaces for the \TeX\ commands
+% \cs{mpdim} and \cs{mpcolor} (see \hyperlink{mpdim}{above}). For example,
+% |mplibdimen("\linewidth")| is basically the same as \cs{mpdim\{\cs{linewidth}\}}, and
+% |mplibcolor("red!50")| is basically the same as \cs{mpcolor\{red!50\}}.
+% The difference is that these \metapost operators can also be used in external |.mp| files,
+% which cannot have \TeX\ commands outside of the |btex| or |verbatimtex| |...| |etex|.
+%
+% \paragraph{\texttt{mplibtexcolor ...}, \texttt{mplibrgbtexcolor ...}}
+% |mplibtexcolor|, which accepts a string argument, is a \metapost operator that converts a \TeX\ color expression
+% to a \metapost color expression, that can be used anywhere color expression is expected
+% as well as after the |withcolor| operator.
+% For instance:
+%\begin{verbatim}
% color col;
% col := mplibtexcolor "olive!50";
-% \end{verbatim}
-% The result may vary in its color model (gray/rgb/cmyk)
+%\end{verbatim}
+% But the result may vary in its color model (gray/rgb/cmyk)
% according to the given \TeX\ color. (Spot colors are forced to
% cmyk model, so this operator is not recommended for spot colors.)
-% Therefore the example shown above would raise a metapost error:
+% Therefore the example shown above would raise a \metapost error:
% |cmykcolor col;| should have been declared.
-% By contrast, |mplibrgbtexcolor| always returns rgb model expressions.
-%
-% \paragraph{\texttt{mplibgraphictext}}
-% For some amusement, luamplib provides its own metapost operator
-% |mplibgraphictext|, the effect of which is similar to that of
-% \ConTeXt's |graphictext|. However syntax is somewhat different.
-% \begin{verbatim}
+% By contrast, |mplibrgbtexcolor| \emph{<string>} always returns rgb model expressions.
+%
+% \paragraph{\texttt{mplibgraphictext ...}}
+% |mplibgraphictext| is a \metapost operator, the effect of which is similar to that of
+% \ConTeXt's |graphictext| or our own |mpliboutlinetext|
+% (see \hyperlink{mpliboutlinetext}{below}).
+% However the syntax is somewhat different.
+%\begin{verbatim}
% mplibgraphictext "Funny"
% fakebold 2.3 % fontspec option
% drawcolor .7blue fillcolor "red!50" % color expressions
-% \end{verbatim}
+%\end{verbatim}
% |fakebold|, |drawcolor| and |fillcolor| are optional;
% default values are |2|, |"black"| and |"white"| respectively.
-% When color expressions are given as string, they are regarded as
-% xcolor's or l3color's expressions (this is the same with shading colors).
-% From v2.30, |scale| option is deprecated and is now a synonym of |scaled|.
+% When the color expressions are given in string type, they are regarded as
+% \pkg{color}, \pkg{xcolor} or \pkg{l3color}'s expressions.
% All from |mplibgraphictext| to the end of sentence will compose an
% anonymous |picture|, which can be drawn or assigned to a variable.
% Incidentally, |withdrawcolor| and |withfillcolor| are synonyms of
% |drawcolor| and |fillcolor|, hopefully to be compatible with |graphictext|.
-% \textsc{n.b.} Because luamplib's current implementation is quite different
-% from the \ConTeXt's, there are some limitations such that you can't
-% apply shading (gradient colors) to the text (But see below).
-% In DVI mode, |unicode-math| package is needed for math formula graphictext,
+%
+% \textsc{n.b.}
+% In some cases, |mplibgraphictext| will produce better results than \ConTeXt\
+% or even than our own |mpliboutlinetext|,
+% especially when processing complicated \TeX\ code
+% such as the vertical writing in Chinese or Japanese.
+% However, because the implementation is quite different from others,
+% there are some limitations such that you can't
+% apply shading (gradient colors) to the text. Again,
+% in DVI mode, \pkg{unicode-math} package is needed for math formula,
% as we cannot embolden type1 fonts in DVI mode.
%
-% \paragraph{\texttt{mplibglyph}, \texttt{mplibdrawglyph}}
-% From v2.30, we provide a new metapost operator |mplibglyph|, which returns a metapost picture
+% \paragraph{\texttt{mplibglyph ... of ...}}
+% From v2.30, we provide a new \metapost operator |mplibglyph|, which returns a \metapost picture
% containing outline paths of a glyph in opentype, truetype or type1 fonts.
-% When a type1 font is specified, metapost primitive |glyph| will be called.
-% \begin{verbatim}
+% When a type1 font is specified, \metapost primitive |glyph| will be called.
+%\begin{verbatim}
% mplibglyph 50 of \fontid\font % slot 50 of current font
% mplibglyph "Q" of "TU/TeXGyrePagella(0)/m/n/10" % font csname
% mplibglyph "Q" of "texgyrepagella-regular.otf" % raw filename
% mplibglyph "Q" of "Times.ttc(2)" % subfont number
% mplibglyph "Q" of "SourceHanSansK-VF.otf[Regular]" % instance name
-% \end{verbatim}
+%\end{verbatim}
% Both arguments before and after of ``|of|'' can be either a number or a string.
% Number arguments are regarded as a glyph slot (GID) and a font id number, repectively.
% String argument at the left side is regarded as a glyph name in the font or a unicode character.
@@ -518,76 +667,108 @@ See source file '\inFileName' for licencing and contact information.
% subfont number (starting from zero) of a TTC font; a string within brackets denotes
% an instance name of a variable font.
%
-% The returned picture will be quite similar to the result of |glyph| primitive in its structure.
-% So, metapost's |draw| command will fill the inner path of the picture with background color.
-% In contrast, |mplibdrawglyph| command fills the paths according to the Nonzero Winding
-% Number Rule. As a result, for instance, the area surrounded by inner path of ``O''
+% \paragraph{\texttt{mplibdrawglyph ...}}
+% The picture returned by |mplibglyph| will be quite similar to the result of |glyph| primitive in its structure.
+% So, \metapost's |draw| command will fill the inner path of the picture with the background color.
+% In contrast, |mplibdrawglyph| \emph{<picture>} command fills the paths according to the nonzero winding
+% number rule. As a result, for instance, the area surrounded by inner path of ``O''
% will remain transparent.
%
-% \paragraph{\texttt{mpliboutlinetext}}
-% From v2.31, we provide a new metapost operator |mpliboutlinetext|, which mimicks
-% metafun's |outlinetext|. So the syntax is the same as metafun's. See the metafun
-% manual \textsection\,8.7 (|texdoc metafun|). A simple example:
-% \begin{verbatim}
+% \leavevmode\llap{\textcolor{red}{☞}\kern1.2\parindent}\relax
+% To apply the nonzero winding number rule to a picture containing paths,
+% luamplib appends |withpostscript| |"collect"|
+% to the paths except the last one in the picture.
+% If you want the even-odd rule instead, you can, with \emph{plain} format as well,
+% additionally declare |withpostscript| |"evenodd"| to the last path in the picture.
+%
+% \paragraph{\texttt{mpliboutlinetext (...)}}
+% \hypertarget{mpliboutlinetext}{}\relax
+% From v2.31, a new \metapost operator |mpliboutlinetext| is available, which mimicks
+% \emph{metafun}'s |outlinetext|. So the syntax is the same: see the \emph{metafun}
+% manual \S\,8.7 (|texdoc metafun|). A simple example:
+%\begin{verbatim}
% draw mpliboutlinetext.b ("$\sqrt{2+\alpha}$")
% (withcolor \mpcolor{red!50})
% (withpen pencircle scaled .2 withcolor red)
% scaled 2 ;
-% \end{verbatim}
-% After the process of |mpliboutlinetext|, |mpliboutlinepic[]|
+%\end{verbatim}
+% After the process, |mpliboutlinepic[]|
% and |mpliboutlinenum| will be preserved as global variables;
-% |mpliboutlinepic[1]| \ldots{} |mpliboutlinepic[mpliboutlinenum]|
+% |mpliboutlinepic[1]| |...| |mpliboutlinepic[mpliboutlinenum]|
% will be an array of images each of which containing a glyph or a rule.
+%
% \textsc{n.b.} As Unicode grapheme cluster is not considered in the array, a unit that must be
% a single cluster might be separated apart.
%
-% \paragraph{\cs{mppattern} \ldots\ \cs{endmppattern}, \texttt{withpattern}}
-% |\mppattern{<name>}| \ldots\ |\endmppattern| defines a tiling pattern
+% \paragraph{\cs{mppattern\{...\} ...} \cs{endmppattern}, \texttt{... withpattern ...}}
+% \hypertarget{mppattern}{}\relax
+% \TeX\ macros
+% \cs{mppattern\{<name>\}} |...| \cs{endmppattern} define a tiling pattern
% associated with the |<name>|.
-% MetaPost operator |withpattern|, the syntax being
-% \textit{path} |withpattern| \textit{string}, will return a metapost picture which fills
-% the given path with a tiling pattern of the |<name>|.
-% \begin{verbatim}
-% \mppattern{mypatt} % or \begin{mppattern}{mypatt}
-% [ % options: see below
-% xstep = 10, ystep = 12,
-% matrix = {0,1,-1,0}, % or "0 1 -1 0"
-% ]
-% \mpfig % or any other TeX code,
-% picture q;
-% q := btex Q etex;
-% fill bbox q withcolor .8[red,white];
-% draw q withcolor .8red;
-% \endmpfig
-% \endmppattern % or \end{mppattern}
+% \metapost operator |withpattern|, the syntax being
+% \emph{<path>}\,\textbar\,\emph{<textual picture>} |withpattern| \emph{<string>},
+% will return a \metapost picture which fills
+% the given path or text with a tiling pattern of the |<name>|
+% by replicating it horizontally and vertically.
+% The \emph{textual picture} here means any text typeset by \TeX, mostly the result
+% of the |btex| command (though technically this is not a true textual picture)
+% or the |infont| operator.
%
-% \mpfig
-% fill fullcircle scaled 100 withpostscript "collect";
-% draw unitsquare shifted - center unitsquare scaled 45
-% withpattern "mypatt"
-% withpostscript "evenodd" ;
-% \endmpfig
-% \end{verbatim}
-% The available options are:
-% \begin{center}
+% An example:
+%\begin{verbatim}
+% \mppattern{mypatt} % or \begin{mppattern}{mypatt}
+% [ % options: see below
+% xstep = 10,
+% ystep = 12,
+% matrix = {0, 1, -1, 0}, % or "0 1 -1 0"
+% ]
+% \mpfig % or any other TeX code,
+% draw (origin--(1,1))
+% scaled 10
+% withcolor 1/3[blue,white]
+% ;
+% draw (up--right)
+% scaled 10
+% withcolor 1/3[red,white]
+% ;
+% \endmpfig
+% \endmppattern % or \end{mppattern}
+%
+% \mpfig
+% draw fullcircle scaled 90
+% withpostscript "collect"
+% ;
+% draw fullcircle scaled 200
+% withpattern "mypatt"
+% withpen pencircle scaled 1
+% withcolor \mpcolor{red!50!blue!50}
+% withpostscript "evenodd"
+% ;
+% \endmpfig
+%\end{verbatim}
+%
+% The available options are listed in Table~\ref{tab:mppatternoptions}.
+% \begin{table}
+% \centering
+% \caption{options for \cs{mppattern}}\label{tab:mppatternoptions}
% \begin{tabular}{lll}\hline
% Key & Value Type & Explanation\\\hline
-% |xstep| &\textit{number} & horizontal spacing between pattern cells\\
-% |ystep| &\textit{number} & vertical spacing between pattern cells\\
-% |xshift| &\textit{number} & horizontal shifting of pattern cells\\
-% |yshift| &\textit{number} & vertical shifting of pattern cells\\
-% |matrix| &\textit{table} or \textit{string} & |xx|, |yx|, |xy|, |yy| values\kern1pt* or MP transform code\\
-% |bbox| &\textit{table} or \textit{string} & |llx|, |lly|, |urx|, |ury| values\kern1pt*\\
-% |resources|&\textit{string} & PDF resources if needed\\
-% |colored| or |coloured| &\textit{boolean}& |false| for uncolored pattern. default: |true|\\\hline
+% |xstep| &\emph{number} & horizontal spacing between pattern cells\\
+% |ystep| &\emph{number} & vertical spacing between pattern cells\\
+% |xshift| &\emph{number} & horizontal shifting of pattern cells\\
+% |yshift| &\emph{number} & vertical shifting of pattern cells\\
+% |bbox| &\emph{table} or \emph{string} & |llx|, |lly|, |urx|, |ury| values\kern1pt*\\
+% |matrix| &\emph{table} or \emph{string} & |xx|, |yx|, |xy|, |yy| values\kern1pt* or MP transform code\\
+% |resources|&\emph{string} & PDF resources if needed\\
+% |colored| or |coloured| &\emph{boolean}& |false| for uncolored pattern. default: |true|\\\hline
% & & \small *\,in string type, numbers are separated by spaces\\
% \end{tabular}
-% \end{center}
+% \end{table}
%
-% For the sake of convenience, width and height values of tiling patterns will be written down
+% For the sake of convenience, the width and height values of tiling patterns will be written down
% into the log file. (depth is always zero.) Users can refer to them for option setting.
%
-% As for |matrix| option, metapost code such as `|rotated 30 slanted .2|' is allowed as well
+% As for |matrix| option, \metapost code such as `|rotated 30 slanted .2|' is allowed as well
% as string or table of four numbers. You can also set |xshift| and |yshift|
% values by using `|shifted|' operator. But when |xshift| or |yshift| option is explicitly
% given, they have precedence over the effect of `|shifted|' operator.
@@ -598,45 +779,257 @@ See source file '\inFileName' for licencing and contact information.
% is not needed in most cases.
%
% Option |colored=false| (|coloured| is a synonym of |colored|) will generate an uncolored pattern which shall have no color at all.
-% Uncolored pattern will be painted later by the color of a metapost object.
+% Uncolored pattern will be painted later by the color of a \metapost object.
% An example:
-% \begin{verbatim}
-% \begin{mppattern}{pattuncolored}
+%\begin{verbatim}
+% \begin{mppattern}{pattnocolor}
% [
% colored = false,
-% matrix = "rotated 30",
+% matrix = "slanted .3 rotated 30",
% ]
% \tiny\TeX
% \end{mppattern}
%
% \begin{mplibcode}
% beginfig(1)
-% picture tex; tex := mpliboutlinetext.p ("\bfseries \TeX");
-% i:=0;
-% for item within tex:
-% i:=i+1;
-% if i < length tex:
-% fill pathpart item scaled 10
-% withpostscript "collect";
-% else:
+% picture tex;
+% tex = mpliboutlinetext.p ("\bfseries \TeX");
+% for i=1 upto mpliboutlinenum:
+% j:=0;
+% for item within mpliboutlinepic[i]:
+% j:=j+1;
% draw pathpart item scaled 10
-% withpattern "pattuncolored"
-% withpen pencircle scaled 0.5
-% withcolor 0.7 blue % paints the pattern
-% ;
-% fi
+% if j < length mpliboutlinepic[i]:
+% withpostscript "collect"
+% else:
+% withpattern "pattnocolor"
+% withpen pencircle scaled 1/2
+% withcolor (i/4)[red,blue] % paints the pattern
+% fi;
+% endfor
% endfor
% endfig;
% \end{mplibcode}
-% \end{verbatim}
+%\end{verbatim}
+% A much simpler and efficient way to obtain a similar result
+% (without colorful characters in this example)
+% is to give a \emph{textual picture} as the operand of |withpattern|:
+%\begin{verbatim}
+% \begin{mplibcode}
+% beginfig(2)
+% picture pic;
+% pic = mplibgraphictext "\bfseries\TeX"
+% fakebold 1/2
+% fillcolor 1/3[red,blue] % paints the pattern
+% drawcolor 2/3[red,blue]
+% scaled 10 ;
+% draw pic withpattern "pattnocolor" ;
+% endfig;
+% \end{mplibcode}
+%\end{verbatim}
+%
+% \paragraph{\texttt{... withfademethod ...}}
+% This is a \metapost operator which makes the color of an object gradiently transparent.
+% The syntax is \emph{<path>}\texttt{\textbar}\emph{<picture>} |withfademethod| \emph{<string>},
+% the latter being either |"linear"| or |"circular"|.
+% Though it is similar to the |withshademethod| from \emph{metafun},
+% the differences are: (1) the operand of |withfademethod| can be a picture as well as a path;
+% (2) you cannot make gradient colors, but can only make gradient opacity.
+%
+% Related macros to control optional values are:
+% \begin{description}
+% \let\bfseries\relax
+% \item [|withfadeopacity (|\emph{number, number}|)|]
+% sets the starting opacity and the ending opacity, default value being |(1,0)|.
+% `|1|' denotes full color; `|0|' full transparency.
+% \item [|withfadevector (|\emph{pair, pair}|)|]
+% sets the starting and ending points. Default value in the linear mode is
+% |(llcorner p, lrcorner p)|, where |p| is the operand,
+% meaning that fading starts from the left edge and ends at the right edge.
+% Default value in the circular mode is |(center p, center p)|, which means
+% centers of both starting and ending circles are the center of the bounding box.
+% \item [|withfadecenter|] is a synonym of |withfadevector|.
+% \item [|withfaderadius (|\emph{number, number}|)|]
+% sets the radii of starting and ending circles. This is no-op in the linear mode.
+% Default value is |(0, abs(center p - urcorner p))|, meaning that fading starts from the
+% center and ends at the four corners of the bounding box.
+% \item [|withfadebbox (|\emph{pair, pair}|)|]
+% sets the bounding box of the fading area, default value being |(llcorner p, urcorner p)|.
+% Though this option is not needed in most cases, there could be cases when users want to
+% explicitly control the bounding box.
+% Particularly, see the description \hyperlink{withgroupbbox}{below}
+% on the analogous macro |withgroupbbox|.
+% \end{description}
+% An example:
+%\begin{verbatim}
+% \mpfig
+% picture mill;
+% mill = btex \includegraphics[width=100bp]{mill} etex;
+% draw mill
+% withfademethod "circular"
+% withfadecenter (center mill, center mill)
+% withfaderadius (20, 50)
+% withfadeopacity (1, 0)
+% ;
+% \endmpfig
+%\end{verbatim}
+%
+% \paragraph{\texttt{... asgroup ...}}
+% \hypertarget{transparencygroup}{}\relax
+% As said \hyperlink{metafunformat}{before},
+% transparency group is available with \emph{plain} as well as \emph{metafun} format.
+% The syntax is exactly the same:
+% \emph{<picture>} \textbar\ \emph{<path>} |asgroup|
+% |""| \textbar\ |"isolated"| \textbar\ |"knockout"| \textbar\ |"isolated,knockout"|,
+% which will return a \metapost picture.
+% It is called \emph{Transparency Group} because the objects contained in the group are composited
+% to produce a single object, so that outer transparency effect, if any,
+% will be applied to the group as a whole, not to the individual objects cumulatively.
+%
+% The additional feature provided by luamplib is that
+% you can reuse the group as many times as you want
+% in the \TeX{} code or in other \metapost code chunks,
+% with infinitesimal increase in the size of PDF file.
+% For this functionality we provide \TeX{} and \metapost macros as follows:
+% \begin{description}
+% \let\bfseries\relax
+% \item[|withgroupname| \emph{<string>}] associates a transparency group with the given name.
+% When this is not appended to the sentence with |asgroup| operator,
+% the default group name `|lastmplibgroup|' will be used.
+%
+% \item[\cs{usemplibgroup\{...\}}]
+% \hypertarget{usemplibgroup}{}\relax
+% is a \TeX\ command to reuse a transparency group of the name
+% once used.
+% Note that the position of the group will be origin-based:
+% in other words, lower-left corner of the group will be shifted to the origin.
+%
+% \item[|usemplibgroup| \emph{<string>}] is a \metapost command which will add
+% a transparency group of the name to the |currentpicture|.
+% Contrary to the \TeX\ command just mentioned,
+% the position of the group is the same as the original transparency group.
+%
+% \item[|withgroupbbox (|\emph{pair}|,|\emph{pair}|)|]
+% \hypertarget{withgroupbbox}{}\relax
+% sets the bounding box of the transparency group,
+% default value being |(llcorner p, urcorner p)|.
+% This option might be needed especially when you draw with a thick pen
+% a path that touches the boundary;
+% you would probably want to append to the sentence
+% `|withgroupbbox| |(bot| |lft| |llcorner| |p,| |top| |rt| |urcorner| |p)|',
+% supposing that the pen was selected by the |pickup| command.
+%
+% \end{description}
+% An example showing the difference between the \TeX\ and \metapost commands:
+%\begin{verbatim}
+% \mpfig
+% draw image(
+% fill fullcircle scaled 100 shifted 25right withcolor blue;
+% fill fullcircle scaled 100 withcolor red ;
+% ) asgroup ""
+% withgroupname "mygroup";
+% draw (left--right) scaled 10;
+% draw (up--down) scaled 10;
+% \endmpfig
+%
+% \noindent
+% \clap{\vrule width 20pt height .25pt depth .25pt}%
+% \clap{\vrule width .5pt height 10pt depth 10pt}%
+% \usemplibgroup{mygroup}
+%
+% \mpfig
+% usemplibgroup "mygroup" rotated 15
+% withprescript "tr_transparency=0.5";
+% draw (left--right) scaled 10;
+% draw (up--down) scaled 10;
+% \endmpfig
+%\end{verbatim}
+%
+% Also note that normally the reused transparency groups are not affected by outer color commands.
+% However, if you have made the original transparency group using |withoutcolor| command,
+% colors will have effects on the uncolored objects in the group.
+%
+% \paragraph{\cs{mplibgroup\{...\} ...} \cs{endmplibgroup}}
+% These \TeX\ macros are described here in this subsection, as they are
+% deeply related to the |asgroup| operator.
+% Users can define a transparency group or a normal \emph{form XObject}
+% with these macros from \TeX\ side.
+% The syntax is similar to the \cs{mppattern} command (see \hyperlink{mppattern}{above}).
+% An example:
+%\begin{verbatim}
+% \mplibgroup{mygrx} % or \begin{mplibgroup}{mygrx}
+% [ % options: see below
+% asgroup="",
+% ]
+% \mpfig % or any other TeX code
+% pickup pencircle scaled 10;
+% draw (left--right) scaled 30 rotated 45 ;
+% draw (left--right) scaled 30 rotated -45 ;
+% \endmpfig
+% \endmplibgroup % or \end{mplibgroup}
+%
+% \usemplibgroup{mygrx}
+%
+% \mpfig
+% usemplibgroup "mygrx" scaled 1.5
+% withprescript "tr_transparency=0.5" ;
+% \endmpfig
+%\end{verbatim}
+%
+% Availabe options, much fewer than those for \cs{mppattern},
+% are listed in Table~\ref{tab:mplibgroupoptions}.
+% Again, the width/height/depth values of the mplibgroup will be written down into the log file.
+% \begin{table}
+% \centering
+% \caption{options for \cs{mplibgroup}}\label{tab:mplibgroupoptions}
+% \begin{tabular}{lll}
+% \hline
+% Key & Value Type & Explanation\\
+% \hline
+% |asgroup| &\emph{string} & |""|, |"isolated"|, |"knockout"|, or |"isolated,knockout"|\\
+% |bbox| &\emph{table} or \emph{string} & |llx|, |lly|, |urx|, |ury| values\kern1pt*\\
+% |matrix| &\emph{table} or \emph{string} & |xx|, |yx|, |xy|, |yy| values\kern1pt*
+% or MP transform code\\
+% |resources| &\emph{string} & PDF resources if needed\\
+% \hline
+% & & \small *\,in string type, numbers are separated by spaces\\
+% \end{tabular}
+% \end{table}
+%
+% When |asgroup| option, including empty string, is not given,
+% a normal form XObject will be generated rather than a transparency group.
+% Thus the individual objects, not the XObject as a whole, will be affected
+% by outer transparency command.
+%
+% As shown in the example, you can reuse the mplibgroup once defined
+% using the \TeX\ command \cs{usemplibgroup} or
+% the \metapost command |usemplibgroup|.
+% The behavior of these commands is the same as that described \hyperlink{usemplibgroup}{above}.
+%
+%
+% \subsection{Lua}
+%
+% \paragraph{\texttt{runscript ...}}
+% Using the primitive |runscript| \emph{<string>}, you can run a Lua code chunk from \metapost side
+% and get some \metapost code returned by Lua if you want.
+% As the functionality is provided by the \mplib library itself,
+% luamplib does not have much to say about it.
+%
+% One thing is worth mentioning, however:
+% if you return a Lua \emph{table} to the \metapost process,
+% it is automatically converted to a relevant \metapost value type
+% such as pair, color, cmykcolor or transform.
+% So users can save some extra toil of converting a table to a string, though it's not a big deal.
+% For instance, |runscript "return {1,0,0}"| will give you the \metapost color expression |(1,0,0)|
+% automatically.
%
% \paragraph{Lua table \texttt{luamplib.instances}}
-% Users can access the Lua table containing mplib instances, |luamplib.instances|,
-% through which metapost variables are also easily accessible
-% as documented in Lua\TeX{} manual \textsection\,11.2.8.4 (|texdoc luatex|).
+% Users can access the Lua table containing \mplib instances, |luamplib.instances|,
+% through which \metapost variables are also easily accessible from Lua side,
+% as documented in Lua\TeX{} manual \S\,11.2.8.4 (|texdoc luatex|).
% The following will print |false|, |3.0|, |MetaPost| and
-% the points and the cyclicity of the path |unitsquare|, consecutively.
-% \begin{verbatim}
+% the knots and the cyclicity of the path |unitsquare|, consecutively.
+%\begin{verbatim}
% \begin{mplibcode}[instance1]
% boolean b; b = 1 > 2;
% numeric n; n = 3;
@@ -646,37 +1039,46 @@ See source file '\inFileName' for licencing and contact information.
%
% \directlua{
% local instance1 = luamplib.instances.instance1
-% print( instance1:get_boolean"b" )
-% print( instance1:get_number"n" )
-% print( instance1:get_string"s" )
-% local t = instance1:get_path"p"
+% print( instance1:get_boolean "b" )
+% print( instance1:get_number "n" )
+% print( instance1:get_string "s" )
+% local t = instance1:get_path "p"
% for k,v in pairs(t) do
% print(k, type(v)=='table' and table.concat(v,' ') or v)
% end
% }
-% \end{verbatim}
-% In this way, it would not be difficult to define a paragraph shape
-% (using \cs{parshape} \TeX\ primitive) which follows an arbitrary metapost path.
-%
-% \paragraph{About figure box metrics}
-% Notice that, after each figure is processed, macro \cs{MPwidth} stores
-% the width value of latest figure; \cs{MPheight}, the height value.
-% Incidentally, also note that \cs{MPllx}, \cs{MPlly}, \cs{MPurx}, and
-% \cs{MPury} store the bounding box information of latest figure
-% without the unit |bp|.
-%
-% \paragraph{luamplib.cfg}
-% At the end of package loading, \textsf{luamplib} searches
-% |luamplib.cfg| and, if found, reads the file in automatically.
-% Frequently used settings such as \cs{everymplib}, \cs{mplibforcehmode}
-% or \cs{mplibcodeinherit} are suitable for going into this file.
-%
-% \bigskip
+%\end{verbatim}
+%
+% \paragraph{Lua function \texttt{luamplib.process\_mplibcode}}
+% Users can execute a \metapost code chunk from Lua side by using this function:
+%\begin{verbatim}
+% luamplib.process_mplibcode (<string> metapost code, <string> instance name)
+%\end{verbatim}
+% The second argument cannot be absent, but can be an empty string (|""|) which
+% means that it has no instance name.
+%
+% Some other elements in the |luamplib| namespace, listed in Table~\ref{tab:elementsinluamplib},
+% can have effects on the process of |process_mplibcode|.
+% \begin{table}
+% \centering
+% \caption{elements in \texttt{luamplib} table (partial)}\label{tab:elementsinluamplib}
+% \begin{tabular}{lll}\hline
+% Key & Type & Related \TeX\ macro \\\hline
+% |codeinherit| & \emph{boolean} & \cs{mplibcodeinherit}\\
+% |everyendmplib| & \emph{table} & \cs{everyendmplib}\\
+% |everymplib| & \emph{table} & \cs{everymplib}\\
+% |getcachedir| & \emph{function} (\emph{<string>}) & \cs{mplibcachedir}\\
+% |globaltextext| & \emph{boolean} & \cs{mplibglobaltextext}\\
+% |legacyverbatimtex| & \emph{boolean} & \cs{mpliblegacybehavior}\\
+% |noneedtoreplace| & \emph{table} & \cs{mplibmakenocache}\\
+% |numbersystem| & \emph{string} & \cs{mplibnumbersystem}\\
+% |setformat| & \emph{function} (\emph{<string>}) & \cs{mplibsetformat}\\
+% |showlog| & \emph{boolean} & \cs{mplibshowlog}\\
+% |textextlabel| & \emph{boolean} & \cs{mplibtextextlabel}\\
+% |verbatiminput| & \emph{boolean} & \cs{mplibverbatim}\\\hline
+% \end{tabular}
+% \end{table}
%
-% There are (basically) two formats for metapost: \emph{plain} and
-% \emph{metafun}. By default, the \emph{plain} format is used, but you can set
-% the format to be used by future figures at any time using
-% \cs{mplibsetformat}\marg{format name}.
%
% \section{Implementation}
%
@@ -690,14 +1092,14 @@ See source file '\inFileName' for licencing and contact information.
luatexbase.provides_module {
name = "luamplib",
- version = "2.32.4",
- date = "2024/07/03",
+ version = "2.35.0",
+ date = "2024/11/12",
description = "Lua package to typeset Metapost with LuaTeX's MPLib.",
}
% \end{macrocode}
%
-% Use the |luamplib| namespace, since |mplib| is for the metapost library
+% Use the |luamplib| namespace, since |mplib| is for the \metapost library
% itself. \ConTeXt{} uses |metapost|.
% \begin{macrocode}
luamplib = luamplib or { }
@@ -733,7 +1135,6 @@ local function termorlog (target, text, kind)
if kind == "Error" then error() end
end
end
-
local function warn (...) -- beware '%' symbol
termorlog("term and log", select("#",...) > 1 and format(...) or ...)
end
@@ -749,33 +1150,23 @@ luamplib.showlog = luamplib.showlog or false
% \end{macrocode}
%
% This module is a stripped down version of libraries that are used by
-% \ConTeXt. Provide a few ``shortcuts'' expected by the imported code.
+% \ConTeXt. Provide a few ``shortcuts'' expected by the code.
% \begin{macrocode}
local tableconcat = table.concat
local tableinsert = table.insert
+local tableunpack = table.unpack
local texsprint = tex.sprint
local texgettoks = tex.gettoks
local texgetbox = tex.getbox
local texruntoks = tex.runtoks
-% \end{macrocode}
-%
-% We don't use |tex.scantoks| anymore. See below reagrding |tex.runtoks|.
-% \begin{verbatim}
-% local texscantoks = tex.scantoks
-% \end{verbatim}
-% \begin{macrocode}
-
if not texruntoks then
err("Your LuaTeX version is too old. Please upgrade it to the latest")
end
-
local is_defined = token.is_defined
local get_macro = token.get_macro
-
local mplib = require ('mplib')
local kpse = require ('kpse')
local lfs = require ('lfs')
-
local lfsattributes = lfs.attributes
local lfsisdir = lfs.isdir
local lfsmkdir = lfs.mkdir
@@ -791,7 +1182,6 @@ local file = file or { }
local replacesuffix = file.replacesuffix or function(filename, suffix)
return (filename:gsub("%.[%a%d]+$","")) .. "." .. suffix
end
-
local is_writable = file.is_writable or function(name)
if lfsisdir(name) then
name = name .. "/_luam_plib_temp_file_"
@@ -813,14 +1203,11 @@ end
% \end{macrocode}
%
% |btex ... etex| in input |.mp| files will be replaced in finder.
-% Because of the limitation of MPLib regarding |make_text|,
+% Because of the limitation of \mplib regarding |make_text|,
% we might have to make cache files modified from input files.
% \begin{macrocode}
-local luamplibtime = kpse.find_file("luamplib.lua")
-luamplibtime = luamplibtime and lfsattributes(luamplibtime,"modification")
-
+local luamplibtime = lfsattributes(kpse.find_file"luamplib.lua", "modification")
local currenttime = os.time()
-
local outputdir, cachedir
if lfstouch then
for i,v in ipairs{'TEXMFVAR','TEXMF_OUTPUT_DIRECTORY','.','TEXMFOUTPUT'} do
@@ -857,10 +1244,9 @@ function luamplib.getcachedir(dir)
end
end
end
-
% \end{macrocode}
%
-% Some basic MetaPost files not necessary to make cache files.
+% Some basic \metapost files not necessary to make cache files.
% \begin{macrocode}
local noneedtoreplace = {
["boxes.mp"] = true, -- ["format.mp"] = true,
@@ -879,7 +1265,6 @@ local noneedtoreplace = {
["mp-tool.mpiv"] = true, ["mp-cont.mpiv"] = true,
}
luamplib.noneedtoreplace = noneedtoreplace
-
% \end{macrocode}
%
% |format.mp| is much complicated, so specially treated.
@@ -901,7 +1286,6 @@ local function replaceformatmp(file,newfile,ofmodify)
lfstouch(newfile,currenttime,ofmodify)
return newfile
end
-
% \end{macrocode}
%
% Replace |btex ... etex| and |verbatimtex ... etex| in input files,
@@ -911,7 +1295,6 @@ local name_b = "%f[%a_]"
local name_e = "%f[^%a_]"
local btex_etex = name_b.."btex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e
local verbatimtex_etex = name_b.."verbatimtex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e
-
local function replaceinputmpfile (name,file)
local ofmodify = lfsattributes(file,"modification")
if not ofmodify then return file end
@@ -924,23 +1307,19 @@ local function replaceinputmpfile (name,file)
return nf.size == 0 and file or newfile
end
end
-
if name == "format.mp" then return replaceformatmp(file,newfile,ofmodify) end
-
local fh = ioopen(file,"r")
if not fh then return file end
local data = fh:read("*all"); fh:close()
-
% \end{macrocode}
-% ``|etex|'' must be followed by a space or semicolon as specified in
-% \LuaTeX\ manual, which is not the case of standalone MetaPost though.
+% ``|etex|'' must be preceded by a space and followed by a space or semicolon as specified in
+% \LuaTeX\ manual, which is not the case of standalone \metapost though.
% \begin{macrocode}
local count,cnt = 0,0
data, cnt = data:gsub(btex_etex, "btex %1 etex ") -- space
count = count + cnt
data, cnt = data:gsub(verbatimtex_etex, "verbatimtex %1 etex;") -- semicolon
count = count + cnt
-
if count == 0 then
noneedtoreplace[name] = true
fh = ioopen(newfile,"w");
@@ -950,7 +1329,6 @@ local function replaceinputmpfile (name,file)
end
return file
end
-
fh = ioopen(newfile,"w")
if not fh then return file end
fh:write(data); fh:close()
@@ -960,8 +1338,8 @@ end
% \end{macrocode}
%
-% As the finder function for MPLib, use the |kpse| library and
-% make it behave like as if MetaPost was used. And replace it with
+% As the finder function for \mplib, use the |kpse| library and
+% make it behave like as if \metapost was used. And replace |.mp| files with
% cache files if needed.
% See also \#74, \#97.
% \begin{macrocode}
@@ -973,12 +1351,10 @@ do
end
mpkpse = kpse.new(arg[exe], "mpost")
end
-
local special_ftype = {
pfb = "type1 fonts",
enc = "enc files",
}
-
function luamplib.finder (name, mode, ftype)
if mode == "w" then
if name and name ~= "mpout.log" then
@@ -1004,9 +1380,9 @@ end
% \end{macrocode}
%
-% Create and load MPLib instances.
-% We do not support ancient version of MPLib any more.
-% (Don't know which version of MPLib started to support
+% Create and load \mplib instances.
+% We do not support ancient version of \mplib any more.
+% (Don't know which version of \mplib started to support
% |make_text| and |run_script|; let the users find it.)
% \begin{macrocode}
local preamble = [[
@@ -1015,17 +1391,15 @@ local preamble = [[
let normalfontsize = fontsize;
input %s ;
]]
-
% \end{macrocode}
%
-% |plain| or |metafun|,
-% though we cannot support |metafun| format fully.
+% \emph{plain} or \emph{metafun},
+% though we cannot support \emph{metafun} format fully.
% \begin{macrocode}
local currentformat = "plain"
function luamplib.setformat (name)
currentformat = name
end
-
% \end{macrocode}
%
% v2.9 has introduced the concept of ``code inherit''
@@ -1034,7 +1408,6 @@ luamplib.codeinherit = false
local mplibinstances = {}
luamplib.instances = mplibinstances
local has_instancename = false
-
local function reporterror (result, prevlog)
if not result then
err("no result object returned")
@@ -1061,7 +1434,7 @@ local function reporterror (result, prevlog)
% \end{macrocode}
% v2.6.1: now luamplib does not disregard |show| command,
% even when |luamplib.showlog| is false. Incidentally,
-% it does not raise error but just prints an info,
+% it does not raise error nor prints an info,
% even if output has no figure.
% \begin{macrocode}
local show = log:match"\n>>? .+"
@@ -1075,11 +1448,10 @@ local function reporterror (result, prevlog)
return log
end
end
-
% \end{macrocode}
%
% |lualibs-os.lua| installs a randomseed. When this file is not loaded,
-% we should explicitly seed a unique interger to get random randomseed for each run.
+% we should explicitly seed a unique integer to get random randomseed for each run.
% \begin{macrocode}
if not math.initialseed then math.randomseed(currenttime) end
local function luamplibload (name)
@@ -1088,10 +1460,8 @@ local function luamplibload (name)
find_file = luamplib.finder,
% \end{macrocode}
% Make use of |make_text| and |run_script|, which will co-operate
-% with \LuaTeX's |tex.runtoks|. And we
-% provide |numbersystem| option since v2.4. Default value ``|scaled|''
-% can be changed by declaring |\mplibnumbersystem{double}|
-% or |\mplibnumbersystem{decimal}|.
+% with \LuaTeX's |tex.runtoks| or other Lua functions. And we
+% provide |numbersystem| option since v2.4.
% See \url{https://github.com/lualatex/luamplib/issues/21}.
% \begin{macrocode}
make_text = luamplib.maketext,
@@ -1102,12 +1472,12 @@ local function luamplibload (name)
extensions = 1,
}
% \end{macrocode}
-% Append our own MetaPost preamble to the preamble above.
+% Append our own \metapost preamble to the preamble above.
% \begin{macrocode}
local preamble = tableconcat{
format(preamble, replacesuffix(name,"mp")),
luamplib.preambles.mplibcode,
- luamplib.legacy_verbatimtex and luamplib.preambles.legacyverbatimtex or "",
+ luamplib.legacyverbatimtex and luamplib.preambles.legacyverbatimtex or "",
luamplib.textextlabel and luamplib.preambles.textextlabel or "",
}
local result, log
@@ -1119,22 +1489,12 @@ local function luamplibload (name)
log = reporterror(result)
return mpx, result, log
end
-
% \end{macrocode}
%
% Here, excute each |mplibcode| data,
% ie |\begin{mplibcode} ... \end{mplibcode}|.
% \begin{macrocode}
local function process (data, instancename)
-% \end{macrocode}
-% The workaround of issue \#70 seems to be unnecessary, as we use
-% |make_text| now.
-% \begin{verbatim}
-% if not data:find(name_b.."beginfig%s*%([%+%-%s]*%d[%.%d%s]*%)") then
-% data = data .. "beginfig(-1);endfig;"
-% end
-% \end{verbatim}
-% \begin{macrocode}
local currfmt
if instancename and instancename ~= "" then
currfmt = instancename
@@ -1144,7 +1504,7 @@ local function process (data, instancename)
currentformat,
luamplib.numbersystem or "scaled",
tostring(luamplib.textextlabel),
- tostring(luamplib.legacy_verbatimtex),
+ tostring(luamplib.legacyverbatimtex),
}
has_instancename = false
end
@@ -1178,40 +1538,30 @@ end
% |dvipdfmx| is supported, though nobody seems to use it.
% \begin{macrocode}
local pdfmode = tex.outputmode > 0
+
% \end{macrocode}
%
-% |make_text| and some |run_script| uses \LuaTeX's |tex.runtoks|,
-% which made possible running \TeX\ code snippets inside |\directlua|.
+% |make_text| and some |run_script| uses \LuaTeX's |tex.runtoks|.
% \begin{macrocode}
local catlatex = luatexbase.registernumber("catcodetable@latex")
local catat11 = luatexbase.registernumber("catcodetable@atletter")
-
% \end{macrocode}
%
% |tex.scantoks| sometimes fail to read catcode properly, especially
% |\#|, |\&|, or |\%|. After some experiment, we dropped using it.
-% Instead, a function containing |tex.script| seems to work nicely.
-% \begin{verbatim}
-% local function run_tex_code_no_use (str, cat)
-% cat = cat or catlatex
-% texscantoks("mplibtmptoks", cat, str)
-% texruntoks("mplibtmptoks")
-% end
-% \end{verbatim}
+% Instead, a function containing |tex.sprint| seems to work nicely.
% \begin{macrocode}
local function run_tex_code (str, cat)
texruntoks(function() texsprint(cat or catlatex, str) end)
end
-
% \end{macrocode}
%
-% Prepare textext box number containers,
-% locals, globals and possibly instances.
+% Prepare textext box number containers, locals and globals.
% |localid| can be any number. They are local anyway.
% The number will be reset at the start of a new code chunk.
% Global boxes will use |\newbox| command in |tex.runtoks| process.
-% This is the same when |codeinherit| is declared as true.
-% Boxes of an instance will also be global, so that
+% This is the same when |codeinherit| is true.
+% Boxes in instances with name will also be global, so that
% their tex boxes can be shared among instances of the same name.
% \begin{macrocode}
local texboxes = { globalid = 0, localid = 4096 }
@@ -1219,11 +1569,9 @@ local texboxes = { globalid = 0, localid = 4096 }
% For conversion of |sp| to |bp|.
% \begin{macrocode}
local factor = 65536*(7227/7200)
-
local textext_fmt = 'image(addto currentpicture doublepath unitsquare \z
-xscaled %f yscaled %f shifted (0,-%f) \z
-withprescript "mplibtexboxid=%i:%f:%f")'
-
+ xscaled %f yscaled %f shifted (0,-%f) \z
+ withprescript "mplibtexboxid=%i:%f:%f")'
local function process_tex_text (str)
if str then
local global = (has_instancename or luamplib.globaltextext or luamplib.codeinherit)
@@ -1238,7 +1586,7 @@ local function process_tex_text (str)
run_tex_code(format([[\expandafter\newbox\csname luamplib.box.%s\endcsname]], boxid))
tex_box_id = tex.getcount'allocationnumber'
end
- run_tex_code(format("%s\\setbox%i\\hbox{%s}", global, tex_box_id, str))
+ run_tex_code(format("\\luamplibtagtextbegin{%i}%s\\setbox%i\\hbox{%s}\\luamplibtagtextend", tex_box_id, global, tex_box_id, str))
local box = texgetbox(tex_box_id)
local wd = box.width / factor
local ht = box.height / factor
@@ -1253,7 +1601,6 @@ end
% Make |color| or |xcolor|'s color expressions usable,
% with \cs{mpcolor} or |mplibcolor|. These commands should be used
% with graphical objects.
-%
% Attempt to support l3color as well.
% \begin{macrocode}
local mplibcolorfmt = {
@@ -1269,7 +1616,6 @@ local mplibcolorfmt = {
[[\color_select:n%s\endgroup]],
},
}
-
local colfmt = is_defined'color_select:n' and "l3color" or "xcolor"
if colfmt == "l3color" then
run_tex_code{
@@ -1283,7 +1629,6 @@ if colfmt == "l3color" then
}
end
local ccexplat = luatexbase.registernumber"luamplibcctabexplat"
-
local function process_color (str)
if str then
if not str:find("%b{}") then
@@ -1344,20 +1689,18 @@ end
%
% For legacy verbatimtex process.
% |verbatimtex ... etex| before |beginfig()| is not ignored,
-% but the \TeX\ code is inserted just before the mplib box. And
-% \TeX\ code inside |beginfig() ... endfig| is inserted after the mplib box.
+% but the \TeX\ code is inserted just before the \mplib box. And
+% \TeX\ code inside |beginfig() ... endfig| is inserted after the \mplib box.
% \begin{macrocode}
local tex_code_pre_mplib = {}
luamplib.figid = 1
luamplib.in_the_fig = false
-
local function process_verbatimtex_prefig (str)
if str then
tex_code_pre_mplib[luamplib.figid] = str
end
return ""
end
-
local function process_verbatimtex_infig (str)
if str then
return format('special "postmplibverbtex=%s";', str)
@@ -1376,17 +1719,16 @@ local runscript_funcs = {
% \end{macrocode}
%
-% For |metafun| format. see issue \#79.
+% For \emph{metafun} format. see issue \#79.
% \begin{macrocode}
mp = mp or {}
local mp = mp
mp.mf_path_reset = mp.mf_path_reset or function() end
mp.mf_finish_saving_data = mp.mf_finish_saving_data or function() end
mp.report = mp.report or info
-
% \end{macrocode}
%
-% metafun 2021-03-09 changes crashes luamplib.
+% \emph{metafun} 2021-03-09 changes crashes luamplib.
% \begin{macrocode}
catcodes = catcodes or {}
local catcodes = catcodes
@@ -1420,7 +1762,6 @@ local function mpprint(buffer,...)
end
end
end
-
function luamplib.runscript (code)
local id, str = code:match("(.-){(.*)}")
if id and str then
@@ -1442,7 +1783,7 @@ function luamplib.runscript (code)
return buffer
end
buffer = {}
- mpprint(buffer, table.unpack(res))
+ mpprint(buffer, tableunpack(res))
return tableconcat(buffer)
end
return ""
@@ -1459,9 +1800,7 @@ local function protecttexcontents (str)
:gsub("%zPerCent%z", "\\%%")
:gsub("%s+", " ")
end
-
-luamplib.legacy_verbatimtex = true
-
+luamplib.legacyverbatimtex = true
function luamplib.maketext (str, what)
if str and str ~= "" then
str = protecttexcontents(str)
@@ -1470,7 +1809,7 @@ function luamplib.maketext (str, what)
not str:find("\\begin%s*{document}") and
not str:find("\\documentstyle"..name_e) and
not str:find("\\usepackage"..name_e) then
- if luamplib.legacy_verbatimtex then
+ if luamplib.legacyverbatimtex then
if luamplib.in_the_fig then
return process_verbatimtex_infig(str)
else
@@ -1489,13 +1828,13 @@ end
% \end{macrocode}
%
-% luamplib's metapost color operators
+% luamplib's \metapost color operators
% \begin{macrocode}
local function colorsplit (res)
- local t, tt = { }, res:gsub("[%[%]]",""):explode()
+ local t, tt = { }, res:gsub("[%[%]]","",2):explode()
local be = tt[1]:find"^%d" and 1 or 2
for i=be, #tt do
- if tt[i]:find"^%a" then break end
+ if not tonumber(tt[i]) then break end
t[#t+1] = tt[i]
end
return t
@@ -1529,7 +1868,7 @@ luamplib.shadecolor = function (str)
if res:find" cs " or res:find"@pdf.obj" then -- spot color shade: l3 only
% \end{macrocode}
% An example of spot color shading:
-% \begin{verbatim}
+%\begin{verbatim}
% \documentclass{article}
% \usepackage{luamplib}
% \mplibsetformat{metafun}
@@ -1575,29 +1914,64 @@ luamplib.shadecolor = function (str)
% endfig;
% \end{mplibcode}
% \end{document}
-% \end{verbatim}
+%\end{verbatim}
+% another one: user-defined DeviceN colorspace
+%\begin{verbatim}
+% \DocumentMetadata{ }
+% \documentclass{article}
+% \usepackage{luamplib}
+% \mplibsetformat{metafun}
+% \ExplSyntaxOn
+% \color_model_new:nnn { pantone1215 }
+% { Separation }
+% { name = PANTONE~1215~U ,
+% alternative-model = cmyk ,
+% alternative-values = {0, 0.15, 0.51, 0}
+% }
+% \color_model_new:nnn { pantone+black }
+% { DeviceN }
+% {
+% names = {pantone1215,black}
+% }
+% \color_set:nnn{purepantone}{pantone+black}{1,0}
+% \color_set:nnn{pureblack} {pantone+black}{0,1}
+% \ExplSyntaxOff
+% \begin{document}
+% \mpfig
+% fill unitsquare xscaled \mpdim{\textwidth} yscaled 30
+% withshademethod "linear"
+% withshadecolors ("purepantone","pureblack")
+% ;
+% \endmpfig
+% \end{document}
+%\end{verbatim}
% \begin{macrocode}
run_tex_code({
[[\color_export:nnN{]], str, [[}{backend}\mplib_@tempa]],
},ccexplat)
- local name = get_macro'mplib_@tempa':match'{(.-)}{.+}'
+ local name, value = get_macro'mplib_@tempa':match'{(.-)}{(.-)}'
local t, obj = res:explode()
if pdfmode then
obj = format("%s 0 R", ltx.pdf.object_id( t[1]:sub(2,-1) ))
else
obj = t[2]
end
- local value = t[3]:match"%[(.-)%]" or t[3]
- return format('(%s) withprescript"mplib_spotcolor=%s:%s"', value,obj,name)
+ return format('(1) withprescript"mplib_spotcolor=%s:%s:%s"', value,obj,name)
end
return colorsplit(res)
end
% \end{macrocode}
%
+% Remove trailing zeros for smaller PDF
+% \begin{macrocode}
+local decimals = "%.%d+"
+local function rmzeros(str) return str:gsub("%.?0+$","") end
+
+% \end{macrocode}
+%
% luamplib's mplibgraphictext operator
% \begin{macrocode}
-local running = -1073741824
local emboldenfonts = { }
local function getemboldenwidth (curr, fakebold)
local width = emboldenfonts.width
@@ -1630,13 +2004,14 @@ local function getrulewhatsit (line, wd, ht, dp)
fmt = "pdf:content "..fmt
pl = node.new("whatsit","special")
end
- pl.data = fmt:format(line, 0, -dp, wd, ht+dp, "B")
+ pl.data = fmt:format(line, 0, -dp, wd, ht+dp, "B") :gsub(decimals,rmzeros)
local ss = node.new"glue"
node.setglue(ss, 0, 65536, 65536, 2, 2)
pl.next = ss
return pl
end
local function getrulemetric (box, curr, bp)
+ local running = -1073741824
local wd,ht,dp = curr.width, curr.height, curr.depth
wd = wd == running and box.width or wd
ht = ht == running and box.height or ht
@@ -1688,7 +2063,8 @@ local function embolden (box, curr, fakebold)
head, curr = node.remove(head, curr)
elseif curr.id == node.id"glyph" and curr.font > 0 then
local f = curr.font
- local i = emboldenfonts[f]
+ local key = format("%s:%s",f,fakebold)
+ local i = emboldenfonts[key]
if not i then
local ft = font.getfont(f) or font.getcopy(f)
if pdfmode then
@@ -1704,7 +2080,7 @@ local function embolden (box, curr, fakebold)
name = format('%s;embolden=%s;',name,fakebold)
_, i = fonts.constructors.readanddefine(name,ft.size)
end
- emboldenfonts[f] = i
+ emboldenfonts[key] = i
end
curr.font = i
end
@@ -1716,6 +2092,9 @@ end
local function graphictextcolor (col, filldraw)
if col:find"^[%d%.:]+$" then
col = col:explode":"
+ for i=1,#col do
+ col[i] = format("%.3f", col[i])
+ end
if pdfmode then
local op = #col == 4 and "k" or #col == 3 and "rg" or "g"
col[#col+1] = filldraw == "fill" and op or op:upper()
@@ -1913,7 +2292,7 @@ end
% mpliboutlinetext : based on mkiv's font-mps.lua
% \begin{macrocode}
local rulefmt = "mpliboutlinepic[%i]:=image(addto currentpicture contour \z
-unitsquare shifted - center unitsquare;) xscaled %f yscaled %f shifted (%f,%f);"
+ unitsquare shifted - center unitsquare;) xscaled %f yscaled %f shifted (%f,%f);"
local outline_horz, outline_vert
function outline_vert (res, box, curr, xshift, yshift)
local b2u = box.dir == "LTL"
@@ -2154,7 +2533,7 @@ end
% \end{macrocode}
%
-% Our MetaPost preambles
+% Our \metapost preambles
% \begin{macrocode}
luamplib.preambles = {
mplibcode = [[
@@ -2339,7 +2718,13 @@ vardef mpliboutlinetext@# (expr t) text rest =
) mplib_do_outline_options_r; )
enddef ;
primarydef t withpattern p =
- image( fill t withprescript "mplibpattern=" & if numeric p: decimal fi p; )
+ image(
+ if cycle t:
+ fill
+ else:
+ draw
+ fi
+ t withprescript "mplibpattern=" & if numeric p: decimal fi p; )
enddef;
vardef mplibtransformmatrix (text e) =
save t; transform t;
@@ -2353,6 +2738,75 @@ vardef mplibtransformmatrix (text e) =
& decimal ypart t & ","
& "}");
enddef;
+primarydef p withfademethod s =
+ if picture p:
+ image(
+ draw p;
+ draw center p withprescript "mplibfadestate=stop";
+ )
+ else:
+ p withprescript "mplibfadestate=stop"
+ fi
+ withprescript "mplibfadetype=" & s
+ withprescript "mplibfadebbox=" &
+ decimal (xpart llcorner p -1/4) & ":" &
+ decimal (ypart llcorner p -1/4) & ":" &
+ decimal (xpart urcorner p +1/4) & ":" &
+ decimal (ypart urcorner p +1/4)
+enddef;
+def withfadeopacity (expr a,b) =
+ withprescript "mplibfadeopacity=" &
+ decimal a & ":" &
+ decimal b
+enddef;
+def withfadevector (expr a,b) =
+ withprescript "mplibfadevector=" &
+ decimal xpart a & ":" &
+ decimal ypart a & ":" &
+ decimal xpart b & ":" &
+ decimal ypart b
+enddef;
+let withfadecenter = withfadevector;
+def withfaderadius (expr a,b) =
+ withprescript "mplibfaderadius=" &
+ decimal a & ":" &
+ decimal b
+enddef;
+def withfadebbox (expr a,b) =
+ withprescript "mplibfadebbox=" &
+ decimal xpart a & ":" &
+ decimal ypart a & ":" &
+ decimal xpart b & ":" &
+ decimal ypart b
+enddef;
+primarydef p asgroup s =
+ image(
+ draw center p
+ withprescript "mplibgroupbbox=" &
+ decimal (xpart llcorner p -1/4) & ":" &
+ decimal (ypart llcorner p -1/4) & ":" &
+ decimal (xpart urcorner p +1/4) & ":" &
+ decimal (ypart urcorner p +1/4)
+ withprescript "gr_state=start"
+ withprescript "gr_type=" & s;
+ draw p;
+ draw center p withprescript "gr_state=stop";
+ )
+enddef;
+def withgroupbbox (expr a,b) =
+ withprescript "mplibgroupbbox=" &
+ decimal xpart a & ":" &
+ decimal ypart a & ":" &
+ decimal xpart b & ":" &
+ decimal ypart b
+enddef;
+def withgroupname expr s =
+ withprescript "mplibgroupname=" & s
+enddef;
+def usemplibgroup primary s =
+ draw maketext("\mplibnoforcehmode\csname luamplib.group." & s & "\endcsname")
+ shifted runscript("return luamplib.trgroupshifts['" & s & "']")
+enddef;
]],
legacyverbatimtex = [[
def specialVerbatimTeX (text t) = runscript("luamplibprefig{"&t&"}") enddef;
@@ -2366,7 +2820,25 @@ extra_endfig := extra_endfig & " let VerbatimTeX = specialVerbatimTeX;"&
"luamplib.in_the_fig=false" &ditto& ");";
]],
textextlabel = [[
-primarydef s infont f = rawtextext(s) enddef;
+let luampliboriginalinfont = infont;
+primarydef s infont f =
+ if (s < char 32)
+ or (s = char 35) % #
+ or (s = char 36) % $
+ or (s = char 37) % %
+ or (s = char 38) % &
+ or (s = char 92) % \
+ or (s = char 94) % ^
+ or (s = char 95) % _
+ or (s = char 123) % {
+ or (s = char 125) % }
+ or (s = char 126) % ~
+ or (s = char 127) :
+ s luampliboriginalinfont f
+ else :
+ rawtextext(s)
+ fi
+enddef;
def fontsize expr f =
begingroup
save size; numeric size;
@@ -2382,7 +2854,6 @@ enddef;
% When \cs{mplibverbatim} is enabled, do not expand |mplibcode| data.
% \begin{macrocode}
luamplib.verbatiminput = false
-
% \end{macrocode}
%
% Do not expand |btex ... etex|, |verbatimtex ... etex|, and
@@ -2398,7 +2869,6 @@ local function protect_expansion (str)
return format("\\unexpanded{%s}",str)
end
end
-
local function unprotect_expansion (str)
if str then
return str:gsub("!!!Control!!!", "\\")
@@ -2408,25 +2878,20 @@ local function unprotect_expansion (str)
:gsub("!!!RBrace!!!", "}")
end
end
-
luamplib.everymplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end })
luamplib.everyendmplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end })
-
function luamplib.process_mplibcode (data, instancename)
texboxes.localid = 4096
-
% \end{macrocode}
% This is needed for legacy behavior
% \begin{macrocode}
- if luamplib.legacy_verbatimtex then
+ if luamplib.legacyverbatimtex then
luamplib.figid, tex_code_pre_mplib = 1, {}
end
-
local everymplib = luamplib.everymplib[instancename]
local everyendmplib = luamplib.everyendmplib[instancename]
data = format("\n%s\n%s\n%s\n",everymplib, data, everyendmplib)
:gsub("\r","\n")
-
% \end{macrocode}
% These five lines are needed for |mplibverbatim| mode.
% \begin{macrocode}
@@ -2466,7 +2931,6 @@ function luamplib.process_mplibcode (data, instancename)
return format("verbatimtex %s etex", unprotect_expansion(str))
end)
end
-
process(data, instancename)
end
@@ -2474,21 +2938,12 @@ end
%
% For parsing |prescript| materials.
% \begin{macrocode}
-local further_split_keys = {
- mplibtexboxid = true,
- sh_color_a = true,
- sh_color_b = true,
-}
local function script2table(s)
local t = {}
for _,i in ipairs(s:explode("\13+")) do
local k,v = i:match("(.-)=(.*)") -- v may contain = or empty.
if k and v and k ~= "" and not t[k] then
- if further_split_keys[k] or further_split_keys[k:sub(1,10)] then
- t[k] = v:explode(":")
- else
- t[k] = v
- end
+ t[k] = v
end
end
return t
@@ -2496,144 +2951,34 @@ end
% \end{macrocode}
%
-% Codes below for inserting PDF lieterals are mostly from ConTeXt general,
-% with small changes when needed.
+% pdfliterals will be stored in |figcontents| table, and written to pdf in one go
+% at the end of the flushing figure.
+% Subtable |post| is for the legacy behavior.
% \begin{macrocode}
-local function getobjects(result,figure,f)
- return figure:objects()
-end
-
-function luamplib.convert (result, flusher)
- luamplib.flush(result, flusher)
- return true -- done
-end
-
local figcontents = { post = { } }
local function put2output(a,...)
figcontents[#figcontents+1] = type(a) == "string" and format(a,...) or a
end
-
local function pdf_startfigure(n,llx,lly,urx,ury)
put2output("\\mplibstarttoPDF{%f}{%f}{%f}{%f}",llx,lly,urx,ury)
end
-
local function pdf_stopfigure()
put2output("\\mplibstoptoPDF")
end
-
% \end{macrocode}
%
% |tex.sprint| with catcode regime -2, as sometimes |#| gets doubled
% in the argument of pdfliteral.
% \begin{macrocode}
-local function pdf_literalcode (fmt,...)
- put2output{-2, format(fmt,...)}
-end
-
-local function pdf_textfigure(font,size,text,width,height,depth)
- text = text:gsub(".",function(c)
- return format("\\hbox{\\char%i}",string.byte(c)) -- kerning happens in metapost : false
- end)
- put2output("\\mplibtextext{%s}{%f}{%s}{%s}{%s}",font,size,text,0,0)
-end
-
-local bend_tolerance = 131/65536
-
-local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1
-
-local function pen_characteristics(object)
- local t = mplib.pen_info(object)
- rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty
- divider = sx*sy - rx*ry
- return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width
-end
-
-local function concat(px, py) -- no tx, ty here
- return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider
-end
-
-local function curved(ith,pth)
- local d = pth.left_x - ith.right_x
- if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then
- d = pth.left_y - ith.right_y
- if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then
- return false
- end
- end
- return true
-end
-
-local function flushnormalpath(path,open)
- local pth, ith
- for i=1,#path do
- pth = path[i]
- if not ith then
- pdf_literalcode("%f %f m",pth.x_coord,pth.y_coord)
- elseif curved(ith,pth) then
- pdf_literalcode("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
- else
- pdf_literalcode("%f %f l",pth.x_coord,pth.y_coord)
- end
- ith = pth
- end
- if not open then
- local one = path[1]
- if curved(pth,one) then
- pdf_literalcode("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord )
- else
- pdf_literalcode("%f %f l",one.x_coord,one.y_coord)
- end
- elseif #path == 1 then -- special case .. draw point
- local one = path[1]
- pdf_literalcode("%f %f l",one.x_coord,one.y_coord)
- end
-end
-
-local function flushconcatpath(path,open)
- pdf_literalcode("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty)
- local pth, ith
- for i=1,#path do
- pth = path[i]
- if not ith then
- pdf_literalcode("%f %f m",concat(pth.x_coord,pth.y_coord))
- elseif curved(ith,pth) then
- local a, b = concat(ith.right_x,ith.right_y)
- local c, d = concat(pth.left_x,pth.left_y)
- pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(pth.x_coord, pth.y_coord))
- else
- pdf_literalcode("%f %f l",concat(pth.x_coord, pth.y_coord))
- end
- ith = pth
- end
- if not open then
- local one = path[1]
- if curved(pth,one) then
- local a, b = concat(pth.right_x,pth.right_y)
- local c, d = concat(one.left_x,one.left_y)
- pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord))
- else
- pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord))
- end
- elseif #path == 1 then -- special case .. draw point
- local one = path[1]
- pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord))
- end
-end
-
-local function start_pdf_code()
- if pdfmode then
- pdf_literalcode("q")
- else
- put2output"\\special{pdf:bcontent}"
- end
-end
-local function stop_pdf_code()
- if pdfmode then
- pdf_literalcode("Q")
- else
- put2output"\\special{pdf:econtent}"
- end
+local function pdf_literalcode (...)
+ put2output{ -2, format(...) :gsub(decimals,rmzeros) }
end
+local start_pdf_code = pdfmode
+ and function() pdf_literalcode"q" end
+ or function() put2output"\\special{pdf:bcontent}" end
+local stop_pdf_code = pdfmode
+ and function() pdf_literalcode"Q" end
+ or function() put2output"\\special{pdf:econtent}" end
% \end{macrocode}
%
@@ -2641,7 +2986,7 @@ end
% |textext(...)| or |TEX(...)|, all being the same internally.
% \begin{macrocode}
local function put_tex_boxes (object,prescript)
- local box = prescript.mplibtexboxid
+ local box = prescript.mplibtexboxid:explode":"
local n,tw,th = box[1],tonumber(box[2]),tonumber(box[3])
if n and tw and th then
local op = object.path
@@ -2705,23 +3050,32 @@ local pdfobjs, pdfetcs = {}, {}
pdfetcs.pgfextgs = "pgf@sys@addpdfresource@extgs@plain"
pdfetcs.pgfpattern = "pgf@sys@addpdfresource@patterns@plain"
pdfetcs.pgfcolorspace = "pgf@sys@addpdfresource@colorspaces@plain"
-
-local function update_pdfobjs (os)
- local on = pdfobjs[os]
+local function update_pdfobjs (os, stream)
+ local key = os
+ if stream then key = key..stream end
+ local on = pdfobjs[key]
if on then
return on,false
end
if pdfmode then
- on = pdf.immediateobj(os)
+ if stream then
+ on = pdf.immediateobj("stream",stream,os)
+ else
+ on = pdf.immediateobj(os)
+ end
else
on = pdfetcs.cnt or 1
- texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os))
+ if stream then
+ texsprint(format("\\special{pdf:stream @mplibpdfobj%s (%s) <<%s>>}",on,stream,os))
+ else
+ texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os))
+ end
pdfetcs.cnt = on + 1
end
- pdfobjs[os] = on
+ pdfobjs[key] = on
return on,true
end
-
+pdfetcs.resfmt = pdfmode and "%s 0 R" or "@mplibpdfobj%s"
if pdfmode then
pdfetcs.getpageres = pdf.getpageresources or function() return pdf.pageresources end
local getpageres = pdfetcs.getpageres
@@ -2757,11 +3111,20 @@ if pdfmode then
end
else
texsprint {
+ "\\luamplibatfirstshipout{",
"\\special{pdf:obj @MPlibTr<<>>}",
"\\special{pdf:obj @MPlibSh<<>>}",
"\\special{pdf:obj @MPlibCS<<>>}",
- "\\special{pdf:obj @MPlibPt<<>>}",
+ "\\special{pdf:obj @MPlibPt<<>>}}",
}
+ pdfetcs.resadded = { }
+ pdfetcs.fallback_update_resources = function (name,res,obj)
+ texsprint{"\\special{pdf:put ", obj, " <<", res, ">>}"}
+ if not pdfetcs.resadded[name] then
+ texsprint{"\\luamplibateveryshipout{\\special{pdf:put @resources <</", name, " ", obj, ">>}}"}
+ pdfetcs.resadded[name] = obj
+ end
+ end
end
% \end{macrocode}
@@ -2775,13 +3138,10 @@ local transparancy_modes = { [0] = "Normal",
"Hue", "Saturation", "Color", "Luminosity",
"Compatible",
}
-
-local function update_tr_res(mode,opaq)
- local os = format("<</BM /%s/ca %.3f/CA %.3f/AIS false>>",mode,opaq,opaq)
- local on, new = update_pdfobjs(os)
+local function add_extgs_resources (on, new)
+ local key = format("MPlibTr%s", on)
if new then
- local key = format("MPlibTr%s", on)
- local val = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s", on)
+ local val = format(pdfetcs.resfmt, on)
if pdfmanagement then
texsprint {
"\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ExtGState}{", key, "}{", val, "}"
@@ -2790,103 +3150,102 @@ local function update_tr_res(mode,opaq)
local tr = format("/%s %s", key, val)
if is_defined(pdfetcs.pgfextgs) then
texsprint { "\\csname ", pdfetcs.pgfextgs, "\\endcsname{", tr, "}" }
- elseif pdfmode then
- if is_defined"TRP@list" then
- texsprint(catat11,{
- [[\if@filesw\immediate\write\@auxout{]],
- [[\string\g@addto@macro\string\TRP@list{]],
- tr,
- [[}}\fi]],
- })
- if not get_macro"TRP@list":find(tr) then
- texsprint(catat11,[[\global\TRP@reruntrue]])
- end
- else
- pdfetcs.fallback_update_resources("ExtGState", tr)
+ elseif is_defined"TRP@list" then
+ texsprint(catat11,{
+ [[\if@filesw\immediate\write\@auxout{]],
+ [[\string\g@addto@macro\string\TRP@list{]],
+ tr,
+ [[}}\fi]],
+ })
+ if not get_macro"TRP@list":find(tr) then
+ texsprint(catat11,[[\global\TRP@reruntrue]])
end
else
- texsprint { "\\special{pdf:put @MPlibTr<<", tr, ">>}" }
+ pdfetcs.fallback_update_resources("ExtGState",tr,"@MPlibTr")
end
end
end
- if not pdfmode and not pdfmanagement and not is_defined(pdfetcs.pgfextgs) then
- texsprint"\\special{pdf:put @resources <</ExtGState @MPlibTr>>}"
- end
- return on
+ return key
end
-
local function do_preobj_TR(object,prescript)
if object.postscript == "collect" then return end
local opaq = prescript and prescript.tr_transparency
- local tron_no
if opaq then
+ local key, on, os, new
local mode = prescript.tr_alternative or 1
- mode = transparancy_modes[tonumber(mode)]
- tron_no = update_tr_res(mode, opaq)
- start_pdf_code()
- pdf_literalcode("/MPlibTr%i gs",tron_no)
+ mode = transparancy_modes[tonumber(mode)] or mode
+ opaq = format("%.3f", opaq) :gsub(decimals,rmzeros)
+ for i,v in ipairs{ {mode,opaq},{"Normal",1} } do
+ os = format("<</BM/%s/ca %s/CA %s/AIS false>>",v[1],v[2],v[2])
+ on, new = update_pdfobjs(os)
+ key = add_extgs_resources(on,new)
+ if i == 1 then
+ pdf_literalcode("/%s gs",key)
+ else
+ return format("/%s gs",key)
+ end
+ end
end
- return tron_no
end
% \end{macrocode}
%
-% Shading with |metafun| format.
+% Shading with \emph{metafun} format.
% \begin{macrocode}
local function sh_pdfpageresources(shtype,domain,colorspace,ca,cb,coordinates,steps,fractions)
- local fun2fmt,os = "<</FunctionType 2/Domain [%s]/C0 [%s]/C1 [%s]/N 1>>"
+ for _,v in ipairs{ca,cb} do
+ for i,vv in ipairs(v) do
+ for ii,vvv in ipairs(vv) do
+ v[i][ii] = tonumber(vvv) and format("%.3f",vvv) or vvv
+ end
+ end
+ end
+ local fun2fmt,os = "<</FunctionType 2/Domain[%s]/C0[%s]/C1[%s]/N 1>>"
if steps > 1 then
local list,bounds,encode = { },{ },{ }
for i=1,steps do
if i < steps then
- bounds[i] = fractions[i] or 1
+ bounds[i] = format("%.3f", fractions[i] or 1)
end
encode[2*i-1] = 0
encode[2*i] = 1
os = fun2fmt:format(domain,tableconcat(ca[i],' '),tableconcat(cb[i],' '))
- list[i] = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s",update_pdfobjs(os))
+ :gsub(decimals,rmzeros)
+ list[i] = format(pdfetcs.resfmt, update_pdfobjs(os))
end
os = tableconcat {
"<</FunctionType 3",
- format("/Bounds [%s]", tableconcat(bounds,' ')),
- format("/Encode [%s]", tableconcat(encode,' ')),
- format("/Functions [%s]", tableconcat(list, ' ')),
- format("/Domain [%s]>>", domain),
- }
+ format("/Bounds[%s]", tableconcat(bounds,' ')),
+ format("/Encode[%s]", tableconcat(encode,' ')),
+ format("/Functions[%s]", tableconcat(list, ' ')),
+ format("/Domain[%s]>>", domain),
+ } :gsub(decimals,rmzeros)
else
os = fun2fmt:format(domain,tableconcat(ca[1],' '),tableconcat(cb[1],' '))
+ :gsub(decimals,rmzeros)
end
- local objref = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s",update_pdfobjs(os))
+ local objref = format(pdfetcs.resfmt, update_pdfobjs(os))
os = tableconcat {
format("<</ShadingType %i", shtype),
format("/ColorSpace %s", colorspace),
format("/Function %s", objref),
- format("/Coords [%s]", coordinates),
- "/Extend [true true]/AntiAlias true>>",
- }
+ format("/Coords[%s]", coordinates),
+ "/Extend[true true]/AntiAlias true>>",
+ } :gsub(decimals,rmzeros)
local on, new = update_pdfobjs(os)
if new then
- local key = format("MPlibSh%s", on)
- local val = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s", on)
+ local key, val = format("MPlibSh%s", on), format(pdfetcs.resfmt, on)
if pdfmanagement then
texsprint {
"\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/Shading}{", key, "}{", val, "}"
}
else
local res = format("/%s %s", key, val)
- if pdfmode then
- pdfetcs.fallback_update_resources("Shading", res)
- else
- texsprint { "\\special{pdf:put @MPlibSh<<", res, ">>}" }
- end
+ pdfetcs.fallback_update_resources("Shading",res,"@MPlibSh")
end
end
- if not pdfmode and not pdfmanagement then
- texsprint"\\special{pdf:put @resources <</Shading @MPlibSh>>}"
- end
return on
end
-
local function color_normalize(ca,cb)
if #cb == 1 then
if #ca == 4 then
@@ -2898,7 +3257,6 @@ local function color_normalize(ca,cb)
cb[1], cb[2], cb[3], cb[4] = 1-cb[1], 1-cb[2], 1-cb[3], 0
end
end
-
pdfetcs.clrspcs = setmetatable({ }, { __index = function(t,names)
run_tex_code({
[[\color_model_new:nnn]],
@@ -2910,7 +3268,6 @@ pdfetcs.clrspcs = setmetatable({ }, { __index = function(t,names)
t[names] = colorspace
return colorspace
end })
-
local function do_preobj_SH(object,prescript)
local shade_no
local sh_type = prescript and prescript.sh_type
@@ -2918,14 +3275,14 @@ local function do_preobj_SH(object,prescript)
return
else
local domain = prescript.sh_domain or "0 1"
- local centera = prescript.sh_center_a or "0 0"; centera = centera:explode()
- local centerb = prescript.sh_center_b or "0 0"; centerb = centerb:explode()
+ local centera = (prescript.sh_center_a or "0 0"):explode()
+ local centerb = (prescript.sh_center_b or "0 0"):explode()
local transform = prescript.sh_transform == "yes"
local sx,sy,sr,dx,dy = 1,1,1,0,0
if transform then
- local first = prescript.sh_first or "0 0"; first = first:explode()
- local setx = prescript.sh_set_x or "0 0"; setx = setx:explode()
- local sety = prescript.sh_set_y or "0 0"; sety = sety:explode()
+ local first = (prescript.sh_first or "0 0"):explode()
+ local setx = (prescript.sh_set_x or "0 0"):explode()
+ local sety = (prescript.sh_set_y or "0 0"):explode()
local x,y = tonumber(setx[1]) or 0, tonumber(sety[1]) or 0
if x ~= 0 and y ~= 0 then
local path = object.path
@@ -2947,15 +3304,15 @@ local function do_preobj_SH(object,prescript)
end
end
local ca, cb, colorspace, steps, fractions
- ca = { prescript.sh_color_a_1 or prescript.sh_color_a or {0} }
- cb = { prescript.sh_color_b_1 or prescript.sh_color_b or {1} }
+ ca = { (prescript.sh_color_a_1 or prescript.sh_color_a or "0"):explode":" }
+ cb = { (prescript.sh_color_b_1 or prescript.sh_color_b or "1"):explode":" }
steps = tonumber(prescript.sh_step) or 1
if steps > 1 then
fractions = { prescript.sh_fraction_1 or 0 }
for i=2,steps do
fractions[i] = prescript[format("sh_fraction_%i",i)] or (i/steps)
- ca[i] = prescript[format("sh_color_a_%i",i)] or {0}
- cb[i] = prescript[format("sh_color_b_%i",i)] or {1}
+ ca[i] = (prescript[format("sh_color_a_%i",i)] or "0"):explode":"
+ cb[i] = (prescript[format("sh_color_b_%i",i)] or "1"):explode":"
end
end
if prescript.mplib_spotcolor then
@@ -2964,15 +3321,14 @@ local function do_preobj_SH(object,prescript)
local script = object.prescript:explode"\13+"
for i=#script,1,-1 do
if script[i]:find"mplib_spotcolor" then
- local name, value
- objref, name = script[i]:match"=(.-):(.+)"
- value = script[i+1]:match"=(.+)"
+ local t, name, value = script[i]:explode"="[2]:explode":"
+ value, objref, name = t[1], t[2], t[3]
if not names[name] then
pos = pos+1
names[name] = pos
names[#names+1] = name
end
- local t = { }
+ t = { }
for j=1,names[name] do t[#t+1] = 0 end
t[#t+1] = value
tableinsert(#ca == #cb and ca or cb, t)
@@ -3023,7 +3379,6 @@ local function do_preobj_SH(object,prescript)
else
err"unknown shading type"
end
- pdf_literalcode("q /Pattern cs")
end
return shade_no
end
@@ -3033,43 +3388,15 @@ end
% Patterns
% \begin{macrocode}
pdfetcs.patterns = { }
-local patterns = pdfetcs.patterns
-function luamplib.registerpattern ( boxid, name, opts )
- local box = texgetbox(boxid)
- local wd = format("%.3f",box.width/factor)
- local hd = format("%.3f",(box.height+box.depth)/factor)
- info("w/h/d of '%s': %s %s 0.0", name, wd, hd)
- if opts.xstep == 0 then opts.xstep = nil end
- if opts.ystep == 0 then opts.ystep = nil end
- if opts.colored == nil then
- opts.colored = opts.coloured
- if opts.colored == nil then
- opts.colored = true
- end
+local function gather_resources (optres)
+ local t, do_pattern = { }, not optres
+ local names = {"ExtGState","ColorSpace","Shading"}
+ if do_pattern then
+ names[#names+1] = "Pattern"
end
- if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end
- if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end
- if opts.matrix and opts.matrix:find"%a" then
- local data = format("mplibtransformmatrix(%s);",opts.matrix)
- process(data,"@mplibtransformmatrix")
- local t = luamplib.transformmatrix
- opts.matrix = format("%s %s %s %s", t[1], t[2], t[3], t[4])
- opts.xshift = opts.xshift or t[5]
- opts.yshift = opts.yshift or t[6]
- end
- local attr = {
- "/Type/Pattern",
- "/PatternType 1",
- format("/PaintType %i", opts.colored and 1 or 2),
- "/TilingType 2",
- format("/XStep %s", opts.xstep or wd),
- format("/YStep %s", opts.ystep or hd),
- format("/Matrix [%s %s %s]", opts.matrix or "1 0 0 1", opts.xshift or 0, opts.yshift or 0),
- }
if pdfmode then
- local optres, t = opts.resources or "", { }
if pdfmanagement then
- for _,v in ipairs{"ExtGState","ColorSpace","Shading"} do
+ for _,v in ipairs(names) do
local pp = get_macro(format("g__pdfdict_/g__pdf_Core/Page/Resources/%s_prop",v))
if pp and pp:find"__prop_pair" then
t[#t+1] = format("/%s %s 0 R", v, ltx.pdf.object_id("__pdf/Page/Resources/"..v))
@@ -3078,26 +3405,19 @@ function luamplib.registerpattern ( boxid, name, opts )
else
local res = pdfetcs.getpageres() or ""
run_tex_code[[\mplibtmptoks\expandafter{\the\pdfvariable pageresources}]]
- res = (res .. texgettoks'mplibtmptoks'):explode()
- res = tableconcat(res," "):explode"/+"
+ res = res .. texgettoks'mplibtmptoks'
+ if do_pattern then return res end
+ res = res:explode"/+"
for _,v in ipairs(res) do
+ v = v:match"^%s*(.-)%s*$"
if not v:find"Pattern" and not optres:find(v) then
t[#t+1] = "/" .. v
end
end
end
- optres = optres .. tableconcat(t)
- if opts.bbox then
- attr[#attr+1] = format("/BBox [%s]", opts.bbox)
- end
- local index = tex.saveboxresource(boxid, tableconcat(attr), optres, true, opts.bbox and 4 or 1)
- patterns[name] = { id = index, colored = opts.colored }
else
- local objname = "@mplibpattern"..name
- local metric = format("bbox %s", opts.bbox or format("0 0 %s %s",wd,hd))
- local optres, t = opts.resources or "", { }
if pdfmanagement then
- for _,v in ipairs{"ExtGState","ColorSpace","Shading"} do
+ for _,v in ipairs(names) do
local pp = get_macro(format("g__pdfdict_/g__pdf_Core/Page/Resources/%s_prop",v))
if pp and pp:find"__prop_pair" then
run_tex_code {
@@ -3113,31 +3433,87 @@ function luamplib.registerpattern ( boxid, name, opts )
"\\mplibtmptoks\\expanded{{",
"\\ifpgf@sys@pdf@extgs@exists /ExtGState @pgfextgs\\fi",
"\\ifpgf@sys@pdf@colorspaces@exists /ColorSpace @pgfcolorspaces\\fi",
+ do_pattern and "\\ifpgf@sys@pdf@patterns@exists /Pattern @pgfpatterns \\fi" or "",
"}}",
}, catat11)
t[#t+1] = texgettoks'mplibtmptoks'
+ else
+ for _,v in ipairs(names) do
+ local vv = pdfetcs.resadded[v]
+ if vv then
+ t[#t+1] = format("/%s %s", v, vv)
+ end
+ end
+ end
+ end
+ return tableconcat(t)
+end
+function luamplib.registerpattern ( boxid, name, opts )
+ local box = texgetbox(boxid)
+ local wd = format("%.3f",box.width/factor)
+ local hd = format("%.3f",(box.height+box.depth)/factor)
+ info("w/h/d of pattern '%s': %s 0", name, format("%s %s",wd, hd):gsub(decimals,rmzeros))
+ if opts.xstep == 0 then opts.xstep = nil end
+ if opts.ystep == 0 then opts.ystep = nil end
+ if opts.colored == nil then
+ opts.colored = opts.coloured
+ if opts.colored == nil then
+ opts.colored = true
end
- optres = optres .. tableconcat(t)
+ end
+ if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end
+ if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end
+ if opts.matrix and opts.matrix:find"%a" then
+ local data = format("mplibtransformmatrix(%s);",opts.matrix)
+ process(data,"@mplibtransformmatrix")
+ local t = luamplib.transformmatrix
+ opts.matrix = format("%f %f %f %f", t[1], t[2], t[3], t[4])
+ opts.xshift = opts.xshift or format("%f",t[5])
+ opts.yshift = opts.yshift or format("%f",t[6])
+ end
+ local attr = {
+ "/Type/Pattern",
+ "/PatternType 1",
+ format("/PaintType %i", opts.colored and 1 or 2),
+ "/TilingType 2",
+ format("/XStep %s", opts.xstep or wd),
+ format("/YStep %s", opts.ystep or hd),
+ format("/Matrix[%s %s %s]", opts.matrix or "1 0 0 1", opts.xshift or 0, opts.yshift or 0),
+ }
+ local optres = opts.resources or ""
+ optres = optres .. gather_resources(optres)
+ local patterns = pdfetcs.patterns
+ if pdfmode then
+ if opts.bbox then
+ attr[#attr+1] = format("/BBox[%s]", opts.bbox)
+ end
+ attr = tableconcat(attr) :gsub(decimals,rmzeros)
+ local index = tex.saveboxresource(boxid, attr, optres, true, opts.bbox and 4 or 1)
+ patterns[name] = { id = index, colored = opts.colored }
+ else
+ local cnt = #patterns + 1
+ local objname = "@mplibpattern" .. cnt
+ local metric = format("bbox %s", opts.bbox or format("0 0 %s %s",wd,hd))
texsprint {
- [[\ifvmode\nointerlineskip\fi]],
- format([[\hbox to0pt{\vbox to0pt{\hsize=\wd %i\vss\noindent]], boxid), -- force horiz mode?
- [[\special{pdf:bcontent}]],
- [[\special{pdf:bxobj ]], objname, format(" %s}", metric),
- format([[\raise\dp %i\box %i]], boxid, boxid),
- format([[\special{pdf:put @resources <<%s>>}]], optres),
- [[\special{pdf:exobj <<]], tableconcat(attr), ">>}",
- [[\special{pdf:econtent}]],
- [[\par}\hss}]],
+ "\\expandafter\\newbox\\csname luamplib.patternbox.", cnt, "\\endcsname",
+ "\\global\\setbox\\csname luamplib.patternbox.", cnt, "\\endcsname",
+ "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{",
+ "\\special{pdf:bcontent}",
+ "\\special{pdf:bxobj ", objname, " ", metric, "}",
+ "\\raise\\dp\\csname luamplib.patternbox.", cnt, "\\endcsname",
+ "\\box\\csname luamplib.patternbox.", cnt, "\\endcsname",
+ "\\special{pdf:put @resources <<", optres, ">>}",
+ "\\special{pdf:exobj <<", tableconcat(attr), ">>}",
+ "\\special{pdf:econtent}}",
}
- patterns[#patterns+1] = objname
- patterns[name] = { id = #patterns, colored = opts.colored }
+ patterns[cnt] = objname
+ patterns[name] = { id = cnt, colored = opts.colored }
end
end
local function pattern_colorspace (cs)
local on, new = update_pdfobjs(format("[/Pattern %s]", cs))
if new then
- local key = format("MPlibCS%i",on)
- local val = pdfmode and format("%i 0 R",on) or format("@mplibpdfobj%i",on)
+ local key, val = format("MPlibCS%i",on), format(pdfetcs.resfmt,on)
if pdfmanagement then
texsprint {
"\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ColorSpace}{", key, "}{", val, "}"
@@ -3146,21 +3522,17 @@ local function pattern_colorspace (cs)
local res = format("/%s %s", key, val)
if is_defined(pdfetcs.pgfcolorspace) then
texsprint { "\\csname ", pdfetcs.pgfcolorspace, "\\endcsname{", res, "}" }
- elseif pdfmode then
- pdfetcs.fallback_update_resources("ColorSpace", res)
else
- texsprint { "\\special{pdf:put @MPlibCS<<", res, ">>}" }
+ pdfetcs.fallback_update_resources("ColorSpace",res,"@MPlibCS")
end
end
end
- if not pdfmode and not pdfmanagement and not is_defined(pdfetcs.pgfcolorspace) then
- texsprint"\\special{pdf:put @resources <</ColorSpace @MPlibCS>>}"
- end
return on
end
local function do_preobj_PAT(object, prescript)
local name = prescript and prescript.mplibpattern
if not name then return end
+ local patterns = pdfetcs.patterns
local patt = patterns[name]
local index = patt and patt.id or err("cannot get pattern object '%s'", name)
local key = format("MPlibPt%s",index)
@@ -3200,21 +3572,327 @@ local function do_preobj_PAT(object, prescript)
local res = format("/%s %s", key, val)
if is_defined(pdfetcs.pgfpattern) then
texsprint { "\\csname ", pdfetcs.pgfpattern, "\\endcsname{", res, "}" }
- elseif pdfmode then
- pdfetcs.fallback_update_resources("Pattern", res)
else
- texsprint { "\\special{pdf:put @MPlibPt<<", res, ">>}" }
+ pdfetcs.fallback_update_resources("Pattern",res,"@MPlibPt")
end
end
end
- if not pdfmode and not pdfmanagement and not is_defined(pdfetcs.pgfpattern) then
- texsprint"\\special{pdf:put @resources <</Pattern @MPlibPt>>}"
- end
patt.done = true
end
% \end{macrocode}
%
+% Fading
+% \begin{macrocode}
+pdfetcs.fading = { }
+local function do_preobj_FADE (object, prescript)
+ local fd_type = prescript and prescript.mplibfadetype
+ local fd_stop = prescript and prescript.mplibfadestate
+ if not fd_type then
+ return fd_stop -- returns "stop" (if picture) or nil
+ end
+ local bbox = prescript.mplibfadebbox:explode":"
+ local dx, dy = -bbox[1], -bbox[2]
+ local vec = prescript.mplibfadevector; vec = vec and vec:explode":"
+ if not vec then
+ if fd_type == "linear" then
+ vec = {bbox[1], bbox[2], bbox[3], bbox[2]} -- left to right
+ else
+ local centerx, centery = (bbox[1]+bbox[3])/2, (bbox[2]+bbox[4])/2
+ vec = {centerx, centery, centerx, centery} -- center for both circles
+ end
+ end
+ local coords = { vec[1]+dx, vec[2]+dy, vec[3]+dx, vec[4]+dy }
+ if fd_type == "linear" then
+ coords = format("%f %f %f %f", tableunpack(coords))
+ elseif fd_type == "circular" then
+ local width, height = bbox[3]-bbox[1], bbox[4]-bbox[2]
+ local radius = (prescript.mplibfaderadius or "0:"..math.sqrt(width^2+height^2)/2):explode":"
+ tableinsert(coords, 3, radius[1])
+ tableinsert(coords, radius[2])
+ coords = format("%f %f %f %f %f %f", tableunpack(coords))
+ else
+ err("unknown fading method '%s'", fd_type)
+ end
+ fd_type = fd_type == "linear" and 2 or 3
+ local opaq = (prescript.mplibfadeopacity or "1:0"):explode":"
+ local on, os, new
+ on = sh_pdfpageresources(fd_type, "0 1", "/DeviceGray", {{opaq[1]}}, {{opaq[2]}}, coords, 1)
+ os = format("<</PatternType 2/Shading %s>>", format(pdfetcs.resfmt, on))
+ on = update_pdfobjs(os)
+ bbox = format("0 0 %f %f", bbox[3]+dx, bbox[4]+dy)
+ local streamtext = format("q /Pattern cs/MPlibFd%s scn %s re f Q", on, bbox)
+ :gsub(decimals,rmzeros)
+ os = format("<</Pattern<</MPlibFd%s %s>>>>", on, format(pdfetcs.resfmt, on))
+ on = update_pdfobjs(os)
+ local resources = format(pdfetcs.resfmt, on)
+ on = update_pdfobjs"<</S/Transparency/CS/DeviceGray>>"
+ local attr = tableconcat{
+ "/Subtype/Form",
+ "/BBox[", bbox, "]",
+ "/Matrix[1 0 0 1 ", format("%f %f", -dx,-dy), "]",
+ "/Resources ", resources,
+ "/Group ", format(pdfetcs.resfmt, on),
+ } :gsub(decimals,rmzeros)
+ on = update_pdfobjs(attr, streamtext)
+ os = "<</SMask<</S/Luminosity/G " .. format(pdfetcs.resfmt, on) .. ">>>>"
+ on, new = update_pdfobjs(os)
+ local key = add_extgs_resources(on,new)
+ start_pdf_code()
+ pdf_literalcode("/%s gs", key)
+ if fd_stop then return "standalone" end
+ return "start"
+end
+
+% \end{macrocode}
+%
+% Transparency Group
+% \begin{macrocode}
+pdfetcs.tr_group = { shifts = { } }
+luamplib.trgroupshifts = pdfetcs.tr_group.shifts
+local function do_preobj_GRP (object, prescript)
+ local grstate = prescript and prescript.gr_state
+ if not grstate then return end
+ local trgroup = pdfetcs.tr_group
+ if grstate == "start" then
+ trgroup.name = prescript.mplibgroupname or "lastmplibgroup"
+ trgroup.isolated, trgroup.knockout = false, false
+ for _,v in ipairs(prescript.gr_type:explode",+") do
+ trgroup[v] = true
+ end
+ trgroup.bbox = prescript.mplibgroupbbox:explode":"
+ put2output[[\begingroup\setbox\mplibscratchbox\hbox\bgroup]]
+ elseif grstate == "stop" then
+ local llx,lly,urx,ury = tableunpack(trgroup.bbox)
+ put2output(tableconcat{
+ "\\egroup",
+ format("\\wd\\mplibscratchbox %fbp", urx-llx),
+ format("\\ht\\mplibscratchbox %fbp", ury-lly),
+ "\\dp\\mplibscratchbox 0pt",
+ })
+ local grattr = format("/Group<</S/Transparency/I %s/K %s>>",trgroup.isolated,trgroup.knockout)
+ local res = gather_resources()
+ local bbox = format("%f %f %f %f", llx,lly,urx,ury) :gsub(decimals,rmzeros)
+ if pdfmode then
+ put2output(tableconcat{
+ "\\saveboxresource type 2 attr{/Type/XObject/Subtype/Form/FormType 1",
+ "/BBox[", bbox, "]", grattr, "} resources{", res, "}\\mplibscratchbox",
+ [[\setbox\mplibscratchbox\hbox{\useboxresource\lastsavedboxresourceindex}]],
+ [[\wd\mplibscratchbox 0pt\ht\mplibscratchbox 0pt\dp\mplibscratchbox 0pt]],
+ [[\box\mplibscratchbox\endgroup]],
+ "\\expandafter\\xdef\\csname luamplib.group.", trgroup.name, "\\endcsname{",
+ "\\noexpand\\mplibstarttoPDF{",llx,"}{",lly,"}{",urx,"}{",ury,"}",
+ "\\useboxresource \\the\\lastsavedboxresourceindex\\noexpand\\mplibstoptoPDF}",
+ })
+ else
+ trgroup.cnt = (trgroup.cnt or 0) + 1
+ local objname = format("@mplibtrgr%s", trgroup.cnt)
+ put2output(tableconcat{
+ "\\special{pdf:bxobj ", objname, " bbox ", bbox, "}",
+ "\\unhbox\\mplibscratchbox",
+ "\\special{pdf:put @resources <<", res, ">>}",
+ "\\special{pdf:exobj <<", grattr, ">>}",
+ "\\special{pdf:uxobj ", objname, "}\\endgroup",
+ })
+ token.set_macro("luamplib.group."..trgroup.name, tableconcat{
+ "\\mplibstarttoPDF{",llx,"}{",lly,"}{",urx,"}{",ury,"}",
+ "\\special{pdf:uxobj ", objname, "}\\mplibstoptoPDF",
+ }, "global")
+ end
+ trgroup.shifts[trgroup.name] = { llx, lly }
+ end
+ return grstate
+end
+function luamplib.registergroup (boxid, name, opts)
+ local box = texgetbox(boxid)
+ local wd, ht, dp = node.getwhd(box)
+ local res = (opts.resources or "") .. gather_resources()
+ local attr = { "/Type/XObject/Subtype/Form/FormType 1" }
+ if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end
+ if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end
+ if opts.matrix and opts.matrix:find"%a" then
+ local data = format("mplibtransformmatrix(%s);",opts.matrix)
+ process(data,"@mplibtransformmatrix")
+ opts.matrix = format("%f %f %f %f %f %f",tableunpack(luamplib.transformmatrix))
+ end
+ local grtype = 3
+ if opts.bbox then
+ attr[#attr+1] = format("/BBox[%s]", opts.bbox)
+ grtype = 2
+ end
+ if opts.matrix then
+ attr[#attr+1] = format("/Matrix[%s]", opts.matrix)
+ grtype = opts.bbox and 4 or 1
+ end
+ if opts.asgroup then
+ local t = { isolated = false, knockout = false }
+ for _,v in ipairs(opts.asgroup:explode",+") do t[v] = true end
+ attr[#attr+1] = format("/Group<</S/Transparency/I %s/K %s>>", t.isolated, t.knockout)
+ end
+ local trgroup = pdfetcs.tr_group
+ trgroup.shifts[name] = { get_macro'MPllx', get_macro'MPlly' }
+ local whd
+ local tagpdf = is_defined'picture@tag@bbox@attribute'
+ if pdfmode then
+ attr = tableconcat(attr) :gsub(decimals,rmzeros)
+ local index = tex.saveboxresource(boxid, attr, res, true, grtype)
+ token.set_macro("luamplib.group."..name, tableconcat{
+ "\\prependtomplibbox\\hbox\\bgroup",
+ tagpdf and "\\luamplibtaggingbegin\\setbox\\mplibscratchbox\\hbox\\bgroup" or "",
+ "\\useboxresource ", index,
+ tagpdf and "\\egroup\\luamplibtaggingBBox\\unhbox\\mplibscratchbox\\luamplibtaggingend" or "",
+ "\\egroup",
+ }, "global")
+ whd = format("%.3f %.3f 0", wd/factor, (ht+dp)/factor) :gsub(decimals,rmzeros)
+ else
+ trgroup.cnt = (trgroup.cnt or 0) + 1
+ local objname = format("@mplibtrgr%s", trgroup.cnt)
+ texsprint {
+ "\\expandafter\\newbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname",
+ "\\global\\setbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname",
+ "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{",
+ "\\special{pdf:bcontent}",
+ "\\special{pdf:bxobj ", objname, " width ", wd, "sp height ", ht, "sp depth ", dp, "sp}",
+ "\\unhbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname",
+ "\\special{pdf:put @resources <<", res, ">>}",
+ "\\special{pdf:exobj <<", tableconcat(attr), ">>}",
+ "\\special{pdf:econtent}}",
+ }
+ token.set_macro("luamplib.group."..name, tableconcat{
+ "\\prependtomplibbox\\hbox\\bgroup",
+ tagpdf and "\\luamplibtaggingbegin" or "",
+ "\\setbox\\mplibscratchbox\\hbox{\\special{pdf:uxobj ", objname, "}}",
+ "\\wd\\mplibscratchbox ", wd, "sp",
+ "\\ht\\mplibscratchbox ", ht, "sp",
+ "\\dp\\mplibscratchbox ", dp, "sp",
+ tagpdf and "\\luamplibtaggingBBox" or "",
+ "\\box\\mplibscratchbox",
+ tagpdf and "\\luamplibtaggingend" or "",
+ "\\egroup",
+ }, "global")
+ whd = format("%.3f %.3f %.3f", wd/factor, ht/factor, dp/factor) :gsub(decimals,rmzeros)
+ end
+ info("w/h/d of group '%s': %s", name, whd)
+end
+
+local function stop_special_effects(fade,opaq,over)
+ if fade then -- fading
+ stop_pdf_code()
+ end
+ if opaq then -- opacity
+ pdf_literalcode(opaq)
+ end
+ if over then -- color
+ put2output"\\special{pdf:ec}"
+ end
+end
+
+% \end{macrocode}
+%
+% Codes below for inserting PDF lieterals are mostly from ConTeXt general,
+% with small changes when needed.
+% \begin{macrocode}
+local function getobjects(result,figure,f)
+ return figure:objects()
+end
+
+function luamplib.convert (result, flusher)
+ luamplib.flush(result, flusher)
+ return true -- done
+end
+
+local function pdf_textfigure(font,size,text,width,height,depth)
+ text = text:gsub(".",function(c)
+ return format("\\hbox{\\char%i}",string.byte(c)) -- kerning happens in metapost : false
+ end)
+ put2output("\\mplibtextext{%s}{%f}{%s}{%s}{%s}",font,size,text,0,0)
+end
+
+local bend_tolerance = 131/65536
+
+local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1
+
+local function pen_characteristics(object)
+ local t = mplib.pen_info(object)
+ rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty
+ divider = sx*sy - rx*ry
+ return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width
+end
+
+local function concat(px, py) -- no tx, ty here
+ return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider
+end
+
+local function curved(ith,pth)
+ local d = pth.left_x - ith.right_x
+ if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then
+ d = pth.left_y - ith.right_y
+ if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then
+ return false
+ end
+ end
+ return true
+end
+
+local function flushnormalpath(path,open)
+ local pth, ith
+ for i=1,#path do
+ pth = path[i]
+ if not ith then
+ pdf_literalcode("%f %f m",pth.x_coord,pth.y_coord)
+ elseif curved(ith,pth) then
+ pdf_literalcode("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
+ else
+ pdf_literalcode("%f %f l",pth.x_coord,pth.y_coord)
+ end
+ ith = pth
+ end
+ if not open then
+ local one = path[1]
+ if curved(pth,one) then
+ pdf_literalcode("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord )
+ else
+ pdf_literalcode("%f %f l",one.x_coord,one.y_coord)
+ end
+ elseif #path == 1 then -- special case .. draw point
+ local one = path[1]
+ pdf_literalcode("%f %f l",one.x_coord,one.y_coord)
+ end
+end
+
+local function flushconcatpath(path,open)
+ pdf_literalcode("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty)
+ local pth, ith
+ for i=1,#path do
+ pth = path[i]
+ if not ith then
+ pdf_literalcode("%f %f m",concat(pth.x_coord,pth.y_coord))
+ elseif curved(ith,pth) then
+ local a, b = concat(ith.right_x,ith.right_y)
+ local c, d = concat(pth.left_x,pth.left_y)
+ pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(pth.x_coord, pth.y_coord))
+ else
+ pdf_literalcode("%f %f l",concat(pth.x_coord, pth.y_coord))
+ end
+ ith = pth
+ end
+ if not open then
+ local one = path[1]
+ if curved(pth,one) then
+ local a, b = concat(pth.right_x,pth.right_y)
+ local c, d = concat(one.left_x,one.left_y)
+ pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord))
+ else
+ pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord))
+ end
+ elseif #path == 1 then -- special case .. draw point
+ local one = path[1]
+ pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord))
+ end
+end
+
+% \end{macrocode}
+%
% Finally, flush figures by inserting PDF literals.
% \begin{macrocode}
function luamplib.flush (result,flusher)
@@ -3234,11 +3912,11 @@ function luamplib.flush (result,flusher)
% luamplib silently ignores this invalid figure for those
% that do not contain |beginfig ... endfig|. (issue \#70)
% Original code of ConTeXt general was:
-% \begin{verbatim}
+%\begin{verbatim}
% -- invalid
% pdf_startfigure(fignum,0,0,0,0)
% pdf_stopfigure()
-% \end{verbatim}
+%\end{verbatim}
% \begin{macrocode}
else
% \end{macrocode}
@@ -3256,13 +3934,16 @@ function luamplib.flush (result,flusher)
local object = objects[o]
local objecttype = object.type
% \end{macrocode}
-% The following 6 lines are part of |btex...etex| patch.
+% The following 9 lines are part of |btex...etex| patch.
% Again, colors are processed at this stage.
% \begin{macrocode}
local prescript = object.prescript
prescript = prescript and script2table(prescript) -- prescript is now a table
local cr_over = do_preobj_CR(object,prescript) -- color
local tr_opaq = do_preobj_TR(object,prescript) -- opacity
+ local fading_ = do_preobj_FADE(object,prescript) -- fading
+ local trgroup = do_preobj_GRP(object,prescript) -- transparency group
+ local pattern_ = do_preobj_PAT(object,prescript) -- pattern
if prescript and prescript.mplibtexboxid then
put_tex_boxes(object,prescript)
elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then --skip
@@ -3288,7 +3969,7 @@ function luamplib.flush (result,flusher)
pdf_literalcode("%f %f %f %f %f %f cm",ot[3],ot[4],ot[5],ot[6],ot[1],ot[2])
pdf_textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
stop_pdf_code()
- else
+ elseif not trgroup and fading_ ~= "stop" then
local evenodd, collect, both = false, false, false
local postscript = object.postscript
if not object.istext then
@@ -3314,10 +3995,7 @@ function luamplib.flush (result,flusher)
else
% \end{macrocode}
% Removed from ConTeXt general: color stuff.
-% Added instead : shading stuff
% \begin{macrocode}
- local shade_no = do_preobj_SH(object,prescript) -- shading
- local pattern_ = do_preobj_PAT(object,prescript) -- pattern
local ml = object.miterlimit
if ml and ml ~= miterlimit then
miterlimit = ml
@@ -3359,6 +4037,14 @@ function luamplib.flush (result,flusher)
objecttype = 'fill'
end
end
+% \end{macrocode}
+% Added : shading
+% \begin{macrocode}
+ local shade_no = do_preobj_SH(object,prescript) -- shading
+ if shade_no then
+ pdf_literalcode"q /Pattern cs"
+ objecttype = false
+ end
if transformed then
start_pdf_code()
end
@@ -3379,27 +4065,25 @@ function luamplib.flush (result,flusher)
else
flushnormalpath(path,open)
end
-% \end{macrocode}
-% Shading seems to conflict with these ops
-% \begin{macrocode}
- if not shade_no then -- conflict with shading
- if objecttype == "fill" then
- pdf_literalcode(evenodd and "h f*" or "h f")
- elseif objecttype == "outline" then
- if both then
- pdf_literalcode(evenodd and "h B*" or "h B")
- else
- pdf_literalcode(open and "S" or "h S")
- end
- elseif objecttype == "both" then
+ if objecttype == "fill" then
+ pdf_literalcode(evenodd and "h f*" or "h f")
+ elseif objecttype == "outline" then
+ if both then
pdf_literalcode(evenodd and "h B*" or "h B")
+ else
+ pdf_literalcode(open and "S" or "h S")
end
+ elseif objecttype == "both" then
+ pdf_literalcode(evenodd and "h B*" or "h B")
end
end
if transformed then
stop_pdf_code()
end
local path = object.htap
+% \end{macrocode}
+% How can we generate an |htap| object? Please let us know if you have succeeded.
+% \begin{macrocode}
if path then
if transformed then
start_pdf_code()
@@ -3433,18 +4117,29 @@ function luamplib.flush (result,flusher)
end
end
% \end{macrocode}
-% Added to ConTeXt general: post-object color and shading stuff.
+% Added to ConTeXt general: post-object colors and shading stuff.
+% We should beware the |q ... Q| scope.
% \begin{macrocode}
if shade_no then -- shading
- pdf_literalcode("W n /MPlibSh%s sh Q",shade_no)
+ pdf_literalcode("W%s n /MPlibSh%s sh Q",evenodd and "*" or "",shade_no)
end
end
end
- if tr_opaq then -- opacity
- stop_pdf_code()
+ if fading_ == "start" then
+ pdfetcs.fading.specialeffects = {fading_, tr_opaq, cr_over}
+ elseif trgroup == "start" then
+ pdfetcs.tr_group.specialeffects = {fading_, tr_opaq, cr_over}
+ elseif fading_ == "stop" then
+ local se = pdfetcs.fading.specialeffects
+ if se then stop_special_effects(se[1], se[2], se[3]) end
+ elseif trgroup == "stop" then
+ local se = pdfetcs.tr_group.specialeffects
+ if se then stop_special_effects(se[1], se[2], se[3]) end
+ else
+ stop_special_effects(fading_, tr_opaq, cr_over)
end
- if cr_over then -- color
- put2output"\\special{pdf:ec}"
+ if fading_ or trgroup then -- extgs resetted
+ miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
end
end
end
@@ -3497,15 +4192,34 @@ end
% First we need to load some packages.
%
% \begin{macrocode}
-\bgroup\expandafter\expandafter\expandafter\egroup
-\expandafter\ifx\csname selectfont\endcsname\relax
- \input ltluatex
-\else
+\ifcsname ProvidesPackage\endcsname
+% \end{macrocode}
+% We need \LaTeX\ 2024-06-01 as we use |ltx.pdf.object_id| when pdfmanagement is loaded.
+% But as \pkg{fp} package does not accept an option, we do not append the date option.
+% \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{luamplib}
- [2024/07/03 v2.32.4 mplib package for LuaTeX]
- \ifx\newluafunction\@undefined
+ [2024/11/12 v2.35.0 mplib package for LuaTeX]
+\fi
+\ifdefined\newluafunction\else
\input ltluatex
+\fi
+% \end{macrocode}
+%
+% In DVI mode, a new XObject (mppattern, mplibgroup) must be encapsulated in an \cs{hbox}.
+% But this should not affect typesetting. So we use Hook mechanism provided by \LaTeX\ kernel.
+% In Plain, \pkg{atbegshi.sty} is loaded.
+% \begin{macrocode}
+\ifnum\outputmode=0
+ \ifdefined\AddToHookNext
+ \def\luamplibatnextshipout{\AddToHookNext{shipout/background}}
+ \def\luamplibatfirstshipout{\AddToHook{shipout/firstpage}}
+ \def\luamplibateveryshipout{\AddToHook{shipout/background}}
+ \else
+ \input atbegshi.sty
+ \def\luamplibatnextshipout#1{\AtBeginShipoutNext{\AtBeginShipoutAddToBox{#1}}}
+ \let\luamplibatfirstshipout\AtBeginShipoutFirst
+ \def\luamplibateveryshipout#1{\AtBeginShipout{\AtBeginShipoutAddToBox{#1}}}
\fi
\fi
% \end{macrocode}
@@ -3525,7 +4239,7 @@ end
\fi
% \end{macrocode}
%
-% Set the format for metapost.
+% Set the format for \metapost.
% \begin{macrocode}
\def\mplibsetformat#1{\directlua{luamplib.setformat("#1")}}
% \end{macrocode}
@@ -3567,6 +4281,40 @@ end
\def\mplibputtextbox#1{\vbox to 0pt{\vss\hbox to 0pt{\raise\dp#1\copy#1\hss}}}
% \end{macrocode}
%
+% use Transparency Group
+% \begin{macrocode}
+\protected\def\usemplibgroup#1#{\usemplibgroupmain}
+\def\usemplibgroupmain#1{\csname luamplib.group.#1\endcsname}
+\protected\def\mplibgroup#1{%
+ \begingroup
+ \def\MPllx{0}\def\MPlly{0}%
+ \def\mplibgroupname{#1}%
+ \mplibgroupgetnexttok
+}
+\def\mplibgroupgetnexttok{\futurelet\nexttok\mplibgroupbranch}
+\def\mplibgroupskipspace{\afterassignment\mplibgroupgetnexttok\let\nexttok= }
+\def\mplibgroupbranch{%
+ \ifx [\nexttok
+ \expandafter\mplibgroupopts
+ \else
+ \ifx\mplibsptoken\nexttok
+ \expandafter\expandafter\expandafter\mplibgroupskipspace
+ \else
+ \let\mplibgroupoptions\empty
+ \expandafter\expandafter\expandafter\mplibgroupmain
+ \fi
+ \fi
+}
+\def\mplibgroupopts[#1]{\def\mplibgroupoptions{#1}\mplibgroupmain}
+\def\mplibgroupmain{\setbox\mplibscratchbox\hbox\bgroup\ignorespaces}
+\protected\def\endmplibgroup{\egroup
+ \directlua{ luamplib.registergroup(
+ \the\mplibscratchbox, '\mplibgroupname', {\mplibgroupoptions}
+ )}%
+ \endgroup
+}
+% \end{macrocode}
+%
% Patterns
% \begin{macrocode}
{\def\:{\global\let\mplibsptoken= } \: }
@@ -3605,7 +4353,7 @@ end
}
% \end{macrocode}
%
-% simple way to use mplib:
+% simple way to use \mplib:
% |\mpfig draw fullcircle scaled 10; \endmpfig|
% \begin{macrocode}
\def\mpfiginstancename{@mpfig}
@@ -3617,9 +4365,14 @@ end
\ifx *\nexttok
\expandafter\mplibprempfig
\else
- \expandafter\mplibmainmpfig
+ \ifx [\nexttok
+ \expandafter\expandafter\expandafter\mplibgobbleoptsmpfig
+ \else
+ \expandafter\expandafter\expandafter\mplibmainmpfig
+ \fi
\fi
}
+\def\mplibgobbleoptsmpfig[#1]{\mplibmainmpfig}
\def\mplibmainmpfig{%
\begingroup
\mplibsetupcatcodes
@@ -3628,16 +4381,16 @@ end
\long\def\mplibdomainmpfig#1\endmpfig{%
\endgroup
\directlua{
- local legacy = luamplib.legacy_verbatimtex
+ local legacy = luamplib.legacyverbatimtex
local everympfig = luamplib.everymplib["\mpfiginstancename"] or ""
local everyendmpfig = luamplib.everyendmplib["\mpfiginstancename"] or ""
- luamplib.legacy_verbatimtex = false
+ luamplib.legacyverbatimtex = false
luamplib.everymplib["\mpfiginstancename"] = ""
luamplib.everyendmplib["\mpfiginstancename"] = ""
luamplib.process_mplibcode(
"beginfig(0) "..everympfig.." "..[===[\unexpanded{#1}]===].." "..everyendmpfig.." endfig;",
"\mpfiginstancename")
- luamplib.legacy_verbatimtex = legacy
+ luamplib.legacyverbatimtex = legacy
luamplib.everymplib["\mpfiginstancename"] = everympfig
luamplib.everyendmplib["\mpfiginstancename"] = everyendmpfig
}%
@@ -3651,14 +4404,14 @@ end
\long\def\mplibdoprempfig#1\endmpfig{%
\endgroup
\directlua{
- local legacy = luamplib.legacy_verbatimtex
+ local legacy = luamplib.legacyverbatimtex
local everympfig = luamplib.everymplib["\mpfiginstancename"]
local everyendmpfig = luamplib.everyendmplib["\mpfiginstancename"]
- luamplib.legacy_verbatimtex = false
+ luamplib.legacyverbatimtex = false
luamplib.everymplib["\mpfiginstancename"] = ""
luamplib.everyendmplib["\mpfiginstancename"] = ""
luamplib.process_mplibcode([===[\unexpanded{#1}]===],"\mpfiginstancename")
- luamplib.legacy_verbatimtex = legacy
+ luamplib.legacyverbatimtex = legacy
luamplib.everymplib["\mpfiginstancename"] = everympfig
luamplib.everyendmplib["\mpfiginstancename"] = everyendmpfig
}%
@@ -3737,9 +4490,9 @@ end
\def\mpliblegacybehavior#1{\directlua{
local s = string.lower("#1")
if s == "enable" or s == "true" or s == "yes" then
- luamplib.legacy_verbatimtex = true
+ luamplib.legacyverbatimtex = true
else
- luamplib.legacy_verbatimtex = false
+ luamplib.legacyverbatimtex = false
end
}}
\def\mplibverbatim#1{\directlua{
@@ -3811,14 +4564,13 @@ end
%
% Allow \TeX\ dimen/color macros. Now |runscript| does the job,
% so the following lines are not needed for most cases.
-% But the macros will be expanded when they are used in another macro.
% \begin{macrocode}
\def\mpdim#1{ runscript("luamplibdimen{#1}") }
\def\mpcolor#1#{\domplibcolor{#1}}
\def\domplibcolor#1#2{ runscript("luamplibcolor{#1{#2}}") }
% \end{macrocode}
%
-% MPLib's number system. Now |binary| has gone away.
+% \mplib's number system. Now |binary| has gone away.
% \begin{macrocode}
\def\mplibnumbersystem#1{\directlua{
local t = "#1"
@@ -3883,12 +4635,13 @@ end
% \end{macrocode}
%
% The followings are from ConTeXt general, mostly.
-% % We use a dedicated scratchbox.
+%
+% We use a dedicated scratchbox.
% \begin{macrocode}
\ifx\mplibscratchbox\undefined \newbox\mplibscratchbox \fi
% \end{macrocode}
%
-% We encapsulate the litterals.
+% We encapsulate the literals.
% \begin{macrocode}
\def\mplibstarttoPDF#1#2#3#4{%
\prependtomplibbox
@@ -3954,6 +4707,343 @@ end
\fi
% \end{macrocode}
%
+% Code for \pkg{tagpdf}
+% \begin{macrocode}
+\def\luamplibtagtextbegin#1{}
+\let\luamplibtagtextend\relax
+\ifcsname SuspendTagging\endcsname\else\endinput\fi
+\ifcsname picture@tag@bbox@attribute\endcsname \else
+ \ExplSyntaxOn
+ \keys_define:nn{luamplib/notag}
+ {
+ ,alt .code:n = { }
+ ,actualtext .code:n = { }
+ ,artifact .code:n = { }
+ ,text .code:n = { }
+ ,correct-BBox .code:n = { }
+ ,tag .code:n = { }
+ ,debug .code:n = { }
+ ,instance .code:n = { \tl_gset:Nn \currentmpinstancename {#1} }
+ ,instancename .meta:n = { instance = {#1} }
+ ,unknown .code:n = { \tl_gset:Ne \currentmpinstancename {\l_keys_key_str} }
+ }
+ \RenewDocumentCommand\mplibcode{O{}}
+ {
+ \tl_gset_eq:NN \currentmpinstancename \c_empty_tl
+ \keys_set:nn{luamplib/notag}{#1}
+ \mplibtmptoks{}\ltxdomplibcode
+ }
+ \ExplSyntaxOff
+ \let\mplibalttext \luamplibtagtextbegin
+ \let\mplibactualtext \mplibalttext
+ \endinput\fi
+\let\mplibstarttoPDForiginal\mplibstarttoPDF
+\let\mplibstoptoPDForiginal\mplibstoptoPDF
+\let\mplibputtextboxoriginal\mplibputtextbox
+\ExplSyntaxOn
+\tl_new:N \l__tag_luamplib_alt_tl
+\tl_new:N \l__tag_luamplib_alt_dflt_tl
+\tl_set:Nn\l__tag_luamplib_alt_dflt_tl {metapost~figure}
+\tl_new:N \l__tag_luamplib_actual_tl
+\tl_new:N \l__tag_luamplib_struct_tl
+\tl_set:Nn\l__tag_luamplib_struct_tl {Figure}
+\bool_new:N \l__tag_luamplib_usetext_bool
+\bool_set_false:N \l__tag_luamplib_usetext_bool
+\cs_set_nopar:Npn \luamplibtagtextbegin #1
+{
+ \bool_if:NTF \l__tag_luamplib_usetext_bool
+ {
+ \tag_mc_end_push:
+ \tag_struct_begin:n{tag=NonStruct,stash}
+ \tag_if_active:T {
+ \expandafter\xdef\csname luamplib.tagbox.#1\endcsname{\tag_get:n{struct_num}}
+ }
+ \tag_mc_begin:n{}
+ }
+ {
+ \tag_if_active:TF
+ { \chardef\mplibtmpnum\@ne }
+ { \chardef\mplibtmpnum\z@ }
+ \SuspendTagging{luamplib.textext}
+ }
+}
+\cs_set_nopar:Npn \luamplibtagtextend
+{
+ \bool_if:NTF \l__tag_luamplib_usetext_bool
+ {
+ \tag_mc_end:
+ \tag_struct_end:
+ \tag_mc_begin_pop:n{}
+ }
+ {
+ \ifnum\mplibtmpnum=\@ne
+ \ResumeTagging{luamplib.textext}
+ \fi
+ }
+}
+\msg_new:nnn {luamplib}{figure-text-reuse}
+{
+ textext~box~#1~probably~is~incorrectly~tagged.\\
+ Reusing~a~box~in~text-keyed~figures~is~strongly~discouraged.
+}
+\cs_set_nopar:Npn \mplibputtextbox #1
+{
+ \vbox to 0pt{\vss\hbox to 0pt{%
+ \bool_if:NTF \l__tag_luamplib_usetext_bool
+ {
+ \ResumeTagging{luamplib.puttextbox}
+ \tag_mc_end:
+ \cs_if_exist:cTF {luamplib.tagbox.#1}
+ {
+ \tag_struct_use_num:n {\csname luamplib.tagbox.#1\endcsname}
+ \raise\dp#1\copy#1\hss
+ }
+ {
+ \msg_warning:nnn{luamplib}{figure-text-reuse}{#1}
+ \tag_mc_begin:n{}
+ \chardef\mplibtmpnum=#1\relax
+ \tag_mc_reset_box:N \mplibtmpnum
+ \raise\dp#1\copy#1\hss
+ \tag_mc_end:
+ }
+ \tag_mc_begin:n{artifact}
+ }
+ {
+ \chardef\mplibtmpnum=#1\relax
+ \tag_mc_reset_box:N \mplibtmpnum
+ \raise\dp#1\copy#1\hss
+ }
+ }}
+}
+\cs_new_nopar:Npn \__luamplib_tagging_begin_figure:
+{
+ \tag_if_active:T
+ {
+ \tag_mc_end_push:
+ \tl_if_empty:NT\l__tag_luamplib_alt_tl
+ {
+ \msg_warning:nne{luamplib}{alt-text-missing}{\l__tag_luamplib_alt_dflt_tl}
+ \tl_set:Ne\l__tag_luamplib_alt_tl {\l__tag_luamplib_alt_dflt_tl}
+ }
+ \tag_struct_begin:n
+ {
+ tag=\l__tag_luamplib_struct_tl,
+ alt=\l__tag_luamplib_alt_tl,
+ }
+ \tag_mc_begin:n{}
+ }
+}
+\cs_new_nopar:Npn \__luamplib_tagging_end_figure:
+{
+ \tag_if_active:T
+ {
+ \tag_mc_end:
+ \tag_struct_end:
+ \tag_mc_begin_pop:n{}
+ }
+}
+\cs_new_nopar:Npn \__luamplib_tagging_begin_actualtext:
+{
+ \tag_if_active:T
+ {
+ \tag_mc_end_push:
+ \tag_struct_begin:n
+ {
+ tag=Span,
+ actualtext=\l__tag_luamplib_actual_tl,
+ }
+ \tag_mc_begin:n{}
+ }
+}
+\cs_set_eq:NN \__luamplib_tagging_end_actualtext: \__luamplib_tagging_end_figure:
+\cs_new_nopar:Npn \__luamplib_tagging_begin_artifact:
+{
+ \tag_if_active:T
+ {
+ \tag_mc_end_push:
+ \tag_mc_begin:n{artifact}
+ }
+}
+\cs_new_nopar:Npn \__luamplib_tagging_end_artifact:
+{
+ \tag_if_active:T
+ {
+ \tag_mc_end:
+ \tag_mc_begin_pop:n{}
+ }
+}
+\cs_set_eq:NN \luamplibtaggingbegin \__luamplib_tagging_begin_figure:
+\cs_set_eq:NN \luamplibtaggingend \__luamplib_tagging_end_figure:
+\keys_define:nn{luamplib/tag}
+ {
+ ,alt .code:n =
+ {
+ \bool_set_true:N \l__tag_graphic_BBox_bool
+ \bool_set_false:N \l__tag_luamplib_usetext_bool
+ \tl_set:Ne\l__tag_luamplib_alt_tl{\text_purify:n{#1}}
+ }
+ ,actualtext .code:n =
+ {
+ \bool_set_false:N \l__tag_graphic_BBox_bool
+ \bool_set_false:N \l__tag_luamplib_usetext_bool
+ \tl_set:Ne\l__tag_luamplib_actual_tl{\text_purify:n{#1}}
+ \cs_set_eq:NN \luamplibtaggingbegin \__luamplib_tagging_begin_actualtext:
+ \cs_set_eq:NN \luamplibtaggingend \__luamplib_tagging_end_actualtext:
+ \tag_if_active:T {\noindent}
+ }
+ ,artifact .code:n =
+ {
+ \bool_set_false:N \l__tag_graphic_BBox_bool
+ \bool_set_false:N \l__tag_luamplib_usetext_bool
+ \cs_set_eq:NN \luamplibtaggingbegin \__luamplib_tagging_begin_artifact:
+ \cs_set_eq:NN \luamplibtaggingend \__luamplib_tagging_end_artifact:
+ }
+ ,text .code:n =
+ {
+ \bool_set_false:N \l__tag_graphic_BBox_bool
+ \bool_set_true:N \l__tag_luamplib_usetext_bool
+ \cs_set_eq:NN \luamplibtaggingbegin \__luamplib_tagging_begin_artifact:
+ \cs_set_eq:NN \luamplibtaggingend \__luamplib_tagging_end_artifact:
+ \tag_if_active:T {\noindent}
+ }
+ ,correct-BBox .code:n =
+ {
+ \bool_set_true:N \l__tag_graphic_bboxcorr_bool
+ \seq_set_split:Nnn\l__tag_graphic_bboxcorr_seq{~}{#1~0pt~0pt~0pt~0pt}
+ }
+ ,tag .code:n =
+ {
+ \str_case:nnF {#1}
+ {
+ {artifact}
+ {
+ \bool_set_false:N \l__tag_graphic_BBox_bool
+ \bool_set_false:N \l__tag_luamplib_usetext_bool
+ \cs_set_eq:NN \luamplibtaggingbegin \__luamplib_tagging_begin_artifact:
+ \cs_set_eq:NN \luamplibtaggingend \__luamplib_tagging_end_artifact:
+ }
+ {text}
+ {
+ \bool_set_false:N \l__tag_graphic_BBox_bool
+ \bool_set_true:N \l__tag_luamplib_usetext_bool
+ \cs_set_eq:NN \luamplibtaggingbegin \__luamplib_tagging_begin_artifact:
+ \cs_set_eq:NN \luamplibtaggingend \__luamplib_tagging_end_artifact:
+ \tag_if_active:T {\noindent}
+ }
+ {false}
+ {
+ \SuspendTagging{luamplib.tagfalse}
+ }
+ }
+ {
+ \tl_set:Nn\l__tag_luamplib_struct_tl{#1}
+ }
+ }
+ ,debug .code:n =
+ { \bool_set_true:N \l__tag_graphic_debug_bool }
+ ,instance .code:n =
+ { \tl_gset:Nn \currentmpinstancename {#1} }
+ ,instancename .meta:n = { instance = {#1} }
+ ,unknown .code:n =
+ { \tl_gset:Ne \currentmpinstancename {\l_keys_key_str} }
+ }
+\cs_new_nopar:Npn \luamplibtaggingBBox
+{
+ \let\@picbox\mplibscratchbox \picture@tag@bbox@attribute
+}
+\cs_set_nopar:Npn \mplibstarttoPDF #1 #2 #3 #4
+ {
+ \prependtomplibbox
+ \hbox dir TLT\bgroup
+ \luamplibtaggingbegin % begin tagging
+ \xdef\MPllx{#1}\xdef\MPlly{#2}%
+ \xdef\MPurx{#3}\xdef\MPury{#4}%
+ \xdef\MPwidth{\the\dimexpr#3bp-#1bp\relax}%
+ \xdef\MPheight{\the\dimexpr#4bp-#2bp\relax}%
+ \parskip0pt
+ \leftskip0pt
+ \parindent0pt
+ \everypar{}%
+ \setbox\mplibscratchbox\vbox\bgroup
+ \SuspendTagging{luamplib.mplibtopdf}% stop tag inside figure
+ \noindent
+ }
+\cs_set_nopar:Npn \mplibstoptoPDF
+ {
+ \par
+ \egroup
+ \setbox\mplibscratchbox\hbox
+ {\hskip-\MPllx bp
+ \raise-\MPlly bp
+ \box\mplibscratchbox}%
+ \setbox\mplibscratchbox\vbox to \MPheight
+ {\vfill
+ \hsize\MPwidth
+ \wd\mplibscratchbox0pt
+ \ht\mplibscratchbox0pt
+ \dp\mplibscratchbox0pt
+ \box\mplibscratchbox}%
+ \wd\mplibscratchbox\MPwidth
+ \ht\mplibscratchbox\MPheight
+ \luamplibtaggingBBox % BBox
+ \box\mplibscratchbox
+ \luamplibtaggingend % end tagging
+ \egroup
+ }
+\RenewDocumentCommand\mplibcode{O{}}
+ {
+ \msg_set:nnn {luamplib}{alt-text-missing}
+ {
+ Alternative~text~for~mplibcode~is~missing.\\
+ Using~the~default~value~'##1'~instead.
+ }
+ \tl_gset_eq:NN \currentmpinstancename \c_empty_tl
+ \keys_set:nn{luamplib/tag}{#1}
+ \tl_if_empty:NF \currentmpinstancename
+ { \tl_set:Nn\l__tag_luamplib_alt_dflt_tl {metapost~figure~\currentmpinstancename} }
+ \mplibtmptoks{}\ltxdomplibcode
+ }
+\RenewDocumentCommand\mpfig{s O{}}
+ {
+ \begingroup
+ \IfBooleanTF{#1}
+ {\mplibprempfig *}
+ {
+ \msg_set:nnn {luamplib}{alt-text-missing}
+ {
+ Alternative~text~for~mpfig~is~missing.\\
+ Using~the~default~value~'##1'~instead.
+ }
+ \keys_set:nn{luamplib/tag}{#2}
+ \tl_if_empty:NF \mpfiginstancename
+ { \tl_set:Nn\l__tag_luamplib_alt_dflt_tl {metapost~figure~\mpfiginstancename} }
+ \mplibmainmpfig
+ }
+ }
+\RenewDocumentCommand\usemplibgroup{O{} m}
+ {
+ \begingroup
+ \msg_set:nnn {luamplib}{alt-text-missing}
+ {
+ Alternative~text~for~usemplibgroup~is~missing.\\
+ Using~the~default~value~'##1'~instead.
+ }
+ \keys_set:nn{luamplib/tag}{#1}
+ \tl_set:Nn\l__tag_luamplib_alt_dflt_tl {metapost~figure~#2}
+ \csname luamplib.group.#2\endcsname
+ \endgroup
+ }
+\cs_new_nopar:Npn \mplibalttext #1
+{
+ \tl_set:Ne \l__tag_luamplib_alt_tl {\text_purify:n{#1}}
+}
+\cs_new_nopar:Npn \mplibactualtext #1
+{
+ \tl_set:Ne \l__tag_luamplib_actual_tl {\text_purify:n{#1}}
+}
+\ExplSyntaxOff
+% \end{macrocode}
+%
% That's all folks!
%
% \iffalse
diff --git a/macros/luatex/generic/luamplib/luamplib.pdf b/macros/luatex/generic/luamplib/luamplib.pdf
index 5e8e97a778..a6f7e5c362 100644
--- a/macros/luatex/generic/luamplib/luamplib.pdf
+++ b/macros/luatex/generic/luamplib/luamplib.pdf
Binary files differ
diff --git a/macros/luatex/generic/luamplib/test-luamplib-latex.tex b/macros/luatex/generic/luamplib/test-luamplib-latex.tex
index c2d8cb498b..8435ee070c 100644
--- a/macros/luatex/generic/luamplib/test-luamplib-latex.tex
+++ b/macros/luatex/generic/luamplib/test-luamplib-latex.tex
@@ -7,7 +7,7 @@
\usepackage{fontspec}
\setmainfont{latin modern roman}
\usepackage{luamplib}
-\usepackage{xcolor}
+\usepackage{graphicx,xcolor}
\everymplib{ beginfig(0); }
\everyendmplib{ endfig; }
\mpliblegacybehavior{true}%
@@ -246,19 +246,19 @@ draw mpliboutlinetext.b ("$\displaystyle\frac{1}{1-x^2}$")
\endmpfig
\par\leavevmode
\mppattern{mypatt}
- \mpfig
- picture q;
- q := btex Q etex;
- fill bbox q withcolor .8[red,white];
- draw q withcolor .8red;
- \endmpfig
+[
+ xstep = 5, ystep = 6,
+ matrix = "rotated 90 scaled .75",
+]
+\mpfig
+draw (origin--right+up) scaled 5 withcolor 1/3[blue,white] ;
+draw (up--right) scaled 5 withcolor 1/3[red,white] ;
+\endmpfig
\endmppattern
\mpfig
- fill fullcircle scaled 100 withpostscript "collect";
- draw unitsquare shifted - center unitsquare scaled 45
- withpattern "mypatt"
- withpostscript "evenodd"
- ;
+draw unitsquare shifted -center unitsquare scaled 45 withpostscript "collect" ;
+draw fullcircle scaled 100 withpattern "mypatt" withpen pencircle scaled 1
+ withcolor \mpcolor{red!50!blue!50} withpostscript "evenodd" ;
\endmpfig
\begin{mppattern}{pattuncolored}
[
@@ -268,19 +268,81 @@ draw mpliboutlinetext.b ("$\displaystyle\frac{1}{1-x^2}$")
\tiny\TeX
\end{mppattern}\relax
\mpfig
- picture tex; tex := mpliboutlinetext.p ("\bfseries \TeX");
- i:=0;
- for item within tex:
- i:=i+1;
- if i < length tex:
- fill pathpart item scaled 10 withpostscript "collect";
- else:
- draw pathpart item scaled 10 withpattern "pattuncolored"
- withpen pencircle scaled 1/2
- withcolor 0.7 blue;
- fi
+ picture tex;
+ tex = mpliboutlinetext.p ("\bfseries \TeX");
+ for i=1 upto mpliboutlinenum:
+ draw image(
+ j:=0;
+ for item within mpliboutlinepic[i]:
+ j:=j+1;
+ draw pathpart item scaled 10
+ if j < length mpliboutlinepic[i]:
+ withpostscript "collect"
+ else:
+ withpattern "pattuncolored"
+ withpen pencircle scaled 1/2
+ withcolor (i/4)[red,blue]
+ fi;
+ endfor
+ )
+ withfademethod "linear"
+ ;
endfor
\endmpfig
+\par\leavevmode
+\mpfig
+ picture mill; mill = btex \includegraphics[width=100bp]{mill} etex;
+ draw mill;
+ mill := mill shifted 125right;
+ draw mill
+ withfademethod "circular"
+ withfadecenter (center mill, center mill)
+ withfaderadius (30, abs(lrcorner mill - llcorner mill)/2)
+ withfadeopacity (1, 0)
+ ;
+\endmpfig
+\par
+\def\test#1{%
+ \mpfig
+ fill unitsquare shifted -center unitsquare scaled 200
+ withshademethod "linear"
+ withshadecolors (.3[red,white], .3[blue,white])
+ ;
+ label( btex MetaPost etex scaled 3, origin) ;
+ draw image(
+ fill fullcircle scaled 100 shifted 25left
+ withcolor .8white
+ withtransparency (2,1)
+ ;
+ fill fullcircle scaled 100 shifted 25right
+ withcolor .8white
+ withtransparency (2,1)
+ ;
+ )
+ asgroup "#1"
+ withgroupname "test:#1"
+ withtransparency (1,.5)
+ ;
+ currentpicture := currentpicture scaled 2/3;
+ \endmpfig
+}%
+\hbox{\test{isolated,knockout}\,\test{isolated}\vbox to\MPheight{\vss\hbox{~isolated}\vss}}%
+\hbox{\test{knockout}\,\test{}}%
+\hbox to\MPwidth{\hss knockout\hss}%
+\leavevmode
+\mpfig usemplibgroup "test:isolated,knockout" scaled 2/3 rotated 15 ; \endmpfig
+\usemplibgroup{test:isolated,knockout}%
+\begin{mplibgroup}{mytex}[matrix="rotated 15"] \TeX \end{mplibgroup}%
+\hbox to0pt{\hss\vrule width.5pt height5pt depth5pt\hss}%
+\hbox to0pt{\hss\vrule width10pt height.25pt depth.25pt\hss}%
+\usemplibgroup{mytex}%
+\mpfig usemplibgroup "mytex"; draw (left--right) scaled 5; draw (up--down) scaled 5; \endmpfig
+\par
+\mpfig
+ picture test; test = mplibgraphictext "\textbf{MPLIB}"
+ fakebold 1 fillcolor "red!70" drawcolor .7red scaled 7;
+ draw test withpattern "mypatt" ;
+\endmpfig
\tracingcommands0
\vskip 2\baselineskip
diff --git a/macros/luatex/generic/luamplib/test-luamplib-plain.tex b/macros/luatex/generic/luamplib/test-luamplib-plain.tex
index c178acb31a..05c2f8138d 100644
--- a/macros/luatex/generic/luamplib/test-luamplib-plain.tex
+++ b/macros/luatex/generic/luamplib/test-luamplib-plain.tex
@@ -1,4 +1,4 @@
-\input luaotfload.sty
+%\input luaotfload.sty
\input miniltx
\input color
\definecolor{orange}{cmyk}{0,.5,1,0}
@@ -261,18 +261,62 @@ draw mpliboutlinetext.b ("$\overbrace{x+\cdots+x}^k$")
\fiverm\TeX
\endmppattern
\mpfig
- picture tex; tex := mpliboutlinetext.p ("\bf \TeX");
- i:=0;
- for item within tex:
- i:=i+1;
- if i < length tex:
- fill pathpart item scaled 10 withpostscript "collect";
- else:
- draw pathpart item scaled 10 withpattern "pattuncolored"
- withpen pencircle scaled 1/2
- withcolor 0.7 blue;
- fi
+ picture tex;
+ tex = mpliboutlinetext.p ("\bf \TeX");
+ draw image(
+ for i=1 upto mpliboutlinenum:
+ j:=0;
+ for item within mpliboutlinepic[i]:
+ j:=j+1;
+ draw pathpart item scaled 10
+ if j < length mpliboutlinepic[i]:
+ withpostscript "collect"
+ else:
+ withpattern "pattuncolored"
+ withpen pencircle scaled 1/2
+ withcolor (i/4)[red,blue]
+ fi;
+ endfor
endfor
+ )
+ withfademethod "linear"
+ ;
+\endmpfig
+\par\leavevmode
+\mpfig
+fill fullcircle scaled 125 withcolor 2/3[blue,white];
+fill fullcircle scaled 80 withcolor 2/3[red,white];
+draw image(
+ draw (left--right) rotated 45 scaled 50
+ withpen pencircle scaled 20
+ ;
+ draw (left--right) rotated -45 scaled 50
+ withpen pencircle scaled 20
+ ;
+ )
+ asgroup ""
+ withgroupname "testTRgroup"
+ withprescript "tr_transparency=.3"
+ ;
+\endmpfig
+\mpfig
+fill fullcircle scaled 125 withcolor 2/3[red,white];
+fill fullcircle scaled 80 withcolor 2/3[blue,white];
+usemplibgroup "testTRgroup"
+ withfademethod "circular"
+ withfaderadius (0,60)
+ ;
+\endmpfig
+\mplibgroup{mytex}[matrix="rotated 15"] \TeX \endmplibgroup
+\hbox to0pt{\hss\vrule width.5pt height5pt depth5pt\hss}%
+\hbox to0pt{\hss\vrule width10pt height.25pt depth.25pt\hss}%
+\usemplibgroup{mytex}%
+\mpfig usemplibgroup "mytex"; draw (left--right) scaled 5; draw (up--down) scaled 5; \endmpfig
+\par
+\mpfig
+ picture test; test = mplibgraphictext "\bf MPLIB"
+ fakebold 1 fillcolor .7[white,blue] drawcolor .7blue scaled 7;
+ draw test withpattern "pattuncolored" ;
\endmpfig
\tracingcommands0
diff --git a/macros/luatex/generic/luatexja/README b/macros/luatex/generic/luatexja/README
index 9224c83b0b..b70a6c36fb 100644
--- a/macros/luatex/generic/luatexja/README
+++ b/macros/luatex/generic/luatexja/README
@@ -1,4 +1,4 @@
-The LuaTeX-ja Package 20240514.0
+The LuaTeX-ja Package 20241014.0
--------------------------------
Copyright (c) 2011--2024 The LuaTeX-ja project
License: modified BSD (see COPYING)
@@ -42,7 +42,7 @@ Installation
2. If you must/want to install manually:
a. Download the source archive from CTAN,
- or tagged as 20240514.0 in the Git repository by
+ or tagged as 20241014.0 in the Git repository by
<https://github.com/luatexja/luatexja/archive/refs/tags/$VER.zip>
b. Extract the archive and process following three files by LuaLaTeX
@@ -92,4 +92,4 @@ Notes
This file and ltj-kinsoku.lua are not used anymore.
(Do not remove ltj-kinsoku.tex.)
-Last commit date: Tue May 14 19:12:05 2024 +0900
+Last commit date: Mon Oct 14 10:29:57 2024 +0900
diff --git a/macros/luatex/generic/luatexja/doc/ltjsclasses.pdf b/macros/luatex/generic/luatexja/doc/ltjsclasses.pdf
index 786e01afa5..1c20815985 100644
--- a/macros/luatex/generic/luatexja/doc/ltjsclasses.pdf
+++ b/macros/luatex/generic/luatexja/doc/ltjsclasses.pdf
Binary files differ
diff --git a/macros/luatex/generic/luatexja/doc/luatexja-en.pdf b/macros/luatex/generic/luatexja/doc/luatexja-en.pdf
index de28e1d279..4a64b591fa 100644
--- a/macros/luatex/generic/luatexja/doc/luatexja-en.pdf
+++ b/macros/luatex/generic/luatexja/doc/luatexja-en.pdf
Binary files differ
diff --git a/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf b/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf
index 49a8a6feef..981d4ddf93 100644
--- a/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf
+++ b/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf
Binary files differ
diff --git a/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf b/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf
index 06cf336b44..0914351b11 100644
--- a/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf
+++ b/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf
Binary files differ
diff --git a/macros/luatex/generic/luatexja/doc/luatexja-ruby.tex b/macros/luatex/generic/luatexja/doc/luatexja-ruby.tex
index c7e05e8c51..8a12307e70 100644
--- a/macros/luatex/generic/luatexja/doc/luatexja-ruby.tex
+++ b/macros/luatex/generic/luatexja/doc/luatexja-ruby.tex
@@ -1,7 +1,7 @@
%#! lualatex
%%% a test of ruby.
\documentclass[a4paper,10ptj]{ltjsarticle}
-\usepackage[rgb]{xcolor}
+\usepackage[cmyk]{xcolor}
\usepackage{enumitem,mleftright,pict2e}
\usepackage{luatexja-otf,amsmath,bxghost}
\usepackage[no-math]{fontspec}
@@ -17,15 +17,21 @@
\def\cs#1{\eghostguarded{\texttt{\textbackslash#1}}}
\usepackage[textwidth=45\zw, lines=45, footskip=2\zh]{geometry}
\advance\leftmargini-1\zw\advance\leftmarginii-1\zw
-\protected\def\Param#1{\eghostguarded{\underline{\smash{\textsf{\mdseries #1}}}}}
+\protected\def\Param#1{\texorpdfstring{\eghostguarded{\underline{\smash{\textsf{\mdseries #1}}}}}{#1}}
+\def\Pkg#1{\texorpdfstring{\textsf{#1}}{#1}} % packages/classes
\usepackage{luatexja-ruby,showexpl,booktabs}
-\definecolor{mygreen}{rgb}{0,0.5,0}
+\usepackage{luacolor,lua-ul}
+\definecolor{green}{cmyk}{.75,0,.65,0}
+\definecolor{blue}{cmyk}{1,.45,0,0}
+\definecolor{b_blue}{cmyk}{.3,0,0,0}
\lstset{
preset=\normalsize, basicstyle=\small\ttfamily, basewidth=0.5em,
explpreset={numberstyle=\tiny, numbers=left, numbersep=1em, columns=fixed},
- morecomment=[l]{\%}, commentstyle=\color{mygreen}
+ morecomment=[l]{\%}, commentstyle=\color{green}
}
+\newunderlinetype\beginActIntr{\color{blue}\leaders\vrule height -.16\zh depth .24\zh}
+\NewDocumentCommand\ActIntr{+m}{{\beginActIntr#1}}
\makeatletter\let\SX@Info=\relax\makeatother
\fboxsep=0mm
@@ -37,41 +43,23 @@
allcolors=blue,
pdftitle={luatexja-ruby.sty}
}
-\def\Node#1#2{\,\vcenter{\hbox{\fboxsep=1pt\fbox{\vbox{\small\halign{\hfil##\hfil\cr
- #1\mathstrut\cr\noalign{\smallskip\hrule height.4pt\smallskip}\strut\<#2\cr}}}}}\,}
+\makeatletter
+\def\Node{\@ifstar{\@Node[b_blue]}{\@Node[white]}}
+\def\@Node[#1]#2#3{\,\vcenter{\hbox{\fboxsep=1pt\fcolorbox{black}{#1}{%
+ \vbox{\small\halign{\hfil##\hfil\cr
+ #2\mathstrut\cr\noalign{\smallskip\hrule height.4pt\smallskip}\strut\<#3\cr}}}}}\,}
\def\LuaTeX{Lua\TeX}
% 三分ルビ用
\DeclareFontShape{JY3}{mc}{mc}{n}{<-> [0.92487]
HaranoAjiMincho-Regular:extend=0.67;jfm=ujisc33}{}
-%%%% \ltjruby 内に \texorpdfstring + | 除去を仕込む
-\RequirePackage{xparse}
-\makeatletter
-\def\removevert#1{\if\noexpand#1|%
- \expandafter\expandafter\expandafter\@firstofone\expandafter{%
- \else\ifnum0=1}\fi#1%
- \ifx#1\relax\expandafter\@gobble%
- \else\expandafter\@firstofone
- \fi{\expandafter\@firstofone
- \fi
- \removevert}}
-\makeatother
-\ExplSyntaxOn
-\cs_set_eq:NN \myorigltjruby \ltjruby
-\DeclareExpandableDocumentCommand {\ltjruby} { O{} m m } {%
- \texorpdfstring {
- \exp_not:n { \myorigltjruby[#1]{#2}{#3} }
- } {
- \removevert #2 \relax
- }
-}
-\cs_set_eq:NN \ruby \ltjruby
-\ExplSyntaxOff
-%%%%
-
\makeatletter
+\let\origunderscore=\_
+\def\_{\ifx\f@family\ttdefault\origunderscore\else
+ \leavevmode \kern .06em\vbox {\hrule \@width .333em}\fi}
\def\figureautorefname{図}
+\def\tableautorefname{表}
\def\HyRef@autoref#1#2{%
\begingroup
\Hy@safe@activestrue
@@ -79,14 +67,29 @@
\endgroup\eghostguarded{}%" 欧文ゴースト
}
-\title{\textsf{luatexja-ruby}パッケージ}
+\title{\Pkg{luatexja-ruby}パッケージ}
\author{\LuaTeX-jaプロジェクトチーム}
\date{\texttt{\csname ver@luatexja-ruby.sty\endcsname}(\today)}
\makeatletter
-\def\mybox#1{\unskip\ {\Large \fboxrule=0.1mm\fboxsep=0mm\fcolorbox{cyan}{white}{\hbox{\<\ltjjachar#1\<}}}\ }
-
+\def\mybox{\@ifnextchar({\mybox@@{2}{0}}{%
+ \@ifnextchar){\mybox@@{0}{2}}{\@ifnextchar={\mybox@@{1}{1}}{\mybox@@{0}{0}.}}}}
+\def\mybox@@#1#2#3#4{\unskip\ {\Large \fboxrule=0.1mm\fboxsep=0mm%
+ \ifnum#1>0\fcolorbox{blue}{b_blue}{\vrule width 0pt height .88\zh depth .12\zh\kern#1\dimexpr.25\zw}\kern-\fboxrule\fi%
+ \fcolorbox{blue}{white}{\hbox{\<\ltjjachar#4\<}}%
+ \ifnum#2>0\kern-\fboxrule\fcolorbox{blue}{b_blue}{\vrule width 0pt height .88\zh depth .12\zh\kern#2\dimexpr.25\zw}\fi%
+}\ }
+
+\def\RULER#1#2{%
+ \noindent
+ \rlap{\smash{\phantom{#1}\ \color{cyan}%
+ \@tempcnta=0
+ \loop\ifnum\@tempcnta<20
+ \kern-.05mm\vrule height \dimexpr-\baselineskip+0.88\zh depth \dimexpr #2\baselineskip+0.12\zh width0.1mm\kern-.05mm%
+ \kern\zw\advance\@tempcnta 1
+ \repeat
+ }}\ignorespaces
+}
\begin{document}
-
\catcode`\<=13
\def<#1>{{\rmfamily\mdseries\itshape$\langle$#1\/$\rangle$}}
%\fontsize{13.19873}{15}\selectfont%
@@ -94,7 +97,7 @@
\maketitle
\begin{abstract}
-\textsf{luatexja-ruby}パッケージは,\LuaTeX-jaの機能を利用してルビの組版処理を行う追加パッケージである.
+\Pkg{luatexja-ruby}パッケージは,\LuaTeX-jaの機能を利用してルビの組版処理を行う追加パッケージである.
\LuaTeX, \LuaTeX-jaの内部処理に割り込むことにより,熟語ルビ中の行分割や,
行頭形・行中形・行末形の自動検出,また進入許容量の自動設定などを可能とした.
@@ -105,15 +108,15 @@ v0.3より前とは親文字の高さの扱いが変わっている(\texttt{ba
\tableofcontents
\newpage
-\section{\ltjruby{利|用|方|法}{り|よう|ほう|ほう}}
+\section[利用方法]{\ltjruby{利|用|方|法}{り|よう|ほう|ほう}}
パッケージ読み込みは,\verb+\usepackage{luatexja-ruby}+ で良く,
必要ならば自動的に\LuaTeX-ja本体を読み込む.
plain \LuaTeX でのロードはまだサポートしておらず,
\LaTeXe のみサポートしている.
-\subsection{\ltjruby{用|語}{よう|ご}}
+\subsection[用語]{\ltjruby{用|語}{よう|ご}}
「進入(intrusion)」「突出(protrusion)」という用語は,
-\href{http://zrbabbler.sp.land.to/pxrubrica.html}{\textsf{pxrubrica}パッケージ}%
+\href{http://zrbabbler.sp.land.to/pxrubrica.html}{\Pkg{pxrubrica}パッケージ}%
での用法に準ずる.
\begin{quote}
@@ -125,15 +128,15 @@ plain \LuaTeX でのロードはまだサポートしておらず,
なお,本パッケージでは親文字と直前・直後の文字の間に0でない和文処理グルー
\footnote{JFMで指定されたグルーや,標準の和文間空白(\Param{kanjiskip}),標準の和欧文間空白(\Param{xkanjiskip}).}%
-がくることも考慮しているため,「\emph{前後の文字への}\underline{進入(許容)量}」と「進入(許容)量」とは異なる可能性がある.
+がくることも考慮しているため,「\emph{前後の文字への}\ActIntr{進入(許容)量}」と「進入(許容)量」とは異なる可能性がある.
この文書では次のように称する:
\begin{description}
- \item[\underline{文字進入量}] 前後の文字へルビ文字が実際にかかる長さ.常に下線を引くことにする.
+ \item[\ActIntr{文字進入量}] 前後の文字へルビ文字が実際にかかる長さ.常に下線を引くことにする.
\item[進入量] 前後の文字,およびそれとルビの間の和文処理グルーにかかる長さ
\end{description}
-多くの場合,和文処理グルーは0以上の長さのため,進入量は\underline{文字進入量}以上である.
+多くの場合,和文処理グルーは0以上の長さのため,進入量は\ActIntr{文字進入量}以上である.
-例えば次の例では,直前の文字「来」への\underline{前文字進入量}は0であるが,
+例えば次の例では,直前の文字「来」への\ActIntr{前文字進入量}は0であるが,
前進入量は(和欧文間空白にかかる分まで含めるので)正である.
\begin{quote}
\Large
@@ -145,15 +148,15 @@ plain \LuaTeX でのロードはまだサポートしておらず,
\cs{zw},~\cs{zh}命令で取得できる値を表す.多くの場合は両者は同じ値であるが,いつでもそうであるという保証はない.
「ルビ全角幅」「ルビ全角高さ」についても同様である.
-\subsection{\ruby{命|令}{めい|れい}}
+\subsection[命令]{\ruby{命|令}{めい|れい}}
\label{ssec:ltjruby}
\paragraph{\cs{ltjruby}}
-ルビ出力用命令の本体.\verb+\ruby+ という別名を定義している.
+ルビ出力用命令の本体.\cs{ruby}という別名を定義している.
\begin{quote}
\ttfamily \textbackslash ltjruby[<option>]\{親|文|字\}\{おや|も|じ\}
\end{quote}
のように親文字→ルビの順序で指定する.
-第2・第3引数内の「\verb+|+」はグループの区切りを表す.詳細は\ref{ssec:grp}\nobreak 小節を参照.
+第2・第3引数内の「\texttt{|}」はグループの区切りを表す.詳細は\ref{ssec:grp}\nobreak 小節を参照.
\medskip
<option>には以下の内容をkey-valueリストで指定可能である.
@@ -162,16 +165,16 @@ plain \LuaTeX でのロードはまだサポートしておらず,
\begin{description}
\def\makelabel#1{\ttfamily\bfseries #1}
-\item[pre=<real>] \underline{前文字進入許容量}をルビ全角単位で指定.
+\item[pre=<real>] \ActIntr{前文字進入許容量}をルビ全角単位で指定.
負の長さを指定した場合は,ルビの状況や直前の文字に応じた自動指定を意味する.
既定値は負(つまり,自動指定).
-\item[post=<real>] 同様に,\underline{後文字進入許容量}を指定する.既定値は負(自動指定).
+\item[post=<real>] 同様に,\ActIntr{後文字進入許容量}を指定する.既定値は負(自動指定).
\item[mode] 進入処理のモードを表すbit vector.下位2\,bitは,\texttt{pre}や
\texttt{post}が負である場合にしか効力を発揮しない.既定値は$(00001)_2 = 1$.
\begin{description}
- \item[bit 0] 前後の\underline{文字への進入}を無効にするならば0,有効にするならば1.
+ \item[bit 0] 前後の\ActIntr{文字への進入}を無効にするならば0,有効にするならば1.
\item[bit 1] 前進入許容量$B$と後進入許容量$A$が異なった場合,
そのまま処理する場合は0,小さい方に揃えるならば1.
\item[bit 2--4] ルビ文字の突出量から実際の前・後進入量の計算方法を指定する.
@@ -186,44 +189,41 @@ plain \LuaTeX でのロードはまだサポートしておらず,
\item[stretchruby=\{<left>\}\{<middle>\}\{<right>\}] 親文字の合計長が
ルビ文字の合計長より長い時に,ルビ文字の前・間・後に入れる空白の割合であり,
それぞれ0--7の自然数で指定する.
-既定値は\ \verb+{1}{2}{1}+ である.
+既定値は\>\verb+{1}{2}{1}+\>である.
<left>はルビ文字の先頭までの空き量,<middle>はルビ文字間の空き量,<right>はルビ文字の末尾からの
空き量(の比)を表す.以下が例である.
-\begin{LTXexample}[width=0.3\textwidth, preset=\Large]
+\begin{LTXexample}[width=0.25\textwidth, preset=\Large]
\ltjruby[stretchruby=123,maxmargin=2]%
{◯◯◯◯}{◆◆}
\end{LTXexample}
\item[stretch=\{<left>\}\{<middle>\}\{<right>\}]
行中形でルビ文字の方が長い場合,親文字の前・中・後に入れる空白の割合.
-既定値は\ \verb+{1}{2}{1}+ である.それ以外の代表的な値としては,例えば次のようなものがある.
+既定値は\>\verb+{1}{2}{1}+\>である.それ以外の代表的な値としては,例えば次のようなものがある.
\begin{description}
-\item[親文字均等割禁止] \verb+{1}{0}{1}+\ など<middle>を0にした値
+\item[親文字均等割禁止] \verb+{1}{0}{1}+\>など<middle>を0にした値
\item[前突出禁止] \verb+{0}{1}{1}+
\item[後突出禁止] \verb+{1}{1}{0}+
\end{description}
\item[stretchbol=\{<left>\}\{<middle>\}\{<right>\}] 行頭形に対する\texttt{stretch}と同様の指定.
-既定値は\ \verb+{0}{1}{1}+ である.
+既定値は\>\verb+{0}{1}{1}+\>である.
\item[stretcheol=\{<left>\}\{<middle>\}\{<right>\}] 行末形に対する\texttt{stretch}と同様の指定.
-既定値は\ \verb+{1}{1}{0}+ である.
+既定値は\>\verb+{1}{1}{0}+\>である.
\item[maxmargin=<real>] 親文字の方がルビより長い時に,ルビの先頭と親文字の先頭,及び
-ルビ末尾と親文字の末尾の間に許される最大の空白量.\textbf{親文字全角幅単位}で指定し,既定値は0.5.
+ルビ末尾と親文字の末尾の間に許される最大の空白量.\emph{親文字全角幅単位}で指定し,既定値は0.5.
\medskip
\item[size=<real>] ルビ文字の親文字に対する大きさ.既定値は0.5.
\item[intergap=<real>] ルビ文字と親文字との空きを親文字全角高さ単位で指定.既定値は0.
\item[rubysmash=<bool>] ルビの高さを0にするか.既定値は偽.次が例である.
-\begin{LTXexample}[width=0.3\textwidth, preset=\Large]
+\begin{LTXexample}[width=0.25\textwidth, preset=\Large]
\ \vrule width 0pt height 2\zw depth 1\zw
\frame{\ltjruby[rubysmash=false]{本}{ほん}}\
\frame{\ltjruby[rubysmash=true]{本}{ほん}}\
-\frame{\ltjruby[rubysmash=false,intergap=0.2]
- {本}{ほん}}\
-\frame{\ltjruby[rubysmash=true,intergap=0.2]
- {本}{ほん}}\
-\frame{\ltjruby[rubysmash=false,intergap=-1.5]
- {本}{ほん}}
+\frame{\ltjruby[rubysmash=false,intergap=0.2]{本}{ほん}}\
+\frame{\ltjruby[rubysmash=true,intergap=0.2]{本}{ほん}}\
+\frame{\ltjruby[rubysmash=false,intergap=-1.5]{本}{ほん}}
\end{LTXexample}
\item[ybaseheight=<real>] 非負の値が指定された場合,
\emph{縦組以外での}親文字の高さを親文字全角高さの<real>倍と強制的に固定する.
@@ -233,7 +233,7 @@ plain \LuaTeX でのロードはまだサポートしておらず,
既定値は$0.5$.
\item[baseheight=<real>] \texttt{ybaseheight},\texttt{tbaseheight}を同時に指定したことと同義.
次の例や\autoref{fig:ruby_vertical}を参照.
-\begin{LTXexample}[width=0.3\textwidth, preset=\Large]
+\begin{LTXexample}[width=0.25\textwidth, preset=\Large]
\noindent
\ltjruby[baseheight=0.88]{本}{ほん}\
\ltjruby[baseheight=0.88]{dvi}{ディーヴィーアイ}\\
@@ -252,7 +252,7 @@ plain \LuaTeX でのロードはまだサポートしておらず,
既定値は$0.5$.
\item[rubydepth=<real>] \texttt{yrubydepth},\texttt{trubydepth}を同時に指定したことと同義.
次の例や\autoref{fig:ruby_vertical}を参照.
-\begin{LTXexample}[width=0.3\textwidth, preset=\Large]
+\begin{LTXexample}[width=0.25\textwidth, preset=\Large]
\noindent
\ltjruby[rubydepth=0.12]{◆}{ほん}\
\ltjruby[rubydepth=0.12]{◆}{pdf}\
@@ -305,20 +305,43 @@ plain \LuaTeX でのロードはまだサポートしておらず,
\item[kenten=<command>]各文字につく圏点の出力命令を指定する.
- 既定値は「\verb+\textbullet+」である.
+ 既定値は「\cs{textbullet}」である.
\item[fontcmd=<command>]ルビ用のフォント切り替え命令を指定する.
このキーの内容が実行された後に\ \verb+\fontsize...\selectfont+\ が実行されるので,
-このキーの指定では最後に\ \verb+\selectfont+ を加える必要はない.
+このキーの指定では最後に\cs{selectfont}を加える必要はない.
-この \texttt{fontcmd} キーの内容は多くの回数実行される.
-例えば,\textsf{luatexja-fontspec}パッケージを用いてOpenTypeフォントを用いる場合,
+この \texttt{fontcmd}キーの内容は多くの回数実行される.
+例えば,\Pkg{luatexja-fontspec}パッケージを用いてOpenTypeフォントを用いる場合,
\begin{lstlisting}
fontcmd=\addjfontfeatures{Style=Ruby}
\end{lstlisting}
のようにしてルビ用字形を用いることが可能だが,現在の実装ではタイプセットに時間がかかるようになる.
+ \item[always\_highlight\_protrusion=<bool>] この\cs{ltjruby}命令が\Pkg{lua-ul}パッケージの提供する「下線」命令
+ \footnote{\cs{underLine}, \cs{highLight},およびそのほか.}の引数に含まれていた場合,
+ ルビの突出部(ただし\ActIntr{文字進入部分}は除く)に「下線」がいつでも引かれるか否か.
+
+ 既定値は偽.この場合は,突出部に「下線」が引かれるのは,ルビの前後の文字も同じ「下線」命令の有効範囲になっているときに
+ 限られる.\autoref{fig:always-highlight-protrusion}を参照.
\end{description}
+\begin{figure}[t]
+\begin{LTXexample}[width=13.2\zw, preset=\large]
+ \ltjsetruby{stretchbol=121,stretcheol=121}
+ あ\highLight{\ruby{◆}{□□□□□□□□}}あ
+ い\highLight{\ruby{◆}{□□□□□□□□}い}\\
+ \highLight{う\ruby{◆}{□□□□□□□□}}う\\\kern7\zw
+ あ\highLight{\ruby{◆|◆}{□□□□□|□□□□□}}あ
+
+ \ltjsetruby{always_highlight_protrusion}
+ あ\highLight{\ruby{◆}{□□□□□□□□}}あ
+ い\highLight{\ruby{◆}{□□□□□□□□}い}\\
+ \highLight{う\ruby{◆}{□□□□□□□□}}う\\\kern7\zw
+ あ\highLight{\ruby{◆|◆}{□□□□□|□□□□□}}あ
+\end{LTXexample}
+ \caption{\texttt{always\_highlight\_protrusion}キー有無による組版結果の違い}
+ \label{fig:always-highlight-protrusion}
+\end{figure}
\medskip
次の2つは,以上で説明した複数のオプションを一度に設定するためのものである.
@@ -337,7 +360,7 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
\end{description}
\paragraph{\cs{ltjsetruby}\{<option>\}}
-<option>の既定値を指定する.\textsf{luatexja-ruby}読み込み時の値は
+<option>の既定値を指定する.\Pkg{luatexja-ruby}読み込み時の値は
各項目の所で既に説明してあるが,次のようになっている.
\begin{verbatim}
pre=-1, post=-1, mode=1,
@@ -346,24 +369,51 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
maxmargin=0.5, size=0.5, intergap=0, rubysmash=false,
kenten=\textbullet, fontcmd=\relax, ybaseheight=0.88, tbaseheight=0.5,
yrubydepth=0.12, trubydepth=0.5,
- intrude_jfmgk, intrude_kanjiskip, intrude_xkanjiskip
+ intrude_jfmgk, intrude_kanjiskip, intrude_xkanjiskip,
+ always_highlight_protrusion=false
\end{verbatim}
\paragraph{\cs{ltjsetparamater} に追加されるキー}
\begin{description}
\item[\Param{rubypreintrusion}\ttfamily =\{<chr\_code>, <pre\_int>\}]
- 文字<chr\_code> に,その\textbf{直後}のルビによって掛けられるルビ文字列の最大長
- (つまり,\underline{前文字進入許容量})をルビ全角幅単位で指定.
-
- \textbf{v0.5以降での変更点}:もし$-a$~($a>0$)を指定した場合は,実際の\underline{前文字進入許容量}は$a$となるが,
- 文字<chr\_code>の前のJFMグルーに進入が発生した場合には,\underline{前文字進入許容量}が0でなかった場合は,の後のJFMグルーにはルビの進入は発生しない.
-
+ 文字<chr\_code> に,その\emph{直後}のルビによって掛けられるルビ文字列の最大長
+ (つまり,\ActIntr{前文字進入許容量})をルビ全角幅単位で指定.
+
+ \emph{v0.5以降での変更点}:もし$-a$~($a>0$)を指定した場合は,次のようになる.
+ \begin{itemize}
+ \item 実際の\ActIntr{前文字進入許容量}は$a$となる.
+ \item ただし,文字<chr\_code>の直前のルビによって
+ 文字の前のJFMグルーに進入が発生した場合,文字<chr\_code>の直後のルビの前進入は発生しない.
+ \end{itemize}
+ 後者の状況の例を以下に載せる.
+\begin{LTXexample}[width=0.25\textwidth, preset=\Large]
+\ltjsetparameter{rubypreintrusion={`\【,-1}}
+\ruby{鴎}{かもめ}【\ruby{隼}{はやぶさ}
+% 「\ruby{鴎}{かもめ}」は,「【」直前のJFMグルーに
+% ルビ半角だけ進入している
+% →「\ruby{隼}{はやぶさ}」の「【」への進入はない.
+\end{LTXexample}
+
+
\item[\Param{rubypostintrusion}\ttfamily =\{<chr\_code>, <post\_int>\}]
- 文字<chr\_code> に,その\textbf{直前}のルビによって掛けられるルビ文字列の最大長
- (つまり,\underline{後文字進入許容量})をルビ全角幅単位で指定.
-
- \textbf{v0.5以降での変更点}:もし$-a$~($a>0$)を指定した場合は,実際の\underline{後文字進入許容量}は$a$となるが,
- 実際の\underline{後文字進入量}が0でなかった場合は,文字<chr\_code>の後のJFMグルーにはルビの進入は発生しない.
+ 文字<chr\_code>に,その\emph{直前}のルビによって掛けられるルビ文字列の最大長
+ (つまり,\ActIntr{後文字進入許容量})をルビ全角幅単位で指定.
+
+ \emph{v0.5以降での変更点}:もし$-a$~($a>0$)を指定した場合は,次のようになる.
+ \begin{itemize}
+ \item 実際の\ActIntr{後文字進入許容量}は$a$となる.
+ \item ただし,文字<chr\_code>の直前のルビによってこの文字への実際の\ActIntr{後文字進入}が
+ 発生した場合,文字<chr\_code>の直後のJFMグルーにはルビの前進入は発生しない.
+ \end{itemize}
+ 今回も後者の状況の例を以下に載せる.
+\begin{LTXexample}[width=0.25\textwidth, preset=\Large]
+\ltjsetparameter{rubypostintrusion={`\】,-1}}
+\ruby{鴎}{かもめ}】\ruby{隼}{はやぶさ}\\
+\hskip.25\zw 鴎】隼\\% 比較用
+% 「\ruby{鴎}{かもめ}」は,「】」にルビ半角だけ進入
+% →「\ruby{隼}{はやぶさ}」は「】」直前のJFMグルー
+% には進入しない.
+\end{LTXexample}
\end{description}
以下の文字は\Param{rubypreintrusion},~\Param{rubypostintrusion}とも初期値は1である:
\begin{quote}
@@ -374,51 +424,51 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
\end{quote}
次に,以下の文字は\Param{rubypreintrusion},~\Param{rubypostintrusion}とも初期値は0.5である:
\begin{quote}
- 中点類\@for\@tmp:=`\・,`\:,`\; \do{\mybox{\@tmp}}
+ 中点類\@for\@tmp:=`\・,`\:,`\; \do{\mybox={\@tmp}}
\end{quote}
また,以下の文字は\Param{rubypreintrusion}のみ初期値は$-1$である:
-\begin{quote}
+\begin{itemize}
+ \item
始め括弧類
- \@for\@tmp:=`\‘,`\“,`\〈,`\《,`\「,`\『,`\【,`\〔,`\〖,`\〘,`\〝,`\(,`\[,`\{,`\⦅ \do{\mybox{\@tmp}}\unskip,
- 「文字コード$-1$の文字
- \footnote{段落開始の\ \cs{parindent}分インデントを表す.
+ \@for\@tmp:=`\‘,`\“,`\〈,`\《,`\「,`\『,`\【,`\〔,`\〖,`\〘,`\〝,`\(,`\[,`\{,`\⦅ \do{\mybox({\@tmp}}\unskip
+ \item 「文字コード$-1$の文字
+ \footnote{ここでは段落開始の\ \cs{parindent}分インデントを表す.
通常の\LuaTeX-jaにおける指定では「文字コード$-1$」は文中数式境界を表していることに注意.}」
-\end{quote}
+\end{itemize}
さらに,以下の文字は\Param{rubypostintrusion}のみ初期値は$-1$である:
-\begin{quote}
- 閉じ括弧類
- \@for\@tmp:=`\’,`\”,`\〉,`\》,`\」,`\』,`\】,`\〕,`\〗,`\〙,`\〟,`\),`\],`\},`\⦆ \do{\mybox{\@tmp}}\unskip,
- 句読点類\@for\@tmp:=`\、,`\,,`\。,`\. \do{\mybox{\@tmp}}
-\end{quote}
+\begin{itemize}
+ \item 閉じ括弧類
+ \@for\@tmp:=`\’,`\”,`\〉,`\》,`\」,`\』,`\】,`\〕,`\〗,`\〙,`\〟,`\),`\],`\},`\⦆ \do{\mybox){\@tmp}}
+ \item 句読点類\@for\@tmp:=`\、,`\,,`\。,`\. \do{\mybox){\@tmp}}
+\end{itemize}
\makeatother
以上で記述されていない\Param{rubypreintrusion},~\Param{rubypostintrusion}の初期値はすべて0である.
-
\paragraph{\cs{ltjkenten}}
圏点を出力する命令であり,
\begin{quote}
\ttfamily\cs{ltjkenten}[<option>]\{親文字\}
\end{quote}
-のように使用する.複数文字に圏点をつける場合でも,\verb+\ltjruby+ のように「\texttt{|}」を使って
-文字を区切る\textbf{必要はない}(\verb+\kenten+内では「\texttt{|}」は特別な意味を持たない)
+のように使用する.複数文字に圏点をつける場合でも,\cs{ltjruby}のように「\texttt{|}」を使って
+文字を区切る\emph{必要はない}(\cs{kenten}内では「\texttt{|}」は特別な意味を持たない)
ことに注意してほしい.
圏点として出力される文字は\texttt{kenten}キーによって指定し,
圏点自身の大きさは(ルビと同様に)\texttt{size}キーで指定する.
-\subsection{グループの\ruby{指|定}{し|てい}}
+\subsection[グループの指定]{グループの\ruby{指|定}{し|てい}}
\label{ssec:grp}
\begin{quote}
\ttfamily\cs{ltjruby}[<option>]\{親|文|字\}\{おや|も|じ\}
\end{quote}
-のように,\verb+\ltjruby+\ の第2・第3引数内の「\verb+|+」はグループの区切りを表す.
+のように,\cs{ltjruby}の第2・第3引数内の「\texttt{|}」はグループの区切りを表す.
グループの数は両者で一致しなければならず,
\verb+\ltjruby{紋章}{もん|しよう}+ のようには\emph{できない}.
1グループのみのルビ(単純グループルビ)はグループルビとして組まれる.そのため,
もしモノルビを使いたければ,面倒でも
-\begin{LTXexample}[width=0.4\textwidth, preset=\Large]
+\begin{LTXexample}[width=0.25\textwidth, preset=\Large]
の\ltjruby{紋}{もん}\ltjruby{章}{しよう}が
\end{LTXexample}
のように,複数回使用すること.
@@ -427,7 +477,7 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
実際には\TeX での長さの計算誤差($2^{-16}$~ptの整数倍として計算していることによる)を考慮し,
親文字全角の$1/1000$だけルビ文字列が長くなることを許容している.
}であれば,
-単純グループルビの並びとして扱われる.すなわち,次ページ冒頭の2行は全くの等価となる.
+単純グループルビの並びとして扱われる.すなわち,次の2行は全くの等価となる.
\begin{verbatim}
\ltjruby{普|通|車}{ふ|つう|しや}
\ltjruby{普}{ふ}\ltjruby{通}{つう}\ltjruby{車}{しや}
@@ -439,20 +489,23 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
\begin{verbatim}
…の\ltjruby{表|現|力}{ひよう|げん|りよく}は…
\end{verbatim}
-という入力からは得られる組版結果は,次のいずれかになる.
-\begin{center}
- \begin{tabular}{ll}
+という入力からは得られる組版結果は,\autoref{tab:group_ruby}のいずれかになる.
+\begin{table}[t]
+\centering\small
+ \begin{tabular}{lll}
\toprule
改行なし(行中形)&\Large …の\ltjruby{表|現|力}{ひよう|げん|りよく}は…\\
直前で改行&\Large \vrule \ltjruby[stretch=011]{表|現|力}{ひよう|げん|りよく}は…\\
-&\Large …の\ltjruby[stretch=110]{表}{ひよう}
- \vrule\ltjruby[stretch=011]{現|力}{げん|りよく}は…\\
-&\Large …の\ltjruby[stretch=110]{表|現}{ひよう|げん}
- \vrule\ltjruby[stretch=011]{力}{りよく}は…\\
+ 途中で改行&\Large …の\ltjruby[stretch=110]{表}{ひよう}
+ \vrule\ltjruby[stretch=011]{現|力}{げん|りよく}は…
+ &\Large …の\ltjruby[stretch=110]{表|現}{ひよう|げん}
+ \vrule\ltjruby[stretch=011]{力}{りよく}は…\\
直後に改行&\Large …の\ltjruby[stretch=110]{表|現|力}{ひよう|げん|りよく}\vrule\\
\bottomrule
\end{tabular}
-\end{center}
+ \caption{可動グループルビの組まれ方(縦線は行の境目)}
+ \label{tab:group_ruby}
+\end{table}
\begin{itemize}
\item これらの行分割によってペナルティは発生しない.
\item 上記の例で見られるように,
@@ -481,44 +534,44 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
\item \cs{kenten}がすでに定義されていない場合に限り,\cs{kenten}が\cs{ltjkenten}の別名として定義される.
\end{itemize}
-\section{\ruby{注|意|点}{ちゆう|い|てん}}
+\section[注意点]{\ruby{注|意|点}{ちゆう|い|てん}}
\paragraph{前後からのルビ文字のはみ出し1}
「日本語組版処理の要件」の図117\footnote{2020-08-11版での番号.2012-04-03版では図3.82.}のように,
前後からのルビ文字のはみ出しが繋がらないようにする処理が組み込まれている.
例えば,
-\begin{LTXexample}[preset=\Large,width=0.3\textwidth]
+\begin{LTXexample}[preset=\Large,width=0.25\textwidth]
\ltjruby{陵}{りよう}と\ltjruby{陵}{みささぎ}\\
\ltjruby{陵}{りよう}と\ltjruby[pre=1]{陵}{みささぎ}
\end{LTXexample}
において,1行目右側の「\ltjruby{陵}{みささぎ}」のルビが前の「と」にかかる量は次のように決まる:
\begin{enumerate}
-\item 1回目の実行では,行分割前に「\ltjruby{陵}{りよう}」の\underline{後文字進入量}は前もって知ることはでき
+\item 1回目の実行では,行分割前に「\ltjruby{陵}{りよう}」の\ActIntr{後文字進入量}は前もって知ることはでき
ない.そのため,「\ltjruby{陵}{りよう}」は行中形で組まれるものと想定し,
「\ltjruby{陵}{みささぎ}」の前文字進入許容量は
\[
\underbrace{0.5\,\mathrm{zw}}_{\text{元々の許容量}}
--\underbrace{0.25\,\mathrm{zw}}_{\text{前のルビの\underline{後文字進入量}(行中形)}}=0.25\,\mathrm{zw}
+-\underbrace{0.25\,\mathrm{zw}}_{\text{前のルビの\ActIntr{後文字進入量}(行中形)}}=0.25\,\mathrm{zw}
\]
-となる.行分割後,「\ltjruby{陵}{りよう}」の実際の\underline{後文字進入量}は
+となる.行分割後,「\ltjruby{陵}{りよう}」の実際の\ActIntr{後文字進入量}は
<jobname>\texttt{.ltjruby}ファイルに記述される.
-\item 2回目以降の実行では,<jobname>\texttt{.ltjruby}ファイルに保存された「\ltjruby{陵}{りよう}」の\underline{後文字進入量}
-を用いて,「\ltjruby{陵}{みささぎ}」の\underline{前文字進入許容量}を次のように計算する:
+\item 2回目以降の実行では,<jobname>\texttt{.ltjruby}ファイルに保存された「\ltjruby{陵}{りよう}」の\ActIntr{後文字進入量}
+を用いて,「\ltjruby{陵}{みささぎ}」の\ActIntr{前文字進入許容量}を次のように計算する:
\[
\underbrace{0.5\,\mathrm{zw}}_{\text{元々の許容量}}
--\underbrace{0.25\,\mathrm{zw}}_{\text{前のルビの\underline{後文字進入量}(from \texttt{.ltjruby})}}=0.25\,\mathrm{zw}.
+-\underbrace{0.25\,\mathrm{zw}}_{\text{前のルビの\ActIntr{後文字進入量}(from \texttt{.ltjruby})}}=0.25\,\mathrm{zw}.
\]
\end{enumerate}
-<jobname>\texttt{.ltjruby}ファイルに保存する際,各 \verb+\ltjruby+ 命令の呼び出しを識別するキーが必要になるが,
-そのキーとしては単純に「何個目の \verb+\ltjruby+ 命令か」である.
+<jobname>\texttt{.ltjruby}ファイルに保存する際,各\cs{ltjruby}命令の呼び出しを識別するキーが必要になるが,
+そのキーとしては単純に「何個目の\cs{ltjruby}命令か」である.
なお,以上の処理は,1行目と2行目を比較すれば分かるように,
-「\ltjruby{陵}{みささぎ}」の\emph{\underline{前文字進入許容量}指定(\texttt{pre})が自動}になっている場合のみ
-実施される.
-
+「\ltjruby{陵}{みささぎ}」の\emph{\ActIntr{前文字進入許容量}指定が自動(\texttt{pre}の値が負)}に
+なっている場合のみ実施される.
+\newpage
\paragraph{前後からのルビ文字のはみ出し2}
-また,本パッケージのv0.**以降では,「日本語組版処理の要件」にある
+また,本パッケージでは,「日本語組版処理の要件」にある
\begin{quote}
後ろにくる終わり括弧類,句点類若しくは読点類,又は前にくる始め括弧類には,
最大でルビ文字サイズの全角までルビ文字を掛けてもよい.
@@ -533,17 +586,17 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
これは,段落の「本当の」末尾には \verb+\penalty10000\parfillskip+ があるためで,
ルビ処理用に作った最後のグルー(\ref{ssec:impl}\nobreak 小節の説明では$g_2$)が消去されないことによる.
-\verb+\parfillskip+ の長さ(や,場合によっては \verb+\rightskip+)を実測し,
+\cs{parfillskip}(や,場合によっては\cs{rightskip})の長さを実測し,
それによって処理を変えるのも可能だが,そのようなことはしなかった.
-段落がルビで終わることは普通ない(最低でも句点が続くだろう)と思うからである.
+段落がルビで終わることは普通ない(最低でも句点が続くだろう)と考えられるからである.
\paragraph{段落先頭のルビ}
同様に,段落先頭のルビは行頭形にはならない.
-\texttt{pre}が負(つまり,自動指定)のとき,段落最初の \verb+\parindent+ 分への
-進入は可能である.ここ\verb+\parindent+ 分のインデントへの\underline{進入許容量}は
+\texttt{pre}が負(つまり,自動指定)のとき,段落最初の\cs{parindent}分への
+進入は可能である.この\cs{parindent}分のインデントへの\ActIntr{進入許容量}は
「文字$-1$」に対する\Param{rubypreintrusion}(既定値は1,ルビ全角単位)と
-\ \verb+\parindent+ の長さのうち小さい方である.
-\begin{LTXexample}[preset=\Large,width=0.4\textwidth]
+\cs{parindent}の長さのうち小さい方である.
+\begin{LTXexample}[preset=\Large,width=0.25\textwidth]
\parindent1\zw\noindent 012345\par
\ltjruby{陵}{みささぎ}は……\par
\ltjruby{承}{うけたまわ}り……\par
@@ -561,54 +614,52 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
例えば,標準設定での中黒「・」の直後のルビからの中黒への進入許容量は
\begin{quote}
- 中黒「・」への\underline{前進入許容量}はルビ全角の半分で,
+ 中黒「・」への\ActIntr{前進入許容量}はルビ全角の半分で,
中黒本体の後の四分空きには進入可能
\end{quote}
となっている.そのため,下の例の1, 2行目ともルビの前進入量は0.5\,zwとなる.
しかし,2行目では詰め量の0.5\,zwがほとんど中黒周囲の四分空きで負担されるため,
実際には「中黒本体にほぼ0.5\,zwが進入する」という望ましくない結果が得られている.
+ \typeout{===========}
\begin{LTXexample}[preset=\Large,width=0.4\textwidth]
\leavevmode\hbox{あ・\ltjruby{◆}{◆◆◆◆}漢字}\\
\hbox spread-0.5\zw{あ・\ltjruby{◆}{◆◆◆◆}漢字}
\end{LTXexample}
-また,次の例では2行目では前後の\underline{文字進入量}がルビ1字分と等しくなっているが,
-3行目のように1全角伸ばすという調整が行われた後は\underline{文字進入量}が不揃いになってしまっている.
+また,次の例では2行目では前後の\ActIntr{文字進入量}がルビ1字分と等しくなっているが,
+3行目のように1全角伸ばすという調整が行われた後は\ActIntr{文字進入量}が不揃いになってしまっている.
これはもともと「\texttt{;}」には後側にのみ和欧文間空白が入ることと,3行目ではこの和欧文間空白が
伸びているためである.
-\begin{LTXexample}[preset=\Large,width=0.4\textwidth]
-\leavevmode\hbox{……に%
- \ltjruby{\texttt{;}}{セミコロン}という……}\\
-\hbox spread\zw{……に%
- \ltjruby{\texttt{;}}{セミコロン}という……}
+\begin{LTXexample}[preset=\Large,width=0.25\textwidth]
+\leavevmode\hbox{に\ltjruby{\texttt{;}}{セミコロン}という}\\
+\hbox spread\zw{に\ltjruby{\texttt{;}}{セミコロン}という}
\end{LTXexample}
-\newpage
-\section{\ruby{実|装}{じつ|そう}について}
+\section[実装について]{\ruby{実|装}{じつ|そう}について}
\subsection{進入量の計算}
\label{ssec:calc_intrusion}
ルビ文字を自然に組んだときの幅が親文字のそれより多い場合,
ルビの前後への進入量は次のように決定される.
\begin{enumerate}[label=\emph{Step~\arabic*},labelindent=0pt,itemindent=*,itemsep=\medskipamount]
- \item \underline{前文字進入許容量}$B_0$,\underline{後進入文字許容量}$A_0$の算出.
+ \item \ActIntr{前文字進入許容量}$B_0$,\ActIntr{後進入文字許容量}$A_0$の算出.
ルビ全角の長さを$r$とする.
\begin{enumerate}
- \item \texttt{pre}の指定値が非負であった場合は,それに$r$を掛けたものを$B_0$とする.\\
+ \item \texttt{pre}の指定値が非負であった場合は,それに$r$を掛けたものを$B_0$とする.
+
そうでなかった場合は,「ルビの直前の文字」に対する
- \Param{rubypreintrusion}の値に$r$を掛けたものを$B_0$とする
+ \Param{rubypreintrusion}の絶対値に$r$を掛けたものを$B_0$とする
\footnote{%
なお,「ルビの直前の文字」が段落最初の\cs{parindent}分のインデントであった場合,
$B_0$を,「文字コード$-1$の文字」\Param{rubypreintrusion}の値に$r$を掛けた値と\cs{parindent}のうち
小さい方とする.
- }.
+ }.ここで,もし2つ前の文字がルビで,その直後(つまりいま処理しているルビから見れば直前)
+ の文字へ$a'$だけの進入があった場合,現在のルビについて$B_0←\min(0,B_0-a')$とする.
\item \texttt{post}の指定値が非負であった場合は,それに$r$を掛けたものを$A_0$とする.\\
そうでなかった場合は,「ルビの直後の文字」に対する
- \Param{rubypostintrusion}の値にルビ全角の値を掛けたものを$A_0$とする.
- \item \texttt{mode}の最下位ビット(bit~0)が0であった場合は,$B_0\leftarrow 0$, $A\leftarrow 0$とする.
- \item もし2つ前の文字がルビで,その直後(つまりいま処理しているルビから見れば直前)
- の文字へ$a'$だけの進入があった場合,現在のルビについて$B_0←\min(0,B_0-a')$とする.
+ \Param{rubypostintrusion}の絶対値にルビ全角の値を掛けたものを$A_0$とする.
+ \item \texttt{mode}の最下位ビット(bit~0)が0であった場合は,$B_0\leftarrow 0$, $A_0\leftarrow 0$とする.
\end{enumerate}
\item 前進入許容量$B$,後進入許容量$A$の算出.
\begin{enumerate}
@@ -624,8 +675,8 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
それぞれ$g$の自然長を$B$に加算する.
ルビとその直後の文字との間に和文処理グルーが挿入された場合も同様である.
-
- \item \texttt{mode}の2番目のビット(bit~1)が0であった場合は,$B$,~$A\leftarrow \min(B,A)$とする.
+
+ \item \texttt{mode}の2番目のビット(bit~1)が1であった場合は,$B$,~$A\leftarrow \min(B,A)$とする.
\end{enumerate}
\item 実際の前進入量$b$,後進入量$a$の計算.
@@ -704,21 +755,28 @@ mode=9, stretch={1}{2}{1}, stretchruby={0}{0}{1}
\end{LTXexample}
\begin{enumerate}
-\item \verb|\ltjruby|コマンド自体は,一旦次のnode listを値とするwhatsit~$W$を作って,
-現在の水平リストへと挿入する(必要ならば\verb|\leavevmode|も実行):
+\item \cs{ltjruby}命令自体は,一旦次のnode listを値とするwhatsit~$W$を作って,
+現在の水平リストへと挿入する(必要ならば\cs{leavevmode}も実行):
\[
\Node{whatsit $w$}{value: 2}\longrightarrow
\Node{hlist $s_1$}{「りゆう」}\longrightarrow
\Node{hlist $p_1$}{「流」}\longrightarrow
\Node{hlist $s_2$}{「ちよう」}\longrightarrow
- \Node{hlist $p_2$}{「暢」}
+ \Node{hlist $p_2$}{「暢」}\longrightarrow
+ \Node{kern $k_b$}{}\longrightarrow
+ \Node{kern $k_a$}{}
\]
ここで,最初の$w$の値2は,ルビが2つのパーツ「\ltjruby{流}{りゆう}」「\ltjruby{暢}{ちよう}」からなっていることを
表している.この値を$\mathit{cmp}$とおこう.
$s_i$達の中の文字は既にルビの大きさである.
+
+また,$k_b$,~$k_a$は\texttt{always\_highlight\_protrusion}キーが偽の場合にのみ生成されるが,
+実際に使われるのは行分割後になってからである.\Pkg{lua-ul}対応のためには,和文処理グルーの挿入処理で
+「前・後のクラスタそれぞれとのattributeの共通部分」を保存する必要があり,この段階でノードとして$k_b$,~$k_a$を
+準備している.
\item \LuaTeX-jaの和文処理グルー挿入処理において,
このwhatsit~$W$はまとめて
-「先頭が『流』,最後が『暢』であるようなhboxを \verb|\unhbox| で展開したもの」と扱われる.
+「先頭が『流』,最後が『暢』であるようなhboxを\cs{unhbox}で展開したもの」と扱われる.
言い換えれば,ルビ部分を無視した単なる「流暢」という和文文字の並びとして扱われる\footnote{「流」「暢」の間のグルーは既に入っている,と扱われる.}.
次のサンプルを参照
\begin{LTXexample}[preset=\Large]
@@ -729,7 +787,10 @@ $s_i$達の中の文字は既にルビの大きさである.
\end{LTXexample}
\item 和文処理グルーの挿入が終わった後で,可動グループルビのためのノードの挿入に入る.
\begin{enumerate}
-\item $W$の前後に$2\mathit{cmp}+1=5$個のノードが挿入され,$W$の周辺は次のようなノード列になる.
+\item $W$の前後に$2\mathit{cmp}+1=5$個のノードが挿入され,$W$の周辺は次のようなノード列になる
+ \footnote{%
+ $r_2$と$g_2$の間には,ルビと直後の文字の間にある禁則処理のためのペナルティなどが入っているかもしれない.
+ }.
\begin{align*}
(\text{other nodes})&\longrightarrow
\Node{glue $g_0$}{}\longrightarrow \Node{whatsit~$W$}{元からある}\longrightarrow \Node{rule $r_1$}{}
@@ -743,14 +804,14 @@ $s_i$達の中の文字は既にルビの大きさである.
\begin{gather*}
(\text{other nodes})\longrightarrow
\Node{glue $g_0$}{}\longrightarrow \Node{whatsit~$W$}{元からある}\longrightarrow \Node{rule $r_1$}{}\\
-\noalign{\hrulefill 行の境目\hrulefill}
+\noalign{\leavevmode\hskip 2\zw\hrulefill 行の境目\hrulefill\kern 2\zw\null}
\Node{rule $r_2$}{}\longrightarrow
\Node{glue $g_2$}{}\longrightarrow (\text{other nodes})
\end{gather*}
のようになったとしたら,「\ltjruby{流}{りゆう}」「\ltjruby{暢}{ちよう}」の間で行分割が起きた,ということがわかり,
$g_i$,~$r_i$達のノードを適切に置き換えればよい(後で詳しく説明する).
-\begin{figure}\centering\small
+\begin{table}\centering\small
\begin{tabular}{cllll}
\toprule
\multicolumn{1}{c}{\bf ノード}&\multicolumn{1}{c}{\bf 組み方}&\multicolumn{1}{c}{\bf サンプル}%
@@ -766,16 +827,16 @@ $n_2$&行末2グループ&
\fbox{を}{\color{blue!50!black}\gt\ltjruby[pre=1,mode=1,stretch=110]{流|暢}{りゆう|ちよう}}\vrule
&
$g_0\rightarrow W\rightarrow r_1\rightarrow g_2 \rightarrow r_2$\\
-$n_3$&行頭1グループ&
-\Large
-\vrule{\color{blue!50!black}\gt\ltjruby[post=1,mode=1,stretch=011]{暢}{ちよう}}\fbox{に}
-&
-$r_2\rightarrow g_2$\\
-$n_4$&行頭2グループ&
+$n_3$&行頭2グループ&
\Large
\vrule{\color{blue!50!black}\gt\ltjruby[post=1,mode=1,stretch=011]{流|暢}{りゆう|ちよう}}\fbox{に}
&
$W\rightarrow r_1\rightarrow g_2 \rightarrow r_2\rightarrow g_2$\\
+$n_4$&行頭1グループ&
+\Large
+\vrule{\color{blue!50!black}\gt\ltjruby[post=1,mode=1,stretch=011]{暢}{ちよう}}\fbox{に}
+&
+$r_2\rightarrow g_2$\\
$n_5$&行中&
\Large
\fbox{を}{\color{blue!50!black}\gt\ltjruby[post=0.5,pre=0.5,mode=1]{流|暢}{りゆう|ちよう}}\fbox{に}
@@ -785,32 +846,31 @@ $g_0\rightarrow W\rightarrow r_1\rightarrow g_2 \rightarrow r_2\rightarrow g_2$\
\bottomrule
\end{tabular}
\caption{ルビの組み方と対応するノード並び}
-\label{fig:node}
-\end{figure}
+\label{tab:node}
+\end{table}
\item なお,$r_i$達の高さ・深さは組み上がった後のそれである.
-$g_i$,~$r_i$達の幅は,図\nobreak\ref{fig:node}に示したような対応に沿って算出する.
-例えばこの場合,行中形$n_5$に対して
+$g_i$,~$r_i$達の幅は,\autoref{tab:node}に示したような対応に沿って算出する.
+例えば,行中形$n_5$に対して,記号は乱用気味であるが,
\[
g_0+r_1+g_2+r_2+g_2 = (3-0.25\times 2)\,\mathrm{zw}=2.5\,\mathrm{zw}
\]
-という方程式が立つ(zwは親文字全角の幅,進入量込).
-$n_1$から$n_5$まで計5本の方程式が立つが,これらはGau\ss の消去法で解くことができて
-$g_i$,~$r_i$達の幅が求まる.
+という方程式が立つ(zwは親文字全角の幅,引き算は進入量の分).
+方程式と未知数($g_i$,~$r_i$達の幅)はどちらも$2\mathit{cmp}+1$であるので,
+Gau\ss の消去法により$g_i$,~$r_i$達の幅が求まる.
\item また,ルビ処理を統括しているwhatsit~$W$の値も
-\[
- \Node{whatsit $w$}{value: 2}\longrightarrow
- \Node{vlist $n_1$}{末1}\longrightarrow
- \Node{vlist $n_2$}{末2}\longrightarrow
- \Node{vlist $n_3$}{頭1}\longrightarrow
- \Node{vlist $n_4$}{頭2}\longrightarrow
- \Node{vlist $n_5$}{中}
-\]
+\begin{align*}
+ \Node{whatsit $w$}{value: 2}
+ &\longrightarrow\Node{vlist $n_1$}{末1}\longrightarrow\Node{vlist $n_2$}{末2}
+ \longrightarrow\Node{vlist $n_3$}{頭2}\longrightarrow\Node{vlist $n_4$}{頭1}
+ \longrightarrow\Node{vlist $n_5$}{中}\\
+ &\longrightarrow\Node{kern $k_b$}{}\longrightarrow\Node{kern $k_a$}{}
+\end{align*}
に置き換えておく.
\end{enumerate}
-\item \LuaTeX の行分割処理を普通に行う.
+\item \LuaTeX の行分割処理を通常通りに行う.
\item 行分割の結果に従って,$g_i$,~$r_i$達を適切に置換する.
例えば行分割の結果
@@ -818,7 +878,7 @@ $g_i$,~$r_i$達の幅が求まる.
(\text{other nodes})\longrightarrow
\Node{glue $g_0$}{}\longrightarrow \Node{whatsit~$W$}{元からある}\longrightarrow \Node{rule $r_1$}{}
\tag{行A}\\
-\noalign{\hrulefill 行の境目\hrulefill}
+\noalign{\leavevmode\hskip 2\zw\hrulefill 行の境目\hrulefill\kern 2\zw\null}
\Node{rule $r_2$}{}\longrightarrow
\Node{glue $g_2$}{}\longrightarrow (\text{other nodes})\tag{行B}
\end{gather*}
@@ -833,25 +893,32 @@ $g_i$,~$r_i$達の幅が求まる.
$g_0$,~($W$,)~$r_1$というノードの並びは,「行末1グループ」$n_1$に対応しているので,
$g_0$,~$r_1$を行Aから除去・メモリ解放し,代わりに$n_1$を行Aの中身に追加する.
+\item
+\texttt{always\_highlight\_protrusion}キーが偽であり,かつルビの前への突出量から\ActIntr{前文字進入量}を引いた値
+ $\mathit{prot\_b}$が0でない場合は,$n_1$の直前にkern~$k_b$(長さ$\mathit{prot\_b}$)と,それを打ち消すkern(長さ$-\mathit{prot\_b}$)を挿入する.
+
\item 次に行Bの処理にうつる.行Aでルビの処理は完了していない(2グループのルビなのにまだ1グループしか使っていない)ので,
「whatsit~$W$由来」のノードがいくつか行B内に残っているはずである.
-案の定,$r_2$,~$g_2$というノード列が見つかった.これは「行頭1グループ」$n_3$に対応しているので,
-$r_2$,~$g_2$を行Bから除去・メモリ解放し,代わりに$n_3$を行Bの中身に挿入する.
-
+案の定,$r_2$,~$g_2$というノード列が見つかった.これは「行頭1グループ」$n_4$に対応しているので,
+$r_2$,~$g_2$を行Bから除去・メモリ解放し,代わりに$n_4$を行Bの中身に挿入する.
+\texttt{always\_highlight\_protrusion}キーが偽であったときは,行Aのときと同様にkernを2つ挿入する.
+
\item これで2グループとも使い切ったことになるので,
隔離しておいた$W$を,(使われなかった$n_2$,~$n_4$,~$n_5$などと共に)メモリ解放する.結果として
-次のようになった:
+次のようになる.
\begin{gather*}
- (\text{other nodes})\longrightarrow
- \Node{vlist $n_1$}{末1}\tag{行A}\\
-\noalign{\hrulefill 行の境目\hrulefill}
-\Node{vlist $n_3$}{頭1}\longrightarrow (\text{other nodes})\tag{行B}
+ (\text{other nodes})\longrightarrow \Node{kern~$k_b$}{$\mathit{prot\_b}$}
+ \longrightarrow \Node*{kern}{$-\mathit{prot\_b}$}\longrightarrow \Node*{vlist $n_1$}{末1}\tag{行A}\\
+\noalign{\leavevmode\hskip 2\zw\hrulefill 行の境目\hrulefill\kern 2\zw\null}
+\Node*{vlist $n_4$}{頭1}\longrightarrow \Node*{kern}{$-\mathit{prot\_a}$}
+ \longrightarrow \Node{kern~$k_a$}{$\mathit{prot\_a}$}\longrightarrow (\text{other nodes})\tag{行B}
\end{gather*}
+ ここで,色付きの4ノードは,\Pkg{lua-ul}パッケージの提供する「下線」命令が
+ \cs{ltjruby}のみを有効範囲として発行された場合の,「下線」がかかってくるノードたちである.
\end{enumerate}
\end{enumerate}
-\newpage
\section{いくつかの例}
\def\rubytest{\ruby{黄金橋}{ゴールデンゲートブリッジ}\relax}
@@ -949,7 +1016,6 @@ $r_2$,~$g_2$を行Bから除去・メモリ解放し,代わりに$n_3$を行B
\item[肩つき] {\ltjsetruby{kata}\sample}
\end{description}
-\newpage
\begin{LTXexample}[pos=t]
{\ltjsetruby{stretch=101}% 親文字均等割り禁止
\ruby{休|憩|中}{きゆう|けい|ちゆう}かつ\ruby{視|聴|中}{し|ちよう|ちゆう}%
@@ -961,7 +1027,6 @@ $r_2$,~$g_2$を行Bから除去・メモリ解放し,代わりに$n_3$を行B
\end{LTXexample}
-\typeout{AAAAAA}
\setbox0=\vbox{\hsize20\zw
\ltjsetparameter{rubypreintrusion={`◆,1}}
\ltjsetparameter{rubypostintrusion={`◆,1}}
@@ -1005,6 +1070,18 @@ $r_2$,~$g_2$を行Bから除去・メモリ解放し,代わりに$n_3$を行B
}
\fbox{\box0}
+\medskip
+\begin{LTXexample}[pos=t]
+% lua-ul
+たとえば\strikeThrough{\ruby{取|消|線}{とりけしの|いちじゆう|せん}を}引いてみる.
+\strikeThrough{\ruby{取|消|線}{とりけしの|いちじゆう|せん}}だとどうかな
+
+\ltjsetparameter{prebreakpenalty={`な,2893}}% デバッグ用.あっても正常動作を確認.
+\highLight{周囲が}\ruby{aaaaaaaaaaa}{全く}\highLight{ない}
+ああああああああああああああああああああああああああ
+周囲が\highLight{\ruby{あ|い|う|え}{おそらく|きっと|たぶん|maybe}}ないと思う
+\end{LTXexample}
+
\newpage
\section{『日本語組版処理の要件』20120403の例}
@@ -1053,7 +1130,6 @@ $r_2$,~$g_2$を行Bから除去・メモリ解放し,代わりに$n_3$を行B
\obeylines
3.62 の\ruby{葯}{やく}に
3.63 版面の\ruby{地}{ち}に\quad 版面の\ruby[kata]{地}{ち}に
-% 横組肩つきはしないが,現状では縦組未サポートだし,仕方ないね
3.65 の\ruby{砦}{とりで}に
{\ltjsetruby{kata}%
3.66上 の\ruby{旬}{しゆん}に\quad 後\ruby{旬}{しゆん}に
@@ -1094,29 +1170,32 @@ $r_2$,~$g_2$を行Bから除去・メモリ解放し,代わりに$n_3$を行B
の\ruby{成|就}{じよう|じゆ}を\quad の\ruby{紋|章}{もん|しよう}を\quad%
の\ruby{象|徴}{しよう|ちよう}を}
% モノルビ配置.望ましくない
-3.76× の\ruby{流}{りゆう}\ruby{儀}{ぎ}を\quad の\ruby{無}{む}\ruby{常}{じよう}を\quad%
-
-3.77 {\ltjsetruby{stretchbol=121, stretcheol=121}% 行頭・行末揃えず
- \hbox{\vrule\vbox{\hsize=10\zw\noindent\kern.75\zw
- あああああああの\ruby{流|儀}{りゆう|ぎ}がある.}\vrule}\quad%
+3.76× の\ruby{流}{りゆう}\ruby{儀}{ぎ}を\quad の\ruby{無}{む}\ruby{常}{じよう}を
+3.77 {\ltjsetruby{stretchbol=121, stretcheol=121}% 行頭・行末揃えず
+ \hbox{\vrule\vbox{\hsize=5.25\zw ……等を\ruby{流|儀}{りゆう|ぎ}が}\vrule}\quad%
\hbox{\vrule\vbox{\hsize=5\zw ……等の\ruby{無|常}{む|じよう}を}\vrule}}
\end{LTXexample}
\paragraph{3.3.8節 ルビはみ出し}\
\begin{LTXexample}[pos=t]
-\obeylines
+\obeylines\RULER{3.78}{4}
3.78 \ruby{人}{ひと}は\ruby{死}{し}して\ruby{名}{な}を\ruby{残}{のこ}す
3.79 漢字の部首には\ruby{偏}{へん}・\ruby{冠}{かんむり}・\ruby{脚}{きやく}・%
\ruby{旁}{つくり}がある
+3.79 {\ltjsetparameter{rubypreintrusion={`\,,1}}%
+漢字の部首には\ruby{偏}{へん},\ruby{冠}{かんむり},\ruby{脚}{きやく},%
+\ruby{旁}{つくり}がある}
+% 上が『要件』にある例.標準(下)では句読点の直前にはルビがかからない.
3.79 漢字の部首には\ruby{偏}{へん},\ruby{冠}{かんむり},\ruby{脚}{きやく},%
- \ruby{旁}{つくり}がある
+\ruby{旁}{つくり}がある
\end{LTXexample}
\begin{LTXexample}[pos=t]
-\obeylines
+\obeylines\RULER{3.79}{9}
3.79 この\ruby{\噂}{うわさ}の好きな人は\ruby{懐}{ふところ}ぐあいもよく、\ruby{檜}{ひのき}を
3.80 漢字の部首には「\ruby{偏}{へん}」「\ruby{冠}{かんむり}」「\ruby{脚}{きやく}」%
「\ruby{旁}{つくり}」がある
-3.80 この\ruby{\噂}{うわさ}好きな人は\ruby{懐}{ふところ}具合もよく、\ruby{檜}{ひのき}材を
+3.80 {\ltjsetparameter{rubypreintrusion={`\,,1}}%
+この\ruby{\噂}{うわさ}好きな人は\ruby{懐}{ふところ}具合もよく、\ruby{檜}{ひのき}材を}
3.81× に\ruby{暁}{あかつき}の\kern-1\zw の\ruby{趣}{おもむき}を(良くない例)
3.82 に\ruby{暁}{あかつき}の\ruby{趣}{おもむき}を
diff --git a/macros/luatex/generic/luatexja/doc/luatexja.dtx b/macros/luatex/generic/luatexja/doc/luatexja.dtx
index b550dc58d4..5b46c260fc 100644
--- a/macros/luatex/generic/luatexja/doc/luatexja.dtx
+++ b/macros/luatex/generic/luatexja/doc/luatexja.dtx
@@ -1467,14 +1467,19 @@ Alphabetic fonts
&\cs{fontspec}&\cs{setmainfont}&\cs{setsansfont}&\cs{setmonofont}\\
\midrule
Japanese fonts
-&\cs{newjfontfamily}&\cs{renewjfontfamily}&\cs{setjfontfamily}\\
+&\cs{newjfontfamily}&\cs{renewjfontfamily}&\cs{setjfontfamily}&\cs{providejfontfamily}\\
Alphabetic fonts
-&\cs{newfontfamily}&\cs{renewfontfamily}&\cs{setfontfamily}\\
+&\cs{newfontfamily}&\cs{renewfontfamily}&\cs{setfontfamily}&\cs{providefontfamily}\\
\midrule
Japanese fonts
-&\cs{newjfontface}&\cs{defaultjfontfeatures}&\cs{addjfontfeatures}\\
+&\cs{newjfontface}&\cs{renewjfontface}&\cs{setjfontface}&\cs{providejfontface}\\
Alphabetic fonts
-&\cs{newfontface}&\cs{defaultfontfeatures}&\cs{addfontfeatures}\\
+&\cs{newfontface}&\cs{renewfontface}&\cs{setfontface}&\cs{providefontface}\\
+\midrule
+Japanese fonts
+&\cs{defaultjfontfeatures}&\cs{addjfontfeatures}\\
+Alphabetic fonts
+&\cs{defaultfontfeatures}&\cs{addfontfeatures}\\
\bottomrule
\end{tabular}\par}
\end{table}
@@ -1492,14 +1497,19 @@ Alphabetic fonts
&\cs{fontspec}&\cs{setmainfont}&\cs{setsansfont}&\cs{setmonofont}\\
\midrule
\emph{和文}
-&\cs{newjfontfamily}&\cs{renewjfontfamily}&\cs{setjfontfamily}\\
+&\cs{newjfontfamily}&\cs{renewjfontfamily}&\cs{setjfontfamily}&\cs{providejfontfamily}\\
+\emph{欧文}
+&\cs{newfontfamily}&\cs{renewfontfamily}&\cs{setfontfamily}&\cs{providefontfamily}\\
+\midrule
+\emph{和文}
+&\cs{newjfontface}&\cs{renewjfontface}&\cs{setjfontface}&\cs{providejfontface}\\
\emph{欧文}
-&\cs{newfontfamily}&\cs{renewfontfamily}&\cs{setfontfamily}\\
+&\cs{newfontface}&\cs{renewfontface}&\cs{setfontface}&\cs{providefontface}\\
\midrule
\emph{和文}
-&\cs{newjfontface}&\cs{defaultjfontfeatures}&\cs{addjfontfeatures}\\
+&\cs{defaultjfontfeatures}&\cs{addjfontfeatures}\\
\emph{欧文}
-&\cs{newfontface}&\cs{defaultfontfeatures}&\cs{addfontfeatures}\\
+&\cs{defaultfontfeatures}&\cs{addfontfeatures}\\
\bottomrule
\end{tabular}\par}
\end{table}
@@ -3752,9 +3762,9 @@ This means the following two lines have the same meaning:
\jfont\hoge=hogem:jfm=ujis;-kern at 3.5mm % ==> kern 無効(明示)
\jfont\hoge=hogem:jfm=ujis at 3.5mm % ==> kern 有効(暗黙)
\jfont\hoge=hogem:jfm=ujis;+kern at 3.5mm % ==> kern 有効(明示)
-\DeclareFontShape{JY3}{fuga}{m}{n}{<-> s*hogem:jfm=-ujis}{} % ==> kern 無効(明示)
-\DeclareFontShape{JY3}{fuga}{m}{n}{<-> s*hogem:jfm=ujis}{} % ==> kern 有効(暗黙)
-\DeclareFontShape{JY3}{fuga}{m}{n}{<-> s*hogem:jfm=ujis+kern}{}% ==> kern 有効(明示)
+\DeclareFontShape{JY3}{fuga}{m}{n}{<-> s*hogem:jfm=ujis;-kern}{} % ==> kern 無効(明示)
+\DeclareFontShape{JY3}{fuga}{m}{n}{<-> s*hogem:jfm=ujis}{} % ==> kern 有効(暗黙)
+\DeclareFontShape{JY3}{fuga}{m}{n}{<-> s*hogem:jfm=ujis;+kern}{} % ==> kern 有効(明示)
\end{lstlisting}
\item バージョン20220411.0以降では,\LuaTeX-ja読み込み時や,\Pkg{ltjclasses}, \Pkg{ltjsclasses}において
@@ -3765,7 +3775,7 @@ This means the following two lines have the same meaning:
\ltj@stdmcfont:-kern;jfm=\ltj@stdyokojfm}{}
\end{lstlisting}
とOpenType機能\texttt{kern}を明示的に無効化した状態で定義する.
- これは標準JFM(\texttt{jfm-ujis.lua}, \texttt{jfm-ujisv.lua})が
+ これは標準JFM\ (\texttt{jfm-ujis.lua}, \texttt{jfm-ujisv.lua})が
フォント由来のカーニングが入ることを期待していないためである.
\item
@@ -4852,7 +4862,7 @@ Hence we have the following result:
%<*ja>
(任意,バージョン2以降)
-優先順位付き行長調整が有効であり,かつ現在の文字クラスの文字が
+ 行末文字の位置調整が有効であり,かつ現在の文字クラスの文字が
行末に来た時に,この文字と行末の間には指定された値のいずれかの大きさのカーンが
挿入される(\autoref{ssec:adj}参照).
@@ -7619,7 +7629,7 @@ for making the difference obvious.}
\item[「中身までみた」行送り計算]
複数行に渡る文章を組版するときには行間に空きが入ることが普通である.
\TeX では各行が一つずつの水平ボックスをなしていることを思い出すと,隣り合った
- 2つの行(つまり水平ボックス)の間の空きは次のようにして決まるのだった:
+ 2\nobreak つの行(つまり水平ボックス)の間の空きは次のようにして決まるのだった:
\begin{itemize}
\item 「通常に組んだときの行間」$d$を,\cs{baselineskip}から
「前の行」の深さと「次の行」の高さを加えたものを引いた値とする.
@@ -7833,7 +7843,7 @@ stretch_priority={kanjiskip=-35,xkanjiskip=-25,others=50}
さらに,バージョン20220211.0以降では次の命令が提供される.
\begin{cslist}
- \item[\cs{ltjghostbeginjachar}]
+ \item[\cs{ltjghostbeforejachar}]
\LuaTeX-ja本体が提供している\cs{ltjfakeparbegin}, \cs{ltjfakeboxbdd}と類似の命令である.
実行された箇所が(限定・非限定を問わず)水平モードであった場合に,実行された箇所は
「文字クラス0の\textbf{JAchar}」の直前であると,
@@ -7850,7 +7860,7 @@ A\LG 字 \par% ==> ALchar--(xkanjiskip)--\LG
\end{LTXexample}
\item[\cs{ltjghostafterjachar}]
- \cs{ltjghostbeginjachar}と対を成す命令で,実行された箇所は
+ \cs{ltjghostbeforejachar}と対を成す命令で,実行された箇所は
「文字クラス0の\textbf{JAchar}」の直後であると,
\LuaTeX-jaの和文処理グルー挿入処理に認識させる.以下の実行例を参照.
@@ -11286,7 +11296,16 @@ $n$個あるペナルティの箇所が改行可能箇所である.いずれ
なお,段落の末尾には\cs{penalty10000}と\cs{parfillskip}由来のグルーが自動的に入るが,
これらとの兼ね合いのため\emph{最後のクラスタについては上記のノード挿入処理は行われない}.
-段落最終行の行末文字の位置調整は,すでに述べた「行分割後の場合」における最終行の処理をそのまま用いている.
+段落最終行の行末文字の位置調整は,すでに述べた「行分割後の場合」における最終行の処理を流用しているが,
+そのままでは「段落末尾をぶら下げ組($a_1=-0.5$)にする」ことができない
+\footnote{通常時は\cs{parfillskip}の内容は\texttt{0pt plus 1fil}であるため,
+ 負の長さになることはない.これに伴って,「段落末尾はぶら下げ組が望ましい」状況であっても,
+ 実際には末尾の句点(とその前の数文字)がまとめて次の行に追い出されてしまう.}ため,
+\begin{itemize}
+ \item 段落末尾の\cs{penalty10000}\cs{parfillskip}の直前に,$a_1\text{\cs{zw}}$のカーンを挿入する
+ \item 行分割後,行末文字の位置調整を行う前に,そのカーンを削除する
+\end{itemize}
+という前処理を追加している.
\subsection{グルーの調整}
$|\textit{total}|$の分だけが,行中のグルーの伸び量,あるいは縮み量に応じて負担されることになる.
diff --git a/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-27c.sty b/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-27c.sty
index 76ebf8716c..6ef3ff1751 100644
--- a/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-27c.sty
+++ b/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-27c.sty
@@ -3,7 +3,7 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luatexja-fontspec-27c}[2024-02-19 fontspec support of LuaTeX-ja]
+\ProvidesPackage{luatexja-fontspec-27c}[2024-05-27 fontspec support of LuaTeX-ja]
\RequirePackage{l3keys2e}
\ExplSyntaxOn
@@ -359,10 +359,24 @@
\setfontfamily #1 [#2,#4] {#3}
\cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn
}
+\DeclareDocumentCommand \providejfontfamily { m O{} m O{} } {
+ \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_fontspec_set_family:Nnn
+ \providefontfamily #1 [#2,#4] {#3}
+ \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn
+}
\DeclareDocumentCommand \newjfontface { m O{} m O{} } {
\newjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
}
+\DeclareDocumentCommand \renewjfontface { m O{} m O{} } {
+ \renewjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
+\DeclareDocumentCommand \setjfontface { m O{} m O{} } {
+ \setjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
+\DeclareDocumentCommand \providejfontface { m O{} m O{} } {
+ \providejfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
\prop_new:N \l__ltj_fontspec_tmp_prop
\prop_new:N \g__ltj_fontspec_fontopts_prop
diff --git a/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29c.sty b/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29c.sty
index 1415cee127..51af33447c 100644
--- a/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29c.sty
+++ b/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29c.sty
@@ -3,7 +3,7 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luatexja-fontspec-29c}[2024-05-05 fontspec support of LuaTeX-ja]
+\ProvidesPackage{luatexja-fontspec-29c}[2024-05-27 fontspec support of LuaTeX-ja]
\RequirePackage{l3keys2e}
\ExplSyntaxOn
@@ -359,10 +359,24 @@
\setfontfamily #1 [#2,#4] {#3}
\cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn
}
+\DeclareDocumentCommand \providejfontfamily { m O{} m O{} } {
+ \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_fontspec_set_family:Nnn
+ \providefontfamily #1 [#2,#4] {#3}
+ \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn
+}
\DeclareDocumentCommand \newjfontface { m O{} m O{} } {
\newjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
}
+\DeclareDocumentCommand \renewjfontface { m O{} m O{} } {
+ \renewjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
+\DeclareDocumentCommand \setjfontface { m O{} m O{} } {
+ \setjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
+\DeclareDocumentCommand \providejfontface { m O{} m O{} } {
+ \providejfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
\prop_new:N \l__ltj_fontspec_tmp_prop
\prop_new:N \g__ltj_fontspec_fontopts_prop
diff --git a/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29e.sty b/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29e.sty
index e763d91ad7..02a79ad6ca 100644
--- a/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29e.sty
+++ b/macros/luatex/generic/luatexja/src/addons/luatexja-fontspec-29e.sty
@@ -3,7 +3,7 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luatexja-fontspec-29e}[2024-05-14 fontspec support of LuaTeX-ja]
+\ProvidesPackage{luatexja-fontspec-29e}[2024-05-27 fontspec support of LuaTeX-ja]
\RequirePackage{l3keys2e}
\ExplSyntaxOn
@@ -359,10 +359,24 @@
\setfontfamily #1 [#2,#4] {#3}
\cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn
}
+\DeclareDocumentCommand \providejfontfamily { m O{} m O{} } {
+ \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_fontspec_set_family:Nnn
+ \providefontfamily #1 [#2,#4] {#3}
+ \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn
+}
\DeclareDocumentCommand \newjfontface { m O{} m O{} } {
\newjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
}
+\DeclareDocumentCommand \renewjfontface { m O{} m O{} } {
+ \renewjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
+\DeclareDocumentCommand \setjfontface { m O{} m O{} } {
+ \setjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
+\DeclareDocumentCommand \providejfontface { m O{} m O{} } {
+ \providejfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3}
+}
\prop_new:N \l__ltj_fontspec_tmp_prop
\prop_new:N \g__ltj_fontspec_fontopts_prop
diff --git a/macros/luatex/generic/luatexja/src/addons/luatexja-ruby.sty b/macros/luatex/generic/luatexja/src/addons/luatexja-ruby.sty
index d0d8e5488d..4c64b27d14 100644
--- a/macros/luatex/generic/luatexja/src/addons/luatexja-ruby.sty
+++ b/macros/luatex/generic/luatexja/src/addons/luatexja-ruby.sty
@@ -4,7 +4,7 @@
% LaTeX only!
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luatexja-ruby}[2023-10-08 v0.62]
+\ProvidesPackage{luatexja-ruby}[2024-09-09 v0.7]
\RequirePackage{luatexja}
%%------------------
@@ -18,9 +18,9 @@
%% 引数:ルビ全角を単位とした実数
%% この文字への pre-, post-intrusion の許容量
\define@key[ltj]{japaram}{rubypreintrusion}{%
- \ltj@@set@stack@real{RIPRE}{-0x7FFFFFFF}{0x7FFFFFFF}#1 }
+ \ltj@@set@stack@real{RIPRE}{-0x7FFFFFFF}{0x7FFFFFFF}#1\relax}
\define@key[ltj]{japaram}{rubypostintrusion}{%
- \ltj@@set@stack@real{RIPOST}{-0x7FFFFFFF}{0x7FFFFFFF}#1 }
+ \ltj@@set@stack@real{RIPOST}{-0x7FFFFFFF}{0x7FFFFFFF}#1\relax}
\def\ltj@@set@stack@real#1#2#3{%
\directlua{luatexja.stack.set_stack_perchar(luatexja.stack_table_index.#1,
#2, #3, token.scan_word)}}
@@ -29,7 +29,6 @@
\def\ltj@@rkeydef#1{
\define@key[ltj]{ruby}{#1}{\expandafter\def\csname ltj@@rubyip@#1\endcsname{##1}}
}
-%% attr_ruby_mode
%% bit 0: intrusion を有効にするか(1: 有効)
%% bit 1: 前後の intrusion 許容量を小さい方に揃える (1: yes)
%% bit 2--4: intrusion をどう使って親文字を配置するか
@@ -42,10 +41,10 @@
%% ※ 01--100 で,intrusion で賄えきれなかった場合はいつものように伸長する.
%% bit 5: 熟語ルビの際の処理方法(0: 常にグループ,1: 可能な限りブロックごとに)
%% ↑bit 5 は今は無効
+%% bit 6: 突出部分について lua-ul 有効範囲から除外する処理するか
\ltj@@rkeydef{mode}
%% intrusion 量強制固定(bit 0, bit 1 より優先,負数で「自動」)
%% attr は sp 単位だが,ユーザーはルビ全角単位で指定する
-%% attr_ruby_maxprep, attr_ruby_maxpostp
\ltj@@rkeydef{pre}
\ltj@@rkeydef{post}
%% 親文字伸長の際の比,{0}{1}{1} などと0--7 の数 3 つで指定
@@ -54,14 +53,11 @@
\ltj@@rkeydef{stretcheol} % 行末形
\ltj@@rkeydef{stretch} % 行中形
%% ルビが伸長するときの比 {1}{2}{1} などと0--7 の数 3 つで指定
-%% attr_ruby_mode 上位部分
\ltj@@rkeydef{stretchruby}
%% ルビ<親のとき,ルビと親文字の端の最大値
-%% attr_ruby_maxmargin
%% attr は sp 単位だが,ユーザーは親文字全角単位で指定
\ltj@@rkeydef{maxmargin}
%% ルビと親文字の垂直方向の空き
-%% attr_ruby_intergap
%% attr は sp 単位だが,ユーザーは親文字全角単位で指定
\ltj@@rkeydef{intergap}
@@ -92,6 +88,7 @@
\define@boolkey[ltj]{ruby}{intrude_xkanjiskip}[true]{}
\define@boolkey[ltj]{ruby}{intrude_kanjiskip}[true]{}
+\define@boolkey[ltj]{ruby}{always_highlight_protrusion}[true]{}
%%%%%%%% setkeys の別名
\protected\def\ltjsetruby{\setkeys[ltj]{ruby}}
@@ -141,7 +138,8 @@
[luatexja.icflag_table.XKANJI_SKIP]=true,
[luatexja.icflag_table.XKANJI_SKIP_JFM]=true,
\fi
- \string}
+ \string},
+ quirk_protrusion = \ifltj@ruby@always_highlight_protrusion false \else true \fi
\string}
}
\egroup
@@ -264,7 +262,7 @@
kenten=\textbullet, fontcmd=\relax, ybaseheight=0.88, tbaseheight=0.5,
yrubydepth=0.12, trubydepth=0.5,
intrude_jfmgk, intrude_kanjiskip, intrude_xkanjiskip,
- epsilon=0.0001,
+ epsilon=0.0001, always_highlight_protrusion=false,
}
\endinput
diff --git a/macros/luatex/generic/luatexja/src/lltjext.sty b/macros/luatex/generic/luatexja/src/lltjext.sty
index a83c9305c9..aebdc3987c 100644
--- a/macros/luatex/generic/luatexja/src/lltjext.sty
+++ b/macros/luatex/generic/luatexja/src/lltjext.sty
@@ -3,7 +3,7 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{lltjext}[2022-08-24 v1.2k-ltj-14 Macros for vertical writing]
+\ProvidesPackage{lltjext}[2024-07-23 v1.2k-ltj-16 Macros for vertical writing]
\RequirePackage{luatexja}
\newcount\ltj@ext@dir
\let\ltj@lltjext@orig@tabular=\tabular
@@ -27,7 +27,7 @@
\def\p@stabular<#1>#2{%
\setlength\dimen@{#2}%
\edef\@halignto{to\the\dimen@}\p@tabular<#1>}
-\def\p@tabular<#1>{\leavevmode \hbox \bgroup
+\def\p@tabular<#1>{\leavevmode\null \hbox \bgroup % fixed for unwanted xkanjiskip
\ltj@ext@dir=\ltjgetparameter{direction}\relax
$\let\@acol\@tabacol
\let\@classz\@tabclassz
@@ -78,7 +78,7 @@
\ifhmode \@preamerr\z@ \@@par\fi
\@preamble}
\def\endarray{\crcr\egroup\egroup\@end@alignbox}
-\def\endtabular{\endarray$\egroup}
+\def\endtabular{\endarray$\egroup\null}
\expandafter \let \csname endtabular*\endcsname = \endtabular
% ↓中身\周囲→ yoko tate utod dtou
% yoko A* B* B B
@@ -541,17 +541,11 @@
\else \let\box@dir\utod
\fi
\fi
-\ifltj@ltfilehook@avail % LaTeX2e >= 2020-10-01
\@defaultunitsset\@picht{#3}\unitlength
\@defaultunitsset\@picwd{#2}\unitlength
\setbox\@picbox\hbox to\@picwd\bgroup\box@dir
\@defaultunitsset\@tempdimc{#4}\unitlength\hskip-\@tempdimc
\@defaultunitsset\@tempdimc{#5}\unitlength\lower\@tempdimc\hbox\bgroup
-\else % <= 2020-02-02
- \@picwd=#2\unitlength \@picht=#3\unitlength
- \setbox\@picbox\hbox to\@picwd\bgroup\box@dir
- \hskip-#4\unitlength\lower#5\unitlength\hbox\bgroup
-\fi
\ltj@@pic@reset@blshift
\ignorespaces}
\def\endpicture{%
@@ -675,6 +669,7 @@
%% Compatibility with array package
+\ExplSyntaxOn
\def\ltj@lltjext@patch@array{%
\patchcmd{\@tabular}{\hbox\bgroup}{%
\hbox\bgroup\ltj@ext@dir=\ltjgetparameter{direction}\relax}{}{}
@@ -682,10 +677,6 @@
{\ltj@ext@dir=\ltjgetparameter{direction}\relax}{}{}
\expandafter\let\csname tabular*\endcsname=\ltj@lltjext@orig@tabularS
\patchcmd{\p@stabular}{\p@tabular}{\@tabular}{}{}
- \@ifpackagelater{array}{2018/09/13}
- {\expandafter\@firstoftwo}
- {\expandafter\@secondoftwo}
- {% = for 2018/09/13 v2.4i or newer
\def\@startpbox##1{\bgroup
\color@begingroup
\box@dir\adjustbaseline%%% これを追加
@@ -694,16 +685,8 @@
\vrule \@height \ltjgetht\@arstrutbox \@width \z@
\everypar{}}%
}%
- }{ % = for 2018/04/30 v2.4h or older
- \def\@startpbox##1{\bgroup
- \box@dir\adjustbaseline%%% これを追加
- \setlength\hsize{##1}\@arrayparboxrestore
- \everypar{%
- \vrule \@height \ltjgetht\@arstrutbox \@width \z@
- \everypar{}}%
- }%
- }%
\def\@tabarray{\@ifnextchar<\p@tabarray{\p@tabarray<Z>}}
+\ifcsname tbl_save_outer_table_cols:\endcsname %%%%%% >=2024-06-01
\def\p@array<##1>[##2]##3{%
\ifnum\ltj@ext@dir=0 \ltj@ext@dir=\ltjgetparameter{direction}\relax\fi
\let\box@dir\relax
@@ -736,20 +719,98 @@
\@depth \arraystretch \ltjgetdp \@tempcnta
\@width \z@}%
\fork@array@option<##1>[##2]%
+ \tbl_save_outer_table_cols:
\begingroup
\@mkpream{##3}%
- \xdef\@preamble{\noexpand\ialign \noexpand \@halignto
- \bgroup \tabskip \z@skip \@arstrut \@preamble
- \tabskip \z@ \cr}%
+ \tbl_count_table_cols:
+ \xdef\@preamble{
+ \noexpand\ar@ialign
+ \@halignto
+ \bgroup \@arstrut
+ \UseTaggingSocket{tbl/row/begin}
+ \tbl_init_cell_data_for_row:
+ \@preamble
+ \tabskip \z@ \cr}%
\endgroup
\@arrayleft
- \@begin@alignbox\bgroup\box@dir\adjustbaseline
- \let\par\@empty
- \let\@sharp####\let\protect\relax
- \let\\\@arraycr\let\tabularnewline\\\let\par\@empty
- \lineskip\z@skip\baselineskip\z@skip\@preamble}
- \def\endarray{\crcr\egroup\egroup\@end@alignbox\@arrayright}
+ \@begin@alignbox
+ \bgroup
+ \box@dir\adjustbaseline
+ \let\@sharp####\let\protect\relax
+ \lineskip \z@
+ \baselineskip \z@
+ \mathsurround \z@
+ \let\\\@arraycr \let\tabularnewline\\\let\par\@empty
+ \UseTaggingSocket{tbl/init}
+ \@preamble
+ }
+ \def\endtabular{\endarray$\egroup
+ \UseTaggingSocket {tbl/hmode/end}\null}
+ \def\endarray{
+ \tbl_crcr:n{endarray} \egroup
+ \UseTaggingSocket{tbl/finalize}
+ \tbl_restore_outer_cell_data:
+ \egroup\@end@alignbox
+ \@arrayright \gdef\@preamble{}%
+ }
+\else %%%%%% <2024-06-01
+ \def\p@array<##1>[##2]##3{%
+ \ifnum\ltj@ext@dir=0 \ltj@ext@dir=\ltjgetparameter{direction}\relax\fi
+ \let\box@dir\relax
+ \if ##1z\relax
+ \ifnum\ltj@ext@dir=3\relax
+ \let\box@dir\utod\@tempcnta=\zstrutbox
+ \fi
+ \else
+ \if ##1y\relax
+ \let\box@dir\yoko\@tempcnta=\ystrutbox
+ \else\if ##1t\relax
+ \let\box@dir\tate\@tempcnta=\tstrutbox
+ \else\if ##1d\relax
+ \let\box@dir\dtou\@tempcnta=\dstrutbox
+ \else\if ##1u\relax
+ \let\box@dir\utod\@tempcnta=\zstrutbox
+ \fi\fi\fi\fi
+ \fi
+ \ifx\box@dir\relax
+ \ifcase\ltj@ext@dir
+ \or \let\box@dir\dtou\@tempcnta=\dstrutbox% dtou
+ \or\or\let\box@dir\tate\@tempcnta=\tstrutbox% tate
+ \or \let\box@dir\yoko\@tempcnta=\ystrutbox% yoko
+ \else \let\box@dir\utod\@tempcnta=\zstrutbox% utod
+ \fi
+ \fi
+ \setbox \@arstrutbox \hbox{\box@dir\vrule
+ \@height \arraystretch
+ \dimexpr \ltjgetht\@tempcnta+\extrarowheight\relax
+ \@depth \arraystretch \ltjgetdp \@tempcnta
+ \@width \z@}%
+ \fork@array@option<##1>[##2]%
+ \begingroup
+ \@mkpream{##3}%
+ \xdef\@preamble{\noexpand\ialign \@halignto
+ \bgroup \@arstrut\@preamble \tabskip \z@ \cr}%
+ \endgroup
+ \@arrayleft
+ \@begin@alignbox
+ \bgroup
+ \box@dir\adjustbaseline
+ \let\@sharp####\let\protect\relax
+ \lineskip \z@
+ \baselineskip \z@
+ \m@th
+ \let\\\@arraycr \let\tabularnewline\\\let\par\@empty
+ \@preamble
+ }
+ \def\endtabular{\endarray\m@th$\egroup\null}
+ \def\endarray{
+ \crcr\egroup\egroup\@end@alignbox
+ \@arrayright \gdef\@preamble{}%
+ }
+\fi
+ \expandafter \let \csname endtabular*\endcsname = \endtabular
}
+\ExplSyntaxOff
\@ifpackageloaded{array}{%
\let\tabular=\ltj@lltjext@orig@tabular
\let\array=\ltj@lltjext@orig@array
@@ -760,9 +821,4 @@
\ltj@ExecuteAfterPackage{array}{\ltj@lltjext@patch@array}
}
-%%%%%%%%%%%%%%%% LuaTeX-ja original
-
-%%\define@key[ltj]{japaram}{autouprightnum}{% %COUNT
-%% \ltj@@set@stack{AURN}{0}{10000}\z@#1 }
-
\endinput
diff --git a/macros/luatex/generic/luatexja/src/ltj-adjust.lua b/macros/luatex/generic/luatexja/src/ltj-adjust.lua
index 89ad7816d2..2214f1907c 100644
--- a/macros/luatex/generic/luatexja/src/ltj-adjust.lua
+++ b/macros/luatex/generic/luatexja/src/ltj-adjust.lua
@@ -37,6 +37,7 @@ local node_tail = node.direct.tail
local get_attr = node.direct.get_attribute
local set_attr = node.direct.set_attribute
local insert_after = node.direct.insert_after
+local node_remove = node.direct.remove
local id_glyph = node.id 'glyph'
local id_kern = node.id 'kern'
@@ -205,7 +206,7 @@ end
-- step 1 最終行用
local min, max = math.min, math.max
local setsubtype = node.direct.setsubtype
-local function aw_step1_last(p, total)
+local function aw_step1_last(p, total, removed_le)
local head = getlist(p)
local x = node_tail(head); if not x then return total, false end
-- x: \rightskip
@@ -246,6 +247,7 @@ local function aw_step1_last(p, total)
-- 続行条件2: min(eadt[1], 0)<= \parfillskip <= max(eadt[#eadt], 0)
local pfw = getwidth(pf)
+ (total>0 and getfield(pf, 'stretch') or -getfield(pf, 'shrink')) *getfield(p, 'glue_set')
+ + removed_le
if pfw<min(0,eadt[1]) or max(0,eadt[#eadt])<pfw then return total, false end
-- \parfillskip を 0 にする
total = total + getwidth(pf)
@@ -393,6 +395,21 @@ do
end
end
end
+local insert_lineend_kern_tail
+do
+ local insert_before = node.direct.insert_before
+ local KINSOKU = luatexja.icflag_table.KINSOKU
+ insert_lineend_kern_tail = function (head, nq, last)
+ if nq.met then
+ local eadt = nq.met.char_type[nq.class].end_adjust
+ if eadt and eadt[1]<0 then
+ local x = node_new(id_kern, 1)
+ setkern(x, eadt[1]); set_attr(x, attr_icflag, LINEEND)
+ insert_before(head, node_prev(last), x)
+ end
+ end
+ end
+end
local adjust_width
do
@@ -408,7 +425,15 @@ do
last_p = p
end
if last_p then
- myaw_step2(last_p, myaw_step1_last(last_p, get_total_stretched(last_p)))
+ local removed_le = 0
+ local p = getlist(last_p); local pf = node_prev(node_tail(p))
+ if getid(pf) == id_glue and getsubtype(pf) == 15 then
+ pf = node_prev(node_prev(pf))
+ if getid(pf) == id_kern and get_attr_icflag(pf)==LINEEND then
+ removed_le = getwidth(pf); node_remove(p, pf); node_free(pf)
+ end
+ end
+ myaw_step2(last_p, myaw_step1_last(last_p, get_total_stretched(last_p), removed_le))
end
return to_node(head)
end
@@ -426,6 +451,7 @@ do
if status_le==2 then
if not luatexbase.in_callback('luatexja.adjust_jfmglue', 'luatexja.adjust') then
ltjb.add_to_callback('luatexja.adjust_jfmglue', insert_lineend_kern, 'luatexja.adjust')
+ ltjb.add_to_callback('luatexja.adjust_jfmglue_tail', insert_lineend_kern_tail, 'luatexja.adjust')
end
myaw_step1, myaw_step1_last = dummy, aw_step1_last
else
@@ -435,7 +461,8 @@ do
myaw_step1, myaw_step1_last = aw_step1, aw_step1_last
end
if luatexbase.in_callback('luatexja.adjust_jfmglue', 'luatexja.adjust') then
- luatexbase.remove_from_callback('luatexja.adjust_jfmglue', 'luatexja.adjust')
+ luatexbase.remove_from_callback('luatexja.adjust_jfmglue', 'luatexja.adjust')
+ luatexbase.remove_from_callback('luatexja.adjust_jfmglue_tail', 'luatexja.adjust')
end
end
myaw_step2 = (status_pr>0) and aw_step2 or aw_step2_dummy
@@ -560,7 +587,6 @@ end
do
local ltja = luatexja.adjust
local sid_user = node.subtype 'user_defined'
- local node_remove = node.direct.remove
local node_write = node.direct.write
local getvalue = node.direct.getdata
local setvalue = node.direct.setdata
diff --git a/macros/luatex/generic/luatexja/src/ltj-ivd_aj1.lua b/macros/luatex/generic/luatexja/src/ltj-ivd_aj1.lua
index de18150d79..bb265c7e54 100644
--- a/macros/luatex/generic/luatexja/src/ltj-ivd_aj1.lua
+++ b/macros/luatex/generic/luatexja/src/ltj-ivd_aj1.lua
@@ -6,6 +6,9 @@ table_ivd_aj1 = {
-- #
-- # History:
-- #
+-- # 2022-09-13 Registration of additional sequences in the Adobe-Japan1
+-- # collection.
+-- #
-- # 2020-11-06 Registration of additional sequences in the MSARG
-- # collection.
-- #
@@ -36,7 +39,7 @@ table_ivd_aj1 = {
-- # For more details on the IVD, see UTS #37:
-- # https://www.unicode.org/reports/tr37/
-- #
--- # Copyright 2006-2020 Unicode, Inc.
+-- # Copyright 2006-2022 Unicode, Inc.
-- # For terms of use, see: https://www.unicode.org/copyright.html#8
-- #
-- # EOF
@@ -1952,7 +1955,7 @@ table_ivd_aj1 = {
0x209dbf,0x209db5,0x209db9,0x209dbd,0x209dc9,0x209dda,0x209de0,0x209de3,0x204d07,0x209e0a,
0x209e02,0x209e0d,0x209e1c,0x209e7b,0x222218,0x209e80,0x209e85,0x209e9b,0x22a38c,0x209ebd,
0x22a437,0x209edf,0x209eff,0x209f02,0x204d77,0x209f03,0x209f3a,0x209f3d,0x209f46,0x209f53,
- 0x209f55,0x209f58,0x22a5f1,0x209f5d,0x22a602,0x209f69,0x209f6d,0x209f70,0x22a6b2,0x20793a,
+ 0x209f55,0x209f58,0x22a5f1,0x209f5d,0x22a602,0x209f69,0x209f6d,0x209f70,0x22a6b2,0x231350,
0x203614,0x2038ad,0x203dcc,0x203fdc,0x2045be,0x204610,0x204b7e,0x204c38,0x204ce1,0x204e0c,
0x204e23,0x204e24,0x204e79,0x204ef3,0x204f0c,0x204f19,0x204f2b,0x204f2e,0x204f31,0x204f84,
0x204f9e,0x204fb7,0x205004,0x20500c,0x20504c,0x20505f,0x205062,0x205077,0x20508e,0x20509e,
diff --git a/macros/luatex/generic/luatexja/src/ltj-jfmglue.lua b/macros/luatex/generic/luatexja/src/ltj-jfmglue.lua
index ed7ab6daa8..38d9a72d52 100644
--- a/macros/luatex/generic/luatexja/src/ltj-jfmglue.lua
+++ b/macros/luatex/generic/luatexja/src/ltj-jfmglue.lua
@@ -3,7 +3,7 @@
--
luatexbase.provides_module({
name = 'luatexja.jfmglue',
- date = '2022-08-29',
+ date = '2024-10-12',
description = 'Insertion process of JFM glues, [x]kanjiskip and others',
})
luatexja.jfmglue = luatexja.jfmglue or {}
@@ -587,7 +587,7 @@ function calc_np(last, lp)
if k then return lp end
end
end
- Np=nil
+ Np = nil
end
end -- 001 -----------------------------------------------
@@ -693,6 +693,7 @@ end
-------------------- 最下層の処理
luatexbase.create_callback('luatexja.adjust_jfmglue', 'simple', function(n) return n end)
+luatexbase.create_callback('luatexja.adjust_jfmglue_tail', 'simple', function(n) return n end)
-- change penalties (or create a new penalty, if needed)
local function handle_penalty_normal(post, pre, g)
@@ -741,7 +742,7 @@ local function handle_penalty_jwp()
local a = table_current_stack[luatexja.stack_table_index.JWP]
if #widow_Bp == 0 then
if a~=0 then
- local p = node_new(id_penalty, widow_Np.nuc)
+ local p = node_new(id_penalty, nil, widow_Np.nuc)
if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end
setpenalty(p, a); head = insert_before(head, widow_Np.first, p)
widow_Bp[1]=p; set_attr(p, attr_icflag, KINSOKU)
@@ -1059,7 +1060,7 @@ local function handle_np_jachar(mode)
real_insert(g)
end
if mode and Np.kcat%2~=1 then
- widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp
+ widow_Np.nuc, widow_Np.first, widow_Bp, Bp = Np.nuc, Np.first, Bp, widow_Bp
end
end
@@ -1138,6 +1139,7 @@ local function handle_list_tail(mode, last)
adjust_nq()
if mode then
-- the current list is to be line-breaked.
+ call_callback('luatexja.adjust_jfmglue_tail', head, Nq, last)
-- Insert \jcharwidowpenalty
if widow_Np.first then handle_penalty_jwp() end
else
diff --git a/macros/luatex/generic/luatexja/src/ltj-jfont.lua b/macros/luatex/generic/luatexja/src/ltj-jfont.lua
index 26c76d88a1..3e685c0b41 100644
--- a/macros/luatex/generic/luatexja/src/ltj-jfont.lua
+++ b/macros/luatex/generic/luatexja/src/ltj-jfont.lua
@@ -3,7 +3,7 @@
--
luatexbase.provides_module({
name = 'luatexja.jfont',
- date = '2023-04-06',
+ date = '2024-07-25',
description = 'Loader for Japanese fonts',
})
@@ -434,17 +434,18 @@ do
end
-- extract jfm_name, jfm_spec and jfm_var
-- normalize position of 'jfm=' and 'jfmvar=' keys
+ local match, sub = string.match, string.sub
local function extract_jfm_spec(name)
- name = (name:match '^{(.*)}$') or (name:match '^"(.*)"$') or name
+ name = (match(name,'^{(.*)}$')) or (match(name,'^"(.*)"$')) or name
jfm_spec, jfm_var = '', ''
- local tmp, index = name:sub(1, 5), 1
+ local tmp, index = sub(name,1, 5), 1
if tmp == 'file:' or tmp == 'name:' or tmp == 'psft:' then
index = 6
end
name = lpegmatch(jf_remainder, name)
if jfm_name~='' then
if luatexja.jfont.jfm_feature then
- local l, t2 = name:sub(-1), {}
+ local l, t2 = sub(name,-1), {}
for i,v in pairs(luatexja.jfont.jfm_feature) do
-- print(i,type(v),v)
t2[#t2+1] = (v==true) and i
@@ -454,7 +455,7 @@ do
else
jfm_spec = jfm_name
end
- l = name:sub(-1)
+ l = sub(name,-1)
name = name .. ((l==':' or l==';') and '' or ';') .. 'jfm=' .. jfm_spec
if jfm_var~='' then
name = name .. ';jfmvar=' .. jfm_var
@@ -806,9 +807,9 @@ local list_rotate_glyphs
do
local ceil = math.ceil
-- output of function_uax50.lua
- -- UAX#50 for Unicode 14.0.0
+ -- UAX#50 for Unicode 15.1.0
-- t[0] = true
- local t={ 0, 167, 168, 169, 170, 174, 175, 177, 178, 188, 191, 215, 216, 247, 248, 746, 748, 888, 890, 896, 900, 907, 908, 909, 910, 930, 931, 1328, 1329, 1367, 1369, 1419, 1421, 1424, 1425, 1480, 1488, 1515, 1519, 1525, 1536, 1806, 1807, 1867, 1869, 1970, 1984, 2043, 2045, 2094, 2096, 2111, 2112, 2140, 2142, 2143, 2144, 2155, 2160, 2191, 2192, 2194, 2200, 2436, 2437, 2445, 2447, 2449, 2451, 2473, 2474, 2481, 2482, 2483, 2486, 2490, 2492, 2501, 2503, 2505, 2507, 2511, 2519, 2520, 2524, 2526, 2527, 2532, 2534, 2559, 2561, 2564, 2565, 2571, 2575, 2577, 2579, 2601, 2602, 2609, 2610, 2612, 2613, 2615, 2616, 2618, 2620, 2621, 2622, 2627, 2631, 2633, 2635, 2638, 2641, 2642, 2649, 2653, 2654, 2655, 2662, 2679, 2689, 2692, 2693, 2702, 2703, 2706, 2707, 2729, 2730, 2737, 2738, 2740, 2741, 2746, 2748, 2758, 2759, 2762, 2763, 2766, 2768, 2769, 2784, 2788, 2790, 2802, 2809, 2816, 2817, 2820, 2821, 2829, 2831, 2833, 2835, 2857, 2858, 2865, 2866, 2868, 2869, 2874, 2876, 2885, 2887, 2889, 2891, 2894, 2901, 2904, 2908, 2910, 2911, 2916, 2918, 2936, 2946, 2948, 2949, 2955, 2958, 2961, 2962, 2966, 2969, 2971, 2972, 2973, 2974, 2976, 2979, 2981, 2984, 2987, 2990, 3002, 3006, 3011, 3014, 3017, 3018, 3022, 3024, 3025, 3031, 3032, 3046, 3067, 3072, 3085, 3086, 3089, 3090, 3113, 3114, 3130, 3132, 3141, 3142, 3145, 3146, 3150, 3157, 3159, 3160, 3163, 3165, 3166, 3168, 3172, 3174, 3184, 3191, 3213, 3214, 3217, 3218, 3241, 3242, 3252, 3253, 3258, 3260, 3269, 3270, 3273, 3274, 3278, 3285, 3287, 3293, 3295, 3296, 3300, 3302, 3312, 3313, 3315, 3328, 3341, 3342, 3345, 3346, 3397, 3398, 3401, 3402, 3408, 3412, 3428, 3430, 3456, 3457, 3460, 3461, 3479, 3482, 3506, 3507, 3516, 3517, 3518, 3520, 3527, 3530, 3531, 3535, 3541, 3542, 3543, 3544, 3552, 3558, 3568, 3570, 3573, 3585, 3643, 3647, 3676, 3713, 3715, 3716, 3717, 3718, 3723, 3724, 3748, 3749, 3750, 3751, 3774, 3776, 3781, 3782, 3783, 3784, 3790, 3792, 3802, 3804, 3808, 3840, 3912, 3913, 3949, 3953, 3992, 3993, 4029, 4030, 4045, 4046, 4059, 4096, 4294, 4295, 4296, 4301, 4302, 4304, 4352, 4608, 4681, 4682, 4686, 4688, 4695, 4696, 4697, 4698, 4702, 4704, 4745, 4746, 4750, 4752, 4785, 4786, 4790, 4792, 4799, 4800, 4801, 4802, 4806, 4808, 4823, 4824, 4881, 4882, 4886, 4888, 4955, 4957, 4989, 4992, 5018, 5024, 5110, 5112, 5118, 5120, 5121, 5760, 5789, 5792, 5881, 5888, 5910, 5919, 5943, 5952, 5972, 5984, 5997, 5998, 6001, 6002, 6004, 6016, 6110, 6112, 6122, 6128, 6138, 6144, 6170, 6176, 6265, 6272, 6315, 6400, 6431, 6432, 6444, 6448, 6460, 6464, 6465, 6468, 6510, 6512, 6517, 6528, 6572, 6576, 6602, 6608, 6619, 6622, 6684, 6686, 6751, 6752, 6781, 6783, 6794, 6800, 6810, 6816, 6830, 6832, 6863, 6912, 6989, 6992, 7039, 7040, 7156, 7164, 7224, 7227, 7242, 7245, 7305, 7312, 7355, 7357, 7368, 7376, 7419, 7424, 7958, 7960, 7966, 7968, 8006, 8008, 8014, 8016, 8024, 8025, 8026, 8027, 8028, 8029, 8030, 8031, 8062, 8064, 8117, 8118, 8133, 8134, 8148, 8150, 8156, 8157, 8176, 8178, 8181, 8182, 8191, 8192, 8214, 8215, 8224, 8226, 8240, 8242, 8251, 8253, 8258, 8259, 8263, 8266, 8273, 8274, 8293, 8294, 8306, 8308, 8335, 8336, 8349, 8352, 8385, 8400, 8413, 8417, 8418, 8421, 8433, 8450, 8451, 8458, 8463, 8464, 8467, 8469, 8470, 8472, 8478, 8484, 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8501, 8512, 8517, 8523, 8524, 8526, 8527, 8586, 8588, 8592, 8734, 8735, 8756, 8758, 8960, 8968, 8972, 8992, 8996, 9001, 9003, 9004, 9085, 9115, 9150, 9166, 9167, 9168, 9169, 9180, 9186, 9251, 9252, 9472, 9632, 9754, 9760, 10088, 10102, 10132, 11026, 11056, 11088, 11098, 11124, 11126, 11158, 11160, 11192, 11218, 11219, 11244, 11248, 11264, 11508, 11513, 11558, 11559, 11560, 11565, 11566, 11568, 11624, 11631, 11633, 11647, 11671, 11680, 11687, 11688, 11695, 11696, 11703, 11704, 11711, 11712, 11719, 11720, 11727, 11728, 11735, 11736, 11743, 11744, 11856, 11858, 11870, 12296, 12306, 12308, 12320, 12336, 12337, 12448, 12449, 12540, 12541, 42192, 42540, 42560, 42744, 42752, 42955, 42960, 42962, 42963, 42964, 42965, 42970, 42994, 43053, 43056, 43066, 43072, 43128, 43136, 43206, 43214, 43226, 43232, 43348, 43359, 43360, 43392, 43470, 43471, 43482, 43486, 43519, 43520, 43575, 43584, 43598, 43600, 43610, 43612, 43715, 43739, 43767, 43777, 43783, 43785, 43791, 43793, 43799, 43808, 43815, 43816, 43823, 43824, 43884, 43888, 44014, 44016, 44026, 55296, 57344, 64256, 64263, 64275, 64280, 64285, 64311, 64312, 64317, 64318, 64319, 64320, 64322, 64323, 64325, 64326, 64451, 64467, 64912, 64914, 64968, 64975, 64976, 65008, 65040, 65056, 65072, 65097, 65104, 65112, 65119, 65123, 65127, 65136, 65141, 65142, 65277, 65279, 65280, 65288, 65290, 65293, 65294, 65306, 65311, 65339, 65340, 65341, 65342, 65343, 65344, 65371, 65471, 65474, 65480, 65482, 65488, 65490, 65496, 65498, 65501, 65507, 65508, 65512, 65519, 65529, 65532, 65536, 65548, 65549, 65575, 65576, 65595, 65596, 65598, 65599, 65614, 65616, 65630, 65664, 65787, 65792, 65795, 65799, 65844, 65847, 65935, 65936, 65949, 65952, 65953, 66000, 66046, 66176, 66205, 66208, 66257, 66272, 66300, 66304, 66340, 66349, 66379, 66384, 66427, 66432, 66462, 66463, 66500, 66504, 66518, 66560, 66718, 66720, 66730, 66736, 66772, 66776, 66812, 66816, 66856, 66864, 66916, 66927, 66939, 66940, 66955, 66956, 66963, 66964, 66966, 66967, 66978, 66979, 66994, 66995, 67002, 67003, 67005, 67072, 67383, 67392, 67414, 67424, 67432, 67456, 67462, 67463, 67505, 67506, 67515, 67584, 67590, 67592, 67593, 67594, 67638, 67639, 67641, 67644, 67645, 67647, 67670, 67671, 67743, 67751, 67760, 67808, 67827, 67828, 67830, 67835, 67868, 67871, 67898, 67903, 67904, 68000, 68024, 68028, 68048, 68050, 68100, 68101, 68103, 68108, 68116, 68117, 68120, 68121, 68150, 68152, 68155, 68159, 68169, 68176, 68185, 68192, 68256, 68288, 68327, 68331, 68343, 68352, 68406, 68409, 68438, 68440, 68467, 68472, 68498, 68505, 68509, 68521, 68528, 68608, 68681, 68736, 68787, 68800, 68851, 68858, 68904, 68912, 68922, 69216, 69247, 69248, 69290, 69291, 69294, 69296, 69298, 69376, 69416, 69424, 69466, 69488, 69514, 69552, 69580, 69600, 69623, 69632, 69710, 69714, 69750, 69759, 69827, 69837, 69838, 69840, 69865, 69872, 69882, 69888, 69941, 69942, 69960, 69968, 70007, 70016, 70112, 70113, 70133, 70144, 70162, 70163, 70207, 70272, 70279, 70280, 70281, 70282, 70286, 70287, 70302, 70303, 70314, 70320, 70379, 70384, 70394, 70400, 70404, 70405, 70413, 70415, 70417, 70419, 70441, 70442, 70449, 70450, 70452, 70453, 70458, 70459, 70469, 70471, 70473, 70475, 70478, 70480, 70481, 70487, 70488, 70493, 70500, 70502, 70509, 70512, 70517, 70656, 70748, 70749, 70754, 70784, 70856, 70864, 70874, 71168, 71237, 71248, 71258, 71264, 71277, 71296, 71354, 71360, 71370, 71424, 71451, 71453, 71468, 71472, 71495, 71680, 71740, 71840, 71923, 71935, 71943, 71945, 71946, 71948, 71956, 71957, 71959, 71960, 71990, 71991, 71993, 71995, 72007, 72016, 72026, 72096, 72104, 72106, 72152, 72154, 72165, 72384, 72441, 72704, 72713, 72714, 72759, 72760, 72774, 72784, 72813, 72816, 72848, 72850, 72872, 72873, 72887, 72960, 72967, 72968, 72970, 72971, 73015, 73018, 73019, 73020, 73022, 73023, 73032, 73040, 73050, 73056, 73062, 73063, 73065, 73066, 73103, 73104, 73106, 73107, 73113, 73120, 73130, 73440, 73465, 73648, 73649, 73664, 73714, 73727, 74650, 74752, 74863, 74864, 74869, 74880, 75076, 77712, 77811, 92160, 92729, 92736, 92767, 92768, 92778, 92782, 92863, 92864, 92874, 92880, 92910, 92912, 92918, 92928, 92998, 93008, 93018, 93019, 93026, 93027, 93048, 93053, 93072, 93760, 93851, 93952, 94027, 94031, 94088, 94095, 94112, 113664, 113771, 113776, 113789, 113792, 113801, 113808, 113818, 113820, 113828, 119296, 119366, 119808, 119893, 119894, 119965, 119966, 119968, 119970, 119971, 119973, 119975, 119977, 119981, 119982, 119994, 119995, 119996, 119997, 120004, 120005, 120070, 120071, 120075, 120077, 120085, 120086, 120093, 120094, 120122, 120123, 120127, 120128, 120133, 120134, 120135, 120138, 120145, 120146, 120486, 120488, 120780, 120782, 120832, 122624, 122655, 122880, 122887, 122888, 122905, 122907, 122914, 122915, 122917, 122918, 122923, 123136, 123181, 123184, 123198, 123200, 123210, 123214, 123216, 123536, 123567, 123584, 123642, 123647, 123648, 124896, 124903, 124904, 124908, 124909, 124911, 124912, 124927, 124928, 125125, 125127, 125143, 125184, 125260, 125264, 125274, 125278, 125280, 126065, 126133, 126209, 126270, 126464, 126468, 126469, 126496, 126497, 126499, 126500, 126501, 126503, 126504, 126505, 126515, 126516, 126520, 126521, 126522, 126523, 126524, 126530, 126531, 126535, 126536, 126537, 126538, 126539, 126540, 126541, 126544, 126545, 126547, 126548, 126549, 126551, 126552, 126553, 126554, 126555, 126556, 126557, 126558, 126559, 126560, 126561, 126563, 126564, 126565, 126567, 126571, 126572, 126579, 126580, 126584, 126585, 126589, 126590, 126591, 126592, 126602, 126603, 126620, 126625, 126628, 126629, 126634, 126635, 126652, 126704, 126706, 129024, 129036, 129040, 129096, 129104, 129114, 129120, 129160, 129168, 129198, 129200, 129202, 129792, 129939, 129940, 129995, 130032, 130042, 917505, 917506, 917536, 917632, 917760, 918000 }
+ local t={ 0, 167, 168, 169, 170, 174, 175, 177, 178, 188, 191, 215, 216, 247, 248, 746, 748, 888, 890, 896, 900, 907, 908, 909, 910, 930, 931, 1328, 1329, 1367, 1369, 1419, 1421, 1424, 1425, 1480, 1488, 1515, 1519, 1525, 1536, 1806, 1807, 1867, 1869, 1970, 1984, 2043, 2045, 2094, 2096, 2111, 2112, 2140, 2142, 2143, 2144, 2155, 2160, 2191, 2192, 2194, 2200, 2436, 2437, 2445, 2447, 2449, 2451, 2473, 2474, 2481, 2482, 2483, 2486, 2490, 2492, 2501, 2503, 2505, 2507, 2511, 2519, 2520, 2524, 2526, 2527, 2532, 2534, 2559, 2561, 2564, 2565, 2571, 2575, 2577, 2579, 2601, 2602, 2609, 2610, 2612, 2613, 2615, 2616, 2618, 2620, 2621, 2622, 2627, 2631, 2633, 2635, 2638, 2641, 2642, 2649, 2653, 2654, 2655, 2662, 2679, 2689, 2692, 2693, 2702, 2703, 2706, 2707, 2729, 2730, 2737, 2738, 2740, 2741, 2746, 2748, 2758, 2759, 2762, 2763, 2766, 2768, 2769, 2784, 2788, 2790, 2802, 2809, 2816, 2817, 2820, 2821, 2829, 2831, 2833, 2835, 2857, 2858, 2865, 2866, 2868, 2869, 2874, 2876, 2885, 2887, 2889, 2891, 2894, 2901, 2904, 2908, 2910, 2911, 2916, 2918, 2936, 2946, 2948, 2949, 2955, 2958, 2961, 2962, 2966, 2969, 2971, 2972, 2973, 2974, 2976, 2979, 2981, 2984, 2987, 2990, 3002, 3006, 3011, 3014, 3017, 3018, 3022, 3024, 3025, 3031, 3032, 3046, 3067, 3072, 3085, 3086, 3089, 3090, 3113, 3114, 3130, 3132, 3141, 3142, 3145, 3146, 3150, 3157, 3159, 3160, 3163, 3165, 3166, 3168, 3172, 3174, 3184, 3191, 3213, 3214, 3217, 3218, 3241, 3242, 3252, 3253, 3258, 3260, 3269, 3270, 3273, 3274, 3278, 3285, 3287, 3293, 3295, 3296, 3300, 3302, 3312, 3313, 3316, 3328, 3341, 3342, 3345, 3346, 3397, 3398, 3401, 3402, 3408, 3412, 3428, 3430, 3456, 3457, 3460, 3461, 3479, 3482, 3506, 3507, 3516, 3517, 3518, 3520, 3527, 3530, 3531, 3535, 3541, 3542, 3543, 3544, 3552, 3558, 3568, 3570, 3573, 3585, 3643, 3647, 3676, 3713, 3715, 3716, 3717, 3718, 3723, 3724, 3748, 3749, 3750, 3751, 3774, 3776, 3781, 3782, 3783, 3784, 3791, 3792, 3802, 3804, 3808, 3840, 3912, 3913, 3949, 3953, 3992, 3993, 4029, 4030, 4045, 4046, 4059, 4096, 4294, 4295, 4296, 4301, 4302, 4304, 4352, 4608, 4681, 4682, 4686, 4688, 4695, 4696, 4697, 4698, 4702, 4704, 4745, 4746, 4750, 4752, 4785, 4786, 4790, 4792, 4799, 4800, 4801, 4802, 4806, 4808, 4823, 4824, 4881, 4882, 4886, 4888, 4955, 4957, 4989, 4992, 5018, 5024, 5110, 5112, 5118, 5120, 5121, 5760, 5789, 5792, 5881, 5888, 5910, 5919, 5943, 5952, 5972, 5984, 5997, 5998, 6001, 6002, 6004, 6016, 6110, 6112, 6122, 6128, 6138, 6144, 6170, 6176, 6265, 6272, 6315, 6400, 6431, 6432, 6444, 6448, 6460, 6464, 6465, 6468, 6510, 6512, 6517, 6528, 6572, 6576, 6602, 6608, 6619, 6622, 6684, 6686, 6751, 6752, 6781, 6783, 6794, 6800, 6810, 6816, 6830, 6832, 6863, 6912, 6989, 6992, 7039, 7040, 7156, 7164, 7224, 7227, 7242, 7245, 7305, 7312, 7355, 7357, 7368, 7376, 7419, 7424, 7958, 7960, 7966, 7968, 8006, 8008, 8014, 8016, 8024, 8025, 8026, 8027, 8028, 8029, 8030, 8031, 8062, 8064, 8117, 8118, 8133, 8134, 8148, 8150, 8156, 8157, 8176, 8178, 8181, 8182, 8191, 8192, 8214, 8215, 8224, 8226, 8240, 8242, 8251, 8253, 8258, 8259, 8263, 8266, 8273, 8274, 8293, 8294, 8306, 8308, 8335, 8336, 8349, 8352, 8385, 8400, 8413, 8417, 8418, 8421, 8433, 8450, 8451, 8458, 8463, 8464, 8467, 8469, 8470, 8472, 8478, 8484, 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8501, 8512, 8517, 8523, 8524, 8526, 8527, 8586, 8588, 8592, 8734, 8735, 8756, 8758, 8960, 8968, 8972, 8992, 8996, 9001, 9003, 9004, 9085, 9115, 9150, 9166, 9167, 9168, 9169, 9180, 9186, 9251, 9252, 9472, 9632, 9754, 9760, 10088, 10102, 10132, 11026, 11056, 11088, 11098, 11124, 11126, 11158, 11160, 11192, 11218, 11219, 11244, 11248, 11264, 11508, 11513, 11558, 11559, 11560, 11565, 11566, 11568, 11624, 11631, 11633, 11647, 11671, 11680, 11687, 11688, 11695, 11696, 11703, 11704, 11711, 11712, 11719, 11720, 11727, 11728, 11735, 11736, 11743, 11744, 11856, 11858, 11870, 12296, 12306, 12308, 12320, 12336, 12337, 12448, 12449, 12540, 12541, 42192, 42540, 42560, 42744, 42752, 42955, 42960, 42962, 42963, 42964, 42965, 42970, 42994, 43053, 43056, 43066, 43072, 43128, 43136, 43206, 43214, 43226, 43232, 43348, 43359, 43360, 43392, 43470, 43471, 43482, 43486, 43519, 43520, 43575, 43584, 43598, 43600, 43610, 43612, 43715, 43739, 43767, 43777, 43783, 43785, 43791, 43793, 43799, 43808, 43815, 43816, 43823, 43824, 43884, 43888, 44014, 44016, 44026, 55296, 57344, 64256, 64263, 64275, 64280, 64285, 64311, 64312, 64317, 64318, 64319, 64320, 64322, 64323, 64325, 64326, 64451, 64467, 64912, 64914, 64968, 64975, 64976, 65008, 65040, 65056, 65072, 65097, 65104, 65112, 65119, 65123, 65127, 65136, 65141, 65142, 65277, 65279, 65280, 65288, 65290, 65293, 65294, 65306, 65311, 65339, 65340, 65341, 65342, 65343, 65344, 65371, 65471, 65474, 65480, 65482, 65488, 65490, 65496, 65498, 65501, 65507, 65508, 65512, 65519, 65529, 65532, 65536, 65548, 65549, 65575, 65576, 65595, 65596, 65598, 65599, 65614, 65616, 65630, 65664, 65787, 65792, 65795, 65799, 65844, 65847, 65935, 65936, 65949, 65952, 65953, 66000, 66046, 66176, 66205, 66208, 66257, 66272, 66300, 66304, 66340, 66349, 66379, 66384, 66427, 66432, 66462, 66463, 66500, 66504, 66518, 66560, 66718, 66720, 66730, 66736, 66772, 66776, 66812, 66816, 66856, 66864, 66916, 66927, 66939, 66940, 66955, 66956, 66963, 66964, 66966, 66967, 66978, 66979, 66994, 66995, 67002, 67003, 67005, 67072, 67383, 67392, 67414, 67424, 67432, 67456, 67462, 67463, 67505, 67506, 67515, 67584, 67590, 67592, 67593, 67594, 67638, 67639, 67641, 67644, 67645, 67647, 67670, 67671, 67743, 67751, 67760, 67808, 67827, 67828, 67830, 67835, 67868, 67871, 67898, 67903, 67904, 68000, 68024, 68028, 68048, 68050, 68100, 68101, 68103, 68108, 68116, 68117, 68120, 68121, 68150, 68152, 68155, 68159, 68169, 68176, 68185, 68192, 68256, 68288, 68327, 68331, 68343, 68352, 68406, 68409, 68438, 68440, 68467, 68472, 68498, 68505, 68509, 68521, 68528, 68608, 68681, 68736, 68787, 68800, 68851, 68858, 68904, 68912, 68922, 69216, 69247, 69248, 69290, 69291, 69294, 69296, 69298, 69373, 69416, 69424, 69466, 69488, 69514, 69552, 69580, 69600, 69623, 69632, 69710, 69714, 69750, 69759, 69827, 69837, 69838, 69840, 69865, 69872, 69882, 69888, 69941, 69942, 69960, 69968, 70007, 70016, 70112, 70113, 70133, 70144, 70162, 70163, 70210, 70272, 70279, 70280, 70281, 70282, 70286, 70287, 70302, 70303, 70314, 70320, 70379, 70384, 70394, 70400, 70404, 70405, 70413, 70415, 70417, 70419, 70441, 70442, 70449, 70450, 70452, 70453, 70458, 70459, 70469, 70471, 70473, 70475, 70478, 70480, 70481, 70487, 70488, 70493, 70500, 70502, 70509, 70512, 70517, 70656, 70748, 70749, 70754, 70784, 70856, 70864, 70874, 71168, 71237, 71248, 71258, 71264, 71277, 71296, 71354, 71360, 71370, 71424, 71451, 71453, 71468, 71472, 71495, 71680, 71740, 71840, 71923, 71935, 71943, 71945, 71946, 71948, 71956, 71957, 71959, 71960, 71990, 71991, 71993, 71995, 72007, 72016, 72026, 72096, 72104, 72106, 72152, 72154, 72165, 72384, 72441, 72448, 72458, 72704, 72713, 72714, 72759, 72760, 72774, 72784, 72813, 72816, 72848, 72850, 72872, 72873, 72887, 72960, 72967, 72968, 72970, 72971, 73015, 73018, 73019, 73020, 73022, 73023, 73032, 73040, 73050, 73056, 73062, 73063, 73065, 73066, 73103, 73104, 73106, 73107, 73113, 73120, 73130, 73440, 73465, 73472, 73489, 73490, 73531, 73534, 73562, 73648, 73649, 73664, 73714, 73727, 74650, 74752, 74863, 74864, 74869, 74880, 75076, 77712, 77811, 92160, 92729, 92736, 92767, 92768, 92778, 92782, 92863, 92864, 92874, 92880, 92910, 92912, 92918, 92928, 92998, 93008, 93018, 93019, 93026, 93027, 93048, 93053, 93072, 93760, 93851, 93952, 94027, 94031, 94088, 94095, 94112, 113664, 113771, 113776, 113789, 113792, 113801, 113808, 113818, 113820, 113828, 119296, 119366, 119488, 119508, 119808, 119893, 119894, 119965, 119966, 119968, 119970, 119971, 119973, 119975, 119977, 119981, 119982, 119994, 119995, 119996, 119997, 120004, 120005, 120070, 120071, 120075, 120077, 120085, 120086, 120093, 120094, 120122, 120123, 120127, 120128, 120133, 120134, 120135, 120138, 120145, 120146, 120486, 120488, 120780, 120782, 120832, 122624, 122655, 122661, 122667, 122880, 122887, 122888, 122905, 122907, 122914, 122915, 122917, 122918, 122923, 122928, 122990, 123023, 123024, 123136, 123181, 123184, 123198, 123200, 123210, 123214, 123216, 123536, 123567, 123584, 123642, 123647, 123648, 124112, 124154, 124896, 124903, 124904, 124908, 124909, 124911, 124912, 124927, 124928, 125125, 125127, 125143, 125184, 125260, 125264, 125274, 125278, 125280, 126065, 126133, 126209, 126270, 126464, 126468, 126469, 126496, 126497, 126499, 126500, 126501, 126503, 126504, 126505, 126515, 126516, 126520, 126521, 126522, 126523, 126524, 126530, 126531, 126535, 126536, 126537, 126538, 126539, 126540, 126541, 126544, 126545, 126547, 126548, 126549, 126551, 126552, 126553, 126554, 126555, 126556, 126557, 126558, 126559, 126560, 126561, 126563, 126564, 126565, 126567, 126571, 126572, 126579, 126580, 126584, 126585, 126589, 126590, 126591, 126592, 126602, 126603, 126620, 126625, 126628, 126629, 126634, 126635, 126652, 126704, 126706, 129024, 129036, 129040, 129096, 129104, 129114, 129120, 129160, 129168, 129198, 129200, 129202, 129792, 129939, 129940, 129995, 130032, 130042, 917505, 917506, 917536, 917632, 917760, 918000 }
local function rotate_in_uax50(i)
local lo, hi = 1, #t
while lo < hi do
@@ -902,9 +903,10 @@ do
end
end
+ local sub = string.sub
local function prepare_extra_data_font(id, res, name)
if type(res)=='table' and (res.psname or res.filename) then
- if (res.embedding=='no') and (type(name)=='string') and (name:sub(1,5)=='psft:') then
+ if (res.embedding=='no') and (type(name)=='string') and (sub(name,1,5)=='psft:') then
font_extra_info[id] = res.resources.ltj_extra
else
local bname = res.psname or nameonly(res.filename)
diff --git a/macros/luatex/generic/luatexja/src/ltj-latex.sty b/macros/luatex/generic/luatexja/src/ltj-latex.sty
index 7e091d161d..9f6e4d0a36 100644
--- a/macros/luatex/generic/luatexja/src/ltj-latex.sty
+++ b/macros/luatex/generic/luatexja/src/ltj-latex.sty
@@ -12,45 +12,14 @@
\def\luatexjalatexLoaded{\endinput}
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{ltj-latex}[2022-10-15 LaTeX support of LuaTeX-ja]
+\ProvidesPackage{ltj-latex}[2024-05-16 LaTeX support of LuaTeX-ja]
\endlinechar=13
%%% LaTeX version detection
-\newif\ifltj@ltfilehook@avail
-\@ifl@t@r\fmtversion{2020-10-01}{%
- \@ifl@t@r\fmtversion{2020-10-02}%
- {\ltj@ltfilehook@availtrue}
- {%
- \ifnum\patch@level<0\relax \ifnum\patch@level<-6 \ltj@ltfilehook@availtrue\fi
- \else\ltj@ltfilehook@availtrue\fi
- }%
-}{}
-\@ifl@t@r\fmtversion{2021-05-01}{\count@=1 }{\count@=0 }
-\ifnum \count@<1
-%%%% \shipout
- \newluafunction\ltj@@lua@shipout@inner
- \directlua{
- local t = lua.get_functions_table()
- local getbox, setbox = tex.getbox, tex.setbox
- local shipout_lthook = luatexja.direction.shipout_lthook
- t[\the\ltj@@lua@shipout@inner]=function ()
- local a = shipout_lthook(getbox(\the\ltj@afbox))
- if a then setbox(\the\ltj@afbox, a) end
- end
- }
- \protected\luadef\ltj@@lua@shipout\ltj@@lua@shipout@inner
- \def\ltj@shipout@{\ltj@@lua@shipout\primitive\shipout\box\ltj@afbox}%
- \protected\def\ltj@shipout{\ltj@afterbox\ltj@shipout@}
- \ifprimitive\shipout\let\shipout\ltj@shipout\fi
- \ifcsname tex_shipout:D\endcsname
- \expandafter\let\csname tex_shipout:D\endcsname\ltj@shipout
- \fi
-\else
- \directlua{%
- luatexbase.add_to_callback('pre_shipout_filter',luatexja.direction.shipout_lthook, 'ltj.direction', 1)
- }
-\fi
+\directlua{%
+ luatexbase.add_to_callback('pre_shipout_filter',luatexja.direction.shipout_lthook, 'ltj.direction', 1)
+}
%%% patch for LaTeX
\usepackage{lltjfont}
@@ -59,36 +28,30 @@
\RequirePackage{lltjp-atbegshi}
%%% patching commands
-\ifltj@ltfilehook@avail
- %% \ltj@ExecuteBeforePackage <package><code> and \ltj@ExecuteAfterPackage <package><code>
- %% no-starred version: do nothing if the <package> is loaded
- %% starred version: do <code> right away if the <package> is loaded
- \def\ltj@ExecuteAfterPackage{\@ifstar\@@ltj@ExecuteAfterPackage@star\@@ltj@ExecuteAfterPackage}
- \def\ltj@ExecuteBeforePackage{\@ifstar\@@ltj@ExecuteBeforePackage@star\@@ltj@ExecuteBeforePackage}
- \@ifl@t@r\fmtversion{2021-11-15}{% 2021-11-15
- \def\@@ltj@ExecuteAfterPackage@star#1{\AddToHook{package/#1/after}}
- \def\@@ltj@ExecuteBeforePackage@star#1{\AddToHook{package/#1/before}}
- \long\def\@@ltj@ExecuteAfterPackage#1#2{%
- \@ifpackageloaded{#1}{}{\AddToHook{package/#1/after}{#2}}%
- }
- \long\def\@@ltj@ExecuteBeforePackage#1#2{%
- \@ifpackageloaded{#1}{}{\AddToHook{package/#1/before}{#2}}%
- }
- }{
- \def\@@ltj@ExecuteAfterPackage#1{\AddToHook{package/after/#1}}
- \def\@@ltj@ExecuteBeforePackage#1{\AddToHook{package/before/#1}}
- \long\def\@@ltj@ExecuteAfterPackage@star#1#2{%
- \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteAfterPackage{#1}{#2}}%
- }
- \long\def\@@ltj@ExecuteBeforePackage@star#1#2{%
- \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteBeforePackage{#1}{#2}}%
- }
+%% \ltj@ExecuteBeforePackage <package><code> and \ltj@ExecuteAfterPackage <package><code>
+%% no-starred version: do nothing if the <package> is loaded
+%% starred version: do <code> right away if the <package> is loaded
+\def\ltj@ExecuteAfterPackage{\@ifstar\@@ltj@ExecuteAfterPackage@star\@@ltj@ExecuteAfterPackage}
+\def\ltj@ExecuteBeforePackage{\@ifstar\@@ltj@ExecuteBeforePackage@star\@@ltj@ExecuteBeforePackage}
+\@ifl@t@r\fmtversion{2021-11-15}{% 2021-11-15
+ \def\@@ltj@ExecuteAfterPackage@star#1{\AddToHook{package/#1/after}}
+ \def\@@ltj@ExecuteBeforePackage@star#1{\AddToHook{package/#1/before}}
+ \long\def\@@ltj@ExecuteAfterPackage#1#2{%
+ \@ifpackageloaded{#1}{}{\AddToHook{package/#1/after}{#2}}%
}
-\else
- \RequirePackage{filehook}
- \let\ltj@ExecuteAfterPackage=\AtEndOfPackageFile
- \let\ltj@ExecuteBeforePackage=\AtBeginOfPackageFile
-\fi
+ \long\def\@@ltj@ExecuteBeforePackage#1#2{%
+ \@ifpackageloaded{#1}{}{\AddToHook{package/#1/before}{#2}}%
+ }
+}{
+ \def\@@ltj@ExecuteAfterPackage#1{\AddToHook{package/after/#1}}
+ \def\@@ltj@ExecuteBeforePackage#1{\AddToHook{package/before/#1}}
+ \long\def\@@ltj@ExecuteAfterPackage@star#1#2{%
+ \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteAfterPackage{#1}{#2}}%
+ }
+ \long\def\@@ltj@ExecuteBeforePackage@star#1#2{%
+ \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteBeforePackage{#1}{#2}}%
+ }
+}
\def\ltj@pkgpatch@im#1{\@ifnextchar[{\ltj@pkgpatch@im@{#1}}{\ltj@pkgpatch@im@{#1}[lltjp-#1]}}%]
\def\ltj@pkgpatch@im@#1[#2]{\ltj@ExecuteAfterPackage*{#1}{\ltj@pkgpatch@im@@{#2}}}
\def\ltj@pkgpatch@im@@#1{%
@@ -101,11 +64,7 @@
}
\def\ltj@pkgpatch#1{\@ifnextchar[{\ltj@pkgpatch@{#1}}{\ltj@pkgpatch@{#1}[lltjp-#1]}}%]
-\ifltj@ltfilehook@avail
- \def\ltj@pkgpatch@@{\AddToHook{begindocument/before}[luatexja.autopatch]}
-\else
- \def\ltj@pkgpatch@@{\gpreto\@begindocumenthook}
-\fi
+\def\ltj@pkgpatch@@{\AddToHook{begindocument/before}[luatexja.autopatch]}
\def\ltj@pkgpatch@#1[#2]{%
\ltj@pkgpatch@@{%
\@ifpackageloaded{#1}{\@ifpackageloaded{#2}{}{\RequirePackage{#2}}}{}%
@@ -121,18 +80,12 @@
\ltj@pkgpatch{tascmac}
\ltj@pkgpatch{ascmac}[lltjp-tascmac]
\ltj@pkgpatch@im{stfloats}
-\ifltj@ltfilehook@avail\else
- \ltj@pkgpatch{preview} %% at \begin{document}
- % (preview package has delayed option)
-\fi
\ltj@pkgpatch{array} %% at \begin{document}
\ltj@pkgpatch{collcell} %% at \begin{document}
\ltj@pkgpatch@im{unicode-math}
\ltj@pkgpatch@im{siunitx}
\ltj@pkgpatch@im{microtype}
-\ifltj@ltfilehook@avail\else\ltj@pkgpatch@im{everyshi}\fi
-
\RequirePackage{lltjp-geometry}
\ltj@ExecuteBeforePackage{breqn}{%
diff --git a/macros/luatex/generic/luatexja/src/ltj-pretreat.lua b/macros/luatex/generic/luatexja/src/ltj-pretreat.lua
index 43ecac806c..8f5632be4e 100644
--- a/macros/luatex/generic/luatexja/src/ltj-pretreat.lua
+++ b/macros/luatex/generic/luatexja/src/ltj-pretreat.lua
@@ -156,16 +156,17 @@ function set_box_stack_level(head, mode)
if ltjs.list_dir == dir_tate then
for p in traverse_glyph(to_direct(head)) do
if getlang(p)==lang_ja and has_attr(p, attr_icflag, 0) then
- local pf = ltjf_replace_altfont(attr_curtfnt, getchar(p), p)
+ local pc = ltjs_orig_char_table[p] or getchar(p)
+ local pf = ltjf_replace_altfont(attr_curtfnt, pc, p)
if ltjf_font_metric_table[pf].vert_activated then
- local pc = ltjf_font_metric_table[pf].vform[pc]; if pc then setchar(p, pc) end
+ pc = ltjf_font_metric_table[pf].vform[pc]; if pc then setchar(p, pc) end
end
end
end
else
for p in traverse_glyph(to_direct(head)) do
if getlang(p)==lang_ja and has_attr(p, attr_icflag, 0) then
- ltjf_replace_altfont(attr_curjfnt, getchar(p), p)
+ ltjf_replace_altfont(attr_curjfnt, ltjs_orig_char_table[p] or getchar(p), p)
end
end
end
diff --git a/macros/luatex/generic/luatexja/src/ltj-rmlgbm.lua b/macros/luatex/generic/luatexja/src/ltj-rmlgbm.lua
index 88352dd5b2..23d20be3f3 100644
--- a/macros/luatex/generic/luatexja/src/ltj-rmlgbm.lua
+++ b/macros/luatex/generic/luatexja/src/ltj-rmlgbm.lua
@@ -15,7 +15,8 @@ do
end
local cidfont_data = {}
local cache_chars = {}
-local cache_ver = 13
+local cache_ver = 14
+local sub = string.sub
local identifiers = fonts.hashes.identifiers
local cid_reg, cid_order, cid_supp, cid_name
@@ -147,7 +148,7 @@ do
descender = 655360*0.12,
},
embedding = "no", cache = "yes", factor = 0, hfactor = 0, vfactor = 0,
- tounicode = 1,
+ tounicode = 1, filename="",
properties = { language = "dflt", script = "dflt" },
}
cidfont_data[cid_name] = k
@@ -300,7 +301,6 @@ local function prepare_cid_font(reg, ord)
return cidfont_data[cid_name] or read_cid_font()
end
-
local definers = fonts.definers
local function mk_rml(name, size, id)
local specification = definers.analyze(name,size)
@@ -308,8 +308,8 @@ local function mk_rml(name, size, id)
specification.detail = specification.detail or ''
do
local n = specification.name
- if n:sub(1,1)=="{" then n=n:sub(2) end
- if n:sub(-1)=="}" then n=n:sub(1,-2) end
+ if sub(n,1,1)=="{" then n=sub(n,2) end
+ if sub(n,-1)=="}" then n=sub(n,1,-2) end
specification.name=n
end
local fontdata = {}
@@ -382,11 +382,11 @@ local function mk_rml(name, size, id)
end
local function font_callback(name, size, id, fallback)
- if name:sub(1,1)=="{" and name:sub(-1)=="}" then name = name:sub(2,-2) end
+ if sub(name,1,1)=="{" and sub(name,-1)=="}" then name = sub(name,2,-2) end
local p = name:find(":") or 0
- if name:sub(1, p-1) == 'psft' then
+ if sub(name,1, p-1) == 'psft' then
local s = "Adobe-Japan1-7"
- local basename = name:sub(p+1)
+ local basename = sub(name,p+1)
local p = basename:find(":")
local q = basename:find("/[BI][BI]?")
if q and p and q<=p then
@@ -394,12 +394,12 @@ local function font_callback(name, size, id, fallback)
p = basename:find(":")
end
if p then
- local xname = basename:sub(p+1)
+ local xname = sub(basename,p+1)
p = 1
while p do
local q = xname:find(";", p+1) or xname:len()+1
- if xname:sub(p, p+3)=='cid=' and q>p+4 then
- s = xname:sub(p+4, q-1)
+ if sub(xname,p, p+3)=='cid=' and q>p+4 then
+ s = sub(xname,p+4, q-1)
end
if xname:len()+1==q then p = nil else p = q + 1 end
end
diff --git a/macros/luatex/generic/luatexja/src/ltj-ruby.lua b/macros/luatex/generic/luatexja/src/ltj-ruby.lua
index 29b6fe1998..2ff41be947 100644
--- a/macros/luatex/generic/luatexja/src/ltj-ruby.lua
+++ b/macros/luatex/generic/luatexja/src/ltj-ruby.lua
@@ -3,7 +3,7 @@
--
luatexbase.provides_module({
name = 'luatexja.ruby',
- date = '2022-12-31',
+ date = '2024-09-06',
description = 'Ruby annotation',
})
luatexja.ruby = {}
@@ -37,7 +37,7 @@ local setwhd = node.direct.setwhd
local setlist = node.direct.setlist
local setvalue = node.direct.setdata
-local node_new = node.direct.new
+local node_new = luatexja.dnode_new
local node_remove = node.direct.remove
local node_next = node.direct.getnext
local node_copy, node_tail = node.direct.copy, node.direct.tail
@@ -45,6 +45,7 @@ local node_free = node.direct.flush_node or node.direct.free
local get_attr, set_attr = node.direct.get_attribute, node.direct.set_attribute
local insert_before, insert_after = node.direct.insert_before, node.direct.insert_after
local hpack = node.direct.hpack
+local node_inherit_attr = luatexja.node_inherit_attr
local id_hlist = node.id 'hlist'
local id_vlist = node.id 'vlist'
@@ -175,10 +176,10 @@ do
set_attr(nh, attr_icflag,
get_attr_icflag(nh) + PROCESSED_BEGIN_FLAG)
setnext(node_tail(h), nh)
- setlist(f, nil); node_free(f)
+ setlist(f, nil)
setlist(b, nil); node_free(b)
local g = luatexja.jfmglue.main(h,false)
- return hpack(g)
+ g = node_inherit_attr(hpack(g),f); node_free(f); return g
else
return f
end
@@ -207,6 +208,7 @@ end
-- box の中身のノードは再利用される
local enlarge
do
+ local dimensions = node.direct.dimensions
local FROM_JFM = luatexja.icflag_table.FROM_JFM
local PROCESSED = luatexja.icflag_table.PROCESSED
local KANJI_SKIP = luatexja.icflag_table.KANJI_SKIP
@@ -239,18 +241,22 @@ do
hx = node_next(hx)
end
-- 先頭の空白を挿入
- local k = node_new(id_glue);
- setglue(k, prenw, round(pre*65536), 0, 2, 0)
- h = insert_before(h, h, k);
+ local k1 = node_new(id_glue);
+ setglue(k1, prenw, round(pre*65536), 0, 2, 0)
+ h = insert_before(h, h, k1);
-- 末尾の空白を挿入
- local k = node_new(id_glue);
- setglue(k, postnw, round(post*65536), 0, 2, 0)
- insert_after(h, node_tail(h), k);
+ local k2 = node_new(id_glue);
+ setglue(k2, postnw, round(post*65536), 0, 2, 0)
+ insert_after(h, node_tail(h), k2);
-- hpack
- setlist(box, nil); node_free(box)
- box = hpack(h, new_width, 'exactly')
- setheight(box, hh); setdepth(box, hd)
- return box
+ setlist(box, nil);
+ local new_box = node_inherit_attr(hpack(h, new_width, 'exactly'), box)
+ setheight(new_box, hh); setdepth(new_box, hd)
+ node_free(box);
+ local gset, gsign, go
+ = getfield(new_box,'glue_set'), getfield(new_box,'glue_sign'), getfield(new_box,'glue_order')
+ return new_box, (dimensions(gset, gsign, go, k1, node_next(k1))), (dimensions(gset, gsign, go, k2))
+ -- return value: (enlarged box), (width of left glue), (width of right glue)
end
end
@@ -307,15 +313,18 @@ function luatexja.ruby.texiface(rst, rtlr, rtlp)
end
----------------------------------------------------------------
--- pre_line_break
+-- pre_linebreak
----------------------------------------------------------------
-- r, p の中身のノードは再利用される
local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end)
-- r: ルビ部分の格納された box,p: 同,親文字
+ -- no_begin: 行頭形ではないか
+ -- no_end: 行末形ではないか
local rwidth = getwidth(r)
local sumprot = rwidth - getwidth(p) -- >0
local pre_intrusion, post_intrusion
+ local pre_protrusion, post_protrusion
local ppre, pmid, ppost = tmp_tbl.ppre, tmp_tbl.pmid, tmp_tbl.ppost
local mapre, mapost = tmp_tbl.mapre, tmp_tbl.mapost
local intmode = (tmp_tbl.mode//4)%8
@@ -325,17 +334,17 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end)
mapre = min(mapre,mapost); mapost = mapre
end
if intmode == 0 then -- とりあえず組んでから決める
- p = enlarge(p, rwidth, ppre, pmid, ppost, 0, 0)
+ p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, 0, 0)
pre_intrusion = min(mapre, round(ppre*getfield(p, 'glue_set')*65536))
post_intrusion = min(mapost, round(ppost*getfield(p, 'glue_set')*65536))
elseif intmode == 1 then
pre_intrusion = min(mapre, sumprot);
post_intrusion = min(mapost, max(sumprot-pre_intrusion, 0))
- p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
+ p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
elseif intmode == 2 then
post_intrusion = min(mapost, sumprot);
pre_intrusion = min(mapre, max(sumprot-post_intrusion, 0))
- p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
+ p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
elseif intmode==3 then
local n = min(mapre, mapost)*2
if n < sumprot then
@@ -343,7 +352,7 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end)
else
pre_intrusion = sumprot//2; post_intrusion = sumprot - pre_intrusion
end
- p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
+ p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
pre_intrusion = min(mapre, pre_intrusion + round(ppre*getfield(p, 'glue_set')*65536))
post_intrusion = min(mapost, post_intrusion + round(ppost*getfield(p, 'glue_set')*65536))
else -- intmode == 4
@@ -354,7 +363,7 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end)
else
pre_intrusion, post_intrusion = mapre, min(mapost, sumprot-mapre)
end
- p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
+ p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion)
end
setshift(r, -pre_intrusion)
local rwidth = rwidth - pre_intrusion - post_intrusion
@@ -371,24 +380,29 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end)
post_jfmgk = (post_intrusion > 0)
end
end
- return r, p, orig_post_intrusion, post_jfmgk
+ return r, p, orig_post_intrusion, post_jfmgk,
+ pre_protrusion - pre_intrusion, post_protrusion - post_intrusion
end
-- ルビボックスの生成(単一グループ)
-- returned value: <new box>, <ruby width>, <post_intrusion>
local max_margin
-local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end)
+local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end, w)
+ -- no_begin: 行頭形ではないか
+ -- no_end: 行末形ではないか
local post_intrusion, post_jfmgk = 0, false
local imode
local ppre, pmid, ppost = tmp_tbl.ppre, tmp_tbl.pmid, tmp_tbl.ppost
local mapre, mapost = tmp_tbl.mapre, tmp_tbl.mapost
local rpre, rmid, rpost, rsmash
+ local protrusion = {0, 0}; tmp_tbl[tmp_tbl.index] = protrusion
imode = tmp_tbl.mode//0x100000; rsmash = (imode%2 ==1)
imode = imode//2; rpost = imode%8;
imode = (imode-rpost)/8; rmid = imode%8;
imode = (imode-rmid)/8; rpre = imode%8
if getwidth(r) > getwidth(p) then -- change the width of p
- r, p, post_intrusion, post_jfmgk = enlarge_parent(r, p, tmp_tbl, no_begin, no_end)
+ r, p, post_intrusion, post_jfmgk, protrusion[1], protrusion[2]
+ = enlarge_parent(r, p, tmp_tbl, no_begin, no_end)
elseif getwidth(r) < getwidth(p) then -- change the width of r
r = enlarge(r, getwidth(p), rpre, rmid, rpost, 0, 0)
post_intrusion = 0
@@ -406,7 +420,7 @@ local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end)
end
if need_repack then
local rt = r
- r = hpack(getlist(r), getwidth(r), 'exactly')
+ r = node_inherit_attr(hpack(getlist(r), getwidth(r), 'exactly'), r)
setlist(rt, nil); node_free(rt);
end
end
@@ -416,7 +430,8 @@ local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end)
insert_after(r, k, p); setnext(p, nil)
if tmp_tbl.rubydepth >= 0 then setdepth(r, tmp_tbl.rubydepth) end
if tmp_tbl.baseheight >= 0 then setheight(p, tmp_tbl.baseheight) end
- a = node.direct.vpack(r); setshift(a, 0)
+ a = node_inherit_attr(node.direct.vpack(r), p)
+ setshift(a, 0)
set_attr(a, attr_ruby, post_intrusion)
set_attr(a, attr_ruby_post_jfmgk, post_jfmgk and 1 or 0)
if rsmash or getheight(a)<getheight(p) then
@@ -437,7 +452,10 @@ local max_allow_pre, max_allow_post
local flush_list = node.direct.flush_list
-- 中付き熟語ルビ,cmp containers
-- 「文字の構成を考えた」やつはどうしよう
-local function pre_low_cal_box(w, cmp)
+local pre_low_cal_box
+do
+ local node_prev = node.direct.getprev
+pre_low_cal_box = function (w, cmp)
local rb = {}
local pb = {}
local kf = {}
@@ -450,18 +468,24 @@ local function pre_low_cal_box(w, cmp)
local coef = {} -- 連立一次方程式の拡大係数行列
local rtb = expand_3bits(rst.stretch)
+ local kb, ka
+ if rst.quirk_protrusion then
+ kb = node_prev(node_tail(wv)); ka = node_next(kb)
+ node_remove(wv, kb); node_remove(wv, ka)
+ end
-- node list 展開・行末形の計算
local nt, nta, ntb = wv, nil, nil -- nt*: node temp
rst.ppre, rst.pmid, rst.ppost = rtb[6], rtb[5], rtb[4]
rst.mapre, rst.mapost = max_allow_pre, 0
- for i = 1, cmp do
+ for i = 1, cmp do
nt = node_next(nt); rb[i] = nt; nta = concat(nta, node_copy(nt))
nt = node_next(nt); pb[i] = nt; ntb = concat(ntb, node_copy(nt))
coef[i] = {}
for j = 1, 2*i do coef[i][j] = 1 end
for j = 2*i+1, 2*cmp+1 do coef[i][j] = 0 end
+ rst.index = i
kf[i], coef[i][2*cmp+2]
- = new_ruby_box(node_copy(nta), node_copy(ntb), rst, true, false)
+ = new_ruby_box(node_copy(nta), node_copy(ntb), rst, true, false, w)
end
node_free(nta); node_free(ntb)
@@ -474,8 +498,9 @@ local function pre_low_cal_box(w, cmp)
for j = 1, 2*i-1 do coef[cmp+i][j] = 0 end
for j = 2*i, 2*cmp+1 do coef[cmp+i][j] = 1 end
nta = concat(node_copy(rb[i]), nta); ntb = concat(node_copy(pb[i]), ntb)
+ rst.index = 2*cmp+1-i
kf[cmp+i], coef[cmp+i][2*cmp+2]
- = new_ruby_box(node_copy(nta), node_copy(ntb), rst, false, true)
+ = new_ruby_box(node_copy(nta), node_copy(ntb), rst, false, true, w)
end
-- ここで,nta, ntb には全 container を連結した box が入っているので
@@ -484,13 +509,17 @@ local function pre_low_cal_box(w, cmp)
for j = 1, 2*cmp+1 do coef[2*cmp+1][j] = 1 end
rst.ppre, rst.pmid, rst.ppost = rtb[3], rtb[2], rtb[1]
rst.mapre, rst.mapost = max_allow_pre, max_allow_post
+ rst.index = 2*cmp+1
kf[2*cmp+1], coef[2*cmp+1][2*cmp+2], post_intrusion_backup, post_jfmgk_backup
- = new_ruby_box(nta, ntb, rst, true, true)
+ = new_ruby_box(nta, ntb, rst, true, true, w)
-- w.value の node list 更新.
local nt = wv
flush_list(node_next(wv))
for i = 1, 2*cmp+1 do setnext(nt, kf[i]); nt = kf[i] end
+ if rst.quirk_protrusion then
+ insert_after(wv, nt, kb); insert_after(wv, kb, ka);
+ end
if cmp==1 then solve_1(coef)
elseif cmp==2 then solve_2(coef)
@@ -499,6 +528,7 @@ local function pre_low_cal_box(w, cmp)
end
return coef
end
+end
local first_whatsit
do
@@ -512,25 +542,40 @@ do
end
local next_cluster_array = {}
+local pre_high
-- ノード追加
-local function pre_low_app_node(head, w, cmp, coef, ht, dp)
+do
+ local node_prev = node.direct.getprev
+ local KINSOKU = luatexja.icflag_table.KINSOKU
+ local function pre_low_app_node (head, w, cmp, coef, ht, dp)
-- メインの node list 更新
- local nt = node_new(id_glue)
+ local nt = node_new(id_glue, nil, w) -- INHERIT ATTRIBUTES OF w
setglue(nt, coef[1][2*cmp+2], 0, 0, 0, 0)
set_attr(nt, attr_ruby, 1); set_attr(w, attr_ruby, 2)
head = insert_before(head, w, nt)
nt = w
for i = 1, cmp do
-- rule
- local nta = node_new(id_rule, 0);
+ local nta = node_new(id_rule, 0, w); -- INHERIT ATTRIBUTES OF w
setwhd(nta, coef[i*2][2*cmp+2], ht, dp)
insert_after(head, nt, nta)
set_attr(nta, attr_ruby, 2*i+1)
-- glue
if i~=cmp or not next_cluster_array[w] then
- nt = node_new(id_glue); insert_after(head, nta, nt)
+ nt = node_new(id_glue, nil, w); -- INHERIT ATTRIBUTE OF w
+ insert_after(head, nta, nt)
else
- nt = next_cluster_array[w]
+ nt = next_cluster_array[w]; local f = node_next(nta)
+ while f and f~=nt do
+ if getid(f)==id_glue then
+ node_remove(head, nt); insert_before(head, f, nt); f = nil
+ else if getid(f)==id_penalty and get_attr_icflag(f)==KINSOKU then
+ node_inherit_attr(f, w) -- INHERIT ATTRIBUTE OF w; for lua-ul
+ set_attr(f, attr_icflag, KINSOKU)
+ end
+ f = node_next(f)
+ end
+ end
end
setglue(nt, coef[i*2+1][2*cmp+2], 0, 0, 0, 0)
set_attr(nt, attr_ruby, 2*i+2)
@@ -540,14 +585,13 @@ local function pre_low_app_node(head, w, cmp, coef, ht, dp)
next_cluster_array[w]=nil
return head, first_whatsit(node_next(nt))
end
-
-local function pre_high(ahead)
+pre_high = function (ahead)
if not ahead then return ahead end
local head = to_direct(ahead)
post_intrusion_backup, post_jfmgk_backup = 0, false
local n = first_whatsit(head)
while n do
- if getsubtype(n) == sid_user and getfield(n, 'user_id') == RUBY_PRE then
+ if getsubtype(n) == sid_user and getfield(n, 'user_id') == RUBY_PRE then
local nv = getvalue(n)
local rst = getvalue(nv)
max_allow_pre = rst.pre or 0
@@ -567,13 +611,13 @@ local function pre_high(ahead)
if rst.exclude_pre_jfmgk_from_prev_ruby
and atr>0 and ((old_break_info[atr] or post_intrusion_backup) > 0) then
-- 「直前のルビが文字に進入→現在のルビの和文処理グルーへの進入はなし」という状況
- rst.before_jfmgk = 0
+ max_allow_pre = 0;rst.before_jfmgk = 0
end
post_intrusion_backup, post_jfmgk_backup = 0, false
max_allow_post = rst.post or 0
max_margin = rst.maxmargin or 0
local coef = pre_low_cal_box(n, rst.count)
- local s = node_tail(nv) --ルビ文字
+ local s = node_tail(nv); if rst.quirk_protrusion then s = node_prev(node_prev(s)) end --ルビ文字
head, n = pre_low_app_node(
head, n, rst.count, coef, getheight(s), getdepth(s)
)
@@ -583,11 +627,11 @@ local function pre_high(ahead)
end
return to_node(head)
end
+end
luatexbase.add_to_callback('pre_linebreak_filter', pre_high, 'ltj.ruby.pre', 100)
-luatexbase.add_to_callback('hpack_filter', pre_high, 'ltj.ruby.pre', 100)
----------------------------------------------------------------
--- post_line_break
+-- post_linebreak
----------------------------------------------------------------
local post_lown
do
@@ -595,47 +639,110 @@ do
local id = get_attr(wv, attr_ruby_id) or 0
if id>0 and cache_handle then
cache_handle:write(
- 'lrob[' .. tostring(id) .. ']=' .. num .. '\nlrob[' .. tostring(-id) .. ']=' .. tostring(bool) .. '\n')
+ 'lrob[' .. tostring(id) .. ']=' .. tostring(num) .. '\nlrob[' .. tostring(-id) .. ']=' .. tostring(bool) .. '\n')
end
end
-
+ local node_prev = node.direct.getprev
+ local function new_kern(inherit, num)
+ local k = node_new(id_kern, 1, inherit); setkern(k, num); set_attr(kt, attr_icflag, PROCESSED);
+ return k;
+ end
post_lown = function (rs, rw, cmp, ch)
-- ch: the head of `current' hlist
if #rs ==0 or not rw then return ch end
local hn = get_attr(rs[1], attr_ruby)
local fn = get_attr(rs[#rs], attr_ruby)
- local wv = getvalue(rw)
+ local wv = getvalue(rw); local rst = getvalue(wv)
if hn==1 then
if fn==2*cmp+2 then
- local hn = node_tail(wv)
- node_remove(wv, hn)
- insert_after(ch, rs[1], hn)
- set_attr(hn, attr_icflag, PROCESSED)
- write_aux(wv, get_attr(hn, attr_ruby), get_attr(hn, attr_ruby_post_jfmgk))-- 行中形
+ -- 行中形
+ local nn = node_tail(wv);
+ if rst.quirk_protrusion then
+ local prot_b,prot_a = rst[fn-1][1], rst[fn-1][2]
+ local ka=nn; local kb = node_prev(nn); nn = node_prev(kb)
+ node_remove(wv, nn); node_remove(wv, kb); node_remove(wv, ka)
+ if prot_b~=0 then
+ setkern(kb, prot_b); insert_after(ch, rs[1], kb);
+ local kt = new_kern(rw, -prot_b); insert_after(ch, kb, kt);
+ insert_after(ch, kt, nn);
+ else
+ node_free(kb); insert_after(ch, rs[1], nn)
+ end
+ if prot_a~=0 then
+ local kt = new_kern(rw, -prot_a); insert_after(ch, nn, kt);
+ setkern(ka, prot_a); insert_after(ch, kt, ka);
+ else node_free(ka)
+ end
+ else
+ node_remove(wv, nn); insert_after(ch, rs[1], nn)
+ end
+ set_attr(nn, attr_icflag, PROCESSED)
+ write_aux(wv, get_attr(nn, attr_ruby), get_attr(nn, attr_ruby_post_jfmgk))
else
- local deg, hn = (fn-1)/2, wv
- for i = 1, deg do hn = node_next(hn) end;
- node_remove(wv, hn)
- setnext(hn, nil)
- insert_after(ch, rs[1], hn)
- set_attr(hn, attr_icflag, PROCESSED)
- write_aux(wv, get_attr(hn, attr_ruby), get_attr(hn, attr_ruby_post_jfmgk))
+ -- 行末形
+ local deg, nn = (fn-1)/2, wv; for i = 1, deg do nn = node_next(nn) end
+ if rst.quirk_protrusion then
+ local prot_b = rst[deg][1]
+ local ka = node_tail(wv); local kb = node_prev(ka);
+ node_remove(wv, nn); setnext(nn, nil); node_remove(wv, kb);
+ if prot_b~=0 then
+ setkern(kb, prot_b); insert_after(ch, rs[1], kb);
+ local kt = new_kern(rw, -prot_b); insert_after(ch, kb, kt);
+ insert_after(ch, kt, nn);
+ else
+ node_free(kb); insert_after(ch, rs[1], nn)
+ end
+ if deg==cmp then
+ local prot_a = rst[deg][2]; node_remove(wv,ka)
+ if prot_a~=0 then
+ local kt = new_kern(rw, -prot_a); insert_after(ch, nn, kt);
+ setkern(ka, prot_a); insert_after(ch, kt, ka);
+ else node_free(ka)
+ end
+ end
+ else
+ node_remove(wv, nn); setnext(nn, nil); insert_after(ch, rs[1], nn)
+ end
+
+ set_attr(nn, attr_icflag, PROCESSED)
+ write_aux(wv, get_attr(nn, attr_ruby), get_attr(nn, attr_ruby_post_jfmgk))
end
else
- local deg, hn = max((hn-1)/2,2), wv
- for i = 1, cmp+deg-1 do hn = node_next(hn) end
+ -- 行頭形
+ local nn = wv; for i = 1, cmp+max((hn-1)/2,2)-1 do nn = node_next(nn) end
-- -1 is needed except the case hn = 3,
-- because a ending-line form is removed already from the list
- node_remove(wv, hn); setnext(hn, nil)
- insert_after(ch, rs[1], hn)
- set_attr(hn, attr_icflag, PROCESSED)
+ if rst.quirk_protrusion then
+ local deg=(hn-1)/2; local prot_b = rst[2*cmp+1-deg][1]
+ local ka = node_tail(wv); node_remove(wv, nn); setnext(nn, nil);
+ if deg==1 then
+ local kb = node_prev(ka); node_remove(wv, kb);
+ if prot_b~=0 then
+ setkern(kb, prot_b); insert_after(ch, rs[1], kb);
+ local kt = new_kern(rw, -prot_b); insert_after(ch, kb, kt);
+ insert_after(ch, kt, nn);
+ else
+ node_free(kb); insert_after(ch, rs[1], nn)
+ end
+ else insert_after(ch, rs[1], nn)
+ end
+ local prot_a = rst[2*cmp+1-deg][2]; node_remove(wv,ka)
+ if prot_a~=0 then
+ local kt = new_kern(rw, -prot_a); insert_after(ch, nn, kt);
+ setkern(ka, prot_a); insert_after(ch, kt, ka);
+ else node_free(ka)
+ end
+ else
+ node_remove(wv, nn); setnext(nn, nil); insert_after(ch, rs[1], nn)
+ end
+
+ set_attr(nn, attr_icflag, PROCESSED)
if fn == 2*cmp-1 then
- write_aux(wv, get_attr(hn, attr_ruby), get_attr(hn, attr_ruby_post_jfmgk))
+ write_aux(wv, get_attr(nn, attr_ruby), get_attr(nn, attr_ruby_post_jfmgk))
end
end
for i = 1,#rs do
- local ri = rs[i]
- ch = node_remove(ch, ri); node_free(ri);
+ local ri = rs[i]; ch = node_remove(ch, ri); node_free(ri)
end
-- cleanup
if fn >= 2*cmp+1 then node_free(rw) end
@@ -644,73 +751,86 @@ do
end
local traverse_id = node.direct.traverse_id
-local function post_high_break(head)
- local rs = {} -- rs: sequence of ruby_nodes,
- local rw = nil -- rw: main whatsit
- local cmp = -2 -- dummy
- for h in traverse_id(id_hlist, to_direct(head)) do
- for i = 1, #rs do rs[i] = nil end
- local ha = getlist(h)
+local post_high_hbox, post_high_break
+do
+ local rs, rw = {}, nil -- rs: sequence of ruby_nodes, rw: main whatsit
+ local cmp
+ post_high_break = function (head)
+ for h in traverse_id(id_hlist, to_direct(head)) do
+ for i = 1, #rs do rs[i] = nil end
+ local ha = getlist(h);
+ while ha do
+ local hai = getid(ha)
+ local i = ((hai == id_glue and getsubtype(ha)==0)
+ or (hai == id_rule and getsubtype(ha)==0)
+ or (hai == id_whatsit and getsubtype(ha)==sid_user
+ and getfield(ha, 'user_id', RUBY_POST)))
+ and get_attr(ha, attr_ruby) or 0
+ if i==0 then
+ ha = node_next(ha)
+ elseif i==1 then
+ setlist(h, post_lown(rs, rw, cmp, getlist(h)))
+ for i = 2, #rs do rs[i] = nil end -- rs[1] is set by the next statement
+ rs[1], rw = ha, nil; ha = node_next(ha)
+ elseif i==2 then
+ rw = ha
+ cmp = getvalue(getvalue(rw)).count
+ local hb, hc = node_remove(getlist(h), rw)
+ setlist(h, hb); ha = hc
+ else -- i>=3
+ rs[#rs+1] = ha; ha = node_next(ha)
+ end
+ end
+ setlist(h, post_lown(rs, rw, cmp, getlist(h)))
+ end
+ return head
+ end
+ post_high_hbox = function (ahead)
+ for i = 1, #rs do rs[i] = nil end; rw = nil
+ local ha = to_direct(ahead); local head = ha
while ha do
local hai = getid(ha)
local i = ((hai == id_glue and getsubtype(ha)==0)
- or (hai == id_rule and getsubtype(ha)==0)
- or (hai == id_whatsit and getsubtype(ha)==sid_user
- and getfield(ha, 'user_id', RUBY_POST)))
- and get_attr(ha, attr_ruby) or 0
+ or (hai == id_rule and getsubtype(ha)==0)
+ or (hai == id_whatsit and getsubtype(ha)==sid_user
+ and getfield(ha, 'user_id', RUBY_POST)))
+ and get_attr(ha, attr_ruby) or 0
if i==0 then
ha = node_next(ha)
elseif i==1 then
- setlist(h, post_lown(rs, rw, cmp, getlist(h)))
+ head = post_lown(rs, rw, cmp, head)
for i = 2, #rs do rs[i] = nil end -- rs[1] is set by the next statement
rs[1], rw = ha, nil; ha = node_next(ha)
elseif i==2 then
rw = ha
cmp = getvalue(getvalue(rw)).count
- local hb, hc = node_remove(getlist(h), rw)
- setlist(h, hb); ha = hc
- else -- i>=3
+ head, ha = node_remove(head, rw)
+ else -- i >= 3
rs[#rs+1] = ha; ha = node_next(ha)
end
end
- setlist(h, post_lown(rs, rw, cmp, getlist(h)))
+ return to_node(post_lown(rs, rw, cmp, head))
end
- return head
end
-
-local function post_high_hbox(ahead)
- local ha = to_direct(ahead); local head = ha
- local rs = {}; -- rs: sequence of ruby_nodes,
- local rw = nil; -- rw: main whatsit
- local cmp
- while ha do
- local hai = getid(ha)
- local i = ((hai == id_glue and getsubtype(ha)==0)
- or (hai == id_rule and getsubtype(ha)==0)
- or (hai == id_whatsit and getsubtype(ha)==sid_user
- and getfield(ha, 'user_id', RUBY_POST)))
- and get_attr(ha, attr_ruby) or 0
- if i==0 then
- ha = node_next(ha)
- elseif i==1 then
- head = post_lown(rs, rw, cmp, head)
- for i = 2, #rs do rs[i] = nil end -- rs[1] is set by the next statement
- rs[1], rw = ha, nil; ha = node_next(ha)
- elseif i==2 then
- rw = ha
- cmp = getvalue(getvalue(rw)).count
- head, ha = node_remove(head, rw)
- else -- i >= 3
- rs[#rs+1] = ha; ha = node_next(ha)
- end
+do
+ -- pre_append_to_vlist_filter.lua: distributed with lua-ul
+ local found = kpse.find_file('pre_append_to_vlist_filter.lua', 'lua')
+ if not found then
+ luatexbase.add_to_callback('post_linebreak_filter', post_high_break, 'ltj.ruby.post_break')
+ else
+ require 'pre_append_to_vlist_filter'
+ luatexbase.add_to_callback('pre_append_to_vlist_filter',
+ function(head, loc)
+ return (loc~='post_linebreak') and head or post_high_break(head)
+ end,
+ 'ltj.ruby.post_break',
+ (luatexbase.priority_in_callback('pre_append_to_vlist_filter', 'add underlines to list') or 1))
end
- return to_node(post_lown(rs, rw, cmp, head))
+ luatexbase.add_to_callback('hpack_filter',
+ function(head) return post_high_hbox(pre_high(head)) end, 'ltj.ruby',
+ luatexbase.priority_in_callback('hpack_filter', 'add underlines to list') or nil)
end
-luatexbase.add_to_callback('post_linebreak_filter', post_high_break, 'ltj.ruby.post_break', 100)
-luatexbase.add_to_callback('hpack_filter', post_high_hbox, 'ltj.ruby.post_hbox', 101)
-
-
----------------------------------------------------------------
-- for jfmglue callbacks
----------------------------------------------------------------
@@ -736,7 +856,7 @@ do
p = 0
end
rst.pre = -p; rst.exclude_pre_from_prev_ruby = (s<0);
- rst.exclude_pre_jfmgk_from_prev_ruby = (ltjs.table_current_stack[RIPOST +Nq.char] or 0)<0;
+ rst.exclude_pre_jfmgk_from_prev_ruby = (ltjs.table_current_stack[RIPOST +Nq.char] or 0)<0;
end
if Nq.prev_ruby then
set_attr(lp, attr_ruby, Nq.prev_ruby)
@@ -758,6 +878,11 @@ do
elseif rst.pre < 0 then -- auto
rst.pre = 0
end
+ if rst.quirk_protrusion then
+ local lk = node_new(id_kern, 1, Nq.nuc, lp); set_attr(lk, attr_icflag, PROCESSED)
+ insert_after(lpv, node_tail(lpv), lk)
+ insert_after(lpv, node_tail(lpv), node_new(id_kern, 1))
+ end
return Np
else
return Np
@@ -780,10 +905,12 @@ do
end
local RIPOST = luatexja.stack_table_index.RIPOST
local abs = math.abs
+ local ltjj_after_hlist=luatexja.jfmglue.after_hlist
+ local ltjj_check_box_high=luatexja.jfmglue.check_box_high
local function whatsit_after_callback(s, Nq, Np, head)
if not s and getfield(Nq.nuc, 'user_id') == RUBY_PRE then
if Np then
- local last_glue = node_new(id_glue)
+ local last_glue = node_new(id_glue, nil, Nq.nuc) -- INHERIT ATTRIBUTE OF Nq.nuc
set_attr(last_glue, attr_icflag, 0)
insert_before(Nq.nuc, Np.first, last_glue)
Np.first = last_glue
@@ -798,8 +925,8 @@ do
end
local x = node_next(node_next(nqnv))
for i = 2, rst.count do x = node_next(node_next(x)) end
- Nq.last_char = luatexja.jfmglue.check_box_high(Nq, getlist(x), nil)
- luatexja.jfmglue.after_hlist(Nq)
+ Nq.last_char = ltjj_check_box_high(Nq, getlist(x), nil)
+ ltjj_after_hlist(Nq)
if Np and Np.id ~=id_pbox_w and type(Np.char)=='number' then
-- Np is a JAchar
if rst.post < 0 then -- auto
@@ -812,12 +939,15 @@ do
Np.prev_ruby = get_attr(getvalue(Nq.nuc), attr_ruby_id)
-- 前のクラスタがルビであったことのフラグ
else -- 直前が文字以外
- local nqnv = getvalue(Nq.nuc)
- local rst = getvalue(nqnv)
if rst.post < 0 then -- auto
rst.post = 0
end
end
+ if rst.quirk_protrusion then
+ local lk = node_tail(nqnv);
+ node_inherit_attr(lk, Nq.nuc, Np and Np.nuc)
+ set_attr(lk, attr_icflag, PROCESSED)
+ end
return head
else
return s
diff --git a/macros/luatex/generic/luatexja/src/ltjsclasses.dtx b/macros/luatex/generic/luatexja/src/ltjsclasses.dtx
index a151210339..19f54e35a1 100644
--- a/macros/luatex/generic/luatexja/src/ltjsclasses.dtx
+++ b/macros/luatex/generic/luatexja/src/ltjsclasses.dtx
@@ -44,7 +44,7 @@
%<*driver>
\ProvidesFile{ltjsclasses.dtx}
%</driver>
- [2022/09/12 ltjsclasses ]
+ [2023/07/26 ltjsclasses ]
%<*driver>
\IfFileExists{luatex85.sty}{\RequirePackage{luatex85}}{}
\documentclass[disablejfam]{ltjsarticle}
@@ -5139,6 +5139,13 @@
%
% [2018-03-11] |\next|などいくつかの内部命令を |\jsc@...| 付きの
% ユニークな名前にしました。
+%
+% [2022-09-13] \LaTeXe~2021-11-15 (ltfloat.dtx 2021/10/14 v1.2g)で
+% |\@currentcounter|が追加されましたので,追随します。
+% なお,\LaTeXe~2021-06-01 (ltfloat.dtx 2021/02/10 v1.2e)で
+% parhook対応として\cs{par}が追加されていますが,
+% 実は同時に\cs{color@endgroup}も\cs{endgraf}するように変更
+% されていますので,不要だと思います。というわけで追加しません。
% \begin{macrocode}
\long\def\@footnotetext{%
\insert\footins\bgroup
diff --git a/macros/luatex/generic/luatexja/src/luatexja-core.sty b/macros/luatex/generic/luatexja/src/luatexja-core.sty
index a5801ed386..341c7aa37d 100644
--- a/macros/luatex/generic/luatexja/src/luatexja-core.sty
+++ b/macros/luatex/generic/luatexja/src/luatexja-core.sty
@@ -68,7 +68,7 @@
\expandafter\let\csname ifltj@in@latex\expandafter\endcsname
\csname iftrue\endcsname
\NeedsTeXFormat{LaTeX2e}
- \ProvidesPackage{luatexja-core}[2023-04-06 Core of LuaTeX-ja]
+ \ProvidesPackage{luatexja-core}[2023-10-07 Core of LuaTeX-ja]
\endlinechar=13 %
\RequirePackage{infwarerr}
\endlinechar=-1 %
@@ -774,10 +774,9 @@
%%------------------ Load module for the format
-
\directlua{luatexja.load_kinsoku()}
\ifltj@in@latex %<*LaTeX>
- \RequirePackage{ltj-latex}
+ \usepackage{ltj-latex}
\else\ifltj@in@plain %<*plain>
\input ltj-plain.sty %
\fi\fi %</LaTeX|plain>
diff --git a/macros/luatex/generic/luatexja/src/luatexja.lua b/macros/luatex/generic/luatexja/src/luatexja.lua
index 1013684496..e815ba3d68 100644
--- a/macros/luatex/generic/luatexja/src/luatexja.lua
+++ b/macros/luatex/generic/luatexja/src/luatexja.lua
@@ -423,9 +423,10 @@ local utfchar = utf.char
local function debug_show_node_X(p,print_fn, limit, inner_depth)
local k = prefix
local s
- local pt, pic = node_type(p.id), (get_attr(p, attr_icflag) or 0) % icflag_table.PROCESSED_BEGIN_FLAG
- local base = prefix .. '[' .. string.format('%7d', node.direct.todirect(p)) .. '] ' ..
- string.format('%X', pic) .. ' ' .. pt .. ' ' .. tostring(p.subtype) .. ' '
+ local pt, pic = node_type(p.id), (get_attr(p, attr_icflag) or 0) --% icflag_table.PROCESSED_BEGIN_FLAG
+ local base = prefix .. '[' .. string.format('%7d', node.direct.todirect(p))
+ .. ', ' .. tostring(get_attr(p, 27)) .. '] '
+ .. string.format('%X', pic) .. ' ' .. pt .. ' ' .. tostring(p.subtype) .. ' '
if pt == 'glyph' then
s = base .. ' '
.. (p.char<0xF0000 and utfchar(p.char) or '')
diff --git a/macros/luatex/generic/luatexja/src/luatexja.sty b/macros/luatex/generic/luatexja/src/luatexja.sty
index a54ede6758..8b05af3cc9 100644
--- a/macros/luatex/generic/luatexja/src/luatexja.sty
+++ b/macros/luatex/generic/luatexja/src/luatexja.sty
@@ -27,7 +27,7 @@
\newif\ifltj@disablejfam
%%%% VERSION
-\def\LuaTeXjaversion{20240514.0}
+\def\LuaTeXjaversion{20241014.0}
%% Check if LaTeX is used.
\begingroup\expandafter\expandafter\expandafter\endgroup
diff --git a/macros/luatex/generic/luatexja/src/patches/lltjcore.sty b/macros/luatex/generic/luatexja/src/patches/lltjcore.sty
index 1c8f9cba4e..121a45ba0b 100644
--- a/macros/luatex/generic/luatexja/src/patches/lltjcore.sty
+++ b/macros/luatex/generic/luatexja/src/patches/lltjcore.sty
@@ -219,10 +219,6 @@
\@latex@error{Direction change inside float!?\MessageBreak
Use \noexpand\layoutcaption provided in \string\usepackage{lltjext}}\@ehc}
%%% Remove extra xkanjiskip
-\renewcommand\@makefnmark{%
- \unless\ifnum\ltjgetparameter{direction}=3
- \hbox{}\hbox{\@textsuperscript{\normalfont\@thefnmark}}\hbox{}%
- \else\hbox{\yoko \@textsuperscript{\normalfont\@thefnmark}}\fi}
\begingroup
\@ifpackageloaded{hyperref}
{\ifdefined\real@setref\def\ltj@setrefprefix{real}\else\def\ltj@setrefprefix{}\fi}
@@ -250,6 +246,11 @@
\gdef\@stopfield{%
\inhibitglue\@tempskipa\lastskip\unskip
\hskip\@tempskipa\color@endgroup\egroup}
+%%%%%% footnote
+\renewcommand\@makefnmark{%
+ \unless\ifnum\ltjgetparameter{direction}=3
+ \hbox{}\hbox{\@textsuperscript{\normalfont\@thefnmark}}\hbox{}%
+ \else\hbox{\yoko \@textsuperscript{\normalfont\@thefnmark}}\fi}
\pretocmd{\footnote}{\inhibitglue}{}{}
\pretocmd{\footnotemark}{\inhibitglue}{}{}
\def\@ltj@process@makefnmark#1{%
diff --git a/macros/luatex/generic/luatexja/src/patches/lltjdefs.sty b/macros/luatex/generic/luatexja/src/patches/lltjdefs.sty
index bb63ac6877..00c858e349 100644
--- a/macros/luatex/generic/luatexja/src/patches/lltjdefs.sty
+++ b/macros/luatex/generic/luatexja/src/patches/lltjdefs.sty
@@ -3,7 +3,7 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{lltjdefs}[2022-01-23 Default font settings of LuaTeX-ja]
+\ProvidesPackage{lltjdefs}[2024-05-16 Default font settings of LuaTeX-ja]
\ifdefined\ltj@stdmcfont\else\def\ltj@stdmcfont{file:HaranoAjiMincho-Regular.otf}\fi
\ifdefined\ltj@stdgtfont\else\def\ltj@stdgtfont{file:HaranoAjiGothic-Medium.otf}\fi
@@ -18,13 +18,8 @@
\DeclareKanjiEncodingDefaults{}{}
\DeclareErrorKanjiFont{JY3}{mc}{m}{n}{10}
\kanjifamily{mc}
-\ifnum\ltj@@latex@plv<1500 % <=2020-10-01
- \kanjiseries{m}
- \kanjishape{n}
-\else % >=2021-06-01
- \def\k@series{m}
- \def\k@shape{n}
-\fi
+\def\k@series{m}
+\def\k@shape{n}
\fontsize{10}{10}
\DeclareYokoKanjiEncoding{JY3}{}{}
\DeclareKanjiSubstitution{JY3}{mc}{m}{n}
@@ -37,12 +32,7 @@
\newcommand\kanjiencodingdefault{JY3}
\newcommand\kanjifamilydefault{\mcdefault}
\newcommand\kanjiseriesdefault{\mddefault}
-\ifnum 0\ifltj@ltfilehook@avail1\fi\ifdefined\@rmfamilyhook1\fi>0
- %% LaTeX 2020-02-02
- \newcommand\kanjishapedefault{\shapedefault}
-\else%% LaTeX 2019-10-01
- \newcommand\kanjishapedefault{\updefault}
-\fi
+\newcommand\kanjishapedefault{\shapedefault}
\DeclareKanjiFamily{JY3}{mc}{}
\DeclareFontShape{JY3}{mc}{m}{n}{<-> s*[\ifdefined\Cjascale\Cjascale\else 0.962216\fi]
@@ -90,57 +80,36 @@
\DeclareMathAlphabet{\mathgt}{JY3}{gt}{m}{n}
\fi
-\ifnum 0\ifltj@ltfilehook@avail1\fi\ifdefined\@rmfamilyhook1\fi>0
-%%%% LaTeX >= 2020-02-02
\edef\bfseries@mc{\bfdefault}
\edef\bfseries@gt{\bfdefault}
\def\bfseries@jtt{\bfseries@mc}
\edef\mdseries@mc{\mddefault}
\edef\mdseries@gt{\mddefault}
\def\mdseries@jtt{\mdseries@mc}
-\fi
\newif\if@ltj@match@family
\@ltj@match@familyfalse
\def\verbatim@font{\normalfont\ttfamily\kanjifamily\jttdefault\selectfont}
-\def\@ltj@hook@family#1#2{% #1: csname #2: contents
- \ifltj@ltfilehook@avail % LaTeX 2020-10-01
- \AddToHook{#1}{#2}%
- \else\ifcsname @#1hook\endcsname % LaTeX 2020-02-02
- \csappto{@#1hook}{#2}%
- \else % LaTeX 2019-10-01
- \ifcsname #1 \endcsname
- \expandafter\patchcmd\csname #1 \endcsname{\selectfont}{#2\selectfont}{}{}%
- \else
- \PackageWarningNoLine{luatexja}{%
- Patching to \expandafter\string\csname #1\endcsname\space failed%
- }%
- \fi
- \fi\fi
-}
-\@ltj@hook@family{rmfamily}{%
+\AddToHook{rmfamily}{%
\if@ltj@match@family
\prepare@family@series@update@kanji{mc}\mcdefault
\fi
}
-\@ltj@hook@family{sffamily}{%
+\AddToHook{sffamily}{%
\if@ltj@match@family
\prepare@family@series@update@kanji{gt}\gtdefault
\fi
}
-\@ltj@hook@family{ttfamily}{%
+\AddToHook{ttfamily}{%
\if@ltj@match@family
\prepare@family@series@update@kanji{jtt}\jttdefault
\fi
}
-\let\@ltj@hook@family\@undefined
-\ifltj@ltfilehook@avail % 2020-10-01
- \expandafter\def\expandafter\kanjiseriesdefault
- \expandafter{\kanjiseriesdefault\@empty}
- \let\kanjiseriesdefault@kernel\kanjiseriesdefault
-\fi
+\expandafter\def\expandafter\kanjiseriesdefault
+ \expandafter{\kanjiseriesdefault\@empty}
+\let\kanjiseriesdefault@kernel\kanjiseriesdefault
%%% \eminnershape
\expandafter\patchcmd\csname em \endcsname{\itshape}{\gtfamily\itshape}{}{}
diff --git a/macros/luatex/generic/luatexja/src/patches/lltjfont.sty b/macros/luatex/generic/luatexja/src/patches/lltjfont.sty
index 2efc9435ab..52ac44d5cc 100644
--- a/macros/luatex/generic/luatexja/src/patches/lltjfont.sty
+++ b/macros/luatex/generic/luatexja/src/patches/lltjfont.sty
@@ -3,31 +3,10 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{lltjfont}[2023-04-13 Patch to NFSS2 for LuaTeX-ja]
+\ProvidesPackage{lltjfont}[2023-05-16 Patch to NFSS2 for LuaTeX-ja]
%%%%%%%% LaTeX2e version detection
-\newcount\ltj@@latex@plv \ltj@@latex@plv=-1
-\ifltj@ltfilehook@avail
-\ExplSyntaxOn
- \cs_if_exist:NTF \__shipout_execute_main_cont:Nnnn
- {\ltj@@latex@plv=1500}% 2021-06-01
- {\ifnum\patch@level>2\relax \ltj@@latex@plv=1300\else \ltj@@latex@plv=1000\fi}% 2020-10-01
-\ExplSyntaxOff
-\else\ifdefined\@rmfamilyhook % 2020-02-02
- \ifx\@forced@seriestrue\@undefined % 2020-02-02 pl0--2
- \@latex@error
- {Please update LaTeX2e!^^J\space\space
- At least LaTeX2e 2020-02-02 patch level 3 is required}%
- {LaTeX2e 2020-02-02 patch level 2 (and 4) has a bug.}
- \else
- \ltj@@latex@plv=300
- \unless\ifx\series@maybe@drop@one@m@x\@undefined % pl5 + develop
- \ltj@@latex@plv=501
- \else\unless\ifx\series@maybe@drop@one@m\@undefined % patch level 5
- \ltj@@latex@plv=500
- \fi\fi
- \fi
-\fi\fi
+\newcount\ltj@@latex@plv \ltj@@latex@plv=1500
%% patching \DeclareTextComposite
\def\ltj@chardef@text@cmd#1{%
@@ -400,59 +379,7 @@
\directlua{luatexja.jfont.pickup_alt_font_a('\f@size')}%
}%
\fi}
-\ifnum\ltj@@latex@plv<1500 % <=2020-10-01
-\protected\def\ltj@selectfont@patch{%
- \ltj@@start@time@measure{selectfont}%
- \let\tmp@error@fontshape\error@fontshape
- \let\error@fontshape\error@kfontshape
- \ltj@@is@kyenc{\k@encoding}\ifin@%
- \let\cy@encoding\k@encoding
- \ensure@KanjiEncodingPair{t}%
- \edef\ct@encoding{\csname t@enc@\k@encoding\endcsname}%
- \else
- \ltj@@is@ktenc{\k@encoding}\ifin@%
- \let\ct@encoding\k@encoding
- \ensure@KanjiEncodingPair{y}%
- \edef\cy@encoding{\csname y@enc@\k@encoding\endcsname}%
- \else
- \@latex@error{KANJI Encoding scheme `\k@encoding' unknown}\@eha
- \fi
- \fi
- \global\let\ltj@afont@name=\font@name
- \ltj@selectfont@tate\ltj@selectfont@yoko
-%
- \edef\tmp@item{{\k@encoding}}\expandafter\def\expandafter\k@encoding\tmp@item
- \kenc@update
- \global\let\font@name=\ltj@afont@name
- \let\error@fontshape\tmp@error@fontshape
- \if@knjcmd \@knjcmdfalse
- \expandafter\ifx
- \csname rel@\k@encoding/\k@family/\k@series/\k@shape\endcsname\relax
- \expandafter\ifx
- \csname rel@\k@encoding/\k@family/\k@series/all\endcsname\relax
- \else
- \csname rel@\k@encoding/\k@family/\k@series/all\endcsname\selectfont
- % 警告: 再び \selectfont を呼んでいる.無限ループの可能性あり
- \fi
- \else
- \csname rel@\k@encoding/\k@family/\k@series/\k@shape\endcsname\selectfont
- % 警告: 再び \selectfont を呼んでいる.無限ループの可能性あり
- \fi
- \fi
- \global\let\font@name=\ltj@afont@name
- \ltj@@stop@time@measure{selectfont}%
-}
-\RequirePackage{everysel}
-\let\ltj@@EverySelectfont@Init=\@EverySelectfont@Init
-\expandafter\expandafter\expandafter\let
-\expandafter\expandafter\csname ltj@@orig@selectfont\endcsname\csname selectfont\space\endcsname
-\@EverySelectfont@Init% for LaTeX2e <=2020-10-01
-\def\@EverySelectfont@Init{%
- \expandafter\let\csname selectfont\space \endcsname \ltj@@orig@selectfont
- \ltj@@EverySelectfont@Init
-}
-\EverySelectfont{\ltj@selectfont@patch}
-\else % LaTeX2e >=2021-06-01
+
\AddToHook{selectfont}[luatexja]{%
\ltj@@start@time@measure{selectfont}%
\ifx\delayed@k@adjustment\@empty
@@ -514,7 +441,6 @@
\global\let\font@name=\ltj@afont@name
\ltj@@stop@time@measure{selectfont}%
}
-\fi
\bgroup
\def\ltj@@dir@patch#1#2#3{%
@@ -721,8 +647,6 @@
}
%%%%%%
-\ifnum\ltj@@latex@plv>0
-%%%% LaTeX >= 2020-02-02
\newif\if@shape@roman@kanji
\def\merge@kanji@series#1{%
@@ -740,76 +664,11 @@
\@nil
}
-\ifnum\ltj@@latex@plv>500 % !!! pl5 + develop
\def\set@target@series@kanji#1{%
\edef\k@series{#1}%
\series@maybe@drop@one@m\k@series\k@series
}
-\else\ifnum\ltj@@latex@plv>300 % !!! pl5
- \def\set@target@series@kanji#1{%
- \edef\k@series{#1}%
- \expandafter\series@maybe@drop@one@m\expandafter{\k@series}\k@series
- }
-\else % !!! pl 3, 4
- \def\set@target@series@kanji#1{%
- \edef\k@series{#1}%
- \edef\k@series{\expandafter\series@drop@one@m\k@series mm\series@drop@one@m}%
- }
-\fi\fi
-\ifnum\ltj@@latex@plv<1500% <=2020-10-01
-\def\merge@kanji@series@#1#2#3\@nil{%
- \def\reserved@a{#3}%
- \ifx\reserved@a\@empty
- \set@target@series@kanji{#2}%
- \else
- \begingroup\let\f@encoding\k@encoding\let\f@family\k@family
- \maybe@load@fontshape\endgroup
- \edef\reserved@a{\k@encoding /\k@family /#1/\k@shape}%
- \ifcsname \reserved@a \endcsname
- \set@target@series@kanji{#1}%
- \else
- \ifcsname \k@encoding /\k@family /#2/\k@shape \endcsname
- \set@target@series@kanji{#2}%
- {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}%
- \else
- \set@target@series@kanji{#3}%
- {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}%
- \fi
- \fi
- \fi
-}
-\def\merge@kanji@shape@#1#2#3\@nil{%
- \def\reserved@a{#3}%
- \ifx\reserved@a\@empty
- \if@shape@roman@kanji
- \set@safe@kanji@shape{#2}{}%
- \else
- \edef\k@shape{#2}%
- \fi
- \else
- \begingroup\let\f@encoding\k@encoding\let\f@family\k@family
- \maybe@load@fontshape\endgroup
- \edef\reserved@a{\k@encoding /\k@family /\k@series/#1}%
- \ifcsname \reserved@a\endcsname
- \edef\k@shape{#1}%
- \else
- \ifcsname \k@encoding /\k@family /\k@series/#2\endcsname
- \edef\k@shape{#2}%
- {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}%
- \else
- \if@shape@roman@kanji
- \set@safe@kanji@shape{#3}%
- {{\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}}%
- \else
- \edef\k@shape{#3}%
- {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}%
- \fi
- \fi
- \fi
- \fi
-}
-\else % >=2021-06-01
\def\merge@kanji@series@#1#2#3\@nil{%
\def\reserved@a{#3}%
\ifx\reserved@a\@empty
@@ -892,20 +751,8 @@
\fi
}
\let\delayed@merge@kanji@shape\merge@kanji@shape@without@substitution
-\fi
\newif\if@forced@series@kanji
-\ifnum\ltj@@latex@plv=300 % 2020-02-02 pl3, 4
- \DeclareRobustCommand\romanseries[1]{\merge@font@series{#1}}
- \DeclareRobustCommand\kanjiseries[1]{\merge@kanji@series{#1}}
- \DeclareRobustCommand\romanseriesforce[1]{\edef\f@series{#1}}
- \DeclareRobustCommand\kanjiseriesforce[1]{\edef\k@series{#1}}
-\else\ifnum\ltj@@latex@plv<1500% >=2020-02-02 pl5 and <=2020-10-01
- \DeclareRobustCommand\romanseries[1]{\@forced@seriesfalse\merge@font@series{#1}}
- \DeclareRobustCommand\kanjiseries[1]{\@forced@series@kanjifalse\merge@kanji@series{#1}}
- \DeclareRobustCommand\romanseriesforce[1]{\@forced@seriestrue\edef\f@series{#1}}
- \DeclareRobustCommand\kanjiseriesforce[1]{\@forced@series@kanjitrue\edef\k@series{#1}}
-\else% >=2021-06-01
\DeclareRobustCommand\romanseries[1]{\@forced@seriesfalse
\expandafter\def\expandafter\delayed@f@adjustment\expandafter
{\delayed@f@adjustment\delayed@merge@font@series{#1}}}
@@ -919,22 +766,9 @@
\expandafter\def\expandafter\delayed@k@adjustment\expandafter
{\delayed@k@adjustment\edef\k@series{#1}}}
\let\delayed@k@adjustment\@empty
-\fi\fi
\DeclareRobustCommand\fontseries[1]{\kanjiseries{#1}\romanseries{#1}}%
\DeclareRobustCommand\fontseriesforce[1]{\kanjiseriesforce{#1}\romanseriesforce{#1}}%
-\ifnum\ltj@@latex@plv<1500% <=2020-10-01
- \DeclareRobustCommand\romanshape[1]{\merge@font@shape{#1}}
- \DeclareRobustCommand\kanjishape[1]{\merge@kanji@shape{#1}}
- \DeclareRobustCommand\fontshape[1]{%
- \@shape@roman@kanjitrue
- \merge@font@shape{#1}\merge@kanji@shape{#1}%
- \@shape@roman@kanjifalse
- }
- \DeclareRobustCommand\romanshapeforce[1]{\edef\f@shape{#1}}
- \DeclareRobustCommand\kanjishapeforce[1]{\edef\k@shape{#1}}
- \DeclareRobustCommand\fontshapeforce[1]{\set@safe@kanji@shape{#1}{}\romanshapeforce{#1}}
-\else% >=2021-06-01
\DeclareRobustCommand\romanshape[1]{%
\expandafter\def\expandafter\delayed@f@adjustment\expandafter
{\delayed@f@adjustment\delayed@merge@font@shape{#1}}}
@@ -956,9 +790,6 @@
\romanshapeforce{#1}%
\expandafter\def\expandafter\delayed@k@adjustment\expandafter
{\delayed@k@adjustment\set@safe@kanji@shape{#1}{}}}
-\fi
-\ifnum\ltj@@latex@plv>1499
-%%%% LaTeX >= 2021-06-01
\DeclareRobustCommand\usekanji[4]{%
\kanjiencoding{#1}\edef\k@family{#2}%
\set@target@series@kanji{#3}\edef\k@shape{#4}%
@@ -969,42 +800,6 @@
\set@target@series{#3}\edef\f@shape{#4}%
\let\delayed@f@adjustment\@empty
\selectfont\ignorespaces}
-\else\ifnum\ltj@@latex@plv>1000
-%%%% 2021-10-01 PL3 <= LaTeX < 2021-06-01
-\DeclareRobustCommand\usekanji[4]{%
- \kanjiencoding{#1}\edef\k@family{#2}%
- \set@target@series@kanji{#3}\edef\k@shape{#4}%
- \selectfont\ignorespaces}
-\DeclareRobustCommand\useroman[4]{%
- \romanencoding{#1}\edef\f@family{#2}%
- \set@target@series{#3}\edef\f@shape{#4}%
- \selectfont\ignorespaces}
-\else
-%%%% 2020-10-01 <= LaTeX < 2020-10-01 PL3
-\DeclareRobustCommand\usekanji[4]{%
- \kanjiencoding{#1}\edef\k@family{#2}\edef\k@series{#3}\edef\k@shape{#4}%
- \selectfont\ignorespaces}
-\DeclareRobustCommand\useroman[4]{%
- \romanencoding{#1}\edef\f@family{#2}\edef\f@series{#3}\edef\f@shape{#4}%
- \selectfont\ignorespaces}
-\fi\fi
-%%%% LaTeX >= 2020-02-02 END
-\else
-%%%% LaTeX 2019-10-01
-\DeclareRobustCommand\romanseries[1]{\edef\f@series{#1}}
-\DeclareRobustCommand\kanjiseries[1]{\edef\k@series{#1}}
-\DeclareRobustCommand\fontseries[1]{\kanjiseries{#1}\romanseries{#1}}
-\DeclareRobustCommand\romanshape[1]{\edef\f@shape{#1}}
-\DeclareRobustCommand\kanjishape[1]{\edef\k@shape{#1}}
-\DeclareRobustCommand\fontshape[1]{\set@safe@kanji@shape{#1}{}\romanshape{#1}}
-\DeclareRobustCommand\usekanji[4]{%
- \kanjiencoding{#1}\kanjifamily{#2}\kanjiseries{#3}\kanjishape{#4}%
- \selectfont\ignorespaces}
-\DeclareRobustCommand\useroman[4]{%
- \romanencoding{#1}\romanfamily{#2}\romanseries{#3}\romanshape{#4}%
- \selectfont\ignorespaces}
-%%%% LaTeX 2019-10-01 END
-\fi
%%%%
\def\set@safe@kanji@shape#1#2{%
@@ -1026,8 +821,6 @@
\else\useroman{#1}{#2}{#3}{#4}%
\fi}
-\ifnum 0\ifltj@ltfilehook@avail1\fi\ifdefined\@rmfamilyhook1\fi>0
-%%%% LaTeX >= 2020-02-02
\let\prepare@family@series@update@kanji=\prepare@family@series@update
\patchcmd\prepare@family@series@update{\fontfamily}{\romanfamily}{}{}
\patchcmd\prepare@family@series@update@kanji{\if@forced@series}{\if@forced@series@kanji}{}{}
@@ -1049,34 +842,16 @@
\patchcmd\update@series@target@value@kanji{\f@series}{\k@series}{}{}
\def\@meta@family@list@kanji{\@elt{mc}\@elt{gt}\@elt{jtt}}
-\def\@ltj@hook@series@default#1#2#3{%
- \ifltj@ltfilehook@avail % 2020-10-01
- \AddToHook{#1/defaults}{#2}%
- \else\ifcsname @set#1defaultshook\endcsname %after 2020-04-07
- \expandafter\g@addto@macro\csname @set#1defaultshook\endcsname{#2}%
- \else % 2020-02-02
- \expandafter\patchcmd\csname #1 \endcsname{#3}{#3#2}{}{}%
- \fi\fi
-}
-\@ltj@hook@series@default{bfseries}{%
+\AddToHook{bfseries/defaults}{%
\let\bfseries@mc\bfdef@ult
\let\bfseries@gt\bfdef@ult
\let\bfseries@jtt\bfdef@ult
-}{%
- \let\bfseries@rm\bfdef@ult
- \let\bfseries@sf\bfdef@ult
- \let\bfseries@tt\bfdef@ult
}
-\@ltj@hook@series@default{mdseries}{%
+\AddToHook{mdseries/defaults}{%
\let\mdseries@mc\mddef@ult
\let\mdseries@gt\mddef@ult
\let\mdseries@jtt\mddef@ult
-}{%
- \let\mdseries@rm\mddef@ult
- \let\mdseries@sf\mddef@ult
- \let\mdseries@tt\mddef@ult
}
-\let\@ltj@hook@series@default\@undefined
\expandafter\patchcmd\csname bfseries \endcsname{%
\ifx\f@family\rmdef@ult \fontseries\bfseries@rm
@@ -1105,105 +880,41 @@
\fi\fi\fi
}{}{}
-\def\@ltj@hook@series#1#2{%
- \ifltj@ltfilehook@avail % 2020-10-01
- \AddToHook{#1}{#2}%
- \else % 2020-02-02
- \expandafter\patchcmd\csname #1 \endcsname{\selectfont}{#2\selectfont}{}{}%
- \fi
-}
-\@ltj@hook@series{bfseries}{%
+\AddToHook{bfseries}{%
\ifx\k@family\mcdef@ult \kanjiseries\bfseries@mc
\else\ifx\k@family\gtdef@ult \kanjiseries\bfseries@gt
\else\ifx\k@family\jttdef@ult\kanjiseries\bfseries@jtt
\else \kanjiseries\bfdefault
\fi\fi\fi
}
-\@ltj@hook@series{mdseries}{%
+\AddToHook{mdseries}{%
\ifx\k@family\mcdef@ult \kanjiseries\mdseries@mc
\else\ifx\k@family\gtdef@ult \kanjiseries\mdseries@gt
\else\ifx\k@family\jttdef@ult\kanjiseries\mdseries@jtt
\else \kanjiseries\mddefault
\fi\fi\fi
}
-\let\@ltj@hook@series\@undefined
-\def\@ltj@hook@efd#1#2{%
- \ifltj@ltfilehook@avail % 2020-10-01
- \AddToHook{expand@font@defaults}{#1}%
- \else\ifdefined\@expandfontdefaultshook %after 2020-04-07
- \g@addto@macro\@expandfontdefaultshook{#1#2}%
- \else % 2020-02-02
- \appto\expand@font@defaults{#1#2}%
- \fi\fi
-}
-\@ltj@hook@efd{%
+\AddToHook{expand@font@defaults}{%
\edef\mcdef@ult{\mcdefault}%
\edef\gtdef@ult{\gtdefault}%
\edef\jttdef@ult{\jttdefault}%
-}{\edef\kanjidef@ult{\kanjifamilydefault}}
-\let\@ltj@hook@efd\@undefined
+ \edef\kanjidef@ult{\kanjifamilydefault}}
-\ifltj@ltfilehook@avail % 2020-10-01
- \appto\init@series@setup{%
- \ifx\kanjiseriesdefault\kanjiseriesdefault@kernel
- \mdseries
- \let\kanjiseriesdefault\k@series
- \fi
- }
-\else
- \appto\init@series@setup{%
- \ifx\kanjidef@ult\mcdef@ult \mcfamily
- \else\ifx\kanjidef@ult\gtdef@ult \gtfamily
- \else\ifx\kanjidef@ult\jttdef@ult \jttfamily
- \fi\fi\fi
- }
-\fi
-
-\def\@ltj@hook@dfh#1{%
- \ifltj@ltfilehook@avail % 2020-10-01
- \AddToHook{normalfont}{#1}%
- \else\ifdefined\@defaultfamilyhook
- \g@addto@macro\@defaultfamilyhook{#1}%
- \else % 2020-02-02
- \expandafter\patchcmd\csname normalfont \endcsname{\selectfont}{#1\selectfont}{}{}%
- \let\reset@font\normalfont
- \fi\fi
+\appto\init@series@setup{%
+ \ifx\kanjiseriesdefault\kanjiseriesdefault@kernel
+ \mdseries
+ \let\kanjiseriesdefault\k@series
+ \fi
}
-\ifnum\ltj@@latex@plv>1499
-\@ltj@hook@dfh{%
+\AddToHook{normalfont}{%
\kanjiencoding{\kanjiencodingdefault}%
\edef\k@family{\kanjifamilydefault}%
\edef\k@series{\kanjiseriesdefault}%
\edef\k@shape{\kanjishapedefault}%
\let\delayed@k@adjustment\empty
}
-\else
-\@ltj@hook@dfh{%
- \kanjiencoding{\kanjiencodingdefault}%
- \edef\k@family{\kanjifamilydefault}%
- \edef\k@series{\kanjiseriesdefault}%
- \edef\k@shape{\kanjishapedefault}%
-}
-\fi
-\let\@ltj@hook@dfh\@undefined
-%%%% LaTeX >= 2020-02-02 END
-\else
-%%%% LaTeX 2019-10-01
-\DeclareRobustCommand\normalfont{%
- \useroman\encodingdefault\familydefault\seriesdefault\shapedefault
- \usekanji\kanjiencodingdefault\kanjifamilydefault\kanjiseriesdefault\kanjishapedefault}
-\let\reset@font\normalfont
-%%%% LaTeX 2019-10-01 END
-\fi
-
-
-
-\ifdefined\prepare@family@series@update@kanji\else
-%%%% LaTeX 2019-10-01
-\def\prepare@family@series@update@kanji#1{\kanjifamily}
-\fi
\DeclareRobustCommand\mcfamily{%
\unless\ifltj@disablejfam\not@math@alphabet\mcfamily\mathmc\fi
diff --git a/macros/luatex/generic/luatexja/src/patches/lltjp-array.sty b/macros/luatex/generic/luatexja/src/patches/lltjp-array.sty
index 9575dc3c04..fa5fe7b933 100644
--- a/macros/luatex/generic/luatexja/src/patches/lltjp-array.sty
+++ b/macros/luatex/generic/luatexja/src/patches/lltjp-array.sty
@@ -3,7 +3,9 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{lltjp-array}[2019-07-18 Patch to array for LuaTeX-ja]
+\ProvidesPackage{lltjp-array}[2024-07-21 Patch to array for LuaTeX-ja]
+
+\@ifpackageloaded{array}{\def\lltjp@array@array@loaded{1}}{\let\lltjp@array@array@loaded\relax}
\RequirePackage{etoolbox,array}
@@ -11,16 +13,23 @@
\ignorespaces\@sharp\unskip
}{%
\ltj@sharp
-}{}{}
+}{}{% >=2024-06-01
+ \def\ltj@sharp{\relax\pltx@next@inhibitglue\ltjfakeboxbdd\ignorespaces\@sharp\textonly@unskip\ltjfakeboxbdd}
+ \patchcmd\insert@column{%
+ \ignorespaces\@sharp\textonly@unskip
+ }{%
+ \ltj@sharp
+ }{}{}
+}
\@ifpackageloaded{lltjext}{}{%
%% remove extra \xkanjiskip (pLaTeX2e 2016/04/17)
-\def\@tabular{%
- \leavevmode \null
- \hbox \bgroup $\col@sep\tabcolsep \let\d@llarbegin\begingroup
- \let\d@llarend\endgroup
- \@tabarray}
-\def\endtabular{\endarray $\egroup\null}
+\ifnum0<0\lltjp@array@array@loaded
+ \patchcmd{\@tabular}{\leavevmode}{\leavevmode\null}{}{}
+ \patchcmd{\@tabular}{\leavevmode\null\null}{\leavevmode\null}{}{}
+ \apptocmd{\endtabular}{\null}{}{}
+ \let\lltjp@array@array@loaded\undefined
+\fi
\expandafter \let \csname endtabular*\endcsname = \endtabular
}
\endinput
diff --git a/macros/luatex/generic/luatexja/src/patches/lltjp-footmisc.sty b/macros/luatex/generic/luatexja/src/patches/lltjp-footmisc.sty
index edde267d3c..5ee7d407ce 100644
--- a/macros/luatex/generic/luatexja/src/patches/lltjp-footmisc.sty
+++ b/macros/luatex/generic/luatexja/src/patches/lltjp-footmisc.sty
@@ -3,63 +3,7 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{lltjp-footmisc}[2022-02-19 Patch to footmisc for LuaTeX-ja]
+\ProvidesPackage{lltjp-footmisc}[2024-07-22 Patch to footmisc for LuaTeX-ja]
-\@ifpackagelater{footmisc}{2022-02-10}{%% v6.0a or later
- \patchcmd{\@makenormalcolbox}{\vskip -\@outputbox@depth\@textbottom}%
- {\pltx@adjust@wd@outputbox\vskip -\@outputbox@depth\@textbottom}{}{}%
- \let\ltj@next\endinput
-}{\let\ltj@next\relax}
-\ltj@next
-
-% old (v5.5)
-\let \if@tempswa \ifFN@bottom
-\ifFN@para \@tempswatrue \fi
-\if@tempswa
- \ifFN@bottom
- \toks@{\setbox\@outputbox \box\@cclv
- \xdef\@freelist{\@freelist\@midlist}%
- \global\let\@midlist\@empty
- \@combinefloats
- \ifvoid\footins
- \else
- \setbox\@outputbox \vbox\bgroup
- \boxmaxdepth\@maxdepth
- \unvbox\@outputbox
- \vfill\relax
- }
- \the\FN@temptoken
- \toks@\expandafter{\the\toks@\egroup\fi}
- \else
- \toks@{\ifvoid\footins
- \setbox\@outputbox\box\@cclv
- \else
- \setbox\@outputbox \vbox\bgroup
- \boxmaxdepth\@maxdepth
- \unvbox\@cclv
- }
- \the\FN@temptoken
- \toks@\expandafter{\the\toks@
- \egroup
- \fi
- \xdef\@freelist{\@freelist\@midlist}%
- \global\let\@midlist\@empty
- \@combinefloats
- }%
- \fi
- \toks@\expandafter{\the\toks@
- \ifvbox\@kludgeins
- \@makespecialcolbox
- \else
- \setbox\@outputbox \vbox to\@colht{%
- \@texttop \dimen@\dp\@outputbox
- \unvbox\@outputbox
- \pltx@adjust@wd@outputbox
- \vskip -\dimen@\@textbottom
- }%
- \fi
- \global\maxdepth\@maxdepth
- }
- \edef\@makecol{\the\toks@}
-\fi
-\endinput
+\patchcmd{\@makenormalcolbox}{\vskip -\@outputbox@depth\@textbottom}%
+ {\pltx@adjust@wd@outputbox\vskip -\@outputbox@depth\@textbottom}{}{}%
diff --git a/macros/luatex/generic/luatexja/src/patches/lltjp-geometry.sty b/macros/luatex/generic/luatexja/src/patches/lltjp-geometry.sty
index 67b0f5de7f..641fa4a34d 100644
--- a/macros/luatex/generic/luatexja/src/patches/lltjp-geometry.sty
+++ b/macros/luatex/generic/luatexja/src/patches/lltjp-geometry.sty
@@ -3,7 +3,7 @@
%
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{lltjp-geometry}[2021-10-24 Patch to geometry for LuaTeX-ja with vertical writing mode]
+\ProvidesPackage{lltjp-geometry}[2024-05-16 Patch to geometry for LuaTeX-ja with vertical writing mode]
\RequirePackage{expl3}
\ifdefined\if@ltj@@geometry@tate\expandafter\endinput\fi
\newif\if@ltj@@geometry@tate
@@ -31,15 +31,6 @@
%============================================
\unless\ifdefined\ltj@ExecuteAfterPackage
-\expandafter\newif\csname ifltj@ltfilehook@avail\endcsname
-\@ifl@t@r\fmtversion{2020-10-01}{%
- \@ifl@t@r\fmtversion{2020-10-02}{\ltj@ltfilehook@availtrue}
- {%
- \ifnum\patch@level<0\relax \ifnum\patch@level<-6 \ltj@ltfilehook@availtrue\fi
- \else\ltj@ltfilehook@availtrue\fi
- }%
-}{}
-\ifltj@ltfilehook@avail
%% \ltj@ExecuteBeforePackage <package><code> and \ltj@ExecuteAfterPackage <package><code>
%% no-starred version: do nothing if the <package> is loaded
%% starred version: do <code> right away if the <package> is loaded
@@ -64,11 +55,6 @@
\@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteBeforePackage{#1}{#2}}%
}
}
-\else
- \RequirePackage{filehook}
- \let\ltj@ExecuteAfterPackage=\AtEndOfPackageFile
- \let\ltj@ExecuteBeforePackage=\AtBeginOfPackageFile
-\fi
\fi
%============================================
@@ -89,12 +75,8 @@
\else
\@gobble\iftrue\csname iftdir\endcsname \bool_set_true:N \l_tmpa_bool \fi
\fi
-\ifltj@ltfilehook@avail
- \prop_map_inline:Nn \g__hook_begindocument_code_prop
- { \tl_if_in:nnT {#2} {\tate} {\bool_set_true:N \l_tmpa_bool} }
-\else
- \tl_if_in:NnT \@begindocumenthook {\tate} {\bool_set_true:N \l_tmpa_bool}
-\fi
+\prop_map_inline:Nn \g__hook_begindocument_code_prop
+ { \tl_if_in:nnT {#2} {\tate} {\bool_set_true:N \l_tmpa_bool} }
\bool_if:NT \l_tmpa_bool {
\if@ltj@@geometry@forceoff
\@ltj@@geometry@tatefalse
diff --git a/macros/luatex/generic/luatexja/src/patches/lltjp-siunitx.sty b/macros/luatex/generic/luatexja/src/patches/lltjp-siunitx.sty
index 9c4526c3fd..292cbf00bf 100644
--- a/macros/luatex/generic/luatexja/src/patches/lltjp-siunitx.sty
+++ b/macros/luatex/generic/luatexja/src/patches/lltjp-siunitx.sty
@@ -1,5 +1,5 @@
%
-% lltjp-siunitx.sty 2022-12-14
+% lltjp-siunitx.sty 2024-06-10
%
\NeedsTeXFormat{LaTeX2e}
@@ -7,7 +7,6 @@
\RequirePackage{siunitx}
\makeatletter
\ExplSyntaxOn
-\cs_if_exist:NTF \siunitx_print_text:n {% siunitx v3.00
\cs_set_protected:Npn \siunitx_print_text:n #1
{
\text
@@ -32,104 +31,26 @@
}
}
\cs_set_eq:NN \ltj_orig__siunitx_table_collect_begin:w \__siunitx_table_collect_begin:w
-\cs_set_protected:Npn \__siunitx_table_collect_begin:w #1 \relax
- { \ltj_orig__siunitx_table_collect_begin:w }
-\RenewDocumentCommand \tablenum { O { } m }
- {
- \mode_leave_vertical:
- \group_begin:
- \keys_set:nn { siunitx } {#1}
- \siunitx_cell_begin:w
- \relax \ignorespaces #2
- \siunitx_cell_end:
- \group_end:
- }
-}% siunitx v3.00 end
-{% siunitx v2.xx
-\cs_generate_variant:Nn \tl_if_in:NnT { coT }
-\cs_new:Nn \__ltj_siunix_patch_inner:n {
- \cs_if_exist:cT { #1_tl } {
- \tl_if_in:coT { #1_tl } { \tex_char:D } {
- \tl_replace_all:cnn { #1_tl } { \tex_char:D } { \ltjalchar }
- }
+\cs_set_protected:Npn \__siunitx_table_collect_begin:w #1 {
+ \token_if_eq_meaning:NNTF #1 \relax
+ {\ltj_orig__siunitx_table_collect_begin:w }
+ {\ltj_orig__siunitx_table_collect_begin:w #1 }
}
-}
-\cs_new:Nn \__ltj_siunix_patch_inner_math:n {
- \__ltj_siunix_patch_inner:n { l__siunitx_#1_math }
- \__ltj_siunix_patch_inner:n { l__siunitx_#1_text }
-}
-\AtBeginDocument {
- \__ltj_siunix_patch_inner:n { c__siunitx_minus }
- \__ltj_siunix_patch_inner:n { c__siunitx_mu }
- \__ltj_siunix_patch_inner_math:n { angstrom }
- \__ltj_siunix_patch_inner_math:n { arcminute }
- \__ltj_siunix_patch_inner_math:n { arcsecond }
- \__ltj_siunix_patch_inner_math:n { ohm }
-}
-
-%%%% new table column: S and s
-\bool_new:N \l__ltj_siunitx_table_collect_relax_bool
-\cs_set_protected:Npn \__siunitx_table_collect_begin_s: {
- \bool_set_false:N \l__ltj_siunitx_table_collect_relax_bool
- \cs_set_eq:NN \__siunitx_table_collect_token:N
- \__siunitx_table_collect_token_s:N
- \cs_set_eq:NN \__siunitx_table_print: \__siunitx_table_print_s:
- \__siunitx_table_collect_init_s:
- \__siunitx_table_collect_get:
-}
-
-\cs_set_protected:Npn \__siunitx_table_collect_not_braced:N #1
+\cs_set_protected:Npn \__siunitx_table_collect_token:N #1
{
- \token_if_eq_meaning:NNTF #1 \tex_relax:D {
- \bool_if:NTF \l__ltj_siunitx_table_collect_relax_bool {
- \__siunitx_table_collect_not_braced_aux_i:N #1
- } {
- \bool_set_true:N \l__ltj_siunitx_table_collect_relax_bool
- }
- } {
- \token_if_eq_meaning:NNF #1 \ltjfakeboxbdd {
- \token_if_eq_meaning:NNF #1 \pltx@next@inhibitglue {
- \token_if_eq_meaning:NNF #1 \tex_ignorespaces:D
+ \__siunitx_table_collect_search:NnF #1
{
- \token_if_eq_meaning:NNF #1 \tex_unskip:D
- { \__siunitx_table_collect_not_braced_aux_i:N #1 }
+ \ignorespaces { \__siunitx_table_collect_ignore_spaces:N #1}
+ \pltx@next@inhibitglue { \__siunitx_table_collect_loop:}
+ \ltjfakeboxbdd { \__siunitx_table_collect_loop:}
+ \textonly@unskip { \__siunitx_table_collect_loop:}
+ \unskip { \__siunitx_table_collect_loop:}
+ \end { \tabularnewline \end }
+ \relax { \__siunitx_table_collect_relax:N #1 }
+ \tabularnewline { \tabularnewline }
+ \siunitx_cell_end: { \siunitx_cell_end:}
}
- }}}
- \__siunitx_table_collect_next:
- }
-
-\AtBeginDocument
- {
- \@ifpackageloaded { mdwtab }
- {
- \cs_set_protected:Npn \__siunitx_table_collect_not_braced:N #1
- {
- \token_if_eq_meaning:NNTF #1 \tex_relax:D {
- \bool_if:NTF \l__ltj_siunitx_table_collect_relax_bool {
- \__siunitx_table_collect_not_braced_aux_i:N #1
- } {
- \bool_set_true:N \l__ltj_siunitx_table_collect_relax_bool
- }
- } {
- \token_if_eq_meaning:NNF #1 \ltjfakeboxbdd {
- \token_if_eq_meaning:NNF #1 \pltx@next@inhibitglue {
- \token_if_eq_meaning:NNF #1 \tex_ignorespaces:D
- {
- \token_if_eq_meaning:NNF #1 \tex_unskip:D
- {
- \token_if_eq_meaning:NNF #1 \tab@setcr
- {
- \token_if_eq_meaning:NNF #1 \@maybe@unskip
- { \__siunitx_table_collect_not_braced_aux_i:N #1 }
- }
- }
- }
- }}}
- \__siunitx_table_collect_next:
- }
- }
- { }
- }
+ { \__siunitx_table_collect_token_aux:N #1 }
}
\ExplSyntaxOff
\endinput
diff --git a/macros/luatex/generic/luaxml/README b/macros/luatex/generic/luaxml/README
index 5df074c453..79dec41f2c 100644
--- a/macros/luatex/generic/luaxml/README
+++ b/macros/luatex/generic/luaxml/README
@@ -32,7 +32,7 @@ Author
------
Michal Hoftich
Email: michal.h21@gmail.com
-Version: v0.1r, 2024-02-23
+Version: v0.2a, 2024-10-17
Original authors: Paul Chakravarti and Manoel Campos (http://manoelcampos.com)
diff --git a/macros/luatex/generic/luaxml/luaxml-cssquery.lua b/macros/luatex/generic/luaxml/luaxml-cssquery.lua
index ebb29abb15..452656845c 100644
--- a/macros/luatex/generic/luaxml/luaxml-cssquery.lua
+++ b/macros/luatex/generic/luaxml/luaxml-cssquery.lua
@@ -156,7 +156,7 @@ local function cssquery()
-- make sure we deal with a string
value = tostring(value)
-- make the search string safe for pattern matching
- local escaped_search = search:gsub("([%(%)%.%%%+%–%*%?%[%^%$])", "%%%1")
+ local escaped_search = search:gsub("([%(%)%.%%%+%-%#%*%?%[%^%$])", "%%%1")
if modifier == "" then
return value == search
elseif modifier == "|" then
@@ -434,6 +434,18 @@ local function cssquery()
return querylist
end
+ --- Remove selector from the CSS list object.
+ -- All actions that literaly match the given selector will be removed.
+ function CssQuery:remove_selector(
+ selector -- CSS selector to be removed
+ )
+ for pos, obj in ipairs(self.querylist) do
+ if obj.source == selector then
+ table.remove(self.querylist, pos)
+ end
+ end
+ end
+
--- It tests list of queries agaings a DOM element and executes the
--- coresponding function that is saved for the matched query.
-- @return nothing
diff --git a/macros/luatex/generic/luaxml/luaxml-domobject.lua b/macros/luatex/generic/luaxml/luaxml-domobject.lua
index 73689add5a..d60b1a291e 100644
--- a/macros/luatex/generic/luaxml/luaxml-domobject.lua
+++ b/macros/luatex/generic/luaxml/luaxml-domobject.lua
@@ -6,16 +6,20 @@ local dom = {}
local xml
local handler
local css_query
+local html
if kpse then
xml = require("luaxml-mod-xml")
handler = require("luaxml-mod-handler")
css_query = require("luaxml-cssquery")
+ html = require("luaxml-mod-html")
else
xml = require("luaxml.mod-xml")
handler = require("luaxml.mod-handler")
css_query = require("luaxml.cssquery")
+ html = require("luaxml.mod-html")
end
+local HtmlParser = html.HtmlParser
local void = {area = true, base = true, br = true, col = true, hr = true, img = true, input = true, link = true, meta = true, param = true}
@@ -31,6 +35,13 @@ local escapes = {
["`"] = "&#x60;"
}
+-- declarations of local functions
+local html_to_dom
+local html_parse
+local parse
+
+
+
local function escape(search, text)
return text:gsub(search, function(ch)
return escapes[ch] or ""
@@ -91,9 +102,14 @@ local function serialize_dom(parser, current,level, output)
local format = get_action(typ, "start")
insert(format, el, prepare_attributes(attr))
end
- local function text(typ, text)
+ local function text(typ, text, parent)
+ local parent = parent or {}
local format = get_action(typ, "text")
- insert(format, escape_element(text))
+ if parent.verbatim then
+ insert(format, text)
+ else
+ insert(format, escape_element(text))
+ end
end
local function stop(typ, el)
local format = get_action(typ, "stop")
@@ -141,7 +157,7 @@ local function serialize_dom(parser, current,level, output)
end
start(xtype, name, attributes)
- text(xtype,text_content)
+ text(xtype,text_content, (current or {})._parent)
local children = root._children or {}
for _, child in ipairs(children) do
output = serialize_dom(parser,child, level + 1, output)
@@ -153,7 +169,7 @@ end
--- XML parsing function
-- Parse the XML text and create the DOM object.
-- @return DOM_Object
-local parse = function(
+parse = function(
xmltext --- String to be parsed
,voidElements --- hash table with void elements
)
@@ -411,8 +427,8 @@ local parse = function(
return strings
end
- --- Get table with the inner text of an element -- leading and trailing spaces are removed and elements that contain only white space are ignored.
- --- @return table
+ --- Get table with the inner text of an element - leading and trailing spaces are removed and elements that contain only white space are ignored.
+ -- @return table
function DOM_Object:stripped_strings(
current --- [optional] element to be selected
)
@@ -608,15 +624,169 @@ local parse = function(
return el:get_sibling_node(-1)
end
+ --- parse string as HTML or XML and return created elements
+ --- @return table elements
+ function DOM_Object:create_template(
+ str,
+ is_xml
+ )
+ -- <> is a dummy element, we just need to wrap everything in some element
+ str = "<>" .. (str or "") .. "</>"
+ local template = is_xml and parse(str) or parse(str)
+ local root = template:root_node()._children[1]
+ return root
+ end
+
+ --- parse string as HTML or XML and insert it as a child of the current node
+ function DOM_Object:inner_html(
+ str, --- HTML or XML to be inserted
+ is_xml --- [optional] Pass true to parse as XML, otherwise parse as HTML
+ )
+ local el = self
+ local root = self:create_template(str, is_xml)
+ -- replace original children of the current element with children of the dummy element created by parsing
+ el._children = root._children
+ return el
+ end
+
+
+ --- parse string as HTML or XML and insert it before current the element
+ function DOM_Object:insert_before_begin(
+ str, --- HTML or XML to be inserted
+ is_xml --- [optional] Pass true to parse as XML, otherwise parse as HTML
+ )
+ local el = self
+ local root = self:create_template(str, is_xml)
+ local parent = el:get_parent()
+ local current_pos = el:find_element_pos()
+ local children = root:get_children()
+ for i = 1, #children do
+ parent:add_child_node(children[i], current_pos + i - 1)
+ end
+ end
+
+ --- parse string as HTML or XML and insert it at the beginning of the current the element
+ function DOM_Object:insert_after_begin(
+ str, --- HTML or XML to be inserted
+ is_xml --- [optional] Pass true to parse as XML, otherwise parse as HTML
+ )
+ local el = self
+ local root = self:create_template(str, is_xml)
+ local children = root:get_children()
+ for i = 1, #children do
+ el:add_child_node(children[i], i)
+ end
+ end
+
+ --- parse string as HTML or XML and insert it at the end of the current the element
+ function DOM_Object:insert_before_end(
+ str, --- HTML or XML to be inserted
+ is_xml --- [optional] Pass true to parse as XML, otherwise parse as HTML
+ )
+ local el = self
+ local root = self:create_template(str, is_xml)
+ local children = root:get_children()
+ for i = 1, #children do
+ el:add_child_node(children[i])
+ end
+ end
+
+ --- parse string as HTML or XML and insert it after current the element
+ function DOM_Object:insert_after_end(
+ str, --- HTML or XML to be inserted
+ is_xml --- [optional] Pass true to parse as XML, otherwise parse as HTML
+ )
+ local el = self
+ local root = self:create_template(str, is_xml)
+ local parent = el:get_parent()
+ local current_pos = el:find_element_pos()
+ local children = root:get_children()
+ for i = 1, #children do
+ parent:add_child_node(children[i], current_pos + i)
+ end
+ end
+
-- include the methods to all xml nodes
save_methods(parser._handler.root)
-- parser:
- return parser
+ return parser, DOM_Object
+end
+
+-- table of elements that should be kept without XML escaping in the DOM serialization
+local verbatim_elements = {script=true, style=true}
+
+function html_to_dom(html_object)
+ -- convert parsed HTML DOM to the XML DOM
+ local dom, DOM_Object = parse("") -- use empty text to just initialize the DOM object
+ -- use root of the DOM object as the original parent
+ local current_parent = dom._handler.root
+
+ local function create_node(tbl)
+ -- create node suitable for LuaXML DOM object
+ tbl._children = {}
+ -- this should copy methods from the DOM object to the newly created object
+ tbl.__index = DOM_Object
+ return setmetatable(tbl, DOM_Object)
+ end
+
+ local function build_tree(object)
+ -- convert tree produced by the HTML parser to LuaXML DOM
+ local typ = object._type
+ -- process particular node types from the HTML parser
+ if typ == "doctype" then
+ current_parent:add_child_node(create_node {_name=object.name, _type="DTD"})
+ elseif typ == "comment" then
+ current_parent:add_child_node(create_node {_text=object.text, _type="COMMENT"})
+ elseif typ == "element" then
+ local attributes = {}
+ -- convert attributes to the form expected by the DOM object
+ for _, attr in ipairs(object.attr) do
+ attributes[attr.name] = attr.value
+ end
+ local element = current_parent:create_element(object.tag, attributes)
+ -- disable escaping of text in dom:serialize() for <script> or <style> elements
+ if verbatim_elements[string.lower(object.tag)] then element.verbatim = true end
+ current_parent:add_child_node(element)
+ -- set the current element as parent for the processing of children
+ local old_parent = current_parent
+ current_parent = element
+ -- process children
+ for k,v in ipairs(object.children) do
+ build_tree(v)
+ end
+ -- restore original parent
+ current_parent = old_parent
+ elseif typ == "text" then
+ local text = current_parent:create_text_node(object.text)
+ current_parent:add_child_node(text)
+ else
+ -- for other node types, just process the children
+ for k,v in ipairs(object.children) do
+ build_tree(v)
+ end
+ end
+
+ end
+ build_tree(html_object)
+ return dom
end
+--- Parse HTML text as a DOM object.
+-- It supports all methods as the object returned by the parse() function.
+-- @param html_str string with the HTML code to be parsed
+-- @return DOM_Object
+function html_parse(html_str)
+ local html_obj = HtmlParser:init(html_str)
+ local html_dom = html_obj:parse()
+ return html_to_dom(html_dom)
+end
+
+
+
--- @export
return {
parse = parse,
- serialize_dom= serialize_dom
+ serialize_dom= serialize_dom,
+ html_parse = html_parse
}
diff --git a/macros/luatex/generic/luaxml/luaxml-encodings.lua b/macros/luatex/generic/luaxml/luaxml-encodings.lua
new file mode 100644
index 0000000000..9369f494a5
--- /dev/null
+++ b/macros/luatex/generic/luaxml/luaxml-encodings.lua
@@ -0,0 +1,89 @@
+--- Convert 8-bit encodings to UTF-8
+--- @module luaxml-encodings
+
+-- this table is generated automatically by this command:
+-- texlua encodings/make_encodings.lua
+local encodings = {
+ ['ibm866'] = 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀рстуфхцчшщъыьэюяЁёЄєЇїЎў°∙·√№¤■ ',
+ ['iso-8859-5'] = '�������������������������������� ЁЂЃЄЅІЇЈЉЊЋЌ­ЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя№ёђѓєѕіїјљњћќ§ўџ',
+ ['macintosh'] = 'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›fifl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ',
+ ['windows-1251'] = 'ЂЃ‚ѓ„…†‡€‰Љ‹ЊЌЋЏђ‘’“”•–—�™љ›њќћџ ЎўЈ¤Ґ¦§Ё©Є«¬­®Ї°±Ііґµ¶·ё№є»јЅѕїАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя',
+ ['iso-8859-13'] = '�������������������������������� ”¢£¤„¦§Ø©Ŗ«¬­®Æ°±²³“µ¶·ø¹ŗ»¼½¾æĄĮĀĆÄÅĘĒČÉŹĖĢĶĪĻŠŃŅÓŌÕÖ×ŲŁŚŪÜŻŽßąįāćäåęēčéźėģķīļšńņóōõö÷ųłśūüżž’',
+ ['iso-8859-6'] = '�������������������������������� ���¤�������،­�������������؛���؟�ءآأؤإئابةتثجحخدذرزسشصضطظعغ�����ـفقكلمنهوىيًٌٍَُِّْ',
+ ['iso-8859-8'] = '�������������������������������� �¢£¤¥¦§¨©×«¬­®¯°±²³´µ¶·¸¹÷»¼½¾��������������������������������‗אבגדהוזחטיךכלםמןנסעףפץצקרשת��‎‏',
+ ['iso-8859-4'] = '�������������������������������� ĄĸŖ¤ĨĻ§¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩļˇ¸šēģŧŊžŋĀÁÂÃÄÅÆĮČÉĘËĖÍÎĪĐŅŌĶÔÕÖ×ØŲÚÛÜŨŪßāáâãäåæįčéęëėíîīđņōķôõö÷øųúûüũū˙',
+ ['koi8-r'] = '─│┌┐└┘├┤┬┴┼▀▄█▌▐░▒▓⌠■∙√≈≤≥ ⌡°²·÷═║╒ё╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡Ё╢╣╤╥╦╧╨╩╪╫╬©юабцдефгхийклмнопярстужвьызшэщчъЮАБЦДЕФГХИЙКЛМНОПЯРСТУЖВЬЫЗШЭЩЧЪ',
+ ['iso-8859-7'] = '�������������������������������� ‘’£€₯¦§¨©ͺ«¬­�―°±²³΄΅Ά·ΈΉΊ»Ό½ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ�ΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ',
+ ['iso-8859-3'] = '�������������������������������� Ħ˘£¤�Ĥ§¨İŞĞĴ­�Ż°ħ²³´µĥ·¸ışğĵ½�żÀÁÂ�ÄĊĈÇÈÉÊËÌÍÎÏ�ÑÒÓÔĠÖ×ĜÙÚÛÜŬŜßàáâ�äċĉçèéêëìíîï�ñòóôġö÷ĝùúûüŭŝ˙',
+ ['windows-1256'] = '€پ‚ƒ„…†‡ˆ‰ٹ‹Œچژڈگ‘’“”•–—ک™ڑ›œ‌‍ں ،¢£¤¥¦§¨©ھ«¬­®¯°±²³´µ¶·¸¹؛»¼½¾؟ہءآأؤإئابةتثجحخدذرزسشصض×طظعغـفقكàلâمنهوçèéêëىيîïًٌٍَôُِ÷ّùْûü‎‏ے',
+ ['windows-1258'] = '€�‚ƒ„…†‡ˆ‰�‹Œ����‘’“”•–—˜™�›œ��Ÿ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂĂÄÅÆÇÈÉÊË̀ÍÎÏĐÑ̉ÓÔƠÖ×ØÙÚÛÜỮßàáâăäåæçèéêë́íîïđṇ̃óôơö÷øùúûüư₫ÿ',
+ ['iso-8859-14'] = '�������������������������������� Ḃḃ£ĊċḊ§Ẁ©ẂḋỲ­®ŸḞḟĠġṀṁ¶ṖẁṗẃṠỳẄẅṡÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏŴÑÒÓÔÕÖṪØÙÚÛÜÝŶßàáâãäåæçèéêëìíîïŵñòóôõöṫøùúûüýŷÿ',
+ ['windows-1252'] = '€�‚ƒ„…†‡ˆ‰Š‹Œ�Ž��‘’“”•–—˜™š›œ�žŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ',
+ ['iso-8859-15'] = '�������������������������������� ¡¢£€¥Š§š©ª«¬­®¯°±²³Žµ¶·ž¹º»ŒœŸ¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ',
+ ['x-mac-cyrillic'] = 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ†°Ґ£§•¶І®©™Ђђ≠Ѓѓ∞±≤≥іµґЈЄєЇїЉљЊњјЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёяабвгдежзийклмнопрстуфхцчшщъыьэю€',
+ ['windows-1254'] = '€�‚ƒ„…†‡ˆ‰Š‹Œ����‘’“”•–—˜™š›œ��Ÿ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏĞÑÒÓÔÕÖ×ØÙÚÛÜİŞßàáâãäåæçèéêëìíîïğñòóôõö÷øùúûüışÿ',
+ ['windows-1255'] = '€�‚ƒ„…†‡ˆ‰�‹�����‘’“”•–—˜™�›���� ¡¢£₪¥¦§¨©×«¬­®¯°±²³´µ¶·¸¹÷»¼½¾¿ְֱֲֳִֵֶַָֹֺֻּֽ־ֿ׀ׁׂ׃װױײ׳״�������אבגדהוזחטיךכלםמןנסעףפץצקרשת��‎‏',
+ ['windows-1250'] = '€�‚�„…†‡�‰Š‹ŚŤŽŹ�‘’“”•–—�™š›śťžź ˇ˘Ł¤Ą¦§¨©Ş«¬­®Ż°±˛ł´µ¶·¸ąş»Ľ˝ľżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙',
+ ['koi8-u'] = '─│┌┐└┘├┤┬┴┼▀▄█▌▐░▒▓⌠■∙√≈≤≥ ⌡°²·÷═║╒ёє╔ії╗╘╙╚╛ґў╞╟╠╡ЁЄ╣ІЇ╦╧╨╩╪ҐЎ©юабцдефгхийклмнопярстужвьызшэщчъЮАБЦДЕФГХИЙКЛМНОПЯРСТУЖВЬЫЗШЭЩЧЪ',
+ ['iso-8859-10'] = '�������������������������������� ĄĒĢĪĨĶ§ĻĐŠŦŽ­ŪŊ°ąēģīĩķ·ļđšŧž―ūŋĀÁÂÃÄÅÆĮČÉĘËĖÍÎÏÐŅŌÓÔÕÖŨØŲÚÛÜÝÞßāáâãäåæįčéęëėíîïðņōóôõöũøųúûüýþĸ',
+ ['windows-1257'] = '€�‚�„…†‡�‰�‹�¨ˇ¸�‘’“”•–—�™�›�¯˛� �¢£¤�¦§Ø©Ŗ«¬­®Æ°±²³´µ¶·ø¹ŗ»¼½¾æĄĮĀĆÄÅĘĒČÉŹĖĢĶĪĻŠŃŅÓŌÕÖ×ŲŁŚŪÜŻŽßąįāćäåęēčéźėģķīļšńņóōõö÷ųłśūüżž˙',
+ ['windows-1253'] = '€�‚ƒ„…†‡�‰�‹�����‘’“”•–—�™�›���� ΅Ά£¤¥¦§¨©�«¬­®―°±²³΄µ¶·ΈΉΊ»Ό½ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ�ΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ',
+ ['iso-8859-2'] = '�������������������������������� Ą˘Ł¤ĽŚ§¨ŠŞŤŹ­ŽŻ°ą˛ł´ľśˇ¸šşťź˝žżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙',
+ ['windows-874'] = '€����…�����������‘’“”•–—�������� กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู����฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛',
+ ['iso-8859-16'] = '�������������������������������� ĄąŁ€„Š§š©Ș«Ź­źŻ°±ČłŽ”¶·žčș»ŒœŸżÀÁÂĂÄĆÆÇÈÉÊËÌÍÎÏĐŃÒÓÔŐÖŚŰÙÚÛÜĘȚßàáâăäćæçèéêëìíîïđńòóôőöśűùúûüęțÿ',
+}
+
+
+local utfchar = utf8.char
+
+--- Try to find an encoding in HTML string
+---@param str string HTML document
+---@param len number count of characters from the start of the string where it should search for the encoding metadata
+---@return string encoding identifier, or nil and message if no encoding was found
+local function find_html_encoding(str, len)
+ -- try to find encoding in the html document
+ -- we limit search length, because encoding should be in the document head, ideally near the start
+ local len = len or 4096
+ local sub = str:sub(1, len)
+ for meta in sub:gmatch("<meta (.-)>") do
+ local charset = meta:match("charset%s*=%s*[\"']?(.-)[\"']")
+ if charset then return string.lower(charset) end
+ end
+ return nil, "Cannot find the document encoding"
+end
+
+local function load_mapping(enc_name)
+ local enc_name = enc_name or ""
+ local enc = encodings[enc_name]
+ if not enc then return nil, "Cannot load encoding " .. enc_name end
+ local mapping,i = {}, 128
+ for pos, codepoint in utf8.codes(enc) do
+ mapping[i] = utfchar(codepoint)
+ i = i + 1
+ end
+ return mapping
+end
+
+--- Convert string to utf-8
+---@param text string for converting
+---@param mapping table
+---@return string converted string
+local function recode(text, mapping)
+ return text:gsub("(.)", function(char)
+ local charpoint = string.byte(char)
+ if charpoint > 127 then
+ return mapping[charpoint]
+ else
+ return false
+ end
+ end)
+end
+
+
+return {
+ encodings = encodings,
+ find_html_encoding = find_html_encoding,
+ load_mapping = load_mapping,
+ recode = recode
+}
+
diff --git a/macros/luatex/generic/luaxml/luaxml-mod-html.lua b/macros/luatex/generic/luaxml/luaxml-mod-html.lua
index 69e32b16e2..9f99cc3bc4 100644
--- a/macros/luatex/generic/luaxml/luaxml-mod-html.lua
+++ b/macros/luatex/generic/luaxml/luaxml-mod-html.lua
@@ -1,3 +1,6 @@
+--- HTML parsing module for LuaXML
+-- @module luaxml-mod-html
+-- @author Michal Hoftich <michal.h21@gmail.com
-- Copyright Michal Hoftich, 2022
-- HTML parser inspired by https://browser.engineering/html.html
-- but then redone using https://html.spec.whatwg.org/multipage/parsing.html
@@ -1540,9 +1543,14 @@ local function is_formatting_element(name)
return formatting_element_names[name]
end
-local special_elements = {}
+local function hash_from_array(tbl)
+ local t = {}
+ for _, v in ipairs(tbl) do t[v] = true end
+ return t
+end
+
-local special_elements_list = {"address", "applet", "area", "article", "aside",
+local special_elements_list = hash_from_array {"address", "applet", "area", "article", "aside",
"base", "basefont", "bgsound", "blockquote", "body", "br", "button", "caption",
"center", "col", "colgroup", "dd", "details", "dir", "div", "dl", "dt",
"embed", "fieldset", "figcaption", "figure", "footer", "form", "frame",
@@ -1555,13 +1563,9 @@ local special_elements_list = {"address", "applet", "area", "article", "aside",
"mi","mo","mn","ms","mtext", "annotation-xml","foreignObject","desc", "title"
}
-for k,v in ipairs(special_elements_list) do
- special_elements[v] = true
-end
-
local function is_special(name)
- return special_elements[name]
+ return special_elements_list[name]
end
-- these lists are used in HtmlParser:generate_implied_endtags()
@@ -1649,8 +1653,12 @@ local HtmlTreeStates = {}
+--- @type HtmlParser
local HtmlParser = {}
+--- Initialize the HTML Object
+---@param body string HTML to be parsed
+---@return table initialized object
function HtmlParser:init(body)
local o ={}
setmetatable(o, self)
@@ -1686,10 +1694,11 @@ for _,v in ipairs(self_closing_tags_list) do self_closing_tags[v] = true end
-
+--- Execute the HTML parser
+--- @return table Root node of the HTML DOM
function HtmlParser:parse()
-- we assume utf8 input, you must convert it yourself if the source is
- -- in a different encoding
+ -- in a different encoding. for example using luaxml-encodings library
self.text = {}
self.state = self.default_state
-- this should enable us to pass over some characters that we want to ignore
@@ -1791,6 +1800,7 @@ function HtmlParser:emit(token)
elseif token_type == "start_tag" then
self:add_text()
-- self:start_attribute()
+ self:reset_insertion_mode()
self:start_tag()
-- print("Emit start tag", table.concat(token.name))
-- save last attribute
@@ -1882,8 +1892,142 @@ function HtmlParser:set_xmlns(node, parent)
end
end
+function HtmlParser:pop_element()
+ -- close the current element and add it to the DOM
+ local el = self:close_element()
+ local parent = self:get_parent()
+ parent:add_child(el)
+ return el
+end
+
+local close_p_at_start = hash_from_array {"address", "article", "aside", "blockquote", "center", "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", "search", "section", "summary", "ul", "pre", "listing", "form", "table", "xmp", "hr"}
+
+local close_headers = hash_from_array {"h1", "h2", "h3", "h4", "h5", "h6"}
+
+local body_modes = hash_from_array {"in_body", "in_cell", "in_row", "in_select", "in_table", "in_table_body", "in_frameset"}
+
+local list_items = hash_from_array {"li", "dt", "dd"}
+
+local close_address_at_end = hash_from_array{"address", "article", "aside", "blockquote", "button", "center", "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", "search", "section", "summary", "ul", "form"}
+
+
+function HtmlParser:close_unfinished(name)
+ -- close all unfinished elements until the element with the given name is found
+ for i = #self.unfinished, 1, -1 do
+ local el = self:pop_element()
+ if el.tag == name then
+ break
+ end
+ end
+end
+
+
+function HtmlParser:close_paragraph()
+ -- close currently open <p> elements
+ self:close_unfinished("p")
+end
+
+function HtmlParser:current_element_name()
+ -- return name of the current element
+ return self:get_parent().tag
+end
+
+local not_specials = hash_from_array { "address", "div", "p"}
+
+local function handle_list_item(self, name)
+ -- we handle li, dt and dd. dt and dd should close each other, li closes only itself
+ local names = {dt = true, dd = true}
+ if name == "li" then names = {li=true} end
+ for i = #self.unfinished, 1, -1 do
+ local current = self.unfinished[i]
+ local current_tag = current.tag
+ if names[current_tag] then
+ self:generate_implied_endtags(nil, {current.tag})
+ for j = #self.unfinished, i, -1 do
+ self:pop_element()
+ end
+ break
+ elseif is_special(current_tag) and not not_specials[name] then
+ break
+ end
+ end
+end
+
+local close_paragraph = function(self)
+ if is_in_button_scope(self, "p") then
+ self:close_paragraph()
+ end
+end
+
+function HtmlParser:handle_insertion_mode(token)
+ -- simple handling of https://html.spec.whatwg.org/multipage/parsing.html#tree-construction
+ -- we don't support most rules, just the most important for avoiding mismatched tags
+
+ if body_modes[self.insertion_mode] then
+ if token.type == "start_tag" then
+ local name = table.concat(token.name)
+ if close_p_at_start[name] then close_paragraph(self) end
+ if close_headers[name] then
+ close_paragraph(self)
+ -- close current element if it is already header
+ if close_headers[self:current_element_name()] then
+ self:pop_element()
+ end
+ elseif name == "pre" or name == "listing" then
+ -- we should ignore next "\n" char token
+ elseif name == "image" then
+ -- image tag is an error, change to <img>
+ token.name = {"img"}
+ elseif list_items[name] then
+ handle_list_item(self, name)
+ close_paragraph(self)
+ end
+ elseif token.type == "end_tag" then
+ local name = table.concat(token.name)
+ if close_address_at_end[name] then
+ if is_in_scope(self, name, {}) then
+ self:generate_implied_endtags()
+ self:close_unfinished(name)
+ return false
+ else
+ token.type = "ignore"
+ end
+ elseif name == "p" then
+ if not is_in_button_scope(self, "p") then
+ local parent = self:get_parent()
+ local node = Element:init("p", parent)
+ table.insert(self.unfinished, node)
+ end
+ -- use self:close_paragraph() instead of close_paragraph() because we don't need to check scope at this point
+ self:close_paragraph()
+ elseif name == "br" then
+ token.type = "start_tag"
+ elseif close_headers[name] then
+ local header_in_scope = false
+ -- detect, if there are any open h1-h6 tag and close it
+ for el, _ in pairs(close_headers) do
+ if is_in_scope(self, el, {}) then
+ header_in_scope = el
+ break
+ end
+ end
+ if not header_in_scope then
+ token.type = "ignore"
+ else
+ self:close_unfinished(header_in_scope)
+ end
+ end
+ end
+ end
+ return true
+end
+
+local rawtext_elements = hash_from_array {"style", "textarea", "xmp"}
+
+
function HtmlParser:start_tag()
local token = self.current_token
+ self:handle_insertion_mode(token)
if token.type == "start_tag" then
-- close all currently opened attributes
self:start_attribute()
@@ -1894,18 +2038,18 @@ function HtmlParser:start_tag()
node.attr = token.attr
node.self_closing = token.self_closing
self:set_xmlns(node)
- --
+ -- in this handler we should close <p> or <li> elements without explicit closing tags
if token.self_closing -- <img />
or self_closing_tags[name] -- void elements
then
- parent:add_child(node)
+ parent:add_child(node, node.tag)
else
-- add to the unfinished list
table.insert(self.unfinished, node)
end
if name == "title" then
self.element_state = "rcdata"
- elseif name == "style" then
+ elseif rawtext_elements[name] then
self.element_state = "rawtext"
elseif name == "script" then
self.element_state = "script_data"
@@ -1916,11 +2060,16 @@ end
function HtmlParser:end_tag()
-- close current opened element
local token = self.current_token
+ local should_pop = self:handle_insertion_mode(token)
if token.type == "end_tag" then
if #self.unfinished==0 then return nil end
- local node = self:close_element()
- local parent = self:get_parent()
- parent:add_child(node)
+ -- we shouldn't close elements if handle_insertion_mode() already closed them
+ if should_pop then
+ -- close the current element only if the token is in the current scope
+ if is_in_scope(self, table.concat(token.name), {}) then
+ self:pop_element()
+ end
+ end
end
end
@@ -1964,6 +2113,21 @@ function HtmlParser:adjusted_current_node()
end
+local simple_modes = {
+ body = "in_body",
+ td = "in_cell",
+ th = "in_cell",
+ tr = "in_row",
+ tbody = "in_table_body",
+ thead = "in_table_body",
+ tfoot = "in_table_body",
+ caption = "in_caption",
+ colgroup = "in_column_group",
+ table = "in_table",
+ template = "current_template_insertion_mode",
+ frameset = "in_frameset"
+}
+
function HtmlParser:reset_insertion_mode()
-- https://html.spec.whatwg.org/multipage/parsing.html#reset-the-insertion-mode-appropriately
local last = false
@@ -1977,16 +2141,33 @@ function HtmlParser:reset_insertion_mode()
if name == "head" and last == true then
self:switch_insertion("in_head")
return
- elseif name == "body" then
- self:switch_insertion("in_body")
- return
elseif name == "html" then
- if self.head_pointer then
+ if not self.head_pointer then
self:switch_insertion("before_head")
return
else
self:switch_insertion("after_head")
+ return
+ end
+ elseif name == "select" then
+ if not last then
+ for x = position -1, 1, -1 do
+ if x == 1 then break end
+ local ancestor = self.unfinished[x]
+ local ancestor_name = ancestor.tag
+ if ancestor_name == "template" then
+ break
+ elseif ancestor_name == "table" then
+ self:switch_insertion("in_select_in_table")
+ return
+ end
+ end
end
+ self:switch_insertion("in_select")
+ return
+ elseif simple_modes[name] then
+ self:switch_insertion(simple_modes[name])
+ return
elseif last == true then
self:switch_insertion("in_body")
return
@@ -2006,8 +2187,8 @@ function HtmlParser:generate_implied_endtags(included, ignored)
local current = self:current_node() or {}
-- keep removing elements while they are in the "included" list
if included[current.tag] then
- table.remove(self.unfinished)
- self:generate_implied_endtags(ignored)
+ self:pop_element()
+ self:generate_implied_endtags(included, ignored)
end
end
@@ -2023,9 +2204,7 @@ function HtmlParser:finish()
self:start_tag("html")
end
while #self.unfinished > 0 do
- local node = self:close_element()
- local parent = self:get_parent()
- parent:add_child(node)
+ self:pop_element()
end
-- return root element
return self.Document -- self:close_element()
diff --git a/macros/luatex/generic/luaxml/luaxml-sty.lua b/macros/luatex/generic/luaxml/luaxml-sty.lua
new file mode 100644
index 0000000000..35548688ce
--- /dev/null
+++ b/macros/luatex/generic/luaxml/luaxml-sty.lua
@@ -0,0 +1,155 @@
+--- Helper functions for the luaxml.sty package
+--- @module luaxml-sty
+--- @author Michal Hoftich <michal.h21@gmail.com
+-- provide global object with all variables we will use
+luaxml_sty = {
+ current = {
+ transformation = "default",
+ parameters = {}, -- "parameters" argument for transform:add_action
+ },
+ packages = {},
+ -- we want to support multiple transformation objects, they will be stored here
+ transformations = {},
+}
+luaxml_sty.packages.transform = require "luaxml-transform"
+luaxml_sty.packages.domobject = require "luaxml-domobject"
+
+-- declare default transformer, used if no explicit transformer is used in LuaXML LaTeX commands
+luaxml_sty.transformations.default = luaxml_sty.packages.transform.new()
+
+-- debuggind functions
+function luaxml_sty.error(...)
+ local arg = {...}
+ print("LuaXML error: " .. table.concat(arg, " "))
+end
+
+luaxml_sty.do_debug = false
+
+function luaxml_sty.debug(...)
+ if luaxml_sty.do_debug then
+ local arg = {...}
+ print("LuaXML: " .. table.concat(arg, " "))
+ end
+end
+
+--- Declare new transformer
+---@param name string transformer name
+---@return table transformer object
+function luaxml_sty.declare_transformer(name)
+ luaxml_sty.transformations[name] = luaxml_sty.packages.transform.new()
+ return luaxml_sty.transformations[name]
+end
+
+
+--- Add luaxml-transform rule
+--- @param current string transformer name, empty for the default object
+--- @param selector string CSS selector to be used
+--- @param rule string luaxml-transform rule
+function luaxml_sty.add_rule(current, selector, rule)
+ if current == "" then
+ current = luaxml_sty.current.transformation
+ end
+ -- the +v parameter type in LaTeX replaces newlines with \obeyedline. we need to replace it back to newlines
+ rule = rule:gsub("\\obeyedline", "\n")
+ luaxml_sty.debug("************* luaxml_sty rule: " .. selector, rule, current, (luaxml_sty.current.parameters.verbatim and "verbatim" or "not verbatim"))
+ local transform = luaxml_sty.transformations[current] or luaxml_sty.declare_transformer(current)
+ if not transform then
+ luaxml_sty.error("Cannot find LuaXML transform object: " .. (current or ""))
+ return nil, "Cannot find LuaXML transform object: " .. (current or "")
+ end
+ transform:add_action(selector, rule, luaxml_sty.current.parameters)
+end
+
+-- by default, we will use XML parser, so use_xml is set to true
+luaxml_sty.use_xml = true
+
+--- Use XML parser for parsing of next snippets
+function luaxml_sty.set_xml()
+ luaxml_sty.use_xml = true
+end
+
+
+--- Use HTML parser for parsing of next snippets
+function luaxml_sty.set_html()
+ luaxml_sty.use_xml = false
+end
+
+--- transform XML string and print it to the output
+---@param current string transformer name, empty for the default object
+---@param xml_string string to be transformed
+function luaxml_sty.parse_snippet(current, xml_string)
+ local domobject = luaxml_sty.packages.domobject
+ -- get the current transformer object
+ if current == "" then
+ current = luaxml_sty.current.transformation
+ end
+ local transform = luaxml_sty.transformations[current]
+ if not transform then
+ luaxml_sty.error("Cannot load transformer: " .. current)
+ return nil, "Cannot load transformer: " .. current
+ end
+ local dom
+ -- decide if we should use XML or HTML parser
+ if luaxml_sty.use_xml then
+ dom = domobject.parse(xml_string)
+ else
+ dom = domobject.html_parse(xml_string)
+ end
+ luaxml_sty.debug(dom:serialize())
+ local result = transform:process_dom(dom)
+ luaxml_sty.debug(result)
+ luaxml_sty.packages.transform.print_tex(result)
+end
+
+--- Transform file
+---@param current string transformer name, empty for the default object
+---@param filename string file to be transformed
+function luaxml_sty.parse_file(current, filename)
+ local f = io.open(filename, "r")
+ if not f then
+ luaxml_sty.packages.transform.print_tex("\\textbf{LuaXML error}: cannot find file " .. filename)
+ return nil, "Cannot find file " .. filename
+ end
+ local content = f:read("*a")
+ f:close()
+ luaxml_sty.parse_snippet(current, content)
+end
+
+--- parse environment contents using Lua
+---@param env_name string environment name
+---@param callback_name string name which will be used in the callback registration
+---@return function
+-- idea from https://tex.stackexchange.com/a/574323/2891
+function luaxml_sty.store_lines(env_name, callback_name)
+ return function(str)
+ luaxml_sty.debug("str", str)
+ local env_str = [[\end{]] .. env_name .. "}"
+ if string.find (str , env_str:gsub("%*", "%%*")) then
+ luaxml_sty.debug("end of environment")
+ luatexbase.remove_from_callback ( "process_input_buffer" , callback_name)
+ return env_str -- str
+ else
+ table.insert(luaxml_sty.verb_table, str)
+ end
+ return ""
+ end
+end
+
+--- require line grabbing for an environment
+---@param env_name string environment name
+function luaxml_sty.register_verbatim(env_name)
+ luaxml_sty.verb_table = {}
+ local callback_name = "luaxml_store_lines_".. env_name
+ local fn = luaxml_sty.store_lines(env_name, callback_name)
+ luatexbase.add_to_callback(
+ "process_input_buffer" , fn , callback_name)
+end
+
+--- parse content of the previous environment registered using luaxml_sty.register_verbatim()
+--- and print the transformed content
+---@param transformer string transformer name, empty for the default object
+function luaxml_sty.print_verbatim(transformer)
+ luaxml_sty.parse_snippet(transformer, table.concat(luaxml_sty.verb_table, "\n"))
+end
+
+return luaxml_sty
diff --git a/macros/luatex/generic/luaxml/luaxml-transform.lua b/macros/luatex/generic/luaxml/luaxml-transform.lua
index b6841fea5f..34930df844 100644
--- a/macros/luatex/generic/luaxml/luaxml-transform.lua
+++ b/macros/luatex/generic/luaxml/luaxml-transform.lua
@@ -1,5 +1,5 @@
--- XML transformation module for LuaXML
--- @module luaxml-tranform
+-- @module luaxml-transform
-- @author Michal Hoftich <michal.h21@gmail.com
-- code originaly comes from from https://github.com/michal-h21/luaxml-mathml
@@ -198,6 +198,13 @@ local function add_action(selector, template, parameters, csspar)
end
+--- Remove actions for a given selector
+-- @param selector CSS selector for the matching element
+-- @param csspar cssquery object. Default is set by the library, so it is not necessary to use.
+local function reset_actions(selector, csspar)
+ local css = csspar or css
+ css:remove_selector(selector)
+end
--- Transform XML string
@@ -234,7 +241,7 @@ end
local function print_tex(content)
-- we need to replace "\n" characters with calls to tex.sprint
for s in content:gmatch("([^\n]*)") do
- tex.sprint(s)
+ tex.print(s)
end
end
@@ -243,7 +250,7 @@ end
local Transformer
--- Make new Transformer object
--- @return Tranformer object
+-- @return Transformer object
local function new()
local self = setmetatable({}, Transformer)
self.css = cssquery()
@@ -289,6 +296,12 @@ function Transformer:add_custom_action(selector, fn )
add_custom_action(selector, fn, self.css)
end
+--- Remove all actions that match the given selector
+---@param selector string
+function Transformer:reset_actions(selector)
+ reset_actions(selector, self.css)
+end
+
-- all methods that use transformation functions must
-- correctly handle the cssquery object that this library uses
@@ -338,6 +351,7 @@ local M = {
print_tex = print_tex,
add_action = add_action,
add_custom_action = add_custom_action,
+ reset_actions = reset_actions,
simple_content = simple_content,
load_file = load_file,
process_dom = process_dom,
diff --git a/macros/luatex/generic/luaxml/luaxml.pdf b/macros/luatex/generic/luaxml/luaxml.pdf
index f574a0a528..0a254c8935 100644
--- a/macros/luatex/generic/luaxml/luaxml.pdf
+++ b/macros/luatex/generic/luaxml/luaxml.pdf
Binary files differ
diff --git a/macros/luatex/generic/luaxml/luaxml.sty b/macros/luatex/generic/luaxml/luaxml.sty
new file mode 100644
index 0000000000..686de122d1
--- /dev/null
+++ b/macros/luatex/generic/luaxml/luaxml.sty
@@ -0,0 +1,123 @@
+\ProvidesPackage{luaxml}[2024-10-17 LuaXML package]
+
+\RequirePackage{luacode}
+
+% ToDo: add support for use of transformation rules for other XML syntaxes -- using LaTeX command?
+
+
+\begin{luacode*}
+luaxml_sty = require "luaxml-sty"
+\end{luacode*}
+
+
+\ExplSyntaxOn
+
+\bool_new:N\l_luaxml_verbatim
+\tl_new:N \l_luaxml_transformer
+
+\keys_define:nn{luaxml}{
+ verbatim .bool_set:N = \l_luaxml_verbatim ,
+ verbatim .initial:n = false,
+ unknown .code:n = \tl_set_eq:NN\l_luaxml_transformer\l_keys_key_str,
+ transformer .tl_set:N = \l_luaxml_transformer
+}
+
+
+% select current transformer object
+\NewDocumentCommand\LXMLUseTransformer{m}{
+ \directlua{
+luaxml_sty.current.transformation = "#1",
+}}
+
+% declare new transformer object
+\NewDocumentCommand\LXMLDeclareTransformer{m}{
+ \directlua{
+ % luaxml_sty.transformations["#1"] = luaxml_sty.packages.transform.new()
+ luaxml_sty.declare_transformer("#1")
+ }
+}
+
+% add transformer rule
+% #1 options (verbatim, transformer, unknown key = transformer)
+% #2 CSS selector
+% #3 transformer rule
+\NewDocumentCommand\LXMLRule{O{} m +v}{
+ % handle keyval options
+ \group_begin:
+ \keys_set:nn {luaxml} { #1 }
+ \directlua{luaxml_sty.current.parameters = {}}
+ \bool_if:NT \l_luaxml_verbatim {\directlua{luaxml_sty.current.parameters.verbatim = true}}
+ \directlua{
+ luaxml_sty.add_rule("\l_luaxml_transformer", "#2", "\luaescapestring{#3}")
+ }
+ \group_end:
+}
+
+
+% Transform code snippet
+% \LXMLSnippet* - parse using XML parser
+% \LXMLSnippet - parse using HTML parser
+% #2 transformer object name -- empty = default
+% #3 XML string
+\NewDocumentCommand\LXMLSnippet{s O{} m}{
+ \IfBooleanTF{#1}{
+ \directlua{luaxml_sty.set_html()}
+ }{
+ \directlua{luaxml_sty.set_xml()}
+ }
+ \directlua{
+ luaxml_sty.parse_snippet("\luaescapestring{#2}", [[\detokenize{#3}]])
+ }
+}
+
+% Transform file
+% \LXMLInputFile* - parse using XML parser
+% \LXMLInputFile - parse using HTML parser
+% #2 transformer object name -- empty = default
+% #3 XML string
+\NewDocumentCommand\LXMLInputFile{s O{} m}{
+ \IfBooleanTF{#1}{
+ \directlua{luaxml_sty.set_html()}
+ }{
+ \directlua{luaxml_sty.set_xml()}
+ }
+ \directlua{
+ luaxml_sty.parse_file("\luaescapestring{#2}", "\luaescapestring{#3}")
+ }
+}
+
+
+% Transform XML contained in the environment
+% #1 transformer object name -- empty = default
+\newenvironment{LXMLCode}[1]{
+ \directlua{
+ luaxml_sty.set_xml()
+ luaxml_sty.register_verbatim("LXMLCode")
+ }
+ \tl_set:Nn\l_luaxml_transformer{#1}
+}{
+ \directlua{
+ luaxml_sty.print_verbatim("\l_luaxml_transformer")
+ }
+}
+
+% Transform HTML contained in the environment
+% #1 transformer object name -- empty = default
+%
+% I couldn't process the first line in the environment using the Lua callback with \NewDocumentEnvironment,
+% so \newenvironment is used instead.
+\newenvironment{LXMLCode*}[1]{
+ \directlua{
+ luaxml_sty.set_html()
+ luaxml_sty.register_verbatim("LXMLCode*")
+ }
+ \tl_set:Nn\l_luaxml_transformer{#1}
+}{
+ \directlua{
+ luaxml_sty.print_verbatim("\l_luaxml_transformer")
+ }
+}
+
+
+\ExplSyntaxOff
+\endinput
diff --git a/macros/luatex/generic/luaxml/luaxml.tex b/macros/luatex/generic/luaxml/luaxml.tex
index 5031a0468e..d475516f8b 100644
--- a/macros/luatex/generic/luaxml/luaxml.tex
+++ b/macros/luatex/generic/luaxml/luaxml.tex
@@ -7,7 +7,7 @@
\usepackage{framed}
% Version is defined in the makefile, use default values when compiled directly
\ifdefined\version\else
-\def\version{v0.1r}
+\def\version{v0.2a}
\let\gitdate\date
\fi
\newcommand\modulename[1]{\subsection{#1}\label{sec:#1}}
@@ -17,6 +17,7 @@
\newcommand\functionsummary[1]{#1\\\textbf{Parameters:}\\}
\newcommand\functionparam[2]{\texttt{#1}: #2\\}
\newcommand\functionreturn[1]{\textbf{Return: }\\#1\\}
+\usepackage{luaxml}
\begin{document}
\title{The \textsc{LuaXML} library}
@@ -99,6 +100,67 @@ end
\end{luacode*}
\end{framed}
+\subsection{HTML parsing}
+
+You can parse HTML documents using the \verb|DOM_Object.html_parse| function. This parser is slower
+than the default XML parser, but it can load files that would cause errors in the XML mode.
+It can handle wrongly nested HTML tags, inline JavaScript and CSS styles, and other HTML features
+that would cause XML errors.
+
+\begin{verbatim}
+dom = require "luaxml-domobject"
+local document = [[
+<html>
+<head><title>sample</title></head>
+<body>
+<h1>test</h1>
+<p>hello
+<p>another paragraph
+<ul>
+<li>first
+<li>second
+</ul>
+</body>
+</html>
+ ]]
+
+-- dom.html_parse returns the DOM_Object
+obj = dom.html_parse(document)
+-- print names of all elements contained in body
+for _, x in ipairs(obj:query_selector("body *")) do
+ tex.print(x:get_element_name().. "\\par")
+end
+\end{verbatim}
+
+
+\begin{framed}
+\begin{luacode*}
+dom = require "luaxml-domobject"
+local document = [[
+<html>
+<head><title>sample</title></head>
+<body>
+<h1>test</h1>
+<p>hello
+<p>another paragraph
+<ul>
+<li>first
+<li>second
+</ul>
+</body>
+</html>
+ ]]
+
+-- dom.html_parse returns the DOM_Object
+obj = dom.html_parse(document)
+-- print names of all elements contained in body
+for _, x in ipairs(obj:query_selector("body *")) do
+ tex.print(x:get_element_name().. "\\par")
+end
+\end{luacode*}
+\end{framed}
+
+
\subsection{Void elements}
The \verb|DOM_Object.parse| function tries to support the HTML void elements,
@@ -295,6 +357,52 @@ body:replace_node(oldbody)
\end{framed}
+\subsubsection{Adding raw XML and HTML string}
+
+You can also set XML or HTML markup from a string to an element using the \texttt{DOM\_Object:inner\_html} function.
+Pass true as the second argument to parse string as XML, it is parsed as HTML otherwise.
+
+
+\begin{verbatim}
+local document = [[
+<html><p>hello</p>
+</html>
+]]
+local tree = dom.html_parse(document)
+local p = tree:query_selector("p")[1]
+-- insert inner_html as XML
+p:inner_html("hello <b>this</b> should be the new content")
+print(tree:serialize())
+\end{verbatim}
+
+In this example, we replace contents of the first \verb|<p>| element by new content.
+
+\begin{framed}
+ \ttfamily
+ \begin{luacode*}
+local document = [[
+<html><p>hello</p>
+</html>
+]]
+local tree = dom.html_parse(document)
+local p = tree:query_selector("p")[1]
+-- insert inner_html as XML
+p:inner_html("hello <b>this</b> should be the new content")
+tex.print(tree:serialize())
+ \end{luacode*}
+\end{framed}
+
+There are more variants of raw string methods that add the new content at specific places in the element instead of replacing contents
+of the element:
+
+\begin{description}
+ \item[\texttt{DOM\_Object:insert\_before\_begin}] -- before element.
+ \item[\texttt{DOM\_Object:insert\_after\_begin}] -- just inside the element, before its first child.
+ \item[\texttt{DOM\_Object:insert\_before\_end}] -- just inside the element, after its last child.
+ \item[\texttt{DOM\_Object:insert\_after\_end}] -- after the element.
+\end{description}
+
+
\section{The \texttt{CssQuery} library}
\label{sec:cssquery_library}
@@ -436,7 +544,7 @@ is placed to the output string.
There are two methods for action specification, \verb|add_action| for
text templates, and \verb|add_custom_action| for Lua functions.
-\subsubsection{Transforming using templates}
+\subsubsection{Transforming using templates}\label{sec:transform-templates}
Template actions can be added using the \verb|add_action| method:
@@ -456,6 +564,7 @@ attributes.
\begin{description}
\item[\verb|@\{attribute name\}|] insert value of an attribute
\item[\verb|@<.>|] insert transformed content of the element
+ \item[\texttt{\%s}] insert transformed content of the element. Shortcut for \verb|@<.>|.
\item[\verb|@<number>|] insert transformed content of the child element
selected by it's number in the list of children
\item[\verb|@<element name>|] insert transformed content of the named child element
@@ -641,7 +750,234 @@ transform.print_tex("\\verb|" .. result .. "|")
\end{luacode*}
\end{framed}
+\section{Character sets handling}
+
+The \texttt{luaxml-encodings} library provides functions to convert texts in legacy 8-bit encodings such as WINDOWS-1250
+or ISO-8859-2 to UTF-8. This can be useful in fixing document encoding before HTML parsing using the \texttt{luaxml-mod-html}
+library.
+\subsection{Example}
+
+
+\begin{verbatim}
+kpse.set_program_name "luatex"
+local encodings = require "luaxml-encodings"
+
+--read HTML page from the standard input
+local text = io.read("*all")
+-- find the character encoding in HTML metadata
+local enc = encodings.find_html_encoding(text)
+if enc then
+ -- local conversion table for the found encoding
+ local mapping = encodings.load_mapping(enc)
+ if mapping then
+ -- if the mapping exists, recode the HTML input and print it
+ local converted = encodings.recode(text, mapping)
+ print(converted)
+ end
+end
+\end{verbatim}
+
+\section{The \texttt{luaxml.sty} Package}
+
+The \texttt{luaxml.sty} package is designed to provide an interface for defining
+transformation rules for XML and HTML documents using Lua and \LaTeX\ commands.
+It allows users to declare transformation objects, apply transformation rules
+based on CSS selectors, and process XML or HTML from files or code snippets
+within \LaTeX\ documents.
+
+XML and HTML documents can be inserted from files or directly via commands and environments.
+All commands and environments intended for code input have two variants: with
+an asterisk for inputting HTML documents and without an asterisk for inputting
+XML documents.
+
+\subsection{Example of transformation using \LaTeX\ commands}
+
+\begin{verbatim}
+\LXMLRule[sample]{h1}|\par\noindent{\large\bfseries %s\par}|
+\LXMLRule[sample]{p}|%s\par|
+\LXMLRule[sample]{a[href]}|\href{@{href}}{%s}|
+ %% process HTML code
+\begin{LXMLCode*}{sample}
+<html>
+<h1>Hello</h1>
+<p>Here is a link to <a href="https://tex.stackexchange.com/">TeX.sx</a>
+</p>
+</html>
+\end{LXMLCode*}
+\end{verbatim}
+\begin{framed}
+
+\LXMLRule[sample]{h1}|\par\noindent{\large\bfseries %s\par}|
+\LXMLRule[sample]{p}|%s\par|
+\LXMLRule[sample]{a[href]}|\href{@{href}}{%s}|
+% process HTML code
+\begin{LXMLCode*}{sample}
+<html>
+<h1>Hello</h1>
+<p>Here is a link to <a href="https://tex.stackexchange.com/">TeX.sx</a>
+</p>
+</html>
+\end{LXMLCode*}
+\end{framed}
+
+
+\subsection{Declaring Transformation Rules}
+
+
+
+\begin{verbatim}
+\LXMLRule[<options>]\{<CSS selector>\}|<transformation rule>|
+\end{verbatim}
+
+\noindent Defines a transformation rule for the current transformer. The transformation
+is applied to elements matching the given CSS selector. You can define multiple transformers,
+for example if you want to support multiple XML syntaxes and HTML at the same time.
+
+\medskip
+
+\noindent The \texttt{<options>} parameter can include:
+
+\begin{itemize}
+ \item \texttt{verbatim}: Whether to process the rule in verbatim mode.
+ \item \texttt{transformer}: Specifies a transformer.
+\end{itemize}
+
+Any unknown key acts as a name of the transformer. In the following code, both
+examples add a rule to a transformer named \texttt{sample}.
+
+\begin{verbatim}
+\LXMLRule[transformer=sample]{b}|\textbf{%s}|
+\LXMLRule[sample]{i}|\textit{%s}|
+\end{verbatim}
+
+If you want to support only one syntax though, you don't need to specify the transformer name at all,
+a default object will be used.
+
+By default, spaces are collapsed. If you want to support elements where white spaces
+should be preserved, such as HTML \verb|<pre>| element, use the \verb|verbatim| option:
+
+\begin{verbatim}
+\LXMLRule[verbatim]{pre}|\begin{verbatim}
+ %s
+\end{verbatim}
+% trick to print \end{verbatim}|
+\verb+\end{verbatim}|+
+
+\bigskip
+
+The \texttt{transformation rule} must be delimited by a pair of characters that are not used in the
+text of the rule. We use \verb+|+ in our examples, but you can use other characters if you like.
+This is similar to how the \verb|\verb| command works. You can use the syntax
+shown in the section~\ref{sec:transform-templates} (page~\pageref{sec:transform-templates}).
+
+The following code defines rule that transforms the \verb|<h1>| element to a \verb|\section| command,
+and \verb|<a>| element which has a \verb|href| attribute to \verb|\href|. URL of the link is used
+thanks to the \verb|@{href}| rule.
+
+\begin{verbatim}
+\LXMLRule{h1}|{\section{%s}|
+\LXMLRule{a[href]}|\href{@{href}}{%s}|
+\end{verbatim}
+
+\subsection{Content Transformation}
+
+\begin{verbatim}
+\LXMLSnippet[<transformer name>]{<XML code>}
+\LXMLSnippet*[<transformer name>]{<HTML code>}
+\end{verbatim}
+
+\noindent The \verb|\LXMLSnippet| command processes a code snippet as XML or HTML.
+Use the starred variant for HTML
+input. The \texttt{<transformer name>} argument specifies the transformer object to apply
+(default is used if empty). The code to be transformed is passed in the second
+argument.
+
+\medskip
+
+\noindent{XML snippet transformation:}
+\begin{verbatim}
+\LXMLRule[xmlsnippet]{title}|title: %s|
+\LXMLSnippet{<root><title>Hello</title></root>}
+\end{verbatim}
+
+\begin{framed}
+\LXMLRule[xmlsnippet]{title}|title: %s|
+\LXMLSnippet[xmlsnippet]{<root><title>Hello</title></root>}
+\end{framed}
+
+\noindent{HTML snippet transformation:}
+\begin{verbatim}
+\LXMLRule[htmlsnippet]{h1}|title: %s|
+\LXMLSnippet*[htmlsnippet]{<div><h1>Header</h1></div>}
+\end{verbatim}
+
+\begin{framed}
+\LXMLRule[htmlsnippet]{h1}|title: %s|
+\LXMLSnippet*[htmlsnippet]{<div><h1>Header</h1></div>}
+\end{framed}
+
+\vtop\bgroup
+\begin{verbatim}
+\LXMLInputFile[<transformer>]{<XML file path>}
+\LXMLInputFile*[<transformer>]{<HTML file path>}
+\end{verbatim}
+
+\noindent Processes a file as XML or HTML. Use the starred variant for HTML input. The \texttt{<transformer>} specifies the transformer object to apply (default is used if empty). The file path is passed in the second argument.
+\egroup
+
+\noindent\textbf{Environments}
+
+\medskip
+\noindent \textbf{\texttt{\textbackslash begin\{LXMLCode\}\{<transformer>\}} ... \texttt{\textbackslash end\{LXMLCode\}}}
+
+
+\noindent Processes XML code inside the environment. The \texttt{<transformer>} specifies the transformer object to apply (default is used if empty).
+
+\begin{verbatim}
+\LXMLRule[xmlenv]{element}|hello: %s|
+\begin{LXMLCode}{xmlenv}
+<root>
+ <element>Some content</element>
+</root>
+\end{LXMLCode}
+\end{verbatim}
+
+\begin{framed}
+\LXMLRule[xmlenv]{element}|hello: %s|
+\begin{LXMLCode}{xmlenv}
+<root>
+ <element>Some content</element>
+</root>
+\end{LXMLCode}
+\end{framed}
+
+\medskip
+\noindent\textbf{\texttt{\textbackslash begin\{LXMLCode*\}\{<transformer>\}} ... \texttt{\textbackslash end\{LXMLCode*\}}}
+
+\noindent Processes HTML code inside the environment. The \texttt{<transformer>} specifies the transformer object to apply (default is used if empty).
+
+\begin{verbatim}
+\LXMLRule[htmlenv]{p}|paragraph: %s|
+\begin{LXMLCode*}{htmlenv}
+<div>
+ <p>Some HTML content</p>
+</div>
+\end{LXMLCode*}
+\end{verbatim}
+
+\begin{framed}
+\LXMLRule[htmlenv]{p}|paragraph: %s|
+\begin{LXMLCode*}{htmlenv}
+<div>
+ <p>Some HTML content</p>
+</div>
+\end{LXMLCode*}
+\end{framed}
+
+
+
+\clearpage
\section{The API documentation}
\input{doc/api.tex}
diff --git a/macros/luatex/generic/nodetree/README.md b/macros/luatex/generic/nodetree/README.md
index f9fdb53fff..71297fb7f1 100644
--- a/macros/luatex/generic/nodetree/README.md
+++ b/macros/luatex/generic/nodetree/README.md
@@ -18,7 +18,7 @@ terminal to generate the document.
# License
-Copyright (C) 2016-2023 by Josef Friedrich <josef@friedrich.rocks>
+Copyright (C) 2016-2024 by Josef Friedrich <josef@friedrich.rocks>
------------------------------------------------------------------------
This work may be distributed and/or modified under the conditions of
the LaTeX Project Public License, either version 1.3 of this license
@@ -150,8 +150,8 @@ if not modules then modules = { } end modules ['nodetree'] = {
### Update the copyright year:
```
-sed -i 's/(C) 2016-2023/(C) 2016-2021/g' nodetree.ins
-sed -i 's/(C) 2016-2023/(C) 2016-2021/g' nodetree.dtx
+sed -i 's/(C) 2016-2024/(C) 2016-2021/g' nodetree.ins
+sed -i 's/(C) 2016-2024/(C) 2016-2021/g' nodetree.dtx
```
### Command line tasks:
diff --git a/macros/luatex/generic/nodetree/nodetree-doc.tex b/macros/luatex/generic/nodetree/nodetree-doc.tex
index d9531ad9f2..c992d70246 100644
--- a/macros/luatex/generic/nodetree/nodetree-doc.tex
+++ b/macros/luatex/generic/nodetree/nodetree-doc.tex
@@ -107,7 +107,7 @@
\href{https://github.com/Josef-Friedrich/nodetree}{github.com/Josef-Friedrich/nodetree}\\%
with contributions by Werner Lemberg
}
-\date{v2.3.0 from 2023/09/10}
+\date{v2.4.0 from 2024/09/17}
\maketitle
diff --git a/macros/luatex/generic/nodetree/nodetree.dtx b/macros/luatex/generic/nodetree/nodetree.dtx
index 0b3668a8ce..6c274899d3 100644
--- a/macros/luatex/generic/nodetree/nodetree.dtx
+++ b/macros/luatex/generic/nodetree/nodetree.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-% Copyright (C) 2016-2023 by Josef Friedrich <josef@friedrich.rocks>
+% Copyright (C) 2016-2024 by Josef Friedrich <josef@friedrich.rocks>
% ----------------------------------------------------------------------
% This work may be distributed and/or modified under the conditions of
% the LaTeX Project Public License, either version 1.3 of this license
@@ -28,7 +28,7 @@
%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%<package>\ProvidesPackage{nodetree}
%<*package>
- [2023/09/10 v2.3.0 Visualize node lists in a tree view]
+ [2024/09/17 v2.4.0 Visualize node lists in a tree view]
%</package>
% \fi
%
@@ -218,7 +218,7 @@
% \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{nodetree-embed}
- [2023/09/10 v2.3.0 Embed node trees into a LaTeX document]
+ [2024/09/17 v2.4.0 Embed node trees into a LaTeX document]
% \end{macrocode}
%
% \begin{macrocode}
diff --git a/macros/luatex/generic/nodetree/nodetree.ins b/macros/luatex/generic/nodetree/nodetree.ins
index e1ded5bb1b..fce0ba7c8d 100644
--- a/macros/luatex/generic/nodetree/nodetree.ins
+++ b/macros/luatex/generic/nodetree/nodetree.ins
@@ -1,4 +1,4 @@
-% Copyright (C) 2016-2023 by Josef Friedrich <josef@friedrich.rocks>
+% Copyright (C) 2016-2024 by Josef Friedrich <josef@friedrich.rocks>
% ----------------------------------------------------------------------
% This work may be distributed and/or modified under the conditions of
% the LaTeX Project Public License, either version 1.3c of this license
@@ -21,7 +21,7 @@
This is a generated file.
-Copyright (C) 2016-2023 by Josef Friedrich <josef@friedrich.rocks>
+Copyright (C) 2016-2024 by Josef Friedrich <josef@friedrich.rocks>
----------------------------------------------------------------------
This work may be distributed and/or modified under the conditions of
the LaTeX Project Public License, either version 1.3c of this license
diff --git a/macros/luatex/generic/nodetree/nodetree.lua b/macros/luatex/generic/nodetree/nodetree.lua
index 18a1f204c3..82a3e294d3 100644
--- a/macros/luatex/generic/nodetree/nodetree.lua
+++ b/macros/luatex/generic/nodetree/nodetree.lua
@@ -30,7 +30,7 @@
--- be either `continue` or `stop`.
if not modules then modules = {} end modules ['nodetree'] = {
- version = '2.3.0',
+ version = '2.4.0',
comment = 'nodetree',
author = 'Josef Friedrich',
copyright = 'Josef Friedrich',
@@ -649,30 +649,37 @@ end
--- Convert a Lua table into a format string.
---
----@param table table # A table to generate an inline view of.
+---@param tbl table # A table to generate an inline view of.
---
---@return string
-function template.table_inline(table)
+function template.table_inline(tbl)
local tex_escape = ''
if options.channel == 'tex' then
tex_escape = '\\'
end
- if type(table) == 'table' then
- table = get_all_table_data(table)
+ if type(tbl) == 'table' then
+ tbl = get_all_table_data(tbl)
local output = tex_escape .. '{'
local kv_list = ''
- for key, value in pairs(table) do
- if type(key) ~= 'numbers' then
- key = '\'' ..
- template.colored_string(key, 'cyan', 'dim') .. '\''
- end
- kv_list = kv_list .. '[' .. key .. '] = ' ..
- template.table_inline(value) .. ', '
+ local keys = {}
+ for key in pairs(tbl) do
+ keys[#keys + 1] = key
+ end
+ table.sort(keys)
+ for i = 1, #keys do
+ local key = keys[i]
+ local value = tbl[key]
+ if type(key) ~= 'numbers' then
+ key = '\'' ..
+ template.colored_string(key, 'cyan', 'dim') .. '\''
+ end
+ kv_list = kv_list .. '[' .. key .. '] = ' ..
+ template.table_inline(value) .. ', '
end
output = output .. kv_list:gsub(', $', '')
return output .. tex_escape .. '}'
else
- return tostring(table)
+ return tostring(tbl)
end
end
diff --git a/macros/luatex/generic/nodetree/nodetree.pdf b/macros/luatex/generic/nodetree/nodetree.pdf
index c15b1e5c17..6c228dbe58 100644
--- a/macros/luatex/generic/nodetree/nodetree.pdf
+++ b/macros/luatex/generic/nodetree/nodetree.pdf
Binary files differ
diff --git a/macros/luatex/generic/penlight/README.md b/macros/luatex/generic/penlight/README.md
index a80a51c22e..18427f1141 100644
--- a/macros/luatex/generic/penlight/README.md
+++ b/macros/luatex/generic/penlight/README.md
@@ -5,7 +5,7 @@ This LuaLaTeX package provides a wrapper to use the [penlight](https://github.co
# License
-Copyright (C) 2021-2023 Kale Ewasiuk
+Copyright (C) 2021-2024 Kale Ewasiuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/macros/luatex/generic/penlight/penlight.lua b/macros/luatex/generic/penlight/penlight.lua
index 6ffcaf49b7..e104886a83 100644
--- a/macros/luatex/generic/penlight/penlight.lua
+++ b/macros/luatex/generic/penlight/penlight.lua
@@ -275,7 +275,7 @@ function Doc:reset()
end
---- Append a child to the currrent Node (ignoring current position).
+--- Append a child to the current Node (ignoring current position).
-- @param child a child node (either text or a document)
-- @return the current node (`self`)
-- @usage
@@ -311,7 +311,7 @@ end
--- Set attributes of a document node.
--- Will add/overwite values, but will not remove existing ones.
+-- Will add/overwrite values, but will not remove existing ones.
-- Operates on the Node itself, will not take position into account.
-- @tparam table t a table containing attribute/value pairs
-- @return the current node (`self`)
@@ -447,7 +447,7 @@ do
-- @param self document node to traverse
-- @param tag tag-name to look for
-- @param list array table to add the matching ones to
- -- @param recurse if truthy, recursivly search the node
+ -- @param recurse if truthy, recursively search the node
local function _children_with_name(self, tag, list, recurse)
-- TODO: protect against recursion
for _, child in ipairs(self) do
@@ -942,7 +942,7 @@ local html_empty_elements = { --lists all HTML empty (void) elements
}
--- Parse a well-formed HTML file as a string.
--- Tags are case-insenstive, DOCTYPE is ignored, and empty elements can be .. empty.
+-- Tags are case-insensitive, DOCTYPE is ignored, and empty elements can be .. empty.
-- @param s the HTML
function _M.parsehtml(s)
return _M.basic_parse(s,false,true)
@@ -1335,7 +1335,7 @@ end
-- Multiple short args can be combined like so: ( `-abcd`).
--
-- When specifying the `flags_valid` parameter, its contents can also contain
--- aliasses, to convert short/long flags to the same output name. See the
+-- aliases, to convert short/long flags to the same output name. See the
-- example below.
--
-- Note: if a flag is repeated, the last value wins.
@@ -1354,7 +1354,7 @@ end
-- { "hello", "world" }, -- list of flags taking values
-- { "l", "a", "b"}) -- list of allowed flags (value ones will be added)
--
--- -- More complex example using aliasses:
+-- -- More complex example using aliases:
-- local valid = {
-- long = "l", -- if 'l' is specified, it is reported as 'long'
-- new = { "n", "old" }, -- here both 'n' and 'old' will go into 'new'
@@ -1397,16 +1397,16 @@ function app.parse_args (args,flags_with_values, flags_valid)
valid = setmetatable({},{ __index = function(_, key) return key end })
else
valid = {}
- for k,aliasses in pairs(flags_valid) do
+ for k,aliases in pairs(flags_valid) do
if type(k) == "number" then -- array/list entry
- k = aliasses
+ k = aliases
end
- if type(aliasses) == "string" then -- single alias
- aliasses = { aliasses }
+ if type(aliases) == "string" then -- single alias
+ aliases = { aliases }
end
- if type(aliasses) == "table" then -- list of aliasses
+ if type(aliases) == "table" then -- list of aliases
-- it's the alternate name, so add the proper mappings
- for i, alias in ipairs(aliasses) do
+ for i, alias in ipairs(aliases) do
valid[alias] = k
end
end
@@ -1427,7 +1427,7 @@ function app.parse_args (args,flags_with_values, flags_valid)
end
-- now check that all flags with values are reported as such under all
- -- of their aliasses
+ -- of their aliases
for k, main_alias in pairs(valid) do
if with_values[main_alias] then
with_values[k] = true
@@ -1696,7 +1696,7 @@ function array2d.flatten (t)
return makelist(res)
end
---- reshape a 2D array. Reshape the aray by specifying a new nr of rows.
+--- reshape a 2D array. Reshape the array by specifying a new nr of rows.
-- @array2d t 2d array
-- @int nrows new number of rows
-- @bool co use column-order (Fortran-style) (default false)
@@ -1773,7 +1773,7 @@ end
--- extract the specified columns.
-- @array2d t 2d array
-- @tparam {int} cidx a table of column indices
--- @return a new 2d array with the extracted colums
+-- @return a new 2d array with the extracted columns
function array2d.extract_cols (t,cidx)
assert_arg(1,t,'table')
local res = {}
@@ -2097,7 +2097,7 @@ end,
--------------------
-- Module: 'pl.class'
--------------------
---- Provides a reuseable and convenient framework for creating classes in Lua.
+--- Provides a reusable and convenient framework for creating classes in Lua.
-- Two possible notations:
--
-- B = class(A)
@@ -2124,7 +2124,7 @@ local function call_ctor (c,obj,...)
init = rawget(parent_with_init, '_init')
parent_with_init = rawget(parent_with_init, '_parent_with_init')
end
- if parent_with_init then -- super() points to one above whereever _init came from
+ if parent_with_init then -- super() points to one above wherever _init came from
rawset(obj,'super',function(obj,...)
call_ctor(parent_with_init,obj,...)
end)
@@ -2970,9 +2970,9 @@ end
local config = {}
---- like `io.lines`, but allows for lines to be continued with '`\`'.
+--- like `io.lines`, but allows for lines to be continued with '`\\`'.
-- @param file a file-like object (anything where read() returns the next line) or a filename.
--- Defaults to stardard input.
+-- Defaults to standard input.
-- @return an iterator over the lines, or nil
-- @return error 'not a file-like object' or 'file is nil'
function config.lines(file)
@@ -3353,7 +3353,7 @@ function data.read(file,cnfg)
-- note that using dot as the thousands separator (@thousands_dot)
-- requires a special conversion function! For CSV, _empty fields_ are
- -- considered to default to numerial zeroes.
+ -- considered to default to numerical zeroes.
local tonumber = tonumber
local function try_number(x)
if thousands_dot then x = x:gsub('%.(...)','%1') end
@@ -5327,7 +5327,7 @@ function repr (e,lastpred)
end
func.repr = repr
--- collect all the non-PE values in this PE into vlist, and replace each occurence
+-- collect all the non-PE values in this PE into vlist, and replace each occurrence
-- with a constant PH (_C1, etc). Return the maximum placeholder index found.
local collect_values
function collect_values (e,vlist)
@@ -5411,13 +5411,20 @@ utils.add_function_factory(_PEMT,func.I)
func.bind1 = utils.bind1
func.curry = func.bind1
---- create a function which chains two functions.
+--- create a function which chains multiple functions.
-- @func f a function of at least one argument
-- @func g a function of at least one argument
+-- @param ... additional functions to compose
-- @return a function
--- @usage printf = compose(io.write,string.format)
-function func.compose (f,g)
- return function(...) return f(g(...)) end
+-- @usage printf = compose(io.write, string.format)
+-- @usage printf = compose(io.write, string.lower, string.format)
+function func.compose (...)
+ local args = pack(...)
+ return tablex.reduce(function(f, g)
+ return function(...)
+ return f(g(...))
+ end
+ end, args)
end
--- bind the arguments of a function to given values.
@@ -5475,7 +5482,7 @@ end,
--------------------
--------------
-- PL loader, for loading all PL libraries, only on demand.
--- Whenever a module is implicitly accesssed, the table will have the module automatically injected.
+-- Whenever a module is implicitly accessed, the table will have the module automatically injected.
-- (e.g. `_ENV.tablex`)
-- then that module is dynamically loaded. The submodules are all brought into
-- the table that is provided as the argument, or returned in a new table.
@@ -5573,7 +5580,7 @@ end,
--------------------
--------------
-- Entry point for loading all PL libraries only on demand, into the global space.
--- Requiring 'pl' means that whenever a module is implicitly accesssed
+-- Requiring 'pl' means that whenever a module is implicitly accessed
-- (e.g. `utils.split`)
-- then that module is dynamically loaded. The submodules are all brought into
-- the global space.
@@ -6872,7 +6879,7 @@ function List:insert(i, x)
return self
end
---- Insert an item at the begining of the list.
+--- Insert an item at the beginning of the list.
-- @param x a data item
-- @return the list
function List:put (x)
@@ -7282,7 +7289,7 @@ function List:iter ()
return iter(self)
end
---- Create an iterator over a seqence.
+--- Create an iterator over a sequence.
-- This captures the Python concept of 'sequence'.
-- For tables, iterates over all values with integer indices.
-- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function
@@ -7637,10 +7644,14 @@ local function makelist(t)
return setmetatable(t, require('pl.List'))
end
---- list of keys.
+--- return a List of all keys.
+-- @class function
+-- @name Map:keys
Map.keys = tablex.keys
---- list of values.
+--- return a List of all values.
+-- @class function
+-- @name Map:values
Map.values = tablex.values
--- return an iterator over all key-value pairs.
@@ -7648,7 +7659,7 @@ function Map:iter ()
return pairs(self)
end
---- return a List of all key-value pairs, sorted by the keys.
+--- return a List of all key-value pairs, sorted by the keys in ascending order.
function Map:items()
local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self))
ls:sort(function(t1,t2) return t1[1] < t2[1] end)
@@ -8223,12 +8234,12 @@ local function err_func(name, param, err, code)
end
--- Lua iterator over the entries of a given directory.
--- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
+-- Implicit link to [`luafilesystem.dir`](https://lunarmodules.github.io/luafilesystem/manual.html#dir)
-- @function dir
path.dir = lfs.dir
--- Creates a directory.
--- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
+-- Implicit link to [`luafilesystem.mkdir`](https://lunarmodules.github.io/luafilesystem/manual.html#mkdir)
-- @function mkdir
path.mkdir = function(d)
local ok, err, code = lfs.mkdir(d)
@@ -8239,7 +8250,7 @@ path.mkdir = function(d)
end
--- Removes a directory.
--- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
+-- Implicit link to [`luafilesystem.rmdir`](https://lunarmodules.github.io/luafilesystem/manual.html#rmdir)
-- @function rmdir
path.rmdir = function(d)
local ok, err, code = lfs.rmdir(d)
@@ -8250,7 +8261,7 @@ path.rmdir = function(d)
end
--- Gets attributes.
--- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
+-- Implicit link to [`luafilesystem.attributes`](https://lunarmodules.github.io/luafilesystem/manual.html#attributes)
-- @function attrib
path.attrib = function(d, r)
local ok, err, code = attrib(d, r)
@@ -8261,7 +8272,7 @@ path.attrib = function(d, r)
end
--- Get the working directory.
--- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
+-- Implicit link to [`luafilesystem.currentdir`](https://lunarmodules.github.io/luafilesystem/manual.html#currentdir)
-- @function currentdir
path.currentdir = function()
local ok, err, code = currentdir()
@@ -8272,7 +8283,7 @@ path.currentdir = function()
end
--- Gets symlink attributes.
--- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
+-- Implicit link to [`luafilesystem.symlinkattributes`](https://lunarmodules.github.io/luafilesystem/manual.html#symlinkattributes)
-- @function link_attrib
path.link_attrib = function(d, r)
local ok, err, code = link_attrib(d, r)
@@ -8285,7 +8296,7 @@ end
--- Changes the working directory.
-- On Windows, if a drive is specified, it also changes the current drive. If
-- only specifying the drive, it will only switch drive, but not modify the path.
--- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
+-- Implicit link to [`luafilesystem.chdir`](https://lunarmodules.github.io/luafilesystem/manual.html#chdir)
-- @function chdir
path.chdir = function(d)
local ok, err, code = lfs.chdir(d)
@@ -8329,7 +8340,7 @@ end
--- does a path exist?
-- @string P A file path
--- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise
+-- @return the file path if it exists (either as file, directory, socket, etc), false otherwise
function path.exists(P)
assert_string(1,P)
return attrib(P,'mode') ~= nil and P
@@ -8426,8 +8437,8 @@ end
-- @string[opt] pwd optional start path to use (default is current dir)
function path.abspath(P,pwd)
assert_string(1,P)
- if pwd then assert_string(2,pwd) end
local use_pwd = pwd ~= nil
+ if use_pwd then assert_string(2,pwd) end
if not use_pwd and not currentdir() then return P end
P = P:gsub('[\\/]$','')
pwd = pwd or currentdir()
@@ -8671,17 +8682,35 @@ end
-- In windows, if HOME isn't set, then USERPROFILE is used in preference to
-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows.
-- @string P A file path
+-- @treturn[1] string The file path with the `~` prefix substituted, or the input path if it had no prefix.
+-- @treturn[2] nil
+-- @treturn[2] string Error message if the environment variables were unavailable.
function path.expanduser(P)
assert_string(1,P)
- if at(P,1) == '~' then
- local home = getenv('HOME')
- if not home then -- has to be Windows
- home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH')
- end
- return home..sub(P,2)
- else
+ if P:sub(1,1) ~= '~' then
return P
end
+
+ local home = getenv('HOME')
+ if (not home) and (not path.is_windows) then
+ -- no more options to try on Nix
+ return nil, "failed to expand '~' (HOME not set)"
+ end
+
+ if (not home) then
+ -- try alternatives on Windows
+ home = getenv 'USERPROFILE'
+ if not home then
+ local hd = getenv 'HOMEDRIVE'
+ local hp = getenv 'HOMEPATH'
+ if not (hd and hp) then
+ return nil, "failed to expand '~' (HOME, USERPROFILE, and HOMEDRIVE and/or HOMEPATH not set)"
+ end
+ home = hd..hp
+ end
+ end
+
+ return home..sub(P,2)
end
@@ -8839,7 +8868,7 @@ end
--- an iterator over all permutations of the elements of the given lists.
-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided (see `utils.pack`)
--- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index
+-- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceded by an index
-- @usage
-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety
-- local bools = utils.pack(true, false)
@@ -8977,6 +9006,42 @@ local assert_arg = utils.assert_arg
local original_tostring = tostring
+-- Calculate min and max integer supported by lua_Number
+-- Assumptions:
+-- 1. max_int = 2 ^ n - 1
+-- 2. min_int = -max_int
+-- 3. if n > max_int versions with integer support will have
+-- integer overflow and versions without integers will lose least significant bit
+-- Note: if lua_Integer is smaller than lua_Number mantissa string.format('%d')
+-- can throw runtime error
+local max_int, min_int
+local next_cand = 1
+while next_cand > 0 and next_cand % 2 == 1 do
+ max_int = next_cand
+ min_int = -next_cand
+ next_cand = next_cand * 2 + 1
+end
+
+local function is_integer(value)
+ if _VERSION == "Lua 5.3" or _VERSION == "Lua 5.4" then
+ return mtype(value) == "integer"
+ end
+ if value < min_int or value > max_int then
+ return false
+ end
+ return math.floor(value) == value
+end
+
+local function is_float(value)
+ if _VERSION == "Lua 5.3" or _VERSION == "Lua 5.4" then
+ return mtype(value) == "float"
+ end
+ if value < min_int or value > max_int then
+ return true
+ end
+ return mfloor(value) == value
+end
+
-- Patch tostring to format numbers with better precision
-- and to produce cross-platform results for
-- infinite values and NaN.
@@ -8989,11 +9054,11 @@ local function tostring(value)
return "Inf"
elseif value == -mhuge then
return "-Inf"
- elseif (_VERSION ~= "Lua 5.3" or mtype(value) == "integer") and mfloor(value) == value then
+ elseif is_integer(value) then
return ("%d"):format(value)
else
local res = ("%.14g"):format(value)
- if _VERSION == "Lua 5.3" and mtype(value) == "float" and not res:find("%.") then
+ if is_float(value) and not res:find("%.") then
-- Number is internally a float but looks like an integer.
-- Insert ".0" after first run of digits.
res = res:gsub("%d+", "%0.0", 1)
@@ -11289,7 +11354,7 @@ local function _partition(p,delim,fn)
end
end
---- partition the string using first occurance of a delimiter
+--- partition the string using first occurrence of a delimiter
-- @string s the string
-- @string ch delimiter (match as plain string, no patterns)
-- @return part before ch
@@ -11303,7 +11368,7 @@ function stringx.partition(s,ch)
return _partition(s,ch,stringx.lfind)
end
---- partition the string p using last occurance of a delimiter
+--- partition the string p using last occurrence of a delimiter
-- @string s the string
-- @string ch delimiter (match as plain string, no patterns)
-- @return part before ch
@@ -11575,7 +11640,7 @@ end
---- Miscelaneous
+--- Miscellaneous
-- @section misc
--- return an iterator over all lines in a string
@@ -11593,7 +11658,7 @@ function stringx.lines(s)
return s:gmatch('([^\n]*)\n')
end
---- inital word letters uppercase ('title case').
+--- initial word letters uppercase ('title case').
-- Here 'words' mean chunks of non-space characters.
-- @string s the string
-- @return a string with each word's first letter uppercase
@@ -11614,7 +11679,7 @@ do
--- Return a shortened version of a string.
-- Fits string within w characters. Removed characters are marked with ellipsis.
-- @string s the string
- -- @int w the maxinum size allowed
+ -- @int w the maximum size allowed
-- @bool tail true if we want to show the end of the string (head otherwise)
-- @usage ('1234567890'):shorten(8) == '12345...'
-- @usage ('1234567890'):shorten(8, true) == '...67890'
@@ -11688,7 +11753,7 @@ end
--- Python-style formatting operator.
-- Calling `text.format_operator()` overloads the % operator for strings to give
--- Python/Ruby style formated output.
+-- Python/Ruby style formatted output.
-- This is extended to also do template-like substitution for map-like data.
--
-- Note this goes further than the original, and will allow these cases:
@@ -11698,7 +11763,7 @@ end
-- 3. a map of var=value pairs
-- 4. a function, as in gsub
--
--- For the second two cases, it uses $-variable substituion.
+-- For the second two cases, it uses $-variable substitution.
--
-- When called, this function will monkey-patch the global `string` metatable by
-- adding a `__mod` method.
@@ -12799,59 +12864,72 @@ end,
local utils = require 'pl.utils'
-local append,format,strsub,strfind,strgsub = table.insert,string.format,string.sub,string.find,string.gsub
+local append, concat = table.insert, table.concat
+local format, strsub, strfind, strgsub, strrep = string.format, string.sub, string.find, string.gsub, string.rep
-local APPENDER = "\n__R_size = __R_size + 1; __R_table[__R_size] = "
+local APPENDER = " __R_size = __R_size + 1; __R_table[__R_size] = "
+-- When this function returns, `pieces` is guaranteed to hold a complete Lua
+-- statement, meaning that new statements can be appended without creating
+-- invalid Lua code.
local function parseDollarParen(pieces, chunk, exec_pat, newline)
local s = 1
for term, executed, e in chunk:gmatch(exec_pat) do
- executed = '('..strsub(executed,2,-2)..')'
- append(pieces, APPENDER..format("%q", strsub(chunk,s, term - 1)))
- append(pieces, APPENDER..format("__tostring(%s or '')", executed))
+ executed = '(' .. strsub(executed, 2, -2) .. ')'
+ append(pieces, APPENDER .. format("%q;", strsub(chunk, s, term - 1)))
+ append(pieces, APPENDER .. format("__tostring(%s or '');", executed))
s = e
end
- local r
+ local remainder, newlines_removed
if newline then
- r = format("%q", strgsub(strsub(chunk,s),"\n",""))
+ remainder, newlines_removed = strgsub(strsub(chunk, s), "\n", "")
else
- r = format("%q", strsub(chunk,s))
+ remainder, newlines_removed = strsub(chunk, s), 0
end
- if r ~= '""' then
- append(pieces, APPENDER..r)
+ if remainder ~= "" then
+ append(pieces, APPENDER .. format("%q;", remainder))
+ end
+ if newlines_removed > 0 then
+ append(pieces, strrep("\n", newlines_removed))
end
end
-local function parseHashLines(chunk,inline_escape,brackets,esc,newline)
- local exec_pat = "()"..inline_escape.."(%b"..brackets..")()"
+local function parseHashLines(chunk, inline_escape, brackets, esc, newline)
+ -- Escape special characters to avoid invalid expressions
+ inline_escape = utils.escape(inline_escape)
+ esc = utils.escape(esc)
+
+ local exec_pat = "()" .. inline_escape .. "(%b" .. brackets .. ")()"
- local esc_pat = esc.."+([^\n]*\n?)"
- local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat
- local pieces, s = {"return function()\nlocal __R_size, __R_table, __tostring = 0, {}, __tostring", n = 1}, 1
+ local esc_pat = esc .. "+([^\n]*\n?)"
+ local esc_pat1, esc_pat2 = "^" .. esc_pat, "\n" .. esc_pat
+ local pieces, s = {"return function() local __R_size, __R_table, __tostring = 0, {}, __tostring; "}, 1
while true do
- local _, e, lua = strfind(chunk,esc_pat1, s)
+ local _, e, lua = strfind(chunk, esc_pat1, s)
if not e then
local ss
- ss, e, lua = strfind(chunk,esc_pat2, s)
- parseDollarParen(pieces, strsub(chunk,s, ss), exec_pat, newline)
+ ss, e, lua = strfind(chunk, esc_pat2, s)
+ parseDollarParen(pieces, strsub(chunk, s, ss), exec_pat, newline)
if not e then break end
end
- if strsub(lua, -1, -1) == "\n" then lua = strsub(lua, 1, -2) end
- append(pieces, "\n"..lua)
+ if strsub(lua, -1, -1) ~= "\n" then lua = lua .. "\n" end -- Ensure trailing newline
+ append(pieces, lua)
+ -- since `lua` ends with a newline, there is no danger of subsequent
+ -- statements being gobbled up by comments or being altered
s = e + 1
end
- append(pieces, "\nreturn __R_table\nend")
+ append(pieces, "return __R_table; end")
-- let's check for a special case where there is nothing to template, but it's
-- just a single static string
local short = false
- if (#pieces == 3) and (pieces[2]:find(APPENDER, 1, true) == 1) then
- pieces = { "return " .. pieces[2]:sub(#APPENDER+1,-1) }
+ if (#pieces == 3) and (strfind(pieces[2], APPENDER, 1, true) == 1) then
+ pieces = { "return " .. strsub(pieces[2], #APPENDER + 1, -1) }
short = true
end
-- if short == true, the generated function will not return a table of strings,
-- but a single string
- return table.concat(pieces), short
+ return concat(pieces), short
end
local template = {}
@@ -12859,33 +12937,34 @@ local template = {}
--- expand the template using the specified environment.
-- This function will compile and render the template. For more performant
-- recurring usage use the two step approach by using `compile` and `ct:render`.
--- There are six special fields in the environment table `env`
---
--- * `_parent`: continue looking up in this table (e.g. `_parent=_G`).
--- * `_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
--- * `_escape`: character marking Lua lines, default is '#'
--- * `_inline_escape`: character marking inline Lua expression, default is '$'.
--- * `_chunk_name`: chunk name for loaded templates, used if there
--- is an error in Lua code. Default is 'TMP'.
--- * `_debug`: if truthy, the generated code will be printed upon a render error
---
-- @string str the template string
--- @tab[opt] env the environment
--- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last
--- return value (`source_code`) is only returned if the debug option is used.
-function template.substitute(str,env)
+-- @tparam[opt] table env the environment. This table has the following special fields:
+-- @tparam[opt=nil] table env._parent continue looking up in this table (e.g. `_parent=_G`).
+-- @tparam[opt="()"] string env._brackets bracket pair that wraps inline Lua expressions.
+-- @tparam[opt="#"] string env._escape character marking Lua lines.
+-- @tparam[opt="$"] string env._inline_escape character marking inline Lua expression.
+-- @tparam[opt="TMP"] string env._chunk_name chunk name for loaded templates, used if there
+-- is an error in Lua code.
+-- @tparam[opt=false] boolean env._debug if truthy, the generated code will be printed upon a render error.
+-- @treturn[1] string render result
+-- @treturn[1] nil
+-- @treturn[1] string source_code (only if '`env._debug`' was truthy).
+-- @treturn[2] nil
+-- @treturn[2] string error message
+-- @treturn[2] string source_code (only if '`env._debug`' was truthy).
+function template.substitute(str, env)
env = env or {}
local t, err = template.compile(str, {
- chunk_name = rawget(env,"_chunk_name"),
- escape = rawget(env,"_escape"),
- inline_escape = rawget(env,"_inline_escape"),
- inline_brackets = rawget(env,"_brackets"),
- newline = nil,
- debug = rawget(env,"_debug")
+ chunk_name = rawget(env, "_chunk_name"),
+ escape = rawget(env, "_escape"),
+ inline_escape = rawget(env, "_inline_escape"),
+ inline_brackets = rawget(env, "_brackets"),
+ newline = false,
+ debug = rawget(env, "_debug")
})
if not t then return t, err end
- return t:render(env, rawget(env,"_parent"), rawget(env,"_debug"))
+ return t:render(env, rawget(env, "_parent"), rawget(env, "_debug"))
end
--- executes the previously compiled template and renders it.
@@ -12894,12 +12973,16 @@ end
-- @tab[opt] parent continue looking up in this table (e.g. `parent=_G`).
-- @bool[opt] db if thruthy, it will print the code upon a render error
-- (provided the template was compiled with the debug option).
--- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last return value
--- (`source_code`) is only returned if the template was compiled with the debug option.
+-- @treturn[1] string render result
+-- @treturn[1] nil
+-- @treturn[1] string source_code (only if '`env._debug`' was truthy).
+-- @treturn[2] nil
+-- @treturn[2] string error message
+-- @treturn[2] string source_code (only if '`env._debug`' was truthy).
-- @usage
-- local ct, err = template.compile(my_template)
-- local rendered , err = ct:render(my_env, parent)
-local render = function(self, env, parent, db)
+local function render(self, env, parent, db)
env = env or {}
if parent then -- parent is a bit silly, but for backward compatibility retained
setmetatable(env, {__index = parent})
@@ -12911,25 +12994,27 @@ local render = function(self, env, parent, db)
if self.code and db then print(self.code) end
return nil, out, self.code
end
- return table.concat(out), nil, self.code
+ return concat(out), nil, self.code
end
--- compiles the template.
-- Returns an object that can repeatedly be rendered without parsing/compiling
--- the template again.
--- The options passed in the `opts` table support the following options:
---
--- * `chunk_name`: chunk name for loaded templates, used if there
--- is an error in Lua code. Default is 'TMP'.
--- * `escape`: character marking Lua lines, default is '#'
--- * `inline_escape`: character marking inline Lua expression, default is '$'.
--- * `inline_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
--- * `newline`: string to replace newline characters, default is `nil` (not replacing newlines).
--- * `debug`: if truthy, the generated source code will be retained within the compiled template object, default is `nil`.
---
--- @string str the template string
--- @tab[opt] opts the compilation options to use
--- @return template object, or `nil + error + source_code`
+-- the template again. Preserves the line layout of the template so that line
+-- numbers in error messages should point to the correct lines in the source
+-- string.
+-- @tparam string str the template string
+-- @tparam[opt] table opts the compilation options to use. This table supports the following options:
+-- @tparam[opt="TMP"] string opts.chunk_name chunk name for loaded templates, used if there
+-- is an error in Lua code.
+-- @tparam[opt="#"] string opts.escape character marking Lua lines.
+-- @tparam[opt="$"] string opts.inline_escape character marking inline Lua expression.
+-- @tparam[opt="()"] string opts.inline_brackets bracket pair that wraps inline Lua expressions.
+-- @tparam[opt=false] boolean opts.newline if truthy, newlines will be stripped from text in the template.
+-- @tparam[opt=false] boolean opts.debug if truthy, the generated code will be printed upon a render error.
+-- @treturn[1] ct compiled template object
+-- @treturn[2] nil
+-- @treturn[2] string error message
+-- @treturn[2] string source_code
-- @usage
-- local ct, err = template.compile(my_template)
-- local rendered , err = ct:render(my_env, parent)
@@ -12940,9 +13025,9 @@ function template.compile(str, opts)
local inline_escape = opts.inline_escape or '$'
local inline_brackets = opts.inline_brackets or '()'
- local code, short = parseHashLines(str,inline_escape,inline_brackets,escape,opts.newline)
+ local code, short = parseHashLines(str, inline_escape, inline_brackets, escape, opts.newline)
local env = { __tostring = tostring }
- local fn, err = utils.load(code, chunk_name,'t',env)
+ local fn, err = utils.load(code, chunk_name, 't', env)
if not fn then return nil, err, code end
if short then
@@ -13453,7 +13538,8 @@ local operators
local _function_factories = {}
-local utils = { _VERSION = "1.13.1" }
+local utils = { _VERSION = "1.14.0" }
+
for k, v in pairs(compat) do utils[k] = v end
--- Some standard patterns
@@ -13571,7 +13657,11 @@ end
-- @param value1 Value returned if cond is truthy
-- @param value2 Value returned if cond is falsy
function utils.choose(cond, value1, value2)
- return cond and value1 or value2
+ if cond then
+ return value1
+ else
+ return value2
+ end
end
--- convert an array of values to strings.
@@ -13727,7 +13817,7 @@ end
-- non-existing values, and/or converting strings/identifiers to other values.
--
-- Calling on the object does the same, but returns a soft error; `nil + err`, if
--- the call is succesful (the key exists), it will return the value.
+-- the call is successful (the key exists), it will return the value.
--
-- When calling with varargs or an array the values will be equal to the keys.
-- The enum object is read-only.
@@ -13755,7 +13845,7 @@ end
-- },
--
-- some_method = function(self)
--- return self.ERR.OUT_OF_BOUNDS
+-- return nil, self.ERR.OUT_OF_BOUNDS
-- end,
-- }
--
@@ -14011,6 +14101,11 @@ end
--- OS functions
-- @section OS-functions
+--- Execute a shell command.
+-- This function is a copy of `compat.execute`.
+-- @class function
+-- @name utils.execute
+
--- execute a shell command and return the output.
-- This function redirects the output to tempfiles and returns the content of those files.
-- @param cmd a shell command
@@ -14121,7 +14216,7 @@ end
-- @param s The input string
-- @param re optional A Lua string pattern; defaults to '%s+'
-- @param plain optional If truthy don't use Lua patterns
--- @param n optional maximum number of elements (if there are more, the last will remian un-split)
+-- @param n optional maximum number of elements (if there are more, the last will remain un-split)
-- @return a list-like table
-- @raise error if s is not a string
-- @see splitv
@@ -14175,8 +14270,8 @@ end
-- This is useful if you have a function which is relatively expensive,
-- but you don't know in advance what values will be required, so
-- building a table upfront is wasteful/impossible.
--- @param func a function of at least one argument
--- @return a function with at least one argument, which is used as the key.
+-- @param func a function that takes exactly one argument (which later serves as the cache key) and returns a single value
+-- @return a function taking one argument and returning a single value either from the cache or by running the original input function
function utils.memoize(func)
local cache = {}
return function(k)
diff --git a/macros/luatex/generic/penlight/penlight.pdf b/macros/luatex/generic/penlight/penlight.pdf
index 3932e74389..928e356b85 100644
--- a/macros/luatex/generic/penlight/penlight.pdf
+++ b/macros/luatex/generic/penlight/penlight.pdf
Binary files differ
diff --git a/macros/luatex/generic/penlight/penlight.sty b/macros/luatex/generic/penlight/penlight.sty
index 7a7c94b6a7..8a0bb2c8f0 100644
--- a/macros/luatex/generic/penlight/penlight.sty
+++ b/macros/luatex/generic/penlight/penlight.sty
@@ -1,6 +1,6 @@
% Kale Ewasiuk (kalekje@gmail.com)
-% 2023-07-22
-% Copyright (C) 2021-2023 Kale Ewasiuk
+% 2024-09-15
+% Copyright (C) 2021-2024 Kale Ewasiuk
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,7 @@
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
% OR OTHER DEALINGS IN THE SOFTWARE.
-\ProvidesPackage{penlight}[2023-07-22]
+\ProvidesPackage{penlight}[2024-09-15]
\directlua{penlight = require'penlight'}
diff --git a/macros/luatex/generic/penlight/penlight.tex b/macros/luatex/generic/penlight/penlight.tex
index 93d7bdbfc3..c3f141bcbe 100644
--- a/macros/luatex/generic/penlight/penlight.tex
+++ b/macros/luatex/generic/penlight/penlight.tex
@@ -1,6 +1,6 @@
% Kale Ewasiuk (kalekje@gmail.com)
-% 2023-07-22
-% Copyright (C) 2021-2023 Kale Ewasiuk
+% 2024-09-15
+% Copyright (C) 2021-2024 Kale Ewasiuk
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the "Software"), to deal
@@ -64,7 +64,7 @@
Documentation for Penlight can be found here:\\
\mbox{\url{https://lunarmodules.github.io/Penlight}}
-\\\\ This package uses version \cmd{1.13.1}
+\\\\ This package uses version \cmd{1.14.0} (2024-04-15)
\subsection*{Importing Penlight from within LaTeX}
Loading this package runs the Lua code: \texttt{penlight = require'penlight'}
@@ -95,8 +95,7 @@ Instead of using penlight.sty, you can simply:\\
\newpage
\section*{}
-This package used to contain \cmd{penlightextras.lua}.
-That functionality now belongs to a standalone package named \cmd{penlightplus}.
+Additional functionality is provided in the \cmd{penlightplus} package.
\section*{}
Disclaimer: I am not the author of the Lua Penlight library.
diff --git a/macros/luatex/generic/penlightplus/penlightplus.lua b/macros/luatex/generic/penlightplus/penlightplus.lua
index e51b01c14b..9bfc57605a 100644
--- a/macros/luatex/generic/penlightplus/penlightplus.lua
+++ b/macros/luatex/generic/penlightplus/penlightplus.lua
@@ -1,5 +1,5 @@
--% Kale Ewasiuk (kalekje@gmail.com)
---% 2024-03-14
+--% 2024-09-30
--% Copyright (C) 2021-2024 Kale Ewasiuk
--%
--% Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -295,7 +295,7 @@ function penlight.tex.aliasluastring(s, d)
s = s:delspace():upper():tolist()
d = d:delspace():upper():tolist()
for i, S in penlight.seq.enum(d:slice_assign(1,#s,s)) do
- if (S == 'E') or (S == 'F') then S = '' end -- E or F is fully expanded
+ if (S == 'F') then S = '' end -- F is fully expanded
penlight.tex.prtn('\\let\\plluastring'..penlight.Char(i)..'\\luastring'..S)
end
end
@@ -495,6 +495,85 @@ end
+
+
+
+
+
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- functions below extend the seq module
+
+
+function penlight.seq.check_neg_index(i, len, fallback)
+i = tostring(i):delspace()
+if i == '' then return fallback end
+i = tonumber(i)
+if i == nil then
+ local _ = 1*'"Attempted to use seqstr indexing with negative number, but length of list not provided"'
+ return fallback -- fallback is the number to fall back on if i isn't provided
+end
+len = tonumber(len)
+if i < 0 then
+ if len == nil then
+ local _ = 1*'"Attempted to use seqstr indexing with negative number, but length of list not provided"'
+ return pl.utils.raise("Attempted to use seqstr indexing with negative number, but length of list not provided")
+ end
+ i = len + 1 + i -- negative index
+end
+return i
+end
+
+penlight.seq.train_element_sep = ','
+penlight.seq.train_range_sep = ':'
+
+function penlight.seq.train(s, len)
+ -- parse a range given a string indexer
+ -- syntax is: s = 'i1, i2, r1:r2' where i1 and i2 are individual indexes.
+ -- r1:r2 is a range (inclusive).
+ -- a 'stride' can be given to ranges, eg. ::2 is 1,3,5,..., or 2::3 is 2,5,8,...
+ -- negative numbers can be used to index relative to the length of the table, eg, -1 -> len
+ -- if length is not given, negative indeing cannot be used
+ -- returns a penlight list of numbers
+
+ local t = penlight.List() -- list of indexes
+ local check_neg = penlight.seq.check_neg_index
+ for _, r in ipairs(s:split(penlight.seq.train_element_sep)) do
+ if r:find(penlight.seq.train_range_sep) then
+ r = r:split(penlight.seq.train_range_sep) -- if it's a range
+ t:extend(penlight.List.range(check_neg(r[1], len, 1),
+ check_neg(r[2], len, len),
+ tonumber(r[3])))
+ else
+ t:append(check_neg(r, len))
+ end
+ end
+ return t
+end
+
+function penlight.seq.itrain(s, len)
+-- iterator version of sequence-string
+ local t = penlight.seq.train(s, len)
+ local i = 0
+ return function ()
+ i = i + 1
+ if i <= #t then return t[i] end
+ end
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-- -- -- -- -- -- -- -- -- -- -- -- functions below extend the operator module
function penlight.operator.strgt(a,b) return tostring(a) > tostring(b) end
@@ -517,8 +596,6 @@ end
-
-
-- table stuff below
function penlight.tablex.strinds(t) -- convert indices that are numbers to string indices
@@ -772,37 +849,49 @@ __PDFmetadata__ = {}
penlight.tex.add_xspace_intext = true
-function penlight.tex.updatePDFtable(k, v, o) -- key val overwrite
- k = k:upfirst()
- if penlight.hasval(o) or (__PDFmetadata__[k] == nil) then
- __PDFmetadata__[k] = v
+function penlight.tex.checkPDFkey(k)
+ k = k:delspace():upfirst()
+ local keys_allowed = 'Title Author Subject Date Language Keywords Publisher Copyright CopyrightURL Copyrighted Owner CertificateURL Coverage PublicationType Relation Source Doi ISBN URLlink Journaltitle Journalnumber Volume Issue Firstpage Lastpage CoverDisplayDate CoverDate Advisory BaseURL Identifier Nickname Thumbnails '
+ if not keys_allowed:find(k ..' ') then
+ penlight.tex.pkgerror('penlightplus', 'invalid PDF metadata key assigned "'..k..'"')
end
+ return k
+end
+
+function penlight.tex.makePDFtablekv(kv)
+ local t_new = {}
+ for k, v in pairs(penlight.luakeys.parse(kv)) do
+ k = penlight.tex.checkPDFkey(k)
+ v = penlight.tex.makePDFvarstr(v)
+ t_new[k] = v
+ end
+ return t_new
end
+
penlight.tex.writePDFmetadata = function(t) -- write PDF metadata to xmpdata file
t = t or __PDFmetadata__
local str = ''
for k, v in pairs(t) do
- k = k:upfirst()
str = str..'\\'..k..'{'..v..'}'..'\n'
end
penlight.utils.writefile(tex.jobname..'.xmpdata', str)
end
-
-function penlight.tex.clear_cmds_str(s)
- return s:gsub('%s+', ' '):gsub('\\\\',' '):gsub('\\%a+',''):gsub('{',' '):gsub('}',' '):gsub('%s+',' '):strip()
-end
-
function penlight.tex.makePDFvarstr(s)
s = s:gsub('%s*\\sep%s+','\0'):gsub('%s*\\and%s+','\0') -- turn \and into \sep
+ -- todo preserve \%, \{, \}, \backslash, and \copyright
s = penlight.tex.clear_cmds_str(s)
s = s:gsub('\0','\\sep ')
--penlight.tex.help_wrt(s,'PDF var string')
return s
end
+function penlight.tex.clear_cmds_str(s)
+ return s:gsub('%s+', ' '):gsub('\\\\',' '):gsub('\\%a+',''):gsub('{',' '):gsub('}',' '):gsub('%s+',' '):strip()
+end
+
function penlight.tex.makeInTextstr(s)
local s, c_and = s:gsub('%s*\\and%s+','\0')
s = penlight.tex.clear_cmds_str(s)
@@ -857,12 +946,32 @@ end
+function penlight.caseswitch(s, c, kv)
+ local kvtbl = penlight.luakeys.parse(kv)
+ local sw = kvtbl[c] -- the returned switch
+ if sw == nil then -- if switch not found
+ if s == penlight.tex.xTrue then -- if star, throw error
+ pl.tex.pkgerror('penlight', 'case: "'..c..'" not found in key-vals: "'..kv..'"')
+ sw = ''
+ else
+ sw = kvtbl['__'] or '' -- use __ as not found case
+ end
+ end
+ tex.sprint(sw)
+end
+
+
penlight.tbls = {}
penlight.rec_tbl = ''
penlight.rec_tbl_opts = {}
+
+function penlight.tbl(s)
+ return penlight.get_tbl_item(s)
+end
+
function penlight.get_tbl_name(s)
if s == '' then
return penlight.rec_tbl
@@ -1027,116 +1136,3 @@ if penlight.hasval(__PL_GLOBALS__) then
end
-
-
--- graveyard
-
- --_xTrue = penlight.tex._xTrue
- --_xFalse = penlight.tex._xFalse
- --_xNoValue = penlight.tex._xNoValue
- --
- --prt = penlight.tex.prt
- --prtn = penlight.tex.prtn
- --wrt = penlight.tex.wrt
- --wrtn = penlight.tex.wrtn
- --
- --prtl = penlight.tex.prtl
- --prtt = penlight.tex.prtt
- --
- --help_wrt = penlight.tex.help_wrt
- --prt_array2d = penlight.tex.prt_array2d
- --
- --pkgwarn = penlight.tex.pkgwarn
- --pkgerror = penlight.tex.pkgerror
- --
- --defcmd = penlight.tex.defcmd
- --prvcmd = penlight.tex.prvcmd
- --newcmd = penlight.tex.newcmd
- --renewcmd = penlight.tex.renewcmd
- --deccmd = penlight.tex.deccmd
- --
- --_NumBkts = penlight.tex._NumBkts
- --opencmd = penlight.tex.opencmd
- --reset_bkt_cnt = penlight.tex.reset_bkt_cnt
- --add_bkt_cnt = penlight.tex.add_bkt_cnt
- --close_bkt_cnt = penlight.tex.close_bkt_cnt
-
-
--- graveyard
-
-
--- luakeys parses individual keys as ipairs, this changes the list to a pure map
---function penlight.luakeystomap(t)
--- local t_new = {}
--- for k, v in pairs(t) do
--- if type(k) == 'number' then
--- t_new[v] = true
--- else
--- t_new[k] = v
--- end
--- end
--- return t_new
---end
---if luakeys then -- if luakeys is already loaded
--- function luakeys.parseN(s, ...)
--- local t = luakeys.parse(s,...)
--- t = penlight.luakeystomap(t)
--- return t
--- end
---end
--- might not be needed
-
-
- --local func = check_func(func)
---local function check_func(func) -- check if a function is a PE, if so, make it a function
--- if type(func) ~= 'function' then
--- return I(func)
--- end
--- return func
---end
-
--- -- -- -- -- -- --
--- -- -- -- functions below extend the array2d module
-
-
---function penlight.array2d.map_slice1(func, L, i1, i2) -- map a function to a slice of an array, can use PlcExpr
--- i2 = i2 or i1
--- local len = #L
--- i1 = check_index(i1, len)
--- i2 = check_index(i2, len)
--- func = check_func(func)
--- for i in penlight.seq.range(i1,i2) do
--- L[i] = func(L[i])
--- end
--- return L
---end
-
- -- used this below when iter was not working..
- --i1, j1, i2, j2 = check_slice(M, i1, j1, i2, j2)
- --for i in penlight.seq.range(i1,i2) do
- -- for j in penlight.seq.range(j1,j2) do
- --end
- -- penlight may have fixed this
---local function check_index(ij, rc) -- converts array index to positive value if negative
--- if type(ij) ~= 'number' then
--- return 1
--- else
--- if ij < 0 then
--- ij = rc + ij + 1
--- elseif ij > rc then
--- ij = rc
--- elseif ij == 0 then
--- ij = 1
--- end
--- return ij
--- end
---end
---local function check_slice(M, i1, j1, i2, j2) -- ensure a slice is valid; i.e. all positive numbers
--- r, c = penlight.array2d.size(M)
--- i1 = check_index(i1 or 1, r)
--- i2 = check_index(i2 or r, r)
--- j1 = check_index(j1 or 1, c)
--- j2 = check_index(j2 or c, c)
--- return i1, j1, i2, j2
---end
-
diff --git a/macros/luatex/generic/penlightplus/penlightplus.pdf b/macros/luatex/generic/penlightplus/penlightplus.pdf
index 2e37875101..bb9b8027ff 100644
--- a/macros/luatex/generic/penlightplus/penlightplus.pdf
+++ b/macros/luatex/generic/penlightplus/penlightplus.pdf
Binary files differ
diff --git a/macros/luatex/generic/penlightplus/penlightplus.sty b/macros/luatex/generic/penlightplus/penlightplus.sty
index 3c411d227a..cc37b096b1 100644
--- a/macros/luatex/generic/penlightplus/penlightplus.sty
+++ b/macros/luatex/generic/penlightplus/penlightplus.sty
@@ -1,5 +1,5 @@
% Kale Ewasiuk (kalekje@gmail.com)
-% 2024-03-14
+% 2024-09-30
% Copyright (C) 2021-2024 Kale Ewasiuk
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -22,12 +22,14 @@
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
% OR OTHER DEALINGS IN THE SOFTWARE.
-\ProvidesPackage{penlightplus}[2024-03-14]
+\ProvidesPackage{penlightplus}[2024-09-30]
\RequirePackage{luacode}
\RequirePackage{luakeys}
\RequirePackage[import]{penlight}
\RequirePackage{etoolbox}
+\RequirePackage{tokcycle}
+
\DeclareOption{globals}{\luadirect{__PL_GLOBALS__ = true}}
\DeclareOption{pl}{\directlua{pl = penlight}}
@@ -40,23 +42,37 @@
\global\newcommand{\writePDFmetadata}{\luadirect{penlight.tex.writePDFmetadata()}}
-\NewDocumentCommand{\writePDFmetadatakv}{ s m }{
+\NewDocumentCommand{\writePDFmetadatakv}{ s O{f} m }{%
+\MakeluastringCommands[f]{#2}%
\IfBooleanTF{#1}{% if *, overwrite everything
\luadirect{
- __PDFmetadata__ = penlight.luakeys.parse(\luastring{#2})
- penlight.tex.writePDFmetadata()
+ __PDFmetadata__ = penlight.tex.makePDFtablekv(\plluastringA{#3})
}}{
\luadirect{
__PDFmetadata__ = __PDFmetadata__ or {}
- penlight.tablex.update(__PDFmetadata__, penlight.luakeys.parse(\luastring{#2}))
- penlight.tex.writePDFmetadata()
- }}
+ penlight.tablex.update(__PDFmetadata__, penlight.tex.makePDFtablekv(\plluastringA{#3}))
+ }}%
+ \writePDFmetadata{}%
}
\gdef\luastringT#1{\luastring{\unexpanded\expandafter\expandafter\expandafter{#1}}} % expand luastring twice
\global\let\luastringF\luastring % fully expanded luastring
+
+\xtokcycleenvironment\luastringeenv
+ {\addcytoks{##1}}
+ {\processtoks{##1}}
+ {\addcytoks[1]{##1}}
+ {\addcytoks{##1}}
+ {}
+ {\cytoks\expandafter{\expandafter\luastringO\expandafter{\the\cytoks}}}
+
+\def\luastringE#1{\luastringeenv#1\endluastringeenv}
+%\gdef\luastringE#1{\begin{luastringeenv#1\endluastringeenv}
+
+
+
% allow control over expansion of arguments to a latex function
\NewDocumentCommand{\MakeluastringCommands}{O{} m }{% #1 the desired commands #2 defaults
\luadirect{penlight.tex.aliasluastring(\luastring{#2},\luastring{#1})}%
@@ -97,6 +113,18 @@
+
+
+
+\NewDocumentCommand{\caseswitch}{s m +m}{\ignorespaces\luadirect{penlight.caseswitch(\luastring{#1},\luastring{#2},\luastringN{#3})}\unskip}
+% argument 1 is star option, which throws an error if case is not found
+% argument 2 is the case, fully expanded
+% argument 3 is a luakeys table of options, not expanded
+
+
+
+
+
%%% tbls below
\NewDocumentCommand{\tblnew}{m}{\luadirect{% initialize a tbl and set blank
@@ -134,6 +162,13 @@
table.insert(penlight.tbls[__tbl__], \luastring{#2})
}}
+
+\NewDocumentCommand{\tblappN}{m m}{\luadirect{% append to a table (ie using integer index) with a value (second arg) # todo option for string or number
+ __tbl__ = penlight.get_tbl_name(\luastring{#1})
+ table.insert(penlight.tbls[__tbl__], \luastringN{#2})
+}}
+
+
\NewDocumentCommand{\tblcon}{m m}{\luadirect{% concatenate to a table (ie using integer index) with a list of comma separated values (second arg) #
__tbl__ = penlight.get_tbl_name(\luastring{#1})
for k, v in ipairs(penlight.luakeys.parse(string.subpar(\luastring{#2}), {naked_as_value=true})) do
@@ -141,6 +176,14 @@
end
}}
+
+\NewDocumentCommand{\tblconN}{m m}{\luadirect{% concatenate to a table (ie using integer index) with a list of comma separated values (second arg) #
+ __tbl__ = penlight.get_tbl_name(\luastring{#1})
+ for k, v in ipairs(penlight.luakeys.parse(string.subpar(\luastringN{#2}), {naked_as_value=true})) do
+ table.insert(penlight.tbls[__tbl__], v)
+ end
+}}
+
\NewDocumentCommand{\tbladd}{m m}{\luadirect{% add a kv pair to a table
__tbl__, __key__ = penlight.get_tbl_index(\luastring{#1}, true)
penlight.tbls[__tbl__][__key__] = \luastring{#2}
@@ -167,6 +210,10 @@
penlight.set_tbl_item(\luastring{#1}, \luastring{#2})
}}
+\NewDocumentCommand{\tblsetN}{m m}{\luadirect{% set item with {value}
+ penlight.set_tbl_item(\luastring{#1}, \luastringN{#2})
+}}
+
\NewDocumentCommand{\tbldef}{ m m }{\luadirect{penlight.def_tbl(\luastring{#1}, \luastring{#2})}}
@@ -199,11 +246,3 @@
penlight.tbls[__tbl__][\luastring{#2}] = \luastringN{#3}
}}
-\let\chgtbl\tblchg
-\let\newtbl\tblnew
-\let\gettbl\tblget
-\let\settbl\tblset
-\let\deftbl\tbldef
-\let\gdeftbl\tblgdef
-\let\iftbl\tblif
-\let\iftblv\tblifv \ No newline at end of file
diff --git a/macros/luatex/generic/penlightplus/penlightplus.tex b/macros/luatex/generic/penlightplus/penlightplus.tex
index 015dac98ef..bc719dcd9c 100644
--- a/macros/luatex/generic/penlightplus/penlightplus.tex
+++ b/macros/luatex/generic/penlightplus/penlightplus.tex
@@ -1,5 +1,5 @@
% Kale Ewasiuk (kalekje@gmail.com)
-% 2024-03-14
+% 2024-09-30
% Copyright (C) 2021-2024 Kale Ewasiuk
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -59,18 +59,36 @@
\subtitle{Additions to the Penlight Lua Libraries}
-
\begin{document}
%
\maketitle
-This package first loads the \cmd{[import]penlight} package.\\
+\section*{Package Options and Set-Up}
+
+This package first loads the \cmd{[import]penlight} package---see the documentation here \url{https://lunarmodules.github.io/Penlight/index.html}.\\
The \texttt{pl} option may be passed to this package to create an alias for \cmd{penlight}.\\
-\texttt{globals} option may be used to make several of the functions global (as discussed below).
+
+The following global Lua variables are defined:
+
+\cmd{__SKIP_TEX__} If using the \cmd{penlightplus} package with \cmd{texlua} (good for troubleshooting), set this global before loading \cmd{penlight}\\
+\cmd{__PL_GLOBALS__} If using this package with \cmd{texlua} and you want to set some functions as globals (described in next sections), set this variable to \cmd{true} before loading \cmd{penlight}\\
+\cmd{__PL_NO_HYPERREF__} a flag used to change the behaviour of some functions, depending on if you don't use the hyperref package\\
+\cmd{__PDFmetadata__} a table used to store PDF meta-data for pdfx package.
+
+
+\subsubsection*{globals option}
+If the package option \cmd{globals} is used, many additional globals are set for easier scripting.
+\cmd{pl.hasval}, \cmd{pl.COMP}, \cmd{pl.utils.kpairs}, \cmd{pl.utils.npairs} become globals.
+\cmd{pl.tablex} is aliased as \cmd{pl.tbx and tbx} (which also includes all native Lua table functions), and
+\cmd{pl.array2d} is aliased as \cmd{pl.a2d and a2d}. Since this package uses the penlight \cmd{import} option,
+all \cmd{stringx} functions are injected into the \cmd{string} meta-table and you can use them like so: \cmd{'first name':upfirst()}.
+
+If you want global \cmd{pl.tex} functions and variables, call \cmd{pl.make_tex_global()}.\\
+
\subsection*{texlua usage}
-If you want to use penlightplus.lua with the \texttt{texlua} interpreter
+If you want to use \cmd{penlightplus.lua} with the \texttt{texlua} interpreter
(no document is made, but useful for testing your Lua code),
you can access it by setting \cmd{__SKIP_TEX__ = true} before loading. For example:
\begin{verbatim}
@@ -88,29 +106,12 @@ require'penlightplus'
-
-The following global Lua variables are defined:
-
-\cmd{__SKIP_TEX__} If using the \cmd{penlightplus} package with \cmd{texlua} (good for troubleshooting), set this global before loading \cmd{penlight}\\
-The gloals flags below are taken care of in the package options:\\
-\cmd{__PL_GLOBALS__} If using package with \cmd{texlua} and you don't want to set some globals (described in next sections), set this global before to \cmd{true} loading \cmd{penlight}\\
-\cmd{__PL_NO_HYPERREF__} a flag used to change the behaviour of a function, depending on if you don't use the hyperref package\\
-\cmd{__PDFmetadata__} a table used to store PDF meta-data
-
-
-\subsubsection*{global extras}
-If the package option \cmd{globals} is used, many additional globals are set for easier scripting.
-\cmd{pl.hasval}, \cmd{pl.COMP}, \cmd{pl.utils.kpairs}, \cmd{pl.utils.npairs} become globals.
-\cmd{pl.tablex} is aliased as \cmd{pl.tbx and tbx} (which also includes all native Lua table functions), and
-\cmd{pl.array2d} is aliased as \cmd{pl.a2d and a2d}.
-
-If you want global \cmd{pl.tex} funcs and vars, call \cmd{pl.make_tex_global()}\\
-
-
-\subsubsection*{penlight additions}
+\section*{penlight additions}
Some functionality is added to penlight and Lua.
+\subsection*{General Additions}
+
\llcmd{pl.hasval(x)} Python-like boolean testing\\
\llcmd{COMP'xyz'()} Python-like comprehensions:\\\url{https://lunarmodules.github.io/Penlight/libraries/pl.comprehension.html}\\
@@ -120,6 +121,16 @@ Some functionality is added to penlight and Lua.
\llcmd{math.mod(n,d)}, \cmd{math.mod2(n)} math modulous\\
+\llcmd{pl.utils.}\cmd{filterfiles}\cmd{(dir,filt,rec)} Get files from dir and apply glob-like filters. Set rec to \cmd{true} to include sub directories\\
+
+
+\llcmd{pl.}\cmd{char(n)} return letter corresponding to 1=a, 2=b, etc.\\
+\llcmd{pl.}\cmd{Char(n)} return letter corresponding to 1=A, 2=B, etc.\\
+
+
+
+\subsection*{string additions}
+
\llcmd{string.}\cmd{upfirst(s)} uppercase first letter\\
\llcmd{string.}\cmd{delspace(s)} delete all spaces\\
\llcmd{string.}\cmd{trimfl(s)}remove first and last chars\\
@@ -139,6 +150,7 @@ and \cmd{fmt} can be a table of formats (keys correspond to those in \cmd{t}), o
is processed by luakeys.\\
\llcmd{string.}\cmd{parsekv(s, opts)} parse a string using \cmd{penlight.luakeys}. A string or table can be used for opts.
+\subsection*{tablex additions}
\llcmd{tablex.}\cmd{fmt(t, f)} format a table with table or key-value string f\\
\llcmd{tablex.}\cmd{strinds(t)} convert integer indexes to string indices (1 -> '1')\\
\llcmd{tablex.}\cmd{filterstr(t,e,case)} keep only values in table t that contain expression e, case insensitive by default.\\
@@ -146,15 +158,32 @@ is processed by luakeys.\\
\llcmd{tablex.}\cmd{listcontains(t,v)} checks if a value is in a array-style list \\
+\subsubsection*{seq additions}
+A syntax to produce sequences or a 'train' of numbers is provided. This may be useful for including pages from a pdf, or selecting rows of a table with a concise syntax.
+\cmd{seq.train(trn, len)} produces a pl.List according to the arguments (like choo-choo train)\\
+\cmd{seq.itrain(trn, len)} produces an iterator according to the arguments.
+An example syntax for \cmd{trn} is \cmd{'i1, i2, r1:r2', etc.} where \cmd{i1} and \cmd{i2} are individual indexes/elements, separated by \cmd{,} and
+\cmd{r1:r2} is a range (inclusive of end-point) denoted with a \cmd{:}. The range format follows python's numpy indexing, and
+a 'stride' can be given by including a second colon like \cmd{::2 -> is 1,3,5,...}, or \cmd{2::3 -> 2,5,8,...}.
+Negative numbers can be used to index relative to the length of the table, eg, \cmd{-1 -> len}, but
+if length is not given, negative indexing cannot be used and a number after the first colon must be provided.
+A missing left-number on the colon assumes \cmd{1}, and missing right number assumes \cmd{len}. A missing 'stride' (number after the optional second colon) assumes a value of 1.
-\llcmd{pl.}\cmd{char(n)} return letter corresponding to 1=a, 2=b, etc.\\
-\llcmd{pl.}\cmd{Char(n)} return letter corresponding to 1=A, 2=B, etc.\\
+The default colon and comma separators for ranges and elements can be set with \cmd{seq.train_range_sep} and \cmd{seq.train_element_sep}, respectively.
+\begin{LTXexample}[width=0.5\linewidth]
+\begin{luacode*}
+ for i in
+ pl.seq.itrain('1, :, 6, 0::2, -3 ',
+ 5) do
+ tex.print(i..',')
+ end
+\end{luacode*}
+\end{LTXexample}
-\llcmd{pl.utils.}\cmd{filterfiles}\cmd{(dir,filt,rec)} Get files from dir and apply glob-like filters. Set rec to \cmd{true} to include sub directories\\
-\subsubsection*{A \cmd{pl.tex.} module is added}
+\subsection*{A \cmd{pl.tex.} module is added}
\llcmd{add_bkt}\cmd{_cnt(n), }\cmd{close_bkt_cnt(n), reset_bkt_cnt} functions to keep track of adding curly brackets as strings. \cmd{add} will return \cmd{n} (default 1) \{'s and increment a counter. \cmd{close} will return \cmd{n} \}'s (default will close all brackets) and decrement.\\
\llcmd{_NumBkts} internal integer for tracking the number of brackets\\
\llcmd{opencmd(cs)} prints \cmd{\cs}\{ and adds to the bracket counters.\\
@@ -164,7 +193,7 @@ is processed by luakeys.\\
\llcmd{prt(x),prtn(x)} print without or with a newline at end. Tries to help with special characters or numbers printing.\\
\llcmd{prtl(l),prtt(t)} print a literal string, or table\\
\llcmd{wrt(x), wrtn(x)} write to log\\
-\llcmd{wrh}\cmd{(s1, s2)} pretty-print something to console. S2 is a flag to help you find., alias is \cmd{help_wrt}, also in \cmd{pl.wrth}\\
+\llcmd{wrth}\cmd{(s1, s2)} pretty-print something to console. S2 is a flag to help you find., alias is \cmd{help_wrt}, also in \cmd{pl.wrth}\\
\llcmd{prt_array2d(tt)} pretty print a 2d array\\
\\
\llcmd{pkgwarn}\cmd{(pkg, msg1, msg2)} throw a package warning\\
@@ -180,27 +209,42 @@ to a package warning saying \cmd{'cs' was declared and used in document, but nev
is true, it will overwrite an existing command (using \cmd{defcmd}), otherwise, it will throw error like \cmd{newcmd}.\\
\llcmd{get_ref_info(l)}accesses the \cmd{\r@label} and returns a table\\
-\subsubsection*{Recording latex input}
+
+
+
+\subsection*{Recording LaTeX input as a lua variable}
\cmd{penlight.tex.startrecording()} start recording input buffer without printing to latex\\
\cmd{penlight.tex.stoprecording()} stop recording input buffer\\
\cmd{penlight.tex.readbuf()} internal-use function that interprets the buffer. This will ignore an environment ending (eg. \cmd{end{envir}})\\\\
\cmd{penlight.tex.recordedbuf} the string variable where the recorded buffer is stored\\
+\section*{penlightplus LaTeX Macros}
\subsection*{Macro helpers}
\cmd{\MakeluastringCommands[def]{spec}} will let \cmd{\plluastring(A|B|C..)} be \cmd{\luastring(N|O|T|F)}
-based on the letters that \cmd{spec} is set to (or \cmd{def} if nothing is provided)
+based on the letters that \cmd{spec} is set to (or \cmd{def}(ault) if nothing is provided)
This is useful if you want to write a command with flexibility on argument expansion.
The user can specify \cmd{n}, \cmd{o}, \cmd{t}, and \cmd{f} (case insensitve) if they want
-no, once, twice, or full expansion. For example, we can control the expansion of args 2 and 3 with arg 1:
+none, once, twice, or full expansion.
+
+%\cmd{e} expansion expands every token in the argument only once.
+%\cmd{\luastringE{m}}, expands each token once (note that \cmd{\luastringO} only expands the first token once)
+
+
+
+Variants of luastring are added:\\
+\cmd{\luastringF{m}} = \cmd{\luastring{m}} \\
+\cmd{\luastringT{m}}, expand the first token of m twice\\
+
+For example, we can control the expansion of args 2 and 3 with arg 1:
\begin{verbatim}
\NewDocumentCommand{\splittocomma}{ O{nn} m m }{%
- \MakeluastringCommands[nn]{#1}%
- \luadirect{penlight.tex.split2comma(\plluastringA{#2},\plluastringB{#3})}%
-}
+ \MakeluastringCommands[nn]{#1}%
+ \luadirect{penlight.tex.split2comma(\plluastringA{#2},\plluastringB{#3})}%
+ }
\end{verbatim}
% BELOW IS FOR TROUBLESHOOTING ABOVE
@@ -222,7 +266,7 @@ no, once, twice, or full expansion. For example, we can control the expansion of
%\luastringExpTest{ n o t f }{\NOTexp}
-\subsection*{Lua boolean expressions for LaTeX conditionals}
+\subsection*{Lua boolean expressions}
\cmd{\ifluax{<Lua expr>}{<do if true>}[<do if false>]} and\\ \cmd{\ifluax{<Lua expr>}{<do if true>}[<do if false>]} for truthy (uses \cmd{penlight.hasval})
@@ -234,39 +278,68 @@ no, once, twice, or full expansion. For example, we can control the expansion of
\ifluaxv{''}{true}[false]\\
\end{LTXexample}
-\subsection*{Creating and using Lua tables in LaTeX}
+\subsection*{Case-switch for Conditionals}
+
+\cmd{\caseswitch{case}{kev-val choices}} The starred version will throw an error if the case is not found.
+Use \_\_ as a placeholder for a case that isn't matched.
+
+\begin{LTXexample}[width=0.3\linewidth]
+\def\caseswitchexample{\caseswitch{\mycase}{dog=DOG, cat=CAT, __=INVALID}}
+\def\mycase{dog} \caseswitchexample \\
+\def\mycase{human} \caseswitchexample
+\end{LTXexample}
+%\caseswitch*{\mycase}{dog=DOG, cat=CAT, __=INVALID}
+
+
+\subsection*{Creating and using Lua tables in LaTeX - tbl interace}
+
\cmd{penlightplus} provides a Lua-table interface. Tables are stored in the
\cmd{penlight.tbls} table.
+You can access a table item within lua by using:
+\cmd{penlight.tbl'i'}.
%%%
\cmd{\tblnew{t}} declares a new table with name \cmd{t}\\
\cmd{\tblchg{t}} changes the 'recent' table\\
+\\
\cmd{\tblfrkv{t}{key-val string}[luakeys opts]} new table from key-vals using \cmd{luakeys} \\
\cmd{\tblfrkvN{t}{key-val string}[luakeys opts]} does not expand key-val string \cmd{luakeys} \\
\cmd{\tblfrkvCD{t}{key-val string}[luakeys opts]} define tbl from key-val,
check if any were not defined as defaults (see below), and then push all to definitions\\
+\\
\cmd{\tblkvundefcheck} will throw an error if you use define a table from key-values
-and use a key that was not specified in the luakeys parse options via \cmd{opts.defaults} or \cmd{opts.defs}.
+and use a key that was not specified in the luakeys parse options via \cmd{opts.defaults} or \cmd{opts.defs}.\\
+\\
\cmd{\tblfrcsv{t}{csv}} a shorthand \cmd{\tblfrkv{t}{csv}[naked_as_value=true,opts]}, a good way to convert a comma-separated list to an array\\
- \cmd{\tblfrcsvN{t}{csv}} same as above, but the csv is not expanded.
+ \cmd{\tblfrcsvN{t}{csv}} same as above, but the csv is not expanded.\\
+\\
\cmd{\tblset{i}{v}} sets a value of the table/index \cmd{i} to \cmd{v}\\
+\cmd{\tblsetN{i}{v}} same as above, but the value is not expanded.\\
+\\
\cmd{\tblget{i}} gets the value and \cmd{tex.sprint()}s it\\
+\\
\cmd{\tbladd{i}{v}} add a new value to a table using index method\\
\cmd{\tbladdN{i}{v}} above, but don't expand the value argument\\
+\\
\cmd{\tblcon{t}{csv}} concatenate an array-style csv\\
+\cmd{\tblconN{t}{csv}}\\
+\\
\cmd{\tblapp{t}{v}} append a value (integer-wise) to a table\\
+\cmd{\tblappN{t}{v}}\\
+\\
\cmd{\tbldef{i}{d}} pushes the value to macro \cmd{d}\\
\cmd{\tbldefall{t}{d}} define all item in table \cmd{t} (use recent if blank) with format \cmd{d<key>} where d is your prefix. If d is blank, keys will be defined as \cmd{\dtbl<t><k>}
\cmd{\tblgdef{i}{d}} pushes the defined value to a global\\
\cmd{\tbldefxy{i}{d}} splits the value of item by spaces creates two definitions \cmd{\dx} and \cmd{\dy}. Useful for pasing tikz coordinates like \cmd{xy=0 5}\\
For definiting tables, if \cmd{d} is blank, commands are defined as \cmd{dtbl<t><k>}\\
+\\
\cmd{\iftbl{i}{tr}[fa]} runs code \cmd{ta} if the item is true else \cmd{fr}\\
-\cmd{\iftblv{i}{tr}[fa]} runs code \cmd{ta} if the item is truthy else \cmd{fr}\\
+\cmd{\iftblv{i}{tr}[fa]} runs code \cmd{ta} if the item is truthy (using \cmd{pl.hasval}) else \cmd{fr}\\
\cmd{\tblprt{t}} print the table in console
-There are 3 ways to use the index (placeholder \cmd{{i}} above).
+There are 3 ways to use the index (placeholder \cmd{{i}} above, note that this argument is fully expanded).
\cmd{t.key} where \cmd{t} is the table name and \cmd{key} is a string key,
\cmd{t/int} where \cmd{int} is an integer index (ie. uses \cmd{t[int]}, note that negative indexes are allowered where -1 is the last element),
or simply use \cmd{ind} without the table name, where the assumed table is the last one that was created or changed to, (passing a number will used as an integer index).
@@ -323,8 +396,6 @@ or simply use \cmd{ind} without the table name, where the assumed table is the l
%\tblget{Bonjour}
-Note: for this versions: all latex tbl commands are now prefixed with \cmd{tbl}, eg., \cmd{tblget}, \cmd{tblset}.
-Old-style commands eg. \cmd{gettbl} will be kept as aliases for a few more releases then removed.
\subsubsection*{A practical tbl example}
@@ -359,6 +430,7 @@ C: \tblget{c}
\end{LTXexample}
+
\subsection*{Splitting strings}
Splitting text (or a cmd) into oxford comma format via:
\cmd{\splittocomma[expansion level]{text}{text to split on}}:
@@ -386,12 +458,66 @@ You can do a similar string split but to \cmd{\item} instead of commas with \cmd
-\subsubsection*{PDF meta data (for pdfx package)}
-\cmd{\writePDFmetadatakv*{m}} Take a key-value string (eg. \cmd{title=whatever, author=me}) and writes to the \cmd{jobname.xmpdata} file, to be used by pdfx. \cmd{*} will first clear the data\\
+\subsection*{PDF meta data (for pdfx package)}
+\cmd{\writePDFmetadatakv*[x]{kv}} Take a key-value string (eg. \cmd{title=whatever, author=me}) and then writes to the \cmd{jobname.xmpdata} file, which is used by pdfx. \cmd{*} will first clear \cmd{__PDFmetadata__} which contains the metadata. The un-starred version updates that table.
+You can control the expansion of the key-val argument with \cmd{[x]}, which is fully expanded by default.
+Command sequences are ultimately stripped from the values, except for \cmd{\and} is converted to \cmd{\sep} for pdfx usage (\url{https://texdoc.org/serve/pdfx/0}).
+\\
+
\cmd{\writePDFmetadata} runs the lua function \cmd{penlight.tex.writePDFmetadata()},
which pushes the lua variable \cmd{__PDFmetadata__} (a table) to the xmpdata file.
+This might be useful if you're updating \cmd{__PDFmetadata__} by some other means.
+
+\def\thekeywords{A\and B\and C}
+\def\thekeywords{ABC}
+\begin{LTXexample}
+\writePDFmetadatakv{author=Some One} %
+\writePDFmetadatakv*[n]{author=Kale \and You\xspace} % Overwrites above. Does not expant kv
+\writePDFmetadatakv{date=2024-02-01}
+\end{LTXexample}
+
+%\writePDFmetadatakv{datxe=2024-02-01} % would throw error, invalid key
+%\writePDFmetadatakv[E]{keywords=\thekeywords} %
+
+%\luadirect{texio.write_nl(\luastringE{keywords=\thekeywords})}
+
+
+
+
+%%%%% SAND BOX
+%
+%\xtokcycleenvironment\luastringAenv
+% {\addcytoks{##1}}
+% {\processtoks{##1}}
+% {\addcytoks[1]{##1}}
+% {\addcytoks{##1}}
+% {}
+% {\cytoks\expandafter{\expandafter\luastringO\expandafter{\the\cytoks}}}
+%
+%\def\luastringA#1{\luastringAenv#1\endluastringAenv}
+%\def\zzz{Hi Mom}
+%\NewDocumentCommand{\testcommand}{m}{%
+% \luastringA{#1} % this works as requested :)
+%% \luadirect{texio.write_nl(\luastringA{#1}..' <<<')}
+% }
+%
+%\luastringA{Hmmm. \zzz.}
+%\testcommand{Hmmm. \zzz.}
+
+
+
+
+
+
+
+
+% todo why isnt this working???
+
+
+
+%
%\pyth{} % erro
@@ -421,25 +547,27 @@ which pushes the lua variable \cmd{__PDFmetadata__} (a table) to the xmpdata fil
%
% \THINg[color=blue,size=tiny]{Kale}
%
-\begin{luacode*}
-function prttol()
- local dec = penlight.tbls.tol[4] or 1
- penlight.wrth(dec,'??')
- penlight.tbls.tol[3] = penlight.tbls.tol[3] or 3
- penlight.tbls.tol[4] = penlight.tbls.tol[1]*(1.0-penlight.tbls.tol[3]/100.0) + 0.0
- penlight.tbls.tol[5] = penlight.tbls.tol[1]*(1.0+penlight.tbls.tol[3]/100.0) + 0.0
- --penlight.tbls.tol['k'] = 'fuckboi'
- --ttt = pl.tbx.fmt(penlight.tbls.tol, '.3f')
- penlight.wrth(('$1\\$2 (\\pmpct{$3} tolerance, $4\\ndash$5\\$2)'):fmt(penlight.tbls.tol, '4=.'..dec..'f, 5=.'..dec..'f'), 'XYZ')
-end
-\end{luacode*}
-\NewDocumentCommand{\prttol}{ m }{\tblfrcsv{tol}{#1}\luadirect{prttol()}}% {50.0,kV,3,P} % 50\us (\pmpct{20} tolerance, 40=--60\us), P is optional and precision of the range (number of decimals)
-
-\prttol{50,kV,3}
-\begin{luacode*}
- pl.wrth(pl.filterfiles('.',true,'.*%.tex'), 'FF')
-\end{luacode*}
+%
+%\begin{luacode*}
+%function prttol()
+% local dec = penlight.tbls.tol[4] or 1
+% penlight.wrth(dec,'??')
+% penlight.tbls.tol[3] = penlight.tbls.tol[3] or 3
+% penlight.tbls.tol[4] = penlight.tbls.tol[1]*(1.0-penlight.tbls.tol[3]/100.0) + 0.0
+% penlight.tbls.tol[5] = penlight.tbls.tol[1]*(1.0+penlight.tbls.tol[3]/100.0) + 0.0
+% --penlight.tbls.tol['k'] = 'fuckboi'
+% --ttt = pl.tbx.fmt(penlight.tbls.tol, '.3f')
+% penlight.wrth(('$1\\$2 (\\pmpct{$3} tolerance, $4\\ndash$5\\$2)'):fmt(penlight.tbls.tol, '4=.'..dec..'f, 5=.'..dec..'f'), 'XYZ')
+%end
+%\end{luacode*}
+%\NewDocumentCommand{\prttol}{ m }{\tblfrcsv{tol}{#1}\luadirect{prttol()}}% {50.0,kV,3,P} % 50\us (\pmpct{20} tolerance, 40=--60\us), P is optional and precision of the range (number of decimals)
+%
+%\prttol{50,kV,3}
+%
+%\begin{luacode*}
+% pl.wrth(pl.filterfiles('.',true,'.*%.tex'), 'FF')
+%\end{luacode*}
\end{document} \ No newline at end of file