From da070a532bdc1e46d94957df1a7b62a1b90b63dd Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Thu, 23 Jun 2016 21:36:11 +0000 Subject: cloze (23jun16) git-svn-id: svn://tug.org/texlive/trunk@41531 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/source/lualatex/cloze/cloze.dtx | 462 +++++++++++++++------- 1 file changed, 324 insertions(+), 138 deletions(-) (limited to 'Master/texmf-dist/source/lualatex/cloze') diff --git a/Master/texmf-dist/source/lualatex/cloze/cloze.dtx b/Master/texmf-dist/source/lualatex/cloze/cloze.dtx index a3c062a1d13..18780780f20 100644 --- a/Master/texmf-dist/source/lualatex/cloze/cloze.dtx +++ b/Master/texmf-dist/source/lualatex/cloze/cloze.dtx @@ -28,7 +28,7 @@ %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{cloze} %<*package> - [2016/06/13 v1.1 Package to typeset cloze worksheets or cloze tests] + [2016/06/23 v1.2 Package to typeset cloze worksheets or cloze tests] % %<*driver> \documentclass{ltxdoc} @@ -350,6 +350,26 @@ clozes are default concering the width \tt{\getdefaults{width}}. sed do eiusmod. \end{clozeexample} +Gaps with a fixed width are much harder to solve. + +\begin{clozeexample} +Lorem ipsum dolor \clozefix[align=center,width=3cm]{sit} amet, +\clozefix[align=center,width=3cm]{consectetur} adipisicing elit, sed do +eiusmod tempor incididunt \clozefix[align=center,width=3cm]{ut} labore +et dolore magna aliqua. +\end{clozeexample} + +Using the option |align| you can make nice tabulars like this: + +\begin{clozeexample} +\begin{tabular}{p{5cm}p{4cm}} +\raggedleft Composer & Life span \\ +\clozefix[width=5cm,align=right]{Joseph Haydn} & \clozefix{1723-1809} \\ +\clozefix[width=5cm,align=right]{Wolfgang Amadeus Mozart} & \clozefix{1756-1791} \\ +\clozefix[width=5cm,align=right]{Ludwig van Beethoven} & \clozefix{1770-1827} \\ +\end{tabular} +\end{clozeexample} + %% % \clozefil %% @@ -801,6 +821,28 @@ option is \tt{\getdefaults{width}}. \subsection{Special application areas} +%% +% tabbing +%% + +\subsubsection{The \tt{tabbing} environment} + +\begin{code} +\begin{verbatim} +\begin{tabbing} +col1 \hspace{1cm} \= col2 \hspace{1cm} \= col3 \hspace{1cm} \= col4 \\ +\cloze{col1} \> \> \clozefix{col3} \\ +\end{tabbing} +\end{verbatim} +\end{code} + +\begin{clozeexample} +\begin{tabbing} +col1 \hspace{1cm} \= col2 \hspace{1cm} \= col3 \hspace{1cm} \= col4 \\ +\cloze{col1} \> \> \clozefix{col3} \\ +\end{tabbing} +\end{clozeexample} + %% % picture %% @@ -811,8 +853,8 @@ option is \tt{\getdefaults{width}}. \begin{verbatim} \begin{picture}(320,100) \put(80,25){\cloze{Lorem}} -\put(160,50){\cloze{ipsum}} -\put(240,75){\cloze{dolor}} +\put(160,50){\clozefix{ipsum}} +\put(240,75){\clozefil{dolor}} \end{picture} \end{verbatim} \end{code} @@ -820,8 +862,8 @@ option is \tt{\getdefaults{width}}. \begin{clozeexample} \begin{picture}(320,100) \put(80,25){\cloze{Lorem}} -\put(160,50){\cloze{ipsum}} -\put(240,75){\cloze{dolor}} +\put(160,50){\clozefix{ipsum}} +\put(240,75){\clozefil{dolor}} \end{picture} \end{clozeexample} @@ -831,13 +873,24 @@ option is \tt{\getdefaults{width}}. \subsubsection{The \tt{tabular} environment} +\begin{code} +\begin{verbatim} +\begin{tabular}{l c} +\cloze{Lorem} & \cloze{ipsum} \\ +\clozefix{amet} & \clozefix{consectetur} \\ +\cloze{sed} & \cloze{do} \\ +\end{tabular} +\end{verbatim} +\end{code} + % No |c| inside because of short verbatim. \begin{clozeexample} -\begin{tabular}{l c c r} -\cloze{Lorem} & \cloze{ipsum} & \cloze{dolor} & \cloze{sit} \\ -\cloze{amet} & \cloze{consectetur} & \cloze{adipisicing} & \cloze{elit} \\ -\cloze{sed} & \cloze{do} & \cloze{eiusmod} & \cloze{tempor} \\ +\clozeset{width=1cm} +\begin{tabular}{l c} +\cloze{Lorem} & \cloze{ipsum} \\ +\clozefix{amet} & \clozefix{consectetur} \\ +\cloze{sed} & \cloze{do} \\ \end{tabular} \end{clozeexample} @@ -876,7 +929,7 @@ version 2005/12/01 or later. Since July 2015 the cloze package is included in the Comprehensive TeX Archive Network (CTAN). -* TeX archive: https://www.ctan.org/tex-archive/macros/luatex/latex/cloze +* TeX archive: http://mirror.ctan.org/tex-archive/macros/luatex/latex/cloze * Package page: https://www.ctan.org/pkg/cloze # Repository @@ -905,7 +958,7 @@ or manually: % % \fi % -% \CheckSum{0} +% \CheckSum{128} % % \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 @@ -927,6 +980,8 @@ or manually: % \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} % % \DoNotIndex{\newcommand,\newenvironment,\def,\directlua} % @@ -1428,6 +1483,11 @@ registry.local_options = {} % \begin{macrocode} local cloze = {} % \end{macrocode} +% In the status table are stored state information, which are necessary +% for the recursive cloze generation. +% \begin{macrocode} +cloze.status = {} +% \end{macrocode} % % The |base| table contains some basic functions. |base| is the only % table of this Lua module that will be exported. @@ -1499,6 +1559,27 @@ function nodex.create_line(width) end % \end{macrocode} % +% \clozeluafunction{insert\_list} +% 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'|. +% \begin{macrocode} +function nodex.insert_list(position, current, list, head) + if not head then + head = current + end + for i, insert in ipairs(list) do + if position == 'after' then + head, current = node.insert_after(head, current, insert) + elseif position == 'before' then + head, current = node.insert_before(head, current, insert) + end + end + return current +end +% \end{macrocode} +% % \clozeluafunction{insert\_line} % Enclose a rule node (cloze line) with two PDF colorstack whatsits. The % first colorstack node dyes the line, the seccond resets the color. @@ -1512,14 +1593,16 @@ end % \end{nodelist} % % \begin{macrocode} -function nodex.insert_line(head, current, width) - local n = {} -- node - n.color_line = nodex.create_color('line') - head, n.color_line = node.insert_after(head, current, n.color_line) - n.line = nodex.create_line(width) - head, n.line = node.insert_after(head, n.color_line, n.line) - n.color_reset = nodex.create_color('reset') - return node.insert_after(head, n.line, n.color_reset) +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 % \end{macrocode} % @@ -1612,6 +1695,21 @@ function nodex.write_margin() end % \end{macrocode} % +% \clozeluafunction{search\_hlist} +% Search for a |hlist| (subtype |line|). Return false, if no |hlist| can +% be found. +% \begin{macrocode} +function nodex.search_hlist(head) + while head do + if head.id == node.id('hlist') and head.subtype == 1 then + return nodex.strut_to_hlist(head) + end + head = head.next + end + return false +end +% \end{macrocode} +% % \subsubsection{Option handling (registry)} % % The table |registry| bundels functions that deal with option handling. @@ -1656,6 +1754,20 @@ function registry.write_marker(mode, position) end % \end{macrocode} % +% \clozeluafunction{is\_marker} +% This functions checks if the given node |item| is a marker. +% \begin{macrocode} +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 +% \end{macrocode} +% % \clozeluafunction{check\_marker} % This functions tests, whether the given node |item| is a marker. The % argument |item| is a node. The argument |mode| accepts the string @@ -1719,6 +1831,15 @@ function registry.get_marker_values(marker) end % \end{macrocode} % +% \clozeluafunction{remove\_marker} +% This function removes a given whatsit marker. It only deletes a node, +% if a marker is given. +% \begin{macrocode} +function registry.remove_marker(marker) + if registry.is_marker(marker) then node.remove(marker, marker) end +end +% \end{macrocode} +% % \paragraph{Storage functions (storage)} % % \clozeluafunction{get\_index} @@ -1868,62 +1989,119 @@ end % % \subsubsection{Assembly to cloze texts (cloze)} % +% Some graphics for better understanding of the node tree: +% +% \paragraph{Paragraph} +% +% \noindent\includegraphics[width=\linewidth]{graphics/par} +% +% \paragraph{Tabular environment} +% +% \noindent\includegraphics[width=\linewidth]{graphics/tabular} +% +% \paragraph{Picture environment} +% +% \noindent\includegraphics[width=\linewidth]{graphics/picture} +% % \clozeluafunction{basic\_make} -% The function |cloze.basic_make()| makes one gap. The argument |head| -% is the head node of a node list. The argument |start| is the node, -% where the gap begins. The argument |stop| is the node, where the gap -% ends. +% The function |cloze.basic_make()| makes one gap. The argument |start| +% is the node, where the gap begins. The argument |stop| is the node, +% where the gap ends. % \begin{macrocode} -function cloze.basic_make(head, hlist, start, stop) +function cloze.basic_make(start, stop) local n = {} local l = {} + n.head = start if not start or not stop then return end n.start = start n.stop = stop l.width = node.dimensions( - hlist.glue_set, - hlist.glue_sign, - hlist.glue_order, + cloze.status.hlist.glue_set, + cloze.status.hlist.glue_sign, + cloze.status.hlist.glue_order, n.start, n.stop ) - head, n.line = nodex.insert_line(head, n.start, l.width) - n.color_text = nodex.create_color('text') - head, n.color_text = node.insert_after( - hlist.head, - n.line, - n.color_text - ) + n.line = nodex.insert_line(n.start, l.width) + n.color_text = nodex.insert_list('after', n.line, {nodex.create_color('text')}) if registry.get_value_show() then - n.kern = nodex.create_kern(-l.width) - node.insert_after(head, n.color_text, n.kern) - n.color_reset = nodex.create_color('reset') - node.insert_after(head, n.stop, n.color_reset) + nodex.insert_list('after', n.color_text, {nodex.create_kern(-l.width)}) + nodex.insert_list('before', n.stop, {nodex.create_color('reset')}, n.head) else n.line.next = n.stop.next n.stop.prev = n.line.prev end - +% \end{macrocode} +% I 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. +% \begin{macrocode} + registry.remove_marker(n.start) + registry.remove_marker(n.stop) end % \end{macrocode} % -% \subsubsection{Node trees} -% -% Some graphics for better understanding of the node tree: -% -% \paragraph{Paragraph} -% -% \noindent\includegraphics[width=\linewidth]{graphics/par} -% -% \paragraph{Tabular environment} -% -% \noindent\includegraphics[width=\linewidth]{graphics/tabular} +% \clozeluafunction{basic\_search\_stop} +% Search for a stop marker. +% \begin{macrocode} +function cloze.basic_search_stop(head) + local stop + while head do + cloze.status.continue = true + stop = head + if registry.check_marker(stop, 'basic', 'stop') then + cloze.status.continue = false + break + end + head = head.next + end + return stop +end +% \end{macrocode} % -% \paragraph{Picture environment} +% \clozeluafunction{basic\_search\_start} +% Search for a start marker. Also begin a new cloze, if the boolean +% value |cloze.status.continue| is true. The knowledge of the last +% hlist node is a requirement to begin a cloze. +% \begin{macrocode} +function cloze.basic_search_start(head) + local start + local stop + local n = {} + if cloze.status.continue then + n.hlist = nodex.search_hlist(head) + if n.hlist then + cloze.status.hlist = n.hlist + start = cloze.status.hlist.head + end + elseif registry.check_marker(head, 'basic', 'start') then + start = head + end + if start then + stop = cloze.basic_search_stop(start) + cloze.basic_make(start, stop) + end +end +% \end{macrocode} % -% \noindent\includegraphics[width=\linewidth]{graphics/picture} +% \clozeluafunction{basic\_recursion} +% Parse recursivley the node tree. Start and stop markers can be nested +% deeply into the node tree. +% \begin{macrocode} +function cloze.basic_recursion(head) + while head do + if head.head then + cloze.status.hlist = head + cloze.basic_recursion(head.head) + else + cloze.basic_search_start(head) + end + head = head.next + end +end +% \end{macrocode} % % \clozeluafunction{basic} % The corresponding \LaTeX{} command to this lua function is \cmd{\cloze} @@ -1931,44 +2109,36 @@ end % node list. % \begin{macrocode} function cloze.basic(head) - local n = {} -- node - local b = {} -- boolean - local l = {} -- length - local t = {} -- temp - for hlist in node.traverse_id(node.id('hlist'), head) do - hlist = nodex.strut_to_hlist(hlist) - if b.line_end then - b.init_cloze = true - end - n.current = hlist.head - while n.current do - if - registry.check_marker(n.current, 'basic', 'start') - or - b.init_cloze - then - b.init_cloze = false - n.start = n.current - while n.current do - b.line_end = true - n.stop = n.current - if registry.check_marker(n.stop, 'basic', 'stop') then - b.line_end = false - break - end - n.current = n.current.next - end - cloze.basic_make(head, hlist, n.start, n.stop) - n.current = n.stop - else - n.current = n.current.next - end - end - end + cloze.status.continue = false + cloze.basic_recursion(head) return head end % \end{macrocode} % +% \clozeluafunction{fix\_length} +% Calculate the length of the whitespace before (|l.kern_start|) and +% after (|l.kern_stopt|) the text. +% \begin{macrocode} +function cloze.fix_length(start, stop) + local l = {} + l.width = tex.sp(registry.get_value('width')) + l.text_width = node.dimensions(start, stop) + l.align = registry.get_value('align') + if l.align == 'right' then + l.kern_start = - l.text_width + l.kern_stop = 0 + elseif l.align == 'center' then + l.half = (l.width - l.text_width) / 2 + l.kern_start = - l.half - l.text_width + l.kern_stop = l.half + else + l.kern_start = - l.width + l.kern_stop = l.width - l.text_width + end + return l.width, l.kern_start, l.kern_stop +end +% \end{macrocode} +% % \clozeluafunction{fix\_make} % The function |cloze.fix_make| generates a gap of fixed width. % @@ -1995,65 +2165,74 @@ end % |n.stop| & |whatsit| & |user_definded| & |index| \\ % \end{nodelist} % -% The argument |head| is the head node of a node list. The argument -% |start| is the node, where the gap begins. The argument |stop| is the -% node, where the gap ends. +% The argument |start| is the node, where the gap begins. The argument +% |stop| is the node, where the gap ends. % \begin{macrocode} -function cloze.fix_make(head, start, stop) +function cloze.fix_make(start, stop) local l = {} -- length - l.width = tex.sp(registry.get_value('width')) local n = {} -- node - n.start = start - n.stop = stop - l.text_width = node.dimensions(n.start, n.stop) - local align = registry.get_value('align') - if align == 'right' then - l.kern_start = -l.text_width - l.kern_stop = 0 - elseif align == 'center' then - l.half = (l.width - l.text_width) / 2 - l.kern_start = -l.half - l.text_width - l.kern_stop = l.half - else - l.kern_start = -l.width - l.kern_stop = l.width - l.text_width - end - head, n.line = nodex.insert_line(head, n.start, l.width) + l.width, l.kern_start, l.kern_stop = cloze.fix_length(start, stop) + n.line = nodex.insert_line(start, l.width) if registry.get_value_show() then - n.kern_start = nodex.create_kern(l.kern_start) - head, n.kern_start = node.insert_after(head, n.line, n.kern_start) - n.color_text = nodex.create_color('text') - node.insert_after(head, n.kern_start, n.color_text) - n.color_reset = nodex.create_color('reset') - node.insert_before(head, n.stop, n.color_reset) - n.kern_stop = nodex.create_kern(l.kern_stop) - node.insert_before(head, n.stop, n.kern_stop) + nodex.insert_list( + 'after', + n.line, + { + nodex.create_kern(l.kern_start), + nodex.create_color('text') + } + ) + nodex.insert_list( + 'before', + stop, + { + nodex.create_color('reset'), + nodex.create_kern(l.kern_stop) + }, + start + ) else - n.line.next = n.stop.next + n.line.next = stop.next end + registry.remove_marker(start) + registry.remove_marker(stop) end % \end{macrocode} % -% \clozeluafunction{fix} -% The corresponding \LaTeX{} command to this Lua function is -% \cmd{\clozefix} \secref{sec:command-clozefix}. The argument |head| is +% \clozeluafunction{fix\_recursion} +% Function to recurse the node list and search after marker. |head| is % the head node of a node list. % \begin{macrocode} -function cloze.fix(head) +function cloze.fix_recursion(head) local n = {} -- node n.start, n.stop = false - for current in node.traverse_id(node.id('whatsit'), head) do - if not n.start then - n.start = registry.get_marker(current, 'fix', 'start') - end - if not n.stop then - n.stop = registry.get_marker(current, 'fix', 'stop') - end - if n.start and n.stop then - cloze.fix_make(head, n.start, n.stop) - n.start, n.stop = false + while head do + if head.head then + cloze.fix_recursion(head.head) + else + if not n.start then + n.start = registry.get_marker(head, 'fix', 'start') + end + if not n.stop then + n.stop = registry.get_marker(head, 'fix', 'stop') + end + if n.start and n.stop then + cloze.fix_make(n.start, n.stop) + n.start, n.stop = false + end end + head = head.next end +end +% \end{macrocode} +% +% \clozeluafunction{fix} +% The corresponding \LaTeX{} command to this Lua function is +% \cmd{\clozefix} \secref{sec:command-clozefix}. The argument |head| is +% the head node of a node list. +% \begin{macrocode} +function cloze.fix(head) + cloze.fix_recursion(head) return head end % \end{macrocode} @@ -2094,15 +2273,21 @@ function cloze.par(head) end l.width = hlist.width hlist, n.strut, n.head = nodex.strut_to_hlist(hlist) - head, n.line = nodex.insert_line(head, n.strut, l.width) + n.line = nodex.insert_line(n.strut, l.width) if registry.get_value_show() then - n.kern = nodex.create_kern(-l.width) - head, n.kern = node.insert_after(head, n.line, n.kern) - n.color_text = nodex.create_color('text') - node.insert_after(head, n.kern, n.color_text) - n.tail = node.tail(n.head) - n.color_reset = nodex.create_color('reset') - node.insert_after(n.head, n.tail, n.color_reset) + nodex.insert_list( + 'after', + n.line, + { + nodex.create_kern(-l.width), + nodex.create_color('text') + } + ) + nodex.insert_list( + 'after', + node.tail(head), + {nodex.create_color('reset')} + ) else n.line.next = nil end @@ -2124,6 +2309,7 @@ end % string values |basic|, |fix| and |par|. % \begin{macrocode} function base.register(mode) + local basic if mode == 'par' then luatexbase.add_to_callback( 'post_linebreak_filter', -- cgit v1.2.3