From 6cbf49c3ed1a2881b7fccbc67eccb5532d196b74 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Tue, 17 Dec 2019 03:00:34 +0000 Subject: CTAN sync 201912170300 --- macros/latex/contrib/sdaps/README | 57 + macros/latex/contrib/sdaps/sdapsarray.dtx | 1167 +++++++++++++ macros/latex/contrib/sdaps/sdapsarray.ins | 55 + macros/latex/contrib/sdaps/sdapsarray.pdf | Bin 0 -> 191217 bytes macros/latex/contrib/sdaps/sdapsbase.dtx | 1803 ++++++++++++++++++++ macros/latex/contrib/sdaps/sdapsbase.ins | 55 + macros/latex/contrib/sdaps/sdapsbase.pdf | Bin 0 -> 202251 bytes macros/latex/contrib/sdaps/sdapsclassic.dtx | 1134 ++++++++++++ macros/latex/contrib/sdaps/sdapsclassic.ins | 55 + macros/latex/contrib/sdaps/sdapsclassic.pdf | Bin 0 -> 173223 bytes macros/latex/contrib/sdaps/sdapscode128.tex | 374 ++++ macros/latex/contrib/sdaps/sdapslayout.dtx | 790 +++++++++ macros/latex/contrib/sdaps/sdapslayout.ins | 55 + macros/latex/contrib/sdaps/sdapslayout.pdf | Bin 0 -> 287460 bytes macros/latex/contrib/sdaps/sdapspdf.dtx | 166 ++ macros/latex/contrib/sdaps/sdapspdf.ins | 55 + macros/latex/contrib/sdaps/sdapspdf.pdf | Bin 0 -> 144888 bytes .../translator-sdaps-dictionary-Brazilian.dict | 28 + .../sdaps/translator-sdaps-dictionary-Dutch.dict | 28 + .../sdaps/translator-sdaps-dictionary-English.dict | 28 + .../sdaps/translator-sdaps-dictionary-Finnish.dict | 28 + .../sdaps/translator-sdaps-dictionary-French.dict | 28 + .../sdaps/translator-sdaps-dictionary-German.dict | 28 + .../sdaps/translator-sdaps-dictionary-Italian.dict | 28 + .../sdaps/translator-sdaps-dictionary-Norsk.dict | 28 + .../sdaps/translator-sdaps-dictionary-Polish.dict | 28 + .../translator-sdaps-dictionary-Portuguese.dict | 28 + .../translator-sdaps-dictionary-Romanian.dict | 28 + .../sdaps/translator-sdaps-dictionary-Sinhala.dict | 28 + .../sdaps/translator-sdaps-dictionary-Spanish.dict | 28 + .../sdaps/translator-sdaps-dictionary-Swedish.dict | 28 + .../translator-sdaps-dictionary-Ukrainian.dict | 28 + 32 files changed, 6186 insertions(+) create mode 100644 macros/latex/contrib/sdaps/README create mode 100644 macros/latex/contrib/sdaps/sdapsarray.dtx create mode 100644 macros/latex/contrib/sdaps/sdapsarray.ins create mode 100644 macros/latex/contrib/sdaps/sdapsarray.pdf create mode 100644 macros/latex/contrib/sdaps/sdapsbase.dtx create mode 100644 macros/latex/contrib/sdaps/sdapsbase.ins create mode 100644 macros/latex/contrib/sdaps/sdapsbase.pdf create mode 100644 macros/latex/contrib/sdaps/sdapsclassic.dtx create mode 100644 macros/latex/contrib/sdaps/sdapsclassic.ins create mode 100644 macros/latex/contrib/sdaps/sdapsclassic.pdf create mode 100644 macros/latex/contrib/sdaps/sdapscode128.tex create mode 100644 macros/latex/contrib/sdaps/sdapslayout.dtx create mode 100644 macros/latex/contrib/sdaps/sdapslayout.ins create mode 100644 macros/latex/contrib/sdaps/sdapslayout.pdf create mode 100644 macros/latex/contrib/sdaps/sdapspdf.dtx create mode 100644 macros/latex/contrib/sdaps/sdapspdf.ins create mode 100644 macros/latex/contrib/sdaps/sdapspdf.pdf create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Brazilian.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Dutch.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-English.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Finnish.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-French.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-German.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Italian.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Norsk.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Polish.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Portuguese.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Romanian.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Sinhala.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Spanish.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Swedish.dict create mode 100644 macros/latex/contrib/sdaps/translator-sdaps-dictionary-Ukrainian.dict (limited to 'macros/latex/contrib/sdaps') diff --git a/macros/latex/contrib/sdaps/README b/macros/latex/contrib/sdaps/README new file mode 100644 index 0000000000..3d955574b0 --- /dev/null +++ b/macros/latex/contrib/sdaps/README @@ -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) diff --git a/macros/latex/contrib/sdaps/sdapsarray.dtx b/macros/latex/contrib/sdaps/sdapsarray.dtx new file mode 100644 index 0000000000..185fc9b961 --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapsarray.dtx @@ -0,0 +1,1167 @@ +% \iffalse meta-comment +% +% Copyright (C) 2015 by Benjamin Berg +% +% 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} +% +%\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%\ProvidesPackage{sdapsarray} +%<*package> + [2015/04/10 v0.1 Initial version of SDAPS array package] +% +% +%<*driver> +\documentclass{ltxdoc} +\usepackage{sdapsarray}[2015/04/10] +%\EnableCrossrefs +\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{sdapsarray.dtx} +\end{document} +% +% \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@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 diff --git a/macros/latex/contrib/sdaps/sdapsarray.ins b/macros/latex/contrib/sdaps/sdapsarray.ins new file mode 100644 index 0000000000..9acca30c07 --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapsarray.ins @@ -0,0 +1,55 @@ +%% +%% Copyright (C) 2015 by Benjamin Berg +%% +%% 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 + +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 diff --git a/macros/latex/contrib/sdaps/sdapsarray.pdf b/macros/latex/contrib/sdaps/sdapsarray.pdf new file mode 100644 index 0000000000..3ff9734454 Binary files /dev/null and b/macros/latex/contrib/sdaps/sdapsarray.pdf differ diff --git a/macros/latex/contrib/sdaps/sdapsbase.dtx b/macros/latex/contrib/sdaps/sdapsbase.dtx new file mode 100644 index 0000000000..94d86fd8cc --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapsbase.dtx @@ -0,0 +1,1803 @@ +% \iffalse meta-comment +% +% Copyright (C) 2015 by Benjamin Berg +% +% 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} +% +%\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%\ProvidesPackage{sdapsbase} +%<*package> + [2015/01/14 v0.1 Initial version of SDAPS base package] +% +% +%<*driver> +\documentclass{ltxdoc} +\usepackage{sdapsbase}[2015/01/14] +%\EnableCrossrefs +\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{sdapsbase.dtx} +\end{document} +% +% \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@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@RTL } { + \tl_use:c { if@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 } \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 } \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@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@getposition{pgfpageorigin}{\@sdaps@pageorigin} + \pgfsys@getposition{textboxtop\int_use:N\g__sdaps_textbox_num_int}{\@sdaps@textboxtoppos} + \pgfpointadd{\@sdaps@textboxtoppos}{ + \pgfpointadd{\@sdaps@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@getposition{\pgfpictureid}{\@sdaps@textboxbottompos} + \pgfpointdiff{\@sdaps@textboxtoppos}{\@sdaps@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@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@getposition{pgfpageorigin}{\@sdaps@pageorigin} + \pgfsys@getposition{textboxstart\int_use:N\g__sdaps_textbox_num_int}{\@sdaps@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@textboxpos}{ + \pgfpointadd{\@sdaps@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@getposition{\pgfpictureid}{\@sdaps@textboxendpos} + % Calculate width and add the height to it + \pgfpointadd{ + \pgfpointdiff{\@sdaps@textboxpos}{\@sdaps@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 diff --git a/macros/latex/contrib/sdaps/sdapsbase.ins b/macros/latex/contrib/sdaps/sdapsbase.ins new file mode 100644 index 0000000000..485de90df5 --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapsbase.ins @@ -0,0 +1,55 @@ +%% +%% Copyright (C) 2015 by Benjamin Berg +%% +%% 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 + +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 diff --git a/macros/latex/contrib/sdaps/sdapsbase.pdf b/macros/latex/contrib/sdaps/sdapsbase.pdf new file mode 100644 index 0000000000..df99391a21 Binary files /dev/null and b/macros/latex/contrib/sdaps/sdapsbase.pdf differ diff --git a/macros/latex/contrib/sdaps/sdapsclassic.dtx b/macros/latex/contrib/sdaps/sdapsclassic.dtx new file mode 100644 index 0000000000..d2f4002800 --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapsclassic.dtx @@ -0,0 +1,1134 @@ +% \iffalse meta-comment +% +% Copyright (C) 2015 by Benjamin Berg +% +% 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} +% +%\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%\ProvidesClass{sdapsclassic} +%<*class> + [2015/08/02 v0.1 Initial version of SDAPS classic class] +% +% +%<*driver> +\documentclass{ltxdoc} +%\EnableCrossrefs +\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{sdapsclassic.dtx} +\end{document} +% +% \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@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@sdaps@draft\@sdaps@drafttrue +\DeclareOption{final}{\@sdaps@draftfalse} + +\tl_gset:Nn\g_sdaps_style_tl{qr}% + + +\DefineFamily{SDAPS} +\DefineFamilyMember{SDAPS} +\DefineFamilyKey{SDAPS}{sdaps_style}[qr]% +{% + \KOMA@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@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@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@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@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@processline{ + \iow_now:Nx \l__sdaps_questionnaire_iow {\the\verbatim@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@page}{}{\setcounter{page}{1}} + \@ifundefined{c@part}{}{\setcounter{part}{0}} + \@ifundefined{c@chapter}{}{\setcounter{chapter}{0}} + \@ifundefined{c@paragraph}{}{\setcounter{paragraph}{0}} + \@ifundefined{c@subparagraph}{}{\setcounter{subparagraph}{0}} + \@ifundefined{c@section}{}{\setcounter{section}{0}} + \@ifundefined{c@subsection}{}{\setcounter{subsection}{0}} + \@ifundefined{c@subsubsection}{}{\setcounter{subsubsection}{0}} + \@ifundefined{c@equation}{}{\setcounter{equation}{0}} + \@ifundefined{c@figure}{}{\setcounter{figure}{0}} + \@ifundefined{c@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 diff --git a/macros/latex/contrib/sdaps/sdapsclassic.ins b/macros/latex/contrib/sdaps/sdapsclassic.ins new file mode 100644 index 0000000000..46aeca3506 --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapsclassic.ins @@ -0,0 +1,55 @@ +%% +%% Copyright (C) 2015 by Benjamin Berg +%% +%% 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 + +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 diff --git a/macros/latex/contrib/sdaps/sdapsclassic.pdf b/macros/latex/contrib/sdaps/sdapsclassic.pdf new file mode 100644 index 0000000000..0105d33275 Binary files /dev/null and b/macros/latex/contrib/sdaps/sdapsclassic.pdf differ diff --git a/macros/latex/contrib/sdaps/sdapscode128.tex b/macros/latex/contrib/sdaps/sdapscode128.tex new file mode 100644 index 0000000000..90835f517f --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapscode128.tex @@ -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 string \relax": Each token from "string" gets a value +% (spaces are ignored). First token gets value , second +% and so on. It is possible to reconstruct the value by +% "\csname\string\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 \ +\definetable:0 \SP \relax % the \SP is alternative to \ +\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 " the "\ifchar" has following meaning: +% true, if is in digits, only in A or only in B respectively with +% 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 string\stop" is used, the "\ifnext" has +% the following meaning: +% 1. 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. 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 adds the numerical value of (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 +% represents the line of code table (see standard of Code 128 bellow) +% of output characters. The amount of 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 . +% 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 . +% else include 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 +% 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 . +% else include 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 +% else switch to mode B using +\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 96 113311 + 97 97 411113 + 98 98 411311 + 99 99 113141 +100 114131 +101 311141 +102 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 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 braces are not possible in input. They are special for +decoder. The and are used in our macro, but 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 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 command +switches to codeA mode definitively unless next switch command is used. +The commands and 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. + + + + diff --git a/macros/latex/contrib/sdaps/sdapslayout.dtx b/macros/latex/contrib/sdaps/sdapslayout.dtx new file mode 100644 index 0000000000..6788e55c5a --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapslayout.dtx @@ -0,0 +1,790 @@ +% \iffalse meta-comment +% +% Copyright (C) 2014 by Henry Menke +% Copyright (C) 2015-2016 by Benjamin Berg +% +% 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} +% +%\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%\ProvidesPackage{sdapslayout} +%<*package> + [2015/04/10 v0.1 Initial version of SDAPS layout package] +% +% +%<*driver> +\documentclass{l3doc} +\usepackage{sdapslayout}[2015/04/10] +\EnableCrossrefs +%\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{sdapslayout.dtx} +\end{document} +% +% \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@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 diff --git a/macros/latex/contrib/sdaps/sdapslayout.ins b/macros/latex/contrib/sdaps/sdapslayout.ins new file mode 100644 index 0000000000..c1656b754d --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapslayout.ins @@ -0,0 +1,55 @@ +%% +%% Copyright (C) 2015 by Benjamin Berg +%% +%% 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 + +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 diff --git a/macros/latex/contrib/sdaps/sdapslayout.pdf b/macros/latex/contrib/sdaps/sdapslayout.pdf new file mode 100644 index 0000000000..f1f10773c6 Binary files /dev/null and b/macros/latex/contrib/sdaps/sdapslayout.pdf differ diff --git a/macros/latex/contrib/sdaps/sdapspdf.dtx b/macros/latex/contrib/sdaps/sdapspdf.dtx new file mode 100644 index 0000000000..76d0546abf --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapspdf.dtx @@ -0,0 +1,166 @@ +% \iffalse meta-comment +% +% Copyright (C) 2014 by Henry Menke +% Copyright (C) 2015 by Benjamin Berg +% +% 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} +% +%\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%\ProvidesPackage{sdapspdf} +%<*package> + [2015/04/10 v0.1 Initial version of SDAPS pdf package] +% +% +%<*driver> +\documentclass{ltxdoc} +\usepackage{sdapspdf}[2015/04/10] +%\EnableCrossrefs +\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{sdapspdf.dtx} +\end{document} +% +% \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@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 diff --git a/macros/latex/contrib/sdaps/sdapspdf.ins b/macros/latex/contrib/sdaps/sdapspdf.ins new file mode 100644 index 0000000000..96a5d913c5 --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapspdf.ins @@ -0,0 +1,55 @@ +%% +%% Copyright (C) 2015 by Benjamin Berg +%% +%% 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 + +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 diff --git a/macros/latex/contrib/sdaps/sdapspdf.pdf b/macros/latex/contrib/sdaps/sdapspdf.pdf new file mode 100644 index 0000000000..71199651ef Binary files /dev/null and b/macros/latex/contrib/sdaps/sdapspdf.pdf differ diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Brazilian.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Brazilian.dict new file mode 100644 index 0000000000..b85c012b97 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Brazilian.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Dutch.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Dutch.dict new file mode 100644 index 0000000000..110cfa80c2 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Dutch.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-English.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-English.dict new file mode 100644 index 0000000000..b294ba09b3 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-English.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Finnish.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Finnish.dict new file mode 100644 index 0000000000..41df9c40ab --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Finnish.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-French.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-French.dict new file mode 100644 index 0000000000..821ca1a45a --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-French.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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 :} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-German.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-German.dict new file mode 100644 index 0000000000..594c033e49 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-German.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Italian.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Italian.dict new file mode 100644 index 0000000000..0d8242ee78 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Italian.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Norsk.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Norsk.dict new file mode 100644 index 0000000000..d1d219394e --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Norsk.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Polish.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Polish.dict new file mode 100644 index 0000000000..92a03eec7e --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Polish.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Portuguese.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Portuguese.dict new file mode 100644 index 0000000000..7315d3d411 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Portuguese.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Romanian.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Romanian.dict new file mode 100644 index 0000000000..80051664e5 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Romanian.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Sinhala.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Sinhala.dict new file mode 100644 index 0000000000..415df817bb --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Sinhala.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Spanish.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Spanish.dict new file mode 100644 index 0000000000..b422c25630 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Spanish.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Swedish.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Swedish.dict new file mode 100644 index 0000000000..9c4800e9bf --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Swedish.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} diff --git a/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Ukrainian.dict b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Ukrainian.dict new file mode 100644 index 0000000000..5e05ab2d31 --- /dev/null +++ b/macros/latex/contrib/sdaps/translator-sdaps-dictionary-Ukrainian.dict @@ -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@gmail.com). +% Copyright 2011-2013 Benjamin Berg (benjamin@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:} -- cgit v1.2.3