diff options
Diffstat (limited to 'macros/luatex/generic')
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 Binary files differindex 875fac1157..732135e6d8 100644 --- a/macros/luatex/generic/luaaddplot/luaaddplot.pdf +++ b/macros/luatex/generic/luaaddplot/luaaddplot.pdf 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 Binary files differindex 2ab59e561c..7f10f1f50d 100644 --- a/macros/luatex/generic/luakeys/luakeys.pdf +++ b/macros/luatex/generic/luakeys/luakeys.pdf 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 Binary files differindex 5e8e97a778..a6f7e5c362 100644 --- a/macros/luatex/generic/luamplib/luamplib.pdf +++ b/macros/luatex/generic/luamplib/luamplib.pdf diff --git a/macros/luatex/generic/luamplib/test-luamplib-latex.tex b/macros/luatex/generic/luamplib/test-luamplib-latex.tex index 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 Binary files differindex 786e01afa5..1c20815985 100644 --- a/macros/luatex/generic/luatexja/doc/ltjsclasses.pdf +++ b/macros/luatex/generic/luatexja/doc/ltjsclasses.pdf diff --git a/macros/luatex/generic/luatexja/doc/luatexja-en.pdf b/macros/luatex/generic/luatexja/doc/luatexja-en.pdf Binary files differindex de28e1d279..4a64b591fa 100644 --- a/macros/luatex/generic/luatexja/doc/luatexja-en.pdf +++ b/macros/luatex/generic/luatexja/doc/luatexja-en.pdf diff --git a/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf b/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf Binary files differindex 49a8a6feef..981d4ddf93 100644 --- a/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf +++ b/macros/luatex/generic/luatexja/doc/luatexja-ja.pdf diff --git a/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf b/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf Binary files differindex 06cf336b44..0914351b11 100644 --- a/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf +++ b/macros/luatex/generic/luatexja/doc/luatexja-ruby.pdf 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 = { ["`"] = "`" } +-- 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 Binary files differindex f574a0a528..0a254c8935 100644 --- a/macros/luatex/generic/luaxml/luaxml.pdf +++ b/macros/luatex/generic/luaxml/luaxml.pdf 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 Binary files differindex c15b1e5c17..6c228dbe58 100644 --- a/macros/luatex/generic/nodetree/nodetree.pdf +++ b/macros/luatex/generic/nodetree/nodetree.pdf 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 Binary files differindex 3932e74389..928e356b85 100644 --- a/macros/luatex/generic/penlight/penlight.pdf +++ b/macros/luatex/generic/penlight/penlight.pdf 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 Binary files differindex 2e37875101..bb9b8027ff 100644 --- a/macros/luatex/generic/penlightplus/penlightplus.pdf +++ b/macros/luatex/generic/penlightplus/penlightplus.pdf 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 |