% \iffalse meta-comment
%
% File: newpax.dtx
% Copyright 2006-2008, 2011, 2012 Heiko Oberdiek (original pax.sty)
% Copyright (C) 2021--2023 Ulrike Fischer
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    https://www.latex-project.org/lppl.txt
%
% This file is part of the "newpax bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% -----------------------------------------------------------------------
%
% The development version of the bundle can be found at
%
%    https://github.com/u-fischer/newpax
%
% for those people who are interested.
%
% \fi
%
%    \begin{macrocode}
%<*package>
%<@@=newpax>
\NeedsTeXFormat{LaTeX2e}[2022-11-01]
\ProvidesPackage{newpax}%
  [2025-09-24 v0.57 Annotation support for PDF graphics based on pax.sty adapted by (UF)]%
%    \end{macrocode}
% Test if the pdfmanagement is loaded:
%    \begin{macrocode}
\ExplSyntaxOn
\IfPDFManagementActiveTF 
  {} 
  {  %error for now, perhaps warning later.
    \PackageError{newpax}
     {
       PDF~resource~management~code~not~found!\MessageBreak
       newpax~will~no~work.
     }
     {
       Load~it~with \MessageBreak
       \string\RequirePackage{pdfmanagement}\MessageBreak
       before~loading~the~class
     }
  }

\ExplSyntaxOff
\RequirePackage{graphicx}
\RequirePackage{etoolbox}

\ExplSyntaxOn
%    \end{macrocode}
% \subsection{Variables}
% Variables inherited from the pax code use \cs{NEWPAX}
% as prefix.
% \begin{variable}{\l_@@_tmpa_box,\g_@@_tmpa_tl}
%    \begin{macrocode}
\box_new:N \l_@@_tmpa_box
\tl_new:N\g_@@_tmpa_tl
%    \end{macrocode}
% \end{variable}

% \begin{variable}{ \l_@@_use_attributes_bool}
% Used by the \texttt{usefileattributes} key.
%    \begin{macrocode}
\bool_new:N \l_@@_use_attributes_bool
%    \end{macrocode}
% \end{variable}
% 
% \begin{variable}{ \l_@@_addannots_bool}
% Used by the \texttt{addannots} key. 
%    \begin{macrocode}
\bool_new:N \l_@@_addannots_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{ \l_@@_dests_all_bool}
% Used by the \texttt{dests} key. 
% if true newpax will insert more destinations.
%    \begin{macrocode}
\bool_new:N \l_@@_dests_all_bool
%    \end{macrocode}
% \end{variable}

