[latex3-commits] [git/LaTeX3-latex3-pdfresources] radiobuttons: working on radio buttons (bdb8c9e)
Ulrike Fischer
fischer at troubleshooting-tex.de
Mon May 24 00:37:13 CEST 2021
Repository : https://github.com/latex3/pdfresources
On branch : radiobuttons
Link : https://github.com/latex3/pdfresources/commit/bdb8c9ecf70d1197a2042a76aa08b4f698c2634c
>---------------------------------------------------------------
commit bdb8c9ecf70d1197a2042a76aa08b4f698c2634c
Author: Ulrike Fischer <fischer at troubleshooting-tex.de>
Date: Mon May 24 00:37:13 2021 +0200
working on radio buttons
>---------------------------------------------------------------
bdb8c9ecf70d1197a2042a76aa08b4f698c2634c
...ield-checkbox.dtx => l3pdffield-radiobutton.dtx | 366 +++++++++++++++------
l3pdffield.dtx | 3 +
pdfmanagement-testphase.ins | 1 +
3 files changed, 276 insertions(+), 94 deletions(-)
diff --git a/l3pdffield-checkbox.dtx b/l3pdffield-radiobutton.dtx
similarity index 50%
copy from l3pdffield-checkbox.dtx
copy to l3pdffield-radiobutton.dtx
index 681a8d7..2de531d 100644
--- a/l3pdffield-checkbox.dtx
+++ b/l3pdffield-radiobutton.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-%% File: l3pdfpdffield-checkbox.dtx
+%% File: l3pdfpdffield-radiobutton.dtx
%
% Copyright (C) 2021 The LaTeX Project
%
@@ -38,11 +38,6 @@
\end{document}
%</driver>
% \fi
-% \NewDocElement[
-% idxgroup=checkbox keys,
-% idxtype = {checkbox key},
-% printtype= \textit{checkbox key}
-% ]{Checkboxkey}{checkboxkey}
% \providecommand\hook[1]{\texttt{#1}}
% \ExplSyntaxOn
% \pdffield_appearance:nn {pdffield/bear/Yes}
@@ -55,7 +50,7 @@
% }
% \ExplSyntaxOff
% \title{^^A
-% The \pkg{l3pdffield-checkbox} module\\ Commands to create checkbox form fields ^^A
+% The \pkg{l3pdffield-radiobutton} module\\ Commands to create radio form fields ^^A
% \\ \LaTeX{} PDF management testphase bundle
% }
%
@@ -68,36 +63,173 @@
% }^^A
% }
%
-% \date{Version 0.95d, released 2021-05-14}
+% \date{Version 0.95c, released 2021-03-17}
%
% \maketitle
% \begin{documentation}
-% \section{\pkg{l3pdffield-checkbox} Introduction}
-% This is the documentation for checkbox fields, for general information about form fields
+% \section{\pkg{l3pdffield-radiobutton} Introduction}
+% This is the documentation for radio buttons fields, for general information about form fields
% check the documentation l3pdffield.
%
%
%
% Please keep in mind
% \begin{itemize}
-% \item Not every PDF viewer supports checkboxes.
+% \item Not every PDF viewer supports radio buttons.
% \item The handling can depend on settings in the PDF viewer. In adobe reader for
% example I had to disable an option to avoid that it tries to create an appearance
% itself
% \item Standards like pdf/A disable features of form fields too
% (as you typically can't change the PDF).
% \end{itemize}
-% \section{Checkboxes}
+% \section{Radio buttons}
% Click me:
% \ExplSyntaxOn
-% \pdffield_checkbox:n{name=bear,appearance=pdffield/bear,width=23pt,height=30pt,depth=10pt}
+% ^^A \pdffield_radio:n{name=bear,appearance=pdffield/bear,width=23pt,height=30pt,depth=10pt}
% \ExplSyntaxOff
%
+% Radio buttons are similar to checkboxes and
+% they have like checkboxes two \enquote{states}:
+% checked and unchecked.
+%
+% The difference is that checkboxes are either \enquote{clones} which
+% are checked and unchecked together,
+% or are independent of each other. Radio buttons on the other side build groups
+% where checking one of the buttons uncheck all other buttons.
+% The elements of a such a radio button group are annotations of one field which
+% has a flag set which make it into a radio button group.
+%
+% In a checkbox field the two states have the
+% fix names |/Yes| and |/Off|.
+%
+% \begin{tikzpicture}[level 2/.style={level distance=7mm},
+% level 1/.style={sibling distance=25mm},
+% level 2/.style={sibling distance=15mm}]
+% \node[draw] {checkbox field}
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /Yes\\
+% /Off
+% }}
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /Yes\\
+% /Off
+% }}
+% ;
+% \end{tikzpicture}
+%
+% In radio buttons
+% the off state should still always have the name |/Off|\footnote{%
+% the PDF reference doesn't say anything about this, but various tests showed that
+% one better should stick to this name, with other names the buttons disappeared.}
+% but the on state should be the value. The field dictionary should then set
+% in the |/V| key the start value (this doesn't necessarly mean that the button
+% is also selected from the begin on, this can be set independantly).
+%
+% \begin{tikzpicture}[level 2/.style={level distance=7mm},
+% level 1/.style={sibling distance=25mm},
+% level 2/.style={sibling distance=15mm}]
+% \node[draw,align=left,font=\ttfamily] {\textrm{radio button field}\\/V /value1 }
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /value0 \\
+% /Off
+% }}
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /value1 \\
+% /Off
+% }}
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /value2 \\
+% /Off
+% }}
+% ;
+% \end{tikzpicture}
+%
+% It can be awkward to have to use the values also as names of appearances states, it
+% makes it for example difficult to use unicode for the value names, so there is another
+% option: One can set up an |/Opt| array which contains the values as strings,
+% and used \enquote{named numbers} as appearance state name: The numbers
+% |/0|, |/1| point then to the index position in the array.
+%
+% \begin{tikzpicture}[level 2/.style={level distance=7mm},
+% level 1/.style={sibling distance=25mm},
+% level 2/.style={sibling distance=15mm}]
+% \node[draw,align=left,font=\ttfamily] {\textrm{radio button field}\\
+% /Opt [(value0) (value1) (value2)]
+% /V /0 }
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /0 \\
+% /Off
+% }}
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /1 \\
+% /Off
+% }}
+% child {node[draw,dashed,align=left,font=\ttfamily]
+% {\textrm{annotation}\\ /2 \\
+% /Off
+% }}
+% ;
+% \end{tikzpicture}
+%
+% This method is clearly more flexible, and so it is used in this module.
+%
+% As radio buttons build a group of buttons, there is more interaction going on,
+% and more values have to be set. Also the first command, which initializes the field,
+% has to set the default value of the group.
+%
+% This means a typical setup should do something like this
+%
+% \begin{verbatim}
+% \pdffield_radio:n
+% {
+% group = A, %required, can also be given as name=A or T=A
+% label = button1, %required, on-state of this button
+% default = button4 %default of the group, if not given label (button1) is used
+% %should refer to an existing button!
+% %It will set the V and the DV key
+% %button4 will be checked
+% }
+% \pdffield_radio:n
+% {
+% group = A, %required, can also be given as name=A or T=A
+% label = button2 %required, label/export value of this button
+% }
+% \pdffield_radio:n
+% {
+% group = A, %required, can also be given as name=A or T=A
+% label = button3, %required, label/export value of this button
+% }
+% \pdffield_radio:n
+% {
+% group = A, %required, can also be given as name=A or T=A
+% label = button4 %required, label/export value of this button
+% }
+% \end{verbatim}
+%
+%
+% It is theoretically possible to control the start appearance state for every
+% button so e.g. all
+% buttons could have the \enquote{selected} state when the PDF is opened.
+% But as soon as one button is clicked
+% on you get one selected button and the other are unselected. You can't select or
+% deselect all buttons. The PDF reference mentions an flag |NoToggleToOff| but this
+% doesn't do anything, at least not in the PDF viewers I tried. For this
+% reason this function is currently not supported.
+%
+% If two radio field annotations use the same |label| value they are selected and
+% unselected together, like checkboxes with the same /Yes state. This can be used
+% to build radio groups which works \enquote{in unison}. The flag |RadiosInUnison|
+% is neither needed for this (but doesn't harm either) nor
+% does it change the behaviour, at least again not in the PDF viewers I tried.
+%
+%
+%
+%
% \bigskip
% \subsection{Commands}
-% \begin{function}{\pdffield_checkbox:n}
+% \begin{function}{\pdffield_radio:n}
% \begin{syntax}
-% \cs{pdffield_checkbox:n}\Arg{key val list}
+% \cs{pdffield_radio:n}\Arg{key val list}
% \end{syntax}
% This creates a checkbox to check and uncheck. The list of allowed keys is described below.
% The \meta{key val list} should at least set the name, without it the default name
@@ -265,44 +397,51 @@
% \end{documentation}
%
% \begin{implementation}
-% \section{\pkg{l3pdffield-checkbox} Implementation}
+% \section{\pkg{l3pdffield-radiobutton} Implementation}
% \begin{macrocode}
%<*package>
%<@@=pdffield>
% \end{macrocode}
% \subsection{Variables}
% \begin{macrocode}
+\tl_new:N \l_@@_radio_onstate_tl
+\tl_new:N \l_@@_radio_default_tl
+\int_new:N \l_@@_radio_onstate_num_int
+
% \end{macrocode}
% \subsection{Messages}
% \begin{macrocode}
% \end{macrocode}
% \subsection{Appearances}
-% The default appearances are a cross (\cs{texttimes}),
+% The default appearances are a circle with button in it.
% Every appearance should have two versions and follow the naming
% module/\meta{name}/Yes and module/\meta{name}/Off.
-% \begin{macro}{@@/checkbox/default_appearances:}
+% \begin{macro}{@@/radio/default_appearances:}
% This defines the standard appearance. It is setup at the first
-% use of a checkbox, and will adapt to the font family in use then.
+% use of a radiobutton.
% \begin{macrocode}
-\cs_new_protected:cn {@@/checkbox/default_appearances:}
+\cs_new_protected:cn {@@/radio/default_appearances:}
{
- \pdffield_appearance:nn {pdffield/checkbox/default/Yes}
+ \pdffield_appearance:nn {pdffield/radio/default/Yes}
{
\normalsize
- \fboxsep 0pt
- \framebox
- [ \dim_eval:n { \box_ht:N\strutbox+\box_dp:N\strutbox } ]
- { \texttimes \strut }
+ \usefont{TS1}{cmr}{m}{n}
+ \rule[\dimexpr-\fontchardp\font79-0.5pt]{0pt}{\dimexpr\fontcharwd\font79+1pt}
+ \kern0.5pt
+ \char79
+ \rlap{\kern-0.5\fontcharwd\font 79 \kern-0.5\fontcharwd\font 136 \char136}
+ \kern0.5pt
}
- \pdffield_appearance:nn {pdffield/checkbox/default/Off}
+ \pdffield_appearance:nn {pdffield/radio/default/Off}
{
\normalsize
- \fboxsep 0pt
- \framebox
- [ \dim_eval:n { \box_ht:N\strutbox+\box_dp:N\strutbox } ]
- { \phantom{\texttimes} \strut }
- }
- \cs_gset_eq:cN {@@/checkbox/default_appearances:} \prg_do_nothing:
+ \usefont{TS1}{cmr}{m}{n}
+ \rule[\dimexpr-\fontchardp\font79-0.5pt]{0pt}{\dimexpr\fontcharwd\font79+1pt}
+ \kern0.5pt
+ \char79
+ \kern0.5pt
+ }
+ \cs_gset_eq:cN {@@/radio/default_appearances:} \prg_do_nothing:
}
% \end{macrocode}
% \end{macro}
@@ -310,55 +449,104 @@
%\subsection{Creating the field}
% A field should be created if the name doesn't exist
% \begin{macrocode}
-\cs_new_protected:Npn \@@_checkbox_field:n #1 %name
+\cs_new_protected:Npn \@@_radio_field:n #1 %name
{
- \pdf_object_if_exist:nF {@@/field/@@/checkbox/#1}
+ \pdf_object_if_exist:nF {@@/field/@@/radio/#1}
{
- \@@_field:n { @@/checkbox/#1 }
+% \end{macrocode}
+% We need an object and a seq for the Opt array.
+% The object is written at the end of the document.
+% \begin{macrocode}
+ \pdf_object_new:nn {@@/field/@@/radio-Opt/#1}{array}
+ \pdfdict_put:nnx { l_@@/field }{Opt} { \pdf_object_ref:n {@@/field/@@/radio-Opt/#1} }
+ \seq_new:c { g_@@_radio_opt_#1_seq }
+ \hook_gput_code:nnn {shipout/lastpage}{pdffield/radio}
+ {
+ \pdf_object_write:nx
+ {@@/field/@@/radio-Opt/#1}
+ {\seq_use:cn {g_@@_radio_opt_#1_seq}{~}}
+ }
+% \end{macrocode}
+% The default value is always the first in the array.
+% If a default has been set, it is added to the left
+% of the seq.
+% \begin{macrocode}
+ \tl_if_eq:NnF \l_@@_radio_default_tl{()}
+ {
+ \seq_gput_left:cV { g_@@_radio_opt_#1_seq }\l_@@_radio_default_tl
+ }
+ \pdfdict_put:nnx { l_@@/field }{V} { /0 }
+ %\pdfdict_put:nnx { l_@@/field }{DV} { /0 }
+% \end{macrocode}
+% now we create the field and set it as parent for the following annotation.
+% \begin{macrocode}
+ \@@_field:n { @@/radio/#1 }
}
- \keys_set:nn {pdffield}{parent=@@/checkbox/#1}
+ \keys_set:nn {pdffield}{parent=@@/radio/#1}
}
-\cs_generate_variant:Nn \@@_checkbox_field:n {V}
+\cs_generate_variant:Nn \@@_radio_field:n {V}
% \end{macrocode}
-% \subsection{Assembling the checkbox}
+% \subsection{Assembling the radio}
-% \begin{macro}{\@@_checkbox:n}
+% \begin{macro}{\@@_radio:n}
% \begin{macrocode}
-\cs_new_protected:Npn \@@_checkbox:n #1
+\cs_new_protected:Npn \@@_radio:n #1
{
\group_begin:
- \use:c {@@/checkbox/default_appearances:}
- \cs_set_eq:NN\@@_appearance_handler:nnn \@@_checkbox_appearance_handler:nnn
+ \use:c {@@/radio/default_appearances:}
+ \cs_set_eq:NN\@@_appearance_handler:nnn \@@_radio_appearance_handler:nnn
% \end{macrocode}
-% Setting up the defaults.
-% \begin{macrocode}
- \keys_set:nn {pdffield}
+% Setting up the defaults. To setup the appearance we need the
+% Opt array, so at first the non-annot-keys are set and the
+% field is created.
+% \begin{macrocode}
+ \keys_set_filter:nnn {pdffield}{annot}
{
- fieldID=,
- name=checkbox,
- appearance = pdffield/checkbox/default,
- checked=false,
- width = \normalbaselineskip,
- height = \normalbaselineskip,
+ fieldID=
+ ,name=radio
+ ,width = \normalbaselineskip
+ ,height = \normalbaselineskip
+ ,#1
}
-% \end{macrocode}
-% Value keys should be undefined.
-% \begin{macrocode}
- \@@_key_disable:nnn{checkbox}{V}{checked}
- \@@_key_disable:nnn{checkbox}{DV}{checked}
- \@@_key_disable:nnn{checkbox}{AS}{checked}
- \keys_set:nn { pdffield }{@@/preset/checkbox,#1}
\keys_set:nn { pdffield }
{
- ,unsetFf={Radio,Pushbutton}
+ ,unsetFf={Pushbutton}
+ ,setFf={Radio}
,FT= Btn
}
- \tl_if_empty:NT\l_@@_fieldID_tl
+ \tl_if_empty:NT\l_@@_fieldID_tl
{
\pdfdict_get:nnN {l_@@/field}{T}\l_@@_fieldID_tl
- \tl_put_left:Nn \l_@@_fieldID_tl {@@/checkbox/}
+ \tl_put_left:Nn \l_@@_fieldID_tl {@@/radio/}
+ }
+ \@@_radio_field:V\l_@@_fieldID_tl
+% \end{macrocode}
+%
+% \begin{macrocode}
+ \seq_if_in:cVF { g_@@_radio_opt_ \l_@@_fieldID_tl _seq }\l_@@_radio_onstate_tl
+ {
+ \seq_gput_right:cV { g_@@_radio_opt_ \l_@@_fieldID_tl _seq }\l_@@_radio_onstate_tl
}
- \@@_checkbox_field:V\l_@@_fieldID_tl
+ \int_zero:N \l_@@_radio_onstate_num_int
+ \exp_args:Nc
+ \seq_map_inline:Nn { g_@@_radio_opt_ \l_@@_fieldID_tl _seq }
+ {
+ \str_if_eq:nVTF { ##1 } \l_@@_radio_onstate_tl
+ {
+ \seq_map_break:
+ }
+ {
+ \int_incr:N \l_@@_radio_onstate_num_int
+ }
+ }
+ \keys_set_groups:nnn { pdffield }{annot}
+ {
+ ,appearance = pdffield/radio/default
+ ,#1
+ }
+ \int_compare:nNnTF { \l_@@_radio_onstate_num_int } = 0
+ { \pdfannot_dict_put:nnx {widget}{AS}{/0} }
+ { \pdfannot_dict_put:nnx {widget}{AS}{/Off} }
\@@_annot:
\group_end:
}
@@ -367,26 +555,19 @@
%
% \subsection{Keys}
% Most keys are inherited simply the ones from the generic field and annot keys.
-% A key to decide if the box is initially checked or not.
-% We stay in the same family so that we can build a style.
+% The label keys sets the export value. default the button which is checked on.
+% It is in the annot group, so that it can be set after the label.
% \begin{macrocode}
-\keys_define:nn { pdffield }
+\keys_define:nn { pdffield }
{
- ,checked .choice:
- ,checked / false .code:n =
- {
- \pdfdict_put:nnx { l_@@/field }{V} { /Off }
- \pdfdict_put:nnx { l_@@/field }{DV}{ /Off }
- \pdfannot_dict_put:nnn {widget}{AS}{ /Off }
- }
- ,checked / true .code:n =
- {
- \pdfdict_put:nnx { l_@@/field }{V} { /Yes }
- \pdfdict_put:nnx { l_@@/field }{DV}{ /Yes }
- \pdfannot_dict_put:nnn {widget}{AS}{ /Yes }
- }
- ,checked .default:n = {true}
- ,checked .groups:n = {checkbox}
+ label .code:n =
+ {
+ \pdf_string_from_unicode:nnN {utf8/string}{#1}\l_@@_radio_onstate_tl
+ }
+ ,default .code:n =
+ {
+ \pdf_string_from_unicode:nnN {utf8/string}{#1}\l_@@_radio_default_tl
+ }
}
% \end{macrocode}
% And a key to set a dedicated field ID
@@ -396,23 +577,20 @@
fieldID .tl_set:N = \l_@@_fieldID_tl
}
% \end{macrocode}
-% \begin{macro}{\@@_checkbox_appearance_handler:nnn}
+% \begin{macro}{\@@_radio_appearance_handler:nnn}
% \begin{macrocode}
-\cs_new_protected:Npn \@@_checkbox_appearance_handler:nnn #1 #2 #3 %name, type, text
+
+\cs_new_protected:Npn \@@_radio_appearance_handler:nnn #1 #2 #3 %name, type, text
{
- \pdfxform_if_exist:nTF { #1/Yes }
+ \pdfxform_if_exist:nTF { #1 / Yes }
{
- \pdf_object_if_exist:nF {@@/checkbox/AP/#1}
+ \pdf_object_unnamed_write:nx
+ {dict}
{
- \pdf_object_new:nn {@@/checkbox/AP/#1}{dict}
- \pdf_object_write:nx
- {@@/checkbox/AP/#1}
- {
- /Yes ~ \pdfxform_ref:n { #1/Yes}
- /Off ~ \pdfxform_ref:n { #1/Off}
- }
+ /\int_use:N \l_@@_radio_onstate_num_int \c_space_tl \pdfxform_ref:n { #1/Yes}
+ /Off ~ \pdfxform_ref:n { #1/Off}
}
- \pdfannot_dict_put:nnx {widget/AP}{#2}{\pdf_object_ref:n{@@/checkbox/AP/#1}}
+ \pdfannot_dict_put:nnx {widget/AP}{#2}{\pdf_object_ref_last:}
}
{
\msg_error:nnnn{pdffield}{appearance-missing}{#1}{#3}
@@ -424,9 +602,9 @@
% \end{macro}
%
% \subsection{user commands}
-% \begin{macro}{\pdffield_checkbox:n}
+% \begin{macro}{\pdffield_radio:n}
% \begin{macrocode}
-\cs_set_eq:NN \pdffield_checkbox:n \@@_checkbox:n
+\cs_set_eq:NN \pdffield_radio:n \@@_radio:n
%</package>
% \end{macrocode}
% \end{macro}
diff --git a/l3pdffield.dtx b/l3pdffield.dtx
index e1e5f8b..df8a3d7 100644
--- a/l3pdffield.dtx
+++ b/l3pdffield.dtx
@@ -1381,6 +1381,7 @@
}
,AP/N .groups:n = annot
,appearance .meta:n = {AP/N={#1}}
+ ,appearance .groups:n = annot
}
\keys_define:nn { pdffield }
{
@@ -1396,6 +1397,7 @@
}
,AP/R .groups:n = annot
,rollover-appearance .meta:n = {AP/R={#1}}
+ ,rollover-appearance .groups:n = annot
}
\keys_define:nn { pdffield }
{
@@ -1411,6 +1413,7 @@
}
,AP/D .groups:n = annot
,down-appearance .meta:n = {AP/D={#1}}
+ ,down-appearance .groups:n = annot
}
\keys_define:nn { pdffield }
diff --git a/pdfmanagement-testphase.ins b/pdfmanagement-testphase.ins
index 97db2f0..db1b6b0 100644
--- a/pdfmanagement-testphase.ins
+++ b/pdfmanagement-testphase.ins
@@ -129,6 +129,7 @@ and all files in that bundle must be distributed together.
{%
\from{l3pdffield.dtx}{package}
\from{l3pdffield-checkbox.dtx}{package}
+ \from{l3pdffield-radiobutton.dtx}{package}
\from{l3pdffield-textfield.dtx}{package}
}%
}
More information about the latex3-commits
mailing list.