summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/ocgx2/ocgbase.sty
blob: aa24adfc294b52362c0d0aee7d6a2bd2526c2554 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ocgbase.sty
%
% low-level macros for OCG creation and for managment of global (document-wide)
% OCG related lists;
% (automatic) OCG configuration in the PDF catalog
%
% Copyright 2015--\today, Alexander Grahn
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Support package for ocgx2.sty, media9.sty, animate.sty
%
% Supported workflows:
%
%   pdflatex, lualatex
%   latex-->dvips-->ps2pdf or Distiller
%   latex-->dvipdfmx
%   xelatex
%
%
% Package options:
%
%   xetex,
%   dvipdfmx
%
%
% Commands defined:
%
%   \ocgbase_new_ocg:xxx
%   \ocgbase@new@ocg (LaTeX2e version)
%     #1: name (as shown in the Layers Tab of the Reader GUI)
%     #2: usage dict (may be empty), see PDF reference:
%         http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/
%                pdf_reference_1-7.pdf#G9.3858276
%     #3: initial visibility (1|0|true|false|on|off)
%
%   \ocgbase_last_ocg:
%   \ocgbase@last@ocg (LaTeX2e version)
%     inserts ID of PDF object created during most recent call of
%     \ocgbase_new_ocg:xxx
%
%   --------
%
%   \ocgbase_add_to_order_list:n
%   \ocgbase@add@to@order@list (LaTeX2e version)
%     #1: PDF object ID of OCG
%     Only OCGs added to the global `Order' list using this command will show
%     up in the Layers Tab
%
%   --------
%
%   \ocgbase_add_to_off_list:n
%   \ocgbase@add@to@off@list (LaTeX2e version)
%     #1: PDF object ID of OCG
%     macro for belated setting of initial non-visibility of an OCG
%
%   --------
%
%   \ocgbase_add_ocg_to_radiobtn_grp:xxx
%   \ocgbase@add@ocg@to@radiobtn@grp
%     add OCG #2 (obj ref) to radio button group `#1' (string),
%     #3: (0|1|false|true) list OCG as part of group `#1' in the Layers Tab)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 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://mirrors.ctan.org/help/Catalogue/licenses.lppl.html
% 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 A. Grahn.

\RequirePackage{expl3}
\RequirePackage{atenddvi}

\def\g@ocgbase@date@tl{2015/10/28}
\def\g@ocgbase@version@tl{0.1}

\ProvidesExplPackage{ocgbase}{\g@ocgbase@date@tl}{\g@ocgbase@version@tl}
{support package for ocgx2.sty}