% \begin{variable}{ \l_@@_destsuffic_tl}
% Used by the \texttt{addannots} key. 
%    \begin{macrocode}
\tl_new:N   \l_@@_destsuffix_tl
%    \end{macrocode}
% \end{variable}
% 
% \begin{variable}{\NEWPAX@fileextension}
% the extension of the generated file, the default it newpax.
%    \begin{macrocode}
\tl_new:N \NEWPAX@fileextension
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\NEWPAX@Gin@box@opts}
% this hold the options of a annotation
%    \begin{macrocode}
\tl_new:N \NEWPAX@Gin@box@opts
%    \end{macrocode}
% \end{variable}
% 
% \begin{macro}{\ifNEWPAX@Gin@clip}
%    \begin{macrocode}
\newif\ifNEWPAX@Gin@clip
%    \end{macrocode}
% \end{macro}
%
% \subsection{Variants}
% 
% \begin{macro}{\newpax@str@if@eq@@@@nnT}
% a latex2e variant of \cs{str_if_eq:nnT} to replace 
% \cs{pdf@strcmp} 
%    \begin{macrocode}
\cs_set_eq:NN \newpax@str@if@eq@@@@nnT\str_if_eq:nnT
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\cs_generate_variant:Nn \pdfannot_dict_put:nnn {nnx,xnx,xnn}
\cs_generate_variant:Nn \pdfannot_link_goto_begin:nw {xw}
\cs_generate_variant:Nn \pdf_destination:nn {xx}
%    \end{macrocode}
%
% \subsection{User setup command}
%    \begin{macrocode}
\NewDocumentCommand\newpaxsetup { m }
  {
    \keys_set:nn {newpax}{ #1}
  }

\keys_define:nn {newpax}
  {
    usefileattributes .bool_set:N = \l_@@_use_attributes_bool,
    destsuffix        .code:n     = {\tl_set:Nn \l_@@_destsuffix_tl{@#1}},
    addannots         .bool_set:N = \l_@@_addannots_bool,
    addannots         .default:n  = true,
    addannots         .initial:n  = true,
    paxextension      .choices:nn =
       {pax,newpax}
       {\tl_set:Nn \NEWPAX@fileextension {#1}},
    paxextension .initial:n = newpax,
    dests             .choice:, 
    dests/all         .code:n = {\bool_set_true:N \l_@@_dests_all_bool},
    dests/used        .code:n = {\bool_set_false:N \l_@@_dests_all_bool},
    dests             .initial:n = used
  }

%    \end{macrocode}
% \subsection{Helper commands to create the annotations}
% 
% \begin{macro}{\@NEWPAX@setattributes@n}
% This adds or removes attributes from the annot dictionaries.
% The argument is a link type like URI or Goto.
%    \begin{macrocode}
\cs_new_protected:Npn \@NEWPAX@setattributes@n #1 %link type
  {
    \bool_if:NT \l_@@_use_attributes_bool
      {
        \tl_if_empty:NTF  \NEWPAX@key@C
          {
            \pdfannot_dict_remove:nn {link/#1} { C }
          }
          {
            \pdfannot_dict_put:nnx
              {link/#1}
              { C }
              { \NEWPAX@key@C }
          }
        \tl_if_empty:NTF \NEWPAX@key@Border
          {
            \pdfannot_dict_remove:nn {link/#1} { Border }
          }
          {
            \pdfannot_dict_put:nnx
              {link/#1}
              { Border }
              { \NEWPAX@key@Border }
          }
        \tl_if_empty:NTF \NEWPAX@key@BS
          {
            \pdfannot_dict_remove:nn {link/#1} { BS }
          }
          {
            \pdfannot_dict_put:nnx
              {link/#1}
              { BS }
              { \NEWPAX@key@BS }
          }
        \tl_if_empty:NTF \NEWPAX@key@H
          {
            \pdfannot_dict_remove:nn {link/#1} { H }
          }
          {
            \pdfannot_dict_put:nnx
              {link/#1}
              { H }
              { \NEWPAX@key@H }
          }
      }
  }

%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@NEWPAX@linkgoto@xnn}
% this creates a goto link to the destination given in the first argument,
% and the size given in the second and third argument.
%    \begin{macrocode}
\cs_new_protected:Npn \@NEWPAX@linkgoto@xnn #1 #2 #3  %#1 dest #2 width #3 height
  {
    \group_begin:
      \@NEWPAX@setattributes@n {GoTo}
      \leavevmode
      \pdfannot_link_goto_begin:xw
        { #1 }
        \@NEWPAX@ensurelinkbox@n{\hbox_to_wd:nn {#2}{ { \rule{0pt}{#3} }\hfill}}
      \pdfannot_link_goto_end:
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@NEWPAX@link@setaction@nn}
% This puts the action into the dict 
% if the annot is a link. \#1 is a type like 
% URI or Goto, \#2 the actual action.
%    \begin{macrocode}
\cs_new_protected:Npn \@NEWPAX@link@setaction@nn #1 #2
 {
    \pdfannot_dict_put:nnn{link/#1}{Subtype}{/Link}
    \pdfannot_dict_put:nnx{link/#1}{A}
       {<</Type/Action/S/#1 #2>>}
 }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@NEWPAX@annotboxlink@nnn}
% This creates an annotation link box. 
%    \begin{macrocode}
\cs_new_protected:Npn \@NEWPAX@annotboxlink@nnn #1 #2 #3   %#1 type, #2 width #3 height
  {
    \group_begin:
      \@NEWPAX@setattributes@n {#1}
      \leavevmode
      \pdfannot_box:nnnx {#2}{#3}{0pt}
         {\pdfannot_dict_use:n{link/#1}}
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@NEWPAX@destination@xx}
% This creates a destination. The first argument is the 
% name, the second the placement type (e.g. fit or xyz). 
%    \begin{macrocode}
\cs_new_protected:Npn \@NEWPAX@destination@xx #1 #2
  {
    \pdf_destination:xx {#1}{#2}
  }
%    \end{macrocode}
% \end{macro}
% 
%
% \begin{macro}{\@NEWPAX@ensurelinkbox@n}
% XeTeX needs in some places a specific box to ensure the size.
% TODO: check the mailing list for the alternative. 
%    \begin{macrocode}
\cs_new_eq:NN \@NEWPAX@ensurelinkbox@n \use:n
\AddToHook{package/hyperref/after}
  {
    \sys_if_engine_xetex:T
      {
        \cs_set_eq:NN \@NEWPAX@ensurelinkbox@n \XeTeXLinkBox
      }
  } 
%    \end{macrocode}
% \end{macro}
%
% \subsection{Handling graphics commands}
% We must redefine \cs{includegraphics} to inject the code which
% reinserts the annotation. This can't be done with hooks as the 
% standard code splits everything too much.
% 
% \begin{macro}{\NEWPAX@ORG@includegraphics}
%  At first we redefine \cs{includegraphics} to use our new command.
%    \begin{macrocode}
\NewCommandCopy\NEWPAX@ORG@includegraphics\includegraphics
\RenewDocumentCommand\includegraphics{sO{}m}
  {
    \IfBooleanTF {#1}
      {
        \NEWPAX@includegraphics[clip,#2]{#3} 
      }
      {
        \NEWPAX@includegraphics[#2]{#3} 
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\NEWPAX@includegraphics}
%    \begin{macrocode}
\def\NEWPAX@includegraphics[#1]#2{%
  \group_begin:
    \hbox_set:Nn \l_@@_tmpa_box {\NEWPAX@ORG@includegraphics[{#1}]{#2}}%
    \tl_set:Nx \NEWPAX@inc@width  
      { \dim_eval:n { \box_wd:N \l_@@_tmpa_box } }%
    \tl_set:Nx \NEWPAX@inc@height 
      { \dim_eval:n { \box_dp:N \l_@@_tmpa_box + \box_ht:N \l_@@_tmpa_box } }%
    \mode_leave_vertical:
    \hbox_to_wd:nn 
     { \box_wd:N \l_@@_tmpa_box }
     { 
      \hbox_overlap_right:n { \box_use:N \l_@@_tmpa_box }
      \bool_if:nT
        { \l_@@_addannots_bool
           &&
           (
            \int_compare_p:nNn
             {
               \cs_if_exist:NTF\pdfpages@includegraphics@status
                 {
                    \pdfpages@includegraphics@status
                 }{0}
             }
             <
             {2}
           )
        }
        {
          \box_move_down:nn 
           {\box_dp:N \l_@@_tmpa_box } 
           {
             \hbox:n
               {
                \NEWPAX@AddAnnots{#1}{#2}%
               }
           } 
        }
      \hfill
    }
  \group_end:
}
%    \end{macrocode}
% \end{macro}
% 
% \subsection{Handling the newpax file}
% The following keys were previously defined
% with kvoptions, the names of the internal commands are kept for now.
% The keys are used in the optional argument of \cs{includegraphics} 
% and extend their functions.
%    \begin{macrocode}
\keys_define:nn {newpax/Gin}
 {
   ,page  .tl_set:N = \NEWPAX@Gin@page
   ,page  .initial:n = 1
   ,angle .tl_set:N = \NEWPAX@Gin@angle
   ,angle .initial:n = 0
   ,clip  .legacy_if_set:n = NEWPAX@Gin@clip
   ,viewport .code:n = 
     {
       \tl_put_right:Nn\NEWPAX@Gin@box@opts
         {\NEWPAX@viewport#1\\}
     }
   ,trim .code:n = 
     {
       \tl_put_right:Nn\NEWPAX@Gin@box@opts
        {\NEWPAX@trim#1\\}
     } 
   ,unknown .code:n = {}     
 }
\ExplSyntaxOff

%    \end{macrocode}
%
% Calculate coordinates 
% \begin{macro}{\NEWPAX@viewport,\NEWPAX@trim,\NEWPAX@defaultbp,\NEWPAX@def@bp}
%    \begin{macrocode}
\def\NEWPAX@viewport#1 #2 #3 #4\\{%
  \NEWPAX@defaultbp\NEWPAX@vllx{#1}%
  \NEWPAX@defaultbp\NEWPAX@vlly{#2}%
  \NEWPAX@defaultbp\NEWPAX@vurx{#3}%
  \NEWPAX@defaultbp\NEWPAX@vury{#4}%
  \edef\NEWPAX@page@llx{\dimexpr\NEWPAX@page@llx+\NEWPAX@vllx\relax}%
  \edef\NEWPAX@page@lly{\dimexpr\NEWPAX@page@lly+\NEWPAX@vlly\relax}%
  \edef\NEWPAX@page@urx{\dimexpr\NEWPAX@page@llx+\NEWPAX@vlly\relax}%
  \edef\NEWPAX@page@ury{\dimexpr\NEWPAX@page@lly+\NEWPAX@vury\relax}%
}
\def\NEWPAX@trim#1 #2 #3 #4\\{%
  \NEWPAX@defaultbp\NEWPAX@tllx{#1}%
  \NEWPAX@defaultbp\NEWPAX@tlly{#2}%
  \NEWPAX@defaultbp\NEWPAX@turx{#3}%
  \NEWPAX@defaultbp\NEWPAX@tury{#4}%
  \edef\NEWPAX@page@llx{\dimexpr\NEWPAX@page@llx+\NEWPAX@tllx\relax}%
  \edef\NEWPAX@page@lly{\dimexpr\NEWPAX@page@lly+\NEWPAX@tlly\relax}%
  \edef\NEWPAX@page@urx{\dimexpr\NEWPAX@page@urx-\NEWPAX@turx\relax}%
  \edef\NEWPAX@page@ury{\dimexpr\NEWPAX@page@ury-\NEWPAX@tury\relax}%
}
\def\NEWPAX@defaultbp#1#2{%
  \afterassignment\NEWPAX@def@bp\dimen@#2bp\relax{#1}{#2}%
}
\def\NEWPAX@def@bp#1\relax#2#3{%
  \if!#1!%
    \edef#2{#3bp}%
  \else
    \edef#2{#3}%
  \fi
}
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\NEWPAX@AddAnnots}
% This command is the main command that reads and parses the newpax file. 
%    \begin{macrocode}
\def\NEWPAX@AddAnnots#1#2{%
  \SetKeys[newpax/Gin]{#1}%
  \Grot@setangle{\NEWPAX@Gin@angle}%
  % a little careful, is type of angle int or real?
  \loop
  \ifdim\NEWPAX@Gin@angle\p@<360\p@
  \else
    \edef\NEWPAX@Gin@angle{\the\numexpr-360+\number\NEWPAX@Gin@angle}%
  \repeat
  \loop
  \ifdim\NEWPAX@Gin@angle\p@<\z@
    \edef\NEWPAX@Gin@angle{\strip@pt\dimexpr\NEWPAX@Gin@angle\p@+360\p@}%
  \repeat
  \ifcase0\ifnum\NEWPAX@Gin@angle=0 1\fi
          \ifnum\NEWPAX@Gin@angle=90 1\fi
          \ifnum\NEWPAX@Gin@angle=180 1\fi
          \ifnum\NEWPAX@Gin@angle=270 1\fi
    \PackageWarning{newpax}{Unsupported value for option angle}%
  \fi
  \filename@parse{#2}%
  \def\NEWPAX@file{%\filename@area
  \filename@base.\NEWPAX@fileextension }%
  \let\[\NEWPAX@parser
  \def\<{<}%
  \def\>{>}%
  \endlinechar=-1 %
  \begingroup
  \catcode`\#=12 \catcode`\%=12
  \InputIfFileExists\NEWPAX@file{}{\typeout{* Missing: \NEWPAX@file}}\endgroup
}
%    \end{macrocode}
% \end{macro}
% The parsing commands
% With \cs{NEWPAX@parser}  normally \cs{NEWPAX@cmd@XXX} is called, where
% XXX is file, pagenum, annot, page or dest.  
%    \begin{macrocode}
\def\NEWPAX@parser#1{\NEWPAX@call{cmd}{#1}{}}%
%    \end{macrocode}
%    \begin{macrocode}
\def\NEWPAX@call#1#2#3{%
  \@ifundefined{NEWPAX@#1@#2}\NEWPAX@skip{#3\csname NEWPAX@#1@#2\endcsname}%
}
\def\NEWPAX@skip#1\\{}
\def\NEWPAX@stop#1\\{}

%    \end{macrocode}
% The command to handle the page entry.
%    \begin{macrocode}
\def\NEWPAX@cmd@page#1#2{%
  \NEWPAX@filter@page{#1}{%
    \NEWPAX@getrect{page}#2\@nil
    \NEWPAX@Gin@box@opts
    \ifcase0\ifnum\NEWPAX@Gin@angle=90 1\fi
            \ifnum\NEWPAX@Gin@angle=270 1\fi
    \else
      \let\NEWPAX@temp\NEWPAX@inc@width
      \let\NEWPAX@inc@width\NEWPAX@inc@height
      \let\NEWPAX@inc@height\NEWPAX@temp
    \fi
    \Gscale@div\NEWPAX@scale@x\NEWPAX@inc@width{%
      \dimexpr\NEWPAX@page@urx-\NEWPAX@page@llx\relax
    }%
    \Gscale@div\NEWPAX@scale@y\NEWPAX@inc@height{%
      \dimexpr\NEWPAX@page@ury-\NEWPAX@page@lly\relax
    }%
    \NEWPAX@skip
  }%
}
\def\NEWPAX@filter@page#1{%
  \ifnum\NEWPAX@Gin@page=#1 %
    \expandafter\@firstofone
  \else
    \ifnum\NEWPAX@Gin@page<#1 %
      \csname fi\endcsname
      \csname fi\endcsname
      \expandafter\NEWPAX@stop\@gobblefour
    \fi
    \expandafter\NEWPAX@skip
  \fi
}
\def\NEWPAX@getrect#1#2 #3 #4 #5\@nil{%
  \@namedef{NEWPAX@#1@llx}{#2bp}%
  \@namedef{NEWPAX@#1@lly}{#3bp}%
  \@namedef{NEWPAX@#1@urx}{#4bp}%
  \@namedef{NEWPAX@#1@ury}{#5bp}%
}

\def\NEWPAX@cmd@annot#1#2{%
  \NEWPAX@filter@page{#1}{%
    \NEWPAX@call{annot}{#2}{}%
  }%
}
\def\NEWPAX@annot@Link#1#2#3{%
  \def\NEWPAX@link@type{#2}%
  \NEWPAX@call{link}{#2}{%
    \begingroup
    \NEWPAX@getrect{annot}#1\@nil
    \SetKeys[newpax/key]{#3}%
  }%
  \NEWPAX@skip
}

%    \end{macrocode}

% Now the main command which inserts annotations. 
% \begin{macro}{\NEWPAX@pdf@annot}
%    \begin{macrocode}
\newif\ifNEWPAX@ok
\NEWPAX@oktrue

\newif\ifNEWPAX@GoTo
%    \end{macrocode}
% We need to sanitize destnames in case they contain umlauts and so backslashes for the 
% octal numbers.
%    \begin{macrocode}
\def\NEWPAX@sanitize@destname{%
    \edef\0{\string\0}%
    \edef\1{\string\1}%
    \edef\2{\string\2}%
    \edef\3{\string\3}%    
    \edef\8{\string\8}%
    \edef\9{\string\9}}

\def\NEWPAX@pdf@annot#1{%
  \ifNEWPAX@Gin@clip
    \ifdim\NEWPAX@annot@llx<\NEWPAX@page@llx
      \let\NEWPAX@annot@llx\NEWPAX@page@llx
    \fi
    \ifdim\NEWPAX@annot@lly<\NEWPAX@page@lly
      \let\NEWPAX@annot@lly\NEWPAX@page@lly
    \fi
    \ifdim\NEWPAX@annot@urx>\NEWPAX@page@urx
      \let\NEWPAX@annot@urx\NEWPAX@page@urx
    \fi
    \ifdim\NEWPAX@annot@ury>\NEWPAX@page@ury
      \let\NEWPAX@annot@ury\NEWPAX@page@ury
    \fi
    \NEWPAX@okfalse
    \ifdim\NEWPAX@annot@llx<\NEWPAX@annot@urx\relax
      \ifdim\NEWPAX@annot@lly<\NEWPAX@annot@ury\relax
        \NEWPAX@oktrue
      \fi
    \fi
  \else
    \NEWPAX@oktrue
  \fi
  \ifNEWPAX@ok
    \ifcase 0\ifnum\NEWPAX@Gin@angle=90 1\fi
             \ifnum\NEWPAX@Gin@angle=180 2\fi
             \ifnum\NEWPAX@Gin@angle=270 3\fi\space
      % angle = 0
      \def\NEWPAX@raise{%
        \NEWPAX@scale@y\dimexpr\NEWPAX@annot@lly-\NEWPAX@page@lly\relax
      }%
      \def\NEWPAX@right{%
        \NEWPAX@scale@x\dimexpr\NEWPAX@annot@llx-\NEWPAX@page@llx\relax
      }%
    \or % angle = 90
      \def\NEWPAX@raise{%
        \NEWPAX@scale@x\dimexpr\NEWPAX@annot@llx-\NEWPAX@page@llx\relax
      }%
      \def\NEWPAX@right{%
        \NEWPAX@scale@y\dimexpr\NEWPAX@page@ury-\NEWPAX@annot@ury\relax
      }%
    \or % angle = 180
      \def\NEWPAX@raise{%
        \NEWPAX@scale@y\dimexpr\NEWPAX@page@ury-\NEWPAX@annot@ury\relax
      }%
      \def\NEWPAX@right{%
        \NEWPAX@scale@x\dimexpr\NEWPAX@page@urx-\NEWPAX@annot@urx\relax
      }%
    \or % angle = 270
      \def\NEWPAX@raise{%
        \NEWPAX@scale@x\dimexpr\NEWPAX@page@urx-\NEWPAX@annot@urx\relax
      }%
      \def\NEWPAX@right{%
        \NEWPAX@scale@y\dimexpr\NEWPAX@annot@lly-\NEWPAX@page@lly\relax
      }%
    \fi
    \@namedef{%
      NEWPAX@%
      \ifcase0\ifnum\NEWPAX@Gin@angle=90 1\fi
              \ifnum\NEWPAX@Gin@angle=270 1\fi\space
        width%
      \else
        height%
      \fi
    }{%
      \NEWPAX@scale@x\dimexpr\NEWPAX@annot@urx-\NEWPAX@annot@llx\relax
    }%
    \@namedef{%
      NEWPAX@%
      \ifcase0\ifnum\NEWPAX@Gin@angle=90 1\fi
              \ifnum\NEWPAX@Gin@angle=270 1\fi\space
        height%
      \else
        width%
      \fi
    }{%
      \NEWPAX@scale@y\dimexpr\NEWPAX@annot@ury-\NEWPAX@annot@lly\relax
    }%
    \raise\NEWPAX@raise\hb@xt@\z@{%
      \kern\NEWPAX@right
      \ifNEWPAX@GoTo
        %additional box for lualatex ...
        \hbox{%
          \@NEWPAX@linkgoto@xnn
            {\NEWPAX@file @\NEWPAX@DestName}%
            {\NEWPAX@width}%
            {\NEWPAX@height}%
            }%
      \else
        \hbox{%
        \expandafter\@NEWPAX@link@setaction@nn\expandafter{\NEWPAX@link@type}{#1}%
        \expandafter
         \@NEWPAX@annotboxlink@nnn
         \expandafter {\NEWPAX@link@type}{\NEWPAX@width}{\NEWPAX@height}{}}%
      \fi
      \hss
    }%
  \fi
  \endgroup
}
%    \end{macrocode}
% \end{macro}
% 
% What do they do??
%    \begin{macrocode}
\def\NEWPAX@htype@GoToR{file}
\def\NEWPAX@htype@GoTo{link}
\def\NEWPAX@htype@Named{link}
\def\NEWPAX@htype@URI{url}
%    \end{macrocode}
%
%    \begin{macrocode}
\ExplSyntaxOn
\def\NEWPAX@link@URI{%
  \NEWPAX@pdf@annot{%
    /URI\tl_to_str:V\NEWPAX@key@URI
  }%
}
\def\NEWPAX@link@Named{%
  \NEWPAX@pdf@annot{%
    /N \pdf_name_from_unicode_e:n{\NEWPAX@key@Name} %the value is from a pdf so we can assume it is correctly escaped??
  }%
}
\ExplSyntaxOff
\def\NEWPAX@link@GoToR{%
  \NEWPAX@pdf@annot{%
    /F\NEWPAX@key@File
    /D%
    \ifx\NEWPAX@key@DestName\@empty
      [\NEWPAX@key@DestPage\space\NEWPAX@key@DestView]%
    \else
      \NEWPAX@key@DestName
    \fi
  }%
}
%    \end{macrocode}
%
% Goto links are the most challenging: here it is not enough to create an 
% link annotation, one also have to ensure that the destination they point to exist 
% and are correctly created. The destinations can be on previous (or later) 
% pages that means that one has to use the aux to record what exists and what 
% is needed.
%  
% At first ensure that the commands are defined to avoid errors if newpax is removed
%    \begin{macrocode}
\AddToHook{begindocument}
  {
   \immediate\write\@mainaux{\string\providecommand{\string\NEWPAX@DestReq}[2]{}}
   \immediate\write\@mainaux{\string\providecommand{\string\NEWPAX@DestProv}[2]{}}
  }
\AddToHook
  {include/before}
  {
   \immediate\write\@partaux{\string\providecommand{\string\NEWPAX@DestReq}[2]{}}
   \immediate\write\@partaux{\string\providecommand{\string\NEWPAX@DestProv}[2]{}}
  }
%    \end{macrocode}
%
% After the aux file has been read the commands should do nothing:
%    \begin{macrocode}
\AtBeginDocument{%
  \let\NEWPAX@DestReq\@gobbletwo
  \let\NEWPAX@DestProv\@gobbletwo
}
%    \end{macrocode}

% \begin{macro}{\NEWPAX@DestReq}
% This command records the destinations that are required and should be reinserted.
%    \begin{macrocode}
\def\NEWPAX@DestReq#1#2{{%
  \NEWPAX@sanitize@destname
  \expandafter\gdef\csname NEWPAX@REQ@#1@#2\endcsname{}%
}}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\NEWPAX@DestProv}
% This records the destinations that are provided.
%    \begin{macrocode}
\def\NEWPAX@DestProv#1#2{{%
  \NEWPAX@sanitize@destname
  \expandafter\gdef\csname NEWPAX@PROV@#1@#2\endcsname{}%
}}
%    \end{macrocode}
% \end{macro}

% \begin{macro}[EXP]{\NEWPAX@DestName}
% This command expands either to the number or the name if it exists. 
%    \begin{macrocode}
\ExplSyntaxOn
\cs_new:Npn \NEWPAX@DestName
  {
    \tl_if_empty:eTF 
      { \NEWPAX@key@DestName }
      { \NEWPAX@key@DestLabel}
      { \NEWPAX@key@DestName }
    \l_@@_destsuffix_tl
  }
% \cs_set:Npn \NEWPAX@DestName {  \NEWPAX@key@DestLabel }
%    \end{macrocode}
% \end{macro}
%    \begin{macrocode}
\def\NEWPAX@link@GoTo{%
  \ifnum0<0\NEWPAX@key@DestLabel\relax
    \expandafter\@firstofone
  \else
    \endgroup
    \expandafter\@gobble
  \fi
  {%
    \if@filesw
      \protected@write\@auxout{}{%
        \string\NEWPAX@DestReq{\NEWPAX@file}{\NEWPAX@DestName}%
      }%
    \fi
    % Generate link, if destination exists
    \@ifundefined{NEWPAX@PROV@\NEWPAX@file @\NEWPAX@DestName}{%
      \endgroup
    }{%
      \NEWPAX@GoTotrue
      \NEWPAX@pdf@annot{}%
    }%
  }%
}
%    \end{macrocode}
% This is the command which reads the destination info.
%    \begin{macrocode}
\def\NEWPAX@cmd@dest#1#2#3#4{%
  \def\NEWPAX@key@DestLabel{#2}%
  \def\NEWPAX@key@DestName{}%
  \keys_set_groups:nnn {newpax/key} {destinit} {#4}
  \NEWPAX@filter@page{#1}{%
    \if@filesw
      \protected@write\@auxout{}{%
        \string\NEWPAX@DestProv{\NEWPAX@file}{\NEWPAX@DestName}%
      }%
    \bool_if:NT\l_@@_dests_all_bool
     {
       \protected@write\@auxout{}
        {
          \string\NEWPAX@DestReq{\NEWPAX@file}{\NEWPAX@DestName}%
        }
     }  
    \fi
    \@ifundefined{NEWPAX@REQ@\NEWPAX@file @\NEWPAX@DestName}{%
    }{%
      \begingroup
        \let\NEWPAX@key@DestY\NEWPAX@page@ury
        \let\NEWPAX@key@DestX\NEWPAX@page@llx
        \keys_set_filter:nnn {newpax/key} {destinit} {#4}
        \let\NEWPAX@dest@llx\NEWPAX@key@DestX
        \let\NEWPAX@dest@urx\NEWPAX@key@DestX
        \let\NEWPAX@dest@lly\NEWPAX@key@DestY
        \let\NEWPAX@dest@ury\NEWPAX@key@DestY
        \ifx\NEWPAX@key@DestRect\@empty
        \else
          \def\NEWPAX@temp{dest}%
          \expandafter\NEWPAX@getrect\expandafter\NEWPAX@temp
          \NEWPAX@key@DestRect\@nil
        \fi
        \ifNEWPAX@Gin@clip
          \ifx\NEWPAX@dest@llx<\NEWPAX@page@llx
            \let\NEWPAX@dest@llx\NEWPAX@page@llx
          \fi
          \ifx\NEWPAX@dest@lly<\NEWPAX@page@lly
            \let\NEWPAX@dest@lly\NEWPAX@page@lly
          \fi
          \ifx\NEWPAX@dest@urx>\NEWPAX@page@urx
            \let\NEWPAX@dest@urx\NEWPAX@page@urx
          \fi
          \ifx\NEWPAX@dest@ury>\NEWPAX@page@ury
            \let\NEWPAX@dest@ury\NEWPAX@page@ury
          \fi
          % at least prevent destinations outside the window
          \ifx\NEWPAX@dest@llx>\NEWPAX@page@urx
            \NEWPAX@dest@llx\NEWPAX@page@urx
          \fi
          \ifx\NEWPAX@dest@lly>\NEWPAX@page@ury
            \NEWPAX@dest@lly\NEWPAX@page@ury
          \fi
          \ifx\NEWPAX@dest@urx<\NEWPAX@page@llx
            \NEWPAX@dest@urx\NEWPAX@page@llx
          \fi
          \ifx\NEWPAX@dest@ury<\NEWPAX@page@lly
            \NEWPAX@dest@ury\NEWPAX@page@lly
          \fi
        \fi
        % I don't know, what is the best idea for rotated stuff,
        % perhaps using the corner llx/ury
        \ifcase 0\ifnum\NEWPAX@Gin@angle=90 1\fi
                 \ifnum\NEWPAX@Gin@angle=180 2\fi
                 \ifnum\NEWPAX@Gin@angle=270 3\fi\space
          % angle = 0
          \def\NEWPAX@raise{%
            \NEWPAX@scale@y\dimexpr\NEWPAX@dest@lly-\NEWPAX@page@lly\relax
          }%
          \def\NEWPAX@right{%
            \NEWPAX@scale@x\dimexpr\NEWPAX@dest@llx-\NEWPAX@page@llx\relax
          }%
        \or % angle = 90
          \def\NEWPAX@raise{%
            \NEWPAX@scale@x\dimexpr\NEWPAX@dest@llx-\NEWPAX@page@llx\relax
          }%
          \def\NEWPAX@right{%
            \NEWPAX@scale@y\dimexpr\NEWPAX@page@ury-\NEWPAX@dest@ury\relax
          }%
        \or % angle = 180
          \def\NEWPAX@raise{%
            \NEWPAX@scale@y\dimexpr\NEWPAX@page@ury-\NEWPAX@dest@ury\relax
          }%
          \def\NEWPAX@right{%
            \NEWPAX@scale@x\dimexpr\NEWPAX@page@urx-\NEWPAX@dest@urx\relax
          }%
        \or % angle = 270
          \def\NEWPAX@raise{%
            \NEWPAX@scale@x\dimexpr\NEWPAX@page@urx-\NEWPAX@dest@urx\relax
          }%
          \def\NEWPAX@right{%
            \NEWPAX@scale@y\dimexpr\NEWPAX@dest@lly-\NEWPAX@page@lly\relax
          }%
        \fi
        \edef\NEWPAX@name
          {\NEWPAX@file @\NEWPAX@DestName}%
        \let\NEWPAX@type\@empty
        \newpax@str@if@eq@@@@nnT{#3}{FITR}{\def\NEWPAX@type{xyz}}%too lazy for now for better fitr
        \newpax@str@if@eq@@@@nnT{#3}{XYZ}
          {%
            \def\NEWPAX@type{xyz}%
            \ifx\NEWPAX@key@DestZoom\@empty
            \else
             \edef\NEWPAX@type{\fpeval{\NEWPAX@key@DestZoom *100}}%
            \fi
          }
        \newpax@str@if@eq@@@@nnT{#3}{FIT}{\def\NEWPAX@type{fit}}
        \newpax@str@if@eq@@@@nnT{#3}{FITB}{\def\NEWPAX@type{fitb}}
        \newpax@str@if@eq@@@@nnT{#3}{FITH}{\def\NEWPAX@type{fith}}
        \newpax@str@if@eq@@@@nnT{#3}{FITBH}{\def\NEWPAX@type{fitbh}}
        \newpax@str@if@eq@@@@nnT{#3}{FITV}{\def\NEWPAX@type{fitv}}
        \newpax@str@if@eq@@@@nnT{#3}{FITBV}{\def\NEWPAX@type{fitbv}}
        \ifx\NEWPAX@type\@empty
          \def\NEWPAX@type{xyz}%
        \fi
        \raise\NEWPAX@raise\hb@xt@\z@{%
          \kern\NEWPAX@right
          \hbox{\@NEWPAX@destination@xx {\NEWPAX@name}{\NEWPAX@type}}%
          \hss
        }%      
      \endgroup
%    \end{macrocode}
% we undefine the REQ-command to avoid that the dest is set again.
%    \begin{macrocode}
      \cs_undefine:c{NEWPAX@REQ@\NEWPAX@file @\NEWPAX@DestName}
    }%
    \NEWPAX@skip
  }%
}
%    \end{macrocode}
% These are keys used in the newpax file. The command names are inherited from kvoptions.
% We must predefined \cs{NEWPAX@key@DestName} as that is not done by the key setup.
%    \begin{macrocode}
\def\NEWPAX@key@DestName{}
\keys_define:nn {newpax/key}
  {
   ,URI      .tl_set:N = \NEWPAX@key@URI
   ,Name     .tl_set:N = \NEWPAX@key@Name
   ,DestName .code:n   = 
     { 
       \group_begin:
       \NEWPAX@sanitize@destname
       \tl_gset:Ne \g_@@_tmpa_tl{#1}
       \group_end:
       \cs_set_eq:NN\NEWPAX@key@DestName\g_@@_tmpa_tl
     } 
   ,DestName .groups:n = {destinit}
   ,DestPage .tl_set:N = \NEWPAX@key@DestPage
   ,DestView .tl_set:N = \NEWPAX@key@DestView
   ,File     .tl_set:N = \NEWPAX@key@File
   ,C        .tl_set:N = \NEWPAX@key@C
   ,Border   .tl_set:N = \NEWPAX@key@Border
   ,BS       .tl_set:N = \NEWPAX@key@BS
   ,H        .tl_set:N = \NEWPAX@key@H
   ,DestLabel .tl_set:N = \NEWPAX@key@DestLabel
   ,DestLabel .groups:n = {destinit}
   ,DestRect .tl_set:N = \NEWPAX@key@DestRect
   ,DestZoom .tl_set:N = \NEWPAX@key@DestZoom
   ,DestX    .code:n = { \NEWPAX@defaultbp\NEWPAX@key@DestX{#1} }
   ,DestY    .code:n = { \NEWPAX@defaultbp\NEWPAX@key@DestY{#1} }    
  }
\ExplSyntaxOff

%</package>
%    \end{macrocode}
%    \begin{macrocode}
%<*lua>
local ProvidesLuaModule = {
    name          = "newpax",
    version       = "0.57",       --TAGVERSION
    date          = "2025-09-24", --TAGDATE
    description   = "newpax lua code",
    license       = "The LATEX Project Public License 1.3c"
}

if luatexbase and luatexbase.provides_module then
  luatexbase.provides_module (ProvidesLuaModule)
end



local OPEN              = pdfe.open
local GETSIZE           = pdfe.getsize
local GETINFO           = pdfe.getinfo
local GETPAGE           = pdfe.getpage
local GETNAME           = pdfe.getname
local GETARRAY          = pdfe.getarray
local GETDICTIONARY     = pdfe.getdictionary
local GETFROMDICTIONARY = pdfe.getfromdictionary
local GETFROMARRAY      = pdfe.getfromarray
local PAGESTOTABLE      = pdfe.pagestotable
local DICTIONARYTOTABLE = pdfe.dictionarytotable
local ARRAYTOTABLE      = pdfe.arraytotable
local TYPE              = pdfe.type
local GETFROMREFERENCE  = pdfe.getfromreference
local FILENAMEONLY      = file.nameonly

local strENTRY_BEG = "\\["
local strENTRY_END = "\\\\\n"
local strCMD_BEG = "{"
local strCMD_END = "}"
local strARG_BEG = "{"
local strARG_END = "}"
local strKVS_BEG = "{"
local strKVS_END = "\n}"
local strKVS_EMPTY = "{}"
local strKV_BEG  = "\n  "
local strKV_END  = ","
local strKEY_BEG = ""
local strKEY_END = ""
local strVALUE_BEG = "={"
local strVALUE_END = "}"
local strHEX_STR_BEG = "\\<"
local strHEX_STR_END = "\\>"


local strLIT_STR_BEG = "("
local strLIT_STR_END = ")"
local strDICT_BEG= "<<"
local strDICT_END= ">>"
local strNAME= "/"
local strARRAY_BEG= "["
local strARRAY_END= "]"
local strARRAY_SEP = " "
local strRECT_SEP = " "


local constCMD_ANNOT   = "annot"
local constCMD_BASEURL = "baseurl" -- unused
local constCMD_DEST    = "dest"
local constCMD_FILE    = "file"
local constCMD_INFO    = "info"   -- unused
local constCMD_PAGENUM = "pagenum"
local constCMD_PAGE    = "page"
local constCMD_PAX     = "pax"

-- cmd file
local constKEY_SIZE    = "Size"
local constKEY_DATE    = "Date"

-- cmd annot attributes
local constKEY_C      = "C"
local constKEY_BORDER = "Border"
local constKEY_BS     = "BS"
local constKEY_H      = "H"

-- cmd annot/link/URI
local constKEY_URI    = "URI"
local constKEY_IS_MAP = "IsMap" -- not handled by pax, consider later


-- cmd annot/link/GoToR
local constKEY_FILE      = "File"
local constKEY_DEST_NAME = "DestName" -- handle??
local constKEY_DEST_PAGE = "DestPage" --ok
local constKEY_DEST_VIEW = "DestView" --ok

-- cmd annot/link/GoTo
local constKEY_DEST_RECT  = "DestRect"
local constKEY_DEST_X     = "DestX"
local constKEY_DEST_Y     = "DestY"
local constKEY_DEST_ZOOM  = "DestZoom"
local constKEY_DEST_LABEL = "DestLabel" --ok

-- cmd annot/link/Named
local constKEY_NAME = "Name"

-- destination views
local constDEST_XYZ = "XYZ"
local constDEST_FIT    = "Fit"
local constDEST_FITH   = "FitH"
local constDEST_FITV   = "FitV"
local constDEST_FITR   = "FitR"
local constDEST_FITB   = "FitB"
local constDEST_FITBH  = "FitBH"
local constDEST_FITBV  = "FitBV"


-- get/build data
-- returns table,pagecount where table objref ->page number
local function getpagesdata (pdfedoc)
  local type,pagecount,detail   = GETFROMDICTIONARY (pdfedoc.Catalog.Pages,"Count")
  local pagestable  = PAGESTOTABLE (pdfedoc)
  local pagereftonum ={}
  for i=1,#pagestable do
   pagereftonum[pagestable[i][3]]=i
  end
  return pagereftonum, pagecount
end

-- builds a table destination name -> obj reference (pdfedict) from the Names tree

local function processnamesarray (pdfearray,targettable)
  local tkey={}
  for i=1,#pdfearray do
    local type,value,detail= GETFROMARRAY(pdfearray,i)
    if (i % 2 == 1) then
     tkey = value
    else
      tvalue = value
      targettable[tkey]=tvalue
    end
  end
end

local function findallnamesarrays (pdfedict,deststable)
  local namesarray  = GETARRAY(pdfedict,"Names")
  if namesarray then
     processnamesarray (namesarray,deststable)
  else
     local kidsarray = GETARRAY(pdfedict,"Kids")
     if kidsarray then
       for i=1,#kidsarray do
         findallnamesarrays (kidsarray[i],deststable)
       end
     end
  end
end

-- returns destnames -> objref table
local function getdestreferences (pdfedoc)
  local deststable= {}
  local catnames  = GETDICTIONARY (pdfedoc.Catalog, "Names")
  if catnames then
    local destsdict = GETDICTIONARY (catnames, "Dests")
    if destsdict then
      findallnamesarrays (destsdict,deststable)
    end
  end
  return deststable
end

-- destinations can be an dict (/D [array]) or only an array!

local function getdestdata (name,pagereftonum,destnamestoref)
   local destref
   local type,ref,pagenum,destx,desty = nil, nil, 1,0,0
   local data = {{0,0}, {5,constDEST_XYZ}}
   -- if we get directly an array
   if (TYPE(name) == "pdfe.array") then
     data = ARRAYTOTABLE(name)
     type, ref, pageref = GETFROMARRAY(name,1)
     pagenum = pagereftonum[pageref]
   else
    destref = destnamestoref[name]
   end   
   if destref then
     type,value,detail = GETFROMREFERENCE(destref)
     if TYPE(value) == "pdfe.dictionary" then
       local destarray = GETARRAY(value,"D")
       if destarray then
         data = ARRAYTOTABLE(destarray)
         type, ref, pageref = GETFROMARRAY(destarray,1)
         pagenum = pagereftonum[pageref]
       end
     elseif TYPE(value) == "pdfe.array" then
        data = ARRAYTOTABLE(value)
        type, ref, pageref = GETFROMARRAY(value,1)
        pagenum = pagereftonum[pageref]
     end
   end
  -- print("XXXXXXXXXX",table.serialize(data),pagenum)
  return pagenum, data
end


-- output functions
-- write the info
-- XXXXXX encode/escape the file name?
local function outputENTRY_file (file, pdfedoc)
  local bytes       = GETSIZE(pdfedoc)
  local date        = (GETINFO(pdfedoc) and GETINFO(pdfedoc).CreationDate) or  "D:22222222222222"
  -- file
  local a = strENTRY_BEG
  a = a .. strCMD_BEG .. constCMD_FILE .. strCMD_END
  a = a .. strARG_BEG .. strLIT_STR_BEG .. file .. strLIT_STR_END ..  strARG_END
  a = a .. strKVS_BEG
   a = a .. strKV_BEG .. constKEY_SIZE .. strVALUE_BEG .. bytes .. strVALUE_END .. strKV_END
   a = a .. strKV_BEG .. constKEY_DATE .. strVALUE_BEG .. date .. strVALUE_END.. strKV_END
  a = a .. strKVS_END
  a = a .. strENTRY_END
  return a
end

local function outputENTRY_pagenum (pages)
  local a = strENTRY_BEG
  a = a .. strCMD_BEG .. constCMD_PAGENUM .. strCMD_END
  a = a .. strARG_BEG .. pages .. strARG_END
  a = a .. strENTRY_END
  return a
end

local function outputENTRY_page (pdfedoc,page) -- page=integer
  -- trimbox, bleedbox, cropbox,artbox,rotate etc could be put in
  -- the second argument as keyval:
  -- TrimBox={0 0 300 350},
  -- but pax skips the argument anyway, so not really useful
  local mediabox = pdfe.getbox(GETPAGE(pdfedoc,page),"MediaBox")
  local a=""
  if mediabox then
    a = strENTRY_BEG
    a = a .. strCMD_BEG .. constCMD_PAGE .. strCMD_END
    a = a .. strARG_BEG .. page .. strARG_END
    a = a .. strARG_BEG
      a = a .. mediabox[1]
      for j = 2, 4 do
       a = a .. strRECT_SEP.. mediabox[j]
      end
    a = a .. strARG_END
    a = a .. strKVS_EMPTY
    a = a .. strENTRY_END
  end
  return a
end


local function outputCMD_annot (pdfedict,page,type)
  local rectangle = ARRAYTOTABLE(GETARRAY(pdfedict,"Rect"))
  local a = strCMD_BEG .. constCMD_ANNOT .. strCMD_END
      a = a .. strARG_BEG .. page .. strARG_END
      a = a .. strARG_BEG .. GETNAME(pdfedict,"Subtype") .. strARG_END
      a = a .. strARG_BEG
       a = a .. rectangle[1][2]
        for k = 2, 4 do
         a = a.. strRECT_SEP..rectangle[k][2]
        end
      a = a .. strARG_END
      a = a .. strARG_BEG .. type .. strARG_END
  return a
end


local function outputKV_color (pdfedict)
  local color = GETARRAY(pdfedict,constKEY_C)
  local a =""
  if color then
    local colortable = ARRAYTOTABLE(color)
    a = strKV_BEG .. constKEY_C .. strVALUE_BEG .. strARRAY_BEG
      for i=1,#colortable do
        a = a.. colortable[i][2] .. strARRAY_SEP
      end
    a = a .. strARRAY_END .. strVALUE_END .. strKV_END
  end
  return a
end

local function outputKV_key (pdfedict,key)
  local name = GETNAME(pdfedict,key)
  local a = ""
  if name then
    a = strKV_BEG .. key .. strVALUE_BEG .. strNAME .. name .. strVALUE_END .. strKV_END
  end
  return a
end

-- XXX handle fourth argument
local function outputKV_Border (pdfedict)
  local border = GETARRAY(pdfedict,constKEY_BORDER)
  local a =""
  if border then
    local bordertable = ARRAYTOTABLE(border)
    a = strKV_BEG .. constKEY_BORDER .. strVALUE_BEG .. strARRAY_BEG
    for i=1,3 do
      a = a .. bordertable[i][2] .. strARRAY_SEP
    end
 -- fourth argument later, it is an array (type 7)
   a = a ..strARRAY_END .. strVALUE_END .. strKV_END
  end
  return a
end

local function outputKV_BS (pdfedict)
  local bsstyle = GETDICTIONARY(pdfedict,constKEY_BS)
  local a =""
  if bsstyle then
    local bsstyledict = DICTIONARYTOTABLE(bsstyle)
    a = strKV_BEG .. constKEY_BS .. strVALUE_BEG ..strDICT_BEG
    for k,v in pairs (bsstyledict) do
      a = a .. strNAME.. k
      if v[1]== 5 then
       a = a .. strNAME .. v[2]
      else
       a = a ..strRECT_SEP .. v[2]
      end
    end
    a = a .. strDICT_END .. strVALUE_END .. strKV_END
  end
  return a
end


local function outputKV_uri (pdfedict)
  local type, value, hex = GETFROMDICTIONARY(pdfedict,constKEY_URI)
  if TYPE(value) == "pdfe.reference" then
   type,value,hex  = GETFROMREFERENCE(value)
  end
  local a= strKV_BEG .. constKEY_URI .. strVALUE_BEG
  if hex then
    a = a .. strHEX_STR_BEG .. value .. strHEX_STR_END
  else
    a = a .. strLIT_STR_BEG ..value .. strLIT_STR_END
  end
    a = a .. strVALUE_END .. strKV_END
  return a
end

local function outputKV_N (pdfedict)
  local name = GETNAME(pdfedict,"N")
  local a= strKV_BEG .. constKEY_NAME .. strVALUE_BEG .. name .. strVALUE_END .. strKV_END
  return a
end

-- if a gotoR has a filespec filespec we use this
-- to output the reference. It is rather crude and handles only names and strings
local function outputDICT (dictionary)
 local tkeys = {}
 local dict = DICTIONARYTOTABLE(dictionary)
 for name,value in pairs(dict) do
  table.insert(tkeys,name)
 end
 table.sort(tkeys)
 local a = "<<"
 for _,k in ipairs (tkeys) do
   v=dict[k]
   a = a .. strNAME.. k
      if v[1]== 5 then -- it is a name
       b = string.gsub(v[2], "/", "#2F")
       a = a .. strNAME .. b
      elseif v[1] == 6 then -- it is a string
       local b
       if v[3] then
         b = "<" .. v[2] .. ">"
       else
         b = "(" .. v[2] .. ")"
       end
       a = a ..strRECT_SEP .. b
       -- everything else is ignored for now!
      end
    end
 a = a .. ">>"
 return a
end

local function outputKV_gotor (pdfedict) -- action dictionary
  local type, value, hex = GETFROMDICTIONARY(pdfedict,"F")
  local desttype, destvalue, destdetail =  GETFROMDICTIONARY(pdfedict,"D")
  local a = strKV_BEG .. constKEY_FILE .. strVALUE_BEG
  if TYPE(value) == "pdfe.reference" then
     local x,dictionary  = GETFROMREFERENCE(value)
     if TYPE(dictionary) == "pdfe.dictionary" then
       a = a .. outputDICT (dictionary)
     else
      print("ERROR!? this is not a dictionary!!")
     end
  else
   if hex then
     a = a .. strHEX_STR_BEG .. value .. strHEX_STR_END
   else
     a = a .. strLIT_STR_BEG .. value .. strLIT_STR_END
   end
  end
  a = a .. strVALUE_END .. strKV_END
  if desttype == 7 then
    local type, pagenum    = GETFROMARRAY(GETARRAY (pdfedict,"D"),1)
    local type, fittype    = GETFROMARRAY(GETARRAY (pdfedict,"D"),2)
    a = a .. strKV_BEG .. constKEY_DEST_PAGE .. strVALUE_BEG .. pagenum .. strVALUE_END .. strKV_END
    a = a .. strKV_BEG .. constKEY_DEST_VIEW .. strVALUE_BEG .. strNAME .. fittype .. strVALUE_END .. strKV_END
  elseif desttype == 6 then
   a = a .. strKV_BEG .. constKEY_DEST_NAME .. strVALUE_BEG ..
          strLIT_STR_BEG .. destvalue .. strLIT_STR_END .. strVALUE_END .. strKV_END
  end
  return a
end

-- this outputs the key DestLabel with a count

local function outputKV_goto (count)
  local a = strKV_BEG .. constKEY_DEST_LABEL .. strVALUE_BEG .. count .. strVALUE_END .. strKV_END
  return a
end

-- this outputs the key DestName with a name
local function outputKV_goto_name (name)
  local a = strKV_BEG .. constKEY_DEST_NAME .. strVALUE_BEG .. name .. strVALUE_END .. strKV_END
  return a
end

local function outputENTRY_dest (destcount,name,pagereftonum,destnamestoref,pdfedoc)
 local pagenum, data = getdestdata(name,pagereftonum,destnamestoref)
 local mediabox = pdfe.getbox(GETPAGE(pdfedoc,pagenum),"MediaBox")
 local a = strENTRY_BEG
 a = a .. strCMD_BEG .. constCMD_DEST .. strCMD_END
 a = a .. strARG_BEG .. pagenum .. strARG_END
 a = a .. strARG_BEG .. destcount .. strARG_END
 -- name
 a = a .. strARG_BEG .. data[2][2] .. strARG_END
 a = a .. strKVS_BEG
 if data[2][2] == constDEST_XYZ then
   if data[3] and data[3][2] then
    a = a .. strKV_BEG .. constKEY_DEST_X .. strVALUE_BEG .. data[3][2] .. strVALUE_END .. strKV_END
   else
    a = a .. strKV_BEG .. constKEY_DEST_X .. strVALUE_BEG .. mediabox[1] .. strVALUE_END .. strKV_END
   end
   if data[4] and data[4][2] then
    a = a .. strKV_BEG .. constKEY_DEST_Y .. strVALUE_BEG .. data[4][2] .. strVALUE_END .. strKV_END
   else
    a = a .. strKV_BEG .. constKEY_DEST_Y .. strVALUE_BEG .. mediabox[4] .. strVALUE_END .. strKV_END
   end
   if data[5] and data[5][2] then
    a = a .. strKV_BEG .. constKEY_DEST_ZOOM .. strVALUE_BEG .. data[5][2] .. strVALUE_END .. strKV_END
   end
 elseif data[2][2] == constDEST_FIT  then -- nothing to do
 elseif data[2][2] == constDEST_FITB then -- nothing to do
 elseif data[2][2] == constDEST_FITH then
   if data[3] and data[3][2] then
    a = a .. strKV_BEG .. constKEY_DEST_Y .. strVALUE_BEG .. data[3][2] .. strVALUE_END .. strKV_END
   end
 elseif data[2][2] == constDEST_FITBH then
   if data[3] and data[3][2] then
    a = a .. strKV_BEG .. constKEY_DEST_Y .. strVALUE_BEG  .. data[3][2] .. strVALUE_END .. strKV_END
   end
 elseif data[2][2] == constDEST_FITV then
   if data[3] and data[3][2] then
    a = a .. strKV_BEG .. constKEY_DEST_X .. strVALUE_BEG .. data[3][2] .. strVALUE_END .. strKV_END
   end
 elseif data[2][2] == constDEST_FITBV then
   if data[3] and data[3][2] then
    a = a .. strKV_BEG .. constKEY_DEST_X .. strVALUE_BEG .. data[3][2] .. strVALUE_END .. strKV_END
   end
 elseif data[2][2] == constDEST_FITR and data[6] then
   a = a ..  strKV_BEG .. constKEY_DEST_RECT .. strVALUE_BEG
   a = a .. data[3][2] .. strRECT_SEP
   a = a .. data[4][2] .. strRECT_SEP
   a = a .. data[5][2] .. strRECT_SEP
   a = a .. data[6][2] .. strVALUE_END .. strKV_END
 end
  -- output also the name. This perhaps need a check if the name exists 
  -- print("DEBUG TYPE name", TYPE(name))
  if not (TYPE(name) == "pdfe.array") then    
   a = a .. strKV_BEG .. constKEY_DEST_NAME .. strVALUE_BEG .. name .. strVALUE_END .. strKV_END
  end 
 a = a .. strKVS_END .. strENTRY_END
 return a
end






-- the main function

local function __writepax (ext,file)
  local fileVAR     = assert(kpse.find_file(file ..".pdf"),"file `"..file..".pdf` not found")
  local filebaseVAR = FILENAMEONLY(fileVAR)
  -- getting the data for the concrete document:
  local writeVAR = io.open (filebaseVAR .."."..ext,"w") -- always in current directory
  local function WRITE(content)
    writeVAR:write(content)
  end
  local docVAR   = OPEN (fileVAR)
  local pagereftonumVAR, pagecountVAR = getpagesdata (docVAR)
  local destcountVAR   = 0
  -- build from names table:
  local destnamestorefVAR = getdestreferences (docVAR)
  local collected_destinations = {}
  local useddestnames = {}
  -- output ...
  WRITE(strENTRY_BEG .. "{pax}{0.1l}" .. strENTRY_END)
  WRITE(outputENTRY_file(fileVAR,docVAR))
  WRITE(outputENTRY_pagenum(pagecountVAR))
  for i=1, pagecountVAR do
    WRITE(outputENTRY_page(docVAR,i))
    local annots=GETPAGE(docVAR,i).Annots
    if annots then
      for j = 0,#annots-1 do
        local annot = GETDICTIONARY (annots,j)
        annottable = DICTIONARYTOTABLE (annot)
        if annottable.Dest then
          destcountVAR=destcountVAR + 1
          WRITE (strENTRY_BEG)
          WRITE (outputCMD_annot(annot,i,"GoTo"))
          WRITE (strKVS_BEG) -- begin KVS data
          WRITE ( outputKV_color(annot) )
          WRITE ( outputKV_key(annot,constKEY_H) )
          WRITE ( outputKV_Border (annot) )
          WRITE ( outputKV_BS (annot) )
          WRITE ( outputKV_goto (destcountVAR) )
          local type,annotgoto,hex = GETFROMDICTIONARY(annot,"Dest")
          -- if the Dest is a string add the string as a name: 
          if type == 6 then
          WRITE ( outputKV_goto_name(annotgoto) )
          end                  
          WRITE(strKVS_END)   -- end KVS
          WRITE(strENTRY_END) -- end annot data         
          table.insert(collected_destinations, outputENTRY_dest(destcountVAR,
               annotgoto,pagereftonumVAR,destnamestorefVAR,docVAR))
        else
          local annotaction = GETDICTIONARY(annot,"A")
          --  print("DEBUG A:",table.serialize(DICTIONARYTOTABLE(annotaction)))
          local annotactiontype =""
          if annotaction then
            annotactiontype = GETNAME(annotaction,"S")
            WRITE (strENTRY_BEG)
            WRITE (outputCMD_annot(annot,i,annotactiontype))
            WRITE (strKVS_BEG) -- begin KVS data
            WRITE ( outputKV_color(annot) )
            WRITE ( outputKV_key(annot,constKEY_H) )
            WRITE ( outputKV_Border (annot) )
            WRITE ( outputKV_BS (annot) )
            if annotactiontype == constKEY_URI then
              WRITE ( outputKV_uri(annotaction) )
            elseif annotactiontype =="GoTo" then
              local desttype, destname, destdetail =  GETFROMDICTIONARY(annotaction,"D")
              -- print("DEBUG annotaction", desttype, destname, destdetail)
              destcountVAR=destcountVAR + 1
              WRITE ( outputKV_goto (destcountVAR) )
              -- this needs perhaps a check if destname actually exists.
              if desttype == 6 then
                WRITE ( outputKV_goto_name(destname) )
              end  
            elseif annotactiontype=="GoToR" then
              WRITE ( outputKV_gotor(annotaction) )
            elseif annotactiontype=="Named" then
              WRITE ( outputKV_N (annotaction) )
            end
            WRITE(strKVS_END)   -- end KVS
            WRITE(strENTRY_END) -- end annot data
            if annotactiontype =="GoTo" then
              local type,annotactiongoto,hex = GETFROMDICTIONARY(annotaction,"D")
               if type==6 then
               -- record used name 
               useddestnames[annotactiongoto] = 1
              end
              table.insert(collected_destinations, outputENTRY_dest(destcountVAR,annotactiongoto,pagereftonumVAR,destnamestorefVAR,docVAR))
            end
          end
        end
      end
    end
  end
  for i=1,#collected_destinations do
   WRITE (collected_destinations[i])
  end
  -- write out the rest of the destinations.
  -- We order first the table so that the newpax file doesn't change between 
  -- compilations, see issue #25 and PR #25
  local destnamessorted = {}
  for k,v in pairs (destnamestorefVAR) do
   -- ignore use dests and page destinations. 
    i=string.find(k,"page.",1) 
    if not useddestnames[k] and not i then
      table.insert (destnamessorted,k)
    end
  end  
  table.sort(destnamessorted)
  for i = 1, #destnamessorted do
      k=destnamessorted[i]
      destcountVAR=destcountVAR + 1
      WRITE(outputENTRY_dest(destcountVAR,k,pagereftonumVAR,destnamestorefVAR,docVAR))
  end    
  io.close(writeVAR)
end

local function writepax (file)
 __writepax ("pax",file)
end

local function writenewpax (file)
 __writepax ("newpax",file)
end

newpax = {}
newpax.writepax    = writepax
newpax.writenewpax = writenewpax

return newpax

%</lua>
%    \end{macrocode}
