[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.