texlive[53147] Master: sdaps (16dec19)

commits+karl at tug.org commits+karl at tug.org
Mon Dec 16 22:42:56 CET 2019


Revision: 53147
          http://tug.org/svn/texlive?view=revision&revision=53147
Author:   karl
Date:     2019-12-16 22:42:56 +0100 (Mon, 16 Dec 2019)
Log Message:
-----------
sdaps (16dec19)

Modified Paths:
--------------
    trunk/Master/tlpkg/bin/tlpkg-ctan-check
    trunk/Master/tlpkg/libexec/ctan2tds
    trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/latex/sdaps/
    trunk/Master/texmf-dist/doc/latex/sdaps/README
    trunk/Master/texmf-dist/doc/latex/sdaps/sdapsarray.pdf
    trunk/Master/texmf-dist/doc/latex/sdaps/sdapsbase.pdf
    trunk/Master/texmf-dist/doc/latex/sdaps/sdapsclassic.pdf
    trunk/Master/texmf-dist/doc/latex/sdaps/sdapscode128.tex
    trunk/Master/texmf-dist/doc/latex/sdaps/sdapslayout.pdf
    trunk/Master/texmf-dist/doc/latex/sdaps/sdapspdf.pdf
    trunk/Master/texmf-dist/source/latex/sdaps/
    trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.dtx
    trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.ins
    trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.dtx
    trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.ins
    trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.dtx
    trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.ins
    trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.dtx
    trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.ins
    trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.dtx
    trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.ins
    trunk/Master/texmf-dist/tex/latex/sdaps/
    trunk/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty
    trunk/Master/texmf-dist/tex/latex/sdaps/sdapsbase.sty
    trunk/Master/texmf-dist/tex/latex/sdaps/sdapsclassic.cls
    trunk/Master/texmf-dist/tex/latex/sdaps/sdapslayout.sty
    trunk/Master/texmf-dist/tex/latex/sdaps/sdapspdf.sty
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Brazilian.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Dutch.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-English.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Finnish.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-French.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-German.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Italian.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Norsk.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Polish.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Portuguese.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Romanian.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Sinhala.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Spanish.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Swedish.dict
    trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Ukrainian.dict
    trunk/Master/tlpkg/tlpsrc/sdaps.tlpsrc

Added: trunk/Master/texmf-dist/doc/latex/sdaps/README
===================================================================
--- trunk/Master/texmf-dist/doc/latex/sdaps/README	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/sdaps/README	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,57 @@
+SDAPS Class and Package repository
+==================================
+
+This repository contains LaTeX classes and packages to create machine readable
+questionnaires. Metadata is generated for the whole document and it is
+possible to process created forms fully automatically using the SDAPS main
+program.
+As of now, this package is included in the SDAPS unstable (1.9.0) distribution.
+
+Features
+========
+
+ * Flexible metadata generation
+ * Different question types:
+   - Freeform text
+   - Multiple Choice Questions
+     * may contain freeform text fields
+       (TODO: some people would like a combined checkbox + textfield)
+     * standalone
+     * as a list (to be implemented)
+     * array with questions in rows
+     * array with questions in columns
+   - Single Choice Qeustions:
+     * standalone (to be implemented)
+     * as a list (to be implemented)
+     * array with questions in rows
+     * array with questions in columns
+   - Range (1-n answers with optionally further choices):
+     * standalone
+     * array with questions in rows
+ * Array layout features (used for questions in arrays):
+   - Can flow over multiple pages and repeats the header automatically
+   - Optional document wide alignment of array environments
+   - Has complex layout features like rotating the headers to safe space
+   - Ability to exchange rows and columns on the fly
+ * PDF form generation
+ * Complex document wide and local setting handling
+ * Possibility to pre-fill questionnaires directly in LaTeX
+   (not yet easily usable by end users)
+
+Documentation
+=============
+
+The most up to date documentation is in the sphinx directory. An online copy
+can be found at http://sdaps.org/class-doc. The documentation in the files
+is outdated at this point and likely to be stripped down rather than extended.
+
+Created Files
+-------------
+
+ * sdapsbase.sty: Base functionality on which everything else is build.
+ * sdapsclassic.cls: Class for creating questionnaires
+                     (currently the only proper way of using these packages)
+ * sdapspdf.sty: Makes any SDAPS document into a PDF form
+ * sdapsarray.sty: Base functionality for tabular like layouts
+ * sdapslayout.sty: Base functionality for complex question layouts
+ * sdapscode128.tex: Code 128 barcode renderer (re-licensed upstream copy)


Property changes on: trunk/Master/texmf-dist/doc/latex/sdaps/README
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsarray.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsarray.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/sdaps/sdapsarray.pdf	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/texmf-dist/doc/latex/sdaps/sdapsarray.pdf	2019-12-16 21:42:56 UTC (rev 53147)

Property changes on: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsarray.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsbase.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsbase.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/sdaps/sdapsbase.pdf	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/texmf-dist/doc/latex/sdaps/sdapsbase.pdf	2019-12-16 21:42:56 UTC (rev 53147)

Property changes on: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsbase.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsclassic.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsclassic.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/sdaps/sdapsclassic.pdf	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/texmf-dist/doc/latex/sdaps/sdapsclassic.pdf	2019-12-16 21:42:56 UTC (rev 53147)

Property changes on: trunk/Master/texmf-dist/doc/latex/sdaps/sdapsclassic.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/sdaps/sdapscode128.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/sdaps/sdapscode128.tex	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/sdaps/sdapscode128.tex	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,374 @@
+%% Copyright 1996 Petr Olsak.
+%
+% 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
+%
+% The work has been licensed under these terms with permission
+% by Petr Olsak for distribution with SDAPS.
+%
+% The original file follows
+% See http://math.feld.cvut.cz/olsak/
+
+% Macro for conversion of string to barcodes by Code 128 standard
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% March 1996                                 (C) Petr Ol\v{s}\'ak
+
+% For user information see the file test128.tex
+
+% Comments at programmer level are included in this file. First you can
+% read the end of this file: The description of Code 128 standard.
+
+\wlog{**** Macro for barcodes in "Code 128" by (C) Petr Olsak used ****}
+
+% Declarations:
+\newdimen\X            % the module size X,
+\newdimen\bcorr        % the bar correction (see bellow).
+\newdimen\workdimen \newdimen\barheight   % internal variables
+\newtoks\inputtext \newtoks\icode
+\newcount\tempnum \newcount\chnum \newcount\chtotal
+\newif\ifnext \newif\ifchar
+\def\empty{} \def\End{@@end}
+
+% Implicit values:
+\X=.33mm         % The X module width.
+\bcorr=.020mm    % Bar reduction.
+\barheight=1.5cm % The code height.
+
+% First we declare some tables.
+% "\definetable<lab><num> string \relax": Each token from "string" gets a value
+% (spaces are ignored). First token gets value <num>, second <num+1>
+% and so on. It is possible to reconstruct the value by
+% "\csname<lab>\string<token>\endcsname".
+\def\definetable#1#2 {\tempnum=#2 \def\temp{#1}\let\next=\repeatdefine \next}
+\def\repeatdefine#1{\ifx#1\relax \let\next=\relax \else
+  \expandafter\edef\csname\temp\string#1\endcsname{\the\tempnum}%
+  \advance\tempnum by1 \fi \next}
+
+%%%%%%%%%%%%%%%%%%%%% Basic tables for Code 128: %%%%%%%%%%%%%%%%%%%%%%%%%%%
+\definetable:0  % All input characters from Code B:
+  \  ! " \# \$ \% \& ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @
+  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 [ \\ ] \^ \_ `
+  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 \{ | \} \~ \DEL \relax
+\definetable:64 % Other input characters from Code A:
+  \NUL \SOH \STX \ETX \EOT \ENQ \ACK \BEL \BS \HT \LF \VT \FF \CR \SO \SI
+  \DLE \DCone \DCtwo \DCthree \DCfour \NAK \SYN \ETB \CAN \EM \SUB \ESC
+  \FS \GS \RS \US \relax
+\definetable:0 \
+  \relax    % the \^^M must be the same as \<space>
+\definetable:0 \SP \relax  % the \SP is alternative to \<space>
+\definetable{D:}0 0123456789 \relax  % Digits
+\definetable{B:}0 `abcdefghijklmnopqrstuvwxyz\{\|\}\~ \relax % Only in Code B
+\definetable{A:}0  \NUL \SOH \STX \ETX \EOT \ENQ \ACK \BEL \BS \HT \LF \VT
+  \FF \CR \SO \SI \DLE \DCone \DCtwo \DCthree \DCfour \NAK \SYN \ETB \CAN
+  \EM \SUB \ESC \FS \GS \RS \US \relax % only in code A.
+\def\tableofcode#1{\ifcase#1 % The output characters:
+  212222\or 222122\or 222221\or 121223\or 121322\or % 0-4
+  131222\or 122213\or 122312\or 132212\or 221213\or % 5-9
+  221312\or 231212\or 112232\or 122132\or 122231\or % 10-14
+  113222\or 123122\or 123221\or 223211\or 221132\or % 15-19
+  221231\or 213212\or 223112\or 312131\or 311222\or % 20-24
+  321122\or 321221\or 312212\or 322112\or 322211\or % 25-29
+  212123\or 212321\or 232121\or 111323\or 131123\or % 30-34
+  131321\or 112313\or 132113\or 132311\or 211313\or % 35-39
+  231113\or 231311\or 112133\or 112331\or 132131\or % 40-44
+  113123\or 113321\or 133121\or 313121\or 211331\or % 45-49
+  231131\or 213113\or 213311\or 213131\or 311123\or % 50-54
+  311321\or 331121\or 312113\or 312311\or 332111\or % 55-59
+  314111\or 221411\or 431111\or 111224\or 111422\or % 60-64
+  121124\or 121421\or 141122\or 141221\or 112214\or % 65-69
+  112412\or 122114\or 122411\or 142112\or 142211\or % 70-74
+  241211\or 221114\or 413111\or 241112\or 134111\or % 75-79
+  111242\or 121142\or 121241\or 114212\or 124112\or % 80-84
+  124211\or 411212\or 421112\or 421211\or 212141\or % 85-89
+  214121\or 412121\or 111143\or 111341\or 131141\or % 90-94
+  114113\or 114311\or 411113\or 411311\or 113141\or % 95-99
+  114131\or 311141\or 411131\else 00000\fi }        % 100-102
+\def\startA{211412}
+\def\startB{211214}
+\def\startC{211232}
+\def\stop{23311120}
+
+% Implementations of tests:
+% After "\testchar <lab><token>" the "\ifchar" has following meaning:
+% true, if <token> is in digits, only in A or only in B respectively with
+% <lab> is {D:}, {A:} or {B:}.
+\def\testchar #1#2{\expandafter\ifx\csname#1\string#2\endcsname \relax
+          \charfalse \else \chartrue \fi}
+
+% After "\numofdigits string\stop" is used, the \tempnum register contain
+% the number of digits from first continuosly group of digits from left in
+% "string". If "string" starts with no digit then \tempnum=0.
+\def\numofdigits{\tempnum=0 \let\next=\cyklnumber \cyklnumber}
+\def\cyklnumber#1{\testchar {D:}#1%
+   \ifchar \advance\tempnum by1
+   \else \ifx #1\stop\def\next{}%
+         \else \def\next##1\stop{}\fi
+   \fi \next}
+
+% After "\testnext<lab> string\stop" is used, the "\ifnext" has
+% the following meaning:
+% 1. <lab> is {A:}: "\ifnext" is true, if first character from "string" is
+% only in A code after skip all common characters shared in codeA and B.
+% 2. <lab> is {B:}: "\ifnext" is true, if first character from "string" is
+% only in B code after skip all common characters shared in codeA and B.
+\def\testA{A:}
+\def\testnext#1{\nextfalse
+  \def\tempA{#1}%
+  \ifx\tempA\testA \def\tempB{B:}\else \def\tempB{A:}\fi
+  \let\next=\cyklcontrol \next}
+\def\cyklcontrol#1{%
+   \ifx#1\stop \let\next=\relax
+   \else \testchar \tempB #1%
+         \ifchar \def\next##1\stop{}%
+         \else \testchar \tempA #1%
+               \ifchar \nexttrue \def\next##1\stop{}\fi \fi
+   \fi \next}
+
+% "\addtok \cs" adds the "\cs," into \icode.
+% "\addtoks{string} adds the "string," into \icode. "string" is the number
+% of line in table 1, so we re-calculate the current check sum.
+% "\addchar <token> adds the numerical value of <token> (declared in
+% \definetable:) into \icode. This value is followed by comma too.
+\def\addtok#1{\edef\act{\noexpand\icode={\the\icode\noexpand#1,}}\act}
+\def\addtoks#1{\edef\act{\noexpand\icode={\the\icode#1,}}\act
+  \tempnum=#1 \multiply\tempnum by\chnum
+  \advance\chtotal by\tempnum \advance\chnum by 1\relax}
+\def\addchar#1{\expandafter\ifx\csname:\string#1\endcsname\relax
+  \errmessage{The input token "\string#1" is not included in Code 128
+              table, will ignored}%
+  \else \expandafter\addtoks\expandafter{\csname:\string#1\endcsname}\fi}
+
+% \code{text} first converts the input text into internal representation in
+% \icode. The format of \icode is: "\start,num,num,num,\stop,", where
+% "\start" is one of "\startA" or "\startB" or "\startC". The <num>
+% represents the line of code table (see standard of Code 128 bellow)
+% of output characters. The amount of <num>s is not restricted. The sequence
+% is terminated by "\stop,". See to .log for example of this format.
+%
+% The choice of the start character:
+% If next 4 input characters are digits then \startC
+% else if the next uncommon char is from code A and not from B then \startA
+%      else \startB
+% The "next uncommon char" is first character from left which falls
+% into code A xor code B
+\def\code#1{\inputtext={#1}\wlog{** Code 128 ** input: \the\inputtext}%
+  \icode={}\chnum=1
+  \numofdigits#1\stop % in \tempnum is the number of digits now
+  \ifnum\tempnum>3 \addtok\startC \chtotal=2 \let\Next=\codeC \else
+     \testnext{A:} #1\stop
+     \ifnext \addtok\startA \chtotal=0 \let\Next=\codeA
+     \else \addtok\startB \chtotal=1 \let\Next=\codeB \fi
+  \fi \Next #1\End\End}
+
+% There is mode A. Test to change the mode:
+% If the next 4 input characters are digits and the number of digits are even
+%    then switch to modeC using <codeC>.
+% If the next char is from Code B and not from Code A then:
+%    if the next uncommnon char is from Code B and not from A then
+%         switch to mode B using <codeB>.
+%    else include <SHIFT> and stay in mode A.
+\def\codeA #1#2#3\End{\addchar#1%
+  \numofdigits#2#3\stop
+  \ifnum\tempnum>3 \ifodd\tempnum\else
+      \addtoks{99}\let\Next=\codeC \fi
+  \else \ifx#2\End \let\Next=\finalcode
+        \else \testchar{B:} #2%
+              \ifchar \testnext{B:} #3\stop
+                      \ifnext \addtoks{100}\let\Next=\codeB
+                      \else \addtoks{98}\fi \fi \fi \fi
+  \Next #2#3\End}
+
+% There is mode B. Test to change the mode:
+% If the next 4 input characters are digits and the number of digits are even
+%    then switch to modeC using <codeC>
+% If the next char is from Code A and not from Code B then:
+%    if the next uncommnon char is from Code A and not from B then
+%         switch to mode A using <codeA>.
+%    else include <SHIFT> and stay in mode B.
+\def\codeB #1#2#3\End{\addchar#1%
+  \numofdigits#2#3\stop
+  \ifnum\tempnum>3 \ifodd\tempnum\else
+      \addtoks{99}\let\Next=\codeC \fi
+  \else \ifx#2\End \let\Next=\finalcode
+        \else \testchar{A:} #2%
+              \ifchar \testnext{A:} #3\stop
+                      \ifnext \addtoks{101}\let\Next=\codeA
+                      \else \addtoks{98}\fi \fi \fi \fi
+  \Next #2#3\End}
+
+% There is mode C. Test to change the mode:
+% If not next two chars are digits switch to code A or B by following rule:
+% If the next uncommon char is from Code A and not from B then
+%      switch to mode A using <codeA>
+% else switch to mode B using <codeB>
+\def\codeC #1#2#3#4\End{\addtoks{#1#2}%
+  \ifx#3\End \let\Next=\finalcode
+  \else \testchar{D:} #3%
+        \ifchar \def\temp{#4}%
+                \ifx\temp\empty \switchtoAorB #3\stop
+                \else \separate #4\stop
+                      \edef\act{\noexpand\testchar{D:}\temp}\act
+                      \ifchar
+                      \else \switchtoAorB #3#4\stop \fi \fi
+        \else \switchtoAorB #3#4\stop \fi
+  \fi \Next #3#4\End}
+\def\separate#1#2\stop{\def\temp{#1}}
+\def\switchtoAorB #1\stop{\testnext{A:} #1\stop
+   \ifnext \addtoks{101}\let\Next=\codeA
+   \else \addtoks{100}\let\Next=\codeB \fi}
+
+\def\finalcode\End\End{\addchecksum \addtok\stop \wlog{encoded: \the\icode}%
+  \expandafter\makecode\the\icode}
+
+% \addchecksum adds the check sum into \icode
+\def\addchecksum{\tempnum=\chtotal
+  \divide\tempnum by 103 \multiply\tempnum by 103
+  \advance\chtotal by-\tempnum \addtoks{\the\chtotal}}
+
+% The \makecode converts the \icode from format "\start,num,num,\stop,"
+% into sequence of digits. Each digit repersents the multiple of X module
+% size for bar or space if it is at odd or even position. For example
+% 21141223311120. It means bar of 2X, space 1X, bar 1X, space 4X and so on.
+% This representation of code is stored in macro \internalcode.
+\def\makecode#1,{\let\next=\cyklcode \edef\internalcode{#1}\next}
+\def\cyklcode#1,{%
+  \ifx\stop#1\let\next=\finalmakecode \edef\internalcode{\internalcode\stop}%
+  \else \edef\internalcode{\internalcode\tableofcode{#1}}\fi \next}
+\def\finalmakecode{\wlog{black-white: \internalcode}%
+  \begcode \let\next=\makebars \expandafter\makebars\internalcode}
+
+% \makebars simply makes the \vrules and \kerns of appropriate sizes from
+% \internalcode representation. Each width of \vrule is corrected by \bcorr
+% and opposite for \kern.
+\def\makebars#1#2{\if0#2\let\next=\endcode\fi
+  \workdimen=#1\X \advance\workdimen by-\bcorr \vrule width\workdimen
+  \workdimen=#2\X \advance\workdimen by \bcorr \kern\workdimen
+  \next}
+
+% The begin and end of completed \hbox:
+\def\begcode{\hbox\bgroup\vrule height\barheight width0pt}
+\def\endcode{\egroup}
+
+% User can use \codetext or \codeothertext instead \code:
+\def\codeothertext#1#2{\vbox{\halign{\hfil##\hfil\cr\code{#1}\cr{\tt#2}\cr}}}
+\def\codetext#1{\codeothertext{#1}{#1}}
+
+\endinput %%%%%%%%%%%%%%% End of macros %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+               The description of Code 128 standard
+               ************************************
+
+The characters from input string are converted from left to right
+to so called "output characters" by table 1 (see below). Each output
+character has three bars and spaces. The width of bars and the dimensions
+of spaces between bars are significant. This values are expresed by
+multiples of basic dimension: so called X module size (see \X in macro).
+The multiples varies from 1 to 4. We are using the six digits expression of
+output character. It expressed dimensions of bar, space, bar, space, bar,
+space. For example 122412 means one output character drawn as: 1X bar, 2X
+space, 2X bar, 4X space, 1X bar and 2X space.
+
+The table 1:
+
+num.line  code A    code B    codeC     output character
+-------------------------------------------------------
+  0       [space]    [space]    00        212222
+  1            !         !      01        222122
+  2            "         "      02        222221
+  3            #         #      03        121223
+... and so on ...
+ 63            _         _      63        111224
+ 64         \NUL         `      64        111422
+ 65         \SOH         a      65        121124
+ 66         \STX         b      66        121421
+... and so on ...
+ 93          \GS         }      93        111341
+ 94          \RS         ~      94        131141
+ 95          \US      \DEL      95        114113
+ 96       <FNC3>    <FNC3>      96        113311
+ 97       <FNC2>    <FNC2>      97        411113
+ 98      <SHIFT>   <SHIFT>      98        411311
+ 99      <codeC>   <codeC>      99        113141
+100      <codeB>    <FNC4>   <codeB>      114131
+101       <FNC4>   <codeA>   <codeA>      311141
+102       <FNC1>    <FNC1>    <FNC1>      411131
+
+The whole table is not presented here because you can simply reconstruct it
+from macros.
+
+The columns "code A", "code B", and "code C" inlude all possible input
+characters in input string (excluding "special commands" in lines 96--102
+written in <angle> braces). The special \TeX{} characters must be escaped
+(i.e. user have to write \# and no #). The escaped words (in capitals)
+represents so called "control characters" from ASCII. The meaning of this
+sequences depends on application. The "special commands" in lines 96--102
+written in <angle> braces are not possible in input. They are special for
+decoder. The <SHIFT> and <codeA-C> are used in our macro, but <FNC?> are
+not because they are reserved for special purposes.
+
+The Code B column is whole expressed in \definetable:0 (see in macro). The
+Code A column has the same values in lines 0--63 (capitals, digits and some
+other ASCII characters are included here). Code A differ from Code B in
+lines 64--102. The lowercase letters and another ASCII characters are
+included in "Code B", but control sequences are included in "Code A".
+The different part of "Code A" column is expressed in \definetable:64 (see
+in macro). The "code C" column includes the digits pairs and corresponds to
+number of line in table 1. Two digits in input go to one output character.
+The whole "output character" column is expressed in \tableofcode (see in
+macro).
+
+The "start character" is appended before each barcode. There are three
+types of start character depending on which column of table is used for
+next encoding (so called mode). See macros \startA, \startB and \startC.
+For examlpe: If \startC is used, next output character represents two
+digits in input (codeC mode). If \startB is used, next output character
+represents one ASCII character in input (codeB mode). If \startA is used,
+next output character represents probably the control sequence in input or
+capitals, but not lowercase ASCII (codeA mode).
+
+If some mode for encoding is currently used and next input character is not
+included in appropriate column, the "switching command" is included into
+sequence of output character. The <SHIFT> command switches from mode A to
+B or from B to A only for one next input character and the other input
+characters are coded in the same mode (A or B). The <codeA> command
+switches to codeA mode definitively unless next switch command is used.
+The commands <codeB> and <codeC> makes the same service, but to switch into
+mode B or C respectively. All these commands are expessed in table 1.
+
+It is recomended to chose the start character and switching commands by the
+way, that the resulting length of code is minimised. There are some
+recommendations of this choice. These recommendations are included into our
+macros (see the commnets and macros for \code, \codeA, \codeB and \codeC).
+
+The checksum character is added after the end of input string. Finally,
+the "stop character" is appended after the end of barcode. This character
+has exclusively four bars and not only three. See macro \stop.
+
+The checksum character is calculated from output characters used in the
+code. The number of line in table 1 of each output character is asumed. The
+start character is covered too, but checksum character itself and the
+stop character are not included into calculation. The startA or startB or
+startC characters has its number of line 103 or 104 or 105 respectively. Each
+output character (more exactly its number of line in table 1) is multiplied
+by "weight number" and the total sum is calculated. The weight number of
+start character is one. The weight number of first output characet after
+start is one too. Second character has weight number two, and so on. The
+n-th character has weight number n. The total sum modulo 103 is the line of
+the calculated checksum character.
+
+Example for checksum calculation:
+Input:    123456
+Encoded:  StartC, 12, 34, 56
+Total sum of checksum: 105 + 1*12 + 2*34 + 3*56 = 535
+Modulo 103: 44
+The character from line 44 is apended as checksum.
+The whole encoded code: StartC, 12, 34, 56, 44, Stop
+
+%%% End of file.
+
+
+
+


Property changes on: trunk/Master/texmf-dist/doc/latex/sdaps/sdapscode128.tex
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/sdaps/sdapslayout.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/sdaps/sdapslayout.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/sdaps/sdapslayout.pdf	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/texmf-dist/doc/latex/sdaps/sdapslayout.pdf	2019-12-16 21:42:56 UTC (rev 53147)

Property changes on: trunk/Master/texmf-dist/doc/latex/sdaps/sdapslayout.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/sdaps/sdapspdf.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/sdaps/sdapspdf.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/sdaps/sdapspdf.pdf	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/texmf-dist/doc/latex/sdaps/sdapspdf.pdf	2019-12-16 21:42:56 UTC (rev 53147)

Property changes on: trunk/Master/texmf-dist/doc/latex/sdaps/sdapspdf.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.dtx	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,1167 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%
+% 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 Benjamin Berg.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{sdapsarray.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<package>\ProvidesPackage{sdapsarray}
+%<*package>
+    [2015/04/10 v0.1 Initial version of SDAPS array package]
+%</package>
+%
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage{sdapsarray}[2015/04/10]
+%\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+  \DocInput{sdapsarray.dtx}
+\end{document}
+%</driver>
+% \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/01/14}{Initial version}
+%
+% \GetFileInfo{sdapsarray.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+% 
+%
+% \title{The \textsf{sdapsarray} package\thanks{This document
+%   corresponds to \textsf{sdapsarray}~\fileversion, dated \filedate.}}
+% \author{Benjamin Berg \\ \texttt{benjamin at sipsolutions.net}}
+%
+% \maketitle
+%
+% \section{Documentation}
+%
+% Please refer to \url{https://sdaps.org/class-doc} for documentation.
+%
+% \StopEventually{\PrintChanges\PrintIndex}
+%
+% \section{Implementation}
+%
+% This package uses the \LaTeX3 language internally, so we need to enable it.
+%    \begin{macrocode}
+% We need at least 2011-08-23 for \keys_set_known:nnN
+\RequirePackage{expl3}[2011/08/23]
+%\RequirePackage{xparse}
+\ExplSyntaxOn
+%    \end{macrocode}
+%
+% And we need a number of other packages.
+%    \begin{macrocode}
+\ExplSyntaxOff
+
+\RequirePackage{xparse}
+\RequirePackage{sdapsbase}
+
+
+\ExplSyntaxOn
+
+%    \end{macrocode}
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% \subsection{Tempfile handling}
+%
+% This is a bit weird, but under some conditions we need extended information
+% about each row in the document (for page break detection). As it makes little
+% to no sense to load all this information into memory at start we use two
+% temporary files instead. As these files should not change their content
+% for reruns (that would break e.g. latexmk) we copy the "tic" file into "toc"
+% so that "toc" can be read while "tic" is being re-written.
+% We then go on to define a macro which will read a single
+% line and return true if it is different from the previous line.
+% This is the indicator that a page/column break has happened and the
+% row header needs to be inserted.
+%
+%    \begin{macrocode}
+
+% This will change in early 2018, but the new code apparently does not
+% provide the old name of the definition.
+% i.e. this is a bad hack and should be removed latest in 2019 or so
+\cs_if_exist:NF \ior_str_get:NN { \cs_set_eq:Nc \ior_str_get:NN { ior_get_str:NN } }
+
+\bool_new:N \g__sdaps_array_info_open
+\bool_gset_false:N \g__sdaps_array_info_open
+\iow_new:N \g__sdaps_array_info_iow
+\ior_new:N \g__sdaps_array_info_ior
+
+\cs_generate_variant:Nn \int_set:Nn { Nf }
+\cs_generate_variant:Nn \coffin_join:NnnNnnnn { NnVNnVnn }
+
+\cs_new_protected_nopar:Nn \_sdaps_array_open_tmpfiles:
+{
+  % Guard against being executed multiple times
+  \bool_if:NF \g__sdaps_array_info_open {
+    \bool_gset_true:N \g__sdaps_array_info_open
+
+    % Also ensures toc file exists (i.e. is readable)
+    \iow_open:Nn \g__sdaps_array_info_iow { \c_sys_jobname_str .sdapsarraytoc }
+    \file_if_exist:nTF { \c_sys_jobname_str .sdapsarraytic } {
+      % Copy into toc file, then open that.
+      \ior_open:Nn \g__sdaps_array_info_ior { \c_sys_jobname_str .sdapsarraytic }
+
+      \ior_str_map_inline:Nn \g__sdaps_array_info_ior { \iow_now:Nn \g__sdaps_array_info_iow { ##1 } }
+      \ior_close:N \g__sdaps_array_info_ior
+
+      \ior_open:Nn \g__sdaps_array_info_ior { \c_sys_jobname_str .sdapsarraytoc }
+    } {
+    }
+    \iow_close:N \g__sdaps_array_info_iow
+
+    \iow_open:Nn \g__sdaps_array_info_iow { \c_sys_jobname_str .sdapsarraytic }
+  }
+}
+
+\tl_new:N \g__sdaps_array_last_row_tl
+\tl_gset_eq:NN \g__sdaps_array_last_row_tl \c_empty_tl
+
+\cs_new_protected_nopar:Nn \_sdaps_array_check_insert_header:N
+{
+  \bool_gset_eq:NN #1 \c_false_bool
+  \ior_if_eof:NF \g__sdaps_array_info_ior {
+    \ior_str_get:NN \g__sdaps_array_info_ior \l_tmpa_tl
+
+    \tl_if_eq:NNF \g__sdaps_array_last_row_tl \c_empty_tl {
+      \tl_if_eq:NNF \g__sdaps_array_last_row_tl \l_tmpa_tl {
+        \bool_gset_eq:NN #1 \c_true_bool
+      }
+    }
+    \tl_gset_eq:NN \g__sdaps_array_last_row_tl \l_tmpa_tl
+  }
+}
+
+%    \end{macrocode}
+%
+% \subsection{Initialization}
+%
+% Global definitions and penalty constant.
+%
+%    \begin{macrocode}
+
+\prop_new:N \g__sdaps_array_layouter_prop
+
+% XXX: Penalty in between rows. After the header a nobreak is inserted, but
+%      do we want special penalties elsewhere (preventing orphans/widows?)
+\int_new:N \g_sdaps_array_row_penalty_tl
+\int_gset:Nn \g_sdaps_array_row_penalty_tl { 10 }
+
+%    \end{macrocode}
+%
+% \subsection{Initialization}
+%
+% Define some routines to store width information for columns (across builds).
+%
+%    \begin{macrocode}
+
+\tl_new:N \g_sdaps_array_shared_data_tl
+\tl_new:N \g_sdaps_array_stored_data_tl
+\tl_new:N \g_sdaps_array_local_data_tl
+\tl_new:N \g_sdaps_array_local_data_new_tl
+\prop_new:N \g__sdaps_array_stored_data_prop
+\prop_new:N \g__sdaps_array_shared_data_prop
+\prop_new:N \g__sdaps_array_local_data_prop
+
+\cs_generate_variant:Nn \prop_item:Nn { NV }
+
+\cs_new_protected_nopar:Nn \_sdaps_array_load_data:
+{
+  \tl_gset:Nx \g_sdaps_array_stored_data_tl { \prop_item:NV \g__sdaps_array_stored_data_prop \l__sdaps_array_global_name_tl }
+  \tl_gset:Nx \g_sdaps_array_shared_data_tl { \prop_item:NV \g__sdaps_array_shared_data_prop \l__sdaps_array_global_name_tl }
+  \tl_gset:Nx \g_sdaps_array_local_data_tl { \prop_item:NV \g__sdaps_array_local_data_prop \l__sdaps_array_local_name_tl }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_store_data:
+{
+  % Do not overwrite the "stored" data that we have right now.
+  \prop_gput:NVV \g__sdaps_array_shared_data_prop \l__sdaps_array_global_name_tl \g_sdaps_array_shared_data_tl
+
+  \immediate\write\@auxout{\exp_not:n{\sdapsarrayloadstoreddata}{\l__sdaps_array_global_name_tl}{\g_sdaps_array_shared_data_tl}}
+  \tl_if_empty:NF \g_sdaps_array_local_data_new_tl {
+    \immediate\write\@auxout{\exp_not:n{\sdapsarrayloadlocaldata}{\l__sdaps_array_local_name_tl}{\g_sdaps_array_local_data_new_tl}}
+  }
+}
+
+% Define for loading sdaps code in aux file
+\cs_new_protected_nopar:Nn \sdaps_array_load_stored_data:nn {
+  \prop_gput:Nnn \g__sdaps_array_stored_data_prop { #1 } { #2 }
+}
+\cs_new_eq:NN \sdapsarrayloadstoreddata \sdaps_array_load_stored_data:nn
+
+% Define for loading sdaps code in aux file
+\cs_new_protected_nopar:Nn \sdaps_array_load_local_data:nn {
+  \prop_gput:Nnn \g__sdaps_array_local_data_prop { #1 } { #2 }
+}
+\cs_new_eq:NN \sdapsarrayloadlocaldata \sdaps_array_load_local_data:nn
+
+%    \end{macrocode}
+%
+% \subsection{Array Layouter}
+%
+% \subsubsection{User facing macros}
+%
+%    \begin{macrocode}
+
+\int_new:N \g__sdaps_array_current_id_int
+\tl_new:N \l__sdaps_array_global_name_tl
+\tl_new:N \l__sdaps_array_local_name_tl
+
+\cs_generate_variant:Nn \keys_set:nn { nf }
+\cs_new_protected_nopar:Nn \sdaps_array_begin:nn
+{
+ \tl_set:Nx \l__sdaps_array_local_name_tl { sdapsarray \int_use:N\g__sdaps_array_current_id_int }
+  \int_gincr:N \g__sdaps_array_current_id_int
+  \tl_if_empty:nTF { #2 } {
+    \tl_set:NV \l__sdaps_array_global_name_tl \l__sdaps_array_local_name_tl
+  } {
+    \tl_set:Nx \l__sdaps_array_global_name_tl { #2 }
+  }
+
+  \_sdaps_array_load_data:
+
+  \keys_set:nf { sdaps / array } { \prop_item:Nn \g__sdaps_array_layouter_prop { #1 } }
+
+  \_sdaps_array_open_tmpfiles:
+
+  \tl_gset_eq:NN \g__sdaps_array_last_row_tl \c_empty_tl
+  \bool_gset_true:N \g_sdaps_array_first_row_bool
+
+  \l__sdaps_array_begin_tl
+}
+\cs_generate_variant:Nn \sdaps_array_begin:nn { Vn }
+\cs_generate_variant:Nn \sdaps_array_begin:nn { VV }
+\cs_generate_variant:Nn \sdaps_array_begin:nn { nV }
+
+\cs_new_protected_nopar:Nn \sdaps_array_begin:n
+{
+  \sdaps_array_begin:nn { #1 } { }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_array_row_start:
+{
+  \l__sdaps_array_row_start_tl
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_assign_use: {
+    \box_use:N \l_tmpa_box
+  \egroup
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_assign_colhead:Nw #1
+{
+  \l__sdaps_array_colhead_tl #1
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_colhead:w
+{
+  \bgroup
+    \l__sdaps_array_colhead_tl \l_tmpa_box \bgroup
+    \group_insert_after:N\_sdaps_array_assign_use:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+\cs_new_protected:Npn \sdaps_array_assign_rowhead:Nw #1
+{
+  \l__sdaps_array_rowhead_tl #1
+}
+
+\cs_new_protected:Npn \sdaps_array_rowhead:w
+{
+  \bgroup
+    \l__sdaps_array_rowhead_tl \l_tmpa_box \bgroup
+    \group_insert_after:N\_sdaps_array_assign_use:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_assign_cell:Nw #1
+{
+  \l__sdaps_array_cell_tl #1
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_cell:w
+{
+  \bgroup
+    \l__sdaps_array_cell_tl \l_tmpa_box \bgroup
+    \group_insert_after:N\_sdaps_array_assign_use:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+% XXX: Could this live in local scope instead?
+\box_new:N \g__sdaps_array_header_box
+\dim_new:N \g__sdaps_array_header_dim
+
+\cs_new_protected_nopar:Nn \_sdaps_array_calc_interlineskip:nnN
+{
+  \dim_compare:nNnTF { #1 } > { -1000pt } {
+    \skip_set:Nn #3 { \baselineskip - #1 - #2 }
+    \dim_compare:nNnF { #3 } > { \lineskiplimit } {
+      \skip_set:Nn #3 { \lineskip }
+    }
+  } {
+    \skip_set:Nn #3 { 0pt }
+  }
+  \skip_set:Nn #3 { #3 + \l_sdaps_sdapsarray_rowsep_dim }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_array_row:NN
+{
+  \if_mode_vertical:
+  \else:
+    \msg_error:nn { sdapsarray } { wrong_mode }
+  \fi
+
+  % Pagebreak detection
+  \_sdaps_array_check_insert_header:N \g_tmpa_bool
+  % XXX: \l_tmpa_dim is the height to the first baseline in the box. Note that
+  %      we use the real baseline in the case of the header row!
+  \l__sdaps_array_row_tl #1 #2 \l_tmpb_box \l_tmpa_dim
+
+  \bool_if:NT \g_sdaps_array_first_row_bool {
+    % Stow away the box for later use (before rewriting it to save the position)
+    \box_gset_eq:NN \g__sdaps_array_header_box \l_tmpb_box
+    \dim_gset:Nn \g__sdaps_array_header_dim { \box_ht:N \g__sdaps_array_header_box + \box_dp:N \g__sdaps_array_header_box }
+  }
+
+  \hbox_set:Nn \l_tmpb_box {
+    \pdfsavepos
+    \iow_shipout_x:Nn \g__sdaps_array_info_iow {
+      \thepage,
+      \the\pdflastxpos
+    }
+    \box_use:N \l_tmpb_box
+  }
+
+  \bool_if:NTF \g_sdaps_array_first_row_bool {
+    \bool_gset_false:N \g_sdaps_array_first_row_bool
+
+    \box_use:N \l_tmpb_box
+    % Do not ever allow a break after the header line.
+    \nobreak
+  } {
+    % The idea is simple. Before every line the header is re-inserted (either
+    % the real one or an empty box with the same dimensions). In the case that
+    % there is *no* page break we insert a corresponding negative skip so that
+    % the resulting skip (including interline skip) is exactly the normal
+    % interline skip between the rows.
+    % In the case that the skip *is* discarded we end up with the header box
+    % and the normal interline skip between the new row and the header. i.e.:
+    %   skip = 1 * ( interlineskip_for_row - interlineskip_for_header - header_height - header_depth ) + header_height + header_depth + interlineksip_for_header
+    % or
+    %   skip = 0 * ( interlineskip_for_row - interlineskip_for_header - header_height - header_depth ) + header_height + header_depth + interlineksip_for_header
+
+    % Calculate interlineksip_for_row and interlineskip_for_header
+    \_sdaps_array_calc_interlineskip:nnN { \prevdepth } { \l_tmpa_dim } \l_tmpa_skip
+    \_sdaps_array_calc_interlineskip:nnN { \box_dp:N \g__sdaps_array_header_box } { \l_tmpa_dim } \l_tmpb_skip
+    \nointerlineskip
+    \skip_vertical:n { \l_tmpa_skip - \l_tmpb_skip }
+    \kern -\g__sdaps_array_header_dim
+
+    % Inser the real or fake box
+    \bool_if:NTF \g_tmpa_bool {
+      \box_use:N \g__sdaps_array_header_box
+    } {
+     \hrule height \box_ht:N \g__sdaps_array_header_box depth \box_dp:N \g__sdaps_array_header_box width 0pt
+    }
+    \nobreak
+    % Insert the calculated interline skip (in the same way the TeX would do it.
+    \nointerlineskip
+    \skip_vertical:N \l_tmpb_skip
+
+    \box_use:N \l_tmpb_box
+
+    % And insert the sdapsarray sepecific inter row penalty.
+    \penalty\int_use:N\g_sdaps_array_row_penalty_tl
+  }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_array_end:
+{
+  \l__sdaps_array_end_tl
+  \box_gclear:N \g__sdaps_array_header_box
+}
+
+%    \end{macrocode}
+%
+% \subsubsection{Common Layouter Macros}
+%
+%    \begin{macrocode}
+
+\tl_new:N \l__sdaps_array_begin_tl
+\tl_new:N \l__sdaps_array_row_start_tl
+\tl_new:N \l__sdaps_array_colhead_tl
+\tl_new:N \l__sdaps_array_rowhead_tl
+\tl_new:N \l__sdaps_array_cell_tl
+\tl_new:N \l__sdaps_array_row_tl
+\tl_new:N \l__sdaps_array_end_tl
+
+\keys_define:nn { sdaps / array }
+{
+  begin        .tl_set:N   = \l__sdaps_array_begin_tl,
+  row_start    .tl_set:N   = \l__sdaps_array_row_start_tl,
+  colhead      .tl_set:N   = \l__sdaps_array_colhead_tl,
+  rowhead      .tl_set:N   = \l__sdaps_array_rowhead_tl,
+  cell         .tl_set:N   = \l__sdaps_array_cell_tl,
+  row          .tl_set:N   = \l__sdaps_array_row_tl,
+  end          .tl_set:N   = \l__sdaps_array_end_tl,
+}
+
+
+\seq_new:N \g_sdaps_array_overhangs_left_seq
+\seq_new:N \g_sdaps_array_overhangs_right_seq
+\seq_new:N \g_sdaps_array_shared_colwidths_seq
+\seq_new:N \g_sdaps_array_stored_colwidths_seq
+
+\cs_new_protected_nopar:Npn \_sdaps_array_rowhead_default:Nw #1
+{
+  \tl_if_empty:NTF \g_sdaps_array_local_data_tl {
+      \tl_if_empty:NTF \g_sdaps_array_local_data_new_tl {
+        \dim_set:Nn \l_tmpa_dim { 0.5 \hsize }
+      } {
+        \dim_set:Nn \l_tmpa_dim { \g_sdaps_array_local_data_new_tl }
+      }
+  } {
+    \dim_set:Nn \l_tmpa_dim { \g_sdaps_array_local_data_tl }
+  }
+  % \vbox_set_top:Nw is still missing as of 2017-08-11
+  \tex_setbox:D #1 \tex_vtop:D \bgroup
+    \sdaps_if_rtl:TF {
+      \raggedright
+    } {
+      \raggedleft
+    }
+    \hsize=\dim_use:N\l_tmpa_dim
+    \group_begin:\bgroup
+    \group_insert_after:N \group_end:
+    \group_insert_after:N \egroup
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_array_cell_default:Nw #1
+{
+  \hbox_set:Nw #1 \bgroup
+    % swallow group opening token
+    \group_insert_after:N \hbox_set_end:
+    \tex_let:D\next=
+}
+
+\cs_new:Nn \_sdaps_array_cell_rotated_end: {
+    \hbox_set_end:
+    \dim_set:Nn \l_tmpa_dim { \box_ht:N \l_tmpa_box }
+    \dim_set:Nn \l_tmpb_dim { \box_dp:N \l_tmpa_box }
+
+    \dim_set:Nn \l_tmpa_dim { \l_sdaps_sdapsarray_angle_sine_tl \l_tmpa_dim }
+    \dim_set:Nn \l_tmpb_dim { \l_sdaps_sdapsarray_angle_sine_tl \l_tmpb_dim }
+
+    \box_rotate:Nn \l_tmpa_box { \l_sdaps_sdapsarray_angle_int }
+
+    % We want the baseline of the box to be centered, that only works if we
+    % leave the same space both ways.
+    % That is not ideal, but we cannot move the cell content accordingly.
+    \dim_set:Nn \l_tmpb_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+    \skip_horizontal:n { \l_tmpb_dim }
+    \rlap{
+      \skip_horizontal:n { -\l_tmpa_dim }
+      \box_use:N \l_tmpa_box
+    }
+    \skip_horizontal:n { \l_tmpb_dim }
+    % dummy skip that will be removed again by other code
+    \skip_horizontal:n { 0pt }
+
+    \dim_set:Nn \l_tmpa_dim { \l_tmpa_dim + \l_tmpb_dim }
+
+    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
+    \dim_set:Nn \l_tmpa_dim { \dim_max:nn { 0pt } { \l_tmpb_dim - \l_tmpa_dim } }
+
+    \seq_gpush:Nn \g_sdaps_array_overhangs_left_seq { 0pt }
+    \seq_gpush:Nx \g_sdaps_array_overhangs_right_seq { \dim_use:N \l_tmpa_dim }
+  \egroup
+  \hbox_set_end:
+}
+
+% Only sane for header row
+\cs_new_protected_nopar:Npn \_sdaps_array_cell_rotated:Nw #1
+{
+  \hbox_set:Nw #1 \bgroup
+    \hbox_set:Nw \l_tmpa_box
+    \bgroup
+    \group_insert_after:N \_sdaps_array_cell_rotated_end:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+% XXX: A parbox layouter with fixed width would be nice
+%\cs_new_protected_nopar:Nn \sdaps_array_cell_fixed:n {}
+
+
+\cs_new_protected_nopar:Nn \_sdaps_array_begin_default:
+{
+  \tl_if_empty:NTF \g_sdaps_array_shared_data_tl {
+    \seq_clear:N \g_sdaps_array_shared_colwidths_seq
+  } {
+    \seq_gset_split:NnV \g_sdaps_array_shared_colwidths_seq { ~ } \g_sdaps_array_shared_data_tl
+  }
+  \tl_if_empty:NTF \g_sdaps_array_stored_data_tl {
+    \seq_clear:N \g_sdaps_array_stored_colwidths_seq
+  } {
+    \seq_gset_split:NnV \g_sdaps_array_stored_colwidths_seq { ~ } \g_sdaps_array_stored_data_tl
+  }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_end_default:
+{
+  \tl_gset:Nx \g_sdaps_array_shared_data_tl { \seq_use:Nn \g_sdaps_array_shared_colwidths_seq { ~ } }
+  \tl_gset:Nx \g_sdaps_array_stored_data_tl { \seq_use:Nn \g_sdaps_array_stored_colwidths_seq { ~ } }
+
+  % Clear the global sequences, to save memory
+  \seq_gclear:N \g_sdaps_array_overhangs_left_seq
+  \seq_gclear:N \g_sdaps_array_overhangs_right_seq
+  \seq_gclear:N \g_sdaps_array_shared_colwidths_seq
+  \seq_gclear:N \g_sdaps_array_stored_colwidths_seq
+
+  \_sdaps_array_store_data:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_row_start_default:
+{
+  \seq_gclear:N \g_sdaps_array_overhangs_left_seq
+  \seq_gclear:N \g_sdaps_array_overhangs_right_seq
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_row:NNNN
+{
+  % #1: A vbox with baseline on the *first* item containing the row header
+  %     (\vtop in plain TeX).
+  % #2: Data cells packed into an hbox. Each of these needs to be set to the
+  %     correct width and inserted.
+  % #3: The box register to store the resulting hbox in. The depth of this box
+  %     needs to be correct to calculate the interline glue to the following
+  %     row.
+  % #4: A dim register to store the height of the box in for the purpose of
+  %     calculating the interline glue in front of the produced row.
+  %
+  % { \dim_use:N\@totalleftmargin } { \dim_use:N\linewidth }
+  % The macro should create an hbox which is exactly \linewidth wide and also
+  % contains internal indentation by \@totalleftmargin into the box register #3.
+  % The box will be used while in vertical mode and may% be inserted multiple
+  % times in the case of the header row.
+  % To simplify the iteration it is guaranteed that the data cell boxes are not
+  % completely empty. This means the code can simply unbox until it sees a box
+  % that is void.
+
+  \seq_gclear:N \g_tmpa_seq
+
+  % Insert the boxes into a local hbox to work with them
+  \hbox_set:Nn #2 {
+    \hbox_unpack:N #2
+
+    % Handle the overhang, note that we modify the \g_sdaps_array_overhangs_right_seq locally only!
+    \seq_pop:NNTF \g_sdaps_array_overhangs_right_seq \l_tmpa_tl {
+      \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl }
+    } {
+      \dim_set:Nn \l_tmpb_dim { 0pt }
+    }
+    % Implicit "last" column that contains the overhang
+    \seq_gpop:NNTF \g_sdaps_array_shared_colwidths_seq \l_tmpa_tl {
+      \dim_set:Nn \l_tmpa_dim { \l_tmpa_tl }
+    } {
+      \dim_set:Nn \l_tmpa_dim { 0pt }
+    }
+    \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+
+
+    % MAX with stored values (NOTE: sequence only modified in local scope)
+    \seq_pop:NNTF \g_sdaps_array_stored_colwidths_seq \l_tmpa_tl {
+      \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl }
+    } {
+      \dim_set:Nn \l_tmpb_dim { 0pt }
+    }
+    % Store value from this run, and then calculate max with previous run
+    \seq_gput_right:Nx \g_tmpa_seq { \dim_use:N \l_tmpa_dim }
+    \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+
+
+    % Insert the overhang space
+    \hbox_set:Nn \l_tmpa_box { \skip_horizontal:n { \l_tmpa_dim } }
+
+    % Now grab the first of the cells, and then loop over the rest
+    \box_set_to_last:N #2
+    \bool_do_while:nn { ! \box_if_empty_p:N #2 } {
+      % Strip any trailing glue (i.e. space) coming from the user (for the
+      % leading side we ensure that \tex_ignorespaces:D is called).
+      % Note that this may remove e.g. a trailing \hfill from the user, the
+      % user needs to work around that (in the same way as is required in e.g.
+      % tabular).
+      \hbox_set:Nn #2 { \hbox_unpack:N #2 \unskip }
+
+      % Pop the target width for the current box (i.e. we don't globally
+      % modify the clist here).
+      \seq_gpop:NNTF \g_sdaps_array_shared_colwidths_seq \l_tmpa_tl {
+        \dim_set:Nn \l_tmpa_dim { \l_tmpa_tl }
+      } {
+        \dim_set:Nn \l_tmpa_dim { 0pt }
+      }
+      % Calculate the maximum width of current and previous items
+      \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \box_wd:N #2 } { \l_tmpa_dim } }
+
+
+      % MAX with stored values (NOTE: sequence only modified in local scope)
+      \seq_pop:NNTF \g_sdaps_array_stored_colwidths_seq \l_tmpa_tl {
+        \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl }
+      } {
+        \dim_set:Nn \l_tmpb_dim { 0pt }
+      }
+      % Store value from this run, and then calculate max with previous run
+      \seq_gput_right:Nx \g_tmpa_seq { \dim_use:N \l_tmpa_dim }
+      \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+
+      % Set the box into a new box with the correct width which contains fil
+      % to center it.
+      \hbox_set_to_wd:Nnn \l_tmpb_box \l_tmpa_dim { \hfil \hbox_unpack:N #2 \hfil }
+
+      % This loops works backward, so attach the cell on the right side.
+      % We used to make sure that it is layed out in order, but that is now
+      % obsolete and doing it out of order is simpler in RTL mode.
+      \sdaps_if_rtl:TF {
+        % The boxes are shown on the page from LTR
+        \hbox_set:Nn \l_tmpa_box { \box_use:N \l_tmpa_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpb_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } }
+      } {
+        \hbox_set:Nn \l_tmpa_box { \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpb_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpa_box }
+      }
+
+      % Grab next cell
+      \box_set_to_last:N #2
+    }
+
+    % Get the coffin out of the nested scope by placing it into the box and
+    % placing that into it again ...
+    \box_use:N \l_tmpa_box
+  }
+  \hcoffin_set:Nn \l_tmpa_coffin { \box_use_drop:N #2 }
+
+  \seq_gconcat:NNN \g_sdaps_array_shared_colwidths_seq \g_tmpa_seq \g_sdaps_array_shared_colwidths_seq
+  \seq_gclear:N \g_tmpa_seq
+
+  % Calculate the space that is left for the header column
+  \dim_set:Nn \l_tmpa_dim { \linewidth - \coffin_wd:N \l_tmpa_coffin - 2\l_sdaps_sdapsarray_colsep_dim }
+  \tl_gset:Nx \g_sdaps_array_local_data_new_tl { \dim_use:N \l_tmpa_dim }
+
+  % TODO: The \hfil here is a hack to prevent a warning if the vbox is empty.
+  %       Unfortunately checking for an empty box does not work for some reason.
+  \dim_set:Nn \l_tmpb_dim { \box_ht:N #1 }
+  \sdaps_if_rtl:TF {
+    \hcoffin_set:Nn \l_tmpb_coffin { \hbox_to_wd:nn \l_tmpa_dim { \hfil \vbox:n { \vbox_unpack_clear:N #1 } } \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } }
+    \tl_set:Nn \l_tmpa_tl { l }
+    \tl_set:Nn \l_tmpb_tl { r }
+  } {
+    \hcoffin_set:Nn \l_tmpb_coffin { \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \hbox_to_wd:nn \l_tmpa_dim { \hfil \vbox:n { \vbox_unpack_clear:N #1 } } }
+    \tl_set:Nn \l_tmpa_tl { r }
+    \tl_set:Nn \l_tmpb_tl { l }
+  }
+  \dim_set:Nn \l_tmpa_dim { \coffin_ht:N \l_tmpb_coffin }
+
+  % If the first/last baseline differ then center the vbox, otherwise align the
+  % baseline with the cells
+  \dim_compare:nNnTF { \l_tmpa_dim } = { \l_tmpb_dim } {
+    \coffin_join:NnVNnVnn \l_tmpb_coffin { H } \l_tmpa_tl \l_tmpa_coffin { H } \l_tmpb_tl { \l_sdaps_sdapsarray_colsep_dim } { 0pt }
+    \dim_set:Nn #4 { \coffin_ht:N \l_tmpb_coffin }
+  } {
+    \coffin_join:NnVNnVnn \l_tmpb_coffin { vc } \l_tmpa_tl \l_tmpa_coffin { vc } \l_tmpb_tl { \l_sdaps_sdapsarray_colsep_dim } { 0pt }
+    % XXX: Assume that the header is higher than the content cells
+    \dim_set:Nn #4 { \l_tmpb_dim }
+  }
+
+  \hbox_set:Nn #3 { \skip_horizontal:N \@totalleftmargin \coffin_typeset:Nnnnn \l_tmpb_coffin { H } { l } { 0pt } { 0pt } }
+}
+
+
+\prop_gput:Nnn \g__sdaps_array_layouter_prop { default } {
+  begin = { \_sdaps_array_begin_default: },
+  row_start = { \_sdaps_array_row_start_default: },
+  rowhead = { \_sdaps_array_rowhead_default:Nw },
+  colhead = { \_sdaps_array_cell_default:Nw },
+  cell = { \_sdaps_array_cell_default:Nw },
+  row = { \_sdaps_array_row:NNNN },
+  end = { \_sdaps_array_end_default: },
+}
+
+
+\prop_gput:Nnn \g__sdaps_array_layouter_prop { rotated } {
+  begin = { \_sdaps_array_begin_default: },
+  row_start = { \_sdaps_array_row_start_default: },
+  rowhead = { \_sdaps_array_rowhead_default:Nw },
+  colhead = { \_sdaps_array_cell_rotated:Nw },
+  cell = { \_sdaps_array_cell_default:Nw },
+  row = { \_sdaps_array_row:NNNN },
+  end = { \_sdaps_array_end_default: },
+}
+
+%    \end{macrocode}
+%
+% \subsection{Exporting a tabular/array like environment}
+%
+% \subsubsection{Helper required for the environment}
+%
+%    \begin{macrocode}
+
+\bool_new:N \g_sdaps_array_first_row_bool
+\bool_new:N \l__sdaps_sdapsarray_in_top_group_bool
+\bool_new:N \l__sdaps_sdapsarray_have_content_bool
+\bool_new:N \l_sdaps_sdapsarray_flip_bool
+\tl_new:N \l_sdaps_sdapsarray_layouter_tl
+\tl_new:N \l_sdaps_sdapsarray_align_tl
+\bool_new:N \l_sdaps_sdapsarray_keepenv_bool
+\int_new:N \l_sdaps_sdapsarray_angle_int
+\tl_new:N \l_sdaps_sdapsarray_angle_sine_tl
+\dim_new:N \l_sdaps_sdapsarray_colsep_dim
+\dim_new:N \l_sdaps_sdapsarray_rowsep_dim
+
+\keys_define:nn { sdaps / sdapsarray }
+{
+  flip       .bool_set:N   = \l_sdaps_sdapsarray_flip_bool,
+  flip       .initial:n  = false,
+  flip       .default:n  = true,
+  layouter   .tl_set:N   = \l_sdaps_sdapsarray_layouter_tl,
+  layouter   .initial:n  = default,
+  align      .tl_set:N   = \l_sdaps_sdapsarray_align_tl,
+  align      .initial:n  = { },
+  keepenv    .bool_set:N   = \l_sdaps_sdapsarray_keepenv_bool,
+  keepenv    .initial:n  = false,
+  keepenv    .default:n  = true,
+
+  angle          .code:n     = {
+    \int_set:Nn \l_sdaps_sdapsarray_angle_int {#1}
+    \tl_set:Nx \l_sdaps_sdapsarray_angle_sine_tl { \fp_to_decimal:n {sind(#1)}}
+  },
+  angle          .initial:n  = 70,
+  colsep         .dim_set:N  = \l_sdaps_sdapsarray_colsep_dim,
+  colsep         .initial:n  = 6pt,
+  rowsep         .dim_set:N  = \l_sdaps_sdapsarray_rowsep_dim,
+  rowsep         .initial:n  = 0pt,
+}
+
+
+%    \end{macrocode}
+%
+% \subsubsection{Environment definition}
+%
+%    \begin{macrocode}
+
+\cs_new_nopar:Nn \l_sdaps_sdapsarray_alignment_set_have_content:
+{
+  \bool_set_true:N\l__sdaps_sdapsarray_have_content_bool
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_alignment: {
+    % End the last group, which will be the group that was begun earlier.
+    % If the earlier cell was the first one, then this egroup also starts the
+    % hbox to temporarily store the cells.
+  \egroup
+  \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+    \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { an~alignment~tab }
+  }
+
+  % We need to notify the outside scope that there are items, will be inserted
+  % multiple times, but that does not matter.
+  \group_insert_after:N\l_sdaps_sdapsarray_alignment_set_have_content:
+
+  % Just in case someone leaked a change into our scope
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \cs_set_eq:NN \cr \_sdaps_sdapsarray_newline:
+    \cs_set_eq:NN \\ \cr
+  }
+
+  % Define a cell now, we can just put everything into a new cell, and that
+  % should work fine.
+  % Note that cells are not safe for fragile commands at the moment.
+  \_sdaps_sdapsarray_cell:w \bgroup
+    \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+    \cs_set_eq:NN \\ \cr
+    \tex_ignorespaces:D
+}
+
+\msg_new:nnn { sdapsarray } { unmatched_grouping_level } { The~grouping~level~of~a~cell~was~not~even.~Please~ensure~all~braces~are~balanced~out!~This~error~occured~at~#1. }
+\msg_new:nnn { sdapsarray } { unequal_cols } { The~number~of~columns~is~not~equal~for~all~rows. }
+\msg_new:nnn { sdapsarray } { no_new_line_at_end } { You~have~terminated~the~last~line~with~\textbackslash\textbackslash~or~similar.~This~can~have~side~effects,~please~remove~it. }
+\msg_new:nnn { sdapsarray } { wrong_mode } { The~sdapsarray~environment~can~only~function~in~vertical~mode~(both~inner~and~outer). }
+
+\cs_new:Nn \_sdaps_sdapsarray_start_cells: {
+    \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+      \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~a~row~header }
+    }
+  \egroup
+
+  % We are in the environment scope again here
+
+  % Notify code that we are going to generate cells for a new row.
+  \sdaps_array_row_start:
+
+  % Start an hbox to stow away the cells.
+  % The rest of the setup happens in the alignment handler
+  \hbox_set:Nw \l_tmpb_box \bgroup
+    \group_insert_after:N \hbox_set_end:
+    \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_linestart: {
+  \sdaps_array_assign_rowhead:Nw \l_tmpa_box
+    \bgroup
+      \cs_set_eq:NN \\ \cr
+
+      \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool
+      \bgroup
+        \group_insert_after:N \_sdaps_sdapsarray_start_cells:
+        \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+        % Ignore following spaces by the user
+        \tex_ignorespaces:D
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_newline: {
+    \egroup
+    \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+      \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~a~row }
+    }
+  \egroup
+
+  % We are in the environment scope again here
+  % Output the last line if the cells were non-empty.
+  \bool_if:NT \l__sdaps_sdapsarray_have_content_bool {
+    \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box
+  }
+  \bool_set_false:N \l__sdaps_sdapsarray_have_content_bool
+
+  \cs_set_eq:NN \_sdaps_sdapsarray_cell:w \sdaps_array_cell:w
+  \_sdaps_sdapsarray_linestart:
+}
+
+\cs_new:Npn\sdaps_array_nested_alignenv: {
+  \char_set_catcode_alignment:N &
+  \cs_set_eq:NN \cr \sdaps_orig_cr
+  \cs_set_eq:NN \\ \sdaps_orig_backslash
+}
+
+\cs_new:Npn\sdaps_array_nested_alignenv:w {
+  \bgroup
+    \sdaps_array_nested_alignenv:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+\cs_new_eq:NN \sdapsnested \sdaps_array_nested_alignenv:w
+
+\group_begin:
+\char_set_catcode_active:N &
+\cs_new:Nn \_sdaps_sdapsarray_defines: {
+  \cs_set_eq:NN \sdaps_orig_cr \cr
+  \cs_set_eq:NN \sdaps_orig_backslash \\
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \char_set_catcode_active:N &
+    \cs_set_eq:NN \cr \sdaps_array_newline:
+    \cs_set_eq:NN \\ \cr
+    \cs_set_eq:NN & \sdaps_array_alignment:
+  }
+}
+\group_end:
+
+%%%%%%
+% Flipping environment
+%%%%%%
+
+% First some helpers
+
+\box_new:N \l_sdaps_sdapsarray_headers_box
+\box_new:N \l_sdaps_sdapsarray_boxlist_head_box
+\box_new:N \l_sdaps_sdapsarray_boxlist_tail_box
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_prepend_box:NN {
+  \hbox_set:Nn #2 {
+    \box_use:N #1
+    \hbox_unpack:N #2
+  }
+  \box_clear:N #1
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_append_box:NN {
+  \hbox_set:Nn #2 {
+    \hbox_unpack:N #2
+    \box_use:N #1
+  }
+  \box_clear:N #1
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_pop_last_box:NN {
+  \hbox_set:Nn #2 {
+    \hbox_unpack:N #2
+    \box_gset_to_last:N \g_tmpa_box
+  }
+  \box_set_eq:NN #1 \g_tmpa_box
+  \box_gclear:N \g_tmpa_box
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_pop_last_hbox_unpack:NN {
+  \_sdaps_sdapsarray_pop_last_box:NN #1 #2
+  \hbox_set:Nn #1 {
+    \hbox_unpack:N #1
+    \box_gset_to_last:N \g_tmpa_box
+  }
+  \box_set_eq:NN #1 \g_tmpa_box
+  \box_gclear:N \g_tmpa_box
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_boxlist_void_if_empty:N {
+  \hbox_set:Nn #1 {
+    \hbox_unpack:N #1
+    \box_set_to_last:N #1
+    \box_if_empty:NTF #1 {
+      \bool_gset_true:N \g_tmpa_bool
+    } {
+      \box_use:N #1
+      \bool_gset_false:N \g_tmpa_bool
+    }
+  }
+  \bool_if:NT \g_tmpa_bool {
+    \box_clear:N #1
+  }
+}
+
+% Lets say we are in row 4, cell 2 as defined in the environment, so the actual
+% position is 2, 4. Minus the row headers, this gives us 2, 3.
+% This means we need to append the cell to the box 3rd last box.
+% In that case we have to append the cell
+
+\cs_new_protected_nopar:Nn \_sdaps_sdapsarray_alignment_flip:
+{
+  \_sdaps_sdapsarray_end_cell_flip:
+
+  % Just in case someone leaked a change into our scope
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \cs_set_eq:NN \cr \_sdaps_sdapsarray_newline_flip:
+    \cs_set_eq:NN \\ \cr
+  }
+
+  % Next up is either a cell or a row header. We can figure that out by checking
+  % that the row headings box is void
+  \box_if_empty:NTF \l_sdaps_sdapsarray_headers_box {
+    \sdaps_array_assign_rowhead:Nw \l_tmpa_box \bgroup
+      \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+      \cs_set_eq:NN \\ \cr
+      % Ignore following spaces by the user
+      \tex_ignorespaces:D
+  } {
+    \sdaps_array_assign_cell:Nw \l_tmpa_box \bgroup
+      \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+      \cs_set_eq:NN \\ \cr
+      \tex_ignorespaces:D
+  }
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_end_cell_flip: {
+  \egroup % Finish of the cell
+  \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+    \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { end~of~cell~or~row }
+  }
+
+  % Get last box from head
+  \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box
+  % Append the new box to the list of boxes for this row
+  \_sdaps_sdapsarray_append_box:NN \l_tmpa_box \l_tmpb_box
+  % Prepend the new box to the tail
+  \_sdaps_sdapsarray_prepend_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_tail_box
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_end_line_flip: {
+  % At the end of the line, move tail into head.
+  % First check that head is empty.
+  \_sdaps_sdapsarray_boxlist_void_if_empty:N \l_sdaps_sdapsarray_boxlist_head_box
+  \box_if_empty:NF \l_sdaps_sdapsarray_boxlist_head_box {
+    \msg_error:nn { sdapsarray } { unequal_cols }
+  }
+  \box_set_eq:NN \l_sdaps_sdapsarray_boxlist_head_box \l_sdaps_sdapsarray_boxlist_tail_box
+  \box_clear:N \l_sdaps_sdapsarray_boxlist_tail_box
+
+  % If this was the first row, store it away, these are the headings.
+  \box_if_empty:NT \l_sdaps_sdapsarray_headers_box {
+    \box_set_eq:NN \l_sdaps_sdapsarray_headers_box \l_sdaps_sdapsarray_boxlist_head_box
+    \box_clear:N \l_sdaps_sdapsarray_boxlist_head_box
+  }
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_newline_flip: {
+  \_sdaps_sdapsarray_end_cell_flip:
+  \_sdaps_sdapsarray_end_line_flip:
+
+  % Create next box to store away, this has to be a colhead at this point
+  \sdaps_array_assign_colhead:Nw \l_tmpa_box \bgroup
+    \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+    \cs_set_eq:NN \\ \cr
+    \tex_ignorespaces:D
+}
+
+
+\NewDocumentEnvironment { sdapsarray } { o }
+{
+  \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+  \group_begin:
+
+    \tl_set:Nn \l_tmpa_tl { }
+    \IfNoValueF { #1 } {
+      \tl_set:Nn \l_tmpa_tl { #1 }
+    }
+
+    \box_clear:N \l_tmpa_box
+    \box_clear:N \l_tmpb_box
+
+    % Ensure vertical mode.
+    \tex_par:D
+    \if_mode_vertical:
+    \else:
+      \msg_error:nn { sdapsarray } { wrong_mode }
+    \fi
+
+    % This needs to be initialized here as otherwise the values would be
+    % expanded at import time.
+    \keys_set:nV { sdaps / sdapsarray } \l_tmpa_tl
+
+    \sdaps_array_begin:VV \l_sdaps_sdapsarray_layouter_tl \l_sdaps_sdapsarray_align_tl
+
+    % Note, this environment is fragile; we redefine & to be active.
+    % One can go back into normal mode by using \sdapsnested{} though.
+
+    \bool_if:NTF \l_sdaps_sdapsarray_flip_bool {
+      \cs_set_eq:NN \sdaps_array_newline: \_sdaps_sdapsarray_newline_flip:
+      \cs_set_eq:NN \sdaps_array_alignment: \_sdaps_sdapsarray_alignment_flip:
+
+      \_sdaps_sdapsarray_defines:
+
+      % Two hboxes to hold the content, note that 
+      % a: row headers, b: cells/col headers
+      \box_clear:N \l_sdaps_sdapsarray_headers_box
+      \box_clear:N \l_sdaps_sdapsarray_boxlist_head_box
+      \box_clear:N \l_sdaps_sdapsarray_boxlist_tail_box
+
+      % not sure why we need this group, but nothing works without it
+      \bgroup
+        % This is a bit creative to say the least
+        \sdaps_array_row_start:
+
+        \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool
+        \sdaps_array_assign_rowhead:Nw \l_tmpa_box \bgroup
+          \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+          \cs_set_eq:NN \\ \cr
+          % Ignore following spaces by the user
+          \tex_ignorespaces:D
+    } {
+      \cs_set_eq:NN \sdaps_array_newline: \_sdaps_sdapsarray_newline:
+      \cs_set_eq:NN \sdaps_array_alignment: \_sdaps_sdapsarray_alignment:
+      \_sdaps_sdapsarray_defines:
+
+      \cs_set_eq:NN \_sdaps_sdapsarray_cell:w \sdaps_array_colhead:w
+      \_sdaps_sdapsarray_linestart:
+    }
+
+  % If we redefine &, then the next character might have the wrong catcode
+  % (i.e. it could still be an alignment character). Execute the alignment
+  % code directly if the next character is &.
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \peek_charcode_remove:NT & { \sdaps_array_alignment: }
+  }
+}
+{
+    \bool_if:NTF \l_sdaps_sdapsarray_flip_bool {
+        \_sdaps_sdapsarray_end_cell_flip:
+
+        % At this point we should have swallowed all items from the head list.
+        % If not, then someone likely add a stray \\ command or similar
+        \_sdaps_sdapsarray_boxlist_void_if_empty:N \l_sdaps_sdapsarray_boxlist_head_box
+        \box_if_empty:NTF \l_sdaps_sdapsarray_boxlist_head_box {
+          \_sdaps_sdapsarray_end_line_flip:
+        } {
+          \msg_error:nn { sdapsarray } { no_new_line_at_end }
+        }
+
+        % Now we can have fun!
+        % Pop cells and heading, until we cannot find any new ones.
+        \_sdaps_sdapsarray_pop_last_hbox_unpack:NN \l_tmpa_box \l_sdaps_sdapsarray_headers_box
+        \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box
+        \bool_do_while:nn { ! \box_if_empty_p:N \l_tmpa_box || ! \box_if_empty_p:N \l_tmpb_box } {
+          \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box
+
+          \sdaps_array_row_start:
+
+          \_sdaps_sdapsarray_pop_last_hbox_unpack:NN \l_tmpa_box \l_sdaps_sdapsarray_headers_box
+          \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box
+        }
+
+      \egroup
+    } {
+        \egroup
+
+        \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+          \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~the~environment }
+        }
+      \egroup
+
+      % We are in the environment scope again here
+      % Output the last line if the cells were non-empty.
+      \bool_if:NT \l__sdaps_sdapsarray_have_content_bool {
+        \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box
+      }
+    }
+
+    \sdaps_array_end:
+
+  \group_end:
+}
+
+
+
+
+\ExplSyntaxOff
+
+%
+%    \end{macrocode}
+%
+
+% \Finale
+\endinput


Property changes on: trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.ins	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapsarray.ins	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,55 @@
+%%
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%%
+%% 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 Benjamin Berg.
+%%
+
+\input l3docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/cskeleton}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+
+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.
+
+\endpreamble
+
+\generate{\file{sdapsarray.sty}{\from{sdapsarray.dtx}{package}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{*                                                           *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX:                    *}
+\Msg{*                                                           *}
+\Msg{*     sdapsarray.cls                                        *}
+\Msg{*                                                           *}
+\Msg{* To produce the documentation run the file sdapsarray.dtx  *}
+\Msg{* through LaTeX.                                            *}
+\Msg{*                                                           *}
+\Msg{* Happy TeXing!                                             *}
+\Msg{*                                                           *}
+\Msg{*************************************************************}
+
+\endbatchfile

Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.dtx	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,1803 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%
+% 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 Benjamin Berg.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{sdapsbase.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<package>\ProvidesPackage{sdapsbase}
+%<*package>
+    [2015/01/14 v0.1 Initial version of SDAPS base package]
+%</package>
+%
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage{sdapsbase}[2015/01/14]
+%\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+  \DocInput{sdapsbase.dtx}
+\end{document}
+%</driver>
+% \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/01/14}{Initial version}
+%
+% \GetFileInfo{sdapsbase.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+% 
+%
+% \title{The \textsf{sdapsbase} package\thanks{This document
+%   corresponds to \textsf{sdapsbase}~\fileversion, dated \filedate.}}
+% \author{Benjamin Berg \\ \texttt{benjamin at sipsolutions.net}}
+%
+% \maketitle
+%
+% \section{Documentation}
+%
+% Please refer to \url{https://sdaps.org/class-doc} for documentation.
+%
+% \StopEventually{\PrintChanges\PrintIndex}
+%
+% \section{Implementation}
+%
+% This package uses the \LaTeX3 language internally, so we need to enable it.
+%    \begin{macrocode}
+% We need at least 2011-08-23 for \keys_set_known:nnN
+\RequirePackage{expl3}[2011/08/23]
+%\RequirePackage{xparse}
+\ExplSyntaxOn
+%    \end{macrocode}
+%
+% And we need a number of other packages.
+%    \begin{macrocode}
+\ExplSyntaxOff
+
+\input{sdapscode128}
+
+\RequirePackage{qrcode}
+
+\RequirePackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{positioning}
+\usetikzlibrary{decorations.pathmorphing}
+\ExplSyntaxOn
+
+
+% Define aliases for code128 functions, and generate variants
+\cs_new_eq:NN \code_render:n \code
+\cs_generate_variant:Nn \code_render:n { x, V }
+
+% Also define an alias for the qrcode renderer
+\cs_new_protected_nopar:Nn \qrcode_render:nn {
+  \qrcode[#1] {#2}
+}
+\cs_generate_variant:Nn \qrcode_render:nn { nx, nV }
+
+
+% Define a method to check for RTL languages, as the relevant commands
+% may not always be defined.
+\prg_new_conditional:Npnn \sdaps_if_rtl: { p, T, F, TF }
+{
+  \cs_if_exist:cTF { if at RTL } {
+    \tl_use:c { if at RTL }
+      \prg_return_true:
+    \else
+      \prg_return_false:
+    \fi
+  } {
+    \prg_return_false:
+  }
+}
+
+%    \end{macrocode}
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% \subsection{Context Handling}
+%
+% When creating complex questionnaires we need a mechnism to handle the current
+% context. By choice this mechanism works in global scope as items inside a
+% nested environment (e.g. multicol) need to have an effect on items outside
+% the environment. Because of this, we implement our own context, which works
+% similar to TeX groups containing local definitions.
+%
+%    \begin{macrocode}
+
+\cs_generate_variant:Nn \tl_if_eq:nnTF { Vn }
+\cs_generate_variant:Nn \tl_if_eq:nnT { Vn }
+\cs_generate_variant:Nn \tl_if_eq:nnF { Vn }
+\cs_generate_variant:Nn \int_if_odd:nTF { V }
+\cs_generate_variant:Nn \int_if_odd:nF { V }
+\cs_generate_variant:Nn \int_if_odd:nT { V }
+\cs_generate_variant:Nn \tl_set:Nn { Nv }
+\cs_generate_variant:Nn \msg_error:nnn { nnV }
+\cs_generate_variant:Nn \exp_not:n { f }
+
+
+\tl_new:N \l__sdaps_tmpa_tl
+\tl_new:N \l__sdaps_tmpb_tl
+
+\dim_new:N \l__sdaps_tmpa_dim %
+\dim_new:N \l__sdaps_tmpb_dim %
+
+\prop_new:N \g__sdaps_current_context_prop
+\tl_new:N \g__sdaps_current_context_id_tl
+
+\tl_new:N \g__sdaps_current_context_tl
+
+\seq_new:N \g__sdaps_context_ids_seq
+\seq_new:N \g__sdaps_contexts_seq
+
+% Global metadata write enable variable managed by context
+\bool_new:N \g_sdaps_write_enable_bool
+\bool_gset_false:N \g_sdaps_write_enable_bool
+
+\cs_new_protected_nopar:Nn \_sdaps_context_to_tl:N
+{
+  \tl_set:Nx #1 {_write_enable=\bool_if:NTF\g_sdaps_write_enable_bool{\c_true_bool}{\c_false_bool}}
+  \prop_map_inline:Nn \g__sdaps_current_context_prop {
+    % Could we remove some of the braces in the TL?
+    \tl_if_eq:nnTF { \undefined } { ##2 } {
+      \tl_put_right:Nn #1 {,{##1}}
+    } {
+      \tl_put_right:Nn #1 {,{##1}={##2}}
+    }
+  }
+}
+
+
+% Create new context using given identifier
+\cs_new_protected_nopar:Nn \sdaps_context_begin:n
+{
+  % We need to serialize the current context and save it away.
+
+  \group_begin:
+    % Serialize the current context
+    \_sdaps_context_to_tl:N \l__sdaps_tmpa_tl
+    \tl_gset:NV \g__sdaps_current_context_tl \l__sdaps_tmpa_tl
+    % Stuff it away in our sequence
+    \seq_gput_left:NV \g__sdaps_contexts_seq \g__sdaps_current_context_tl
+    \seq_gput_left:NV \g__sdaps_context_ids_seq \g__sdaps_current_context_id_tl
+
+    % Clear the hooks
+    \sdaps_context_put:nn { _context_hook_end } {}
+    \sdaps_context_put:nn { _context_hook_post_end } {}
+
+    \tl_gset:Nn \g__sdaps_current_context_id_tl { #1 }
+  \group_end:
+}
+
+\msg_new:nnn { sdapsbase } { context_end_none_left } { There ~ is ~ no ~ context ~ to ~ end ~ left! }
+\msg_new:nnn { sdapsbase } { context_end_broken } { The ~ current ~ context ~ with ~ id ~ #1 ~ may ~ not ~ be ~ ended ~ here. }
+
+\cs_new_protected_nopar:Nn \__sdaps_context_end:
+{
+  \seq_if_empty:NTF \g__sdaps_context_ids_seq {
+    \msg_error:nn { sdapsbase } { context_end_none_left }
+  } {
+
+    \group_begin:
+    \sdaps_context_get:nN { _context_hook_end } \l_tmpa_tl
+    \tl_if_eq:VnF \l_tmpa_tl { \q_no_value } { \tl_use:N \l_tmpa_tl }
+
+    % Grab post end hook
+    \sdaps_context_get:nN { _context_hook_post_end } \l_tmpa_tl
+
+    \_sdaps_context_clear:
+    \seq_gpop_left:NN \g__sdaps_contexts_seq \g__sdaps_current_context_tl
+    \seq_gpop_left:NN \g__sdaps_context_ids_seq \l__sdaps_tmpa_tl
+
+    % Unpack context token list
+    \sdaps_context_set:V \g__sdaps_current_context_tl
+
+    \sdaps_context_get:nN { _write_enable } \l_tmpb_tl
+    \bool_gset:Nn \g_sdaps_write_enable_bool { \l_tmpb_tl }
+    \sdaps_context_remove:n { _write_enable }
+
+    \tl_gclear:N \g__sdaps_current_context_tl
+    \tl_gset:NV \g__sdaps_current_context_id_tl \l__sdaps_tmpa_tl
+
+    \tl_if_eq:VnF \l_tmpa_tl { \q_no_value } { \tl_use:N \l_tmpa_tl }
+    \group_end:
+  }
+}
+
+\bool_new:N \l__sdaps_tmp_bool
+
+\cs_new_protected_nopar:Nn \__sdaps_test_context_id:n
+{
+  \tl_if_eq:VnTF \g__sdaps_current_context_id_tl { #1 } {
+    \bool_set:Nn \l__sdaps_tmp_bool \c_true_bool
+  } {
+    \bool_set:Nn \l__sdaps_tmp_bool \c_false_bool
+  }
+}
+
+% Exit first context with passed in identifier
+\cs_new_protected_nopar:Nn \sdaps_context_end:n
+{
+  \__sdaps_test_context_id:n { #1 }
+
+  \bool_until_do:nn { \l__sdaps_tmp_bool } {
+    \sdaps_context_end:
+
+    \__sdaps_test_context_id:n { #1 }
+  }
+  \sdaps_context_end:
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_context_end_local_scope:
+{
+  \__sdaps_test_context_id:n { sdaps_local_scope }
+
+  \bool_until_do:nn { \l__sdaps_tmp_bool } {
+    \__sdaps_context_end:
+
+    \__sdaps_test_context_id:n { sdaps_local_scope }
+  }
+  \__sdaps_context_end:
+}
+
+% Exit current context
+\cs_new_protected_nopar:Nn \sdaps_context_end:
+{
+  % Ensure the current context is not a local group
+  \tl_if_eq:VnTF \g__sdaps_current_context_id_tl { sdaps_local_scope } {
+    \msg_error:nnV { sdapsbase } { context_end_broken } \g__sdaps_current_context_id_tl
+  } {}
+
+  \__sdaps_context_end:
+}
+
+% Create new context using an empty name
+\cs_new_protected_nopar:Nn \sdaps_context_begin:
+{
+  \sdaps_context_begin:n {}
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_begin_local:
+{
+  % Create a new context which will automatically be destroyed at the end of
+  % the current TeX group.
+  \sdaps_context_begin:n { sdaps_local_scope }
+  \group_insert_after:N \__sdaps_context_end_local_scope:
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_put:n
+{
+  \sdaps_context_put:nn { #1 } { \undefined }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_remove:n
+{
+  \prop_gremove:Nn \g__sdaps_current_context_prop { #1 }
+}
+
+\msg_new:nnn { sdapsbase } { context_key_broken } { Keys ~ may ~ not ~ contain ~ any ~ special ~ tokens! ~ However ~ the ~ key ~ #1 ~ does ~ contain ~ tokens ~ that ~ are ~ not ~ permissible! }
+% Directly set a certain key
+\cs_new_protected_nopar:Nn \sdaps_context_put:nn
+{
+  % TODO: How can I ensure that {} are not contained?
+  % Though it would not be that bad actually.
+  \tl_if_in:nnTF {#1} {,} {
+    \msg_error:nnn { sdapsbase } { context_key_broken } {#1}
+  } {
+  }
+
+  \tl_if_in:nnTF {#1} {=} {
+    \msg_error:nnn { sdapsbase } { context_key_broken } {#1}
+  } {
+  }
+
+  \prop_gput:Nnn \g__sdaps_current_context_prop { #1 } { #2 }
+}
+\cs_generate_variant:Nn \sdaps_context_put:nn { nV }
+
+% Set a set of keys using comma separated list of key/value pairs
+\cs_new_protected_nopar:Nn \sdaps_context_set:n
+{
+  \keyval_parse:NNn \sdaps_context_put:n \sdaps_context_put:nn { #1 }
+}
+\cs_generate_variant:Nn \sdaps_context_set:n {V}
+
+\cs_new_protected_nopar:Nn \sdaps_context_get:nN
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #1 } #2
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_gget:nN
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #1 } \l_tmpa_tl
+  \tl_gset_eq:NN #2 \l_tmpa_tl
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_append:nnn
+{
+  \sdaps_context_get:nN { #1 } \l__sdaps_tmpa_tl
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+    \sdaps_context_put:nn { #1 } { #2 }
+  } {
+    \tl_put_right:Nn \l__sdaps_tmpa_tl { #3 }
+    \tl_put_right:Nn \l__sdaps_tmpa_tl { #2 }
+    \sdaps_context_put:nV { #1 } \l__sdaps_tmpa_tl
+  }
+}
+\cs_generate_variant:Nn \sdaps_context_append:nnn { nVn }
+
+
+\cs_new_protected_nopar:Nn \sdaps_context_append:nn
+{
+  \sdaps_context_append:nnn { #1 } { #2 } { , }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_hook_end:n
+{
+  \sdaps_context_append:nnn { _context_hook_end } { #1 } { }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_hook_post_end:n
+{
+  \sdaps_context_append:nnn { _context_hook_post_end } { #1 } { }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_enable_writing:
+{
+  \bool_gset_true:N \g_sdaps_write_enable_bool
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_disable_writing:
+{
+  \bool_gset_false:N \g_sdaps_write_enable_bool
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_context_clear:
+{
+  \prop_gclear:N \g__sdaps_current_context_prop
+}
+
+\cs_new:Nn \sdaps_context_map_function:N
+{
+  \prop_map_function:NN \g__sdaps_current_context_prop #1
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_get:Nn
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #2 } #1
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_get_empty:Nn
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #2 } #1
+  \tl_if_eq:VnT #1 { \q_no_value } {
+    \tl_set:Nn #1 {}
+  }
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_append_from_context:nN
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #1 } \l__sdaps_tmpb_tl
+  \tl_if_eq:VnF \l__sdaps_tmpb_tl { \q_no_value } {
+    \tl_put_right:Nn #2 {,}
+    \tl_put_right:NV #2 {\l__sdaps_tmpb_tl}
+  }
+}
+\cs_generate_variant:Nn \__sdaps_append_from_context:nN { VN }
+
+
+%    \end{macrocode}
+%
+% \subsection{Defining Question and Headings}
+%
+% SDAPS needs to know about questions and headings/sections. Internally SDAPS
+% uses the context management system to number these correctly and assign the
+% variable names including the variable name concatenation automatically.
+%
+% Note that the infrastructure present here will not prevent you from nesting
+% questions, and SDAPS should actually handle this case just fine (ever wanted
+% to put a question inside a textbox?).
+%
+% It is important to keep these balanced. Please note that the SDAPS class does
+% not use TeX groups here, so you could for example start a context inside a
+% table and end it outside of it.
+%
+%    \begin{macrocode}
+
+\seq_new:N \g__sdaps_object_id_seq
+\int_new:N \g__sdaps_object_id_int
+\int_gzero:N \g__sdaps_object_id_int
+
+\cs_new_protected_nopar:Nn \_sdaps_qobject_end_hook:
+{
+  % Take the current implicit variable
+  \sdaps_context_get:nN { _implicit_var } \l__sdaps_tmpa_tl
+
+  % Prepend explicit variable name; we assume that either _implicit_var or _var
+  % have a proper value.
+  \sdaps_context_get:nN { _var } \l__sdaps_tmpb_tl
+  \tl_if_eq:VnF \l__sdaps_tmpb_tl { \q_no_value } {
+    \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+      \tl_clear:N \l__sdaps_tmpa_tl
+    } {
+      \tl_put_left:Nn \l__sdaps_tmpa_tl { _ }
+    }
+    \tl_put_left:NV \l__sdaps_tmpa_tl \l__sdaps_tmpb_tl
+  }
+
+  \sdaps_context_get:nN { id } \l__sdaps_tmpb_tl
+
+  \tl_if_eq:VnTF \l__sdaps_tmpb_tl { \q_no_value } {
+    \msg_warning:nn { sdapsbase } { no_qid }
+  } {
+    \sdaps_info_write_x:x{
+      Variable[\l__sdaps_tmpb_tl]=\l__sdaps_tmpa_tl
+    }
+  }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_qobject_post_hook:
+{
+  \seq_gpop_right:NN \g__sdaps_object_id_seq \l__sdaps_tmpa_tl
+  \int_gset:NV \g__sdaps_object_id_int \l__sdaps_tmpa_tl
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_begin:nnn
+{
+  \int_gincr:N \g__sdaps_object_id_int
+  \seq_gput_right:NV \g__sdaps_object_id_seq \g__sdaps_object_id_int
+  \tl_set:Nx \l__sdaps_tmpa_tl { \int_use:N \g__sdaps_object_id_int }
+  \int_gzero:N \g__sdaps_object_id_int
+
+  \tl_set:Nx \l__sdaps_tmpb_tl { \seq_use:Nn \g__sdaps_object_id_seq {.} }
+
+  \sdaps_context_begin:n {#1}
+    \sdaps_context_put:nV {id} \l__sdaps_tmpb_tl
+    \sdaps_info_write:x {QObject-#2=\tl_use:N\l__sdaps_tmpb_tl. ~ \exp_not:n{#3}}
+    \sdaps_context_append:nVn { _implicit_var } \l__sdaps_tmpa_tl { _ }
+    \sdaps_context_hook_end:n { \_sdaps_qobject_end_hook: }
+    \sdaps_context_hook_post_end:n { \_sdaps_qobject_post_hook: }
+}
+\cs_generate_variant:Nn \sdaps_qobject_begin:nnn { nnV, nVV, nVn }
+
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_end:n
+{
+  % End the context in question, everything else is done from the close hook
+  \sdaps_context_end:n {#1}
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_begin:nn
+{
+  \sdaps_qobject_begin:nnn { unnamed_qobject } { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_begin_local:nn
+{
+  % Empty local context which automatically closes the qobject
+  \sdaps_context_begin_local:
+    \sdaps_qobject_begin:nnn { unnamed_local_qobject } { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_end:
+{
+  \sdaps_qobject_end:n { unnamed_qobject }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_append_var:n
+{
+  % If the given variable name starts with _ then include the implicitly
+  % generated variable name.
+  \tl_if_head_eq_charcode:nNTF { #1 } _ {
+    \sdaps_context_get:nN { _implicit_var } \l__sdaps_tmpa_tl
+    \tl_if_eq:VnF \l__sdaps_tmpa_tl { \q_no_value } {
+      \sdaps_context_append:nVn { _var } \l__sdaps_tmpa_tl { _ }
+    }
+
+    \sdaps_context_append:nnn { _var } { #1 } { }
+  } {
+    \sdaps_context_append:nnn { _var } { #1 } { _ }
+  }
+
+  % We have a proper variable name now, delete the implicit one
+  \sdaps_context_remove:n { _implicit_var }
+}
+\cs_generate_variant:Nn \sdaps_qobject_append_var:n { V }
+
+\msg_new:nnn { sdapsbase } { no_qid } { Trying~to~output~metadata~but~no~question~ID~is~set~on~the~context.~Did~you~start~a~question?~Supressing~the~output! }
+
+\cs_new_protected_nopar:Nn \sdaps_answer:n
+{
+  \sdaps_context_get:nN { id } \l__sdaps_tmpa_tl
+
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+    \msg_warning:nn { sdapsbase } { no_qid }
+  } {
+    \sdaps_info_write:x {
+       Answer[\tl_use:N \l__sdaps_tmpa_tl]=\exp_not:n { #1 }
+    }
+  }
+}
+\cs_generate_variant:Nn \sdaps_answer:n { o }
+\cs_generate_variant:Nn \sdaps_answer:n { f }
+\cs_generate_variant:Nn \sdaps_answer:n { V }
+
+\cs_new_protected_nopar:Nn \sdaps_range:nnn
+{
+  \sdaps_context_get:nN { id } \l__sdaps_tmpa_tl
+
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+    \msg_warning:nn { sdapsbase } { no_qid }
+  } {
+    \sdaps_info_write:x {
+       Range-#1[\tl_use:N \l__sdaps_tmpa_tl]=\int_eval:n{#2},\exp_not:n { #3 }
+    }
+  }
+}
+\cs_generate_variant:Nn \sdaps_range:nnn { nno }
+\cs_generate_variant:Nn \sdaps_range:nnn { nnf }
+\cs_generate_variant:Nn \sdaps_range:nnn { nnV }
+
+\cs_generate_variant:Nn \tl_if_head_eq_charcode:nNT { VN }
+
+\cs_new_protected_nopar:Nn \_sdaps_generate_var:nN
+{
+  \tl_set:Nn \l__sdaps_tmpa_tl { #1 }
+
+  % Generate a variable name if there is none (prepended with _ prefix)
+  \tl_if_empty:VT \l__sdaps_tmpa_tl {
+    \tl_set:Nx \l__sdaps_tmpa_tl { _ \int_eval:n { \g__sdaps_object_id_int + 1 } }
+  }
+
+  % Prepend any implicitly generated variable names if prefixed by _
+  \tl_if_head_eq_charcode:VNT \l__sdaps_tmpa_tl _ {
+    \sdaps_context_get:nN { _implicit_var } \l__sdaps_tmpb_tl
+    \tl_if_eq:VnTF \l__sdaps_tmpb_tl { \q_no_value } {
+      \tl_remove_once:Nn \l__sdaps_tmpa_tl { _ }
+    } {
+      \tl_put_left:NV \l__sdaps_tmpa_tl \l__sdaps_tmpb_tl
+    }
+  }
+
+  % Prepend explicit variable name
+  \sdaps_context_get:nN { _var } \l__sdaps_tmpb_tl
+  \tl_if_eq:VnF \l__sdaps_tmpb_tl { \q_no_value } {
+    \tl_put_left:Nn \l__sdaps_tmpa_tl { _ }
+    \tl_put_left:NV \l__sdaps_tmpa_tl \l__sdaps_tmpb_tl
+  }
+
+  \tl_set:NV #2 \l__sdaps_tmpa_tl
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_box_inc_object_id:
+{
+  \bool_if:NT \g_sdaps_write_enable_bool {
+    \int_gincr:N \g__sdaps_object_id_int
+  }
+}
+
+%    \end{macrocode}
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% \subsection{Data handling and override specification}
+%
+% Often it is useful to set certain flags for specific checkboxes. As the
+% checkbox may only be generated internally by SDAPS it is impossible to pass
+% a flag to it directly. Because of this \textsf{sdapsbase} has some mechanisms
+% to maintain a tree with options.
+%
+% \begin{verbatim}
+% \sdaps_overrides_init:n{*={
+%   *={},
+%   checkbox2={ellipse},
+%   checkbox3,1={width=6mm},
+% }}
+% \end{verbatim}
+%
+%    \begin{macrocode}
+
+\cs_generate_variant:Nn \keyval_parse:NNn { NNV }
+\cs_generate_variant:Nn \int_gset:Nn { NV }
+
+\seq_new:N \g__sdaps_checkbox_overlays_seq
+\seq_new:N \g__sdaps_textbox_overlays_seq
+
+
+\prop_new:N \g__sdaps_id_to_overrides_prop
+\prop_new:N \g__sdaps_overrides_prop
+\prop_new:N \g__sdaps_id_overrides_prop
+
+\cs_new_protected_nopar:Nn \__sdaps_questionnaire_overrides_set:nn
+{
+  \str_if_eq_x:nnTF { #1 } { * } {
+    \__sdaps_parse_overrides:n{ #2 }
+  } {
+    \prop_put:Nnn \g__sdaps_id_to_overrides_prop { #1 } { #2 }
+  }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_overrides_init:n
+{
+  \keyval_parse:NNn \use_none:n \__sdaps_questionnaire_overrides_set:nn { #1 }
+}
+
+
+\cs_new_protected_nopar:Nn \__sdaps_overrides_set:nn
+{
+  \prop_gput:Nnn \g__sdaps_overrides_prop { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_id_overrides_set:nn
+{
+  \prop_gput:Nnn \g__sdaps_id_overrides_prop { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_parse_overrides:n
+{
+  \prop_gclear:N \g__sdaps_overrides_prop
+  \keyval_parse:NNn \use_none:n \__sdaps_overrides_set:nn { #1 }
+}
+
+\tl_new:N \l__sdaps_set_qid_tl
+\cs_new_protected_nopar:Nn \sdaps_set_questionnaire_id:n
+{
+  \tl_gset:Nn \g__sdaps_questionnaire_id_tl { #1 }
+  \prop_gclear:N \g__sdaps_id_overrides_prop
+  \prop_get:NnNT \g__sdaps_id_to_overrides_prop { #1 } \l__sdaps_set_qid_tl {
+    \keyval_parse:NNV \use_none:n \__sdaps_id_overrides_set:nn \l__sdaps_set_qid_tl
+  }
+}
+\cs_generate_variant:Nn \sdaps_set_questionnaire_id:n { V }
+
+
+\cs_new_protected_nopar:Nn \__sdaps_append_override_options:Nnn
+{
+  % Global definition
+  % First generic for all items
+  \prop_get:NnNT \g__sdaps_overrides_prop { * } \l__sdaps_tmpa_tl {
+    \tl_put_right:Nn #1 {,}
+    \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+  }
+  \tl_if_empty:nF { #2 } {
+    % Items with same variable name
+    \prop_get:NnNT \g__sdaps_overrides_prop { #2 } \l__sdaps_tmpa_tl {
+      \tl_put_right:Nn #1 {,}
+      \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+    }
+    \tl_if_empty:nF { #3 } {
+      % Items with same variable name and value
+      \prop_get:NnNT \g__sdaps_overrides_prop { #2&#3 } \l__sdaps_tmpa_tl {
+        \tl_put_right:Nn #1 {,}
+        \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+      }
+    }
+  }
+
+
+  % Local (questionnaire ID specific) definition
+  % First generic for all items
+  \prop_get:NnNT \g__sdaps_id_overrides_prop { * } \l__sdaps_tmpa_tl {
+    \tl_put_right:Nn #1 {,}
+    \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+  }
+  \tl_if_empty:nF { #2 } {
+    % Items with same variable name
+    \prop_get:NnNT \g__sdaps_id_overrides_prop { #2 } \l__sdaps_tmpa_tl {
+      \tl_put_right:Nn #1 {,}
+      \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+    }
+    \tl_if_empty:nF { #3 } {
+      % Items with same variable name and value
+      \prop_get:NnNT \g__sdaps_id_overrides_prop { #2&#3 } \l__sdaps_tmpa_tl {
+        \tl_put_right:Nn #1 {,}
+        \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+      }
+    }
+  }
+}
+\cs_generate_variant:Nn \__sdaps_append_override_options:Nnn  { NVn }
+
+%    \end{macrocode}
+%
+%
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+% First we define constants and global variables for later use.
+%    \begin{macrocode}
+\dim_new:N \g_sdaps_linewidth_dim
+\g_sdaps_linewidth_dim=1bp
+\tl_new:N \g__sdaps_checkbox_last_info_tl
+
+\int_new:N \g__sdaps_textbox_num_int
+\int_set:Nn \g__sdaps_textbox_num_int 0
+
+\tl_new:N \l_sdaps_var_tl
+\dim_new:N \l_sdaps_x_dim
+\dim_new:N \l_sdaps_y_dim
+\dim_new:N \l_sdaps_width_dim
+\dim_new:N \l_sdaps_height_dim
+
+%    \end{macrocode}
+%
+% We need to be able to output data into the .sdaps file. On startup the
+% output is opened but writing is disabled. Note that the write enabled
+% variable is managed by the context.
+%
+%    \begin{macrocode}
+
+\iow_new:N \g_sdaps_infofile_iow
+\iow_open:Nn \g_sdaps_infofile_iow { \c_sys_jobname_str . sdaps }
+\int_new:N \g__sdaps_infofile_line_int
+\int_gset:Nn \g__sdaps_infofile_line_int { 0 }
+
+\cs_new_protected_nopar:Nn \sdaps_info_write:n
+{
+  \bool_if:NT \g_sdaps_write_enable_bool {
+    \int_gincr:N \g__sdaps_infofile_line_int
+    \iow_shipout:Nx \g_sdaps_infofile_iow { [ \int_use:N \g__sdaps_infofile_line_int ] \exp_not:n { #1 } }
+  }
+}
+\cs_generate_variant:Nn \sdaps_info_write:n { x }
+
+\cs_new_protected_nopar:Nn \sdaps_info_write_x:n
+{
+  \bool_if:NT \g_sdaps_write_enable_bool {
+    \int_gincr:N \g__sdaps_infofile_line_int
+    \iow_shipout_x:Nx \g_sdaps_infofile_iow { [ \int_use:N \g__sdaps_infofile_line_int ] \exp_not:n { #1 } }
+  }
+}
+\cs_generate_variant:Nn \sdaps_info_write_x:n { x }
+
+
+%    \end{macrocode}
+%
+% Set some options at the beginning of the document.
+%    \begin{macrocode}
+%\AtBeginDocument{}
+%    \end{macrocode}
+%
+% \subsection{Definition for keyword parameters}
+%
+%    \begin{macrocode}
+
+
+
+\dim_new:N \l_sdaps_checkbox_linewidth_dim
+\dim_new:N \l_sdaps_checkbox_width_dim
+\dim_new:N \l_sdaps_checkbox_height_dim
+\tl_new:N \l_sdaps_checkbox_form_tl
+\tl_new:N \l_sdaps_checkbox_fill_tl
+\tl_new:N \l_sdaps_checkbox_var_tl
+\tl_new:N \l_sdaps_checkbox_value_tl
+\bool_new:N \l_sdaps_checkbox_draw_check_bool
+
+\tl_set:Nn \l_sdaps_checkbox_form_tl { box }
+
+\tl_new:N \l_sdaps_parse_unknown_tl
+
+
+% Internal overlays
+\tl_new:N \l_sdaps_overlay_centered_text_tl
+\tl_new:N \l_sdaps_overlay_minipage_text_tl
+\tl_new:N \l_sdaps_overlay_minipage_pos_tl
+\dim_new:N \l_sdaps_overlay_minipage_pad_dim
+
+% Note that width/height is the *outside* width/height
+\keys_define:nn { sdaps / checkbox }
+{
+  linewidth    .dim_set:N   = \l_sdaps_checkbox_linewidth_dim,
+  linewidth    .initial:n   = 1bp,
+  width        .dim_set:N   = \l_sdaps_checkbox_width_dim,
+  width        .initial:n   = 3.5mm,
+  height       .dim_set:N   = \l_sdaps_checkbox_height_dim,
+  height       .initial:n   = 3.5mm,
+  form         .choices:nn  = { box, ellipse } { \tl_set:Nx \l_sdaps_checkbox_form_tl { \l_keys_choice_tl } },
+  value        .tl_set:N    = \l_sdaps_checkbox_value_tl,
+
+  fill         .tl_set:N    = \l_sdaps_checkbox_fill_tl,
+  fill         .initial:n   = { white },
+
+  draw_check   .bool_set:N  = \l_sdaps_checkbox_draw_check_bool,
+  draw_check   .default:n   = true,
+  draw_check   .initial:n   = false,
+
+  % Simple node overlay
+  centered_text  .tl_set:N    = \l_sdaps_overlay_centered_text_tl,
+  centered_text  .initial:n   = {},
+
+  % minipage overlay
+  text         .tl_set:N    = \l_sdaps_overlay_minipage_text_tl,
+  text         .initial:n   = {},
+  text_align   .tl_set:N    = \l_sdaps_overlay_minipage_pos_tl,
+  text_align   .initial:n   = {c},
+  text_padding .dim_set:N   = \l_sdaps_overlay_minipage_pad_dim,
+  text_padding .initial:n   = {2bp},
+
+  ellipse    .meta:n  = { form=ellipse },
+  box        .meta:n  = { form=box },
+}
+%    \end{macrocode}
+%
+% \subsection{Checkboxes}
+%
+%
+%    \begin{macrocode}
+
+\cs_new_protected_nopar:Nn \__sdaps_checkbox_internal:nn
+{
+  \mbox{
+    \sdaps_if_rtl:T {\beginL}
+    \pdfsavepos
+
+     % Position of page and baseline offset
+    \dim_set:Nn \l_sdaps_x_dim { \hoffset }
+    \dim_set:Nn \l_sdaps_y_dim { \voffset + \l_sdaps_checkbox_height_dim - \dim_eval:n { 0.5\l_sdaps_checkbox_height_dim - 0.8ex } }
+
+    % Size
+    \dim_set:Nn \l_sdaps_width_dim { \l_sdaps_checkbox_width_dim }
+    \dim_set:Nn \l_sdaps_height_dim { \l_sdaps_checkbox_height_dim }
+
+    \bool_if:NT \g_sdaps_write_enable_bool {
+      % pdflast[xy]pos is the PDF position of the baseline at the start of the box
+      % excluding the page origin offset.
+      \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+      \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+        \msg_warning:nn { sdapsbase } { no_qid }
+      } {
+        \sdaps_info_write_x:x{
+          Box[\l__sdaps_tmpa_tl]=Checkbox,
+          \exp_not:n{\int_use:N\g_sdaps_page_int},
+          \exp_not:n{\dim_eval:n} { \exp_not:f {\dim_use:N \l_sdaps_x_dim + \the\pdflastxpos sp} },
+          \exp_not:n{\dim_eval:n} { \exp_not:f {\dim_use:N \l_sdaps_y_dim + \the\pdflastypos sp} },
+          \dim_use:N \l_sdaps_width_dim,
+          \dim_use:N \l_sdaps_height_dim,
+          \tl_to_str:N\l_sdaps_checkbox_form_tl,
+          \dim_use:N \l_sdaps_checkbox_linewidth_dim,
+          #1,\tl_if_empty:nTF { #2 } { \int_use:N \g__sdaps_object_id_int } { #2 }
+        }
+      }
+    }
+
+
+    \tikz[baseline={0.5\l_sdaps_checkbox_height_dim-0.8ex}]{%
+      \tl_if_eq:VnT \l_sdaps_checkbox_form_tl { box } {
+        \draw[line~width=\l_sdaps_checkbox_linewidth_dim,fill=\l_sdaps_checkbox_fill_tl] (0.5\l_sdaps_checkbox_linewidth_dim, 0.5\l_sdaps_checkbox_linewidth_dim) rectangle +($(\l_sdaps_checkbox_width_dim, \l_sdaps_checkbox_height_dim)-(\l_sdaps_checkbox_linewidth_dim,\l_sdaps_checkbox_linewidth_dim)$);%
+      }
+      \tl_if_eq:VnT \l_sdaps_checkbox_form_tl { ellipse } {
+        \draw[line~width=\l_sdaps_checkbox_linewidth_dim,fill=\l_sdaps_checkbox_fill_tl] (0.5\l_sdaps_checkbox_width_dim, 0.5\l_sdaps_checkbox_height_dim) circle [x~radius=0.5\l_sdaps_checkbox_width_dim-0.5\l_sdaps_checkbox_linewidth_dim, y~radius=0.5\l_sdaps_checkbox_height_dim-0.5\l_sdaps_checkbox_linewidth_dim];%
+      }
+
+      % For the overlay we actually position the nodes relative to the checkbox
+      % and not absolute on the page.
+      \dim_set:Nn \l_sdaps_x_dim { 0pt }
+      \dim_set:Nn \l_sdaps_y_dim { \l_sdaps_checkbox_height_dim }
+
+      % Use overlay so that nothing happens if a node is larger than the checkbox
+      \begin{scope}[overlay]
+        \seq_map_inline:Nn \g__sdaps_checkbox_overlays_seq {##1}
+      \end{scope}
+    }
+    \sdaps_if_rtl:T {\endL}
+  }
+}
+\cs_generate_variant:Nn \__sdaps_checkbox_internal:nn { Vn }
+
+\sdaps_context_set:n { checkboxtype=multichoice }
+\cs_new_protected_nopar:Nn \sdaps_checkbox_set_type:n
+{
+  \sdaps_context_set:n { checkboxtype={#1} }
+}
+\cs_generate_variant:Nn \sdaps_checkbox_set_type:n { V }
+
+\cs_new_protected_nopar:Nn \sdaps_checkbox:nn
+{
+  \group_begin:%
+
+    \_sdaps_generate_var:nN { #1 } \l_sdaps_var_tl
+
+    \sdaps_context_get:nN { checkboxtype } \l__sdaps_tmpa_tl
+    \tl_if_eq:VnTF \l__sdaps_tmpa_tl { multichoice } {
+      \tl_set:Nn \l_sdaps_parse_unknown_tl { box }
+    } {
+      \tl_set:Nn \l_sdaps_parse_unknown_tl { ellipse }
+    }
+
+    \__sdaps_append_from_context:nN { * } \l_sdaps_parse_unknown_tl
+    \__sdaps_append_from_context:VN \l__sdaps_tmpa_tl \l_sdaps_parse_unknown_tl
+    \__sdaps_append_override_options:NVn \l_sdaps_parse_unknown_tl \l_sdaps_var_tl { #2 }
+
+    \keys_set_known:nVN { sdaps / checkbox } \l_sdaps_parse_unknown_tl \l_sdaps_parse_unknown_tl
+
+    \_sdaps_box_inc_object_id:
+
+    \__sdaps_checkbox_internal:Vn \l_sdaps_var_tl { #2 }
+  \group_end:%
+  \ignorespaces
+}
+\cs_generate_variant:Nn \sdaps_checkbox:nn { Vn, VV, nV }
+
+
+\cs_new_protected_nopar:Nn \sdaps_overlay_check:
+{
+  \bool_if:NT \l_sdaps_checkbox_draw_check_bool {
+    \begin{scope}[decoration={random~steps,segment~length=4pt,amplitude=1pt}]
+      \draw[line~width=\l_sdaps_checkbox_linewidth_dim, decorate] ($(0, 0) - (2pt,2pt)$) -- (0.5\l_sdaps_checkbox_width_dim, 0.5\l_sdaps_checkbox_height_dim) -- ($(\l_sdaps_checkbox_width_dim, \l_sdaps_checkbox_height_dim) + (2pt,2pt)$);%
+      \draw[line~width=\l_sdaps_checkbox_linewidth_dim, decorate] ($(0, \l_sdaps_checkbox_height_dim) + (-2pt,2pt)$) -- (0.5\l_sdaps_checkbox_width_dim, 0.5\l_sdaps_checkbox_height_dim) -- ($(\l_sdaps_checkbox_width_dim, 0) + (2pt,-2pt)$);%
+    \end{scope}
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \sdaps_overlay_check:
+
+
+\cs_new_protected_nopar:Nn \sdaps_overlay_centered:
+{
+  \tl_if_empty:NF \l_sdaps_overlay_centered_text_tl {
+    \node[anchor=center,inner~sep=0pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \l_sdaps_overlay_centered_text_tl
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \sdaps_overlay_centered:
+\seq_put_left:Nn \g__sdaps_textbox_overlays_seq \sdaps_overlay_centered:
+
+
+\cs_new_protected_nopar:Nn \sdaps_overlay_minipage:
+{
+  \tl_if_empty:NF \l_sdaps_overlay_minipage_text_tl {
+    \node[anchor=center,inner~sep=0pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \dim_set:Nn \l_sdaps_width_dim { \l_sdaps_width_dim - 2\l_sdaps_overlay_minipage_pad_dim }
+      \dim_set:Nn \l_sdaps_height_dim { \l_sdaps_height_dim - 2\l_sdaps_overlay_minipage_pad_dim }
+
+      \begin{minipage}[t][\l_sdaps_height_dim][\l_sdaps_overlay_minipage_pos_tl]{\l_sdaps_width_dim}
+        % Hm, is this sane?
+        \tex_let:D \textheight\l_sdaps_height_dim
+        \l_sdaps_overlay_minipage_text_tl
+      \end{minipage}
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \sdaps_overlay_minipage:
+\seq_put_left:Nn \g__sdaps_textbox_overlays_seq \sdaps_overlay_minipage:
+
+
+%    \end{macrocode}
+%
+
+
+% \subsection{Textboxes}
+%
+%
+%    \begin{macrocode}
+
+\sdaps_context_set:n { textboxtype=textbox }
+\cs_new_protected_nopar:Nn \sdaps_textbox_set_type:n
+{
+  \sdaps_context_set:n { textboxtype={#1} }
+}
+\cs_generate_variant:Nn \sdaps_textbox_set_type:n { V }
+
+\dim_new:N  \l_sdaps_textbox_linewidth_dim
+\tl_new:N   \l_sdaps_textbox_var_tl
+\tl_new:N   \l_sdaps_textbox_fill_tl
+\tl_new:N   \l__sdaps_textbox_boxtype_tl
+
+
+\keys_define:nn { sdaps / textbox }
+{
+  linewidth    .dim_set:N   = \l_sdaps_textbox_linewidth_dim,
+  linewidth    .initial:n   = 1bp,
+
+  fill         .tl_set:N    = \l_sdaps_textbox_fill_tl,
+  fill         .initial:n   = { white },
+
+  % Simple node overlay
+  centered_text  .tl_set:N    = \l_sdaps_overlay_centered_text_tl,
+  centered_text  .initial:n   = {},
+
+  % minipage overlay
+  text         .tl_set:N    = \l_sdaps_overlay_minipage_text_tl,
+  text         .initial:n   = {},
+  text_align   .tl_set:N    = \l_sdaps_overlay_minipage_pos_tl,
+  text_align   .initial:n   = {c},
+  text_padding .dim_set:N   = \l_sdaps_overlay_minipage_pad_dim,
+  text_padding .initial:n   = {2bp},
+}
+
+
+
+
+\dim_new:N \l__sdaps_textbox_dp_dim
+\dim_new:N \l__sdaps_textbox_ht_dim
+\dim_new:N \l__sdaps_textbox_wd_dim
+\dim_new:N \l__sdaps_textbox_pad_dim
+
+
+\msg_new:nnn { sdapsbase } { textbox_wrong_mode } { Impossible~to~layout~a~#1~textbox~in~#2~mode. }
+
+\cs_new_protected_nopar:Nn \__sdaps_textbox_prepare:n
+{
+  \tl_set:Nn \l_sdaps_parse_unknown_tl {}
+
+  \_sdaps_generate_var:nN { #1 } \l_sdaps_var_tl
+
+  \sdaps_context_get:nN { textboxtype } \l__sdaps_tmpa_tl
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { codebox } {
+    \tl_set:Nn \l__sdaps_textbox_boxtype_tl { Codebox }
+  } {
+    \tl_set:Nn \l__sdaps_textbox_boxtype_tl { Textbox }
+  }
+
+  \__sdaps_append_from_context:nN { * } \l_sdaps_parse_unknown_tl
+  \__sdaps_append_from_context:VN \l__sdaps_tmpa_tl \l_sdaps_parse_unknown_tl
+  \__sdaps_append_override_options:NVn \l_sdaps_parse_unknown_tl \l_sdaps_var_tl { }
+
+  \keys_set_known:nVN { sdaps / textbox } \l_sdaps_parse_unknown_tl \l_sdaps_parse_unknown_tl
+
+  \_sdaps_box_inc_object_id:
+}
+
+\cs_new_protected_nopar:Nn \sdaps_textbox_vhstretch:nnn
+{
+%    \end{macrocode}
+%
+% At first we need to ensure that we are not in math mode. We also error out if
+% switching into vertical mode (by inserting a paragraph break) fails.
+%
+% The scaling feature will likely fail in inner vertical mode, but it is still
+% permissible.
+%
+%    \begin{macrocode}
+  \if_mode_math:
+    \msg_error:nnnn { sdapsbase } { textbox_wrong_mode } { vhstretch } { math }
+  \else:
+  \tex_par:D
+  \if_mode_horizontal:
+    \msg_error:nnnn { sdapsbase } { textbox_wrong_mode } { vhstretch } { horizontal }
+  \else:
+  % Go into vertical mode by ending the current paragraph and insert
+  % \cs{widowpenalty} so that we don't get an unwelcome page break. It is
+  % assumed that any explanation for the textbox will be on top.
+  \tex_penalty:D \tex_widowpenalty:D
+
+  \group_begin:%
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+%    \end{macrocode}
+%
+% First we define the height and depth of the box that will be set. Setting the
+% height works by inserting a rule into the first box and adding a negative
+% skip afterwards. The depth is compensated by simply doing the same thing at
+% the bottom.
+%
+% Note that we need to add nobreak/nointerlineskip everywhere so that we
+% don't insert extra spacing and no page break will happen.
+%
+% The first box has a defined height and stores the current position for the
+% frame code later on. Then two cleaders follow which simply implement the
+% required stretching (cleaders are used to prevent page breaking, I do not
+% know whether this is sane, but it seems to work just fine). After this the
+% box containing the main drawing code is placed. We simply use a vbox with
+% right aligned content. The last thing happening is a vskip plus hbox to set
+% the correct depth.
+%
+%    \begin{macrocode}
+    % TODO: Make this configurable
+    \dim_set:Nn \l__sdaps_textbox_dp_dim { 0.5ex }
+    \dim_set:Nn \l__sdaps_textbox_ht_dim { 1.7ex }
+
+    \vbox:n {
+      \sdaps_if_rtl:T {\beginL}
+      \leftskip=0pt
+      \rightskip=0pt plus 1fill
+      \noindent
+      \tex_vrule:D height \l__sdaps_textbox_ht_dim depth 0pt width 0pt
+      \pgfsys at markposition{textboxtop\int_use:N\g__sdaps_textbox_num_int}
+      \sdaps_if_rtl:T {\endL}
+    }
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \tex_cleaders:D\tex_hbox:D{}\skip_vertical:n{#2 - \l__sdaps_textbox_ht_dim}
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \tex_cleaders:D\tex_hbox:D{}\skip_vertical:n{\stretch{#3}}
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \vbox:n {
+      \sdaps_if_rtl:T {\beginL}
+      \noindent
+      \leftskip=0pt plus 1fill
+      \rightskip=0pt
+      \begin{tikzpicture}[remember~picture,overlay,shift=(current~page.south~west)]
+        \pgfsys at getposition{pgfpageorigin}{\@sdaps at pageorigin}
+        \pgfsys at getposition{textboxtop\int_use:N\g__sdaps_textbox_num_int}{\@sdaps at textboxtoppos}
+        \pgfpointadd{\@sdaps at textboxtoppos}{
+          \pgfpointadd{\@sdaps at pageorigin}{\pgfpoint{0}{\l__sdaps_textbox_ht_dim}}
+        }
+        \pgfgetlastxy{\l__sdaps_x}{\l__sdaps_y}
+        \dim_set:Nn \l_sdaps_x_dim {\l__sdaps_x}
+        \dim_set:Nn \l_sdaps_y_dim {\l__sdaps_y}
+
+        \pgfsys at getposition{\pgfpictureid}{\@sdaps at textboxbottompos}
+        \pgfpointdiff{\@sdaps at textboxtoppos}{\@sdaps at textboxbottompos}
+
+        % Is there a more elegant way to multiply the height with -1?
+        \pgfgetlastxy{\l__sdaps_width}{\l__sdaps_height}
+        % Add the minimum height (i.e. depth below the last baseline) to the
+        % overall height.
+        \pgfpoint{\l__sdaps_width}{-\l__sdaps_height + \l__sdaps_textbox_ht_dim}
+        \pgfgetlastxy{\l__sdaps_width}{\l__sdaps_height}
+        \dim_set:Nn \l_sdaps_width_dim {\l__sdaps_width}
+        \dim_set:Nn \l_sdaps_height_dim {\l__sdaps_height}
+
+        % Draw the rectangle
+        \draw[line~width=\l_sdaps_textbox_linewidth_dim,fill=\l_sdaps_textbox_fill_tl] ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5 * (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$) rectangle +($(\l_sdaps_width_dim, -\l_sdaps_height_dim) - (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$);
+
+        \begin{scope}
+          \seq_map_inline:Nn \g__sdaps_textbox_overlays_seq {##1}
+        \end{scope}
+
+        \bool_if:NT \g_sdaps_write_enable_bool {
+          \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+          \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+            \msg_warning:nn { sdapsbase } { no_qid }
+          } {
+            \sdaps_info_write_x:x {
+              Box[\l__sdaps_tmpa_tl]=\l__sdaps_textbox_boxtype_tl,
+              \exp_not:n{\int_use:N\g_sdaps_page_int},
+              \dim_use:N \l_sdaps_x_dim,
+              \dim_use:N \l_sdaps_y_dim,
+              \dim_use:N \l_sdaps_width_dim,
+              \dim_use:N \l_sdaps_height_dim,
+              \dim_use:N \l_sdaps_textbox_linewidth_dim,
+              \tl_use:N \l_sdaps_var_tl,
+            }
+          }
+        }
+      \end{tikzpicture}
+      \sdaps_if_rtl:T {\endL}
+    }
+
+    % We are done here, but we need the correct depth, so skip around a bit
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \skip_vertical:n{ - \l__sdaps_textbox_dp_dim}
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \hbox:n { \vrule depth \l__sdaps_textbox_dp_dim height 0pt width 0pt }
+
+    \int_gincr:N\g__sdaps_textbox_num_int
+
+  \group_end:
+  \fi:
+  \fi:
+}
+\cs_generate_variant:Nn \sdaps_textbox_vhstretch:nnn { Vnn }
+
+\cs_new_protected_nopar:Nn \sdaps_textbox_vhstretch:nn
+{
+  \sdaps_textbox_vhstretch:nnn { #1 } { #2 } { 1 }
+}
+
+
+\cs_new_protected_nopar:Nn \sdaps_textbox_hstretch:nnnnn
+{
+  \group_begin:
+    \sdaps_if_rtl:T {\beginL}
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+    \dim_set:Nn \l_tmpa_dim { #2 }
+    \dim_set:Nn \l_tmpb_dim { #3 }
+
+    % Place a vrule to make space for the top/bottom padding
+    \tex_vrule:D depth \dim_use:N \l_tmpa_dim height \dim_use:N \l_tmpb_dim width 0pt \nobreak
+    \pgfsys at markposition{textboxstart\int_use:N\g__sdaps_textbox_num_int} \nobreak
+
+    \skip_horizontal:n { #4 + \stretch{#5} } \nobreak
+
+    % The textbox (rendered on the background)
+    \begin{tikzpicture}[remember~picture,overlay,shift=(current~page.south~west)]
+      \pgfsys at getposition{pgfpageorigin}{\@sdaps at pageorigin}
+      \pgfsys at getposition{textboxstart\int_use:N\g__sdaps_textbox_num_int}{\@sdaps at textboxpos}
+      % The position here is the position of the baseline.
+      % So move up by height (param 2) to get the correct vertical position.
+      \pgfpointadd{\@sdaps at textboxpos}{
+        \pgfpointadd{\@sdaps at pageorigin}{\pgfpoint{0}{ \dim_use:N \l_tmpb_dim}}
+      }
+      \pgfgetlastxy{\l__sdaps_x}{\l__sdaps_y}
+      \dim_set:Nn \l_sdaps_x_dim {\l__sdaps_x}
+      \dim_set:Nn \l_sdaps_y_dim {\l__sdaps_y}
+
+      \pgfsys at getposition{\pgfpictureid}{\@sdaps at textboxendpos}
+      % Calculate width and add the height to it
+      \pgfpointadd{
+        \pgfpointdiff{\@sdaps at textboxpos}{\@sdaps at textboxendpos}
+      }{\pgfpoint{0pt}{\dim_use:N \l_tmpa_dim +  \dim_use:N \l_tmpb_dim}}
+      \pgfgetlastxy{\l__sdaps_width}{\l__sdaps_height}
+      \dim_set:Nn \l_sdaps_width_dim {\l__sdaps_width}
+      \dim_set:Nn \l_sdaps_height_dim {\l__sdaps_height}
+
+      % Draw the rectangle
+      \draw[line~width=\l_sdaps_textbox_linewidth_dim,fill=\l_sdaps_textbox_fill_tl] ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5 * (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$) rectangle +($(\l_sdaps_width_dim, -\l_sdaps_height_dim) - (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$);
+
+      \begin{scope}
+        \seq_map_inline:Nn \g__sdaps_textbox_overlays_seq {##1}
+      \end{scope}
+
+      \bool_if:NT \g_sdaps_write_enable_bool {
+        \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+        \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+          \msg_warning:nn { sdapsbase } { no_qid }
+        } {
+          \sdaps_info_write_x:x {
+            Box[\l__sdaps_tmpa_tl]=\l__sdaps_textbox_boxtype_tl,
+            \exp_not:n{\int_use:N\g_sdaps_page_int},
+            \dim_use:N \l_sdaps_x_dim,
+            \dim_use:N \l_sdaps_y_dim,
+            \dim_use:N \l_sdaps_width_dim,
+            \dim_use:N \l_sdaps_height_dim,
+            \dim_use:N \l_sdaps_textbox_linewidth_dim,
+            \tl_use:N \l_sdaps_var_tl,
+          }
+        }
+      }
+    \end{tikzpicture}
+
+    \int_gincr:N\g__sdaps_textbox_num_int
+
+    \sdaps_if_rtl:T {\endL}
+  \group_end:
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_textbox_prepare_coffin:
+{
+  \dim_set:Nn \l__sdaps_textbox_ht_dim { \coffin_ht:N \l__sdaps_textbox_coffin }
+  \dim_set:Nn \l__sdaps_textbox_dp_dim { \coffin_dp:N \l__sdaps_textbox_coffin }
+  \dim_set:Nn \l__sdaps_textbox_wd_dim { \coffin_wd:N \l__sdaps_textbox_coffin }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \tex_vrule:D depth 0pt height \dim_eval:n { \l__sdaps_textbox_ht_dim + \l__sdaps_textbox_pad_dim } width 0pt
+
+    \pdfsavepos
+
+    \dim_set:Nn \l_sdaps_width_dim {\l__sdaps_textbox_wd_dim + 2\l__sdaps_textbox_pad_dim}
+    \dim_set:Nn \l_sdaps_height_dim {\l__sdaps_textbox_ht_dim + \l__sdaps_textbox_dp_dim + 2\l__sdaps_textbox_pad_dim}
+
+    % pdflast[xy]pos is the PDF position of the top left corner excluding the
+    % origin
+    \bool_if:NT \g_sdaps_write_enable_bool {
+      \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+      \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+        \msg_warning:nn { sdapsbase } { no_qid }
+      } {
+        \sdaps_info_write_x:x {
+          Box[\l__sdaps_tmpa_tl]=\l__sdaps_textbox_boxtype_tl,
+          \exp_not:n{\int_use:N\g_sdaps_page_int},
+          \exp_not:n{\dim_eval:n {\hoffset + \the\pdflastxpos sp}},
+          \exp_not:n{\dim_eval:n} { \exp_not:n {\voffset + \the\pdflastypos sp} + \dim_use:N \l__sdaps_textbox_ht_dim +  \dim_use:N \l__sdaps_textbox_pad_dim },
+          \dim_use:N \l_sdaps_width_dim,
+          \dim_use:N \l_sdaps_height_dim,
+          \dim_use:N \l_sdaps_textbox_linewidth_dim,
+          \tl_use:N \l_sdaps_var_tl,
+        }
+      }
+    }
+
+    \dim_set:Nn \l_sdaps_x_dim { 0pt }
+    \dim_set:Nn \l_sdaps_y_dim { \l__sdaps_textbox_ht_dim + \l__sdaps_textbox_pad_dim }
+
+    % The textbox (rendered on the background)
+    \begin{tikzpicture}[overlay]
+      % Draw the rectangle
+      \draw[line~width=\l_sdaps_textbox_linewidth_dim,fill=\l_sdaps_textbox_fill_tl] ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5 * (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$) rectangle +($(\l_sdaps_width_dim, -\l_sdaps_height_dim) - (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$);
+
+      \begin{scope}
+          \seq_map_inline:Nn \g__sdaps_textbox_overlays_seq {##1}%
+      \end{scope}
+    \end{tikzpicture}
+    \skip_horizontal:n { \l__sdaps_textbox_pad_dim }
+  }
+
+  \hcoffin_set:Nn \l_tmpb_coffin {
+    \skip_horizontal:n { \l__sdaps_textbox_pad_dim }
+    \tex_vrule:D depth \l__sdaps_textbox_pad_dim height 0pt width 0pt
+  }
+
+  \coffin_join:NnnNnnnn \l_tmpa_coffin { r } { H } \l__sdaps_textbox_coffin { l } { H } { 0pt } { 0pt }
+  \coffin_join:NnnNnnnn \l_tmpa_coffin { r } { b } \l_tmpb_coffin { l } { t } { 0pt } { 0pt }
+
+  \coffin_set_eq:NN \l__sdaps_textbox_coffin \l_tmpa_coffin
+
+
+  \int_gincr:N\g__sdaps_textbox_num_int
+}
+
+\coffin_new:N \l__sdaps_textbox_coffin
+\cs_new_protected_nopar:Nn \sdaps_textbox_hbox:nnn
+{
+  \group_begin:
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+    \hcoffin_set:Nn \l__sdaps_textbox_coffin { \tl_trim_spaces:n { #3 } }
+
+    \dim_set:Nn \l__sdaps_textbox_pad_dim { #2 }
+
+    \__sdaps_textbox_prepare_coffin:
+
+    \coffin_typeset:Nnnnn \l__sdaps_textbox_coffin { l } { H } { 0pt } { 0pt }
+
+  \group_end:
+}
+
+\cs_new_protected:Nn \sdaps_textbox_vbox:nnnn
+{
+  \group_begin:
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+    \dim_set:Nn \l__sdaps_textbox_pad_dim { #3 }
+    \dim_set:Nn \l__sdaps_textbox_wd_dim { #2 - 2\l__sdaps_textbox_pad_dim }
+
+    \vcoffin_set:Nnn \l__sdaps_textbox_coffin { \l__sdaps_textbox_wd_dim } { \tl_trim_spaces:n { #4 }}
+
+    \__sdaps_textbox_prepare_coffin:
+
+    \coffin_typeset:Nnnnn \l__sdaps_textbox_coffin { l } { H } { 0pt } { 0pt }
+
+  \group_end:
+}
+
+%    \end{macrocode}
+%
+%
+% \subsection{Page Marking}
+%
+% SDAPS depends on certain markings on every page. The following function
+% implement drawing these marks.
+%
+%    \begin{macrocode}
+
+\int_new:N \g_sdaps_page_int
+\int_set:Nn \g_sdaps_page_int { 0 }
+
+% Disabling recognition disables all drawings related to automatic recognition
+\bool_new:N \g_sdaps_recognition_bool
+\bool_new:N \g_sdaps_draft_bool
+\bool_new:N \g_sdaps_twoside_bool
+\bool_new:N \g_sdaps_print_questionnaire_id_bool
+
+\bool_set:Nn \g_sdaps_recognition_bool \c_true_bool
+\bool_set:Nn \g_sdaps_draft_bool \c_true_bool
+\bool_set:Nn \g_sdaps_twoside_bool \c_false_bool
+\bool_set:Nn \g_sdaps_print_questionnaire_id_bool \c_false_bool
+
+
+
+\tl_new:N \g_sdaps_style_tl
+\tl_new:N \g_sdaps_checkmode_tl
+\dim_new:N \g_sdaps_edge_left_margin_dim
+\dim_new:N \g_sdaps_edge_right_margin_dim
+\dim_new:N \g_sdaps_edge_top_margin_dim
+\dim_new:N \g_sdaps_edge_bottom_margin_dim
+\dim_new:N \g_sdaps_edge_marker_linewidth_dim
+\dim_new:N \g_sdaps_edge_marker_length_dim
+
+\dim_new:N \g_sdaps_classic_boxpad_dim
+\dim_new:N \g_sdaps_classic_boxsize_dim
+
+
+\tl_gset:Nn \g_sdaps_style_tl { code128 }
+\tl_gset:Nn \g_sdaps_twoside_barcode_tl { both }
+\tl_gset:Nn \g_sdaps_checkmode_tl { checkcorrect }
+\dim_gset:Nn \g_sdaps_edge_left_margin_dim { 10mm }
+\dim_gset:Nn \g_sdaps_edge_right_margin_dim { 10mm }
+\dim_gset:Nn \g_sdaps_edge_top_margin_dim { 12mm }
+\dim_gset:Nn \g_sdaps_edge_bottom_margin_dim { 12mm }
+\dim_gset:Nn \g_sdaps_edge_marker_linewidth_dim { 1bp }
+\dim_gset:Nn \g_sdaps_edge_marker_length_dim { 20mm }
+
+\dim_gset:Nn \g_sdaps_classic_boxpad_dim { 3mm }
+\dim_gset:Nn \g_sdaps_classic_boxsize_dim { 3.5mm }
+
+
+\tl_new:N \g__sdaps_questionnaire_id_tl
+\tl_gset:Nn \g__sdaps_questionnaire_id_tl { }
+
+\tl_new:N \g_sdaps_questionnaire_id_label_tl
+\tl_gset:Nn \g_sdaps_questionnaire_id_label_tl { }
+
+
+\tl_new:N \g_sdaps_survey_id_tl
+\tl_gset:Nn \g_sdaps_survey_id_tl { 32498923 }
+
+\tl_new:N \g_sdaps_global_id_tl
+\tl_gset:Nn \g_sdaps_global_id_tl { }
+
+\tl_new:N \g_sdaps_global_id_label_tl
+\tl_gset:Nn \g_sdaps_global_id_label_tl { }
+
+
+
+
+% Settings for code128 barcodes (used in code128 style, who would have thought?)
+\dim_new:N \c_sdaps_barcode_height_dim
+\dim_gset:Nn \c_sdaps_barcode_height_dim {6.5mm}
+% This is the same as the default
+\dim_new:N \c_sdaps_barcode_bar_width_dim
+\dim_gset:Nn \c_sdaps_barcode_bar_width_dim {0.33mm}
+% Same as default. Barwidth is decreased for printing by this value.
+\dim_new:N \c_sdaps_barcode_bcorr_dim
+\dim_gset:Nn \c_sdaps_barcode_bcorr_dim {0.020mm}
+
+% The padding on the left/right of a barcode. This is the distance that the
+% barcodes will be printed from the cornermarks
+% Choosen to be the same as the barcode height. Note that the Code-128
+% standard requires a quiet zone of max(10*modulesize, ~6.4mm).
+\dim_new:N \c_sdaps_barcode_hpad_dim
+\dim_gset:Nn \c_sdaps_barcode_hpad_dim {6.5mm}
+
+% This needs to be smaller than 6.5mm because otherwise the content is too close.
+% We set it so that it forms a golden ratio 6.5mm*(sqrt(5/4)-0.5). This means
+% we have about 4.5mm padding to the content.
+\dim_new:N \c_sdaps_barcode_vpad_dim
+\dim_gset:Nn \c_sdaps_barcode_vpad_dim {4.02mm}
+
+
+\cs_new_protected_nopar:Nn \sdaps_draw_codes:
+{
+  \group_begin:
+    \begin{scope}[line~width=\g_sdaps_edge_marker_linewidth_dim, shift={(current~page.south~west)}]
+      \str_if_eq:VnT \g_sdaps_style_tl { qr } {
+        \tl_if_empty:VF \g__sdaps_questionnaire_id_tl {
+          \begin{scope}[shift={($(\g_sdaps_edge_left_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+            \node(barcode)[anchor=south~west,outer~sep=0,inner~sep=0]{
+              \qrcode_render:nV {nolink,version=2,level=H,padding,height=10mm} \g__sdaps_questionnaire_id_tl
+            };
+          \end{scope}
+        }
+
+        % We unconditionally print this barcode, it is required for the recognition
+        % process.
+        \begin{scope}[shift={($(\paperwidth, 0) + (-\g_sdaps_edge_right_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+          \pgfmathsetbasenumberlength{4}
+          \pgfmathdectobase{\paddedpage}{\int_use:N\g_sdaps_page_int}{10}% Yes, padding to 4 chars the hard way
+          \node(barcode)[anchor=south~east,outer~sep=0,inner~sep=0]{
+            \qrcode_render:nx {nolink,version=2,level=H,padding,height=10mm} {\tl_use:N\g_sdaps_survey_id_tl\paddedpage}
+          };
+        \end{scope}
+
+        \tl_if_empty:VF \g_sdaps_global_id_tl {
+          \begin{scope}[shift={($(\paperwidth/2-\g_sdaps_edge_right_margin_dim/2+\g_sdaps_edge_left_margin_dim/2, \g_sdaps_edge_bottom_margin_dim)$)}]
+            \node(barcode)[anchor=south,outer~sep=0,inner~sep=0]{
+              \qrcode_render:nV {nolink,version=2,level=H,padding,height=10mm} \g_sdaps_global_id_tl
+            };
+          \end{scope}
+        }
+      }
+
+      \str_if_eq:VnT \g_sdaps_style_tl { code128 } {
+        \tl_if_empty:VF \g__sdaps_questionnaire_id_tl {
+          % TODO: do not hardcode the font!
+          \ttfamily\footnotesize
+
+          \begin{scope}[shift={($(\g_sdaps_edge_left_margin_dim, \g_sdaps_edge_bottom_margin_dim) + (\c_sdaps_barcode_hpad_dim, \c_sdaps_barcode_vpad_dim)$)}]
+            \node(barcode)[anchor=south~west,outer~sep=0,inner~sep=0]{
+              \X = \c_sdaps_barcode_bar_width_dim
+              \bcorr = \c_sdaps_barcode_bcorr_dim
+              \barheight = \c_sdaps_barcode_height_dim
+              \code_render:V \g__sdaps_questionnaire_id_tl
+            };
+            \node[below=1mm~of~barcode,distance=0,anchor=north,outer~sep=0,inner~sep=0]{
+              \tl_if_empty:VTF \g_sdaps_questionnaire_id_label_tl {
+                \tl_use:N \g__sdaps_questionnaire_id_tl
+              } {
+                \tl_use:N \g_sdaps_questionnaire_id_label_tl
+              }
+            };
+          \end{scope}
+        }
+
+        % We unconditionally print this barcode, it is required for the recognition
+        % process.
+        \begin{scope}[shift={($(\paperwidth, 0) + (-\g_sdaps_edge_right_margin_dim, \g_sdaps_edge_bottom_margin_dim) + (-\c_sdaps_barcode_hpad_dim, \c_sdaps_barcode_vpad_dim)$)}]
+          \pgfmathsetbasenumberlength{4}
+          \pgfmathdectobase{\paddedpage}{\int_use:N\g_sdaps_page_int}{10}% Yes, padding to 4 chars the hard way
+          \node(barcode)[anchor=south~east,outer~sep=0,inner~sep=0]{
+            \X = \c_sdaps_barcode_bar_width_dim
+            \bcorr = \c_sdaps_barcode_bcorr_dim
+            \barheight = \c_sdaps_barcode_height_dim
+            \code_render:x {\tl_use:N\g_sdaps_survey_id_tl \paddedpage}
+          };
+          \node[below=1mm~of~barcode,distance=0,anchor=north,outer~sep=0,inner~sep=0]{
+            % TODO: do not hardcode the font!
+            \ttfamily\footnotesize
+
+            \tl_use:N\g_sdaps_survey_id_tl\,\paddedpage
+          };
+        \end{scope}
+
+        \tl_if_empty:VF \g_sdaps_global_id_tl {
+          \begin{scope}[shift={($(\paperwidth/2-\g_sdaps_edge_right_margin_dim/2+\g_sdaps_edge_left_margin_dim/2, 0) + (0, \g_sdaps_edge_bottom_margin_dim) + (0, \c_sdaps_barcode_vpad_dim)$)}]
+            \node(barcode)[anchor=south,outer~sep=0,inner~sep=0]{
+              \X = \c_sdaps_barcode_bar_width_dim
+              \bcorr = \c_sdaps_barcode_bcorr_dim
+              \barheight = \c_sdaps_barcode_height_dim
+              \code_render:V \g_sdaps_global_id_tl
+            };
+            \node[below=1mm~of~barcode,distance=0,anchor=north,outer~sep=0,inner~sep=0]{
+              % TODO: do not hardcode the font!
+              \ttfamily\footnotesize
+
+              \tl_if_empty:VTF \g_sdaps_global_id_label_tl {
+                \tl_use:N \g_sdaps_global_id_tl
+              } {
+                \tl_use:N \g_sdaps_global_id_label_tl
+              }
+            };
+          \end{scope}
+        }
+      }
+    \end{scope}%
+  \group_end:
+}
+
+\msg_new:nnn { sdapsbase } { classic_too_many_pages } { You~cannot~have~more~than~six~pages~with~the~classic~style. }
+\msg_new:nnn { sdapsbase } { odd_page_count } { You~have~an~odd~number~of~pages,~this~does~not~work~in~duplex~mode! }
+
+% This needs to be called once for each page!
+\cs_new_protected_nopar:Nn \sdaps_page_end: {
+  \bool_if:NTF \g_sdaps_recognition_bool {
+    \group_begin:
+
+    \int_gincr:N\g_sdaps_page_int%
+    \sdaps_info_write:x{Pages=\int_use:N\g_sdaps_page_int}
+
+    \normalfont%
+
+    \begin{tikzpicture}[remember~picture,overlay]%
+
+      %---------------------------------------------------------------------------
+      % corner marks and corner boxes in classic mode
+      %---------------------------------------------------------------------------
+      \begin{scope}[line~width=\g_sdaps_edge_marker_linewidth_dim, line~join=miter]
+        \begin{scope}[shift={($(current~page.north~west) + (\g_sdaps_edge_left_margin_dim, -\g_sdaps_edge_top_margin_dim)$)}]
+          \draw (0,-\g_sdaps_edge_marker_length_dim) -- (0, 0) -- (\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnTF \g_sdaps_page_int {
+              { 1 } { \draw }
+              { 2 } { \fill }
+              { 3 } { \fill }
+              { 4 } { \fill }
+              { 5 } { \fill }
+              { 6 } { \draw }
+            } {
+              (\g_sdaps_classic_boxpad_dim, -\g_sdaps_classic_boxpad_dim) rectangle +(\g_sdaps_classic_boxsize_dim,-\g_sdaps_classic_boxsize_dim);
+            } {
+              % Error out (first time)
+              \msg_error:nn { sdapsbase } { classic_too_many_pages }
+            }
+          }
+        \end{scope}
+        \begin{scope}[shift={($(current~page.north~east) + (-\g_sdaps_edge_right_margin_dim, -\g_sdaps_edge_top_margin_dim)$)}]
+          \draw (0,-\g_sdaps_edge_marker_length_dim) -- (0,0) -- (-\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnT \g_sdaps_page_int {
+              { 1 } { \fill }
+              { 2 } { \fill }
+              { 3 } { \draw }
+              { 4 } { \draw }
+              { 5 } { \draw }
+              { 6 } { \draw }
+            } {
+              (-\g_sdaps_classic_boxpad_dim, -\g_sdaps_classic_boxpad_dim) rectangle +(-\g_sdaps_classic_boxsize_dim,-\g_sdaps_classic_boxsize_dim);
+            }
+          }
+        \end{scope}
+        \begin{scope}[shift={($(current~page.south~west) + (\g_sdaps_edge_left_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+          \draw (0,\g_sdaps_edge_marker_length_dim) -- (0, 0) -- (\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnT \g_sdaps_page_int {
+              { 1 } { \fill }
+              { 2 } { \draw }
+              { 3 } { \fill }
+              { 4 } { \fill }
+              { 5 } { \draw }
+              { 6 } { \draw }
+            } {
+              (\g_sdaps_classic_boxpad_dim, \g_sdaps_classic_boxpad_dim) rectangle +(\g_sdaps_classic_boxsize_dim,\g_sdaps_classic_boxsize_dim);
+            }
+          }
+        \end{scope}
+        \begin{scope}[shift={($(current~page.south~east) + (-\g_sdaps_edge_right_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+          \draw (0,\g_sdaps_edge_marker_length_dim) -- (0mm, 0mm) -- (-\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnT \g_sdaps_page_int {
+              { 1 } { \fill }
+              { 2 } { \draw }
+              { 3 } { \fill }
+              { 4 } { \draw }
+              { 5 } { \draw }
+              { 6 } { \fill }
+            } {
+              (-\g_sdaps_classic_boxpad_dim, \g_sdaps_classic_boxpad_dim) rectangle +(-\g_sdaps_classic_boxsize_dim,\g_sdaps_classic_boxsize_dim);
+            }
+          }
+        \end{scope}
+      \end{scope}
+
+      %---------------------------------------------------------------------------
+      % barcodes/qr codes
+      %---------------------------------------------------------------------------
+      \bool_if:NTF \g__sdaps_last_page_bool {
+
+        \int_compare:nNnTF { \g_sdaps_page_int } = { 1 } {
+          % if we have a one page document, just always stamp the last
+          % page. This means that SDAPS can fall back to simplex mode
+          % automatically.
+          \sdaps_draw_codes:
+        } {
+
+          \bool_if:NTF \g_sdaps_twoside_bool {
+            \int_if_odd:VT \g_sdaps_page_int {
+              % This should no happen; draw a barcode though to keep things
+              % somewhat sane.
+              \msg_warning:nn { sdapsbase } { odd_page_count }
+              \sdaps_draw_codes:
+            } {
+              % Even page (i.e. back) has a barcode except in "front" mode
+              \str_if_eq:VnF \g_sdaps_twoside_barcode_tl { front } {
+                \sdaps_draw_codes:
+              }
+            }
+          } {
+            \sdaps_draw_codes:
+          }
+        }
+
+        % TODO: Check whether the page count is as expected?
+      } {
+        \bool_if:NTF \g_sdaps_twoside_bool {
+          \str_if_eq:VnTF \g_sdaps_twoside_barcode_tl { both } {
+            \sdaps_draw_codes:
+          } {
+            \str_if_eq:VnTF \g_sdaps_twoside_barcode_tl { front } {
+              \int_if_odd:VT \g_sdaps_page_int {
+                \sdaps_draw_codes:
+              }
+            } { % And back is the only thing left
+              \int_if_odd:VF \g_sdaps_page_int {
+                \sdaps_draw_codes:
+              }
+            }
+          }
+        } {
+          \sdaps_draw_codes:
+        }
+      }
+
+      %---------------------------------------------------------------------------
+      % watermark for non final mode
+      %---------------------------------------------------------------------------
+      \bool_if:NT \g_sdaps_draft_bool {
+        \node [rotate=60,scale=10,text~opacity=0.2,color=red]
+           at (current~page.center) {\textsc{draft}};
+      }
+    \end{tikzpicture}
+
+    \group_end:
+  } {
+    \int_gincr:N\g_sdaps_page_int
+  }
+}
+
+
+%    \end{macrocode}
+%
+% \subsection{Starting/Ending an SDAPS context}
+%
+% These need to be used to begin rendering into an SDAPS context and finishing
+% everything off.
+%
+% Note that this base package does not automatically call
+% \textbackslash sdaps\_page\_end: for
+% you, so if you are using this class directly you will need to make sure that
+% this handler is called after all form elements on a page. An easy of doing
+% this is to call it from inside the page footer.
+%
+%
+%    %\begin{macrocode}
+
+\bool_new:N \g__sdaps_last_page_bool
+
+\cs_new_protected_nopar:Nn \sdaps_begin: {
+  \bool_gset:Nn \g__sdaps_last_page_bool \c_false_bool
+
+  % TODO: We really want to make sure nobody modifies the values after \sdaps_begin:
+  \sdaps_info_write:x{Duplex=\bool_if:NTF \g_sdaps_twoside_bool {true} {false}}
+  \sdaps_info_write:x{PrintQuestionnaireId=\bool_if:NTF \g_sdaps_print_questionnaire_id_bool {1} {0}}
+  \sdaps_info_write:x{
+    PageSize=\the\paperwidth, \the\paperheight
+  }
+  \sdaps_info_write:x{Style=\g_sdaps_style_tl}
+  \sdaps_info_write:x{CheckMode=\g_sdaps_checkmode_tl}
+  \sdaps_info_write:x{GlobalID=\g_sdaps_global_id_tl}
+  \sdaps_info_write:x{GlobalIDLabel=\g_sdaps_global_id_label_tl}
+
+  \int_gset:Nn \g_sdaps_page_int { 0 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_end: {
+  % Note that using \sdaps_info_write_x:n may not work in some cases.
+  % For this reason we write the out the Pages counter after each page,
+  % which is fine to do.
+  % Note that this means that the below "hack" to make onesided documents
+  % work even in twoside (duplex) mode may not always work either.
+
+  \bool_gset:Nn \g__sdaps_last_page_bool \c_true_bool
+}
+
+
+%
+%    \end{macrocode}
+%
+
+% \Finale
+\endinput


Property changes on: trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.ins	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapsbase.ins	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,55 @@
+%%
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%%
+%% 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 Benjamin Berg.
+%%
+
+\input l3docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/cskeleton}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+
+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.
+
+\endpreamble
+
+\generate{\file{sdapsbase.sty}{\from{sdapsbase.dtx}{package}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{*                                                           *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX:                    *}
+\Msg{*                                                           *}
+\Msg{*     sdapsbase.cls                                         *}
+\Msg{*                                                           *}
+\Msg{* To produce the documentation run the file sdapsbase.dtx   *}
+\Msg{* through LaTeX.                                            *}
+\Msg{*                                                           *}
+\Msg{* Happy TeXing!                                             *}
+\Msg{*                                                           *}
+\Msg{*************************************************************}
+
+\endbatchfile

Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.dtx	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,1134 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%
+% 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 Benjamin Berg.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{sdapsclassic.dtx}
+%</driver>
+%<class>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<class>\ProvidesClass{sdapsclassic}
+%<*class>
+    [2015/08/02 v0.1 Initial version of SDAPS classic class]
+%</class>
+%
+%<*driver>
+\documentclass{ltxdoc}
+%\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+  \DocInput{sdapsclassic.dtx}
+\end{document}
+%</driver>
+% \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/01/14}{Initial version}
+%
+% \GetFileInfo{sdapsclassic.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+% 
+%
+% \title{The \textsf{sdapsclassic} package\thanks{This document
+%   corresponds to \textsf{sdapsclassic}~\fileversion, dated \filedate.}}
+% \author{Benjamin Berg \\ \texttt{benjamin at sipsolutions.net}}
+%
+% \maketitle
+%
+% \section{Documentation}
+%
+% Please refer to \url{https://sdaps.org/class-doc} for documentation.
+%
+% \StopEventually{\PrintChanges\PrintIndex}
+%
+% \section{Implementation}
+%
+% This package uses the \LaTeX3 language internally, so we need to enable it.
+%    \begin{macrocode}
+% We need at least 2011-08-23 for \keys_set_known:nnN
+\RequirePackage{expl3}[2011/08/23]
+%\RequirePackage{xparse}
+\ExplSyntaxOn
+
+\RequirePackage{sdapsbase}
+\RequirePackage{sdapslayout}
+
+% We use verbatim to grab the content of the questionnaire envorinment into a
+% temporary file. This way we can input it multiple times for repeating the
+% the content.
+
+\RequirePackage{verbatim}
+
+
+%    \end{macrocode}
+% Now a lot of LaTeX 2e stuff that is basically just a copy of the old class.
+% Would be nicer to redo this with xparse, but what the heck.
+%    \begin{macrocode}
+
+\RequirePackage{scrkbase}
+
+
+%-------------------------------------------------------------------------------
+% option processing
+%-------------------------------------------------------------------------------
+
+% Whether the main SDAPS program will be used
+\DeclareOption{disable_recognition}{\bool_gset_false:N\g_sdaps_recognition_bool}
+
+% Whether sdaps should print the questionnaire id
+\DeclareOption{no_print_questionnaire_id}{\bool_gset_false:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}}}
+\DeclareOption{print_questionnaire_id}{\bool_gset_true:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{NONE}}}
+
+% pass unknown options to scrartcl
+\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrartcl}}
+
+% We need this if right now as it is set by sdaps.opt.
+\newif\if at sdaps@draft\@sdaps at drafttrue
+\DeclareOption{final}{\@sdaps at draftfalse}
+
+\tl_gset:Nn\g_sdaps_style_tl{qr}%
+
+
+\DefineFamily{SDAPS}
+\DefineFamilyMember{SDAPS}
+\DefineFamilyKey{SDAPS}{sdaps_style}[qr]%
+{%
+  \KOMA at set@ncmdkey{sdaps_style}{@tempa}{%
+    {code128}{0},%
+    {custom}{1},%
+    {qr}{2}%
+  }{#1}%
+  \ifcase \@tempa\relax
+    \tl_gset:Nn\g_sdaps_style_tl{code128}%
+  \or
+    \tl_gset:Nn\g_sdaps_style_tl{custom}%
+  \or
+    \tl_gset:Nn\g_sdaps_style_tl{qr}%
+  \fi
+}
+
+\DefineFamilyKey{SDAPS}{twoside_barcode}[both]%
+{%
+  \KOMA at set@ncmdkey{twoside_barcode}{@tempa}{%
+    {both}{0},%
+    {front}{1},%
+    {back}{2}%
+  }{#1}%
+  \ifcase \@tempa\relax
+    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{both}%
+  \or
+    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{front}%
+  \or
+    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{back}%
+  \fi
+}
+
+\DefineFamilyKey{SDAPS}{checkmode}[checkcorrect]%
+{%
+  \KOMA at set@ncmdkey{checkmode}{@tempa}{%
+    {checkcorrect}{0},%
+    {check}{1},%
+    {fill}{2}%
+  }{#1}%
+  \ifcase \@tempa\relax
+    \tl_gset:Nn\g_sdaps_checkmode_tl{checkcorrect}%
+  \or
+    \tl_gset:Nn\g_sdaps_checkmode_tl{check}%
+  \or
+    \tl_gset:Nn\g_sdaps_checkmode_tl{fill}%
+  \fi
+}
+
+
+\DefineFamilyKey{SDAPS}{globalid}[]
+{
+  \tl_gset:Nn \g_sdaps_global_id_tl { #1 }
+}
+
+\DefineFamilyKey{SDAPS}{globalidlabel}[]
+{
+  \tl_gset:Nn \g_sdaps_global_id_label_tl { #1 }
+}
+
+% Set default options for scrartcl. Is this done correctly?
+\PassOptionsToClass{headings=small}{scrartcl}
+\PassOptionsToClass{twoside}{scrartcl}
+% Well, I don't think that this worked in the past ...
+%\PassOptionsToClass{10pt}{scrartcl}
+
+\seq_new:N \g_sdaps_questionnaire_ids_seq
+\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}}
+
+% process given options
+\FamilyProcessOptions{SDAPS}\relax
+
+%-------------------------------------------------------------------------------
+% load base-class
+%-------------------------------------------------------------------------------
+\LoadClass{scrartcl}
+
+
+%-------------------------------------------------------------------------------
+% required packages
+%-------------------------------------------------------------------------------
+% geometry package
+\RequirePackage{geometry}
+% Corner mark postions plus 2mm, except for the header
+\geometry{top=12mm}
+\geometry{bottom=14mm}
+\geometry{hmargin=12mm}
+\geometry{includeheadfoot}
+\geometry{headheight=\baselineskip}
+\geometry{headsep=\baselineskip}
+% QR code size plus 2mm
+\geometry{footskip=12mm}
+
+% ifthen package
+\RequirePackage{ifthen}
+
+% fontenc package
+\RequirePackage[T1]{fontenc}
+
+% color
+\RequirePackage{color}
+
+% Symbols (boxes)
+\RequirePackage{amssymb}
+
+% For writing out the page number of boxes
+\RequirePackage{refcount}
+
+% Defines the LastPage label
+\RequirePackage{lastpage}
+
+% Environment creation that gets the body as a macro
+\RequirePackage{environ}
+
+% headers and footers
+\usepackage{scrlayer-scrpage}
+\clearpairofpagestyles
+\chead*{\@title}
+\cfoot*{\sdapspagemark}
+
+% hyperrefs
+\RequirePackage{url}
+\RequirePackage{hyperref}
+\hypersetup{%
+  breaklinks,%
+  baseurl       = http://,%
+  pdfborder     = 0 0 0,%
+  pdfpagemode   = UseNone,%
+  pdfcreator    = \LaTeX{} with `sdaps' class,%
+  pdfproducer   = \LaTeX{}
+}
+
+% graphics
+\RequirePackage{graphicx}
+
+% Section formatting
+\RequirePackage{sectsty}
+
+% table of fixed width
+\RequirePackage{tabularx}
+
+% Babel (needed for the translator)
+\RequirePackage{babel}
+
+% Translation
+\RequirePackage{translator}
+\usedictionary{translator-sdaps-dictionary}
+
+
+% Execute sdaps.opt file to allow SDAPS to override any options
+\InputIfFileExists{sdaps.opt}{}{}
+
+
+%-------------------------------------------------------------------------------
+%                class definition
+%-------------------------------------------------------------------------------
+% minimal base settings
+
+\setlength\lineskip{1\p@}
+\setlength\normallineskip{1\p@}
+\renewcommand\baselinestretch{}
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{1.0ex \@plus 1.5ex \@minus -0.25ex}
+%\setlength{\parskip}{0pt}
+\setlength\columnsep{10\p@}
+\setlength\columnseprule{0\p@}
+% Redefine the spacings for \subsection ie. \questions.
+\renewcommand\section{\@startsection {section}{1}{\z@}%
+      {-\parskip}%
+      {\parskip}%
+      {\normalfont\Large\bfseries\SS at sectfont}}
+\renewcommand\subsection{
+  \@startsection{subsection}{2}{\z@}%
+      {0.5\parskip}%
+      {0.25\parskip}% These are deleted again for questions
+      {}%
+}
+\pagestyle{scrheadings}
+\pagenumbering{arabic}
+\raggedbottom
+%\flushbottom
+\onecolumn
+
+%
+% Fonts for the "classic" class
+%
+
+%common font settings
+\newkomafont{questionfont}{}
+\newkomafont{choicefont}{}
+%singlemark
+\newkomafont{singlemarkquestionfont}{\usekomafont{questionfont}}
+\newkomafont{singlemarkchoicefont}{\usekomafont{choicefont}}
+%markgroup
+\newkomafont{marklinequestionfont}{\usekomafont{questionfont}}
+\newkomafont{marklinechoicefont}{\usekomafont{choicefont}}
+%choicequestion
+\newkomafont{choiceitemfont}{\usekomafont{choicefont}}
+%choicegroup
+\newkomafont{choicegroupchoicefont}{\usekomafont{questionfont}}
+\newkomafont{choicegrouplinefont}{\usekomafont{choicefont}}
+
+
+%    \end{macrocode}
+%
+% \subsection{A bunch of old commands}
+%
+%
+%    \begin{macrocode}
+
+
+\providecommand{\addinfo}[2]{
+  \sdaps_info_write:x{\unexpanded{#1}=\unexpanded{#2}}
+}
+
+\newcommand\qid{\tl_use:N \g__sdaps_questionnaire_id_tl}
+
+\def\question#1{%
+  \tl_if_empty:nTF{#1}{
+    \refstepcounter{subsection}%
+    \par%
+  } {
+    % #1 is nonempty
+    \subsection{\usekomafont{questionfont}\strut\ignorespaces#1}%
+    % This is extra spacing after the subsection is removed. By doing this we
+    % get exactly one \parskip
+    \nobreak%
+    \vspace{-0.25\parskip}%
+    \nobreak%
+  }
+}
+
+\newenvironment{info}{%
+  \group_begin:
+    {
+      \par
+      % Prevent top skip glue from being inserted at the start of the page
+      \topskip=0pt
+      \noindent\hrule height 1pt%
+      \nobreak
+      \vspace{-\parskip}
+      \nobreak
+      \vspace{0.5ex}
+      \nobreak
+    }
+}{%
+    \par
+    \nobreak
+    \vspace{-\parskip}
+    \nobreak
+    \vspace{0.5ex}
+    \nobreak
+    \noindent\hrule height 1pt
+  \group_end:
+}
+
+
+\definecolor{sectionbgcolor}{gray}{0.8}
+\definecolor{sectionfgcolor}{gray}{0.0}
+
+\newcommand{\sectbox}[1]{%
+ \noindent\protect\colorbox{sectionbgcolor}{%
+   \@tempdima=\hsize
+   \advance\@tempdima by-2\fboxsep
+   \protect\parbox{\@tempdima}{%
+     \smallskip
+     \raggedright % extra commands here
+     \color{sectionfgcolor}\usekomafont{section}{#1} \smallskip
+    }%
+  }%
+}
+
+\sectionfont{\sectbox}
+
+\setkomafont{disposition}{\normalfont}
+\addtokomafont{section}{\bfseries\sffamily}
+
+
+
+
+
+% Not sure why we even had this ...
+\def\smallskip{\vspace\smallskipamount}
+\def\medskip{\vspace\medskipamount}
+\def\bigskip{\vspace\bigskipamount}
+\newskip\smallskipamount \smallskipamount=3pt  plus 1pt minus 1pt
+\newskip\medskipamount   \medskipamount  =6pt  plus 2pt minus 2pt
+\newskip\bigskipamount   \bigskipamount  =12pt plus 4pt minus 4pt
+
+
+
+\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { nVVnn }
+\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { VVVnn }
+\cs_generate_variant:Nn \int_set:Nn { NV }
+
+
+%    \end{macrocode}
+%
+% \subsection{The different question environments}
+%
+%
+%    \begin{macrocode}
+
+
+\bool_new:N \g__sdaps_classic_have_section
+\bool_gset_false:N \g__sdaps_classic_have_section
+\cs_new_eq:NN\_sdapsclassic_origsection\section
+\renewcommand{\section}[1]{
+  \bool_if:NT \g__sdaps_classic_have_section {
+    \sdaps_qobject_end:n { section }
+  }
+
+  \_sdapsclassic_origsection{#1}
+  \bool_gset_true:N \g__sdaps_classic_have_section
+  \sdaps_qobject_begin:nnn { section }{ Head }{ #1 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_classic_ensure_section: {
+  \bool_if:NF \g__sdaps_classic_have_section {
+    % This is a bad hack to make the numbering start with zero
+    \int_gdecr:N \g__sdaps_object_id_int
+    \bool_gset_true:N \g__sdaps_classic_have_section
+    \sdaps_qobject_begin:nnn { section }{ Head }{ }
+      \sdaps_context_hook_end:n { \bool_gset_false:N \g__sdaps_classic_have_section }
+  }
+}
+
+
+\tl_new:N \l_sdaps_classic_textbox_var_tl
+\tl_new:N \l_sdaps_classic_textbox_text_tl
+\keys_define:nn { sdapsclassic / textbox }
+{
+  var        .tl_set:N   = \l_sdaps_classic_textbox_var_tl,
+  text       .tl_set:N   = \l_sdaps_classic_textbox_text_tl,
+}
+
+% With star non-expanding, without start expanding
+\providecommand{\textbox}{\@ifstar
+    {\bool_set_false:N \l_tmpa_bool \_sdaps_classic_textbox }
+    {\bool_set_true:N \l_tmpa_bool \_sdaps_classic_textbox }
+}
+
+\providecommand{\_sdaps_classic_textbox}[3][]
+{
+  \keys_set:nn { sdapsclassic / textbox } { #1 }
+
+  \sdaps_classic_ensure_section:
+  \question{#3}
+
+  \tl_if_empty:NTF \l_sdaps_classic_textbox_text_tl {
+    \sdaps_qobject_begin:nnn { textbox } { Text } { #3 }
+  } {
+    \sdaps_qobject_begin:nnV { textbox } { Text } \l_sdaps_classic_textbox_text_tl
+  }
+
+  \bool_if:NTF \l_tmpa_bool {
+    \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 1 }
+  }{
+    \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 0 }
+  }
+
+  \sdaps_qobject_end:n { textbox }
+}
+
+\newcounter{markcheckboxcount}
+\setcounter{markcheckboxcount}{5}
+
+
+\tl_new:N \l_sdaps_singlemark_var_tl
+\int_new:N \l_sdaps_singlemark_count_int
+
+\keys_define:nn { sdaps / singlemark }
+{
+  var        .tl_set:N   = \l_sdaps_singlemark_var_tl,
+
+  % 0 is equivalent to using the markcheckboxcount counter
+  count      .int_set:N  = \l_sdaps_singlemark_count_int,
+  count      .initial:n  = 0,
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_prepare:NN
+{
+  \group_begin:
+    \sdaps_context_begin_local:
+    \sdaps_context_disable_writing:
+    \bool_if:NT #1 {
+      \sdaps_context_append:nn { singlechoice } { draw_check=true }
+      \sdaps_context_append:nn { multichoice } { draw_check=true }
+    }
+    \bool_if:NT #2 {
+      \sdaps_context_append:nn { singlechoice } { fill=black }
+      \sdaps_context_append:nn { multichoice } { fill=black }
+    }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_single:
+{
+    \sdaps_checkbox_set_type:n { singlechoice }
+    \sdaps_checkbox:nn { } { }
+  \group_end:
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_multi:
+{
+    \sdaps_checkbox_set_type:n { multichoice }
+    \sdaps_checkbox:nn { } { }
+  \group_end:
+  \ignorespaces
+}
+
+\providecommand{\checkbox}{
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_false_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+\providecommand{\checkedbox}{%
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_false_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+\providecommand{\filledbox}{%
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_true_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+\providecommand{\correctedbox}{%
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_true_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+
+\providecommand*{\singlemark}[4][]{%
+  \sdaps_classic_ensure_section:
+
+  \group_begin:
+
+  \keys_set:nn { sdaps / singlemark } { #1 }
+
+  \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } {
+    \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount }
+  }
+
+  \question{#2}%
+
+  \sdaps_qobject_begin:nnn { singlemark } { range } { #2 }
+
+  \sdaps_checkbox_set_type:n { singlechoice }
+
+  \tl_if_empty:NF \l_sdaps_singlemark_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl
+  }
+
+  \sdaps_range:nnn{lower}{0}{#3}
+  \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4}
+
+  \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}X}
+    {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} &
+      \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & }
+    {\usekomafont{singlemarkchoicefont}#4\hfill}\\%
+  \end{tabularx}%
+
+  \sdaps_qobject_end:n { singlemark }
+  \group_end:
+}
+
+
+\providecommand*{\singlemarkother}[5][]{%
+  \sdaps_classic_ensure_section:
+
+  \group_begin:
+
+  \keys_set:nn { sdaps / singlemark } { #1 }
+
+  \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } {
+    \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount }
+  }
+
+  \question{#2}%
+
+  \sdaps_qobject_begin:nnn { singlemark } { range } { #2 }
+
+  \sdaps_checkbox_set_type:n { singlechoice }
+
+  \tl_if_empty:NF \l_sdaps_singlemark_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl
+  }
+
+  \sdaps_range:nnn{lower}{0}{#3}
+  \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4}
+  \sdaps_answer:n{#5}
+
+  \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}XX}
+    {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} &
+      \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & }
+    {\usekomafont{singlemarkchoicefont}#4} &
+    {\usekomafont{singlemarkchoicefont}\sdaps_checkbox:nn { _0 } { 0 } {} ~ #5\hfill}\\%
+  \end{tabularx}%
+
+  \sdaps_qobject_end:n { singlemark }
+  \group_end:
+}
+
+\dim_new:N \l__sdaps_classic_choiceitem_pad_dim
+\int_new:N \l__sdaps_classic_choiceitem_cols_int
+\int_new:N \l__sdaps_classic_choiceitem_col_int
+\coffin_new:N \l__sdaps_classic_choicequestion_coffin
+
+\msg_new:nnn { sdapsclassic } { choicequestion_wrong_mode } { Mode~should~always~be~vertical~inside~a~choicequestion.\\ This~likely~means~that~the~choicequestion~contains~content~other~than~one~of~the~permissable~macros. }
+
+\msg_new:nnn { sdapsclassic } { choicequestion_no_text } { Textboxes~cannot~be~used~in~singlechoice~questions.\\
+It~is~currently~not~supported~to~use~text~answers~in~single~choice~questions.~Until~support~is~added~
+you~can~get~similar~results~by~temporarily~changing~the~checkbox~style. }
+
+\msg_new:nnn { sdapsclassic } { choicequestion_unknown_key } { The~key~'#1'~is~unknown.\\If~you~are~migrating~from~the~old~class~then~you~need~to~add~'cols='~to~specify~the~number~of~columns. }
+
+\tl_new:N \l_sdaps_choicquestion_type_tl
+\tl_new:N \l_sdaps_choicequestion_var_tl
+\tl_new:N \l_sdaps_choicequestion_text_tl
+\int_new:N \l_sdaps_choicequestion_cols_int
+
+\keys_define:nn { sdaps / choicequestion }
+{
+  var        .tl_set:N   = \l_sdaps_choicequestion_var_tl,
+  text       .tl_set:N   = \l_sdaps_choicequestion_text_tl,
+  colsep     .dim_set:N  = \l_sdaps_choicequestion_colsep_dim,
+  colsep     .initial:n  = 6pt,
+  rowsep     .dim_set:N  = \l_sdaps_choicequestion_rowsep_dim,
+  rowsep     .initial:n  = 0pt,
+  cols       .int_set:N  = \l_sdaps_choicequestion_cols_int,
+  cols       .initial:n  = 3,
+
+  type       .choices:nn  = { multichoice, singlechoice } { \tl_set:Nx \l_sdaps_choicquestion_type_tl { \l_keys_choice_tl } },
+  type       .initial:n   = { multichoice },
+
+  singlechoice .meta:n    = { type=singlechoice },
+  multichoice  .meta:n    = { type=multichoice },
+
+  unknown .code:n = \msg_error:nnV { sdapsclassic } { choicequestion_unknown_key } \l_keys_key_tl
+}
+
+
+\tl_new:N \l_sdaps_choicequestion_choice_var_tl
+\tl_new:N \l_sdaps_choicequestion_choice_val_tl
+\tl_new:N \l_sdaps_choicequestion_choice_text_tl
+
+\keys_define:nn { sdaps / choicequestion / choice }
+{
+  var        .tl_set:N   = \l_sdaps_choicequestion_choice_var_tl,
+  val        .tl_set:N   = \l_sdaps_choicequestion_choice_val_tl,
+  text       .tl_set:N   = \l_sdaps_choicequestion_choice_text_tl,
+}
+
+
+\newenvironment{choicequestion}[2][]{
+  \group_begin:
+
+  \sdaps_classic_ensure_section:
+
+  \tl_clear:N \l_sdaps_choicequestion_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_text_tl
+
+  \keys_set:nn { sdaps / choicequestion } { #1 }
+
+  \question{#2}%
+
+  % Setup the context
+  \tl_if_eq:VnTF \l_sdaps_choicquestion_type_tl { multichoice } {
+    \tl_set:Nn \l_tmpa_tl { Choice }
+  } {
+    \tl_set:Nn \l_tmpa_tl { Option }
+  }
+  \tl_if_empty:NTF \l_sdaps_choicequestion_text_tl {
+    \sdaps_qobject_begin:nVn { choicequestion } \l_tmpa_tl { #2 }
+  } {
+    \sdaps_qobject_begin:nVV { choicequestion } \l_tmpa_tl \l_sdaps_choicequestion_text_tl
+  }
+
+  \sdaps_checkbox_set_type:V \l_sdaps_choicquestion_type_tl
+
+  \tl_if_empty:NF \l_sdaps_choicequestion_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_choicequestion_var_tl
+  }
+
+  \dim_set:Nn \l__sdaps_classic_choiceitem_pad_dim { 1ex }
+
+  \int_set:NV \l__sdaps_classic_choiceitem_cols_int \l_sdaps_choicequestion_cols_int
+  \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 }
+  \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
+
+  % We have to be in vertical mode at this point.
+  \if_mode_vertical:
+    % Nothing
+  \else:
+    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
+  \fi:
+
+  % It is important to look like a paragraph, otherwise latex thinks there is
+  % nothing between the two question sections and doesn't insert a voluntary
+  % page break.
+  \the\everypar
+
+  % Paragraph like spacing
+  \vspace{\parskip}
+
+  \def\choicequestion_clubpenalty{\penalty\clubpenalty\def\choicequestion_clubpenalty{\relax}\def\choicequestion_clubpenalty{\penalty\widowpenalty}}
+  \def\choicequestion_widowpenalty{}
+}{
+  \if_mode_vertical:
+    % Nothing
+  \else:
+    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
+  \fi:
+
+  \choicequestion_widowpenalty
+  \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin }
+  \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
+  \sdaps_qobject_end:n { choicequestion }
+
+  \group_end:
+  \vspace{\parskip}
+}
+
+\newenvironment{optionquestion}[2][]{
+  \choicequestion[singlechoice,#1]{#2}
+} {
+  \endchoicequestion
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_line_shipout_add:Nn
+{
+  % We have to be in vertical mode at this point.
+  \if_mode_vertical:
+    % Nothing
+  \else:
+    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
+  \fi:
+
+  % Is linewidth the right thing here?
+  \int_compare:nT { \l__sdaps_classic_choiceitem_col_int + #2 > \l__sdaps_classic_choiceitem_cols_int } {
+    % We can only typeset a coffin in vertical mode if we use the box function
+    \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin }
+    \skip_vertical:N \l_sdaps_choicequestion_rowsep_dim
+    \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
+    \choicequestion_clubpenalty
+    \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 }
+  }
+
+  \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+  \dim_set:Nn \l_tmpa_dim { \l__sdaps_classic_choiceitem_col_int \l_tmpa_dim }
+
+  \coffin_join:NnnNnnnn \l__sdaps_classic_choicequestion_coffin { l } { H } #1 { l } { H } { \l_tmpa_dim } { 0pt }
+  \int_add:Nn \l__sdaps_classic_choiceitem_col_int { #2 }
+}
+
+\providecommand*{\choiceitem}[2][]{%
+  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl
+
+  \keys_set:nn { sdaps / choicequestion / choice } { #1 }
+
+  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
+   \sdaps_answer:n { #2 }
+  } {
+   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
+  }
+
+  \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\  }
+    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
+    \hskip \l_sdaps_choicequestion_colsep_dim
+    \hbox_unpack_clear:N \l_tmpa_box
+    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}}
+      \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #2 }
+    \end{minipage}
+  }
+
+  \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { 1 }
+  \ignorespaces
+}
+
+\providecommand*{\choicemulticolitem}[3][]{ %
+  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl
+
+  \keys_set:nn { sdaps / choicequestion / choice } { #1 }
+
+  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
+   \sdaps_answer:n { #3 }
+  } {
+   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
+  }
+
+  \dim_set:Nn \l_tmpa_dim { #2\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #2 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\  }
+    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
+    \hskip \l_sdaps_choicequestion_colsep_dim
+    \hbox_unpack_clear:N \l_tmpa_box
+    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}}
+      \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #3 }
+    \end{minipage}
+  }
+
+  \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { #2 }
+  \ignorespaces
+}
+
+\providecommand{\choiceitemtext}[4][]{%
+  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl
+
+  \keys_set:nn { sdaps / choicequestion / choice } { #1 }
+  % TODO: Warn if val has been set here
+
+  % If not multichoice then warn
+  \tl_if_eq:VnF \l_sdaps_choicquestion_type_tl { multichoice } {
+    \msg_error:nn { sdapsclassic } { choicequestion_no_text }
+  }
+
+  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
+   \sdaps_answer:n { #4 }
+  } {
+   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
+  }
+
+  \dim_set:Nn \l_tmpa_dim { #3\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #3 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \hskip \l_sdaps_choicequestion_colsep_dim
+    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - 2\l_sdaps_choicequestion_colsep_dim}}
+      \usekomafont{choiceitemfont}
+      \dim_set:Nn \l_tmpa_dim { #2 }
+      \dim_set:Nn \l_tmpb_dim { #2 }
+      \dim_set:Nn \l_tmpa_dim { 0.5 \l_tmpa_dim - 0.8ex }
+      \dim_set:Nn \l_tmpb_dim { 0.5 \l_tmpb_dim + 0.8ex }
+      \strut\ignorespaces \tl_trim_spaces:n { #4 } ~
+      \sdaps_textbox_hstretch:VVVnn \l_sdaps_choicequestion_choice_var_tl \l_tmpa_dim \l_tmpb_dim { 0pt } { 1 }
+    \end{minipage}
+  }
+
+  \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { #3 }
+  \ignorespaces
+}
+
+
+
+%    \end{macrocode}
+%
+% \subsection{The questionnaire environment}
+%
+% There is a bit of magic here. If (and only if) we need to render multiple
+% pages of the questionnaire, then we write the whole questionnaire into a
+% temporary file. This way we can input the file multiple times, which means
+% that environments changing the parser (e.g. verbatim) work properly.
+%
+%    \begin{macrocode}
+%
+
+\iow_new:N \l__sdaps_questionnaire_iow
+\bool_new:N \l__sdaps_questionnaire_parse_direct_bool
+
+\bool_new:N \l__sdaps_questionnaire_info_bool
+
+\keys_define:nn { sdaps / questionnaire }
+{
+  info         .bool_set:N  = \l__sdaps_questionnaire_info_bool,
+  info         .default:n   = true,
+  info         .initial:n   = true,
+
+  noinfo       .meta:n  = { info=false },
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_show_info:
+{
+  \bool_if:NT \l__sdaps_questionnaire_info_bool {
+    \begin{info}
+      \translate{infotext} \\[1ex]
+      \tl_if_eq:VnTF \g_sdaps_checkmode_tl { checkcorrect } {
+        \begin{tabularx}{\textwidth}{lXllll}
+          \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} & \hspace{1em} \correctedbox {} & \translate{info-corrected} \\
+          \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} & \hspace{1em} \correctedbox* {} & \translate{info-corrected} \\
+        \end{tabularx}
+      } {
+        \tl_if_eq:VnTF \g_sdaps_checkmode_tl { check } {
+          \begin{tabularx}{\textwidth}{lXll}
+            \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} \\
+            \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} \\
+          \end{tabularx}
+        } {
+          \tl_if_eq:VnTF \g_sdaps_checkmode_tl { fill } {
+            \begin{tabularx}{\textwidth}{lXll}
+              \checkbox & \strut \translate{info-multi} & \hspace{2em} \filledbox {} & \translate{info-fill} \\
+              \checkbox* & \strut \translate{info-single} & \hspace{2em} \filledbox* {} & \translate{info-fill} \\
+            \end{tabularx}
+          } {
+            \PackageError{sdaps}{Sorry, there is no help text for the checkmode you have choosen right now! Please pass the noinfo optional argument to the questionnaire environment!}\@ehb %
+          }
+        }
+      }
+    \end{info}
+  }
+  \ignorespaces
+}
+
+\newenvironment { questionnaire } [ 1 ] []
+{
+  \keys_set:nn { sdaps / questionnaire } { #1 }
+
+  \hypersetup{
+    pdfauthor     = \@author,
+    pdftitle      = \@title,
+    pdfsubject    = sdaps questionnaire \@title,
+    pdfkeywords   = sdaps questionnaire \@title
+  }
+
+  % If we only have one questionnaire ID, then parse the environment directly,
+  % otherwise write it into a temporary file and input it multiple times.
+  \group_begin:
+
+  \if at twoside
+    \bool_gset_true:N \g_sdaps_twoside_bool
+  \else
+    \bool_gset_false:N \g_sdaps_twoside_bool
+  \fi
+
+  % Enable all metadata writing by default
+  \sdaps_context_enable_writing:
+
+  \sdaps_info_write:x{Author=\exp_not:o{\@author}}
+  \sdaps_info_write:x{Title=\exp_not:o{\@title}}
+
+  \int_compare:nTF { \seq_count:N \g_sdaps_questionnaire_ids_seq <= 1 } {
+    \bool_set_true:N \l__sdaps_questionnaire_parse_direct_bool
+
+    % Set the questionnaire ID
+    \seq_gpop_left:NN \g_sdaps_questionnaire_ids_seq \l_tmpa_tl
+    \sdaps_set_questionnaire_id:V \l_tmpa_tl
+
+    % And, begin the questionnaire
+    \sdaps_begin:
+      \_sdaps_classic_show_info:
+  } {
+    \bool_set_false:N \l__sdaps_questionnaire_parse_direct_bool
+
+    % Write content into a file, see "verbatim" documentation for more information.
+    % TODO: Allow multiple temporary files by postfixing with integer?
+    \iow_open:Nn \l__sdaps_questionnaire_iow { \c_sys_jobname_str . questionnaire }
+    \cs_set_eq:NN\do\@makeother\dospecials
+    \catcode`\^^M\active
+    \def\verbatim at processline{
+      \iow_now:Nx \l__sdaps_questionnaire_iow {\the\verbatim at line}
+    }
+    \verbatim@
+  }
+} {
+  \bool_if:NTF \l__sdaps_questionnaire_parse_direct_bool {
+        % Just end everything, nothing else to do.
+        % Make sure we always end the current paragraph.
+        \par
+      \sdaps_end:
+
+    \group_end:
+  } {
+       % We are done inputting the questionnaire
+       \iow_close:N \l__sdaps_questionnaire_iow
+    \group_end:
+
+    % Now cycle through all IDS and output it again.
+    \group_begin:
+      \seq_map_inline:Nn \g_sdaps_questionnaire_ids_seq {
+        \sdaps_set_questionnaire_id:n { ##1 }
+
+        % Reset a lot of global LaTeX counters
+        \@ifundefined{c at page}{}{\setcounter{page}{1}}
+        \@ifundefined{c at part}{}{\setcounter{part}{0}}
+        \@ifundefined{c at chapter}{}{\setcounter{chapter}{0}}
+        \@ifundefined{c at paragraph}{}{\setcounter{paragraph}{0}}
+        \@ifundefined{c at subparagraph}{}{\setcounter{subparagraph}{0}}
+        \@ifundefined{c at section}{}{\setcounter{section}{0}}
+        \@ifundefined{c at subsection}{}{\setcounter{subsection}{0}}
+        \@ifundefined{c at subsubsection}{}{\setcounter{subsubsection}{0}}
+        \@ifundefined{c at equation}{}{\setcounter{equation}{0}}
+        \@ifundefined{c at figure}{}{\setcounter{figure}{0}}
+        \@ifundefined{c at table}{}{\setcounter{table}{0}}
+
+        \sdaps_begin:
+          \_sdaps_classic_show_info:
+          \input{ \c_sys_jobname_str . questionnaire }
+          % Make sure we always end the current paragraph.
+          \par
+        \sdaps_end:
+        \newpage
+
+        % Close the output file now (after the page has been shipped out)
+        % XXX: This is a bit of a hack, as disabling metadata writing has side effects
+        \iow_close:N \g_sdaps_infofile_iow
+
+      }
+    \group_end:
+  }
+}
+
+\def\sdapspagemark{
+  \sdaps_page_end:
+}
+
+%    \end{macrocode}
+%
+% \subsection{The group environments}
+%
+% Alias for the group environments.
+%
+%    \begin{macrocode}
+%
+
+
+\newenvironment { choicegroup } [ 2 ] []
+{
+  \group_begin:
+
+    \question{#2}
+
+    % Undefine the question (and choice) commands in local scope so that they
+    % can be redefined by choicearray without any issues.
+    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
+    \cs_set_eq:NN\question\tex_undefined:D
+    \cs_set_eq:NN\choice\tex_undefined:D
+
+    \begin{choicearray}[#1]{#2}
+      % XXX: This is a bit of a hack, set in global scope because the choicearray
+      %      environment does evil things with scopes.
+      \cs_gset_eq:NN\groupaddchoice\choice
+      \cs_gset_eq:NN\choiceline\question
+}
+{
+      \cs_gset_eq:NN\groupaddchoice\undefined
+      \cs_gset_eq:NN\choiceline\undefined
+
+    \end{choicearray}
+  \group_end:
+}
+
+\newenvironment { optiongroup } [ 2 ] []
+{
+  \group_begin:
+
+    \question{#2}
+
+    % Undefine the question (and choice) commands in local scope so that they
+    % can be redefined by optionarray without any issues.
+    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
+    \cs_set_eq:NN\question\tex_undefined:D
+    \cs_set_eq:NN\choice\tex_undefined:D
+
+    \begin{optionarray}[#1]{#2}
+      % XXX: This is a bit of a hack, set in global scope because the optionarray
+      %      environment does evil things with scopes.
+      \cs_gset_eq:NN\groupaddchoice\choice
+      \cs_gset_eq:NN\optionline\question
+}
+{
+      \cs_gset_eq:NN\groupaddoption\undefined
+      \cs_gset_eq:NN\optionline\undefined
+
+    \end{optionarray}
+  \group_end:
+}
+
+\newenvironment { markgroup } [ 2 ] []
+{
+  \group_begin:
+
+    \question{#2}
+
+    % Undefine the question (and choice) commands in local scope so that they
+    % can be redefined by choicearray without any issues.
+    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
+    \cs_set_eq:NN\range\tex_undefined:D
+
+    \begin{rangearray}[#1]{#2}
+      % XXX: This is a bit of a hack, set in global scope because the choicearray
+      %      environment does evil things with scopes.
+      \cs_gset_eq:NN\markline\range
+}
+{
+      \cs_gset_eq:NN\markline\undefined
+
+    \end{rangearray}
+  \group_end:
+}
+
+\ExplSyntaxOff
+
+%    \end{macrocode}
+
+% \Finale
+\endinput


Property changes on: trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.ins	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapsclassic.ins	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,55 @@
+%%
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%%
+%% 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 Benjamin Berg.
+%%
+
+\input l3docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/cskeleton}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+
+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.
+
+\endpreamble
+
+\generate{\file{sdapsclassic.cls}{\from{sdapsclassic.dtx}{class}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{*                                                           *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX:                    *}
+\Msg{*                                                           *}
+\Msg{*     sdapsclassic.cls                                      *}
+\Msg{*                                                           *}
+\Msg{* To produce the documentation run the file sdapsclassic.dtx*}
+\Msg{* through LaTeX.                                            *}
+\Msg{*                                                           *}
+\Msg{* Happy TeXing!                                             *}
+\Msg{*                                                           *}
+\Msg{*************************************************************}
+
+\endbatchfile

Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.dtx	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,790 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2014 by Henry Menke <henrimenke at gmail.com>
+% Copyright (C) 2015-2016 by Benjamin Berg <benjamin at sipsolutions.net>
+%
+% 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 Benjamin Berg.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{sdapslayout.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<package>\ProvidesPackage{sdapslayout}
+%<*package>
+    [2015/04/10 v0.1 Initial version of SDAPS layout package]
+%</package>
+%
+%<*driver>
+\documentclass{l3doc}
+\usepackage{sdapslayout}[2015/04/10]
+\EnableCrossrefs
+%\CodelineIndex
+\RecordChanges
+\begin{document}
+  \DocInput{sdapslayout.dtx}
+\end{document}
+%</driver>
+% \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/01/14}{Initial version}
+%
+% \GetFileInfo{sdapslayout.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+% 
+%
+% \title{The \textsf{sdapslayout} package\thanks{This document
+%   corresponds to \textsf{sdapslayout}~\fileversion, dated \filedate.}}
+% \author{Benjamin Berg \\ \texttt{benjamin at sipsolutions.net}}
+%
+% \maketitle
+%
+% \section{Documentation}
+%
+% Please refer to \url{https://sdaps.org/class-doc} for documentation.
+%
+% \section{Implementation}
+%
+% This package uses the \LaTeX3 language internally, so we need to enable it.
+%    \begin{macrocode}
+% We need at least 2011-08-23 for \keys_set_known:nnN
+\RequirePackage{expl3}[2011/08/23]
+%\RequirePackage{xparse}
+\ExplSyntaxOn
+%    \end{macrocode}
+%
+% And we need a number of other packages.
+%    \begin{macrocode}
+\ExplSyntaxOff
+
+\RequirePackage{sdapsbase}
+\RequirePackage{sdapsarray}
+\RequirePackage{xparse}
+
+
+\ExplSyntaxOn
+
+%    \end{macrocode}
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+% \subsection{Choice Question Layout}
+%
+% \subsubsection{Choice Question Matrix Layout}
+%
+% The following macros provide the funcitonality to layout choice questions in
+% a matrix like fashion.
+%
+%    \begin{macrocode}
+
+\tl_new:N \l_sdaps_choicearray_qobject_type_tl
+\bool_new:N \l_sdaps_choicearray_horizontal_bool
+\tl_new:N \l_sdaps_choicearray_var_tl
+\tl_new:N \l_sdaps_choicearray_text_tl
+\tl_new:N \l_sdaps_choicearray_layouter_tl
+\tl_new:N \l_sdaps_choicearray_align_tl
+\tl_new:N \l_sdaps_choicearray_extra_tl
+\tl_new:N \l_sdaps_choicearray_type_tl
+\tl_new:N \l_sdaps_choice_var_tl
+\tl_new:N \l_sdaps_choice_val_tl
+\tl_new:N \l_sdaps_choice_text_tl
+\tl_new:N \l_sdaps_question_var_tl
+\tl_new:N \l_sdaps_question_text_tl
+\clist_new:N \l_sdaps_question_range_clist
+\int_new:N \g_sdaps_choices_box_int
+\seq_new:N \g_sdaps_choices_filter_seq
+\seq_new:N \g_sdaps_choices_cell_seq
+\seq_new:N \g_sdaps_choices_text_seq
+
+\keys_define:nn { sdaps / choicearray }
+{
+  horizontal .bool_set:N = \l_sdaps_choicearray_horizontal_bool,
+  horizontal .default:n  = true,
+  horizontal .initial:n  = true,
+  vertical   .bool_set_inverse:N = \l_sdaps_choicearray_horizontal_bool,
+  vertical   .default:n  = true,
+  var        .tl_set:N   = \l_sdaps_choicearray_var_tl,
+  text       .tl_set:N   = \l_sdaps_choicearray_text_tl,
+  layouter   .tl_set:N   = \l_sdaps_choicearray_layouter_tl,
+  layouter   .initial:n  = default,
+  align      .tl_set:N   = \l_sdaps_choicearray_align_tl,
+  align      .initial:n  = { },
+
+  type       .choices:nn  = { multichoice, singlechoice } { \tl_set:Nx \l_sdaps_choicearray_type_tl { \l_keys_choice_tl } },
+  type       .initial:n   = { multichoice },
+
+  singlechoice .meta:n   = { type=singlechoice },
+  multichoice  .meta:n   = { type=multichoice },
+
+  noalign    .meta:n  = { align= },
+}
+
+\keys_define:nn { sdaps / choicearray / choice }
+{
+  var        .tl_set:N   = \l_sdaps_choice_var_tl,
+  val        .tl_set:N   = \l_sdaps_choice_val_tl,
+  text       .tl_set:N   = \l_sdaps_choice_text_tl,
+}
+
+\keys_define:nn { sdaps / choicearray / question }
+{
+  var        .tl_set:N   = \l_sdaps_question_var_tl,
+  text       .tl_set:N   = \l_sdaps_question_text_tl,
+
+  range      .clist_set:N   = \l_sdaps_question_range_clist,
+  range      .initial:n = {...}
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_preprocess:nn #1#2
+{
+  \keys_set_known:nnN { sdaps / choicearray } { #1 } \l_sdaps_choicearray_extra_tl
+
+  \tl_if_empty:NTF \l_sdaps_choicearray_text_tl {
+    \sdaps_qobject_begin:nnn { choicearray } { Head } { #2 }
+  } {
+    \sdaps_qobject_begin:nnV { choicearray } { Head } \l_sdaps_choicearray_text_tl
+  }
+
+  \tl_if_empty:NF \l_sdaps_choicearray_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_choicearray_var_tl
+  }
+
+  \sdaps_checkbox_set_type:V \l_sdaps_choicearray_type_tl
+  \tl_if_eq:VnTF \l_sdaps_choicearray_type_tl { multichoice } {
+    \tl_set:Nn \l_sdaps_choicearray_qobject_type_tl { Choice }
+  } {
+    \tl_set:Nn \l_sdaps_choicearray_qobject_type_tl { Option }
+  }
+}
+\cs_generate_variant:Nn \_sdaps_choicearray_preprocess:nn { Vn }
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_postprocess:
+{
+  \sdaps_qobject_end:n { choicearray }
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_process_choice_insert_tail_after:w {
+  \bgroup
+    \group_insert_after:N \_sdaps_choicearray_process_choice_tail:
+    \sdaps_array_nested_alignenv:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_choice_tail: {
+  \ignorespaces
+}
+
+\cs_new_nopar:Nn \_sdaps_choicearray_grab_choice:n {
+  \seq_gput_right:Nn \g_sdaps_choices_text_seq { #1 }
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_choicearray_process_choice_tail:
+}
+
+\cs_new_nopar:Npn \_sdaps_choicearray_process_choice:nw #1
+{
+  % This modifies grouping so it has to be at the start
+  \sdaps_array_alignment:
+  \leavevmode
+
+  \tl_clear:N \l_sdaps_choice_var_tl
+  \tl_clear:N \l_sdaps_choice_val_tl
+  \tl_clear:N \l_sdaps_choice_text_tl
+
+  \keys_set:nn { sdaps / choicearray / choice } { #1 }
+
+  \int_gincr:N \g_sdaps_choices_box_int
+
+  \tl_if_empty:NT \l_sdaps_choice_var_tl {
+    % Prefix with _ to force prefixing with generated variable
+    \tl_set:Nx \l_sdaps_choice_var_tl { \int_use:N \g_sdaps_choices_box_int }
+  }
+  \tl_if_empty:NT \l_sdaps_choice_val_tl {
+    \tl_set:Nx \l_sdaps_choice_val_tl { \int_use:N \g_sdaps_choices_box_int }
+  }
+
+  \tl_if_eq:VnTF \l_sdaps_choicearray_type_tl { multichoice } {
+    \seq_gput_right:NV \g_sdaps_choices_filter_seq \l_sdaps_choice_var_tl
+  } {
+    \seq_gput_right:NV \g_sdaps_choices_filter_seq \l_sdaps_choice_val_tl
+  }
+
+  \seq_gput_right:Nx \g_sdaps_choices_cell_seq { \exp_not:n { \sdaps_checkbox:nn } { _ \l_sdaps_choice_var_tl } { \l_sdaps_choice_val_tl } }
+
+  \tl_if_empty:NTF \l_sdaps_choice_text_tl {
+    % We need to leave a command in the stream that grabs the next parameter
+    % and outputs it immediately
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_grab_choice:n
+  } {
+    % Nothing else to do
+    \seq_gput_right:NV \g_sdaps_choices_text_seq { \l_sdaps_choice_text_tl }
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_process_choice_insert_tail_after:w
+  }
+  \l_tmpa_token
+}
+\cs_generate_variant:Nn \_sdaps_choicearray_process_choice:nw { Vw }
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_question_grab:n {
+  \tl_set:Nn \l_sdaps_question_text_tl { #1 }
+
+  \_sdaps_choicearray_process_question_head:
+
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+
+  \_sdaps_choicearray_process_question_tail:
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_process_question_inline:w {
+  \_sdaps_choicearray_process_question_head:
+  \bgroup
+    \group_insert_after:N \_sdaps_choicearray_process_question_tail:
+    \sdaps_array_nested_alignenv:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_question_head: {
+  \sdaps_qobject_begin:nnV { choicearray_question } \l_sdaps_choicearray_qobject_type_tl \l_sdaps_question_text_tl
+
+  \tl_if_empty:NF \l_sdaps_question_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_question_var_tl
+  }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_question_tail: {
+  \seq_gclear:N \g_tmpa_seq
+  % l_tmpa_bool is whether we are in a run (i.e. ...)
+  \bool_set_false:N \l_tmpa_bool
+
+  \clist_gset_eq:NN \g_tmpa_clist \l_sdaps_question_range_clist
+  \clist_gpop:NN \g_tmpa_clist \l_tmpa_tl
+
+  \seq_map_inline:Nn \g_sdaps_choices_filter_seq {
+    \tl_if_eq:VnT \l_tmpa_tl { ... } {
+      \bool_set_true:N \l_tmpa_bool
+      \clist_gpop:NN \g_tmpa_clist \l_tmpa_tl
+    }
+
+    \tl_if_eq:VnTF \l_tmpa_tl { ##1 } {
+      \seq_gput_right:Nn \g_tmpa_seq { \c_true_bool }
+      \bool_set_false:N \l_tmpa_bool
+      \clist_gpop:NN \g_tmpa_clist \l_tmpa_tl
+    } {
+      % Append if we are handling a run of items and the current item is not
+      % the last one.
+      \bool_if:NTF \l_tmpa_bool {
+        \seq_gput_right:Nn \g_tmpa_seq { \c_true_bool }
+      } {
+        % Otherwise, ignore this item
+        \seq_gput_right:Nn \g_tmpa_seq { \c_false_bool }
+      }
+    }
+  }
+
+  \seq_gset_eq:NN \g_tmpb_seq \g_tmpa_seq
+
+  \seq_map_inline:Nn \g_sdaps_choices_text_seq {
+    \seq_gpop_left:NN \g_tmpa_seq \l_tmpa_tl
+    \tl_if_eq:VnT \l_tmpa_tl { \c_true_bool } {
+      \sdaps_answer:f { ##1 }
+    }
+  }
+
+  \seq_map_inline:Nn \g_sdaps_choices_cell_seq {
+    \sdaps_array_alignment:
+
+    \seq_gpop_left:NN \g_tmpb_seq \l_tmpa_tl
+    \tl_if_eq:VnT \l_tmpa_tl { \c_true_bool } {
+      ##1
+    }
+  }
+
+  \sdaps_qobject_end:n { choicearray_question }
+  \ignorespaces
+}
+
+\cs_new_nopar:Npn \_sdaps_choicearray_process_question:nw #1
+{
+  \sdaps_array_newline:
+  \leavevmode
+
+  \keys_set:nn { sdaps / choicearray / question } { #1 }
+
+  \tl_if_empty:NTF \l_sdaps_question_text_tl {
+    % We need to leave a command in the stream that grabs the next parameter,
+    % outputs it again, and finishes the question.
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_process_question_grab:n
+  } {
+    % Insert the question around the next argument
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_process_question_inline:w
+  }
+  \l_tmpa_token
+}
+\cs_generate_variant:Nn \_sdaps_choicearray_process_question:nw { Vw }
+
+
+
+%
+%    \end{macrocode}
+%
+%
+% \subsection{Range Question Layout}
+%
+% \subsubsection{Range Question Matrix Layout}
+%
+% The following macros provide the functionality to layout range/option
+% questions in a matrix like fashion.
+%
+%    \begin{macrocode}
+
+
+\tl_new:N \l_sdaps_rangearray_var_tl
+\tl_new:N \l_sdaps_rangearray_text_tl
+\tl_new:N \l_sdaps_rangearray_align_tl
+\tl_new:N \l_sdaps_rangearray_extra_tl
+\int_new:N \l_sdaps_rangearray_rangecount_int
+\bool_new:N \l_sdaps_rangearray_other_bool
+\tl_new:N \g_sdaps_question_var_tl
+\tl_new:N \g_sdaps_question_text_tl
+\tl_new:N \g_sdaps_question_lowertext_tl
+\tl_new:N \g_sdaps_question_uppertext_tl
+\tl_new:N \g_sdaps_question_othertext_tl
+
+\keys_define:nn { sdaps / rangearray }
+{
+  var        .tl_set:N   = \l_sdaps_rangearray_var_tl,
+  text       .tl_set:N   = \l_sdaps_rangearray_text_tl,
+  count      .int_set:N  = \l_sdaps_rangearray_rangecount_int,
+  count      .initial:n  = 5,
+  align      .tl_set:N   = \l_sdaps_rangearray_align_tl,
+  align      .initial:n  = { },
+  other      .bool_set:N = \l_sdaps_rangearray_other_bool,
+  other      .default:n  = true,
+  other      .initial:n  = false,
+}
+
+\keys_define:nn { sdaps / rangearray / question }
+{
+  var        .tl_gset:N   = \g_sdaps_question_var_tl,
+  text       .tl_gset:N   = \g_sdaps_question_text_tl,
+  upper      .tl_gset:N   = \g_sdaps_question_uppertext_tl,
+  lower      .tl_gset:N   = \g_sdaps_question_lowertext_tl,
+  other      .tl_gset:N   = \g_sdaps_question_othertext_tl,
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_preprocess:nn #1#2
+{
+  \keys_set_known:nnN { sdaps / rangearray } { #1 } \l_sdaps_rangearray_extra_tl
+
+  \tl_if_empty:NTF \l_sdaps_rangearray_text_tl {
+    \sdaps_qobject_begin:nnn { rangearray } { Head } { #2 }
+  } {
+    \sdaps_qobject_begin:nnV { rangearray } { Head } \l_sdaps_rangearray_text_tl
+  }
+
+  \sdaps_checkbox_set_type:n { singlechoice }
+
+  \tl_if_empty:NF \l_sdaps_rangearray_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_rangearray_var_tl
+  }
+}
+\cs_generate_variant:Nn \_sdaps_rangearray_preprocess:nn { Vn }
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_postprocess:
+{
+  \sdaps_qobject_end:n { rangearray }
+}
+
+% Before/After the different parts
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_question: {
+  \sdaps_array_newline:
+  % Note: This needs to be after sdaps_array_newline as the command may be
+  %       discarded otherwise (i.e. it does not make it into the output stream)
+  %       We also need to leave vmode here
+  \leavevmode
+  \sdaps_qobject_begin:nnV { rangearray_question } { Range } \g_sdaps_question_text_tl
+
+  \tl_if_empty:NF \g_sdaps_question_var_tl {
+    \sdaps_qobject_append_var:V \g_sdaps_question_var_tl
+  }
+
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_lower: {
+  % right align
+  \sdaps_array_alignment:
+  \leavevmode
+  \sdaps_range:nnV { lower } { 0 } \g_sdaps_question_lowertext_tl
+  \sdaps_if_rtl:F {
+    \hfill
+  }
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_upper: {
+  \sdaps_array_alignment:
+  \leavevmode
+  \sdaps_range:nnV { upper } { \l_sdaps_rangearray_rangecount_int - 1 } \g_sdaps_question_uppertext_tl
+  \sdaps_if_rtl:T {
+    \hfill
+  }
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_other: {
+  % Insert an extra empty column for further spacing
+  \sdaps_array_alignment:
+  \sdaps_array_alignment:
+  \leavevmode
+  \sdaps_answer:V \g_sdaps_question_othertext_tl
+  \sdaps_if_rtl:TF {
+    \hfill
+  } {
+    \sdaps_checkbox:nn { } { 0 } {} ~ {}
+  }
+  \ignorespaces
+}
+
+
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_question: {
+  % Insert an extra empty column for further spacing
+  \sdaps_array_alignment:
+  \tl_if_empty:NTF \g_sdaps_question_lowertext_tl {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_lower:n
+  } {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_lower:w
+  }
+  \l_tmpa_token
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_lower: {
+  % Insert the option checkbox column
+  \sdaps_if_rtl:T {
+    \hfill\kern 0pt
+  }
+  \sdaps_array_alignment:
+  \leavevmode
+  % Seems like right to left writing mode is not started without a paragraph
+  \sdaps_if_rtl:T { \beginR }
+  % Assume we have at least one checkbox
+  \sdaps_checkbox:nn { } { 1 }
+  \int_step_inline:nnnn { 2 } { 1 } { \l_sdaps_rangearray_rangecount_int } {
+    \hspace{1em} \sdaps_checkbox:nn { } { ##1 }
+  }
+  \sdaps_if_rtl:T { \endR }
+
+  \tl_if_empty:NTF \g_sdaps_question_uppertext_tl {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_upper:n
+  } {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_upper:w
+  }
+  \l_tmpa_token
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_upper: {
+  \sdaps_if_rtl:F {
+    \hfill\kern 0pt
+  }
+
+  \bool_if:NTF \l_sdaps_rangearray_other_bool {
+    \tl_if_empty:NTF \g_sdaps_question_othertext_tl {
+      \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_other:n
+    } {
+      \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_other:w
+    }
+  } {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_finish:
+  }
+  \l_tmpa_token
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_other: {
+  \sdaps_if_rtl:TF {
+    {} ~ \sdaps_checkbox:nn { } { 0 } {}
+  } {
+    \hfill\kern 0pt
+  }
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_finish: {
+  \sdaps_qobject_end:n { rangearray_question }
+  \ignorespaces
+}
+
+% Processors for inline processing/grabbing the argument
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_question:n {
+  \tl_gset:Nn \g_sdaps_question_text_tl { #1 }
+
+  \_sdaps_rangearray_process_question_before_question:
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_rangearray_process_question_after_question:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_lower:n {
+  \tl_gset:Nn \g_sdaps_question_lowertext_tl { #1 }
+
+  \_sdaps_rangearray_process_question_before_lower:
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_rangearray_process_question_after_lower:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_upper:n {
+  \tl_gset:Nn \g_sdaps_question_uppertext_tl { #1 }
+
+  \_sdaps_rangearray_process_question_before_upper:
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_rangearray_process_question_after_upper:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_other:n {
+  \tl_gset:Nn \g_sdaps_question_othertext_tl { #1 }
+
+  % If the text is empty, assume that this particular question does not have
+  % an alternative choice. Note that this column might not exist and this
+  % macro will not even be called in that case.
+  % If we skip the optional "other" option then we still need to insert the
+  % alignment to create the column.
+  \tl_if_empty:NTF \g_sdaps_question_othertext_tl {
+    \sdaps_array_alignment:
+    \leavevmode
+    \ignorespaces
+  } {
+    \_sdaps_rangearray_process_question_before_other:
+    \group_begin:
+      \sdaps_array_nested_alignenv:
+      #1
+    \group_end:
+    \_sdaps_rangearray_process_question_after_other:
+  }
+  \_sdaps_rangearray_process_question_finish:
+}
+
+
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_question:w {
+  \_sdaps_rangearray_process_question_before_question:
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_question:
+    \sdaps_array_nested_alignenv:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_lower:w {
+  \_sdaps_rangearray_process_question_before_lower:
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_lower:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_upper:w {
+  \_sdaps_rangearray_process_question_before_upper:
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_upper:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_other:w {
+  \_sdaps_rangearray_process_question_before_other:
+  % If we reach this macro then a text has been set for the other item. This
+  % means we never need to ignore the "other" parameter at this point.
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_other:
+    \group_insert_after:N \_sdaps_rangearray_process_question_finish:
+    \tex_let:D\next=
+}
+
+
+
+
+\cs_new_nopar:Npn \_sdaps_rangearray_process_question:nw #1
+{
+  % Is there a better way other than clearing these before parsing?
+  \tl_gclear:N \g_sdaps_question_var_tl
+  \tl_gclear:N \g_sdaps_question_text_tl
+  \tl_gclear:N \g_sdaps_question_uppertext_tl
+  \tl_gclear:N \g_sdaps_question_lowertext_tl
+  \tl_gclear:N \g_sdaps_question_othertext_tl
+
+  \keys_set:nn { sdaps / rangearray / question } { #1 }
+
+  \tl_if_empty:NTF \g_sdaps_question_text_tl {
+    % We need to leave a command in the stream that grabs the next parameter,
+    % outputs it again, and finishes the question.
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_question:n
+  } {
+    % We need to generate the question after the next group stops
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_question:w
+  }
+  \l_tmpa_token
+}
+\cs_generate_variant:Nn \_sdaps_rangearray_process_question:nw { Vw }
+
+
+
+%
+%    \end{macrocode}
+%
+%
+%
+% \subsection{Export user facing environments}
+%
+%    \begin{macrocode}
+%
+
+
+\newenvironment { choicearray } [ 2 ] []
+{
+  \group_begin:
+
+    \sdaps_context_get:nN { choicearray } \l_tmpa_tl
+    \tl_if_eq:NNT \l_tmpa_tl \q_no_value {
+      \tl_set:Nn \l_tmpa_tl {}
+    }
+
+    \tl_if_empty:nF { #1 } {
+      \tl_if_empty:NTF \l_tmpa_tl {
+        \tl_set:Nn \l_tmpa_tl { #1 }
+      } {
+        \tl_set:Nf \l_tmpa_tl { \l_tmpa_tl, #1 }
+      }
+    }
+
+    \_sdaps_choicearray_preprocess:Vn \l_tmpa_tl { #2 }
+    % Clear the variables
+    \seq_gclear:N \g_sdaps_choices_filter_seq
+    \seq_gclear:N \g_sdaps_choices_cell_seq
+    \seq_gclear:N \g_sdaps_choices_text_seq
+
+    \int_gzero:N \g_sdaps_choices_box_int
+
+    % Define new commands
+    \newcommand \choice [ 1 ] [] {
+      \_sdaps_choicearray_process_choice:nw { ##1 }
+    }
+    \newcommand \question [ 1 ] [] {
+      \_sdaps_choicearray_process_question:nw { ##1 }
+    }
+
+    \group_begin:
+
+      \tl_set:Nx \l_tmpb_tl {keepenv,layouter=\tl_use:N\l_sdaps_choicearray_layouter_tl,align=\l_sdaps_choicearray_align_tl\bool_if:NF\l_sdaps_choicearray_horizontal_bool{,flip},\l_sdaps_choicearray_extra_tl}
+      \expandafter\sdapsarray\expandafter[\l_tmpb_tl]
+}
+{
+      \endsdapsarray
+    \group_end:
+    % Process keys
+    \_sdaps_choicearray_postprocess:
+
+  \group_end:
+}
+
+\newenvironment { optionarray } [ 2 ] []
+{
+  \choicearray[singlechoice,#1] { #2 }
+}
+{
+  \endchoicearray
+}
+
+\newenvironment { rangearray } [ 2 ] []
+{
+  \group_begin:
+
+    \sdaps_context_get:nN { rangearray } \l_tmpa_tl
+    \tl_if_eq:NNT \l_tmpa_tl \q_no_value {
+      \tl_set:Nn \l_tmpa_tl {}
+    }
+
+    \tl_if_empty:nF { #1 } {
+      \tl_if_empty:NTF \l_tmpa_tl {
+        \tl_set:Nn \l_tmpa_tl { #1 }
+      } {
+        \tl_set:Nf \l_tmpa_tl { \l_tmpa_tl, #1 }
+      }
+    }
+
+    \_sdaps_rangearray_preprocess:Vn \l_tmpa_tl { #2 }
+
+    \newcommand \range [ 1 ] [] {
+      \_sdaps_rangearray_process_question:nw { ##1 }
+    }
+
+    \group_begin:
+
+      \tl_set:Nx \l_tmpb_tl {keepenv,align=\l_sdaps_rangearray_align_tl,\l_sdaps_rangearray_extra_tl}
+      \expandafter\sdapsarray\expandafter[\l_tmpb_tl]
+}
+{
+      \endsdapsarray
+    \group_end:
+    % Process keys
+    \_sdaps_rangearray_postprocess:
+
+  \group_end:
+}
+
+
+\ExplSyntaxOff
+
+%
+%    \end{macrocode}
+%
+% \iffalse
+% \PrintChanges
+% \PrintIndex
+% \fi
+%
+% \Finale
+\endinput


Property changes on: trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.ins	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapslayout.ins	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,55 @@
+%%
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%%
+%% 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 Benjamin Berg.
+%%
+
+\input l3docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/cskeleton}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+
+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.
+
+\endpreamble
+
+\generate{\file{sdapslayout.sty}{\from{sdapslayout.dtx}{package}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{*                                                           *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX:                    *}
+\Msg{*                                                           *}
+\Msg{*     sdapslayout.cls                                       *}
+\Msg{*                                                           *}
+\Msg{* To produce the documentation run the file sdapslayout.dtx *}
+\Msg{* through LaTeX.                                            *}
+\Msg{*                                                           *}
+\Msg{* Happy TeXing!                                             *}
+\Msg{*                                                           *}
+\Msg{*************************************************************}
+
+\endbatchfile

Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.dtx	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,166 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2014 by Henry Menke <henrimenke at gmail.com>
+% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%
+% 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 Benjamin Berg.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{sdapspdf.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<package>\ProvidesPackage{sdapspdf}
+%<*package>
+    [2015/04/10 v0.1 Initial version of SDAPS pdf package]
+%</package>
+%
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage{sdapspdf}[2015/04/10]
+%\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+  \DocInput{sdapspdf.dtx}
+\end{document}
+%</driver>
+% \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/01/14}{Initial version}
+%
+% \GetFileInfo{sdapspdf.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+% 
+%
+% \title{The \textsf{sdapspdf} package\thanks{This document
+%   corresponds to \textsf{sdapspdf}~\fileversion, dated \filedate.}}
+% \author{Benjamin Berg \\ \texttt{benjamin at sipsolutions.net}}
+%
+% \maketitle
+%
+% \section{Documentation}
+%
+% Please refer to \url{https://sdaps.org/class-doc} for documentation.
+%
+% \StopEventually{\PrintChanges\PrintIndex}
+%
+% \section{Implementation}
+%
+% This package uses the \LaTeX3 language internally, so we need to enable it.
+%    \begin{macrocode}
+% We need at least 2011-08-23 for \keys_set_known:nnN
+\RequirePackage{expl3}[2011/08/23]
+%\RequirePackage{xparse}
+\ExplSyntaxOn
+%    \end{macrocode}
+%
+% And we need a number of other packages.
+%    \begin{macrocode}
+\ExplSyntaxOff
+
+\RequirePackage{sdapsbase}
+\RequirePackage{hyperref}
+
+
+\ExplSyntaxOn
+
+%    \end{macrocode}
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% \subsection{PDF Support}
+%
+% This package adds basic overlays to SDAPS boxes so that they become editable.
+% It is also possible to pre-fill them using overrides or the normal mechanism
+% to set variables for box drawing.
+%
+%    \begin{macrocode}
+
+
+\bool_new:N \l__sdaps_pdf_form_bool
+\bool_new:N \l__sdaps_pdf_checkbox_default_bool
+\tl_new:N \l__sdaps_pdf_checkboxsymbol_tl
+
+\keys_define:nn { sdaps / checkbox / overlay / pdfform }
+{
+  pdf_form         .bool_set:N  = \l__sdaps_pdf_form_bool,
+  pdf_form         .initial:n   = false,
+
+  default          .bool_set:N  = \l__sdaps_pdf_checkbox_default_bool,
+  default          .initial:n   = false,
+
+  checkboxsymbol   .tl_set:N  = \l__sdaps_pdf_checkboxsymbol_tl,
+  checkboxsymbol   .initial:n   = 8,
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_pdf_overlay_checkbox_form:
+{
+  \keys_set_known:nV { sdaps / checkbox / overlay / pdfform } { \l_sdaps_parse_unknown_tl } %
+
+  \bool_if:NT \l__sdaps_pdf_form_bool {
+    \node[anchor=center,inner~sep=0pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \CheckBox[name=\l_sdaps_var_tl,bordersep=0,borderwidth=0pt,checkboxsymbol=\l__sdaps_pdf_checkboxsymbol_tl,bordercolor=,backgroundcolor=,checked=\bool_if:nTF\l__sdaps_pdf_checkbox_default_bool{true}{false},height=\l_sdaps_checkbox_height_dim,width=\l_sdaps_checkbox_width_dim]{\ignorespaces}
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \_sdaps_pdf_overlay_checkbox_form:
+
+
+\cs_new_protected_nopar:Nn \_sdaps_pdf_overlay_text_form:
+{
+  \keys_set_known:nV { sdaps / checkbox / overlay / pdfform } { \l_sdaps_parse_unknown_tl } %
+
+  \bool_if:NT \l__sdaps_pdf_form_bool {
+    \node[anchor=center,inner~sep=2pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \dim_set:Nn \l_sdaps_width_dim { \l_sdaps_width_dim - 4pt }
+      \dim_set:Nn \l_sdaps_height_dim { \l_sdaps_height_dim - 4pt }
+
+      \TextField[name=\l_sdaps_var_tl,bordersep=0pt,borderwidth=0pt,bordercolor=,backgroundcolor=,multiline=true,height=\l_sdaps_height_dim,width=\l_sdaps_width_dim]{\ignorespaces}
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_textbox_overlays_seq \_sdaps_pdf_overlay_text_form:
+
+
+
+\ExplSyntaxOff
+
+%
+%    \end{macrocode}
+%
+
+% \Finale
+\endinput


Property changes on: trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.ins	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/sdaps/sdapspdf.ins	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,55 @@
+%%
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%%
+%% 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 Benjamin Berg.
+%%
+
+\input l3docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/cskeleton}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+
+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.
+
+\endpreamble
+
+\generate{\file{sdapspdf.sty}{\from{sdapspdf.dtx}{package}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{*                                                           *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX:                    *}
+\Msg{*                                                           *}
+\Msg{*     sdapspdf.cls                                          *}
+\Msg{*                                                           *}
+\Msg{* To produce the documentation run the file sdapspdf.dtx    *}
+\Msg{* through LaTeX.                                            *}
+\Msg{*                                                           *}
+\Msg{* Happy TeXing!                                             *}
+\Msg{*                                                           *}
+\Msg{*************************************************************}
+
+\endbatchfile

Added: trunk/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,1015 @@
+%%
+%% This is file `sdapsarray.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% sdapsarray.dtx  (with options: `package')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%% 
+%% 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.
+%% 
+\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\ProvidesPackage{sdapsarray}
+    [2015/04/10 v0.1 Initial version of SDAPS array package]
+\RequirePackage{expl3}[2011/08/23]
+\ExplSyntaxOn
+\ExplSyntaxOff
+
+\RequirePackage{xparse}
+\RequirePackage{sdapsbase}
+
+\ExplSyntaxOn
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\cs_if_exist:NF \ior_str_get:NN { \cs_set_eq:Nc \ior_str_get:NN { ior_get_str:NN } }
+
+\bool_new:N \g__sdaps_array_info_open
+\bool_gset_false:N \g__sdaps_array_info_open
+\iow_new:N \g__sdaps_array_info_iow
+\ior_new:N \g__sdaps_array_info_ior
+
+\cs_generate_variant:Nn \int_set:Nn { Nf }
+\cs_generate_variant:Nn \coffin_join:NnnNnnnn { NnVNnVnn }
+
+\cs_new_protected_nopar:Nn \_sdaps_array_open_tmpfiles:
+{
+  % Guard against being executed multiple times
+  \bool_if:NF \g__sdaps_array_info_open {
+    \bool_gset_true:N \g__sdaps_array_info_open
+
+    % Also ensures toc file exists (i.e. is readable)
+    \iow_open:Nn \g__sdaps_array_info_iow { \c_sys_jobname_str .sdapsarraytoc }
+    \file_if_exist:nTF { \c_sys_jobname_str .sdapsarraytic } {
+      % Copy into toc file, then open that.
+      \ior_open:Nn \g__sdaps_array_info_ior { \c_sys_jobname_str .sdapsarraytic }
+
+      \ior_str_map_inline:Nn \g__sdaps_array_info_ior { \iow_now:Nn \g__sdaps_array_info_iow { ##1 } }
+      \ior_close:N \g__sdaps_array_info_ior
+
+      \ior_open:Nn \g__sdaps_array_info_ior { \c_sys_jobname_str .sdapsarraytoc }
+    } {
+    }
+    \iow_close:N \g__sdaps_array_info_iow
+
+    \iow_open:Nn \g__sdaps_array_info_iow { \c_sys_jobname_str .sdapsarraytic }
+  }
+}
+
+\tl_new:N \g__sdaps_array_last_row_tl
+\tl_gset_eq:NN \g__sdaps_array_last_row_tl \c_empty_tl
+
+\cs_new_protected_nopar:Nn \_sdaps_array_check_insert_header:N
+{
+  \bool_gset_eq:NN #1 \c_false_bool
+  \ior_if_eof:NF \g__sdaps_array_info_ior {
+    \ior_str_get:NN \g__sdaps_array_info_ior \l_tmpa_tl
+
+    \tl_if_eq:NNF \g__sdaps_array_last_row_tl \c_empty_tl {
+      \tl_if_eq:NNF \g__sdaps_array_last_row_tl \l_tmpa_tl {
+        \bool_gset_eq:NN #1 \c_true_bool
+      }
+    }
+    \tl_gset_eq:NN \g__sdaps_array_last_row_tl \l_tmpa_tl
+  }
+}
+
+
+\prop_new:N \g__sdaps_array_layouter_prop
+
+\int_new:N \g_sdaps_array_row_penalty_tl
+\int_gset:Nn \g_sdaps_array_row_penalty_tl { 10 }
+
+
+\tl_new:N \g_sdaps_array_shared_data_tl
+\tl_new:N \g_sdaps_array_stored_data_tl
+\tl_new:N \g_sdaps_array_local_data_tl
+\tl_new:N \g_sdaps_array_local_data_new_tl
+\prop_new:N \g__sdaps_array_stored_data_prop
+\prop_new:N \g__sdaps_array_shared_data_prop
+\prop_new:N \g__sdaps_array_local_data_prop
+
+\cs_generate_variant:Nn \prop_item:Nn { NV }
+
+\cs_new_protected_nopar:Nn \_sdaps_array_load_data:
+{
+  \tl_gset:Nx \g_sdaps_array_stored_data_tl { \prop_item:NV \g__sdaps_array_stored_data_prop \l__sdaps_array_global_name_tl }
+  \tl_gset:Nx \g_sdaps_array_shared_data_tl { \prop_item:NV \g__sdaps_array_shared_data_prop \l__sdaps_array_global_name_tl }
+  \tl_gset:Nx \g_sdaps_array_local_data_tl { \prop_item:NV \g__sdaps_array_local_data_prop \l__sdaps_array_local_name_tl }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_store_data:
+{
+  % Do not overwrite the "stored" data that we have right now.
+  \prop_gput:NVV \g__sdaps_array_shared_data_prop \l__sdaps_array_global_name_tl \g_sdaps_array_shared_data_tl
+
+  \immediate\write\@auxout{\exp_not:n{\sdapsarrayloadstoreddata}{\l__sdaps_array_global_name_tl}{\g_sdaps_array_shared_data_tl}}
+  \tl_if_empty:NF \g_sdaps_array_local_data_new_tl {
+    \immediate\write\@auxout{\exp_not:n{\sdapsarrayloadlocaldata}{\l__sdaps_array_local_name_tl}{\g_sdaps_array_local_data_new_tl}}
+  }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_array_load_stored_data:nn {
+  \prop_gput:Nnn \g__sdaps_array_stored_data_prop { #1 } { #2 }
+}
+\cs_new_eq:NN \sdapsarrayloadstoreddata \sdaps_array_load_stored_data:nn
+
+\cs_new_protected_nopar:Nn \sdaps_array_load_local_data:nn {
+  \prop_gput:Nnn \g__sdaps_array_local_data_prop { #1 } { #2 }
+}
+\cs_new_eq:NN \sdapsarrayloadlocaldata \sdaps_array_load_local_data:nn
+
+
+\int_new:N \g__sdaps_array_current_id_int
+\tl_new:N \l__sdaps_array_global_name_tl
+\tl_new:N \l__sdaps_array_local_name_tl
+
+\cs_generate_variant:Nn \keys_set:nn { nf }
+\cs_new_protected_nopar:Nn \sdaps_array_begin:nn
+{
+ \tl_set:Nx \l__sdaps_array_local_name_tl { sdapsarray \int_use:N\g__sdaps_array_current_id_int }
+  \int_gincr:N \g__sdaps_array_current_id_int
+  \tl_if_empty:nTF { #2 } {
+    \tl_set:NV \l__sdaps_array_global_name_tl \l__sdaps_array_local_name_tl
+  } {
+    \tl_set:Nx \l__sdaps_array_global_name_tl { #2 }
+  }
+
+  \_sdaps_array_load_data:
+
+  \keys_set:nf { sdaps / array } { \prop_item:Nn \g__sdaps_array_layouter_prop { #1 } }
+
+  \_sdaps_array_open_tmpfiles:
+
+  \tl_gset_eq:NN \g__sdaps_array_last_row_tl \c_empty_tl
+  \bool_gset_true:N \g_sdaps_array_first_row_bool
+
+  \l__sdaps_array_begin_tl
+}
+\cs_generate_variant:Nn \sdaps_array_begin:nn { Vn }
+\cs_generate_variant:Nn \sdaps_array_begin:nn { VV }
+\cs_generate_variant:Nn \sdaps_array_begin:nn { nV }
+
+\cs_new_protected_nopar:Nn \sdaps_array_begin:n
+{
+  \sdaps_array_begin:nn { #1 } { }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_array_row_start:
+{
+  \l__sdaps_array_row_start_tl
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_assign_use: {
+    \box_use:N \l_tmpa_box
+  \egroup
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_assign_colhead:Nw #1
+{
+  \l__sdaps_array_colhead_tl #1
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_colhead:w
+{
+  \bgroup
+    \l__sdaps_array_colhead_tl \l_tmpa_box \bgroup
+    \group_insert_after:N\_sdaps_array_assign_use:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+\cs_new_protected:Npn \sdaps_array_assign_rowhead:Nw #1
+{
+  \l__sdaps_array_rowhead_tl #1
+}
+
+\cs_new_protected:Npn \sdaps_array_rowhead:w
+{
+  \bgroup
+    \l__sdaps_array_rowhead_tl \l_tmpa_box \bgroup
+    \group_insert_after:N\_sdaps_array_assign_use:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_assign_cell:Nw #1
+{
+  \l__sdaps_array_cell_tl #1
+}
+
+\cs_new_protected_nopar:Npn \sdaps_array_cell:w
+{
+  \bgroup
+    \l__sdaps_array_cell_tl \l_tmpa_box \bgroup
+    \group_insert_after:N\_sdaps_array_assign_use:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+\box_new:N \g__sdaps_array_header_box
+\dim_new:N \g__sdaps_array_header_dim
+
+\cs_new_protected_nopar:Nn \_sdaps_array_calc_interlineskip:nnN
+{
+  \dim_compare:nNnTF { #1 } > { -1000pt } {
+    \skip_set:Nn #3 { \baselineskip - #1 - #2 }
+    \dim_compare:nNnF { #3 } > { \lineskiplimit } {
+      \skip_set:Nn #3 { \lineskip }
+    }
+  } {
+    \skip_set:Nn #3 { 0pt }
+  }
+  \skip_set:Nn #3 { #3 + \l_sdaps_sdapsarray_rowsep_dim }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_array_row:NN
+{
+  \if_mode_vertical:
+  \else:
+    \msg_error:nn { sdapsarray } { wrong_mode }
+  \fi
+
+  % Pagebreak detection
+  \_sdaps_array_check_insert_header:N \g_tmpa_bool
+  % XXX: \l_tmpa_dim is the height to the first baseline in the box. Note that
+  %      we use the real baseline in the case of the header row!
+  \l__sdaps_array_row_tl #1 #2 \l_tmpb_box \l_tmpa_dim
+
+  \bool_if:NT \g_sdaps_array_first_row_bool {
+    % Stow away the box for later use (before rewriting it to save the position)
+    \box_gset_eq:NN \g__sdaps_array_header_box \l_tmpb_box
+    \dim_gset:Nn \g__sdaps_array_header_dim { \box_ht:N \g__sdaps_array_header_box + \box_dp:N \g__sdaps_array_header_box }
+  }
+
+  \hbox_set:Nn \l_tmpb_box {
+    \pdfsavepos
+    \iow_shipout_x:Nn \g__sdaps_array_info_iow {
+      \thepage,
+      \the\pdflastxpos
+    }
+    \box_use:N \l_tmpb_box
+  }
+
+  \bool_if:NTF \g_sdaps_array_first_row_bool {
+    \bool_gset_false:N \g_sdaps_array_first_row_bool
+
+    \box_use:N \l_tmpb_box
+    % Do not ever allow a break after the header line.
+    \nobreak
+  } {
+    % The idea is simple. Before every line the header is re-inserted (either
+    % the real one or an empty box with the same dimensions). In the case that
+    % there is *no* page break we insert a corresponding negative skip so that
+    % the resulting skip (including interline skip) is exactly the normal
+    % interline skip between the rows.
+    % In the case that the skip *is* discarded we end up with the header box
+    % and the normal interline skip between the new row and the header. i.e.:
+    %   skip = 1 * ( interlineskip_for_row - interlineskip_for_header - header_height - header_depth ) + header_height + header_depth + interlineksip_for_header
+    % or
+    %   skip = 0 * ( interlineskip_for_row - interlineskip_for_header - header_height - header_depth ) + header_height + header_depth + interlineksip_for_header
+
+    % Calculate interlineksip_for_row and interlineskip_for_header
+    \_sdaps_array_calc_interlineskip:nnN { \prevdepth } { \l_tmpa_dim } \l_tmpa_skip
+    \_sdaps_array_calc_interlineskip:nnN { \box_dp:N \g__sdaps_array_header_box } { \l_tmpa_dim } \l_tmpb_skip
+    \nointerlineskip
+    \skip_vertical:n { \l_tmpa_skip - \l_tmpb_skip }
+    \kern -\g__sdaps_array_header_dim
+
+    % Inser the real or fake box
+    \bool_if:NTF \g_tmpa_bool {
+      \box_use:N \g__sdaps_array_header_box
+    } {
+     \hrule height \box_ht:N \g__sdaps_array_header_box depth \box_dp:N \g__sdaps_array_header_box width 0pt
+    }
+    \nobreak
+    % Insert the calculated interline skip (in the same way the TeX would do it.
+    \nointerlineskip
+    \skip_vertical:N \l_tmpb_skip
+
+    \box_use:N \l_tmpb_box
+
+    % And insert the sdapsarray sepecific inter row penalty.
+    \penalty\int_use:N\g_sdaps_array_row_penalty_tl
+  }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_array_end:
+{
+  \l__sdaps_array_end_tl
+  \box_gclear:N \g__sdaps_array_header_box
+}
+
+
+\tl_new:N \l__sdaps_array_begin_tl
+\tl_new:N \l__sdaps_array_row_start_tl
+\tl_new:N \l__sdaps_array_colhead_tl
+\tl_new:N \l__sdaps_array_rowhead_tl
+\tl_new:N \l__sdaps_array_cell_tl
+\tl_new:N \l__sdaps_array_row_tl
+\tl_new:N \l__sdaps_array_end_tl
+
+\keys_define:nn { sdaps / array }
+{
+  begin        .tl_set:N   = \l__sdaps_array_begin_tl,
+  row_start    .tl_set:N   = \l__sdaps_array_row_start_tl,
+  colhead      .tl_set:N   = \l__sdaps_array_colhead_tl,
+  rowhead      .tl_set:N   = \l__sdaps_array_rowhead_tl,
+  cell         .tl_set:N   = \l__sdaps_array_cell_tl,
+  row          .tl_set:N   = \l__sdaps_array_row_tl,
+  end          .tl_set:N   = \l__sdaps_array_end_tl,
+}
+
+\seq_new:N \g_sdaps_array_overhangs_left_seq
+\seq_new:N \g_sdaps_array_overhangs_right_seq
+\seq_new:N \g_sdaps_array_shared_colwidths_seq
+\seq_new:N \g_sdaps_array_stored_colwidths_seq
+
+\cs_new_protected_nopar:Npn \_sdaps_array_rowhead_default:Nw #1
+{
+  \tl_if_empty:NTF \g_sdaps_array_local_data_tl {
+      \tl_if_empty:NTF \g_sdaps_array_local_data_new_tl {
+        \dim_set:Nn \l_tmpa_dim { 0.5 \hsize }
+      } {
+        \dim_set:Nn \l_tmpa_dim { \g_sdaps_array_local_data_new_tl }
+      }
+  } {
+    \dim_set:Nn \l_tmpa_dim { \g_sdaps_array_local_data_tl }
+  }
+  % \vbox_set_top:Nw is still missing as of 2017-08-11
+  \tex_setbox:D #1 \tex_vtop:D \bgroup
+    \sdaps_if_rtl:TF {
+      \raggedright
+    } {
+      \raggedleft
+    }
+    \hsize=\dim_use:N\l_tmpa_dim
+    \group_begin:\bgroup
+    \group_insert_after:N \group_end:
+    \group_insert_after:N \egroup
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_array_cell_default:Nw #1
+{
+  \hbox_set:Nw #1 \bgroup
+    % swallow group opening token
+    \group_insert_after:N \hbox_set_end:
+    \tex_let:D\next=
+}
+
+\cs_new:Nn \_sdaps_array_cell_rotated_end: {
+    \hbox_set_end:
+    \dim_set:Nn \l_tmpa_dim { \box_ht:N \l_tmpa_box }
+    \dim_set:Nn \l_tmpb_dim { \box_dp:N \l_tmpa_box }
+
+    \dim_set:Nn \l_tmpa_dim { \l_sdaps_sdapsarray_angle_sine_tl \l_tmpa_dim }
+    \dim_set:Nn \l_tmpb_dim { \l_sdaps_sdapsarray_angle_sine_tl \l_tmpb_dim }
+
+    \box_rotate:Nn \l_tmpa_box { \l_sdaps_sdapsarray_angle_int }
+
+    % We want the baseline of the box to be centered, that only works if we
+    % leave the same space both ways.
+    % That is not ideal, but we cannot move the cell content accordingly.
+    \dim_set:Nn \l_tmpb_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+    \skip_horizontal:n { \l_tmpb_dim }
+    \rlap{
+      \skip_horizontal:n { -\l_tmpa_dim }
+      \box_use:N \l_tmpa_box
+    }
+    \skip_horizontal:n { \l_tmpb_dim }
+    % dummy skip that will be removed again by other code
+    \skip_horizontal:n { 0pt }
+
+    \dim_set:Nn \l_tmpa_dim { \l_tmpa_dim + \l_tmpb_dim }
+
+    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
+    \dim_set:Nn \l_tmpa_dim { \dim_max:nn { 0pt } { \l_tmpb_dim - \l_tmpa_dim } }
+
+    \seq_gpush:Nn \g_sdaps_array_overhangs_left_seq { 0pt }
+    \seq_gpush:Nx \g_sdaps_array_overhangs_right_seq { \dim_use:N \l_tmpa_dim }
+  \egroup
+  \hbox_set_end:
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_array_cell_rotated:Nw #1
+{
+  \hbox_set:Nw #1 \bgroup
+    \hbox_set:Nw \l_tmpa_box
+    \bgroup
+    \group_insert_after:N \_sdaps_array_cell_rotated_end:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+
+
+\cs_new_protected_nopar:Nn \_sdaps_array_begin_default:
+{
+  \tl_if_empty:NTF \g_sdaps_array_shared_data_tl {
+    \seq_clear:N \g_sdaps_array_shared_colwidths_seq
+  } {
+    \seq_gset_split:NnV \g_sdaps_array_shared_colwidths_seq { ~ } \g_sdaps_array_shared_data_tl
+  }
+  \tl_if_empty:NTF \g_sdaps_array_stored_data_tl {
+    \seq_clear:N \g_sdaps_array_stored_colwidths_seq
+  } {
+    \seq_gset_split:NnV \g_sdaps_array_stored_colwidths_seq { ~ } \g_sdaps_array_stored_data_tl
+  }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_end_default:
+{
+  \tl_gset:Nx \g_sdaps_array_shared_data_tl { \seq_use:Nn \g_sdaps_array_shared_colwidths_seq { ~ } }
+  \tl_gset:Nx \g_sdaps_array_stored_data_tl { \seq_use:Nn \g_sdaps_array_stored_colwidths_seq { ~ } }
+
+  % Clear the global sequences, to save memory
+  \seq_gclear:N \g_sdaps_array_overhangs_left_seq
+  \seq_gclear:N \g_sdaps_array_overhangs_right_seq
+  \seq_gclear:N \g_sdaps_array_shared_colwidths_seq
+  \seq_gclear:N \g_sdaps_array_stored_colwidths_seq
+
+  \_sdaps_array_store_data:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_row_start_default:
+{
+  \seq_gclear:N \g_sdaps_array_overhangs_left_seq
+  \seq_gclear:N \g_sdaps_array_overhangs_right_seq
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_array_row:NNNN
+{
+  % #1: A vbox with baseline on the *first* item containing the row header
+  %     (\vtop in plain TeX).
+  % #2: Data cells packed into an hbox. Each of these needs to be set to the
+  %     correct width and inserted.
+  % #3: The box register to store the resulting hbox in. The depth of this box
+  %     needs to be correct to calculate the interline glue to the following
+  %     row.
+  % #4: A dim register to store the height of the box in for the purpose of
+  %     calculating the interline glue in front of the produced row.
+  %
+  % { \dim_use:N\@totalleftmargin } { \dim_use:N\linewidth }
+  % The macro should create an hbox which is exactly \linewidth wide and also
+  % contains internal indentation by \@totalleftmargin into the box register #3.
+  % The box will be used while in vertical mode and may% be inserted multiple
+  % times in the case of the header row.
+  % To simplify the iteration it is guaranteed that the data cell boxes are not
+  % completely empty. This means the code can simply unbox until it sees a box
+  % that is void.
+
+  \seq_gclear:N \g_tmpa_seq
+
+  % Insert the boxes into a local hbox to work with them
+  \hbox_set:Nn #2 {
+    \hbox_unpack:N #2
+
+    % Handle the overhang, note that we modify the \g_sdaps_array_overhangs_right_seq locally only!
+    \seq_pop:NNTF \g_sdaps_array_overhangs_right_seq \l_tmpa_tl {
+      \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl }
+    } {
+      \dim_set:Nn \l_tmpb_dim { 0pt }
+    }
+    % Implicit "last" column that contains the overhang
+    \seq_gpop:NNTF \g_sdaps_array_shared_colwidths_seq \l_tmpa_tl {
+      \dim_set:Nn \l_tmpa_dim { \l_tmpa_tl }
+    } {
+      \dim_set:Nn \l_tmpa_dim { 0pt }
+    }
+    \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+
+    % MAX with stored values (NOTE: sequence only modified in local scope)
+    \seq_pop:NNTF \g_sdaps_array_stored_colwidths_seq \l_tmpa_tl {
+      \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl }
+    } {
+      \dim_set:Nn \l_tmpb_dim { 0pt }
+    }
+    % Store value from this run, and then calculate max with previous run
+    \seq_gput_right:Nx \g_tmpa_seq { \dim_use:N \l_tmpa_dim }
+    \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+
+    % Insert the overhang space
+    \hbox_set:Nn \l_tmpa_box { \skip_horizontal:n { \l_tmpa_dim } }
+
+    % Now grab the first of the cells, and then loop over the rest
+    \box_set_to_last:N #2
+    \bool_do_while:nn { ! \box_if_empty_p:N #2 } {
+      % Strip any trailing glue (i.e. space) coming from the user (for the
+      % leading side we ensure that \tex_ignorespaces:D is called).
+      % Note that this may remove e.g. a trailing \hfill from the user, the
+      % user needs to work around that (in the same way as is required in e.g.
+      % tabular).
+      \hbox_set:Nn #2 { \hbox_unpack:N #2 \unskip }
+
+      % Pop the target width for the current box (i.e. we don't globally
+      % modify the clist here).
+      \seq_gpop:NNTF \g_sdaps_array_shared_colwidths_seq \l_tmpa_tl {
+        \dim_set:Nn \l_tmpa_dim { \l_tmpa_tl }
+      } {
+        \dim_set:Nn \l_tmpa_dim { 0pt }
+      }
+      % Calculate the maximum width of current and previous items
+      \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \box_wd:N #2 } { \l_tmpa_dim } }
+
+      % MAX with stored values (NOTE: sequence only modified in local scope)
+      \seq_pop:NNTF \g_sdaps_array_stored_colwidths_seq \l_tmpa_tl {
+        \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl }
+      } {
+        \dim_set:Nn \l_tmpb_dim { 0pt }
+      }
+      % Store value from this run, and then calculate max with previous run
+      \seq_gput_right:Nx \g_tmpa_seq { \dim_use:N \l_tmpa_dim }
+      \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } }
+
+      % Set the box into a new box with the correct width which contains fil
+      % to center it.
+      \hbox_set_to_wd:Nnn \l_tmpb_box \l_tmpa_dim { \hfil \hbox_unpack:N #2 \hfil }
+
+      % This loops works backward, so attach the cell on the right side.
+      % We used to make sure that it is layed out in order, but that is now
+      % obsolete and doing it out of order is simpler in RTL mode.
+      \sdaps_if_rtl:TF {
+        % The boxes are shown on the page from LTR
+        \hbox_set:Nn \l_tmpa_box { \box_use:N \l_tmpa_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpb_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } }
+      } {
+        \hbox_set:Nn \l_tmpa_box { \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpb_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpa_box }
+      }
+
+      % Grab next cell
+      \box_set_to_last:N #2
+    }
+
+    % Get the coffin out of the nested scope by placing it into the box and
+    % placing that into it again ...
+    \box_use:N \l_tmpa_box
+  }
+  \hcoffin_set:Nn \l_tmpa_coffin { \box_use_drop:N #2 }
+
+  \seq_gconcat:NNN \g_sdaps_array_shared_colwidths_seq \g_tmpa_seq \g_sdaps_array_shared_colwidths_seq
+  \seq_gclear:N \g_tmpa_seq
+
+  % Calculate the space that is left for the header column
+  \dim_set:Nn \l_tmpa_dim { \linewidth - \coffin_wd:N \l_tmpa_coffin - 2\l_sdaps_sdapsarray_colsep_dim }
+  \tl_gset:Nx \g_sdaps_array_local_data_new_tl { \dim_use:N \l_tmpa_dim }
+
+  % TODO: The \hfil here is a hack to prevent a warning if the vbox is empty.
+  %       Unfortunately checking for an empty box does not work for some reason.
+  \dim_set:Nn \l_tmpb_dim { \box_ht:N #1 }
+  \sdaps_if_rtl:TF {
+    \hcoffin_set:Nn \l_tmpb_coffin { \hbox_to_wd:nn \l_tmpa_dim { \hfil \vbox:n { \vbox_unpack_clear:N #1 } } \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } }
+    \tl_set:Nn \l_tmpa_tl { l }
+    \tl_set:Nn \l_tmpb_tl { r }
+  } {
+    \hcoffin_set:Nn \l_tmpb_coffin { \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \hbox_to_wd:nn \l_tmpa_dim { \hfil \vbox:n { \vbox_unpack_clear:N #1 } } }
+    \tl_set:Nn \l_tmpa_tl { r }
+    \tl_set:Nn \l_tmpb_tl { l }
+  }
+  \dim_set:Nn \l_tmpa_dim { \coffin_ht:N \l_tmpb_coffin }
+
+  % If the first/last baseline differ then center the vbox, otherwise align the
+  % baseline with the cells
+  \dim_compare:nNnTF { \l_tmpa_dim } = { \l_tmpb_dim } {
+    \coffin_join:NnVNnVnn \l_tmpb_coffin { H } \l_tmpa_tl \l_tmpa_coffin { H } \l_tmpb_tl { \l_sdaps_sdapsarray_colsep_dim } { 0pt }
+    \dim_set:Nn #4 { \coffin_ht:N \l_tmpb_coffin }
+  } {
+    \coffin_join:NnVNnVnn \l_tmpb_coffin { vc } \l_tmpa_tl \l_tmpa_coffin { vc } \l_tmpb_tl { \l_sdaps_sdapsarray_colsep_dim } { 0pt }
+    % XXX: Assume that the header is higher than the content cells
+    \dim_set:Nn #4 { \l_tmpb_dim }
+  }
+
+  \hbox_set:Nn #3 { \skip_horizontal:N \@totalleftmargin \coffin_typeset:Nnnnn \l_tmpb_coffin { H } { l } { 0pt } { 0pt } }
+}
+
+\prop_gput:Nnn \g__sdaps_array_layouter_prop { default } {
+  begin = { \_sdaps_array_begin_default: },
+  row_start = { \_sdaps_array_row_start_default: },
+  rowhead = { \_sdaps_array_rowhead_default:Nw },
+  colhead = { \_sdaps_array_cell_default:Nw },
+  cell = { \_sdaps_array_cell_default:Nw },
+  row = { \_sdaps_array_row:NNNN },
+  end = { \_sdaps_array_end_default: },
+}
+
+\prop_gput:Nnn \g__sdaps_array_layouter_prop { rotated } {
+  begin = { \_sdaps_array_begin_default: },
+  row_start = { \_sdaps_array_row_start_default: },
+  rowhead = { \_sdaps_array_rowhead_default:Nw },
+  colhead = { \_sdaps_array_cell_rotated:Nw },
+  cell = { \_sdaps_array_cell_default:Nw },
+  row = { \_sdaps_array_row:NNNN },
+  end = { \_sdaps_array_end_default: },
+}
+
+
+\bool_new:N \g_sdaps_array_first_row_bool
+\bool_new:N \l__sdaps_sdapsarray_in_top_group_bool
+\bool_new:N \l__sdaps_sdapsarray_have_content_bool
+\bool_new:N \l_sdaps_sdapsarray_flip_bool
+\tl_new:N \l_sdaps_sdapsarray_layouter_tl
+\tl_new:N \l_sdaps_sdapsarray_align_tl
+\bool_new:N \l_sdaps_sdapsarray_keepenv_bool
+\int_new:N \l_sdaps_sdapsarray_angle_int
+\tl_new:N \l_sdaps_sdapsarray_angle_sine_tl
+\dim_new:N \l_sdaps_sdapsarray_colsep_dim
+\dim_new:N \l_sdaps_sdapsarray_rowsep_dim
+
+\keys_define:nn { sdaps / sdapsarray }
+{
+  flip       .bool_set:N   = \l_sdaps_sdapsarray_flip_bool,
+  flip       .initial:n  = false,
+  flip       .default:n  = true,
+  layouter   .tl_set:N   = \l_sdaps_sdapsarray_layouter_tl,
+  layouter   .initial:n  = default,
+  align      .tl_set:N   = \l_sdaps_sdapsarray_align_tl,
+  align      .initial:n  = { },
+  keepenv    .bool_set:N   = \l_sdaps_sdapsarray_keepenv_bool,
+  keepenv    .initial:n  = false,
+  keepenv    .default:n  = true,
+
+  angle          .code:n     = {
+    \int_set:Nn \l_sdaps_sdapsarray_angle_int {#1}
+    \tl_set:Nx \l_sdaps_sdapsarray_angle_sine_tl { \fp_to_decimal:n {sind(#1)}}
+  },
+  angle          .initial:n  = 70,
+  colsep         .dim_set:N  = \l_sdaps_sdapsarray_colsep_dim,
+  colsep         .initial:n  = 6pt,
+  rowsep         .dim_set:N  = \l_sdaps_sdapsarray_rowsep_dim,
+  rowsep         .initial:n  = 0pt,
+}
+
+
+\cs_new_nopar:Nn \l_sdaps_sdapsarray_alignment_set_have_content:
+{
+  \bool_set_true:N\l__sdaps_sdapsarray_have_content_bool
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_alignment: {
+    % End the last group, which will be the group that was begun earlier.
+    % If the earlier cell was the first one, then this egroup also starts the
+    % hbox to temporarily store the cells.
+  \egroup
+  \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+    \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { an~alignment~tab }
+  }
+
+  % We need to notify the outside scope that there are items, will be inserted
+  % multiple times, but that does not matter.
+  \group_insert_after:N\l_sdaps_sdapsarray_alignment_set_have_content:
+
+  % Just in case someone leaked a change into our scope
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \cs_set_eq:NN \cr \_sdaps_sdapsarray_newline:
+    \cs_set_eq:NN \\ \cr
+  }
+
+  % Define a cell now, we can just put everything into a new cell, and that
+  % should work fine.
+  % Note that cells are not safe for fragile commands at the moment.
+  \_sdaps_sdapsarray_cell:w \bgroup
+    \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+    \cs_set_eq:NN \\ \cr
+    \tex_ignorespaces:D
+}
+
+\msg_new:nnn { sdapsarray } { unmatched_grouping_level } { The~grouping~level~of~a~cell~was~not~even.~Please~ensure~all~braces~are~balanced~out!~This~error~occured~at~#1. }
+\msg_new:nnn { sdapsarray } { unequal_cols } { The~number~of~columns~is~not~equal~for~all~rows. }
+\msg_new:nnn { sdapsarray } { no_new_line_at_end } { You~have~terminated~the~last~line~with~\textbackslash\textbackslash~or~similar.~This~can~have~side~effects,~please~remove~it. }
+\msg_new:nnn { sdapsarray } { wrong_mode } { The~sdapsarray~environment~can~only~function~in~vertical~mode~(both~inner~and~outer). }
+
+\cs_new:Nn \_sdaps_sdapsarray_start_cells: {
+    \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+      \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~a~row~header }
+    }
+  \egroup
+
+  % We are in the environment scope again here
+
+  % Notify code that we are going to generate cells for a new row.
+  \sdaps_array_row_start:
+
+  % Start an hbox to stow away the cells.
+  % The rest of the setup happens in the alignment handler
+  \hbox_set:Nw \l_tmpb_box \bgroup
+    \group_insert_after:N \hbox_set_end:
+    \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_linestart: {
+  \sdaps_array_assign_rowhead:Nw \l_tmpa_box
+    \bgroup
+      \cs_set_eq:NN \\ \cr
+
+      \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool
+      \bgroup
+        \group_insert_after:N \_sdaps_sdapsarray_start_cells:
+        \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+        % Ignore following spaces by the user
+        \tex_ignorespaces:D
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_newline: {
+    \egroup
+    \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+      \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~a~row }
+    }
+  \egroup
+
+  % We are in the environment scope again here
+  % Output the last line if the cells were non-empty.
+  \bool_if:NT \l__sdaps_sdapsarray_have_content_bool {
+    \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box
+  }
+  \bool_set_false:N \l__sdaps_sdapsarray_have_content_bool
+
+  \cs_set_eq:NN \_sdaps_sdapsarray_cell:w \sdaps_array_cell:w
+  \_sdaps_sdapsarray_linestart:
+}
+
+\cs_new:Npn\sdaps_array_nested_alignenv: {
+  \char_set_catcode_alignment:N &
+  \cs_set_eq:NN \cr \sdaps_orig_cr
+  \cs_set_eq:NN \\ \sdaps_orig_backslash
+}
+
+\cs_new:Npn\sdaps_array_nested_alignenv:w {
+  \bgroup
+    \sdaps_array_nested_alignenv:
+    % swallow group opening token
+    \tex_let:D\next=
+}
+\cs_new_eq:NN \sdapsnested \sdaps_array_nested_alignenv:w
+
+\group_begin:
+\char_set_catcode_active:N &
+\cs_new:Nn \_sdaps_sdapsarray_defines: {
+  \cs_set_eq:NN \sdaps_orig_cr \cr
+  \cs_set_eq:NN \sdaps_orig_backslash \\
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \char_set_catcode_active:N &
+    \cs_set_eq:NN \cr \sdaps_array_newline:
+    \cs_set_eq:NN \\ \cr
+    \cs_set_eq:NN & \sdaps_array_alignment:
+  }
+}
+\group_end:
+
+%%%%%%
+%%%%%%
+
+
+\box_new:N \l_sdaps_sdapsarray_headers_box
+\box_new:N \l_sdaps_sdapsarray_boxlist_head_box
+\box_new:N \l_sdaps_sdapsarray_boxlist_tail_box
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_prepend_box:NN {
+  \hbox_set:Nn #2 {
+    \box_use:N #1
+    \hbox_unpack:N #2
+  }
+  \box_clear:N #1
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_append_box:NN {
+  \hbox_set:Nn #2 {
+    \hbox_unpack:N #2
+    \box_use:N #1
+  }
+  \box_clear:N #1
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_pop_last_box:NN {
+  \hbox_set:Nn #2 {
+    \hbox_unpack:N #2
+    \box_gset_to_last:N \g_tmpa_box
+  }
+  \box_set_eq:NN #1 \g_tmpa_box
+  \box_gclear:N \g_tmpa_box
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_pop_last_hbox_unpack:NN {
+  \_sdaps_sdapsarray_pop_last_box:NN #1 #2
+  \hbox_set:Nn #1 {
+    \hbox_unpack:N #1
+    \box_gset_to_last:N \g_tmpa_box
+  }
+  \box_set_eq:NN #1 \g_tmpa_box
+  \box_gclear:N \g_tmpa_box
+}
+
+\cs_new_protected:Nn \_sdaps_sdapsarray_boxlist_void_if_empty:N {
+  \hbox_set:Nn #1 {
+    \hbox_unpack:N #1
+    \box_set_to_last:N #1
+    \box_if_empty:NTF #1 {
+      \bool_gset_true:N \g_tmpa_bool
+    } {
+      \box_use:N #1
+      \bool_gset_false:N \g_tmpa_bool
+    }
+  }
+  \bool_if:NT \g_tmpa_bool {
+    \box_clear:N #1
+  }
+}
+
+
+\cs_new_protected_nopar:Nn \_sdaps_sdapsarray_alignment_flip:
+{
+  \_sdaps_sdapsarray_end_cell_flip:
+
+  % Just in case someone leaked a change into our scope
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \cs_set_eq:NN \cr \_sdaps_sdapsarray_newline_flip:
+    \cs_set_eq:NN \\ \cr
+  }
+
+  % Next up is either a cell or a row header. We can figure that out by checking
+  % that the row headings box is void
+  \box_if_empty:NTF \l_sdaps_sdapsarray_headers_box {
+    \sdaps_array_assign_rowhead:Nw \l_tmpa_box \bgroup
+      \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+      \cs_set_eq:NN \\ \cr
+      % Ignore following spaces by the user
+      \tex_ignorespaces:D
+  } {
+    \sdaps_array_assign_cell:Nw \l_tmpa_box \bgroup
+      \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+      \cs_set_eq:NN \\ \cr
+      \tex_ignorespaces:D
+  }
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_end_cell_flip: {
+  \egroup % Finish of the cell
+  \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+    \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { end~of~cell~or~row }
+  }
+
+  % Get last box from head
+  \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box
+  % Append the new box to the list of boxes for this row
+  \_sdaps_sdapsarray_append_box:NN \l_tmpa_box \l_tmpb_box
+  % Prepend the new box to the tail
+  \_sdaps_sdapsarray_prepend_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_tail_box
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_end_line_flip: {
+  % At the end of the line, move tail into head.
+  % First check that head is empty.
+  \_sdaps_sdapsarray_boxlist_void_if_empty:N \l_sdaps_sdapsarray_boxlist_head_box
+  \box_if_empty:NF \l_sdaps_sdapsarray_boxlist_head_box {
+    \msg_error:nn { sdapsarray } { unequal_cols }
+  }
+  \box_set_eq:NN \l_sdaps_sdapsarray_boxlist_head_box \l_sdaps_sdapsarray_boxlist_tail_box
+  \box_clear:N \l_sdaps_sdapsarray_boxlist_tail_box
+
+  % If this was the first row, store it away, these are the headings.
+  \box_if_empty:NT \l_sdaps_sdapsarray_headers_box {
+    \box_set_eq:NN \l_sdaps_sdapsarray_headers_box \l_sdaps_sdapsarray_boxlist_head_box
+    \box_clear:N \l_sdaps_sdapsarray_boxlist_head_box
+  }
+}
+
+\cs_new_nopar:Nn \_sdaps_sdapsarray_newline_flip: {
+  \_sdaps_sdapsarray_end_cell_flip:
+  \_sdaps_sdapsarray_end_line_flip:
+
+  % Create next box to store away, this has to be a colhead at this point
+  \sdaps_array_assign_colhead:Nw \l_tmpa_box \bgroup
+    \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+    \cs_set_eq:NN \\ \cr
+    \tex_ignorespaces:D
+}
+
+\NewDocumentEnvironment { sdapsarray } { o }
+{
+  \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+  \group_begin:
+
+    \tl_set:Nn \l_tmpa_tl { }
+    \IfNoValueF { #1 } {
+      \tl_set:Nn \l_tmpa_tl { #1 }
+    }
+
+    \box_clear:N \l_tmpa_box
+    \box_clear:N \l_tmpb_box
+
+    % Ensure vertical mode.
+    \tex_par:D
+    \if_mode_vertical:
+    \else:
+      \msg_error:nn { sdapsarray } { wrong_mode }
+    \fi
+
+    % This needs to be initialized here as otherwise the values would be
+    % expanded at import time.
+    \keys_set:nV { sdaps / sdapsarray } \l_tmpa_tl
+
+    \sdaps_array_begin:VV \l_sdaps_sdapsarray_layouter_tl \l_sdaps_sdapsarray_align_tl
+
+    % Note, this environment is fragile; we redefine & to be active.
+    % One can go back into normal mode by using \sdapsnested{} though.
+
+    \bool_if:NTF \l_sdaps_sdapsarray_flip_bool {
+      \cs_set_eq:NN \sdaps_array_newline: \_sdaps_sdapsarray_newline_flip:
+      \cs_set_eq:NN \sdaps_array_alignment: \_sdaps_sdapsarray_alignment_flip:
+
+      \_sdaps_sdapsarray_defines:
+
+      % Two hboxes to hold the content, note that
+      % a: row headers, b: cells/col headers
+      \box_clear:N \l_sdaps_sdapsarray_headers_box
+      \box_clear:N \l_sdaps_sdapsarray_boxlist_head_box
+      \box_clear:N \l_sdaps_sdapsarray_boxlist_tail_box
+
+      % not sure why we need this group, but nothing works without it
+      \bgroup
+        % This is a bit creative to say the least
+        \sdaps_array_row_start:
+
+        \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool
+        \sdaps_array_assign_rowhead:Nw \l_tmpa_box \bgroup
+          \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool
+          \cs_set_eq:NN \\ \cr
+          % Ignore following spaces by the user
+          \tex_ignorespaces:D
+    } {
+      \cs_set_eq:NN \sdaps_array_newline: \_sdaps_sdapsarray_newline:
+      \cs_set_eq:NN \sdaps_array_alignment: \_sdaps_sdapsarray_alignment:
+      \_sdaps_sdapsarray_defines:
+
+      \cs_set_eq:NN \_sdaps_sdapsarray_cell:w \sdaps_array_colhead:w
+      \_sdaps_sdapsarray_linestart:
+    }
+
+  % If we redefine &, then the next character might have the wrong catcode
+  % (i.e. it could still be an alignment character). Execute the alignment
+  % code directly if the next character is &.
+  \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool {
+    \peek_charcode_remove:NT & { \sdaps_array_alignment: }
+  }
+}
+{
+    \bool_if:NTF \l_sdaps_sdapsarray_flip_bool {
+        \_sdaps_sdapsarray_end_cell_flip:
+
+        % At this point we should have swallowed all items from the head list.
+        % If not, then someone likely add a stray \\ command or similar
+        \_sdaps_sdapsarray_boxlist_void_if_empty:N \l_sdaps_sdapsarray_boxlist_head_box
+        \box_if_empty:NTF \l_sdaps_sdapsarray_boxlist_head_box {
+          \_sdaps_sdapsarray_end_line_flip:
+        } {
+          \msg_error:nn { sdapsarray } { no_new_line_at_end }
+        }
+
+        % Now we can have fun!
+        % Pop cells and heading, until we cannot find any new ones.
+        \_sdaps_sdapsarray_pop_last_hbox_unpack:NN \l_tmpa_box \l_sdaps_sdapsarray_headers_box
+        \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box
+        \bool_do_while:nn { ! \box_if_empty_p:N \l_tmpa_box || ! \box_if_empty_p:N \l_tmpb_box } {
+          \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box
+
+          \sdaps_array_row_start:
+
+          \_sdaps_sdapsarray_pop_last_hbox_unpack:NN \l_tmpa_box \l_sdaps_sdapsarray_headers_box
+          \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box
+        }
+
+      \egroup
+    } {
+        \egroup
+
+        \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool {
+          \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~the~environment }
+        }
+      \egroup
+
+      % We are in the environment scope again here
+      % Output the last line if the cells were non-empty.
+      \bool_if:NT \l__sdaps_sdapsarray_have_content_bool {
+        \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box
+      }
+    }
+
+    \sdaps_array_end:
+
+  \group_end:
+}
+
+\ExplSyntaxOff
+
+
+\endinput
+%%
+%% End of file `sdapsarray.sty'.


Property changes on: trunk/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/latex/sdaps/sdapsbase.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/sdapsbase.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/sdapsbase.sty	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,1535 @@
+%%
+%% This is file `sdapsbase.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% sdapsbase.dtx  (with options: `package')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%% 
+%% 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.
+%% 
+\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\ProvidesPackage{sdapsbase}
+    [2015/01/14 v0.1 Initial version of SDAPS base package]
+\RequirePackage{expl3}[2011/08/23]
+\ExplSyntaxOn
+\ExplSyntaxOff
+
+\input{sdapscode128}
+
+\RequirePackage{qrcode}
+
+\RequirePackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{positioning}
+\usetikzlibrary{decorations.pathmorphing}
+\ExplSyntaxOn
+
+\cs_new_eq:NN \code_render:n \code
+\cs_generate_variant:Nn \code_render:n { x, V }
+
+\cs_new_protected_nopar:Nn \qrcode_render:nn {
+  \qrcode[#1] {#2}
+}
+\cs_generate_variant:Nn \qrcode_render:nn { nx, nV }
+
+\prg_new_conditional:Npnn \sdaps_if_rtl: { p, T, F, TF }
+{
+  \cs_if_exist:cTF { if at RTL } {
+    \tl_use:c { if at RTL }
+      \prg_return_true:
+    \else
+      \prg_return_false:
+    \fi
+  } {
+    \prg_return_false:
+  }
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\cs_generate_variant:Nn \tl_if_eq:nnTF { Vn }
+\cs_generate_variant:Nn \tl_if_eq:nnT { Vn }
+\cs_generate_variant:Nn \tl_if_eq:nnF { Vn }
+\cs_generate_variant:Nn \int_if_odd:nTF { V }
+\cs_generate_variant:Nn \int_if_odd:nF { V }
+\cs_generate_variant:Nn \int_if_odd:nT { V }
+\cs_generate_variant:Nn \tl_set:Nn { Nv }
+\cs_generate_variant:Nn \msg_error:nnn { nnV }
+\cs_generate_variant:Nn \exp_not:n { f }
+
+\tl_new:N \l__sdaps_tmpa_tl
+\tl_new:N \l__sdaps_tmpb_tl
+
+\dim_new:N \l__sdaps_tmpa_dim %
+\dim_new:N \l__sdaps_tmpb_dim %
+
+\prop_new:N \g__sdaps_current_context_prop
+\tl_new:N \g__sdaps_current_context_id_tl
+
+\tl_new:N \g__sdaps_current_context_tl
+
+\seq_new:N \g__sdaps_context_ids_seq
+\seq_new:N \g__sdaps_contexts_seq
+
+\bool_new:N \g_sdaps_write_enable_bool
+\bool_gset_false:N \g_sdaps_write_enable_bool
+
+\cs_new_protected_nopar:Nn \_sdaps_context_to_tl:N
+{
+  \tl_set:Nx #1 {_write_enable=\bool_if:NTF\g_sdaps_write_enable_bool{\c_true_bool}{\c_false_bool}}
+  \prop_map_inline:Nn \g__sdaps_current_context_prop {
+    % Could we remove some of the braces in the TL?
+    \tl_if_eq:nnTF { \undefined } { ##2 } {
+      \tl_put_right:Nn #1 {,{##1}}
+    } {
+      \tl_put_right:Nn #1 {,{##1}={##2}}
+    }
+  }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_begin:n
+{
+  % We need to serialize the current context and save it away.
+
+  \group_begin:
+    % Serialize the current context
+    \_sdaps_context_to_tl:N \l__sdaps_tmpa_tl
+    \tl_gset:NV \g__sdaps_current_context_tl \l__sdaps_tmpa_tl
+    % Stuff it away in our sequence
+    \seq_gput_left:NV \g__sdaps_contexts_seq \g__sdaps_current_context_tl
+    \seq_gput_left:NV \g__sdaps_context_ids_seq \g__sdaps_current_context_id_tl
+
+    % Clear the hooks
+    \sdaps_context_put:nn { _context_hook_end } {}
+    \sdaps_context_put:nn { _context_hook_post_end } {}
+
+    \tl_gset:Nn \g__sdaps_current_context_id_tl { #1 }
+  \group_end:
+}
+
+\msg_new:nnn { sdapsbase } { context_end_none_left } { There ~ is ~ no ~ context ~ to ~ end ~ left! }
+\msg_new:nnn { sdapsbase } { context_end_broken } { The ~ current ~ context ~ with ~ id ~ #1 ~ may ~ not ~ be ~ ended ~ here. }
+
+\cs_new_protected_nopar:Nn \__sdaps_context_end:
+{
+  \seq_if_empty:NTF \g__sdaps_context_ids_seq {
+    \msg_error:nn { sdapsbase } { context_end_none_left }
+  } {
+
+    \group_begin:
+    \sdaps_context_get:nN { _context_hook_end } \l_tmpa_tl
+    \tl_if_eq:VnF \l_tmpa_tl { \q_no_value } { \tl_use:N \l_tmpa_tl }
+
+    % Grab post end hook
+    \sdaps_context_get:nN { _context_hook_post_end } \l_tmpa_tl
+
+    \_sdaps_context_clear:
+    \seq_gpop_left:NN \g__sdaps_contexts_seq \g__sdaps_current_context_tl
+    \seq_gpop_left:NN \g__sdaps_context_ids_seq \l__sdaps_tmpa_tl
+
+    % Unpack context token list
+    \sdaps_context_set:V \g__sdaps_current_context_tl
+
+    \sdaps_context_get:nN { _write_enable } \l_tmpb_tl
+    \bool_gset:Nn \g_sdaps_write_enable_bool { \l_tmpb_tl }
+    \sdaps_context_remove:n { _write_enable }
+
+    \tl_gclear:N \g__sdaps_current_context_tl
+    \tl_gset:NV \g__sdaps_current_context_id_tl \l__sdaps_tmpa_tl
+
+    \tl_if_eq:VnF \l_tmpa_tl { \q_no_value } { \tl_use:N \l_tmpa_tl }
+    \group_end:
+  }
+}
+
+\bool_new:N \l__sdaps_tmp_bool
+
+\cs_new_protected_nopar:Nn \__sdaps_test_context_id:n
+{
+  \tl_if_eq:VnTF \g__sdaps_current_context_id_tl { #1 } {
+    \bool_set:Nn \l__sdaps_tmp_bool \c_true_bool
+  } {
+    \bool_set:Nn \l__sdaps_tmp_bool \c_false_bool
+  }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_end:n
+{
+  \__sdaps_test_context_id:n { #1 }
+
+  \bool_until_do:nn { \l__sdaps_tmp_bool } {
+    \sdaps_context_end:
+
+    \__sdaps_test_context_id:n { #1 }
+  }
+  \sdaps_context_end:
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_context_end_local_scope:
+{
+  \__sdaps_test_context_id:n { sdaps_local_scope }
+
+  \bool_until_do:nn { \l__sdaps_tmp_bool } {
+    \__sdaps_context_end:
+
+    \__sdaps_test_context_id:n { sdaps_local_scope }
+  }
+  \__sdaps_context_end:
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_end:
+{
+  % Ensure the current context is not a local group
+  \tl_if_eq:VnTF \g__sdaps_current_context_id_tl { sdaps_local_scope } {
+    \msg_error:nnV { sdapsbase } { context_end_broken } \g__sdaps_current_context_id_tl
+  } {}
+
+  \__sdaps_context_end:
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_begin:
+{
+  \sdaps_context_begin:n {}
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_begin_local:
+{
+  % Create a new context which will automatically be destroyed at the end of
+  % the current TeX group.
+  \sdaps_context_begin:n { sdaps_local_scope }
+  \group_insert_after:N \__sdaps_context_end_local_scope:
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_put:n
+{
+  \sdaps_context_put:nn { #1 } { \undefined }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_remove:n
+{
+  \prop_gremove:Nn \g__sdaps_current_context_prop { #1 }
+}
+
+\msg_new:nnn { sdapsbase } { context_key_broken } { Keys ~ may ~ not ~ contain ~ any ~ special ~ tokens! ~ However ~ the ~ key ~ #1 ~ does ~ contain ~ tokens ~ that ~ are ~ not ~ permissible! }
+\cs_new_protected_nopar:Nn \sdaps_context_put:nn
+{
+  % TODO: How can I ensure that {} are not contained?
+  % Though it would not be that bad actually.
+  \tl_if_in:nnTF {#1} {,} {
+    \msg_error:nnn { sdapsbase } { context_key_broken } {#1}
+  } {
+  }
+
+  \tl_if_in:nnTF {#1} {=} {
+    \msg_error:nnn { sdapsbase } { context_key_broken } {#1}
+  } {
+  }
+
+  \prop_gput:Nnn \g__sdaps_current_context_prop { #1 } { #2 }
+}
+\cs_generate_variant:Nn \sdaps_context_put:nn { nV }
+
+\cs_new_protected_nopar:Nn \sdaps_context_set:n
+{
+  \keyval_parse:NNn \sdaps_context_put:n \sdaps_context_put:nn { #1 }
+}
+\cs_generate_variant:Nn \sdaps_context_set:n {V}
+
+\cs_new_protected_nopar:Nn \sdaps_context_get:nN
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #1 } #2
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_gget:nN
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #1 } \l_tmpa_tl
+  \tl_gset_eq:NN #2 \l_tmpa_tl
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_append:nnn
+{
+  \sdaps_context_get:nN { #1 } \l__sdaps_tmpa_tl
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+    \sdaps_context_put:nn { #1 } { #2 }
+  } {
+    \tl_put_right:Nn \l__sdaps_tmpa_tl { #3 }
+    \tl_put_right:Nn \l__sdaps_tmpa_tl { #2 }
+    \sdaps_context_put:nV { #1 } \l__sdaps_tmpa_tl
+  }
+}
+\cs_generate_variant:Nn \sdaps_context_append:nnn { nVn }
+
+\cs_new_protected_nopar:Nn \sdaps_context_append:nn
+{
+  \sdaps_context_append:nnn { #1 } { #2 } { , }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_hook_end:n
+{
+  \sdaps_context_append:nnn { _context_hook_end } { #1 } { }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_hook_post_end:n
+{
+  \sdaps_context_append:nnn { _context_hook_post_end } { #1 } { }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_enable_writing:
+{
+  \bool_gset_true:N \g_sdaps_write_enable_bool
+}
+
+\cs_new_protected_nopar:Nn \sdaps_context_disable_writing:
+{
+  \bool_gset_false:N \g_sdaps_write_enable_bool
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_context_clear:
+{
+  \prop_gclear:N \g__sdaps_current_context_prop
+}
+
+\cs_new:Nn \sdaps_context_map_function:N
+{
+  \prop_map_function:NN \g__sdaps_current_context_prop #1
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_get:Nn
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #2 } #1
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_get_empty:Nn
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #2 } #1
+  \tl_if_eq:VnT #1 { \q_no_value } {
+    \tl_set:Nn #1 {}
+  }
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_append_from_context:nN
+{
+  \prop_get:NnN \g__sdaps_current_context_prop { #1 } \l__sdaps_tmpb_tl
+  \tl_if_eq:VnF \l__sdaps_tmpb_tl { \q_no_value } {
+    \tl_put_right:Nn #2 {,}
+    \tl_put_right:NV #2 {\l__sdaps_tmpb_tl}
+  }
+}
+\cs_generate_variant:Nn \__sdaps_append_from_context:nN { VN }
+
+
+\seq_new:N \g__sdaps_object_id_seq
+\int_new:N \g__sdaps_object_id_int
+\int_gzero:N \g__sdaps_object_id_int
+
+\cs_new_protected_nopar:Nn \_sdaps_qobject_end_hook:
+{
+  % Take the current implicit variable
+  \sdaps_context_get:nN { _implicit_var } \l__sdaps_tmpa_tl
+
+  % Prepend explicit variable name; we assume that either _implicit_var or _var
+  % have a proper value.
+  \sdaps_context_get:nN { _var } \l__sdaps_tmpb_tl
+  \tl_if_eq:VnF \l__sdaps_tmpb_tl { \q_no_value } {
+    \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+      \tl_clear:N \l__sdaps_tmpa_tl
+    } {
+      \tl_put_left:Nn \l__sdaps_tmpa_tl { _ }
+    }
+    \tl_put_left:NV \l__sdaps_tmpa_tl \l__sdaps_tmpb_tl
+  }
+
+  \sdaps_context_get:nN { id } \l__sdaps_tmpb_tl
+
+  \tl_if_eq:VnTF \l__sdaps_tmpb_tl { \q_no_value } {
+    \msg_warning:nn { sdapsbase } { no_qid }
+  } {
+    \sdaps_info_write_x:x{
+      Variable[\l__sdaps_tmpb_tl]=\l__sdaps_tmpa_tl
+    }
+  }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_qobject_post_hook:
+{
+  \seq_gpop_right:NN \g__sdaps_object_id_seq \l__sdaps_tmpa_tl
+  \int_gset:NV \g__sdaps_object_id_int \l__sdaps_tmpa_tl
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_begin:nnn
+{
+  \int_gincr:N \g__sdaps_object_id_int
+  \seq_gput_right:NV \g__sdaps_object_id_seq \g__sdaps_object_id_int
+  \tl_set:Nx \l__sdaps_tmpa_tl { \int_use:N \g__sdaps_object_id_int }
+  \int_gzero:N \g__sdaps_object_id_int
+
+  \tl_set:Nx \l__sdaps_tmpb_tl { \seq_use:Nn \g__sdaps_object_id_seq {.} }
+
+  \sdaps_context_begin:n {#1}
+    \sdaps_context_put:nV {id} \l__sdaps_tmpb_tl
+    \sdaps_info_write:x {QObject-#2=\tl_use:N\l__sdaps_tmpb_tl. ~ \exp_not:n{#3}}
+    \sdaps_context_append:nVn { _implicit_var } \l__sdaps_tmpa_tl { _ }
+    \sdaps_context_hook_end:n { \_sdaps_qobject_end_hook: }
+    \sdaps_context_hook_post_end:n { \_sdaps_qobject_post_hook: }
+}
+\cs_generate_variant:Nn \sdaps_qobject_begin:nnn { nnV, nVV, nVn }
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_end:n
+{
+  % End the context in question, everything else is done from the close hook
+  \sdaps_context_end:n {#1}
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_begin:nn
+{
+  \sdaps_qobject_begin:nnn { unnamed_qobject } { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_begin_local:nn
+{
+  % Empty local context which automatically closes the qobject
+  \sdaps_context_begin_local:
+    \sdaps_qobject_begin:nnn { unnamed_local_qobject } { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_end:
+{
+  \sdaps_qobject_end:n { unnamed_qobject }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_qobject_append_var:n
+{
+  % If the given variable name starts with _ then include the implicitly
+  % generated variable name.
+  \tl_if_head_eq_charcode:nNTF { #1 } _ {
+    \sdaps_context_get:nN { _implicit_var } \l__sdaps_tmpa_tl
+    \tl_if_eq:VnF \l__sdaps_tmpa_tl { \q_no_value } {
+      \sdaps_context_append:nVn { _var } \l__sdaps_tmpa_tl { _ }
+    }
+
+    \sdaps_context_append:nnn { _var } { #1 } { }
+  } {
+    \sdaps_context_append:nnn { _var } { #1 } { _ }
+  }
+
+  % We have a proper variable name now, delete the implicit one
+  \sdaps_context_remove:n { _implicit_var }
+}
+\cs_generate_variant:Nn \sdaps_qobject_append_var:n { V }
+
+\msg_new:nnn { sdapsbase } { no_qid } { Trying~to~output~metadata~but~no~question~ID~is~set~on~the~context.~Did~you~start~a~question?~Supressing~the~output! }
+
+\cs_new_protected_nopar:Nn \sdaps_answer:n
+{
+  \sdaps_context_get:nN { id } \l__sdaps_tmpa_tl
+
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+    \msg_warning:nn { sdapsbase } { no_qid }
+  } {
+    \sdaps_info_write:x {
+       Answer[\tl_use:N \l__sdaps_tmpa_tl]=\exp_not:n { #1 }
+    }
+  }
+}
+\cs_generate_variant:Nn \sdaps_answer:n { o }
+\cs_generate_variant:Nn \sdaps_answer:n { f }
+\cs_generate_variant:Nn \sdaps_answer:n { V }
+
+\cs_new_protected_nopar:Nn \sdaps_range:nnn
+{
+  \sdaps_context_get:nN { id } \l__sdaps_tmpa_tl
+
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+    \msg_warning:nn { sdapsbase } { no_qid }
+  } {
+    \sdaps_info_write:x {
+       Range-#1[\tl_use:N \l__sdaps_tmpa_tl]=\int_eval:n{#2},\exp_not:n { #3 }
+    }
+  }
+}
+\cs_generate_variant:Nn \sdaps_range:nnn { nno }
+\cs_generate_variant:Nn \sdaps_range:nnn { nnf }
+\cs_generate_variant:Nn \sdaps_range:nnn { nnV }
+
+\cs_generate_variant:Nn \tl_if_head_eq_charcode:nNT { VN }
+
+\cs_new_protected_nopar:Nn \_sdaps_generate_var:nN
+{
+  \tl_set:Nn \l__sdaps_tmpa_tl { #1 }
+
+  % Generate a variable name if there is none (prepended with _ prefix)
+  \tl_if_empty:VT \l__sdaps_tmpa_tl {
+    \tl_set:Nx \l__sdaps_tmpa_tl { _ \int_eval:n { \g__sdaps_object_id_int + 1 } }
+  }
+
+  % Prepend any implicitly generated variable names if prefixed by _
+  \tl_if_head_eq_charcode:VNT \l__sdaps_tmpa_tl _ {
+    \sdaps_context_get:nN { _implicit_var } \l__sdaps_tmpb_tl
+    \tl_if_eq:VnTF \l__sdaps_tmpb_tl { \q_no_value } {
+      \tl_remove_once:Nn \l__sdaps_tmpa_tl { _ }
+    } {
+      \tl_put_left:NV \l__sdaps_tmpa_tl \l__sdaps_tmpb_tl
+    }
+  }
+
+  % Prepend explicit variable name
+  \sdaps_context_get:nN { _var } \l__sdaps_tmpb_tl
+  \tl_if_eq:VnF \l__sdaps_tmpb_tl { \q_no_value } {
+    \tl_put_left:Nn \l__sdaps_tmpa_tl { _ }
+    \tl_put_left:NV \l__sdaps_tmpa_tl \l__sdaps_tmpb_tl
+  }
+
+  \tl_set:NV #2 \l__sdaps_tmpa_tl
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_box_inc_object_id:
+{
+  \bool_if:NT \g_sdaps_write_enable_bool {
+    \int_gincr:N \g__sdaps_object_id_int
+  }
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\cs_generate_variant:Nn \keyval_parse:NNn { NNV }
+\cs_generate_variant:Nn \int_gset:Nn { NV }
+
+\seq_new:N \g__sdaps_checkbox_overlays_seq
+\seq_new:N \g__sdaps_textbox_overlays_seq
+
+\prop_new:N \g__sdaps_id_to_overrides_prop
+\prop_new:N \g__sdaps_overrides_prop
+\prop_new:N \g__sdaps_id_overrides_prop
+
+\cs_new_protected_nopar:Nn \__sdaps_questionnaire_overrides_set:nn
+{
+  \str_if_eq_x:nnTF { #1 } { * } {
+    \__sdaps_parse_overrides:n{ #2 }
+  } {
+    \prop_put:Nnn \g__sdaps_id_to_overrides_prop { #1 } { #2 }
+  }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_overrides_init:n
+{
+  \keyval_parse:NNn \use_none:n \__sdaps_questionnaire_overrides_set:nn { #1 }
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_overrides_set:nn
+{
+  \prop_gput:Nnn \g__sdaps_overrides_prop { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_id_overrides_set:nn
+{
+  \prop_gput:Nnn \g__sdaps_id_overrides_prop { #1 } { #2 }
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_parse_overrides:n
+{
+  \prop_gclear:N \g__sdaps_overrides_prop
+  \keyval_parse:NNn \use_none:n \__sdaps_overrides_set:nn { #1 }
+}
+
+\tl_new:N \l__sdaps_set_qid_tl
+\cs_new_protected_nopar:Nn \sdaps_set_questionnaire_id:n
+{
+  \tl_gset:Nn \g__sdaps_questionnaire_id_tl { #1 }
+  \prop_gclear:N \g__sdaps_id_overrides_prop
+  \prop_get:NnNT \g__sdaps_id_to_overrides_prop { #1 } \l__sdaps_set_qid_tl {
+    \keyval_parse:NNV \use_none:n \__sdaps_id_overrides_set:nn \l__sdaps_set_qid_tl
+  }
+}
+\cs_generate_variant:Nn \sdaps_set_questionnaire_id:n { V }
+
+\cs_new_protected_nopar:Nn \__sdaps_append_override_options:Nnn
+{
+  % Global definition
+  % First generic for all items
+  \prop_get:NnNT \g__sdaps_overrides_prop { * } \l__sdaps_tmpa_tl {
+    \tl_put_right:Nn #1 {,}
+    \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+  }
+  \tl_if_empty:nF { #2 } {
+    % Items with same variable name
+    \prop_get:NnNT \g__sdaps_overrides_prop { #2 } \l__sdaps_tmpa_tl {
+      \tl_put_right:Nn #1 {,}
+      \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+    }
+    \tl_if_empty:nF { #3 } {
+      % Items with same variable name and value
+      \prop_get:NnNT \g__sdaps_overrides_prop { #2&#3 } \l__sdaps_tmpa_tl {
+        \tl_put_right:Nn #1 {,}
+        \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+      }
+    }
+  }
+
+  % Local (questionnaire ID specific) definition
+  % First generic for all items
+  \prop_get:NnNT \g__sdaps_id_overrides_prop { * } \l__sdaps_tmpa_tl {
+    \tl_put_right:Nn #1 {,}
+    \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+  }
+  \tl_if_empty:nF { #2 } {
+    % Items with same variable name
+    \prop_get:NnNT \g__sdaps_id_overrides_prop { #2 } \l__sdaps_tmpa_tl {
+      \tl_put_right:Nn #1 {,}
+      \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+    }
+    \tl_if_empty:nF { #3 } {
+      % Items with same variable name and value
+      \prop_get:NnNT \g__sdaps_id_overrides_prop { #2&#3 } \l__sdaps_tmpa_tl {
+        \tl_put_right:Nn #1 {,}
+        \tl_put_right:NV #1 \l__sdaps_tmpa_tl
+      }
+    }
+  }
+}
+\cs_generate_variant:Nn \__sdaps_append_override_options:Nnn  { NVn }
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\dim_new:N \g_sdaps_linewidth_dim
+\g_sdaps_linewidth_dim=1bp
+\tl_new:N \g__sdaps_checkbox_last_info_tl
+
+\int_new:N \g__sdaps_textbox_num_int
+\int_set:Nn \g__sdaps_textbox_num_int 0
+
+\tl_new:N \l_sdaps_var_tl
+\dim_new:N \l_sdaps_x_dim
+\dim_new:N \l_sdaps_y_dim
+\dim_new:N \l_sdaps_width_dim
+\dim_new:N \l_sdaps_height_dim
+
+
+\iow_new:N \g_sdaps_infofile_iow
+\iow_open:Nn \g_sdaps_infofile_iow { \c_sys_jobname_str . sdaps }
+\int_new:N \g__sdaps_infofile_line_int
+\int_gset:Nn \g__sdaps_infofile_line_int { 0 }
+
+\cs_new_protected_nopar:Nn \sdaps_info_write:n
+{
+  \bool_if:NT \g_sdaps_write_enable_bool {
+    \int_gincr:N \g__sdaps_infofile_line_int
+    \iow_shipout:Nx \g_sdaps_infofile_iow { [ \int_use:N \g__sdaps_infofile_line_int ] \exp_not:n { #1 } }
+  }
+}
+\cs_generate_variant:Nn \sdaps_info_write:n { x }
+
+\cs_new_protected_nopar:Nn \sdaps_info_write_x:n
+{
+  \bool_if:NT \g_sdaps_write_enable_bool {
+    \int_gincr:N \g__sdaps_infofile_line_int
+    \iow_shipout_x:Nx \g_sdaps_infofile_iow { [ \int_use:N \g__sdaps_infofile_line_int ] \exp_not:n { #1 } }
+  }
+}
+\cs_generate_variant:Nn \sdaps_info_write_x:n { x }
+
+
+\dim_new:N \l_sdaps_checkbox_linewidth_dim
+\dim_new:N \l_sdaps_checkbox_width_dim
+\dim_new:N \l_sdaps_checkbox_height_dim
+\tl_new:N \l_sdaps_checkbox_form_tl
+\tl_new:N \l_sdaps_checkbox_fill_tl
+\tl_new:N \l_sdaps_checkbox_var_tl
+\tl_new:N \l_sdaps_checkbox_value_tl
+\bool_new:N \l_sdaps_checkbox_draw_check_bool
+
+\tl_set:Nn \l_sdaps_checkbox_form_tl { box }
+
+\tl_new:N \l_sdaps_parse_unknown_tl
+
+\tl_new:N \l_sdaps_overlay_centered_text_tl
+\tl_new:N \l_sdaps_overlay_minipage_text_tl
+\tl_new:N \l_sdaps_overlay_minipage_pos_tl
+\dim_new:N \l_sdaps_overlay_minipage_pad_dim
+
+\keys_define:nn { sdaps / checkbox }
+{
+  linewidth    .dim_set:N   = \l_sdaps_checkbox_linewidth_dim,
+  linewidth    .initial:n   = 1bp,
+  width        .dim_set:N   = \l_sdaps_checkbox_width_dim,
+  width        .initial:n   = 3.5mm,
+  height       .dim_set:N   = \l_sdaps_checkbox_height_dim,
+  height       .initial:n   = 3.5mm,
+  form         .choices:nn  = { box, ellipse } { \tl_set:Nx \l_sdaps_checkbox_form_tl { \l_keys_choice_tl } },
+  value        .tl_set:N    = \l_sdaps_checkbox_value_tl,
+
+  fill         .tl_set:N    = \l_sdaps_checkbox_fill_tl,
+  fill         .initial:n   = { white },
+
+  draw_check   .bool_set:N  = \l_sdaps_checkbox_draw_check_bool,
+  draw_check   .default:n   = true,
+  draw_check   .initial:n   = false,
+
+  % Simple node overlay
+  centered_text  .tl_set:N    = \l_sdaps_overlay_centered_text_tl,
+  centered_text  .initial:n   = {},
+
+  % minipage overlay
+  text         .tl_set:N    = \l_sdaps_overlay_minipage_text_tl,
+  text         .initial:n   = {},
+  text_align   .tl_set:N    = \l_sdaps_overlay_minipage_pos_tl,
+  text_align   .initial:n   = {c},
+  text_padding .dim_set:N   = \l_sdaps_overlay_minipage_pad_dim,
+  text_padding .initial:n   = {2bp},
+
+  ellipse    .meta:n  = { form=ellipse },
+  box        .meta:n  = { form=box },
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_checkbox_internal:nn
+{
+  \mbox{
+    \sdaps_if_rtl:T {\beginL}
+    \pdfsavepos
+
+     % Position of page and baseline offset
+    \dim_set:Nn \l_sdaps_x_dim { \hoffset }
+    \dim_set:Nn \l_sdaps_y_dim { \voffset + \l_sdaps_checkbox_height_dim - \dim_eval:n { 0.5\l_sdaps_checkbox_height_dim - 0.8ex } }
+
+    % Size
+    \dim_set:Nn \l_sdaps_width_dim { \l_sdaps_checkbox_width_dim }
+    \dim_set:Nn \l_sdaps_height_dim { \l_sdaps_checkbox_height_dim }
+
+    \bool_if:NT \g_sdaps_write_enable_bool {
+      % pdflast[xy]pos is the PDF position of the baseline at the start of the box
+      % excluding the page origin offset.
+      \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+      \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+        \msg_warning:nn { sdapsbase } { no_qid }
+      } {
+        \sdaps_info_write_x:x{
+          Box[\l__sdaps_tmpa_tl]=Checkbox,
+          \exp_not:n{\int_use:N\g_sdaps_page_int},
+          \exp_not:n{\dim_eval:n} { \exp_not:f {\dim_use:N \l_sdaps_x_dim + \the\pdflastxpos sp} },
+          \exp_not:n{\dim_eval:n} { \exp_not:f {\dim_use:N \l_sdaps_y_dim + \the\pdflastypos sp} },
+          \dim_use:N \l_sdaps_width_dim,
+          \dim_use:N \l_sdaps_height_dim,
+          \tl_to_str:N\l_sdaps_checkbox_form_tl,
+          \dim_use:N \l_sdaps_checkbox_linewidth_dim,
+          #1,\tl_if_empty:nTF { #2 } { \int_use:N \g__sdaps_object_id_int } { #2 }
+        }
+      }
+    }
+
+    \tikz[baseline={0.5\l_sdaps_checkbox_height_dim-0.8ex}]{%
+      \tl_if_eq:VnT \l_sdaps_checkbox_form_tl { box } {
+        \draw[line~width=\l_sdaps_checkbox_linewidth_dim,fill=\l_sdaps_checkbox_fill_tl] (0.5\l_sdaps_checkbox_linewidth_dim, 0.5\l_sdaps_checkbox_linewidth_dim) rectangle +($(\l_sdaps_checkbox_width_dim, \l_sdaps_checkbox_height_dim)-(\l_sdaps_checkbox_linewidth_dim,\l_sdaps_checkbox_linewidth_dim)$);%
+      }
+      \tl_if_eq:VnT \l_sdaps_checkbox_form_tl { ellipse } {
+        \draw[line~width=\l_sdaps_checkbox_linewidth_dim,fill=\l_sdaps_checkbox_fill_tl] (0.5\l_sdaps_checkbox_width_dim, 0.5\l_sdaps_checkbox_height_dim) circle [x~radius=0.5\l_sdaps_checkbox_width_dim-0.5\l_sdaps_checkbox_linewidth_dim, y~radius=0.5\l_sdaps_checkbox_height_dim-0.5\l_sdaps_checkbox_linewidth_dim];%
+      }
+
+      % For the overlay we actually position the nodes relative to the checkbox
+      % and not absolute on the page.
+      \dim_set:Nn \l_sdaps_x_dim { 0pt }
+      \dim_set:Nn \l_sdaps_y_dim { \l_sdaps_checkbox_height_dim }
+
+      % Use overlay so that nothing happens if a node is larger than the checkbox
+      \begin{scope}[overlay]
+        \seq_map_inline:Nn \g__sdaps_checkbox_overlays_seq {##1}
+      \end{scope}
+    }
+    \sdaps_if_rtl:T {\endL}
+  }
+}
+\cs_generate_variant:Nn \__sdaps_checkbox_internal:nn { Vn }
+
+\sdaps_context_set:n { checkboxtype=multichoice }
+\cs_new_protected_nopar:Nn \sdaps_checkbox_set_type:n
+{
+  \sdaps_context_set:n { checkboxtype={#1} }
+}
+\cs_generate_variant:Nn \sdaps_checkbox_set_type:n { V }
+
+\cs_new_protected_nopar:Nn \sdaps_checkbox:nn
+{
+  \group_begin:%
+
+    \_sdaps_generate_var:nN { #1 } \l_sdaps_var_tl
+
+    \sdaps_context_get:nN { checkboxtype } \l__sdaps_tmpa_tl
+    \tl_if_eq:VnTF \l__sdaps_tmpa_tl { multichoice } {
+      \tl_set:Nn \l_sdaps_parse_unknown_tl { box }
+    } {
+      \tl_set:Nn \l_sdaps_parse_unknown_tl { ellipse }
+    }
+
+    \__sdaps_append_from_context:nN { * } \l_sdaps_parse_unknown_tl
+    \__sdaps_append_from_context:VN \l__sdaps_tmpa_tl \l_sdaps_parse_unknown_tl
+    \__sdaps_append_override_options:NVn \l_sdaps_parse_unknown_tl \l_sdaps_var_tl { #2 }
+
+    \keys_set_known:nVN { sdaps / checkbox } \l_sdaps_parse_unknown_tl \l_sdaps_parse_unknown_tl
+
+    \_sdaps_box_inc_object_id:
+
+    \__sdaps_checkbox_internal:Vn \l_sdaps_var_tl { #2 }
+  \group_end:%
+  \ignorespaces
+}
+\cs_generate_variant:Nn \sdaps_checkbox:nn { Vn, VV, nV }
+
+\cs_new_protected_nopar:Nn \sdaps_overlay_check:
+{
+  \bool_if:NT \l_sdaps_checkbox_draw_check_bool {
+    \begin{scope}[decoration={random~steps,segment~length=4pt,amplitude=1pt}]
+      \draw[line~width=\l_sdaps_checkbox_linewidth_dim, decorate] ($(0, 0) - (2pt,2pt)$) -- (0.5\l_sdaps_checkbox_width_dim, 0.5\l_sdaps_checkbox_height_dim) -- ($(\l_sdaps_checkbox_width_dim, \l_sdaps_checkbox_height_dim) + (2pt,2pt)$);%
+      \draw[line~width=\l_sdaps_checkbox_linewidth_dim, decorate] ($(0, \l_sdaps_checkbox_height_dim) + (-2pt,2pt)$) -- (0.5\l_sdaps_checkbox_width_dim, 0.5\l_sdaps_checkbox_height_dim) -- ($(\l_sdaps_checkbox_width_dim, 0) + (2pt,-2pt)$);%
+    \end{scope}
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \sdaps_overlay_check:
+
+\cs_new_protected_nopar:Nn \sdaps_overlay_centered:
+{
+  \tl_if_empty:NF \l_sdaps_overlay_centered_text_tl {
+    \node[anchor=center,inner~sep=0pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \l_sdaps_overlay_centered_text_tl
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \sdaps_overlay_centered:
+\seq_put_left:Nn \g__sdaps_textbox_overlays_seq \sdaps_overlay_centered:
+
+\cs_new_protected_nopar:Nn \sdaps_overlay_minipage:
+{
+  \tl_if_empty:NF \l_sdaps_overlay_minipage_text_tl {
+    \node[anchor=center,inner~sep=0pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \dim_set:Nn \l_sdaps_width_dim { \l_sdaps_width_dim - 2\l_sdaps_overlay_minipage_pad_dim }
+      \dim_set:Nn \l_sdaps_height_dim { \l_sdaps_height_dim - 2\l_sdaps_overlay_minipage_pad_dim }
+
+      \begin{minipage}[t][\l_sdaps_height_dim][\l_sdaps_overlay_minipage_pos_tl]{\l_sdaps_width_dim}
+        % Hm, is this sane?
+        \tex_let:D \textheight\l_sdaps_height_dim
+        \l_sdaps_overlay_minipage_text_tl
+      \end{minipage}
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \sdaps_overlay_minipage:
+\seq_put_left:Nn \g__sdaps_textbox_overlays_seq \sdaps_overlay_minipage:
+
+
+
+\sdaps_context_set:n { textboxtype=textbox }
+\cs_new_protected_nopar:Nn \sdaps_textbox_set_type:n
+{
+  \sdaps_context_set:n { textboxtype={#1} }
+}
+\cs_generate_variant:Nn \sdaps_textbox_set_type:n { V }
+
+\dim_new:N  \l_sdaps_textbox_linewidth_dim
+\tl_new:N   \l_sdaps_textbox_var_tl
+\tl_new:N   \l_sdaps_textbox_fill_tl
+\tl_new:N   \l__sdaps_textbox_boxtype_tl
+
+\keys_define:nn { sdaps / textbox }
+{
+  linewidth    .dim_set:N   = \l_sdaps_textbox_linewidth_dim,
+  linewidth    .initial:n   = 1bp,
+
+  fill         .tl_set:N    = \l_sdaps_textbox_fill_tl,
+  fill         .initial:n   = { white },
+
+  % Simple node overlay
+  centered_text  .tl_set:N    = \l_sdaps_overlay_centered_text_tl,
+  centered_text  .initial:n   = {},
+
+  % minipage overlay
+  text         .tl_set:N    = \l_sdaps_overlay_minipage_text_tl,
+  text         .initial:n   = {},
+  text_align   .tl_set:N    = \l_sdaps_overlay_minipage_pos_tl,
+  text_align   .initial:n   = {c},
+  text_padding .dim_set:N   = \l_sdaps_overlay_minipage_pad_dim,
+  text_padding .initial:n   = {2bp},
+}
+
+\dim_new:N \l__sdaps_textbox_dp_dim
+\dim_new:N \l__sdaps_textbox_ht_dim
+\dim_new:N \l__sdaps_textbox_wd_dim
+\dim_new:N \l__sdaps_textbox_pad_dim
+
+\msg_new:nnn { sdapsbase } { textbox_wrong_mode } { Impossible~to~layout~a~#1~textbox~in~#2~mode. }
+
+\cs_new_protected_nopar:Nn \__sdaps_textbox_prepare:n
+{
+  \tl_set:Nn \l_sdaps_parse_unknown_tl {}
+
+  \_sdaps_generate_var:nN { #1 } \l_sdaps_var_tl
+
+  \sdaps_context_get:nN { textboxtype } \l__sdaps_tmpa_tl
+  \tl_if_eq:VnTF \l__sdaps_tmpa_tl { codebox } {
+    \tl_set:Nn \l__sdaps_textbox_boxtype_tl { Codebox }
+  } {
+    \tl_set:Nn \l__sdaps_textbox_boxtype_tl { Textbox }
+  }
+
+  \__sdaps_append_from_context:nN { * } \l_sdaps_parse_unknown_tl
+  \__sdaps_append_from_context:VN \l__sdaps_tmpa_tl \l_sdaps_parse_unknown_tl
+  \__sdaps_append_override_options:NVn \l_sdaps_parse_unknown_tl \l_sdaps_var_tl { }
+
+  \keys_set_known:nVN { sdaps / textbox } \l_sdaps_parse_unknown_tl \l_sdaps_parse_unknown_tl
+
+  \_sdaps_box_inc_object_id:
+}
+
+\cs_new_protected_nopar:Nn \sdaps_textbox_vhstretch:nnn
+{
+  \if_mode_math:
+    \msg_error:nnnn { sdapsbase } { textbox_wrong_mode } { vhstretch } { math }
+  \else:
+  \tex_par:D
+  \if_mode_horizontal:
+    \msg_error:nnnn { sdapsbase } { textbox_wrong_mode } { vhstretch } { horizontal }
+  \else:
+  % Go into vertical mode by ending the current paragraph and insert
+  % \cs{widowpenalty} so that we don't get an unwelcome page break. It is
+  % assumed that any explanation for the textbox will be on top.
+  \tex_penalty:D \tex_widowpenalty:D
+
+  \group_begin:%
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+    % TODO: Make this configurable
+    \dim_set:Nn \l__sdaps_textbox_dp_dim { 0.5ex }
+    \dim_set:Nn \l__sdaps_textbox_ht_dim { 1.7ex }
+
+    \vbox:n {
+      \sdaps_if_rtl:T {\beginL}
+      \leftskip=0pt
+      \rightskip=0pt plus 1fill
+      \noindent
+      \tex_vrule:D height \l__sdaps_textbox_ht_dim depth 0pt width 0pt
+      \pgfsys at markposition{textboxtop\int_use:N\g__sdaps_textbox_num_int}
+      \sdaps_if_rtl:T {\endL}
+    }
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \tex_cleaders:D\tex_hbox:D{}\skip_vertical:n{#2 - \l__sdaps_textbox_ht_dim}
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \tex_cleaders:D\tex_hbox:D{}\skip_vertical:n{\stretch{#3}}
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \vbox:n {
+      \sdaps_if_rtl:T {\beginL}
+      \noindent
+      \leftskip=0pt plus 1fill
+      \rightskip=0pt
+      \begin{tikzpicture}[remember~picture,overlay,shift=(current~page.south~west)]
+        \pgfsys at getposition{pgfpageorigin}{\@sdaps at pageorigin}
+        \pgfsys at getposition{textboxtop\int_use:N\g__sdaps_textbox_num_int}{\@sdaps at textboxtoppos}
+        \pgfpointadd{\@sdaps at textboxtoppos}{
+          \pgfpointadd{\@sdaps at pageorigin}{\pgfpoint{0}{\l__sdaps_textbox_ht_dim}}
+        }
+        \pgfgetlastxy{\l__sdaps_x}{\l__sdaps_y}
+        \dim_set:Nn \l_sdaps_x_dim {\l__sdaps_x}
+        \dim_set:Nn \l_sdaps_y_dim {\l__sdaps_y}
+
+        \pgfsys at getposition{\pgfpictureid}{\@sdaps at textboxbottompos}
+        \pgfpointdiff{\@sdaps at textboxtoppos}{\@sdaps at textboxbottompos}
+
+        % Is there a more elegant way to multiply the height with -1?
+        \pgfgetlastxy{\l__sdaps_width}{\l__sdaps_height}
+        % Add the minimum height (i.e. depth below the last baseline) to the
+        % overall height.
+        \pgfpoint{\l__sdaps_width}{-\l__sdaps_height + \l__sdaps_textbox_ht_dim}
+        \pgfgetlastxy{\l__sdaps_width}{\l__sdaps_height}
+        \dim_set:Nn \l_sdaps_width_dim {\l__sdaps_width}
+        \dim_set:Nn \l_sdaps_height_dim {\l__sdaps_height}
+
+        % Draw the rectangle
+        \draw[line~width=\l_sdaps_textbox_linewidth_dim,fill=\l_sdaps_textbox_fill_tl] ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5 * (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$) rectangle +($(\l_sdaps_width_dim, -\l_sdaps_height_dim) - (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$);
+
+        \begin{scope}
+          \seq_map_inline:Nn \g__sdaps_textbox_overlays_seq {##1}
+        \end{scope}
+
+        \bool_if:NT \g_sdaps_write_enable_bool {
+          \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+          \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+            \msg_warning:nn { sdapsbase } { no_qid }
+          } {
+            \sdaps_info_write_x:x {
+              Box[\l__sdaps_tmpa_tl]=\l__sdaps_textbox_boxtype_tl,
+              \exp_not:n{\int_use:N\g_sdaps_page_int},
+              \dim_use:N \l_sdaps_x_dim,
+              \dim_use:N \l_sdaps_y_dim,
+              \dim_use:N \l_sdaps_width_dim,
+              \dim_use:N \l_sdaps_height_dim,
+              \dim_use:N \l_sdaps_textbox_linewidth_dim,
+              \tl_use:N \l_sdaps_var_tl,
+            }
+          }
+        }
+      \end{tikzpicture}
+      \sdaps_if_rtl:T {\endL}
+    }
+
+    % We are done here, but we need the correct depth, so skip around a bit
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \skip_vertical:n{ - \l__sdaps_textbox_dp_dim}
+
+    \tex_penalty:D 10000
+    \nointerlineskip
+
+    \hbox:n { \vrule depth \l__sdaps_textbox_dp_dim height 0pt width 0pt }
+
+    \int_gincr:N\g__sdaps_textbox_num_int
+
+  \group_end:
+  \fi:
+  \fi:
+}
+\cs_generate_variant:Nn \sdaps_textbox_vhstretch:nnn { Vnn }
+
+\cs_new_protected_nopar:Nn \sdaps_textbox_vhstretch:nn
+{
+  \sdaps_textbox_vhstretch:nnn { #1 } { #2 } { 1 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_textbox_hstretch:nnnnn
+{
+  \group_begin:
+    \sdaps_if_rtl:T {\beginL}
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+    \dim_set:Nn \l_tmpa_dim { #2 }
+    \dim_set:Nn \l_tmpb_dim { #3 }
+
+    % Place a vrule to make space for the top/bottom padding
+    \tex_vrule:D depth \dim_use:N \l_tmpa_dim height \dim_use:N \l_tmpb_dim width 0pt \nobreak
+    \pgfsys at markposition{textboxstart\int_use:N\g__sdaps_textbox_num_int} \nobreak
+
+    \skip_horizontal:n { #4 + \stretch{#5} } \nobreak
+
+    % The textbox (rendered on the background)
+    \begin{tikzpicture}[remember~picture,overlay,shift=(current~page.south~west)]
+      \pgfsys at getposition{pgfpageorigin}{\@sdaps at pageorigin}
+      \pgfsys at getposition{textboxstart\int_use:N\g__sdaps_textbox_num_int}{\@sdaps at textboxpos}
+      % The position here is the position of the baseline.
+      % So move up by height (param 2) to get the correct vertical position.
+      \pgfpointadd{\@sdaps at textboxpos}{
+        \pgfpointadd{\@sdaps at pageorigin}{\pgfpoint{0}{ \dim_use:N \l_tmpb_dim}}
+      }
+      \pgfgetlastxy{\l__sdaps_x}{\l__sdaps_y}
+      \dim_set:Nn \l_sdaps_x_dim {\l__sdaps_x}
+      \dim_set:Nn \l_sdaps_y_dim {\l__sdaps_y}
+
+      \pgfsys at getposition{\pgfpictureid}{\@sdaps at textboxendpos}
+      % Calculate width and add the height to it
+      \pgfpointadd{
+        \pgfpointdiff{\@sdaps at textboxpos}{\@sdaps at textboxendpos}
+      }{\pgfpoint{0pt}{\dim_use:N \l_tmpa_dim +  \dim_use:N \l_tmpb_dim}}
+      \pgfgetlastxy{\l__sdaps_width}{\l__sdaps_height}
+      \dim_set:Nn \l_sdaps_width_dim {\l__sdaps_width}
+      \dim_set:Nn \l_sdaps_height_dim {\l__sdaps_height}
+
+      % Draw the rectangle
+      \draw[line~width=\l_sdaps_textbox_linewidth_dim,fill=\l_sdaps_textbox_fill_tl] ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5 * (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$) rectangle +($(\l_sdaps_width_dim, -\l_sdaps_height_dim) - (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$);
+
+      \begin{scope}
+        \seq_map_inline:Nn \g__sdaps_textbox_overlays_seq {##1}
+      \end{scope}
+
+      \bool_if:NT \g_sdaps_write_enable_bool {
+        \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+        \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+          \msg_warning:nn { sdapsbase } { no_qid }
+        } {
+          \sdaps_info_write_x:x {
+            Box[\l__sdaps_tmpa_tl]=\l__sdaps_textbox_boxtype_tl,
+            \exp_not:n{\int_use:N\g_sdaps_page_int},
+            \dim_use:N \l_sdaps_x_dim,
+            \dim_use:N \l_sdaps_y_dim,
+            \dim_use:N \l_sdaps_width_dim,
+            \dim_use:N \l_sdaps_height_dim,
+            \dim_use:N \l_sdaps_textbox_linewidth_dim,
+            \tl_use:N \l_sdaps_var_tl,
+          }
+        }
+      }
+    \end{tikzpicture}
+
+    \int_gincr:N\g__sdaps_textbox_num_int
+
+    \sdaps_if_rtl:T {\endL}
+  \group_end:
+}
+
+\cs_new_protected_nopar:Nn \__sdaps_textbox_prepare_coffin:
+{
+  \dim_set:Nn \l__sdaps_textbox_ht_dim { \coffin_ht:N \l__sdaps_textbox_coffin }
+  \dim_set:Nn \l__sdaps_textbox_dp_dim { \coffin_dp:N \l__sdaps_textbox_coffin }
+  \dim_set:Nn \l__sdaps_textbox_wd_dim { \coffin_wd:N \l__sdaps_textbox_coffin }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \tex_vrule:D depth 0pt height \dim_eval:n { \l__sdaps_textbox_ht_dim + \l__sdaps_textbox_pad_dim } width 0pt
+
+    \pdfsavepos
+
+    \dim_set:Nn \l_sdaps_width_dim {\l__sdaps_textbox_wd_dim + 2\l__sdaps_textbox_pad_dim}
+    \dim_set:Nn \l_sdaps_height_dim {\l__sdaps_textbox_ht_dim + \l__sdaps_textbox_dp_dim + 2\l__sdaps_textbox_pad_dim}
+
+    % pdflast[xy]pos is the PDF position of the top left corner excluding the
+    % origin
+    \bool_if:NT \g_sdaps_write_enable_bool {
+      \sdaps_context_get:nN {id} \l__sdaps_tmpa_tl
+      \tl_if_eq:VnTF \l__sdaps_tmpa_tl { \q_no_value } {
+        \msg_warning:nn { sdapsbase } { no_qid }
+      } {
+        \sdaps_info_write_x:x {
+          Box[\l__sdaps_tmpa_tl]=\l__sdaps_textbox_boxtype_tl,
+          \exp_not:n{\int_use:N\g_sdaps_page_int},
+          \exp_not:n{\dim_eval:n {\hoffset + \the\pdflastxpos sp}},
+          \exp_not:n{\dim_eval:n} { \exp_not:n {\voffset + \the\pdflastypos sp} + \dim_use:N \l__sdaps_textbox_ht_dim +  \dim_use:N \l__sdaps_textbox_pad_dim },
+          \dim_use:N \l_sdaps_width_dim,
+          \dim_use:N \l_sdaps_height_dim,
+          \dim_use:N \l_sdaps_textbox_linewidth_dim,
+          \tl_use:N \l_sdaps_var_tl,
+        }
+      }
+    }
+
+    \dim_set:Nn \l_sdaps_x_dim { 0pt }
+    \dim_set:Nn \l_sdaps_y_dim { \l__sdaps_textbox_ht_dim + \l__sdaps_textbox_pad_dim }
+
+    % The textbox (rendered on the background)
+    \begin{tikzpicture}[overlay]
+      % Draw the rectangle
+      \draw[line~width=\l_sdaps_textbox_linewidth_dim,fill=\l_sdaps_textbox_fill_tl] ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5 * (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$) rectangle +($(\l_sdaps_width_dim, -\l_sdaps_height_dim) - (\l_sdaps_textbox_linewidth_dim, -\l_sdaps_textbox_linewidth_dim)$);
+
+      \begin{scope}
+          \seq_map_inline:Nn \g__sdaps_textbox_overlays_seq {##1}%
+      \end{scope}
+    \end{tikzpicture}
+    \skip_horizontal:n { \l__sdaps_textbox_pad_dim }
+  }
+
+  \hcoffin_set:Nn \l_tmpb_coffin {
+    \skip_horizontal:n { \l__sdaps_textbox_pad_dim }
+    \tex_vrule:D depth \l__sdaps_textbox_pad_dim height 0pt width 0pt
+  }
+
+  \coffin_join:NnnNnnnn \l_tmpa_coffin { r } { H } \l__sdaps_textbox_coffin { l } { H } { 0pt } { 0pt }
+  \coffin_join:NnnNnnnn \l_tmpa_coffin { r } { b } \l_tmpb_coffin { l } { t } { 0pt } { 0pt }
+
+  \coffin_set_eq:NN \l__sdaps_textbox_coffin \l_tmpa_coffin
+
+  \int_gincr:N\g__sdaps_textbox_num_int
+}
+
+\coffin_new:N \l__sdaps_textbox_coffin
+\cs_new_protected_nopar:Nn \sdaps_textbox_hbox:nnn
+{
+  \group_begin:
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+    \hcoffin_set:Nn \l__sdaps_textbox_coffin { \tl_trim_spaces:n { #3 } }
+
+    \dim_set:Nn \l__sdaps_textbox_pad_dim { #2 }
+
+    \__sdaps_textbox_prepare_coffin:
+
+    \coffin_typeset:Nnnnn \l__sdaps_textbox_coffin { l } { H } { 0pt } { 0pt }
+
+  \group_end:
+}
+
+\cs_new_protected:Nn \sdaps_textbox_vbox:nnnn
+{
+  \group_begin:
+
+    \__sdaps_textbox_prepare:n { #1 }
+
+    \dim_set:Nn \l__sdaps_textbox_pad_dim { #3 }
+    \dim_set:Nn \l__sdaps_textbox_wd_dim { #2 - 2\l__sdaps_textbox_pad_dim }
+
+    \vcoffin_set:Nnn \l__sdaps_textbox_coffin { \l__sdaps_textbox_wd_dim } { \tl_trim_spaces:n { #4 }}
+
+    \__sdaps_textbox_prepare_coffin:
+
+    \coffin_typeset:Nnnnn \l__sdaps_textbox_coffin { l } { H } { 0pt } { 0pt }
+
+  \group_end:
+}
+
+
+\int_new:N \g_sdaps_page_int
+\int_set:Nn \g_sdaps_page_int { 0 }
+
+\bool_new:N \g_sdaps_recognition_bool
+\bool_new:N \g_sdaps_draft_bool
+\bool_new:N \g_sdaps_twoside_bool
+\bool_new:N \g_sdaps_print_questionnaire_id_bool
+
+\bool_set:Nn \g_sdaps_recognition_bool \c_true_bool
+\bool_set:Nn \g_sdaps_draft_bool \c_true_bool
+\bool_set:Nn \g_sdaps_twoside_bool \c_false_bool
+\bool_set:Nn \g_sdaps_print_questionnaire_id_bool \c_false_bool
+
+\tl_new:N \g_sdaps_style_tl
+\tl_new:N \g_sdaps_checkmode_tl
+\dim_new:N \g_sdaps_edge_left_margin_dim
+\dim_new:N \g_sdaps_edge_right_margin_dim
+\dim_new:N \g_sdaps_edge_top_margin_dim
+\dim_new:N \g_sdaps_edge_bottom_margin_dim
+\dim_new:N \g_sdaps_edge_marker_linewidth_dim
+\dim_new:N \g_sdaps_edge_marker_length_dim
+
+\dim_new:N \g_sdaps_classic_boxpad_dim
+\dim_new:N \g_sdaps_classic_boxsize_dim
+
+\tl_gset:Nn \g_sdaps_style_tl { code128 }
+\tl_gset:Nn \g_sdaps_twoside_barcode_tl { both }
+\tl_gset:Nn \g_sdaps_checkmode_tl { checkcorrect }
+\dim_gset:Nn \g_sdaps_edge_left_margin_dim { 10mm }
+\dim_gset:Nn \g_sdaps_edge_right_margin_dim { 10mm }
+\dim_gset:Nn \g_sdaps_edge_top_margin_dim { 12mm }
+\dim_gset:Nn \g_sdaps_edge_bottom_margin_dim { 12mm }
+\dim_gset:Nn \g_sdaps_edge_marker_linewidth_dim { 1bp }
+\dim_gset:Nn \g_sdaps_edge_marker_length_dim { 20mm }
+
+\dim_gset:Nn \g_sdaps_classic_boxpad_dim { 3mm }
+\dim_gset:Nn \g_sdaps_classic_boxsize_dim { 3.5mm }
+
+\tl_new:N \g__sdaps_questionnaire_id_tl
+\tl_gset:Nn \g__sdaps_questionnaire_id_tl { }
+
+\tl_new:N \g_sdaps_questionnaire_id_label_tl
+\tl_gset:Nn \g_sdaps_questionnaire_id_label_tl { }
+
+\tl_new:N \g_sdaps_survey_id_tl
+\tl_gset:Nn \g_sdaps_survey_id_tl { 32498923 }
+
+\tl_new:N \g_sdaps_global_id_tl
+\tl_gset:Nn \g_sdaps_global_id_tl { }
+
+\tl_new:N \g_sdaps_global_id_label_tl
+\tl_gset:Nn \g_sdaps_global_id_label_tl { }
+
+\dim_new:N \c_sdaps_barcode_height_dim
+\dim_gset:Nn \c_sdaps_barcode_height_dim {6.5mm}
+\dim_new:N \c_sdaps_barcode_bar_width_dim
+\dim_gset:Nn \c_sdaps_barcode_bar_width_dim {0.33mm}
+\dim_new:N \c_sdaps_barcode_bcorr_dim
+\dim_gset:Nn \c_sdaps_barcode_bcorr_dim {0.020mm}
+
+\dim_new:N \c_sdaps_barcode_hpad_dim
+\dim_gset:Nn \c_sdaps_barcode_hpad_dim {6.5mm}
+
+\dim_new:N \c_sdaps_barcode_vpad_dim
+\dim_gset:Nn \c_sdaps_barcode_vpad_dim {4.02mm}
+
+\cs_new_protected_nopar:Nn \sdaps_draw_codes:
+{
+  \group_begin:
+    \begin{scope}[line~width=\g_sdaps_edge_marker_linewidth_dim, shift={(current~page.south~west)}]
+      \str_if_eq:VnT \g_sdaps_style_tl { qr } {
+        \tl_if_empty:VF \g__sdaps_questionnaire_id_tl {
+          \begin{scope}[shift={($(\g_sdaps_edge_left_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+            \node(barcode)[anchor=south~west,outer~sep=0,inner~sep=0]{
+              \qrcode_render:nV {nolink,version=2,level=H,padding,height=10mm} \g__sdaps_questionnaire_id_tl
+            };
+          \end{scope}
+        }
+
+        % We unconditionally print this barcode, it is required for the recognition
+        % process.
+        \begin{scope}[shift={($(\paperwidth, 0) + (-\g_sdaps_edge_right_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+          \pgfmathsetbasenumberlength{4}
+          \pgfmathdectobase{\paddedpage}{\int_use:N\g_sdaps_page_int}{10}% Yes, padding to 4 chars the hard way
+          \node(barcode)[anchor=south~east,outer~sep=0,inner~sep=0]{
+            \qrcode_render:nx {nolink,version=2,level=H,padding,height=10mm} {\tl_use:N\g_sdaps_survey_id_tl\paddedpage}
+          };
+        \end{scope}
+
+        \tl_if_empty:VF \g_sdaps_global_id_tl {
+          \begin{scope}[shift={($(\paperwidth/2-\g_sdaps_edge_right_margin_dim/2+\g_sdaps_edge_left_margin_dim/2, \g_sdaps_edge_bottom_margin_dim)$)}]
+            \node(barcode)[anchor=south,outer~sep=0,inner~sep=0]{
+              \qrcode_render:nV {nolink,version=2,level=H,padding,height=10mm} \g_sdaps_global_id_tl
+            };
+          \end{scope}
+        }
+      }
+
+      \str_if_eq:VnT \g_sdaps_style_tl { code128 } {
+        \tl_if_empty:VF \g__sdaps_questionnaire_id_tl {
+          % TODO: do not hardcode the font!
+          \ttfamily\footnotesize
+
+          \begin{scope}[shift={($(\g_sdaps_edge_left_margin_dim, \g_sdaps_edge_bottom_margin_dim) + (\c_sdaps_barcode_hpad_dim, \c_sdaps_barcode_vpad_dim)$)}]
+            \node(barcode)[anchor=south~west,outer~sep=0,inner~sep=0]{
+              \X = \c_sdaps_barcode_bar_width_dim
+              \bcorr = \c_sdaps_barcode_bcorr_dim
+              \barheight = \c_sdaps_barcode_height_dim
+              \code_render:V \g__sdaps_questionnaire_id_tl
+            };
+            \node[below=1mm~of~barcode,distance=0,anchor=north,outer~sep=0,inner~sep=0]{
+              \tl_if_empty:VTF \g_sdaps_questionnaire_id_label_tl {
+                \tl_use:N \g__sdaps_questionnaire_id_tl
+              } {
+                \tl_use:N \g_sdaps_questionnaire_id_label_tl
+              }
+            };
+          \end{scope}
+        }
+
+        % We unconditionally print this barcode, it is required for the recognition
+        % process.
+        \begin{scope}[shift={($(\paperwidth, 0) + (-\g_sdaps_edge_right_margin_dim, \g_sdaps_edge_bottom_margin_dim) + (-\c_sdaps_barcode_hpad_dim, \c_sdaps_barcode_vpad_dim)$)}]
+          \pgfmathsetbasenumberlength{4}
+          \pgfmathdectobase{\paddedpage}{\int_use:N\g_sdaps_page_int}{10}% Yes, padding to 4 chars the hard way
+          \node(barcode)[anchor=south~east,outer~sep=0,inner~sep=0]{
+            \X = \c_sdaps_barcode_bar_width_dim
+            \bcorr = \c_sdaps_barcode_bcorr_dim
+            \barheight = \c_sdaps_barcode_height_dim
+            \code_render:x {\tl_use:N\g_sdaps_survey_id_tl \paddedpage}
+          };
+          \node[below=1mm~of~barcode,distance=0,anchor=north,outer~sep=0,inner~sep=0]{
+            % TODO: do not hardcode the font!
+            \ttfamily\footnotesize
+
+            \tl_use:N\g_sdaps_survey_id_tl\,\paddedpage
+          };
+        \end{scope}
+
+        \tl_if_empty:VF \g_sdaps_global_id_tl {
+          \begin{scope}[shift={($(\paperwidth/2-\g_sdaps_edge_right_margin_dim/2+\g_sdaps_edge_left_margin_dim/2, 0) + (0, \g_sdaps_edge_bottom_margin_dim) + (0, \c_sdaps_barcode_vpad_dim)$)}]
+            \node(barcode)[anchor=south,outer~sep=0,inner~sep=0]{
+              \X = \c_sdaps_barcode_bar_width_dim
+              \bcorr = \c_sdaps_barcode_bcorr_dim
+              \barheight = \c_sdaps_barcode_height_dim
+              \code_render:V \g_sdaps_global_id_tl
+            };
+            \node[below=1mm~of~barcode,distance=0,anchor=north,outer~sep=0,inner~sep=0]{
+              % TODO: do not hardcode the font!
+              \ttfamily\footnotesize
+
+              \tl_if_empty:VTF \g_sdaps_global_id_label_tl {
+                \tl_use:N \g_sdaps_global_id_tl
+              } {
+                \tl_use:N \g_sdaps_global_id_label_tl
+              }
+            };
+          \end{scope}
+        }
+      }
+    \end{scope}%
+  \group_end:
+}
+
+\msg_new:nnn { sdapsbase } { classic_too_many_pages } { You~cannot~have~more~than~six~pages~with~the~classic~style. }
+\msg_new:nnn { sdapsbase } { odd_page_count } { You~have~an~odd~number~of~pages,~this~does~not~work~in~duplex~mode! }
+
+\cs_new_protected_nopar:Nn \sdaps_page_end: {
+  \bool_if:NTF \g_sdaps_recognition_bool {
+    \group_begin:
+
+    \int_gincr:N\g_sdaps_page_int%
+    \sdaps_info_write:x{Pages=\int_use:N\g_sdaps_page_int}
+
+    \normalfont%
+
+    \begin{tikzpicture}[remember~picture,overlay]%
+
+      %---------------------------------------------------------------------------
+      % corner marks and corner boxes in classic mode
+      %---------------------------------------------------------------------------
+      \begin{scope}[line~width=\g_sdaps_edge_marker_linewidth_dim, line~join=miter]
+        \begin{scope}[shift={($(current~page.north~west) + (\g_sdaps_edge_left_margin_dim, -\g_sdaps_edge_top_margin_dim)$)}]
+          \draw (0,-\g_sdaps_edge_marker_length_dim) -- (0, 0) -- (\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnTF \g_sdaps_page_int {
+              { 1 } { \draw }
+              { 2 } { \fill }
+              { 3 } { \fill }
+              { 4 } { \fill }
+              { 5 } { \fill }
+              { 6 } { \draw }
+            } {
+              (\g_sdaps_classic_boxpad_dim, -\g_sdaps_classic_boxpad_dim) rectangle +(\g_sdaps_classic_boxsize_dim,-\g_sdaps_classic_boxsize_dim);
+            } {
+              % Error out (first time)
+              \msg_error:nn { sdapsbase } { classic_too_many_pages }
+            }
+          }
+        \end{scope}
+        \begin{scope}[shift={($(current~page.north~east) + (-\g_sdaps_edge_right_margin_dim, -\g_sdaps_edge_top_margin_dim)$)}]
+          \draw (0,-\g_sdaps_edge_marker_length_dim) -- (0,0) -- (-\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnT \g_sdaps_page_int {
+              { 1 } { \fill }
+              { 2 } { \fill }
+              { 3 } { \draw }
+              { 4 } { \draw }
+              { 5 } { \draw }
+              { 6 } { \draw }
+            } {
+              (-\g_sdaps_classic_boxpad_dim, -\g_sdaps_classic_boxpad_dim) rectangle +(-\g_sdaps_classic_boxsize_dim,-\g_sdaps_classic_boxsize_dim);
+            }
+          }
+        \end{scope}
+        \begin{scope}[shift={($(current~page.south~west) + (\g_sdaps_edge_left_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+          \draw (0,\g_sdaps_edge_marker_length_dim) -- (0, 0) -- (\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnT \g_sdaps_page_int {
+              { 1 } { \fill }
+              { 2 } { \draw }
+              { 3 } { \fill }
+              { 4 } { \fill }
+              { 5 } { \draw }
+              { 6 } { \draw }
+            } {
+              (\g_sdaps_classic_boxpad_dim, \g_sdaps_classic_boxpad_dim) rectangle +(\g_sdaps_classic_boxsize_dim,\g_sdaps_classic_boxsize_dim);
+            }
+          }
+        \end{scope}
+        \begin{scope}[shift={($(current~page.south~east) + (-\g_sdaps_edge_right_margin_dim, \g_sdaps_edge_bottom_margin_dim)$)}]
+          \draw (0,\g_sdaps_edge_marker_length_dim) -- (0mm, 0mm) -- (-\g_sdaps_edge_marker_length_dim,0);
+
+          \str_if_eq:VnT \g_sdaps_style_tl { classic } {
+            % TODO: Is the filled/non-filled mapping still correct?
+            \int_case:nnT \g_sdaps_page_int {
+              { 1 } { \fill }
+              { 2 } { \draw }
+              { 3 } { \fill }
+              { 4 } { \draw }
+              { 5 } { \draw }
+              { 6 } { \fill }
+            } {
+              (-\g_sdaps_classic_boxpad_dim, \g_sdaps_classic_boxpad_dim) rectangle +(-\g_sdaps_classic_boxsize_dim,\g_sdaps_classic_boxsize_dim);
+            }
+          }
+        \end{scope}
+      \end{scope}
+
+      %---------------------------------------------------------------------------
+      % barcodes/qr codes
+      %---------------------------------------------------------------------------
+      \bool_if:NTF \g__sdaps_last_page_bool {
+
+        \int_compare:nNnTF { \g_sdaps_page_int } = { 1 } {
+          % if we have a one page document, just always stamp the last
+          % page. This means that SDAPS can fall back to simplex mode
+          % automatically.
+          \sdaps_draw_codes:
+        } {
+
+          \bool_if:NTF \g_sdaps_twoside_bool {
+            \int_if_odd:VT \g_sdaps_page_int {
+              % This should no happen; draw a barcode though to keep things
+              % somewhat sane.
+              \msg_warning:nn { sdapsbase } { odd_page_count }
+              \sdaps_draw_codes:
+            } {
+              % Even page (i.e. back) has a barcode except in "front" mode
+              \str_if_eq:VnF \g_sdaps_twoside_barcode_tl { front } {
+                \sdaps_draw_codes:
+              }
+            }
+          } {
+            \sdaps_draw_codes:
+          }
+        }
+
+        % TODO: Check whether the page count is as expected?
+      } {
+        \bool_if:NTF \g_sdaps_twoside_bool {
+          \str_if_eq:VnTF \g_sdaps_twoside_barcode_tl { both } {
+            \sdaps_draw_codes:
+          } {
+            \str_if_eq:VnTF \g_sdaps_twoside_barcode_tl { front } {
+              \int_if_odd:VT \g_sdaps_page_int {
+                \sdaps_draw_codes:
+              }
+            } { % And back is the only thing left
+              \int_if_odd:VF \g_sdaps_page_int {
+                \sdaps_draw_codes:
+              }
+            }
+          }
+        } {
+          \sdaps_draw_codes:
+        }
+      }
+
+      %---------------------------------------------------------------------------
+      % watermark for non final mode
+      %---------------------------------------------------------------------------
+      \bool_if:NT \g_sdaps_draft_bool {
+        \node [rotate=60,scale=10,text~opacity=0.2,color=red]
+           at (current~page.center) {\textsc{draft}};
+      }
+    \end{tikzpicture}
+
+    \group_end:
+  } {
+    \int_gincr:N\g_sdaps_page_int
+  }
+}
+
+
+\bool_new:N \g__sdaps_last_page_bool
+
+\cs_new_protected_nopar:Nn \sdaps_begin: {
+  \bool_gset:Nn \g__sdaps_last_page_bool \c_false_bool
+
+  % TODO: We really want to make sure nobody modifies the values after \sdaps_begin:
+  \sdaps_info_write:x{Duplex=\bool_if:NTF \g_sdaps_twoside_bool {true} {false}}
+  \sdaps_info_write:x{PrintQuestionnaireId=\bool_if:NTF \g_sdaps_print_questionnaire_id_bool {1} {0}}
+  \sdaps_info_write:x{
+    PageSize=\the\paperwidth, \the\paperheight
+  }
+  \sdaps_info_write:x{Style=\g_sdaps_style_tl}
+  \sdaps_info_write:x{CheckMode=\g_sdaps_checkmode_tl}
+  \sdaps_info_write:x{GlobalID=\g_sdaps_global_id_tl}
+  \sdaps_info_write:x{GlobalIDLabel=\g_sdaps_global_id_label_tl}
+
+  \int_gset:Nn \g_sdaps_page_int { 0 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_end: {
+  % Note that using \sdaps_info_write_x:n may not work in some cases.
+  % For this reason we write the out the Pages counter after each page,
+  % which is fine to do.
+  % Note that this means that the below "hack" to make onesided documents
+  % work even in twoside (duplex) mode may not always work either.
+
+  \bool_gset:Nn \g__sdaps_last_page_bool \c_true_bool
+}
+
+
+\endinput
+%%
+%% End of file `sdapsbase.sty'.


Property changes on: trunk/Master/texmf-dist/tex/latex/sdaps/sdapsbase.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/latex/sdaps/sdapsclassic.cls
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/sdapsclassic.cls	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/sdapsclassic.cls	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,958 @@
+%%
+%% This is file `sdapsclassic.cls',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% sdapsclassic.dtx  (with options: `class')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%% 
+%% 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.
+%% 
+\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\ProvidesClass{sdapsclassic}
+    [2015/08/02 v0.1 Initial version of SDAPS classic class]
+\RequirePackage{expl3}[2011/08/23]
+\ExplSyntaxOn
+
+\RequirePackage{sdapsbase}
+\RequirePackage{sdapslayout}
+
+
+\RequirePackage{verbatim}
+
+
+\RequirePackage{scrkbase}
+
+
+\DeclareOption{disable_recognition}{\bool_gset_false:N\g_sdaps_recognition_bool}
+
+\DeclareOption{no_print_questionnaire_id}{\bool_gset_false:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}}}
+\DeclareOption{print_questionnaire_id}{\bool_gset_true:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{NONE}}}
+
+\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrartcl}}
+
+\newif\if at sdaps@draft\@sdaps at drafttrue
+\DeclareOption{final}{\@sdaps at draftfalse}
+
+\tl_gset:Nn\g_sdaps_style_tl{qr}%
+
+\DefineFamily{SDAPS}
+\DefineFamilyMember{SDAPS}
+\DefineFamilyKey{SDAPS}{sdaps_style}[qr]%
+{%
+  \KOMA at set@ncmdkey{sdaps_style}{@tempa}{%
+    {code128}{0},%
+    {custom}{1},%
+    {qr}{2}%
+  }{#1}%
+  \ifcase \@tempa\relax
+    \tl_gset:Nn\g_sdaps_style_tl{code128}%
+  \or
+    \tl_gset:Nn\g_sdaps_style_tl{custom}%
+  \or
+    \tl_gset:Nn\g_sdaps_style_tl{qr}%
+  \fi
+}
+
+\DefineFamilyKey{SDAPS}{twoside_barcode}[both]%
+{%
+  \KOMA at set@ncmdkey{twoside_barcode}{@tempa}{%
+    {both}{0},%
+    {front}{1},%
+    {back}{2}%
+  }{#1}%
+  \ifcase \@tempa\relax
+    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{both}%
+  \or
+    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{front}%
+  \or
+    \tl_gset:Nn\g_sdaps_twoside_barcode_tl{back}%
+  \fi
+}
+
+\DefineFamilyKey{SDAPS}{checkmode}[checkcorrect]%
+{%
+  \KOMA at set@ncmdkey{checkmode}{@tempa}{%
+    {checkcorrect}{0},%
+    {check}{1},%
+    {fill}{2}%
+  }{#1}%
+  \ifcase \@tempa\relax
+    \tl_gset:Nn\g_sdaps_checkmode_tl{checkcorrect}%
+  \or
+    \tl_gset:Nn\g_sdaps_checkmode_tl{check}%
+  \or
+    \tl_gset:Nn\g_sdaps_checkmode_tl{fill}%
+  \fi
+}
+
+\DefineFamilyKey{SDAPS}{globalid}[]
+{
+  \tl_gset:Nn \g_sdaps_global_id_tl { #1 }
+}
+
+\DefineFamilyKey{SDAPS}{globalidlabel}[]
+{
+  \tl_gset:Nn \g_sdaps_global_id_label_tl { #1 }
+}
+
+\PassOptionsToClass{headings=small}{scrartcl}
+\PassOptionsToClass{twoside}{scrartcl}
+
+\seq_new:N \g_sdaps_questionnaire_ids_seq
+\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}}
+
+\FamilyProcessOptions{SDAPS}\relax
+
+\LoadClass{scrartcl}
+
+\RequirePackage{geometry}
+\geometry{top=12mm}
+\geometry{bottom=14mm}
+\geometry{hmargin=12mm}
+\geometry{includeheadfoot}
+\geometry{headheight=\baselineskip}
+\geometry{headsep=\baselineskip}
+\geometry{footskip=12mm}
+
+\RequirePackage{ifthen}
+
+\RequirePackage[T1]{fontenc}
+
+\RequirePackage{color}
+
+\RequirePackage{amssymb}
+
+\RequirePackage{refcount}
+
+\RequirePackage{lastpage}
+
+\RequirePackage{environ}
+
+\usepackage{scrlayer-scrpage}
+\clearpairofpagestyles
+\chead*{\@title}
+\cfoot*{\sdapspagemark}
+
+\RequirePackage{url}
+\RequirePackage{hyperref}
+\hypersetup{%
+  breaklinks,%
+  baseurl       = http://,%
+  pdfborder     = 0 0 0,%
+  pdfpagemode   = UseNone,%
+  pdfcreator    = \LaTeX{} with `sdaps' class,%
+  pdfproducer   = \LaTeX{}
+}
+
+\RequirePackage{graphicx}
+
+\RequirePackage{sectsty}
+
+\RequirePackage{tabularx}
+
+\RequirePackage{babel}
+
+\RequirePackage{translator}
+\usedictionary{translator-sdaps-dictionary}
+
+\InputIfFileExists{sdaps.opt}{}{}
+
+
+\setlength\lineskip{1\p@}
+\setlength\normallineskip{1\p@}
+\renewcommand\baselinestretch{}
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{1.0ex \@plus 1.5ex \@minus -0.25ex}
+\setlength\columnsep{10\p@}
+\setlength\columnseprule{0\p@}
+\renewcommand\section{\@startsection {section}{1}{\z@}%
+      {-\parskip}%
+      {\parskip}%
+      {\normalfont\Large\bfseries\SS at sectfont}}
+\renewcommand\subsection{
+  \@startsection{subsection}{2}{\z@}%
+      {0.5\parskip}%
+      {0.25\parskip}% These are deleted again for questions
+      {}%
+}
+\pagestyle{scrheadings}
+\pagenumbering{arabic}
+\raggedbottom
+\onecolumn
+
+
+\newkomafont{questionfont}{}
+\newkomafont{choicefont}{}
+\newkomafont{singlemarkquestionfont}{\usekomafont{questionfont}}
+\newkomafont{singlemarkchoicefont}{\usekomafont{choicefont}}
+\newkomafont{marklinequestionfont}{\usekomafont{questionfont}}
+\newkomafont{marklinechoicefont}{\usekomafont{choicefont}}
+\newkomafont{choiceitemfont}{\usekomafont{choicefont}}
+\newkomafont{choicegroupchoicefont}{\usekomafont{questionfont}}
+\newkomafont{choicegrouplinefont}{\usekomafont{choicefont}}
+
+
+\providecommand{\addinfo}[2]{
+  \sdaps_info_write:x{\unexpanded{#1}=\unexpanded{#2}}
+}
+
+\newcommand\qid{\tl_use:N \g__sdaps_questionnaire_id_tl}
+
+\def\question#1{%
+  \tl_if_empty:nTF{#1}{
+    \refstepcounter{subsection}%
+    \par%
+  } {
+    % #1 is nonempty
+    \subsection{\usekomafont{questionfont}\strut\ignorespaces#1}%
+    % This is extra spacing after the subsection is removed. By doing this we
+    % get exactly one \parskip
+    \nobreak%
+    \vspace{-0.25\parskip}%
+    \nobreak%
+  }
+}
+
+\newenvironment{info}{%
+  \group_begin:
+    {
+      \par
+      % Prevent top skip glue from being inserted at the start of the page
+      \topskip=0pt
+      \noindent\hrule height 1pt%
+      \nobreak
+      \vspace{-\parskip}
+      \nobreak
+      \vspace{0.5ex}
+      \nobreak
+    }
+}{%
+    \par
+    \nobreak
+    \vspace{-\parskip}
+    \nobreak
+    \vspace{0.5ex}
+    \nobreak
+    \noindent\hrule height 1pt
+  \group_end:
+}
+
+\definecolor{sectionbgcolor}{gray}{0.8}
+\definecolor{sectionfgcolor}{gray}{0.0}
+
+\newcommand{\sectbox}[1]{%
+ \noindent\protect\colorbox{sectionbgcolor}{%
+   \@tempdima=\hsize
+   \advance\@tempdima by-2\fboxsep
+   \protect\parbox{\@tempdima}{%
+     \smallskip
+     \raggedright % extra commands here
+     \color{sectionfgcolor}\usekomafont{section}{#1} \smallskip
+    }%
+  }%
+}
+
+\sectionfont{\sectbox}
+
+\setkomafont{disposition}{\normalfont}
+\addtokomafont{section}{\bfseries\sffamily}
+
+\def\smallskip{\vspace\smallskipamount}
+\def\medskip{\vspace\medskipamount}
+\def\bigskip{\vspace\bigskipamount}
+\newskip\smallskipamount \smallskipamount=3pt  plus 1pt minus 1pt
+\newskip\medskipamount   \medskipamount  =6pt  plus 2pt minus 2pt
+\newskip\bigskipamount   \bigskipamount  =12pt plus 4pt minus 4pt
+
+\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { nVVnn }
+\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { VVVnn }
+\cs_generate_variant:Nn \int_set:Nn { NV }
+
+
+\bool_new:N \g__sdaps_classic_have_section
+\bool_gset_false:N \g__sdaps_classic_have_section
+\cs_new_eq:NN\_sdapsclassic_origsection\section
+\renewcommand{\section}[1]{
+  \bool_if:NT \g__sdaps_classic_have_section {
+    \sdaps_qobject_end:n { section }
+  }
+
+  \_sdapsclassic_origsection{#1}
+  \bool_gset_true:N \g__sdaps_classic_have_section
+  \sdaps_qobject_begin:nnn { section }{ Head }{ #1 }
+}
+
+\cs_new_protected_nopar:Nn \sdaps_classic_ensure_section: {
+  \bool_if:NF \g__sdaps_classic_have_section {
+    % This is a bad hack to make the numbering start with zero
+    \int_gdecr:N \g__sdaps_object_id_int
+    \bool_gset_true:N \g__sdaps_classic_have_section
+    \sdaps_qobject_begin:nnn { section }{ Head }{ }
+      \sdaps_context_hook_end:n { \bool_gset_false:N \g__sdaps_classic_have_section }
+  }
+}
+
+\tl_new:N \l_sdaps_classic_textbox_var_tl
+\tl_new:N \l_sdaps_classic_textbox_text_tl
+\keys_define:nn { sdapsclassic / textbox }
+{
+  var        .tl_set:N   = \l_sdaps_classic_textbox_var_tl,
+  text       .tl_set:N   = \l_sdaps_classic_textbox_text_tl,
+}
+
+\providecommand{\textbox}{\@ifstar
+    {\bool_set_false:N \l_tmpa_bool \_sdaps_classic_textbox }
+    {\bool_set_true:N \l_tmpa_bool \_sdaps_classic_textbox }
+}
+
+\providecommand{\_sdaps_classic_textbox}[3][]
+{
+  \keys_set:nn { sdapsclassic / textbox } { #1 }
+
+  \sdaps_classic_ensure_section:
+  \question{#3}
+
+  \tl_if_empty:NTF \l_sdaps_classic_textbox_text_tl {
+    \sdaps_qobject_begin:nnn { textbox } { Text } { #3 }
+  } {
+    \sdaps_qobject_begin:nnV { textbox } { Text } \l_sdaps_classic_textbox_text_tl
+  }
+
+  \bool_if:NTF \l_tmpa_bool {
+    \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 1 }
+  }{
+    \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 0 }
+  }
+
+  \sdaps_qobject_end:n { textbox }
+}
+
+\newcounter{markcheckboxcount}
+\setcounter{markcheckboxcount}{5}
+
+\tl_new:N \l_sdaps_singlemark_var_tl
+\int_new:N \l_sdaps_singlemark_count_int
+
+\keys_define:nn { sdaps / singlemark }
+{
+  var        .tl_set:N   = \l_sdaps_singlemark_var_tl,
+
+  % 0 is equivalent to using the markcheckboxcount counter
+  count      .int_set:N  = \l_sdaps_singlemark_count_int,
+  count      .initial:n  = 0,
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_prepare:NN
+{
+  \group_begin:
+    \sdaps_context_begin_local:
+    \sdaps_context_disable_writing:
+    \bool_if:NT #1 {
+      \sdaps_context_append:nn { singlechoice } { draw_check=true }
+      \sdaps_context_append:nn { multichoice } { draw_check=true }
+    }
+    \bool_if:NT #2 {
+      \sdaps_context_append:nn { singlechoice } { fill=black }
+      \sdaps_context_append:nn { multichoice } { fill=black }
+    }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_single:
+{
+    \sdaps_checkbox_set_type:n { singlechoice }
+    \sdaps_checkbox:nn { } { }
+  \group_end:
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_multi:
+{
+    \sdaps_checkbox_set_type:n { multichoice }
+    \sdaps_checkbox:nn { } { }
+  \group_end:
+  \ignorespaces
+}
+
+\providecommand{\checkbox}{
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_false_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+\providecommand{\checkedbox}{%
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_false_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+\providecommand{\filledbox}{%
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_true_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+\providecommand{\correctedbox}{%
+  \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_true_bool
+  \@ifstar
+    \_sdaps_classic_dummy_checkbox_single:
+    \_sdaps_classic_dummy_checkbox_multi:
+}
+
+\providecommand*{\singlemark}[4][]{%
+  \sdaps_classic_ensure_section:
+
+  \group_begin:
+
+  \keys_set:nn { sdaps / singlemark } { #1 }
+
+  \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } {
+    \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount }
+  }
+
+  \question{#2}%
+
+  \sdaps_qobject_begin:nnn { singlemark } { range } { #2 }
+
+  \sdaps_checkbox_set_type:n { singlechoice }
+
+  \tl_if_empty:NF \l_sdaps_singlemark_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl
+  }
+
+  \sdaps_range:nnn{lower}{0}{#3}
+  \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4}
+
+  \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}X}
+    {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} &
+      \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & }
+    {\usekomafont{singlemarkchoicefont}#4\hfill}\\%
+  \end{tabularx}%
+
+  \sdaps_qobject_end:n { singlemark }
+  \group_end:
+}
+
+\providecommand*{\singlemarkother}[5][]{%
+  \sdaps_classic_ensure_section:
+
+  \group_begin:
+
+  \keys_set:nn { sdaps / singlemark } { #1 }
+
+  \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } {
+    \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount }
+  }
+
+  \question{#2}%
+
+  \sdaps_qobject_begin:nnn { singlemark } { range } { #2 }
+
+  \sdaps_checkbox_set_type:n { singlechoice }
+
+  \tl_if_empty:NF \l_sdaps_singlemark_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl
+  }
+
+  \sdaps_range:nnn{lower}{0}{#3}
+  \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4}
+  \sdaps_answer:n{#5}
+
+  \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}XX}
+    {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} &
+      \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & }
+    {\usekomafont{singlemarkchoicefont}#4} &
+    {\usekomafont{singlemarkchoicefont}\sdaps_checkbox:nn { _0 } { 0 } {} ~ #5\hfill}\\%
+  \end{tabularx}%
+
+  \sdaps_qobject_end:n { singlemark }
+  \group_end:
+}
+
+\dim_new:N \l__sdaps_classic_choiceitem_pad_dim
+\int_new:N \l__sdaps_classic_choiceitem_cols_int
+\int_new:N \l__sdaps_classic_choiceitem_col_int
+\coffin_new:N \l__sdaps_classic_choicequestion_coffin
+
+\msg_new:nnn { sdapsclassic } { choicequestion_wrong_mode } { Mode~should~always~be~vertical~inside~a~choicequestion.\\ This~likely~means~that~the~choicequestion~contains~content~other~than~one~of~the~permissable~macros. }
+
+\msg_new:nnn { sdapsclassic } { choicequestion_no_text } { Textboxes~cannot~be~used~in~singlechoice~questions.\\
+It~is~currently~not~supported~to~use~text~answers~in~single~choice~questions.~Until~support~is~added~
+you~can~get~similar~results~by~temporarily~changing~the~checkbox~style. }
+
+\msg_new:nnn { sdapsclassic } { choicequestion_unknown_key } { The~key~'#1'~is~unknown.\\If~you~are~migrating~from~the~old~class~then~you~need~to~add~'cols='~to~specify~the~number~of~columns. }
+
+\tl_new:N \l_sdaps_choicquestion_type_tl
+\tl_new:N \l_sdaps_choicequestion_var_tl
+\tl_new:N \l_sdaps_choicequestion_text_tl
+\int_new:N \l_sdaps_choicequestion_cols_int
+
+\keys_define:nn { sdaps / choicequestion }
+{
+  var        .tl_set:N   = \l_sdaps_choicequestion_var_tl,
+  text       .tl_set:N   = \l_sdaps_choicequestion_text_tl,
+  colsep     .dim_set:N  = \l_sdaps_choicequestion_colsep_dim,
+  colsep     .initial:n  = 6pt,
+  rowsep     .dim_set:N  = \l_sdaps_choicequestion_rowsep_dim,
+  rowsep     .initial:n  = 0pt,
+  cols       .int_set:N  = \l_sdaps_choicequestion_cols_int,
+  cols       .initial:n  = 3,
+
+  type       .choices:nn  = { multichoice, singlechoice } { \tl_set:Nx \l_sdaps_choicquestion_type_tl { \l_keys_choice_tl } },
+  type       .initial:n   = { multichoice },
+
+  singlechoice .meta:n    = { type=singlechoice },
+  multichoice  .meta:n    = { type=multichoice },
+
+  unknown .code:n = \msg_error:nnV { sdapsclassic } { choicequestion_unknown_key } \l_keys_key_tl
+}
+
+\tl_new:N \l_sdaps_choicequestion_choice_var_tl
+\tl_new:N \l_sdaps_choicequestion_choice_val_tl
+\tl_new:N \l_sdaps_choicequestion_choice_text_tl
+
+\keys_define:nn { sdaps / choicequestion / choice }
+{
+  var        .tl_set:N   = \l_sdaps_choicequestion_choice_var_tl,
+  val        .tl_set:N   = \l_sdaps_choicequestion_choice_val_tl,
+  text       .tl_set:N   = \l_sdaps_choicequestion_choice_text_tl,
+}
+
+\newenvironment{choicequestion}[2][]{
+  \group_begin:
+
+  \sdaps_classic_ensure_section:
+
+  \tl_clear:N \l_sdaps_choicequestion_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_text_tl
+
+  \keys_set:nn { sdaps / choicequestion } { #1 }
+
+  \question{#2}%
+
+  % Setup the context
+  \tl_if_eq:VnTF \l_sdaps_choicquestion_type_tl { multichoice } {
+    \tl_set:Nn \l_tmpa_tl { Choice }
+  } {
+    \tl_set:Nn \l_tmpa_tl { Option }
+  }
+  \tl_if_empty:NTF \l_sdaps_choicequestion_text_tl {
+    \sdaps_qobject_begin:nVn { choicequestion } \l_tmpa_tl { #2 }
+  } {
+    \sdaps_qobject_begin:nVV { choicequestion } \l_tmpa_tl \l_sdaps_choicequestion_text_tl
+  }
+
+  \sdaps_checkbox_set_type:V \l_sdaps_choicquestion_type_tl
+
+  \tl_if_empty:NF \l_sdaps_choicequestion_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_choicequestion_var_tl
+  }
+
+  \dim_set:Nn \l__sdaps_classic_choiceitem_pad_dim { 1ex }
+
+  \int_set:NV \l__sdaps_classic_choiceitem_cols_int \l_sdaps_choicequestion_cols_int
+  \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 }
+  \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
+
+  % We have to be in vertical mode at this point.
+  \if_mode_vertical:
+    % Nothing
+  \else:
+    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
+  \fi:
+
+  % It is important to look like a paragraph, otherwise latex thinks there is
+  % nothing between the two question sections and doesn't insert a voluntary
+  % page break.
+  \the\everypar
+
+  % Paragraph like spacing
+  \vspace{\parskip}
+
+  \def\choicequestion_clubpenalty{\penalty\clubpenalty\def\choicequestion_clubpenalty{\relax}\def\choicequestion_clubpenalty{\penalty\widowpenalty}}
+  \def\choicequestion_widowpenalty{}
+}{
+  \if_mode_vertical:
+    % Nothing
+  \else:
+    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
+  \fi:
+
+  \choicequestion_widowpenalty
+  \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin }
+  \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
+  \sdaps_qobject_end:n { choicequestion }
+
+  \group_end:
+  \vspace{\parskip}
+}
+
+\newenvironment{optionquestion}[2][]{
+  \choicequestion[singlechoice,#1]{#2}
+} {
+  \endchoicequestion
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_line_shipout_add:Nn
+{
+  % We have to be in vertical mode at this point.
+  \if_mode_vertical:
+    % Nothing
+  \else:
+    \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode }
+  \fi:
+
+  % Is linewidth the right thing here?
+  \int_compare:nT { \l__sdaps_classic_choiceitem_col_int + #2 > \l__sdaps_classic_choiceitem_cols_int } {
+    % We can only typeset a coffin in vertical mode if we use the box function
+    \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin }
+    \skip_vertical:N \l_sdaps_choicequestion_rowsep_dim
+    \coffin_clear:N \l__sdaps_classic_choicequestion_coffin
+    \choicequestion_clubpenalty
+    \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 }
+  }
+
+  \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+  \dim_set:Nn \l_tmpa_dim { \l__sdaps_classic_choiceitem_col_int \l_tmpa_dim }
+
+  \coffin_join:NnnNnnnn \l__sdaps_classic_choicequestion_coffin { l } { H } #1 { l } { H } { \l_tmpa_dim } { 0pt }
+  \int_add:Nn \l__sdaps_classic_choiceitem_col_int { #2 }
+}
+
+\providecommand*{\choiceitem}[2][]{%
+  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl
+
+  \keys_set:nn { sdaps / choicequestion / choice } { #1 }
+
+  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
+   \sdaps_answer:n { #2 }
+  } {
+   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
+  }
+
+  \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\  }
+    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
+    \hskip \l_sdaps_choicequestion_colsep_dim
+    \hbox_unpack_clear:N \l_tmpa_box
+    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}}
+      \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #2 }
+    \end{minipage}
+  }
+
+  \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { 1 }
+  \ignorespaces
+}
+
+\providecommand*{\choicemulticolitem}[3][]{ %
+  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl
+
+  \keys_set:nn { sdaps / choicequestion / choice } { #1 }
+
+  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
+   \sdaps_answer:n { #3 }
+  } {
+   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
+  }
+
+  \dim_set:Nn \l_tmpa_dim { #2\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #2 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\  }
+    \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box }
+    \hskip \l_sdaps_choicequestion_colsep_dim
+    \hbox_unpack_clear:N \l_tmpa_box
+    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}}
+      \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #3 }
+    \end{minipage}
+  }
+
+  \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { #2 }
+  \ignorespaces
+}
+
+\providecommand{\choiceitemtext}[4][]{%
+  \tl_clear:N \l_sdaps_choicequestion_choice_var_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_val_tl
+  \tl_clear:N \l_sdaps_choicequestion_choice_text_tl
+
+  \keys_set:nn { sdaps / choicequestion / choice } { #1 }
+  % TODO: Warn if val has been set here
+
+  % If not multichoice then warn
+  \tl_if_eq:VnF \l_sdaps_choicquestion_type_tl { multichoice } {
+    \msg_error:nn { sdapsclassic } { choicequestion_no_text }
+  }
+
+  \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl {
+   \sdaps_answer:n { #4 }
+  } {
+   \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl
+  }
+
+  \dim_set:Nn \l_tmpa_dim { #3\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #3 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int }
+
+  \hcoffin_set:Nn \l_tmpa_coffin {
+    \hskip \l_sdaps_choicequestion_colsep_dim
+    \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - 2\l_sdaps_choicequestion_colsep_dim}}
+      \usekomafont{choiceitemfont}
+      \dim_set:Nn \l_tmpa_dim { #2 }
+      \dim_set:Nn \l_tmpb_dim { #2 }
+      \dim_set:Nn \l_tmpa_dim { 0.5 \l_tmpa_dim - 0.8ex }
+      \dim_set:Nn \l_tmpb_dim { 0.5 \l_tmpb_dim + 0.8ex }
+      \strut\ignorespaces \tl_trim_spaces:n { #4 } ~
+      \sdaps_textbox_hstretch:VVVnn \l_sdaps_choicequestion_choice_var_tl \l_tmpa_dim \l_tmpb_dim { 0pt } { 1 }
+    \end{minipage}
+  }
+
+  \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { #3 }
+  \ignorespaces
+}
+
+
+\iow_new:N \l__sdaps_questionnaire_iow
+\bool_new:N \l__sdaps_questionnaire_parse_direct_bool
+
+\bool_new:N \l__sdaps_questionnaire_info_bool
+
+\keys_define:nn { sdaps / questionnaire }
+{
+  info         .bool_set:N  = \l__sdaps_questionnaire_info_bool,
+  info         .default:n   = true,
+  info         .initial:n   = true,
+
+  noinfo       .meta:n  = { info=false },
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_classic_show_info:
+{
+  \bool_if:NT \l__sdaps_questionnaire_info_bool {
+    \begin{info}
+      \translate{infotext} \\[1ex]
+      \tl_if_eq:VnTF \g_sdaps_checkmode_tl { checkcorrect } {
+        \begin{tabularx}{\textwidth}{lXllll}
+          \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} & \hspace{1em} \correctedbox {} & \translate{info-corrected} \\
+          \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} & \hspace{1em} \correctedbox* {} & \translate{info-corrected} \\
+        \end{tabularx}
+      } {
+        \tl_if_eq:VnTF \g_sdaps_checkmode_tl { check } {
+          \begin{tabularx}{\textwidth}{lXll}
+            \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} \\
+            \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} \\
+          \end{tabularx}
+        } {
+          \tl_if_eq:VnTF \g_sdaps_checkmode_tl { fill } {
+            \begin{tabularx}{\textwidth}{lXll}
+              \checkbox & \strut \translate{info-multi} & \hspace{2em} \filledbox {} & \translate{info-fill} \\
+              \checkbox* & \strut \translate{info-single} & \hspace{2em} \filledbox* {} & \translate{info-fill} \\
+            \end{tabularx}
+          } {
+            \PackageError{sdaps}{Sorry, there is no help text for the checkmode you have choosen right now! Please pass the noinfo optional argument to the questionnaire environment!}\@ehb %
+          }
+        }
+      }
+    \end{info}
+  }
+  \ignorespaces
+}
+
+\newenvironment { questionnaire } [ 1 ] []
+{
+  \keys_set:nn { sdaps / questionnaire } { #1 }
+
+  \hypersetup{
+    pdfauthor     = \@author,
+    pdftitle      = \@title,
+    pdfsubject    = sdaps questionnaire \@title,
+    pdfkeywords   = sdaps questionnaire \@title
+  }
+
+  % If we only have one questionnaire ID, then parse the environment directly,
+  % otherwise write it into a temporary file and input it multiple times.
+  \group_begin:
+
+  \if at twoside
+    \bool_gset_true:N \g_sdaps_twoside_bool
+  \else
+    \bool_gset_false:N \g_sdaps_twoside_bool
+  \fi
+
+  % Enable all metadata writing by default
+  \sdaps_context_enable_writing:
+
+  \sdaps_info_write:x{Author=\exp_not:o{\@author}}
+  \sdaps_info_write:x{Title=\exp_not:o{\@title}}
+
+  \int_compare:nTF { \seq_count:N \g_sdaps_questionnaire_ids_seq <= 1 } {
+    \bool_set_true:N \l__sdaps_questionnaire_parse_direct_bool
+
+    % Set the questionnaire ID
+    \seq_gpop_left:NN \g_sdaps_questionnaire_ids_seq \l_tmpa_tl
+    \sdaps_set_questionnaire_id:V \l_tmpa_tl
+
+    % And, begin the questionnaire
+    \sdaps_begin:
+      \_sdaps_classic_show_info:
+  } {
+    \bool_set_false:N \l__sdaps_questionnaire_parse_direct_bool
+
+    % Write content into a file, see "verbatim" documentation for more information.
+    % TODO: Allow multiple temporary files by postfixing with integer?
+    \iow_open:Nn \l__sdaps_questionnaire_iow { \c_sys_jobname_str . questionnaire }
+    \cs_set_eq:NN\do\@makeother\dospecials
+    \catcode`\^^M\active
+    \def\verbatim at processline{
+      \iow_now:Nx \l__sdaps_questionnaire_iow {\the\verbatim at line}
+    }
+    \verbatim@
+  }
+} {
+  \bool_if:NTF \l__sdaps_questionnaire_parse_direct_bool {
+        % Just end everything, nothing else to do.
+        % Make sure we always end the current paragraph.
+        \par
+      \sdaps_end:
+
+    \group_end:
+  } {
+       % We are done inputting the questionnaire
+       \iow_close:N \l__sdaps_questionnaire_iow
+    \group_end:
+
+    % Now cycle through all IDS and output it again.
+    \group_begin:
+      \seq_map_inline:Nn \g_sdaps_questionnaire_ids_seq {
+        \sdaps_set_questionnaire_id:n { ##1 }
+
+        % Reset a lot of global LaTeX counters
+        \@ifundefined{c at page}{}{\setcounter{page}{1}}
+        \@ifundefined{c at part}{}{\setcounter{part}{0}}
+        \@ifundefined{c at chapter}{}{\setcounter{chapter}{0}}
+        \@ifundefined{c at paragraph}{}{\setcounter{paragraph}{0}}
+        \@ifundefined{c at subparagraph}{}{\setcounter{subparagraph}{0}}
+        \@ifundefined{c at section}{}{\setcounter{section}{0}}
+        \@ifundefined{c at subsection}{}{\setcounter{subsection}{0}}
+        \@ifundefined{c at subsubsection}{}{\setcounter{subsubsection}{0}}
+        \@ifundefined{c at equation}{}{\setcounter{equation}{0}}
+        \@ifundefined{c at figure}{}{\setcounter{figure}{0}}
+        \@ifundefined{c at table}{}{\setcounter{table}{0}}
+
+        \sdaps_begin:
+          \_sdaps_classic_show_info:
+          \input{ \c_sys_jobname_str . questionnaire }
+          % Make sure we always end the current paragraph.
+          \par
+        \sdaps_end:
+        \newpage
+
+        % Close the output file now (after the page has been shipped out)
+        % XXX: This is a bit of a hack, as disabling metadata writing has side effects
+        \iow_close:N \g_sdaps_infofile_iow
+
+      }
+    \group_end:
+  }
+}
+
+\def\sdapspagemark{
+  \sdaps_page_end:
+}
+
+
+\newenvironment { choicegroup } [ 2 ] []
+{
+  \group_begin:
+
+    \question{#2}
+
+    % Undefine the question (and choice) commands in local scope so that they
+    % can be redefined by choicearray without any issues.
+    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
+    \cs_set_eq:NN\question\tex_undefined:D
+    \cs_set_eq:NN\choice\tex_undefined:D
+
+    \begin{choicearray}[#1]{#2}
+      % XXX: This is a bit of a hack, set in global scope because the choicearray
+      %      environment does evil things with scopes.
+      \cs_gset_eq:NN\groupaddchoice\choice
+      \cs_gset_eq:NN\choiceline\question
+}
+{
+      \cs_gset_eq:NN\groupaddchoice\undefined
+      \cs_gset_eq:NN\choiceline\undefined
+
+    \end{choicearray}
+  \group_end:
+}
+
+\newenvironment { optiongroup } [ 2 ] []
+{
+  \group_begin:
+
+    \question{#2}
+
+    % Undefine the question (and choice) commands in local scope so that they
+    % can be redefined by optionarray without any issues.
+    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
+    \cs_set_eq:NN\question\tex_undefined:D
+    \cs_set_eq:NN\choice\tex_undefined:D
+
+    \begin{optionarray}[#1]{#2}
+      % XXX: This is a bit of a hack, set in global scope because the optionarray
+      %      environment does evil things with scopes.
+      \cs_gset_eq:NN\groupaddchoice\choice
+      \cs_gset_eq:NN\optionline\question
+}
+{
+      \cs_gset_eq:NN\groupaddoption\undefined
+      \cs_gset_eq:NN\optionline\undefined
+
+    \end{optionarray}
+  \group_end:
+}
+
+\newenvironment { markgroup } [ 2 ] []
+{
+  \group_begin:
+
+    \question{#2}
+
+    % Undefine the question (and choice) commands in local scope so that they
+    % can be redefined by choicearray without any issues.
+    % Note that \cs_undefine:N works in global scope, so we cannot use it here.
+    \cs_set_eq:NN\range\tex_undefined:D
+
+    \begin{rangearray}[#1]{#2}
+      % XXX: This is a bit of a hack, set in global scope because the choicearray
+      %      environment does evil things with scopes.
+      \cs_gset_eq:NN\markline\range
+}
+{
+      \cs_gset_eq:NN\markline\undefined
+
+    \end{rangearray}
+  \group_end:
+}
+
+\ExplSyntaxOff
+
+
+\endinput
+%%
+%% End of file `sdapsclassic.cls'.


Property changes on: trunk/Master/texmf-dist/tex/latex/sdaps/sdapsclassic.cls
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/latex/sdaps/sdapslayout.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/sdapslayout.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/sdapslayout.sty	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,669 @@
+%%
+%% This is file `sdapslayout.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% sdapslayout.dtx  (with options: `package')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%% 
+%% 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.
+%% 
+\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\ProvidesPackage{sdapslayout}
+    [2015/04/10 v0.1 Initial version of SDAPS layout package]
+\RequirePackage{expl3}[2011/08/23]
+\ExplSyntaxOn
+\ExplSyntaxOff
+
+\RequirePackage{sdapsbase}
+\RequirePackage{sdapsarray}
+\RequirePackage{xparse}
+
+\ExplSyntaxOn
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\tl_new:N \l_sdaps_choicearray_qobject_type_tl
+\bool_new:N \l_sdaps_choicearray_horizontal_bool
+\tl_new:N \l_sdaps_choicearray_var_tl
+\tl_new:N \l_sdaps_choicearray_text_tl
+\tl_new:N \l_sdaps_choicearray_layouter_tl
+\tl_new:N \l_sdaps_choicearray_align_tl
+\tl_new:N \l_sdaps_choicearray_extra_tl
+\tl_new:N \l_sdaps_choicearray_type_tl
+\tl_new:N \l_sdaps_choice_var_tl
+\tl_new:N \l_sdaps_choice_val_tl
+\tl_new:N \l_sdaps_choice_text_tl
+\tl_new:N \l_sdaps_question_var_tl
+\tl_new:N \l_sdaps_question_text_tl
+\clist_new:N \l_sdaps_question_range_clist
+\int_new:N \g_sdaps_choices_box_int
+\seq_new:N \g_sdaps_choices_filter_seq
+\seq_new:N \g_sdaps_choices_cell_seq
+\seq_new:N \g_sdaps_choices_text_seq
+
+\keys_define:nn { sdaps / choicearray }
+{
+  horizontal .bool_set:N = \l_sdaps_choicearray_horizontal_bool,
+  horizontal .default:n  = true,
+  horizontal .initial:n  = true,
+  vertical   .bool_set_inverse:N = \l_sdaps_choicearray_horizontal_bool,
+  vertical   .default:n  = true,
+  var        .tl_set:N   = \l_sdaps_choicearray_var_tl,
+  text       .tl_set:N   = \l_sdaps_choicearray_text_tl,
+  layouter   .tl_set:N   = \l_sdaps_choicearray_layouter_tl,
+  layouter   .initial:n  = default,
+  align      .tl_set:N   = \l_sdaps_choicearray_align_tl,
+  align      .initial:n  = { },
+
+  type       .choices:nn  = { multichoice, singlechoice } { \tl_set:Nx \l_sdaps_choicearray_type_tl { \l_keys_choice_tl } },
+  type       .initial:n   = { multichoice },
+
+  singlechoice .meta:n   = { type=singlechoice },
+  multichoice  .meta:n   = { type=multichoice },
+
+  noalign    .meta:n  = { align= },
+}
+
+\keys_define:nn { sdaps / choicearray / choice }
+{
+  var        .tl_set:N   = \l_sdaps_choice_var_tl,
+  val        .tl_set:N   = \l_sdaps_choice_val_tl,
+  text       .tl_set:N   = \l_sdaps_choice_text_tl,
+}
+
+\keys_define:nn { sdaps / choicearray / question }
+{
+  var        .tl_set:N   = \l_sdaps_question_var_tl,
+  text       .tl_set:N   = \l_sdaps_question_text_tl,
+
+  range      .clist_set:N   = \l_sdaps_question_range_clist,
+  range      .initial:n = {...}
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_preprocess:nn #1#2
+{
+  \keys_set_known:nnN { sdaps / choicearray } { #1 } \l_sdaps_choicearray_extra_tl
+
+  \tl_if_empty:NTF \l_sdaps_choicearray_text_tl {
+    \sdaps_qobject_begin:nnn { choicearray } { Head } { #2 }
+  } {
+    \sdaps_qobject_begin:nnV { choicearray } { Head } \l_sdaps_choicearray_text_tl
+  }
+
+  \tl_if_empty:NF \l_sdaps_choicearray_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_choicearray_var_tl
+  }
+
+  \sdaps_checkbox_set_type:V \l_sdaps_choicearray_type_tl
+  \tl_if_eq:VnTF \l_sdaps_choicearray_type_tl { multichoice } {
+    \tl_set:Nn \l_sdaps_choicearray_qobject_type_tl { Choice }
+  } {
+    \tl_set:Nn \l_sdaps_choicearray_qobject_type_tl { Option }
+  }
+}
+\cs_generate_variant:Nn \_sdaps_choicearray_preprocess:nn { Vn }
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_postprocess:
+{
+  \sdaps_qobject_end:n { choicearray }
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_process_choice_insert_tail_after:w {
+  \bgroup
+    \group_insert_after:N \_sdaps_choicearray_process_choice_tail:
+    \sdaps_array_nested_alignenv:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_choice_tail: {
+  \ignorespaces
+}
+
+\cs_new_nopar:Nn \_sdaps_choicearray_grab_choice:n {
+  \seq_gput_right:Nn \g_sdaps_choices_text_seq { #1 }
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_choicearray_process_choice_tail:
+}
+
+\cs_new_nopar:Npn \_sdaps_choicearray_process_choice:nw #1
+{
+  % This modifies grouping so it has to be at the start
+  \sdaps_array_alignment:
+  \leavevmode
+
+  \tl_clear:N \l_sdaps_choice_var_tl
+  \tl_clear:N \l_sdaps_choice_val_tl
+  \tl_clear:N \l_sdaps_choice_text_tl
+
+  \keys_set:nn { sdaps / choicearray / choice } { #1 }
+
+  \int_gincr:N \g_sdaps_choices_box_int
+
+  \tl_if_empty:NT \l_sdaps_choice_var_tl {
+    % Prefix with _ to force prefixing with generated variable
+    \tl_set:Nx \l_sdaps_choice_var_tl { \int_use:N \g_sdaps_choices_box_int }
+  }
+  \tl_if_empty:NT \l_sdaps_choice_val_tl {
+    \tl_set:Nx \l_sdaps_choice_val_tl { \int_use:N \g_sdaps_choices_box_int }
+  }
+
+  \tl_if_eq:VnTF \l_sdaps_choicearray_type_tl { multichoice } {
+    \seq_gput_right:NV \g_sdaps_choices_filter_seq \l_sdaps_choice_var_tl
+  } {
+    \seq_gput_right:NV \g_sdaps_choices_filter_seq \l_sdaps_choice_val_tl
+  }
+
+  \seq_gput_right:Nx \g_sdaps_choices_cell_seq { \exp_not:n { \sdaps_checkbox:nn } { _ \l_sdaps_choice_var_tl } { \l_sdaps_choice_val_tl } }
+
+  \tl_if_empty:NTF \l_sdaps_choice_text_tl {
+    % We need to leave a command in the stream that grabs the next parameter
+    % and outputs it immediately
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_grab_choice:n
+  } {
+    % Nothing else to do
+    \seq_gput_right:NV \g_sdaps_choices_text_seq { \l_sdaps_choice_text_tl }
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_process_choice_insert_tail_after:w
+  }
+  \l_tmpa_token
+}
+\cs_generate_variant:Nn \_sdaps_choicearray_process_choice:nw { Vw }
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_question_grab:n {
+  \tl_set:Nn \l_sdaps_question_text_tl { #1 }
+
+  \_sdaps_choicearray_process_question_head:
+
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+
+  \_sdaps_choicearray_process_question_tail:
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_choicearray_process_question_inline:w {
+  \_sdaps_choicearray_process_question_head:
+  \bgroup
+    \group_insert_after:N \_sdaps_choicearray_process_question_tail:
+    \sdaps_array_nested_alignenv:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_question_head: {
+  \sdaps_qobject_begin:nnV { choicearray_question } \l_sdaps_choicearray_qobject_type_tl \l_sdaps_question_text_tl
+
+  \tl_if_empty:NF \l_sdaps_question_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_question_var_tl
+  }
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_choicearray_process_question_tail: {
+  \seq_gclear:N \g_tmpa_seq
+  % l_tmpa_bool is whether we are in a run (i.e. ...)
+  \bool_set_false:N \l_tmpa_bool
+
+  \clist_gset_eq:NN \g_tmpa_clist \l_sdaps_question_range_clist
+  \clist_gpop:NN \g_tmpa_clist \l_tmpa_tl
+
+  \seq_map_inline:Nn \g_sdaps_choices_filter_seq {
+    \tl_if_eq:VnT \l_tmpa_tl { ... } {
+      \bool_set_true:N \l_tmpa_bool
+      \clist_gpop:NN \g_tmpa_clist \l_tmpa_tl
+    }
+
+    \tl_if_eq:VnTF \l_tmpa_tl { ##1 } {
+      \seq_gput_right:Nn \g_tmpa_seq { \c_true_bool }
+      \bool_set_false:N \l_tmpa_bool
+      \clist_gpop:NN \g_tmpa_clist \l_tmpa_tl
+    } {
+      % Append if we are handling a run of items and the current item is not
+      % the last one.
+      \bool_if:NTF \l_tmpa_bool {
+        \seq_gput_right:Nn \g_tmpa_seq { \c_true_bool }
+      } {
+        % Otherwise, ignore this item
+        \seq_gput_right:Nn \g_tmpa_seq { \c_false_bool }
+      }
+    }
+  }
+
+  \seq_gset_eq:NN \g_tmpb_seq \g_tmpa_seq
+
+  \seq_map_inline:Nn \g_sdaps_choices_text_seq {
+    \seq_gpop_left:NN \g_tmpa_seq \l_tmpa_tl
+    \tl_if_eq:VnT \l_tmpa_tl { \c_true_bool } {
+      \sdaps_answer:f { ##1 }
+    }
+  }
+
+  \seq_map_inline:Nn \g_sdaps_choices_cell_seq {
+    \sdaps_array_alignment:
+
+    \seq_gpop_left:NN \g_tmpb_seq \l_tmpa_tl
+    \tl_if_eq:VnT \l_tmpa_tl { \c_true_bool } {
+      ##1
+    }
+  }
+
+  \sdaps_qobject_end:n { choicearray_question }
+  \ignorespaces
+}
+
+\cs_new_nopar:Npn \_sdaps_choicearray_process_question:nw #1
+{
+  \sdaps_array_newline:
+  \leavevmode
+
+  \keys_set:nn { sdaps / choicearray / question } { #1 }
+
+  \tl_if_empty:NTF \l_sdaps_question_text_tl {
+    % We need to leave a command in the stream that grabs the next parameter,
+    % outputs it again, and finishes the question.
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_process_question_grab:n
+  } {
+    % Insert the question around the next argument
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_choicearray_process_question_inline:w
+  }
+  \l_tmpa_token
+}
+\cs_generate_variant:Nn \_sdaps_choicearray_process_question:nw { Vw }
+
+
+\tl_new:N \l_sdaps_rangearray_var_tl
+\tl_new:N \l_sdaps_rangearray_text_tl
+\tl_new:N \l_sdaps_rangearray_align_tl
+\tl_new:N \l_sdaps_rangearray_extra_tl
+\int_new:N \l_sdaps_rangearray_rangecount_int
+\bool_new:N \l_sdaps_rangearray_other_bool
+\tl_new:N \g_sdaps_question_var_tl
+\tl_new:N \g_sdaps_question_text_tl
+\tl_new:N \g_sdaps_question_lowertext_tl
+\tl_new:N \g_sdaps_question_uppertext_tl
+\tl_new:N \g_sdaps_question_othertext_tl
+
+\keys_define:nn { sdaps / rangearray }
+{
+  var        .tl_set:N   = \l_sdaps_rangearray_var_tl,
+  text       .tl_set:N   = \l_sdaps_rangearray_text_tl,
+  count      .int_set:N  = \l_sdaps_rangearray_rangecount_int,
+  count      .initial:n  = 5,
+  align      .tl_set:N   = \l_sdaps_rangearray_align_tl,
+  align      .initial:n  = { },
+  other      .bool_set:N = \l_sdaps_rangearray_other_bool,
+  other      .default:n  = true,
+  other      .initial:n  = false,
+}
+
+\keys_define:nn { sdaps / rangearray / question }
+{
+  var        .tl_gset:N   = \g_sdaps_question_var_tl,
+  text       .tl_gset:N   = \g_sdaps_question_text_tl,
+  upper      .tl_gset:N   = \g_sdaps_question_uppertext_tl,
+  lower      .tl_gset:N   = \g_sdaps_question_lowertext_tl,
+  other      .tl_gset:N   = \g_sdaps_question_othertext_tl,
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_preprocess:nn #1#2
+{
+  \keys_set_known:nnN { sdaps / rangearray } { #1 } \l_sdaps_rangearray_extra_tl
+
+  \tl_if_empty:NTF \l_sdaps_rangearray_text_tl {
+    \sdaps_qobject_begin:nnn { rangearray } { Head } { #2 }
+  } {
+    \sdaps_qobject_begin:nnV { rangearray } { Head } \l_sdaps_rangearray_text_tl
+  }
+
+  \sdaps_checkbox_set_type:n { singlechoice }
+
+  \tl_if_empty:NF \l_sdaps_rangearray_var_tl {
+    \sdaps_qobject_append_var:V \l_sdaps_rangearray_var_tl
+  }
+}
+\cs_generate_variant:Nn \_sdaps_rangearray_preprocess:nn { Vn }
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_postprocess:
+{
+  \sdaps_qobject_end:n { rangearray }
+}
+
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_question: {
+  \sdaps_array_newline:
+  % Note: This needs to be after sdaps_array_newline as the command may be
+  %       discarded otherwise (i.e. it does not make it into the output stream)
+  %       We also need to leave vmode here
+  \leavevmode
+  \sdaps_qobject_begin:nnV { rangearray_question } { Range } \g_sdaps_question_text_tl
+
+  \tl_if_empty:NF \g_sdaps_question_var_tl {
+    \sdaps_qobject_append_var:V \g_sdaps_question_var_tl
+  }
+
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_lower: {
+  % right align
+  \sdaps_array_alignment:
+  \leavevmode
+  \sdaps_range:nnV { lower } { 0 } \g_sdaps_question_lowertext_tl
+  \sdaps_if_rtl:F {
+    \hfill
+  }
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_upper: {
+  \sdaps_array_alignment:
+  \leavevmode
+  \sdaps_range:nnV { upper } { \l_sdaps_rangearray_rangecount_int - 1 } \g_sdaps_question_uppertext_tl
+  \sdaps_if_rtl:T {
+    \hfill
+  }
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_before_other: {
+  % Insert an extra empty column for further spacing
+  \sdaps_array_alignment:
+  \sdaps_array_alignment:
+  \leavevmode
+  \sdaps_answer:V \g_sdaps_question_othertext_tl
+  \sdaps_if_rtl:TF {
+    \hfill
+  } {
+    \sdaps_checkbox:nn { } { 0 } {} ~ {}
+  }
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_question: {
+  % Insert an extra empty column for further spacing
+  \sdaps_array_alignment:
+  \tl_if_empty:NTF \g_sdaps_question_lowertext_tl {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_lower:n
+  } {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_lower:w
+  }
+  \l_tmpa_token
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_lower: {
+  % Insert the option checkbox column
+  \sdaps_if_rtl:T {
+    \hfill\kern 0pt
+  }
+  \sdaps_array_alignment:
+  \leavevmode
+  % Seems like right to left writing mode is not started without a paragraph
+  \sdaps_if_rtl:T { \beginR }
+  % Assume we have at least one checkbox
+  \sdaps_checkbox:nn { } { 1 }
+  \int_step_inline:nnnn { 2 } { 1 } { \l_sdaps_rangearray_rangecount_int } {
+    \hspace{1em} \sdaps_checkbox:nn { } { ##1 }
+  }
+  \sdaps_if_rtl:T { \endR }
+
+  \tl_if_empty:NTF \g_sdaps_question_uppertext_tl {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_upper:n
+  } {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_upper:w
+  }
+  \l_tmpa_token
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_upper: {
+  \sdaps_if_rtl:F {
+    \hfill\kern 0pt
+  }
+
+  \bool_if:NTF \l_sdaps_rangearray_other_bool {
+    \tl_if_empty:NTF \g_sdaps_question_othertext_tl {
+      \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_other:n
+    } {
+      \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_other:w
+    }
+  } {
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_finish:
+  }
+  \l_tmpa_token
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_after_other: {
+  \sdaps_if_rtl:TF {
+    {} ~ \sdaps_checkbox:nn { } { 0 } {}
+  } {
+    \hfill\kern 0pt
+  }
+  \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_finish: {
+  \sdaps_qobject_end:n { rangearray_question }
+  \ignorespaces
+}
+
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_question:n {
+  \tl_gset:Nn \g_sdaps_question_text_tl { #1 }
+
+  \_sdaps_rangearray_process_question_before_question:
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_rangearray_process_question_after_question:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_lower:n {
+  \tl_gset:Nn \g_sdaps_question_lowertext_tl { #1 }
+
+  \_sdaps_rangearray_process_question_before_lower:
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_rangearray_process_question_after_lower:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_upper:n {
+  \tl_gset:Nn \g_sdaps_question_uppertext_tl { #1 }
+
+  \_sdaps_rangearray_process_question_before_upper:
+  \group_begin:
+    \sdaps_array_nested_alignenv:
+    #1
+  \group_end:
+  \_sdaps_rangearray_process_question_after_upper:
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_rangearray_process_question_grab_other:n {
+  \tl_gset:Nn \g_sdaps_question_othertext_tl { #1 }
+
+  % If the text is empty, assume that this particular question does not have
+  % an alternative choice. Note that this column might not exist and this
+  % macro will not even be called in that case.
+  % If we skip the optional "other" option then we still need to insert the
+  % alignment to create the column.
+  \tl_if_empty:NTF \g_sdaps_question_othertext_tl {
+    \sdaps_array_alignment:
+    \leavevmode
+    \ignorespaces
+  } {
+    \_sdaps_rangearray_process_question_before_other:
+    \group_begin:
+      \sdaps_array_nested_alignenv:
+      #1
+    \group_end:
+    \_sdaps_rangearray_process_question_after_other:
+  }
+  \_sdaps_rangearray_process_question_finish:
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_question:w {
+  \_sdaps_rangearray_process_question_before_question:
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_question:
+    \sdaps_array_nested_alignenv:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_lower:w {
+  \_sdaps_rangearray_process_question_before_lower:
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_lower:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_upper:w {
+  \_sdaps_rangearray_process_question_before_upper:
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_upper:
+    \tex_let:D\next=
+}
+
+\cs_new_protected_nopar:Npn \_sdaps_rangearray_process_question_inline_other:w {
+  \_sdaps_rangearray_process_question_before_other:
+  % If we reach this macro then a text has been set for the other item. This
+  % means we never need to ignore the "other" parameter at this point.
+  \bgroup
+    \group_insert_after:N \_sdaps_rangearray_process_question_after_other:
+    \group_insert_after:N \_sdaps_rangearray_process_question_finish:
+    \tex_let:D\next=
+}
+
+\cs_new_nopar:Npn \_sdaps_rangearray_process_question:nw #1
+{
+  % Is there a better way other than clearing these before parsing?
+  \tl_gclear:N \g_sdaps_question_var_tl
+  \tl_gclear:N \g_sdaps_question_text_tl
+  \tl_gclear:N \g_sdaps_question_uppertext_tl
+  \tl_gclear:N \g_sdaps_question_lowertext_tl
+  \tl_gclear:N \g_sdaps_question_othertext_tl
+
+  \keys_set:nn { sdaps / rangearray / question } { #1 }
+
+  \tl_if_empty:NTF \g_sdaps_question_text_tl {
+    % We need to leave a command in the stream that grabs the next parameter,
+    % outputs it again, and finishes the question.
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_grab_question:n
+  } {
+    % We need to generate the question after the next group stops
+    \cs_set_eq:NN \l_tmpa_token \_sdaps_rangearray_process_question_inline_question:w
+  }
+  \l_tmpa_token
+}
+\cs_generate_variant:Nn \_sdaps_rangearray_process_question:nw { Vw }
+
+
+\newenvironment { choicearray } [ 2 ] []
+{
+  \group_begin:
+
+    \sdaps_context_get:nN { choicearray } \l_tmpa_tl
+    \tl_if_eq:NNT \l_tmpa_tl \q_no_value {
+      \tl_set:Nn \l_tmpa_tl {}
+    }
+
+    \tl_if_empty:nF { #1 } {
+      \tl_if_empty:NTF \l_tmpa_tl {
+        \tl_set:Nn \l_tmpa_tl { #1 }
+      } {
+        \tl_set:Nf \l_tmpa_tl { \l_tmpa_tl, #1 }
+      }
+    }
+
+    \_sdaps_choicearray_preprocess:Vn \l_tmpa_tl { #2 }
+    % Clear the variables
+    \seq_gclear:N \g_sdaps_choices_filter_seq
+    \seq_gclear:N \g_sdaps_choices_cell_seq
+    \seq_gclear:N \g_sdaps_choices_text_seq
+
+    \int_gzero:N \g_sdaps_choices_box_int
+
+    % Define new commands
+    \newcommand \choice [ 1 ] [] {
+      \_sdaps_choicearray_process_choice:nw { ##1 }
+    }
+    \newcommand \question [ 1 ] [] {
+      \_sdaps_choicearray_process_question:nw { ##1 }
+    }
+
+    \group_begin:
+
+      \tl_set:Nx \l_tmpb_tl {keepenv,layouter=\tl_use:N\l_sdaps_choicearray_layouter_tl,align=\l_sdaps_choicearray_align_tl\bool_if:NF\l_sdaps_choicearray_horizontal_bool{,flip},\l_sdaps_choicearray_extra_tl}
+      \expandafter\sdapsarray\expandafter[\l_tmpb_tl]
+}
+{
+      \endsdapsarray
+    \group_end:
+    % Process keys
+    \_sdaps_choicearray_postprocess:
+
+  \group_end:
+}
+
+\newenvironment { optionarray } [ 2 ] []
+{
+  \choicearray[singlechoice,#1] { #2 }
+}
+{
+  \endchoicearray
+}
+
+\newenvironment { rangearray } [ 2 ] []
+{
+  \group_begin:
+
+    \sdaps_context_get:nN { rangearray } \l_tmpa_tl
+    \tl_if_eq:NNT \l_tmpa_tl \q_no_value {
+      \tl_set:Nn \l_tmpa_tl {}
+    }
+
+    \tl_if_empty:nF { #1 } {
+      \tl_if_empty:NTF \l_tmpa_tl {
+        \tl_set:Nn \l_tmpa_tl { #1 }
+      } {
+        \tl_set:Nf \l_tmpa_tl { \l_tmpa_tl, #1 }
+      }
+    }
+
+    \_sdaps_rangearray_preprocess:Vn \l_tmpa_tl { #2 }
+
+    \newcommand \range [ 1 ] [] {
+      \_sdaps_rangearray_process_question:nw { ##1 }
+    }
+
+    \group_begin:
+
+      \tl_set:Nx \l_tmpb_tl {keepenv,align=\l_sdaps_rangearray_align_tl,\l_sdaps_rangearray_extra_tl}
+      \expandafter\sdapsarray\expandafter[\l_tmpb_tl]
+}
+{
+      \endsdapsarray
+    \group_end:
+    % Process keys
+    \_sdaps_rangearray_postprocess:
+
+  \group_end:
+}
+
+\ExplSyntaxOff
+
+\endinput
+%%
+%% End of file `sdapslayout.sty'.


Property changes on: trunk/Master/texmf-dist/tex/latex/sdaps/sdapslayout.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/latex/sdaps/sdapspdf.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/sdapspdf.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/sdapspdf.sty	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,83 @@
+%%
+%% This is file `sdapspdf.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% sdapspdf.dtx  (with options: `package')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2015 by Benjamin Berg <benjamin at sipsolutions.net>
+%% 
+%% 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.
+%% 
+\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\ProvidesPackage{sdapspdf}
+    [2015/04/10 v0.1 Initial version of SDAPS pdf package]
+\RequirePackage{expl3}[2011/08/23]
+\ExplSyntaxOn
+\ExplSyntaxOff
+
+\RequirePackage{sdapsbase}
+\RequirePackage{hyperref}
+
+\ExplSyntaxOn
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\bool_new:N \l__sdaps_pdf_form_bool
+\bool_new:N \l__sdaps_pdf_checkbox_default_bool
+\tl_new:N \l__sdaps_pdf_checkboxsymbol_tl
+
+\keys_define:nn { sdaps / checkbox / overlay / pdfform }
+{
+  pdf_form         .bool_set:N  = \l__sdaps_pdf_form_bool,
+  pdf_form         .initial:n   = false,
+
+  default          .bool_set:N  = \l__sdaps_pdf_checkbox_default_bool,
+  default          .initial:n   = false,
+
+  checkboxsymbol   .tl_set:N  = \l__sdaps_pdf_checkboxsymbol_tl,
+  checkboxsymbol   .initial:n   = 8,
+}
+
+\cs_new_protected_nopar:Nn \_sdaps_pdf_overlay_checkbox_form:
+{
+  \keys_set_known:nV { sdaps / checkbox / overlay / pdfform } { \l_sdaps_parse_unknown_tl } %
+
+  \bool_if:NT \l__sdaps_pdf_form_bool {
+    \node[anchor=center,inner~sep=0pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \CheckBox[name=\l_sdaps_var_tl,bordersep=0,borderwidth=0pt,checkboxsymbol=\l__sdaps_pdf_checkboxsymbol_tl,bordercolor=,backgroundcolor=,checked=\bool_if:nTF\l__sdaps_pdf_checkbox_default_bool{true}{false},height=\l_sdaps_checkbox_height_dim,width=\l_sdaps_checkbox_width_dim]{\ignorespaces}
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_checkbox_overlays_seq \_sdaps_pdf_overlay_checkbox_form:
+
+\cs_new_protected_nopar:Nn \_sdaps_pdf_overlay_text_form:
+{
+  \keys_set_known:nV { sdaps / checkbox / overlay / pdfform } { \l_sdaps_parse_unknown_tl } %
+
+  \bool_if:NT \l__sdaps_pdf_form_bool {
+    \node[anchor=center,inner~sep=2pt,outer~sep=0pt] at ($(\l_sdaps_x_dim, \l_sdaps_y_dim) + 0.5*(\l_sdaps_width_dim, -\l_sdaps_height_dim)$) {
+      \dim_set:Nn \l_sdaps_width_dim { \l_sdaps_width_dim - 4pt }
+      \dim_set:Nn \l_sdaps_height_dim { \l_sdaps_height_dim - 4pt }
+
+      \TextField[name=\l_sdaps_var_tl,bordersep=0pt,borderwidth=0pt,bordercolor=,backgroundcolor=,multiline=true,height=\l_sdaps_height_dim,width=\l_sdaps_width_dim]{\ignorespaces}
+    };
+  }
+}
+\seq_put_left:Nn \g__sdaps_textbox_overlays_seq \_sdaps_pdf_overlay_text_form:
+
+\ExplSyntaxOff
+
+
+\endinput
+%%
+%% End of file `sdapspdf.sty'.


Property changes on: trunk/Master/texmf-dist/tex/latex/sdaps/sdapspdf.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Brazilian.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Brazilian.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Brazilian.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Brazilian}
+
+\providetranslation{answers}{Respostas}
+\providetranslation{draft}{projeto}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{M{\'e}dia}
+\providetranslation{questionnaireid}{ID do Question{\'a}rio:}
+\providetranslation{standard-deviation}{Desvio Padr{\~a}o}
+\providetranslation{surveyid}{ID da Pesquisa:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Dutch.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Dutch.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Dutch.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Dutch}
+
+\providetranslation{answers}{Antwoorden}
+\providetranslation{draft}{voorontwerp}
+\providetranslation{info-corrected}{Selectie ongedaan maken}
+\providetranslation{info-cross}{Geselecteerd}
+\providetranslation{info-fill}{Markeer om te selecteren}
+\providetranslation{info-multi}{Meerkeuze (selecteer alle opties die van toepassing zijn)}
+\providetranslation{info-single}{Enkelvoudige keuze (selecteer enkel {\'e}{\'e}n optie)}
+\providetranslation{infotext}{De vragenlijst wordt elektronisch ingelezen. Gebruik een balpen of dunne viltstift om als volgt te antwoorden:}
+\providetranslation{mean}{Mediaan}
+\providetranslation{questionnaireid}{Vragenlijst-ID:}
+\providetranslation{standard-deviation}{Standaardafwijking}
+\providetranslation{surveyid}{Onderzoeks-ID:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-English.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-English.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-English.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{English}
+
+\providetranslation{answers}{Answers}
+\providetranslation{draft}{draft}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Mean}
+\providetranslation{questionnaireid}{Questionnaire-ID:}
+\providetranslation{standard-deviation}{Standard-Deviation}
+\providetranslation{surveyid}{Survey-ID:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Finnish.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Finnish.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Finnish.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Finnish}
+
+\providetranslation{answers}{Vastaukset}
+\providetranslation{draft}{luonnos}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Keskiarvo}
+\providetranslation{questionnaireid}{Kyselylomakkeen tunnus:}
+\providetranslation{standard-deviation}{Keskihajonta}
+\providetranslation{surveyid}{Kyselyn tunnus:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-French.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-French.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-French.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{French}
+
+\providetranslation{answers}{R{\'e}ponses}
+\providetranslation{draft}{brouillon}
+\providetranslation{info-corrected}{Annuler la s{\'e}lection}
+\providetranslation{info-cross}{S{\'e}lectionn{\'e}}
+\providetranslation{info-fill}{Cocher pour s{\'e}lectionner}
+\providetranslation{info-multi}{Choix multiples (s{\'e}lectionnez toutes les options applicables)}
+\providetranslation{info-single}{{\`A} choix unique (ne choisir qu{\textquoteright}une seule option)}
+\providetranslation{infotext}{Ce questionnaire est {\'e}valu{\'e} automatiquement. Veuillez utiliser un stylo pour remplir vos r{\'e}ponses comme suit :}
+\providetranslation{mean}{Moyenne}
+\providetranslation{questionnaireid}{ID-Questionnaire :}
+\providetranslation{standard-deviation}{{\'E}cart-type}
+\providetranslation{surveyid}{ID-Sondage :}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-German.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-German.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-German.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{German}
+
+\providetranslation{answers}{Antworten}
+\providetranslation{draft}{Entwurf}
+\providetranslation{info-corrected}{Auswahl zur{\"u}cknehmen}
+\providetranslation{info-cross}{Ausgew{\"a}hlt}
+\providetranslation{info-fill}{Ausf{\"u}llen zum ausw{\"a}hlen}
+\providetranslation{info-multi}{Mehrfachauswahl (alle zutreffenden Optionen ausw{\"a}hlen)}
+\providetranslation{info-single}{Einzelauswahl (nur eine Option ausw{\"a}hlen)}
+\providetranslation{infotext}{Der Fragebogen wird maschinell erfasst. Bitte mit Kugelschreiber wie folgt ausf{\"u}llen:}
+\providetranslation{mean}{Durchschnitt}
+\providetranslation{questionnaireid}{Fragebogen-ID:}
+\providetranslation{standard-deviation}{Standardabweichung}
+\providetranslation{surveyid}{Umfrage-ID:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Italian.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Italian.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Italian.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Italian}
+
+\providetranslation{answers}{Risposte}
+\providetranslation{draft}{bozza}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Media}
+\providetranslation{questionnaireid}{ID del Questionario:}
+\providetranslation{standard-deviation}{Deviazione Standard}
+\providetranslation{surveyid}{ID del sondaggio:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Norsk.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Norsk.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Norsk.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Norsk}
+
+\providetranslation{answers}{Svar}
+\providetranslation{draft}{utkast}
+\providetranslation{info-corrected}{Angre utvalg}
+\providetranslation{info-cross}{Valgt}
+\providetranslation{info-fill}{Fyll for {\r a} velge}
+\providetranslation{info-multi}{Flervalg (velg alle passende alternativer)}
+\providetranslation{info-single}{Enkeltvalg (velg kun ett alternativ)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Gjennomsnitt}
+\providetranslation{questionnaireid}{Sp{\o}rreskjema-ID:}
+\providetranslation{standard-deviation}{Standardavvik}
+\providetranslation{surveyid}{Sp{\o}rreunders{\o}kelses-ID:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Polish.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Polish.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Polish.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Polish}
+
+\providetranslation{answers}{Odpowiedzi}
+\providetranslation{draft}{szkic}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Znaczenie}
+\providetranslation{questionnaireid}{Questionnaire-ID:}
+\providetranslation{standard-deviation}{Standard-Deviation}
+\providetranslation{surveyid}{Survey-ID:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Portuguese.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Portuguese.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Portuguese.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Portuguese}
+
+\providetranslation{answers}{Respostas}
+\providetranslation{draft}{projeto}
+\providetranslation{info-corrected}{Desfazer sele{\c c}{\~a}o}
+\providetranslation{info-cross}{Selecionado}
+\providetranslation{info-fill}{Preencha para selecionar}
+\providetranslation{info-multi}{Escolha m{\'u}ltipla (selecione todas as op{\c c}{\~o}es aplic{\'a}veis)}
+\providetranslation{info-single}{Escolha {\'u}nica (selecione apenas uma op{\c c}{\~a}o)}
+\providetranslation{infotext}{Este question{\'a}rio {\'e} lido automaticamente por um programa. Por favor utilize uma caneta para preench{\^e}-lo conforme o seguinte:}
+\providetranslation{mean}{M{\'e}dia}
+\providetranslation{questionnaireid}{ID do Question{\'a}rio:}
+\providetranslation{standard-deviation}{Desvio Padr{\~a}o}
+\providetranslation{surveyid}{ID da Pesquisa:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Romanian.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Romanian.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Romanian.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Romanian}
+
+\providetranslation{answers}{Answers}
+\providetranslation{draft}{ciorn{\u a}}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Mean}
+\providetranslation{questionnaireid}{ID-Chestionar:}
+\providetranslation{standard-deviation}{Standard-Deviation}
+\providetranslation{surveyid}{ID-Sondaj:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Sinhala.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Sinhala.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Sinhala.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Sinhala}
+
+\providetranslation{answers}{Answers}
+\providetranslation{draft}{draft}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Mean}
+\providetranslation{questionnaireid}{Questionnaire-ID:}
+\providetranslation{standard-deviation}{Standard-Deviation}
+\providetranslation{surveyid}{Survey-ID:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Spanish.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Spanish.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Spanish.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Spanish}
+
+\providetranslation{answers}{Respuestas}
+\providetranslation{draft}{borrador}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Media}
+\providetranslation{questionnaireid}{ID de cuestionario:}
+\providetranslation{standard-deviation}{Desviaci{\'o}n est{\'a}ndar}
+\providetranslation{surveyid}{ID de encuesta:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Swedish.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Swedish.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Swedish.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Swedish}
+
+\providetranslation{answers}{Answers}
+\providetranslation{draft}{draft}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Mean}
+\providetranslation{questionnaireid}{Questionnaire-ID:}
+\providetranslation{standard-deviation}{Standard-Deviation}
+\providetranslation{surveyid}{Survey-ID:}

Added: trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Ukrainian.dict
===================================================================
--- trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Ukrainian.dict	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/sdaps/translator-sdaps-dictionary-Ukrainian.dict	2019-12-16 21:42:56 UTC (rev 53147)
@@ -0,0 +1,28 @@
+% This file is auto-generated from gettext translations (.po files).
+% The header of the original file follows for reference:
+%
+% Translations for sdaps LaTeX classes.
+% Copyright 2010-2013 Ferdinand Schwenk (ferdisdot at gmail.com).
+% Copyright 2011-2013 Benjamin Berg (benjamin at sipsolutions.net).
+%
+% 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
+%
+
+\ProvidesDictionary{translator-sdaps-dictionary}{Ukrainian}
+
+\providetranslation{answers}{Answers}
+\providetranslation{draft}{{\cyrch}{\cyre}{\cyrr}{\cyrn}{\cyre}{\cyrt}{\cyrk}{\cyra}}
+\providetranslation{info-corrected}{Undo select}
+\providetranslation{info-cross}{Selected}
+\providetranslation{info-fill}{Fill to select}
+\providetranslation{info-multi}{Multiple-choice (select all applicable options)}
+\providetranslation{info-single}{Single-choice (select only one option)}
+\providetranslation{infotext}{This questionnaire is evaluated automatically. Please use a pen to fill in your answers as follows:}
+\providetranslation{mean}{Mean}
+\providetranslation{questionnaireid}{{\CYRA}{\cyrn}{\cyrk}{\cyre}{\cyrt}{\cyra}-ID:}
+\providetranslation{standard-deviation}{{\CYRS}{\cyrt}{\cyra}{\cyrn}{\cyrd}{\cyra}{\cyrr}{\cyrt}{\cyrn}{\cyre} {\cyrv}{\cyrii}{\cyrd}{\cyrh}{\cyri}{\cyrl}{\cyre}{\cyrn}{\cyrn}{\cyrya}}
+\providetranslation{surveyid}{{\CYRO}{\cyrp}{\cyri}{\cyrt}{\cyru}{\cyrv}{\cyra}{\cyrn}{\cyrn}{\cyrya}-ID:}

Modified: trunk/Master/tlpkg/bin/tlpkg-ctan-check
===================================================================
--- trunk/Master/tlpkg/bin/tlpkg-ctan-check	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/tlpkg/bin/tlpkg-ctan-check	2019-12-16 21:42:56 UTC (rev 53147)
@@ -624,7 +624,7 @@
     sciposter scientific-thesis-cover scontents
     scratch scratch3 scratchx screenplay screenplay-pkg
     scrjrnl scrlttr2copy scsnowman
-    sdrt sduthesis
+    sdaps sdrt sduthesis
     secdot section sectionbox sectionbreak sectsty seealso
     selectp selinput selnolig semantic semantic-markup semaphor
     seminar semioneside semproc sepfootnotes sepnum seqsplit

Modified: trunk/Master/tlpkg/libexec/ctan2tds
===================================================================
--- trunk/Master/tlpkg/libexec/ctan2tds	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/tlpkg/libexec/ctan2tds	2019-12-16 21:42:56 UTC (rev 53147)
@@ -1998,6 +1998,7 @@
  'sa-tikz',     'tikzlib.*tex|' . $standardtex,
  'sansmath',    '^..[^s].*\.sty',	# not miscdoc.sty
  'scrlttr2copy','\.lco',
+ 'sdaps',	'\.dict$|' . $standardtex,
  'sduthesis',	'figures|' . $standardtex,
  'selnolig',    '\.lua|' . $standardtex,
  'seminar',     '\.bug|\.bg2|\.cls|\.sty|2up.tex',

Modified: trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc	2019-12-16 21:39:23 UTC (rev 53146)
+++ trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc	2019-12-16 21:42:56 UTC (rev 53147)
@@ -1014,6 +1014,7 @@
 depend schedule
 depend scontents
 depend scrlttr2copy
+depend sdaps
 depend sdrt
 depend secdot
 depend sectionbox

Added: trunk/Master/tlpkg/tlpsrc/sdaps.tlpsrc
===================================================================


More information about the tex-live-commits mailing list