%unknown package option error message
\msg_set:nnnn{ocgbase}{unknown~package~option}{Unknown~package~option~`#1'.}{
  Package option~'#1'~is~unknown;\\
  perhaps~it~is~spelled~incorrectly.
}

%package opts
\keys_define:nn{ocgbase}{
  xetex .code:n = {\PassOptionsToPackage{xetex}{pdfbase}},
  dvipdfmx .code:n = {\PassOptionsToPackage{dvipdfmx}{pdfbase}}
}

%process package options
\ProcessKeysOptions{ocgbase}

\AtBeginDocument{
  \@ifpackageloaded{pdfbase}{}{\RequirePackage{pdfbase}}
}

\tl_new:N\g_ocgbase_ocgs_tl %takes ocg object refs
\tl_new:N\g_ocgbase_offocgs_tl
\seq_new:N\g_ocgbase_ocgorder_seq

\AtEndDvi{
  \tl_if_empty:NF\g_ocgbase_ocgs_tl{
    %global OCG array
    \pbs_pdfobj:nnn{}{array}{\g_ocgbase_ocgs_tl}
    \tl_set:Nx\l_ocgbase_ocgarray_tl{\pbs_pdflastobj:}
    \tl_new:N\l_ocgbase_offocgentry_tl
    %global OFF list
    \tl_if_empty:NF\g_ocgbase_offocgs_tl{
      \pbs_pdfobj:nnn{}{array}{\g_ocgbase_offocgs_tl}
      \tl_set:Nx\l_ocgbase_offocgentry_tl{/OFF~\pbs_pdflastobj:}
    }
    %build Order list
    \tl_new:N\l_ocgbase_ocgorderentry_tl
    \seq_if_empty:NF\g_ocgbase_ocgorder_seq{
      %process radio button groups in the Order array
      \seq_map_inline:Nn\g_ocgbase_ocgorder_seq{
        \cs_if_exist:cT{g_ocgbase_#1_rbtn_order_seq}{
          \int_compare:nTF{\seq_count:c{g_ocgbase_#1_rbtn_groups_seq}>\c_one}{
            \int_compare:nT{\seq_count:c{g_ocgbase_#1_rbtn_order_seq}>\c_zero}{
              %remove all occurrences in Order array outside current rbtn group
              \seq_map_inline:cn{g_ocgbase_#1_rbtn_order_seq}{
                \seq_gremove_all:Nn\g_ocgbase_ocgorder_seq{##1}
              }
            }
          }{
            %ignore single-item radio button groups
            \seq_gremove_all:Nn\g_ocgbase_ocgorder_seq{#1}
          }
        }
      }
      %second sweep on Order array to generate the final Order entry
      \tl_new:N\l_ocgbase_ocgorder_tl
      \seq_map_inline:Nn\g_ocgbase_ocgorder_seq{
        \cs_if_exist:cTF{g_ocgbase_#1_rbtn_order_seq}{
          \int_compare:nT{\seq_count:c{g_ocgbase_#1_rbtn_order_seq}>\c_zero}{
            \tl_put_right:Nx\l_ocgbase_ocgorder_tl{
              ~[(#1)~\seq_use:cn{g_ocgbase_#1_rbtn_order_seq}{~}]
            }
          }
        }{
          %ordinary (non-radiobutton group) item (ocg obj ref, `[', `]')
          \tl_put_right:Nn\l_ocgbase_ocgorder_tl{~#1}
        }
      }
      \tl_if_empty:NF\l_ocgbase_ocgorder_tl{
        \pbs_pdfobj:nnn{}{array}{\l_ocgbase_ocgorder_tl}
        \tl_set:Nx\l_ocgbase_ocgorderentry_tl{/Order~\pbs_pdflastobj:}
      }
    }
    %generate RBGroups entry (radio button groups)
    \tl_new:N\l_ocgbase_rbgroups_tl
    \seq_map_inline:Nn\g_ocgbase_ocgorder_seq{
      \cs_if_exist:cT{g_ocgbase_#1_rbtn_groups_seq}{
        \int_compare:nT{\seq_count:c{g_ocgbase_#1_rbtn_groups_seq}>\c_one}{
          \tl_put_right:Nx\l_ocgbase_rbgroups_tl{
            ~[\seq_use:cn{g_ocgbase_#1_rbtn_groups_seq}{~}]
          }
        }
      }
    }
    \tl_new:N\l_ocgbase_rbgroupsentry_tl
    \tl_if_empty:NF\l_ocgbase_rbgroups_tl{
      \pbs_pdfobj:nnn{}{array}{\l_ocgbase_rbgroups_tl}
      \tl_set:Nx\l_ocgbase_rbgroupsentry_tl{/RBGroups~\pbs_pdflastobj:}
    }
    \pbs_pdfcatalog:n{
      /OCProperties~<<
        /OCGs~\l_ocgbase_ocgarray_tl
        /D~<<
          /AS~[
            <</Event/View  /Category[/View]  /OCGs~\l_ocgbase_ocgarray_tl>>
            <</Event/Print /Category[/Print] /OCGs~\l_ocgbase_ocgarray_tl>>
            <</Event/Export/Category[/Export]/OCGs~\l_ocgbase_ocgarray_tl>>
          ]
          /BaseState/ON~\l_ocgbase_offocgentry_tl
          \l_ocgbase_ocgorderentry_tl
          \l_ocgbase_rbgroupsentry_tl
          /ListMode/VisiblePages
        >>
      >>
    }
  }
}

%macro for inserting new OCG object
\cs_new:Nn\ocgbase_new_ocg:nnn{
  \pbs_pdfobj:nnn{}{dict}{
    /Type/OCG/Name~(#1)~\tl_if_blank:oF{#2}{/Usage<<#2>>}
  }
  \tl_gput_right:Nx\g_ocgbase_ocgs_tl{~\g_pbs_pdflastobj_tl}
  \bool_if:nT{
    \str_if_eq_p:nn{#3}{\c_zero} ||
    \str_if_eq_p:nn{#3}{off} ||
    \str_if_eq_p:nn{#3}{false}
  }{
    \tl_gput_right:Nx\g_ocgbase_offocgs_tl{~\g_pbs_pdflastobj_tl}
    \tl_new:c{off:\g_pbs_pdflastobj_tl}
  }
  \tl_gset_eq:NN\g_ocgbase_last_ocg_tl\g_pbs_pdflastobj_tl
}
\cs_generate_variant:Nn\ocgbase_new_ocg:nnn{xxx}

\cs_new:Nn\ocgbase_last_ocg:{\g_ocgbase_last_ocg_tl}

%macro for appending OCG object to global `Order' list
% in Layers Tab of the GUI)
\cs_new:Nn\ocgbase_add_to_order_list:n{
  \seq_gput_right:Nx\g_ocgbase_ocgorder_seq{#1}
}

%macro for belated appending an OCG object ref to the global `OFF' list
%(initial non-visibility)
\cs_new:Nn\ocgbase_add_to_off_list:n{
  \tl_if_exist:cF{off:#1}{
    \tl_gput_right:Nx\g_ocgbase_offocgs_tl{~#1}\tl_new:c{off:#1}
  }
}

\cs_new:Nn\ocgbase_add_ocg_to_radiobtn_grp:nnn{
  % #1: rbtn group name,
  % #2: OCG obj ref,
  % #3: visible in Layers Tab
  \seq_if_in:NnF\g_ocgbase_ocgorder_seq{#1}{
    \seq_gput_right:Nn\g_ocgbase_ocgorder_seq{#1}
    \seq_new:c{g_ocgbase_#1_rbtn_order_seq}
    \seq_new:c{g_ocgbase_#1_rbtn_groups_seq}
  }
  \seq_if_in:cnTF{g_ocgbase_#1_rbtn_order_seq}{#2}{
    \bool_if:nT{
      \str_if_eq_p:nn{#3}{\c_zero} ||
      \str_if_eq_p:nn{#3}{false}
    }{
      \seq_gremove_all:cn{g_ocgbase_#1_rbtn_order_seq}{#2}
    }
  }{
    \bool_if:nF{
      \str_if_eq_p:nn{#3}{\c_zero} ||
      \str_if_eq_p:nn{#3}{false}
    }{
      \seq_gput_right:cn{g_ocgbase_#1_rbtn_order_seq}{#2}
    }
  }
  \seq_gput_right:cn{g_ocgbase_#1_rbtn_groups_seq}{#2}
}
\cs_generate_variant:Nn\ocgbase_add_ocg_to_radiobtn_grp:nnn{xxx}

%l2e versions
\cs_gset_eq:NN\ocgbase@new@ocg\ocgbase_new_ocg:xxx
\cs_gset_eq:NN\ocgbase@last@ocg\ocgbase_last_ocg:
\cs_gset_eq:NN\ocgbase@add@to@order@list\ocgbase_add_to_order_list:n
\cs_gset_eq:NN\ocgbase@add@to@off@list\ocgbase_add_to_off_list:n
\cs_gset_eq:NN\ocgbase@add@ocg@to@radiobtn@grp\ocgbase_add_ocg_to_radiobtn_grp:xxx