diff options
author | Norbert Preining <norbert@preining.info> | 2020-07-01 03:03:30 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2020-07-01 03:03:30 +0000 |
commit | d79a721c345c9a2ced6c0640e88c10cc3378c1f0 (patch) | |
tree | 400d84326bfe896b980146734c9650006b72e550 /macros/luatex/generic | |
parent | 70c32b2936f7dac3a0898faafe7c554c3bedeae9 (diff) |
CTAN sync 202007010303
Diffstat (limited to 'macros/luatex/generic')
-rw-r--r-- | macros/luatex/generic/cloze/README.md | 105 | ||||
-rw-r--r-- | macros/luatex/generic/cloze/cloze.dtx | 907 | ||||
-rw-r--r-- | macros/luatex/generic/cloze/cloze.ins | 57 | ||||
-rw-r--r-- | macros/luatex/generic/cloze/cloze.lua | 1045 | ||||
-rw-r--r-- | macros/luatex/generic/cloze/cloze.pdf | bin | 0 -> 600199 bytes |
5 files changed, 2114 insertions, 0 deletions
diff --git a/macros/luatex/generic/cloze/README.md b/macros/luatex/generic/cloze/README.md new file mode 100644 index 0000000000..d6919d4c8d --- /dev/null +++ b/macros/luatex/generic/cloze/README.md @@ -0,0 +1,105 @@ +# Description + +EN: `cloze` is a LuaLaTeX/LaTeX package to generate cloze. It uses the +capabilities of the modern TeX engine LuaTex. + +DE: `cloze` ist a LuaLaTeX/LaTeX-Paket zum Erstellen von Lückentexten. +Es nutzt die Möglichkeiten der modernen TeX-Engine LuaTeX. + +# License + +Copyright (C) 2015-2020 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 +or (at your option) any later version. The latest version of this +license is in: + + http://www.latex-project.org/lppl.txt + +and version 1.3 or later is part of all distributions of LaTeX +version 2005/12/01 or later. + +# CTAN + +Since July 2015 the cloze package is included in the Comprehensive TeX +Archive Network (CTAN). + +* TeX archive: http://mirror.ctan.org/tex-archive/macros/luatex/latex/cloze +* Package page: https://www.ctan.org/pkg/cloze + +# Repository + +https://github.com/Josef-Friedrich/cloze + +# Installation + +Get source: + + git clone git@github.com:Josef-Friedrich/cloze.git + cd cloze + +Compile: + + make + +or manually: + + luatex cloze.ins + lualatex --shell-escape cloze.dtx + makeindex -s gglo.ist -o cloze.gls cloze.glo + makeindex -s gind.ist -o cloze.ind cloze.idx + lualatex --shell-escape cloze.dtx + +# Development + +First delete the stable version installed by TeX Live. Because the +package `cloze` belongs to the collection `collection-latexextra`, the +option `--force` must be used to delete the package. + + tlmgr remove --force cloze + +## Deploying a new version + +Update the version number in the file `cloze.dtx` on this locations: + +### In the markup for the file `cloze.sty` (approximately at the line number 30) + + %<*package> + [2020/05/20 v1.4 Package to typeset cloze worksheets or cloze tests] + %<*package> + +Add a changes entry (approximately at the line 90): + +```latex +\changes{v1.4}{2020/05/20}{...} +``` + +### In the package documentation `documentation.tex` (approximately at the line number 125) + +```latex +\date{v1.6~from 2020/06/30} +``` + +### In the markup for the file `cloze.lua` (approximately at the line number 1900) + +```lua +if not modules then modules = { } end modules ['cloze'] = { + version = '1.4' +} +``` + +### Update the copyright year: + +``` +sed -i 's/(C) 2015-2020/(C) 2015-2021/g' cloze.ins +sed -i 's/(C) 2015-2020/(C) 2015-2021/g' cloze.dtx +``` + +### Command line tasks: + +``` +git tag v1.4 +make +make ctan +``` diff --git a/macros/luatex/generic/cloze/cloze.dtx b/macros/luatex/generic/cloze/cloze.dtx new file mode 100644 index 0000000000..1c18af683b --- /dev/null +++ b/macros/luatex/generic/cloze/cloze.dtx @@ -0,0 +1,907 @@ +% \iffalse meta-comment +% +% Copyright (C) 2015-2020 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 +% or (at your option) any later version. The latest version of this +% license is in: +% +% http://www.latex-project.org/lppl.txt +% +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Josef Friedrich. +% +% This work consists of the files cloze.dtx and cloze.ins +% and the derived filebase cloze.sty and cloze.lua. +% +% \fi +% +% \iffalse +%<*driver> +\ProvidesFile{cloze.dtx} +%</driver> +%<latex>\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%<latex>\ProvidesPackage{cloze} +%<*latex> + [2020/06/60 v1.6 Package to typeset cloze worksheets or cloze tests] +%</latex> +% \fi +% +% \CheckSum{0} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% +% \changes{v0.1}{2015/06/16}{Converted to DTX file} +% +% \changes{v1.0}{2015/07/08}{Inital release} +% +% \changes{v1.1}{2016/06/13}{Make cloze compatible to LuaTeX version +% 0.95} +% +% \changes{v1.2}{2016/06/23}{The cloze makros are now working in +% tabular, tabbing and picture environments} +% +% \changes{v1.3}{2017/03/13}{Add the new macros \cmd{\clozenol} and +% \cmd{\clozeextend} and the environments \texttt{clozebox} and +% \texttt{clozespace} (This version was not published on CTAN.)} +% +% \changes{v1.4}{2020/05/20}{Add the new macro \cmd{\clozestrike} and +% improve the documentation} +% +% \changes{v1.5}{2020/05/27}{ +% * The Lua part of the package (cloze.lua) is now being developed in a +% separate file. +% * The readme file is now a standalone mardown file and not embedded in +% the dtx file any more. +% * \href{https://github.com/stevedonovan/LDoc}{LDoc} is being used +% to generate +% \href{https://josef-friedrich.github.io/cloze}{source code documentation}. +% * This version fixes two bugs (cloze in display math, line color and +% hide). +% } +% +% \changes{v1.6}{2020/06/30}{ +% * Implement basic plain \TeX{} respectively plain Lua\TeX{} interface. +% * Fix issue: Duplicate line generation on the second line in cloze. +% * Fix issue: width of first line wrong in itemize, mdframed. +% * Fix issue \#4: \texttt{\string\clozenol} not transparent. +% * Fix issue: \texttt{clozebox} not transparent. +% } +% +% \DoNotIndex{\newcommand,\newenvironment,\def,\directlua} +% +% \StopEventually{} +% \pagebreak +% \section{Implementation} +% +% \iffalse +%<*plaintex> +% \fi +% \MacroTopsep = 10pt plus 2pt minus 2pt +% \MacrocodeTopsep = 10pt plus 1.2pt minus 1pt +% +% \makeatletter +% \c@CodelineNo 25 \relax +% \makeatother +% +% \subsection{The file \tt{cloze.tex}} +% +% The cloze package uses following naming conventions: Internal / private +% macros / commands / envirnoments are written in PascalCase, public +% ones are written in lowercase. Earlier versions of this package used +% @ characters for private macros. The lower level / private macros +% are now defined in the plain Lua\TeX{} version of the package and +% used to set cloze text in plain Lua\TeX{}. +% +% \begin{macrocode} +\directlua{ + cloze = require('cloze') +} +% \end{macrocode} +% +% \begin{macrocode} +\newif\ifclozeshow\clozeshowtrue +% \end{macrocode} +% +% \subsubsection{Internal macros} +% +% \begin{macro}{\ClozeSetToGlobal} +% Set the Lua variable |registry.is_global| to |true|. All options +% are then stored in the variable |registry.global_options|. +% \begin{macrocode} +\def\ClozeSetToGlobal{% + \directlua{cloze.set_is_global(true)}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeSetToLocal} +% First unset the variable |registry.local_options|. Now set the Lua +% variable |registry.is_global| to |false|. All options are then stored +% in the variable |registry.local_options|. +% \begin{macrocode} +\def\ClozeSetToLocal{% + \directlua{ + cloze.unset_local_options() + cloze.set_is_global(false) + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeGetOption} +% This macro is used in the documentation to show the default values +% of some options. +% \begin{macrocode} +\def\ClozeGetOption#1{% + \directlua{ + tex.print(cloze.get_value('#1')) + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeColor} +% Convert a color definiton name to a PDF colorstack string, for example +% convert the color name |blue| to the colorstack string +% |0 0 1 rg 0 0 1 RG|. The macro definition |\ClozeColor{blue}| +% builds itself the macro |\color@blue|, which expands to the PDF +% colorstack string. The colorstack string is necessary to generate a +% PDF colorstack whatsit. +% \begin{macrocode} +\def\ClozeColor#1{\csname\string\color@#1\endcsname} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeStartMarker} +% At the begining |\ClozeStartMarker| registers the required Lua +% callbacks. Then it inserts a whatsit marker which marks the begin of a +% gap. +% \begin{macrocode} +\def\ClozeStartMarker#1{% + \strut\directlua{ + cloze.register('#1') + cloze.marker('#1', 'start') + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeStopMarker} +% \cmd{\ClozeStopMarker} inserts a whatsit marker that marks the end +% of gap. +% \begin{macrocode} +\def\ClozeStopMarker#1{% + \strut\directlua{ + cloze.marker('#1', 'stop') + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeMargin} +% \cmd{\ClozeMargin} surrounds a text in a gap with two |kerns|. +% \begin{macrocode} +\def\ClozeMargin#1{% + \directlua{cloze.margin()}% + #1% + \directlua{cloze.margin()}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Public plain \TeX{} macros} +% +% \begin{macro}{\clozesetoption} +% \cmd{\clozesetoption} is a wrapper for the Lua function +% |registry.set_option|. \cmd{\clozesetoption}\marg{key}\marg{value} +% sets a key \meta{key} to the value \meta{value}. +% \begin{macrocode} +\def\clozesetoption#1#2{% + \ClozeSetToGlobal% + \directlua{cloze.set_option('#1', '#2')}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozereset} +% The usage of the command \cmd{\clozereset} is described in detail in +% section \TmpSecRef{sec:command-clozereset}. +% \begin{macrocode} +\def\clozereset{% + \ClozeSetToGlobal% + \directlua{cloze.reset()}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozeshow} +% The usage of the command \cmd{\clozeshow} is described in detail in +% section \TmpSecRef{sec:command-clozeshow}. +% \begin{macrocode} +\def\clozeshow{% + \clozeshowtrue% + \ClozeSetToGlobal% + \clozesetoption{show}{true}% + \clozesetoption{hide}{false}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozehide} +% The usage of the command \cmd{\clozehide} is described in detail in +% section \TmpSecRef{sec:command-clozehide}. +% \begin{macrocode} +\def\clozehide{% + \clozeshowfalse% + \ClozeSetToGlobal% + \clozesetoption{hide}{true}% + \clozesetoption{show}{false}% +} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\clozefont} +% The usage of the command \cmd{\clozefont} is described in detail in +% section \TmpSecRef{sec:command-clozefont}. +% \begin{macrocode} +\def\clozefont{\it} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozesetfont} +% The usage of the command \cmd{\clozesetfont} is described in detail in +% section \TmpSecRef{sec:command-clozesetfont}. +% \begin{macrocode} +\def\clozesetfont#1{% + \def\clozefont{% + #1% + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\cloze} +% This is the plain Lua\TeX-Version of the macro \cmd{\cloze}. +% \begin{macrocode} +\def\cloze#1{% + \ClozeStartMarker{basic}% + {% + \clozefont\relax% + \ClozeMargin{#1}% + }% + \ClozeStopMarker{basic}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozefix} +% This is the plain Lua\TeX-Version of \cmd{\clozefix}. The usage of the +% command \cmd{\clozefix} is described in detail in +% section \TmpSecRef{sec:command-clozefix}. +% \begin{macrocode} +\def\clozefix#1{% + \ClozeStartMarker{fix}% + {% + \clozefont\relax% + \ClozeMargin{#1}% + }% + \ClozeStopMarker{fix}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozenol} +% This is the plain Lua\TeX-Version of the macro \cmd{\clozenol}. The +% usage of the command \cmd{\clozenol} is described in detail in +% section \TmpSecRef{sec:command-clozenol}. +% \begin{macrocode} +\def\clozenol#1{% + \clozesetoption{thickness}{0pt}% + \ClozeStartMarker{basic}% + {% + \clozefont\relax% + \ClozeMargin{#1}% + }% + \ClozeStopMarker{basic}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozeline} +% This is the plain Lua\TeX-Version of the macro \cmd{\clozeline}. The +% usage of the command \cmd{\clozeline} is described in detail in +% section \TmpSecRef{sec:command-clozeline}. +% \begin{macrocode} +\def\clozeline{% + \directlua{cloze.line()}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozelinefil} +% This is the plain Lua\TeX-Version of the macro \cmd{\clozelinefil}. +% The usage of the command \cmd{\clozelinefil} is described in detail in +% section \TmpSecRef{sec:command-clozelinefil}. +% \begin{macrocode} +\def\clozelinefil{% + \strut% + \directlua{cloze.linefil()}% + \strut% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozefil} +% This is the plain Lua\TeX-Version of the macro \cmd{\clozefil}. +% The usage of the command \cmd{\clozefil} is described in detail in +% section \TmpSecRef{sec:command-clozefil}. +% \begin{macrocode} +\def\clozefil#1{% + \cloze{#1}\clozelinefil% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozeparcmd} +% The usage of the macro \cmd{\clozeparcmd} is described in detail in +% section \TmpSecRef{sec:command-clozeparcmd}. +% \begin{macrocode} +\def\clozeparcmd#1\par {% + \par% + \ClozeStartMarker{par}% + \clozefont\relax% + #1% + \ClozeStopMarker{par}% + \par% + \directlua{cloze.unregister('par')}% +} +% \end{macrocode} +% \end{macro} +% +% \iffalse +%</plaintex> +% \fi +% +% \iffalse +%<*latex> +% \fi +% \MacroTopsep = 10pt plus 2pt minus 2pt +% \MacrocodeTopsep = 10pt plus 1.2pt minus 1pt +% +% \makeatletter +% \c@CodelineNo 25 \relax +% \makeatother +% +% \subsection{The file \tt{cloze.sty}} +% +% \begin{macrocode} +\input{cloze.tex} +% \end{macrocode} +% +% This packages are used to build \emph{cloze}: +% +% \subsubsection{Dependencies} +% +% The package \href{https://www.ctan.org/pkg/fontspec}{fontspec} is not +% necessarily required. When using Lua\LaTeX{} it is good form to load +% it. Apart from this the package supplies helpful messages, when you +% compile a Lua\LaTeX{} document with pdf\LaTeX{}. +% \begin{macrocode} +\RequirePackage{fontspec} +% \end{macrocode} +% The package \href{https://www.ctan.org/pkg/luatexbase}{luatexbase} +% allows to register multiple Lua callbacks. +% \begin{macrocode} +\RequirePackage{luatexbase-mcb} +% \end{macrocode} +% The package \href{https://www.ctan.org/pkg/kvoptions}{kvoptions} +% takes the handling of the options. +% \begin{macrocode} +\RequirePackage{kvoptions} +% \end{macrocode} +% The package \href{https://www.ctan.org/pkg/setspace}{setspace} is used +% by the environment |clozespace|. +% \begin{macrocode} +\RequirePackage{setspace} +% \end{macrocode} +% The package \href{https://www.ctan.org/pkg/xcolor}{xcolor} is required +% to colorize the text and the line of a gap. +% \begin{macrocode} +\RequirePackage{xcolor} +% \end{macrocode} +% The package \href{https://www.ctan.org/pkg/xcolor}{xparse} is used by +% the environment |clozebox|. +% \begin{macrocode} +\RequirePackage{xparse} +% \end{macrocode} +% The package \href{https://www.ctan.org/pkg/stackengine}{stackengine} +% is required by the command |\clozestrike{}{}|. +% \begin{macrocode} +\RequirePackage{stackengine} +% \end{macrocode} +% The package \href{https://www.ctan.org/pkg/ulem}{ulem} is required by +% the command |\clozestrike{}{}|. +% \begin{macrocode} +\RequirePackage{ulem} +\normalem +% \end{macrocode} +% +% \begin{macrocode} +\RequirePackage{transparent} +% \end{macrocode} +% +% Load the cloze lua module and put all return values in the variable +% |cloze|. +% +% \begin{macro}{\clozesetoption} +% \cmd{\clozesetoption} is a wrapper for the Lua function +% |registry.set_option|. \cmd{\clozesetoption}\marg{key}\marg{value} +% sets a key \meta{key} to the value \meta{value}. The plain Lua\TeX{} +% version always sets to the global options. The Lua\LaTeX-version +% can set values both to the local and the global options store. +% \begin{macrocode} +\let\clozesetoption=\undefined +\newcommand{\clozesetoption}[2]{% + \directlua{cloze.set_option('#1', '#2')}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeSetLocalOptions} +% This macro is used in all cloze commands to handle the optional +% arguments. First it sets the option storage to local and then it +% commits the options to the package \emph{kvoptions} via the macro +% |\kvsetkeys{CLZ}{}|. +% \begin{macrocode} +\def\ClozeSetLocalOptions#1{% + \ClozeSetToLocal% + \kvsetkeys{CLZ}{#1}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Options} +% +% \emph{cloze} offers key-value pairs to use as options. For processing +% the key-value pairs we use the package +% \href{https://www.ctan.org/pkg/kvoptions}{kvoptions}. +% To make all key-value pairs accessibly to Lua code, we use the +% declaration +% \cmd{\define@key}\marg{CLZ}\marg{option}\oarg[]\marg{\dots}. This +% declaration comes from the package +% \href{https://www.ctan.org/pkg/keyval}{keyval}. +% +% At start all values are declared as global options. At the Lua side +% all values are now stored in the |registry.global_options| table. +% +% \begin{macrocode} +\ClozeSetToGlobal +% \end{macrocode} +% +% We use the abbreviation |CLZ| for \emph{cloze} as family name and +% prefix. +% +% \begin{macrocode} +\SetupKeyvalOptions{ + family=CLZ, + prefix=CLZ@ +} +% \end{macrocode} +% +% \paragraph{\tt{align}} +% +% \noindent Please read the section \TmpSecRef{sec:option-align} how to use +% the option |align|. |align| affects only the command \cmd{\clozefix} +% \TmpSecRef{sec:command-clozefix}. +% +% \begin{macrocode} +\DeclareStringOption{align} +\define@key{CLZ}{align}[]{\clozesetoption{align}{#1}} +% \end{macrocode} +% +% \paragraph{\tt{boxheight}} +% +% \noindent Please read the section \TmpSecRef{sec:command-clozebox} +% how to use the option |boxheight|. |boxheight| affects only the +% environment |clozebox|. +% \TmpSecRef{sec:command-clozefix}. +% +% \begin{macrocode} +\DeclareStringOption{boxheight} +\define@key{CLZ}{boxheight}[]{\clozesetoption{boxheight}{#1}} +% \end{macrocode} +% +% \paragraph{\tt{boxwidth}} +% +% \noindent Please read the section \TmpSecRef{sec:command-clozebox} +% how to use the option |boxwidth|. |boxwidth| affects only the +% environment |clozebox|. +% \TmpSecRef{sec:command-clozefix}. +% +% \begin{macrocode} +\DeclareStringOption{boxwidth} +\define@key{CLZ}{boxwidth}[]{\clozesetoption{boxwidth}{#1}} +% \end{macrocode} +% +% \paragraph{\tt{distance}} +% +% \noindent Please read the section \TmpSecRef{sec:option-distance} how to +% use the option |distance|. +% +% \begin{macrocode} +\DeclareStringOption{distance} +\define@key{CLZ}{distance}[]{\clozesetoption{distance}{#1}} +% \end{macrocode} +% +% \paragraph{\tt{hide}} +% +% \noindent If the option |hide| appears in the commands, |hide| will be +% set to \emph{true} and |show| to \emph{false} on the Lua side. Please +% read the section \TmpSecRef{sec:option-hide} how to use the option +% |hide|. +% +% \begin{macrocode} +\DeclareVoidOption{hide}{% + \clozeshowfalse% + \clozesetoption{hide}{true}% + \clozesetoption{show}{false}% +} +% \end{macrocode} +% +% \paragraph{\tt{linecolor}} +% +% \noindent Please read the section \TmpSecRef{sec:option-linecolor} how to +% use the option |linecolor|. +% +% \begin{macrocode} +\DeclareStringOption{linecolor} +\define@key{CLZ}{linecolor}[]{% + \clozesetoption{linecolor}{\ClozeColor{#1}}% + \clozesetoption{linecolor_name}{#1}% +} +% \end{macrocode} +% +% \paragraph{\tt{margin}} +% +% \noindent Please read the section \TmpSecRef{sec:option-margin} how to +% use the option |margin|. +% +% \begin{macrocode} +\DeclareStringOption{margin} +\define@key{CLZ}{margin}[]{\clozesetoption{margin}{#1}} +% \end{macrocode} +% +% \paragraph{\tt{show}} +% +% \noindent If the option |show| appears in the commands, |show| will be +% set to \emph{true} and |true| to \emph{false} on the Lua side. Please +% read the section \TmpSecRef{sec:option-show} how to use the option +% |show|. +% +% \begin{macrocode} +\DeclareVoidOption{show}{% + \clozeshowtrue% + \clozesetoption{show}{true}% + \clozesetoption{hide}{false}% +} +% \end{macrocode} +% +% \paragraph{\tt{spacing}} +% +% \noindent Please read the section \TmpSecRef{sec:option-spacing} how to +% use the option |spacing|. +% +% \begin{macrocode} +\DeclareStringOption{spacing} +\define@key{CLZ}{spacing}[]{\clozesetoption{spacing}{#1}} +% \end{macrocode} +% +% \paragraph{\tt{textcolor}} +% +% \noindent Please read the section \TmpSecRef{sec:option-textcolor} how to +% use the option |textcolor|. +% +% \begin{macrocode} +\DeclareStringOption{textcolor} +\define@key{CLZ}{textcolor}[]{% + \clozesetoption{textcolor}{\ClozeColor{#1}}% + \clozesetoption{textcolor_name}{#1}% +} +% \end{macrocode} +% +% \paragraph{\tt{thickness}} +% +% \noindent Please read the section \TmpSecRef{sec:option-thickness} how to +% use the option |thickness|. +% +% \begin{macrocode} +\DeclareStringOption{thickness} +\define@key{CLZ}{thickness}[]{\clozesetoption{thickness}{#1}} +% \end{macrocode} +% +% \paragraph{\tt{width}} +% +% \noindent Please read the section \TmpSecRef{sec:option-width} how to use +% the option |width|. |width| affects only the command \cmd{\clozefix} +% \TmpSecRef{sec:command-clozefix}. +% +% \begin{macrocode} +\DeclareStringOption{width} +\define@key{CLZ}{width}[]{\clozesetoption{width}{#1}} +% \end{macrocode} +% +% \begin{macrocode} +\ProcessKeyvalOptions{CLZ} +% \end{macrocode} +% +% \subsubsection{Public macros} +% +% All public macros are prefixed with |\cloze|. +% +% \begin{macro}{\clozeset} +% The usage of the command \cmd{\clozeset} is described in detail in +% section \TmpSecRef{sec:command-clozeset}. +% \begin{macrocode} +\newcommand{\clozeset}[1]{% + \ClozeSetToGlobal% + \kvsetkeys{CLZ}{#1}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozeshow} +% The usage of the command \cmd{\clozeshow} is described in detail in +% section \TmpSecRef{sec:command-clozeshow}. +% \begin{macrocode} +\let\clozeshow=\undefined +\newcommand{\clozeshow}{% + \clozeset{show} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozehide} +% The usage of the command \cmd{\clozehide} is described in detail in +% section \TmpSecRef{sec:command-clozehide}. +% \begin{macrocode} +\let\clozehide=\undefined +\newcommand{\clozehide}{% + \clozeset{hide} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\cloze} +% The usage of the command \cmd{\cloze} is described in detail in +% section \TmpSecRef{sec:command-cloze}. +% \begin{macrocode} +\let\clozeplain=\cloze +\let\cloze=\undefined +\newcommand{\cloze}[2][]{% + \ClozeSetLocalOptions{#1}% + \clozeplain{#2}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozefix} +% The usage of the command \cmd{\clozefix} is described in detail in +% section \TmpSecRef{sec:command-clozefix}. +% \begin{macrocode} +\let\clozefixplain=\clozefix +\let\clozefix=\undefined +\newcommand{\clozefix}[2][]{% + \ClozeSetLocalOptions{#1}% + \clozefixplain{#2}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozenol} +% The usage of the command \cmd{\clozenol} is described in detail in +% section \TmpSecRef{sec:command-clozenol}. +% \begin{macrocode} +\let\clozenolplain=\clozenol +\let\clozenol=\undefined +\newcommand{\clozenol}[2][]{% + \ClozeSetLocalOptions{#1}% + \clozenolplain{#2}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{environment}{clozepar} +% The usage of the environment |clozepar| is described in detail in +% section \TmpSecRef{sec:command-clozepar}. +% \begin{macrocode} +\newenvironment{clozepar}[1][]% +{% + \par% + \ClozeSetLocalOptions{#1}% + \ClozeStartMarker{par}% + \clozefont\relax% +}% +{% + \ClozeStopMarker{par}% + \par% + \directlua{cloze.unregister('par')}% +} +% \end{macrocode} +% \end{environment} +% +% \begin{environment}{clozebox} +% The usage of the environment |clozebox| is described in detail in +% section \TmpSecRef{sec:command-clozebox}. +% TODO: Realize this macro with lua code, without ugly |\color{white}| +% command. +% \begin{macrocode} +\newsavebox{\ClozeBox}% +\NewDocumentEnvironment{clozebox}{ s O{} +b}{% + \ClozeSetLocalOptions{#2}% + \noindent% + \begin{lrbox}{\ClozeBox}% + \directlua{ + local boxheight = cloze.get_value('boxheight') + local boxwidth = cloze.get_value('boxwidth') + if boxheight then + tex.print('\\begin{minipage}[t][' .. boxheight .. '][t]{' .. boxwidth .. '}') + else + tex.print('\\begin{minipage}[t]{' .. boxwidth .. '}') + end + }% + \setlength{\parindent}{0pt}% + \clozenol[margin=0pt]{#3}% + \end{minipage}% + \end{lrbox}% + \IfBooleanTF{#1}% + {\usebox{\ClozeBox}}% + {\fbox{\usebox{\ClozeBox}}}% +}{} +% \end{macrocode} +% \end{environment} +% +% \begin{environment}{clozespace} +% The usage of the environment |clozespace| is described in detail in +% section \TmpSecRef{sec:command-clozespace}. +% TODO: Realization without setspace package. +% \begin{macrocode} +\newenvironment{clozespace}[1][]% +{% + \ClozeSetLocalOptions{#1}% + \begin{spacing}{\directlua{tex.print(cloze.get_value('spacing'))}}% +}{\end{spacing}} +% \end{macrocode} +% \end{environment} +% +% \begin{macro}{\clozeline} +% The usage of the command \cmd{\clozeline} is described in detail in +% section \TmpSecRef{sec:command-clozeline}. +% \begin{macrocode} +\let\clozelineplain=\clozeline +\let\clozeline=\undefined +\newcommand{\clozeline}[1][]{% + \ClozeSetLocalOptions{#1}% + \clozelineplain% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozelinefil} +% The usage of the command \cmd{\clozelinefil} is described in detail in +% section \TmpSecRef{sec:command-clozelinefil}. +% \begin{macrocode} +\let\clozelinefilplain=\clozelinefil +\let\clozelinefil=\undefined +\newcommand{\clozelinefil}[1][]{% + \ClozeSetLocalOptions{#1}% + \clozelinefilplain% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozefil} +% The usage of the command \cmd{\clozefil} is described in detail in +% section \TmpSecRef{sec:command-clozefil}. +% \begin{macrocode} +\let\clozefil=\undefined +\newcommand{\clozefil}[2][]{% + \cloze[#1]{#2}\clozelinefil[#1]% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozeextend} +% TODO: Use node library to create kern nodes. +% \begin{macrocode} +\newcommand{\clozeextend}[1][1]{% + \directlua{ + local loop = #1 + for variable = 1, loop do + tex.print(' \string\\hspace{1em} \string\\strut') + end + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeTextColor} +% \begin{macrocode} +\newcommand{\ClozeTextColor}[1]{% + \textcolor% + {\directlua{tex.print(cloze.get_value('textcolor_name'))}}% + {#1}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ClozeStrikeLine} +% \begin{macrocode} +\newcommand\ClozeStrikeLine{% + \bgroup% + \markoverwith{% + \ClozeTextColor{% + \rule[0.5ex]{2pt}{1pt}% + }% + }% + \ULon% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clozestrike} +% \begin{macrocode} +\newcommand{\clozestrike}[3][]{% + \ClozeSetLocalOptions{#1}% + \ifclozeshow% + \stackengine% + {\Sstackgap}% \Sstackgap or \Lstackgap or \stackgap or stacklength + {\ClozeStrikeLine{#2}}% anchor + {\ClozeTextColor{\clozefont{}#3}}% item + {O}% O or U + {c}% \stackalignment or l or c or r + {\quietstack}% \quietstack or T or F + {T}% \useanchorwidth or T or F + {\stacktype}% \stacktype or S or L + \else% + \stackengine% + {\Sstackgap}% \Sstackgap or \Lstackgap or \stackgap or stacklength + {#2}% anchor + {\texttransparent{0}{\clozefont{}#3}}% item + {O}% O or U + {c}% \stackalignment or l or c or r + {\quietstack}% \quietstack or T or F + {T}% \useanchorwidth or T or F + {\stacktype}% \stacktype or S or L + \fi% +} +% \end{macrocode} +% \end{macro} +% +% \iffalse +%</latex> +% \fi +% +% \Finale +\endinput diff --git a/macros/luatex/generic/cloze/cloze.ins b/macros/luatex/generic/cloze/cloze.ins new file mode 100644 index 0000000000..9ed14f6199 --- /dev/null +++ b/macros/luatex/generic/cloze/cloze.ins @@ -0,0 +1,57 @@ +% Copyright (C) 2015-2020 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 +% or (at your option) any later version. The latest version of this +% license is in: +% +% http://www.latex-project.org/lppl.txt +% +% and version 1.3c or later is part of all distributions of LaTeX +% version 2008/05/05 or later. + + +\input docstrip.tex +\keepsilent + +\usedir{tex/lualatex/cloze} + +\askforoverwritefalse + +\preamble + +This is a generated file. + +Copyright (C) 2015-2020 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 +or (at your option) any later version. The latest version of this +license is in: + + http://www.latex-project.org/lppl.txt + +and version 1.3c or later is part of all distributions of LaTeX +version 2008/05/05 or later. + +\endpreamble + +\generate{\file{cloze.tex}{\from{cloze.dtx}{plaintex}}} +\generate{\file{cloze.sty}{\from{cloze.dtx}{latex}}} + +\obeyspaces +\Msg{*************************************************************} +\Msg{* *} +\Msg{* To finish the installation you have to move the following *} +\Msg{* file into a directory searched by TeX: *} +\Msg{* *} +\Msg{* cloze.sty *} +\Msg{* *} +\Msg{* To produce the documentation run the file cloze.dtx *} +\Msg{* through LaTeX. *} +\Msg{* *} +\Msg{* Happy TeXing! *} +\Msg{* *} +\Msg{*************************************************************} + +\endbatchfile diff --git a/macros/luatex/generic/cloze/cloze.lua b/macros/luatex/generic/cloze/cloze.lua new file mode 100644 index 0000000000..1563155a1b --- /dev/null +++ b/macros/luatex/generic/cloze/cloze.lua @@ -0,0 +1,1045 @@ +--- Cloze uses [LDoc](https://github.com/stevedonovan/ldoc) for the +-- source code documentation. The supported tags are described on in +-- the [wiki](https://github.com/stevedonovan/LDoc/wiki). +-- +-- <h3>Naming conventions</h3> +-- +-- * _Variable_ names for _nodes_ are suffixed with `_node`, for example +-- `head_node`. +-- * _Variable_ names for _lengths_ (dimensions) are suffixed with +-- `_length`, for example `width`. +-- +-- @module cloze + +-- luacheck: globals node tex modules luatexbase callback + +-- __cloze.lua__ + +-- __Initialisation of the function tables__ + +-- It is good form to provide some background informations about this Lua +-- module. +if not modules then modules = { } end modules ['cloze'] = { + version = '1.6', + comment = 'cloze', + author = 'Josef Friedrich, R.-M. Huber', + copyright = 'Josef Friedrich, R.-M. Huber', + license = 'The LaTeX Project Public License Version 1.3c 2008-05-04' +} + +--- `nodex` is a abbreviation for __node eXtended__. +local nodex = {} + +--- All values and functions, which are related to the option management, +-- are stored in a table called `registry`. +local registry = {} + +--- I didn't know what value I should take as `user_id`. Therefore I +-- took my birthday and transformed it to a large number. +registry.user_id = 3121978 +registry.storage = {} +registry.defaults = { + ['align'] = 'l', + ['boxheight'] = false, + ['boxwidth'] = '\\linewidth', + ['distance'] = '1.5pt', + ['hide'] = false, + ['linecolor'] = '0 0 0 rg 0 0 0 RG', -- black + ['linecolor_name'] = 'black', + ['margin'] = '3pt', + ['resetcolor'] = '0 0 0 rg 0 0 0 RG', -- black + ['resetcolor_name'] = 'black', + ['show_text'] = true, + ['show'] = true, + ['spacing'] = '1.6', + ['textcolor'] = '0 0 1 rg 0 0 1 RG', -- blue + ['textcolor_name'] = 'blue', -- blue + ['thickness'] = '0.4pt', + ['width'] = '2cm', +} +registry.global_options = {} +registry.local_options = {} + +-- The `base` table contains some basic functions. `base` is the only +-- table of this Lua module that will be exported. +local base = {} +base.is_registered = {} + +--- Node precessing (nodex) +-- @section nodex + +-- All functions in this section are stored in a table called `nodex`. +-- `nodex` is a abbreviation for __node eXtended__. The `nodex` table +-- bundles all functions, which extend the built-in `node` library. + +-- __Color handling (color)__ + +-- __create_colorstack__ +-- Create a whatsit node of the subtype `pdf_colorstack`. `data` is a PDF +-- colorstack string like `0 0 0 rg 0 0 0 RG`. +function nodex.create_colorstack(data) + if not data then + data = '0 0 0 rg 0 0 0 RG' -- black + end + local whatsit = node.new('whatsit', 'pdf_colorstack') + whatsit.stack = 0 + whatsit.data = data + return whatsit +end + +--- +-- `nodex.create_color()` is a wrapper for the function +-- `nodex.create_colorstack()`. It queries the current values of the +-- options `linecolor` and `textcolor`. The argument `option` accepts the +-- strings `line`, `text` and `reset`. +function nodex.create_color(option) + local data + if option == 'line' then + data = registry.get_value('linecolor') + elseif option == 'text' then + data = registry.get_value('textcolor') + elseif option == 'reset' then + data = nil + else + data = nil + end + return nodex.create_colorstack(data) +end + +-- __Line handling (line)__ + +--- Create a rule node, which is used as a line for the cloze texts. The +-- `depth` and the `height` of the rule are calculated form the options +-- `thickness` and `distance`. The argument `width` must have the length +-- unit __scaled points__. +function nodex.create_line(width) + local rule = node.new(node.id('rule')) + local thickness = tex.sp(registry.get_value('thickness')) + local distance = tex.sp(registry.get_value('distance')) + rule.depth = distance + thickness + rule.height = - distance + rule.width = width + return rule +end + +--- Insert a `list` of nodes after or before the `current`. The `head` +-- argument is optional. In some edge cases it is unfortately necessary. +-- if `head` is omitted the `current` node is used. The argument +-- `position` can take the values `'after'` or `'before'`. +function nodex.insert_list(position, current, list, head_node) + if not head_node then + head_node = current + end + for _, insert in ipairs(list) do + if position == 'after' then + head_node, current = node.insert_after(head_node, current, insert) + elseif position == 'before' then + head_node, current = node.insert_before(head_node, current, insert) + end + end + return current +end + +--- Enclose a rule node (cloze line) with two PDF colorstack whatsits. +-- The first colorstack node dyes the line, the seccond resets the +-- color. +-- +-- __Node list__ +-- +-- <table> +-- <thead> +-- <tr> +-- <th>`color_line_node`</th> +-- <th>`whatsit`</th> +-- <th>`pdf_colorstack`</th> +-- <th>Line color</th> +-- </tr> +-- </thead> +-- <tbody> +-- <tr> +-- <td>`line_node`</td> +-- <td>`rule`</td> +-- <td></td> +-- <td>`width`</td> +-- </tr> +-- <tr> +-- <td>`color_reset_node`</td> +-- <td>`whatsit`</td> +-- <td>`pdf_colorstack`</td> +-- <td>Reset color</td> +-- </tr> +-- </tbody> +-- </table> +function nodex.insert_line(current, width) + return nodex.insert_list( + 'after', + current, + { + nodex.create_color('line'), + nodex.create_line(width), + nodex.create_color('reset') + } + ) +end + +--- This function enclozes a rule node with color nodes as it the function +-- `nodex.insert_line` does. In contrast to `nodex.insert_line` the three +-- nodes are appended to \TeX’s ‘current list’. They are not inserted in +-- a node list, which is accessed by a Lua callback. +-- +-- __Node list__ +-- +-- <table> +-- <thead> +-- <tr> +-- <th>-</th> +-- <th>`whatsit`</th> +-- <th>`pdf_colorstack`</th> +-- <th>Line color</th> +-- </tr> +-- </thead> +-- <tbody> +-- <tr> +-- <td>-</td> +-- <td>`rule`</td> +-- <td></td> +-- <td>`width`</td> +-- </tr> +-- <tr> +-- <td>-</td> +-- <td>`whatsit`</td> +-- <td>`pdf_colorstack`</td> +-- <td>Reset color</td> +-- </tr> +-- </tbody> +-- </table> +function nodex.write_line() + node.write(nodex.create_color('line')) + node.write(nodex.create_line(tex.sp(registry.get_value('width')))) + node.write(nodex.create_color('reset')) +end + +-- __Handling of extendable lines (linefil)__ + +--- This function creates a line which stretchs indefinitely in the +-- horizontal direction. +function nodex.create_linefil() + local glue = node.new('glue') + glue.subtype = 100 + glue.stretch = 65536 + glue.stretch_order = 3 + local rule = nodex.create_line(0) + rule.dir = 'TLT' + glue.leader = rule + return glue +end + +--- The function `nodex.write_linefil` surrounds a indefinitely strechable +-- line with color whatsits and puts it to \TeX’s ‘current (node) list’. +function nodex.write_linefil() + node.write(nodex.create_color('line')) + node.write(nodex.create_linefil()) + node.write(nodex.create_color('reset')) +end + +-- __Kern handling (kern)__ + +--- This function creates a kern node with a given width. The argument +-- `width` had to be specified in scaled points. +local function create_kern_node(width) + local kern_node = node.new(node.id('kern')) + kern_node.kern = width + return kern_node +end + +--- Add at the beginning of each `hlist` node list a strut (a invisible +-- character). +-- +-- Now we can add line, color etc. nodes after the first node of a hlist +-- not before - after is much more easier. +-- +-- @tparam node hlist_node +-- +-- @treturn node hlist_node +-- @treturn node strut_node +-- @treturn node prev_head_node +local function insert_strut_into_hlist(hlist_node) + local prev_head_node = hlist_node.head + local kern_node = create_kern_node(0) + local strut_node = node.insert_before(hlist_node.head, prev_head_node, kern_node) + hlist_node.head = prev_head_node.prev + return hlist_node, strut_node, prev_head_node +end + +--- Write kern nodes to the current node list. This kern nodes can be used +-- to build a margin. +function nodex.write_margin() + local kern = create_kern_node(tex.sp(registry.get_value('margin'))) + node.write(kern) +end + +--- Search for a `hlist` (subtype `line`) and nsert a strut node into +-- the list if a hlist is found. +-- +-- @tparam node head_node The head of a node list. +-- +-- @treturn node hlist_node +-- @treturn node strut_node +-- @treturn node prev_head_node +local function search_hlist(head_node) + while head_node do + if head_node.id == node.id('hlist') and head_node.subtype == 1 then + return insert_strut_into_hlist(head_node) + end + head_node = head_node.next + end +end + +--- Option handling. +-- +-- The table `registry` bundels functions that deal with option handling. +-- +-- <h2>Marker processing (marker)</h2> +-- +-- A marker is a whatsit node of the subtype `user_defined`. A marker has +-- two purposes: +-- +-- * Mark the begin and the end of a gap. +-- * Store a index number, that points to a Lua table, which holds +-- some additional data like the local options. +-- @section registry + +--- We create a user defined whatsit node that can store a number (type +-- = 100). +-- +-- In order to distinguish this node from other user defined whatsit +-- nodes we set the `user_id` to a large number. We call this whatsit +-- node a marker. The argument `index` is a number, which is associated +-- to values in the `registry.storage` table. +function registry.create_marker(index) + local marker = node.new('whatsit','user_defined') + marker.type = 100 -- number + marker.user_id = registry.user_id + marker.value = index + return marker +end + +--- Write a marker node to TeX's current node list. +-- +-- The argument `mode` accepts the string values `basic`, `fix` and +-- `par`. The argument `position`. The argument `position` is either set +-- to `start` or to `stop`. +function registry.write_marker(mode, position) + local index = registry.set_storage(mode, position) + local marker = registry.create_marker(index) + node.write(marker) +end + +--- This functions checks if the given node `item` is a marker. +function registry.is_marker(item) + if item.id == node.id('whatsit') + and item.subtype == node.subtype('user_defined') + and item.user_id == registry.user_id then + return true + else + return false + end +end + +--- This functions tests, whether the given node `item` is a marker. +-- +-- The argument `item` is a node. The argument `mode` accepts the string +-- values `basic`, `fix` and `par`. The argument `position` is either +-- set to `start` or to `stop`. +function registry.check_marker(item, mode, position) + local data = registry.get_marker_data(item) + if data and data.mode == mode and data.position == position then + return true + else + return false + end +end + +--- `registry.get_marker` returns the given marker. +-- +-- The argument `item` is a node. The argument `mode` accepts the string +-- values `basic`, `fix` and `par`. The argument `position` is either +-- set to `start` or to `stop`. +function registry.get_marker(item, mode, position) + local out + if registry.check_marker(item, mode, position) then + out = item + else + out = false + end + if out and position == 'start' then + registry.get_marker_values(item) + end + return out +end + +--- `registry.get_marker_data` tests whether the node `item` is a +-- marker. +-- +-- The argument `item` is a node of unspecified type. +function registry.get_marker_data(item) + if item.id == node.id('whatsit') + and item.subtype == node.subtype('user_defined') + and item.user_id == registry.user_id then + return registry.get_storage(item.value) + else + return false + end +end + +--- First this function saves the associatied values of a marker to the +-- local options table. Second it returns this values. The argument +-- `marker` is a whatsit node. +function registry.get_marker_values(marker) + local data = registry.get_marker_data(marker) + registry.local_options = data.values + return data.values +end + +--- This function removes a given whatsit marker. +-- +-- It only deletes a node, if a marker is given. +-- +-- @treturn node head +-- @treturn node current +function registry.remove_marker(marker) + if registry.is_marker(marker) then + return node.remove(marker, marker) + end +end + +-- __Storage functions (storage)__ + +--- `registry.index` is a counter. The functions `registry.get_index()` +-- increases the counter by one and then returns it. +function registry.get_index() + if not registry.index then + registry.index = 0 + end + registry.index = registry.index + 1 + return registry.index +end + +--- `registry.set_storage()` stores the local options in the Lua table +-- `registry.storage`. +-- +-- It returns a numeric index number. This index number is the key, +-- where the local options in the Lua table are stored. The argument +-- `mode` accepts the string values `basic`, `fix` and `par`. +function registry.set_storage(mode, position) + local index = registry.get_index() + local data = { + ['mode'] = mode, + ['position'] = position + } + data.values = registry.local_options + registry.storage[index] = data + return index +end + +--- The function `registry.get_storage()` retrieves values which belong +-- to a whatsit marker. +-- +-- The argument `index` is a numeric value. +function registry.get_storage(index) + return registry.storage[index] +end + +-- __Option processing (option)__ + +--- This function stores a value `value` and his associated key `key` +-- either to the global (`registry.global_options`) or to the local +-- (`registry.local_options`) option table. +-- +-- The global boolean variable `registry.local_options` controls in +-- which table the values are stored. +function registry.set_option(key, value) + if value == '' or value == '\\color@ ' then + return false + end + if registry.is_global == true then + registry.global_options[key] = value + else + registry.local_options[key] = value + end +end + +--- `registry.set_is_global()` sets the variable `registry.is_global` to +-- the value `value`. It is intended, that the variable takes boolean +-- values. +function registry.set_is_global(value) + registry.is_global = value +end + +--- This function unsets the local options. +function registry.unset_local_options() + registry.local_options = {} +end + +--- `registry.unset_global_options` empties the global options storage. +function registry.unset_global_options() + registry.global_options = {} +end + +--- Retrieve a value from a given key. First search for the value in the +-- local options, then in the global options. If both option storages are +-- empty, the default value will be returned. +function registry.get_value(key) + if registry.has_value(registry.local_options[key]) then + return registry.local_options[key] + end + if registry.has_value(registry.global_options[key]) then + return registry.global_options[key] + end + return registry.defaults[key] +end + + +--- The function `registry.get_value_show()` returns the boolean value +-- `true` if the option `show` is true. In contrast to the function +-- `registry.get_value()` it converts the string value `true' to a +-- boolean value. +function registry.get_value_show() + if + registry.get_value('show') == true + or + registry.get_value('show') == 'true' + then + return true + else + return false + end +end + +--- This function tests whether the value `value` is not empty and has a +-- value. +function registry.has_value(value) + if value == nil or value == '' or value == '\\color@ ' then + return false + else + return true + end +end + +--- `registry.get_defaults(option)` returns a the default value of the +-- given option. +function registry.get_defaults(option) + return registry.defaults[option] +end + +--- Assembly to cloze texts. +-- @section cloze_functions + +--- Assemble a possibly muliline cloze. +-- +-- The corresponding LaTeX command to this Lua function is `\cloze`. +-- This function is used by other cloze TeX macros too: `\clozenol`, +-- `\clozefil` +-- +-- @tparam node head_node_input The head of a node list. +-- +-- @treturn node The head of the node list. +local function make_basic(head_node_input) + -- This local variables are overloaded by function who + -- call each other. + local continue_cloze, search_stop + + --- The function `make_single()` makes one gap. The argument + -- `start_node` is the node where the gap begins. The argument + -- `stop_node` is the node where the gap ends. + -- + -- @tparam node start_node The node to start / begin a new cloze. + -- @tparam node stop_node The node to stop / end a new cloze. + -- @tparam node parent_node The parent node (hlist) of the start and + -- the stop node. + -- + -- @treturn node stop_node The stop node. + -- @treturn parent_node The parent node (hlist) of the stop node. + local function make_single(start_node, stop_node, parent_node) + local node_head = start_node + local line_width = node.dimensions( + parent_node.glue_set, + parent_node.glue_sign, + parent_node.glue_order, + start_node, + stop_node + ) + local line_node = nodex.insert_line(start_node, line_width) + local color_text_node = nodex.insert_list('after', line_node, {nodex.create_color('text')}) + if registry.get_value_show() then + nodex.insert_list('after', color_text_node, {create_kern_node(-line_width)}) + nodex.insert_list('before', stop_node, {nodex.create_color('reset')}, node_head) + else + line_node.next = stop_node.next + stop_node.prev = line_node -- not line_node.prev -> line color leaks out + end + -- In some edge cases the lua callbacks get fired up twice. After the + -- cloze has been created, the start and stop whatsit markers can be + -- deleted. + registry.remove_marker(start_node) + return registry.remove_marker(stop_node), parent_node + end + + --- Search for a stop marker or make a cloze up to the end of the node + -- list. + -- + -- @tparam node start_node The node to start a new cloze. + -- @tparam node parent_node The parent node (hlist) of the start node. + -- + -- @treturn head_node The fast forwarded new head of the node list. + -- @treturn parent_node The parent node (hlist) of the head node. + function search_stop(start_node, parent_node) + local head_node = start_node + local last_node + while head_node do + if registry.check_marker(head_node, 'basic', 'stop') then + return make_single(start_node, head_node, parent_node) + end + last_node = head_node + head_node = head_node.next + end + -- Make a cloze until the end of the node list. + head_node = make_single(start_node, last_node, parent_node) + if parent_node.next then + return continue_cloze(parent_node.next) + else + return head_node, parent_node + end + end + + --- Continue a multiline cloze. + -- + -- @tparam node parent_node A parent node to search for a hlist node. + -- + -- @treturn head_node The fast forwarded new head of the node list. + -- @treturn parent_node The parent node (hlist) of the head node. + function continue_cloze(parent_node) + local hlist_node = search_hlist(parent_node) + if hlist_node then + local start_node = hlist_node.head + return search_stop(start_node, hlist_node) + end + end + + --- Search for a start marker. + -- + -- @tparam node head_node The head of a node list. + -- @tparam node parent_node The parent node (hlist) of the head node. + local function search_start(head_node, parent_node) + while head_node do + if head_node.head then + search_start(head_node.head, head_node) + elseif registry.check_marker(head_node, 'basic', 'start') and + parent_node and + parent_node.id == node.id('hlist') then + -- Adds also a strut at the first position. It prepars the + -- hlist and makes it ready to build a cloze. + search_hlist(parent_node) + head_node, parent_node = search_stop(head_node, parent_node) + end + if head_node then + head_node = head_node.next + else + break + end + end + end + + search_start(head_node_input) + return head_node_input +end + +--- The corresponding LaTeX command to this Lua function is `\clozefix`. +-- +-- @tparam node head_node_input The head of a node list. +local function make_fix(head_node_input) + + --- Calculate the length of the whitespace before (`kern_start_length`) and + -- after (`kern_stop_length`) the text. + local function calculate_length(start, stop) + local width, kern_start_length, kern_stop_length, text_width, half_length, align + width = tex.sp(registry.get_value('width')) + text_width = node.dimensions(start, stop) + align = registry.get_value('align') + if align == 'right' then + kern_start_length = - text_width + kern_stop_length = 0 + elseif align == 'center' then + half_length = (width - text_width) / 2 + kern_start_length = - half_length - text_width + kern_stop_length = half_length + else + kern_start_length = - width + kern_stop_length = width - text_width + end + return width, kern_start_length, kern_stop_length + end + + --- The function `make_single` generates a gap of fixed width. + -- + -- __Node lists__ + -- + -- __Show text:__ + -- + -- <table> + -- <tbody> + -- <tr> + -- <td>`start_node`</td> + -- <td>`whatsit`</td> + -- <td>`user_definded`</td> + -- <td>`index`</td> + -- </tr> + -- <tr> + -- <td>`line_node`</td> + -- <td>`rule`</td> + -- <td></td> + -- <td>`width`</td> + -- </tr> + -- <tr> + -- <td>`kern_start_node`</td> + -- <td>`kern`</td> + -- <td>& Depends on `align`</td> + -- <td></td> + -- </tr> + -- <tr> + -- <td>`color_text_node`</td> + -- <td>`whatsit`</td> + -- <td>`pdf_colorstack`</td> + -- <td>Text color</td> + -- </tr> + -- <tr> + -- <td></td> + -- <td>`glyphs`</td> + -- <td>& Text to show</td> + -- <td></td> + -- </tr> + -- <tr> + -- <td>`color_reset_node`</td> + -- <td>`whatsit`</td> + -- <td>`pdf_colorstack`</td> + -- <td>Reset color</td> + -- </tr> + -- <tr> + -- <td>`kern_stop_node`</td> + -- <td>`kern`</td> + -- <td>& Depends on `align`</td> + -- <td></td> + -- </tr> + -- <tr> + -- <td>`stop_node`</td> + -- <td>`whatsit`</td> + -- <td>`user_definded`</td> + -- <td>`index`</td> + -- </tr> + -- </tbody> + -- </table> + -- + -- __Hide text:__ + -- + -- <table> + -- <thead> + -- <tr> + -- <th>`start_node`</th> + -- <th>`whatsit`</th> + -- <th>`user_definded`</th> + -- <th>`index`</th> + -- </tr> + -- </thead> + -- <tbody> + -- <tr> + -- <td>`line_node`</td> + -- <td>`rule`</td> + -- <td></td> + -- <td>`width`</td> + -- </tr> + -- <tr> + -- <td>`stop_node`</td> + -- <td>`whatsit`</td> + -- <td>`user_definded`</td> + -- <td>`index`</td> + -- </tr> + -- </tbody> + -- </table> + -- + -- Make fixed size cloze. + -- + -- @param start The node, where the gap begins + -- @param stop The node, where the gap ends + local function make_single(start, stop) + local width, kern_start_length, kern_stop_length, line_node + width, kern_start_length, kern_stop_length = calculate_length(start, stop) + line_node = nodex.insert_line(start, width) + if registry.get_value_show() then + nodex.insert_list( + 'after', + line_node, + { + create_kern_node(kern_start_length), + nodex.create_color('text') + } + ) + nodex.insert_list( + 'before', + stop, + { + nodex.create_color('reset'), + create_kern_node(kern_stop_length) + }, + start + ) + else + line_node.next = stop.next + end + registry.remove_marker(start) + registry.remove_marker(stop) + end + + --- Function to recurse the node list and search after marker. + -- + -- @tparam node head_node The head of a node list. + local function make_fix_recursion(head_node) + local start_node, stop_node = false, false + while head_node do + if head_node.head then + make_fix_recursion(head_node.head) + else + if not start_node then + start_node = registry.get_marker(head_node, 'fix', 'start') + end + if not stop_node then + stop_node = registry.get_marker(head_node, 'fix', 'stop') + end + if start_node and stop_node then + make_single(start_node, stop_node) + start_node, stop_node = false, false + end + end + head_node = head_node.next + end + end + + make_fix_recursion(head_node_input) + return head_node_input +end + +--- The corresponding LaTeX environment to this lua function is +-- `clozepar`. +-- +-- __Node lists__ +-- +-- __Show text:__ +-- +-- <table> +-- <thead> +-- <tr> +-- <th>`strut_node`</th> +-- <th>`kern`</th> +-- <th></th> +-- <th>width = 0</th> +-- </tr> +-- </thead> +-- <tbody> +-- <tr> +-- <td>`line_node`</td> +-- <td>`rule`</td> +-- <td></td> +-- <td>`width` (Width from hlist)</td> +-- </tr> +-- <tr> +-- <td>`kern_node`</td> +-- <td>`kern`</td> +-- <td></td> +-- <td>`-width`</td> +-- </tr> +-- <tr> +-- <td>`color_text_node`</td> +-- <td>`whatsit`</td> +-- <td>`pdf_colorstack`</td> +-- <td>Text color</td> +-- </tr> +-- <tr> +-- <td></td> +-- <td>`glyphs`</td> +-- <td></td> +-- <td>Text to show</td> +-- </tr> +-- <tr> +-- <td>`tail_node`</td> +-- <td>`glyph`</td> +-- <td></td> +-- <td>Last glyph in hlist</td> +-- </tr> +-- <tr> +-- <td>`color_reset_node`</td> +-- <td>`whatsit`</td> +-- <td>`pdf_colorstack`</td> +-- <td>Reset color</td> +-- </tr> +-- </tbody> +-- </table> +-- +-- __Hide text:__ +-- +-- <table> +-- <thead> +-- <tr> +-- <th>`strut_node`</th> +-- <th>`kern`</th> +-- <th></th> +-- <th>width = 0</th> +-- </tr> +-- </thead> +-- <tbody> +-- <tr> +-- <td>`line_node`</td> +-- <td>`rule`</td> +-- <td></td> +-- <td>`width` (Width from hlist)</td> +-- </tr> +-- </tbody> +-- </table> +-- +-- @tparam node head_node The head of a node list. +local function make_par(head_node) + local strut_node, line_node, width + for hlist_node in node.traverse_id(node.id('hlist'), head_node) do + for whatsit in node.traverse_id(node.id('whatsit'), hlist_node.head) do + registry.get_marker(whatsit, 'par', 'start') + end + width = hlist_node.width + hlist_node, strut_node, _ = insert_strut_into_hlist(hlist_node) + line_node = nodex.insert_line(strut_node, width) + if registry.get_value_show() then + nodex.insert_list( + 'after', + line_node, + { + create_kern_node(-width), + nodex.create_color('text') + } + ) + nodex.insert_list( + 'after', + node.tail(head_node), + {nodex.create_color('reset')} + ) + else + line_node.next = nil + end + end + return head_node +end + +--- +-- @tparam string callback_name The name of a callback +-- @tparam function func A function to register for the callback +-- @tparam string description Only used in LuaLatex +local function register_callback(callback_name, func, description) + if luatexbase then + luatexbase.add_to_callback( + callback_name, + func, + description + ) + else + callback.register(callback_name, func) + end +end + +--- +-- @tparam string callback_name The name of a callback +-- @tparam string description Only used in LuaLatex +local function unregister_callback(callback_name, description) + if luatexbase then + luatexbase.remove_from_callback( + callback_name, + description + ) + else + callback.register(callback_name, nil) + end +end + +--- Basic module functions. +-- The `base` table contains functions which are published to the +-- `cloze.sty` file. +-- @section base + +--- This function registers the functions `make_par`, `make_basic` and +-- `make_fix` the Lua callbacks. +-- +-- `make_par` and `make_basic` are registered to the callback +-- `post_linebreak_filter` and `make_fix` to the callback +-- `pre_linebreak_filter`. The argument `mode` accepts the string values +-- `basic`, `fix` and `par`. A special treatment is needed for clozes in +-- display math mode. The `post_linebreak_filter` is not called on +-- display math formulas. I’m not sure if the `pre_output_filter` is the +-- right choice to capture the display math formulas. +function base.register(mode) + if mode == 'par' then + register_callback( + 'post_linebreak_filter', + make_par, + mode + ) + return true + end + if not base.is_registered[mode] then + if mode == 'basic' then + register_callback( + 'post_linebreak_filter', + make_basic, + mode + ) + register_callback( + 'pre_output_filter', + make_basic, + mode + ) + elseif mode == 'fix' then + register_callback( + 'pre_linebreak_filter', + make_fix, + mode + ) + else + return false + end + base.is_registered[mode] = true + end +end + +--- `base.unregister(mode)` deletes the registered functions from the +-- Lua callbacks. +-- +-- @tparam string mode The argument `mode` accepts the string values +-- `basic`, `fix` and `par`. +function base.unregister(mode) + if mode == 'basic' then + unregister_callback('post_linebreak_filter', mode) + unregister_callback('pre_output_filter', mode) + elseif mode == 'fix' then + unregister_callback('pre_linebreak_filter', mode) + else + unregister_callback('post_linebreak_filter', mode) + end +end + +-- Publish some functions to the `cloze.sty` file. +base.linefil = nodex.write_linefil +base.line = nodex.write_line +base.margin = nodex.write_margin +base.set_option = registry.set_option +base.set_is_global = registry.set_is_global +base.unset_local_options = registry.unset_local_options +base.reset = registry.unset_global_options +base.get_defaults = registry.get_defaults +base.get_value = registry.get_value +base.marker = registry.write_marker + +return base diff --git a/macros/luatex/generic/cloze/cloze.pdf b/macros/luatex/generic/cloze/cloze.pdf Binary files differnew file mode 100644 index 0000000000..5901e8dd85 --- /dev/null +++ b/macros/luatex/generic/cloze/cloze.pdf |