% \iffalse meta-comment
%
% File: newpax.dtx
% Copyright 2006-2008, 2011, 2012 Heiko Oberdiek (original pax.sty)
% Copyright (C) 2021, 2022 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}
\ProvidesPackage{newpax}%
  [2022-09-15 v0.53 Annotation support for PDF graphics based on pax.sty adapted by (UF)]%
\ExplSyntaxOn
\bool_if:nF
  {
    \cs_if_exist_p:N \pdfmanagement_if_active_p:
  }
  {  %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-testphase}\MessageBreak
       before~loading~the~class
     }
  }

\ExplSyntaxOff
\RequirePackage{graphicx}
\RequirePackage{ltxcmds}[2011/04/18]
\RequirePackage{kvsetkeys}[2011/04/07]
\RequirePackage{kvoptions}[2010/12/23]
\RequirePackage{auxhook}[2011/03/04]
\RequirePackage{etoolbox}
\RequirePackage{xfp}

\ExplSyntaxOn
%to replace \pdf@strcmp
\cs_set_eq:NN \newpax@str@if@eq@@@@nnT\str_if_eq:nnT

\bool_new:N \l_@@_use_attributes_bool
\bool_new:N \l_@@_addannots_bool

\tl_new:N   \l_@@_destsuffix_tl
\tl_new:N \NEWPAX@fileextension

\cs_generate_variant:Nn \pdfannot_dict_put:nnn {nnx,xnx,xnn}
\NewDocumentCommand\newpaxsetup { m }
  {
    \keys_set:nn {newpax}{ #1}
  }

\keys_define:nn {newpax}
  {
    usefileattributes .bool_set:N = \l_@@_use_attributes_bool,
    destsuffix        .tl_set:N   = \l_@@_destsuffix_tl,
    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
  }

\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 }
          }
      }
  }




\cs_new_protected:Npn \@NEWPAX@linkgoto@xnn #1 #2 #3  %#1 dest #2 width #3 height
  {
    \group_begin:
      \exp_args:Nx\@NEWPAX@setattributes@n {GoTo}
      \leavevmode
      \exp_args:Nx\pdfannot_link_goto_begin:nw
        { #1 }
        \@NEWPAX@ensurelinkbox@n{\hbox_to_wd:nn {#2}{ { \rule{0pt}{#3} }\hfill}}
      \pdfannot_link_goto_end:
    \group_end:
  }

\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>>}
 }



\cs_new_protected:Npn \@NEWPAX@annotboxlink@nnn #1 #2 #3   %#1 type, #2 width #3 height
  {
    \group_begin:
      \exp_args:Nx\@NEWPAX@setattributes@n {#1}
      \leavevmode
      \pdfannot_box:nnnx {#2}{#3}{0pt}
         {\pdfannot_dict_use:n{link/#1}}
    \group_end:
  }

\cs_new_protected:Npn \@NEWPAX@destination@xx #1 #2
  {
    \exp_args:Nxx \pdf_destination:nn {#1}{#2}
  }

% only defined if hyperref is loaded ...
\sys_if_engine_xetex:TF
  {
    \cs_set_eq:NN \@NEWPAX@ensurelinkbox@n \XeTeXLinkBox
  }
  {
    \cs_set_eq:NN \@NEWPAX@ensurelinkbox@n \use:n
  }
\ExplSyntaxOff
% Hook into \includegraphics of graphicx with one optional
% star, one optional argument with key value pairs and
% the graphics name
\let\NEWPAX@ORG@includegraphics\includegraphics
\def\includegraphics{%
  \@ifstar{%
    \@ifnextchar[{%
      \begingroup
        \def\x{\endgroup
           \NEWPAX@includegraphics[clip,%
        }%
      \expandafter\x\@gobble
    }{%
      \NEWPAX@includegraphics[clip]%
    }%
  }{%
    \@ifnextchar[{%
      \NEWPAX@includegraphics
    }{%
      \NEWPAX@includegraphics[]%
    }%
  }%
}

\ExplSyntaxOn
\def\NEWPAX@includegraphics[#1]#2{%
  \begingroup
    \sbox0{\NEWPAX@ORG@includegraphics[{#1}]{#2}}%
    \edef\NEWPAX@inc@width{\the\wd0}%
    \edef\NEWPAX@inc@height{\the\dimexpr\dp0+\ht0}%
    \leavevmode
    \hbox to \wd0{%
      \rlap{\copy0}%
      \bool_if:nT
        { \l_@@_addannots_bool
           &&
           (
            \int_compare_p:nNn
             {
               \cs_if_exist:NTF\pdfpages@includegraphics@status
                 {
                    \pdfpages@includegraphics@status
                 }{0}
             }
             <
             {2}
           )
        }
        {
          \raise-\dp0\hbox
            {
              \NEWPAX@AddAnnots{#1}{#2}%
            }
        }
      \hfill
    }
  \endgroup
}
\ExplSyntaxOff
\SetupKeyvalOptions{family=NEWPAX@Gin,prefix=NEWPAX@Gin@}
\DeclareStringOption[1]{page}
\DeclareStringOption[0]{angle}
\DeclareBoolOption{clip}
\define@key{NEWPAX@Gin}{viewport}{%
  \ltx@LocalAppendToMacro\NEWPAX@Gin@box@opts{%
    \NEWPAX@viewport#1\\%
  }%
}
\define@key{NEWPAX@Gin}{trim}{%
  \ltx@LocalAppendToMacro\NEWPAX@Gin@box@opts{%
    \NEWPAX@trim#1\\%
  }%
}
\let\NEWPAX@Gin@box@opts\@empty

\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
}

% ignore unknown options in family `NEWPAX@Gin'
\kv@set@family@handler{NEWPAX@Gin}{}

\def\NEWPAX@AddAnnots#1#2{%
  \kvsetkeys{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
}
\def\NEWPAX@parser#1{\NEWPAX@call{cmd}{#1}{}}%
\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\\{}

\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
    \kvsetkeys{NEWPAX}{#3}%
  }%
  \NEWPAX@skip
}
\newif\ifNEWPAX@ok
\NEWPAX@oktrue

\newif\ifNEWPAX@GoTo

\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@\NEWPAX@file @\NEWPAX@key@DestLabel @\csname l_@@_destsuffix_tl\endcsname}%
            {\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
}


\def\NEWPAX@htype@GoToR{file}
\def\NEWPAX@htype@GoTo{link}
\def\NEWPAX@htype@Named{link}
\def\NEWPAX@htype@URI{url}

\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
  }%
}

% GoTo

\AddLineBeginAux{%
  \string\providecommand{\string\NEWPAX@DestReq}[2]{}%
}
\AddLineBeginAux{%
  \string\providecommand{\string\NEWPAX@DestProv}[2]{}%
}
\AtBeginDocument{%
  \let\NEWPAX@DestReq\@gobbletwo
  \let\NEWPAX@DestProv\@gobbletwo
}
\def\NEWPAX@DestReq#1#2{%
  \expandafter\gdef\csname NEWPAX@REQ@#1@#2\endcsname{}%
}
\def\NEWPAX@DestProv#1#2{%
  \expandafter\gdef\csname NEWPAX@PROV@#1@#2\endcsname{}%
}

\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@key@DestLabel}%
      }%
    \fi
    % Generate link, if destination exists
    \@ifundefined{NEWPAX@PROV@\NEWPAX@file @\NEWPAX@key@DestLabel}{%
      \endgroup
    }{%
      \NEWPAX@GoTotrue
      \NEWPAX@pdf@annot{}%
    }%
  }%
}

% Read destinations
\def\NEWPAX@cmd@dest#1#2#3#4{%
  \NEWPAX@filter@page{#1}{%
    \if@filesw
      \protected@write\@auxout{}{%
        \string\NEWPAX@DestProv{\NEWPAX@file}{#2}%
      }%
    \fi
    \@ifundefined{NEWPAX@REQ@\NEWPAX@file @#2}{%
    }{%
      \begingroup
        \let\NEWPAX@key@DestY\NEWPAX@page@ury
        \let\NEWPAX@key@DestX\NEWPAX@page@llx
        \kvsetkeys{NEWPAX}{#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@\NEWPAX@file @#2@\csname l_@@_destsuffix_tl\endcsname}%
        \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
    }%
    \NEWPAX@skip
  }%
}

\SetupKeyvalOptions{family=NEWPAX,prefix=NEWPAX@key@}
\DeclareStringOption{URI}
\DeclareStringOption{Name}
\DeclareStringOption{DestName}
\DeclareStringOption{DestPage}
\DeclareStringOption{DestView}
\DeclareStringOption{File}
\DeclareStringOption{C}
\DeclareStringOption{Border}
\DeclareStringOption{BS}
\DeclareStringOption{H}
\DeclareStringOption{DestLabel}
\DeclareStringOption{DestRect}
\DeclareStringOption{DestZoom}
\define@key{NEWPAX}{DestX}{%
  \NEWPAX@defaultbp\NEWPAX@key@DestX{#1}%
}
\define@key{NEWPAX}{DestY}{%
  \NEWPAX@defaultbp\NEWPAX@key@DestY{#1}%
}
%</package>
%    \end{macrocode}
%    \begin{macrocode}
%<*lua>
local ProvidesLuaModule = {
    name          = "newpax",
    version       = "0.53",       --TAGVERSION
    date          = "2022-09-15", --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 = destnamestoref[name]
   local type,ref,pagenum,destx,desty = nil, nil, 1,0,0
   local data = {{0,0}, {5,constDEST_XYZ}}
   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
  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

local function outputKV_goto (count)
  local a = strKV_BEG .. constKEY_DEST_LABEL .. strVALUE_BEG .. count .. 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_X .. 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
 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 = {}
  -- 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) )
          WRITE(strKVS_END)   -- end KVS
          WRITE(strENTRY_END) -- end annot data
          local type,annotgoto,hex = GETFROMDICTIONARY(annot,"Dest")
          table.insert(collected_destinations, outputENTRY_dest(destcountVAR,
               annotgoto,pagereftonumVAR,destnamestorefVAR,docVAR))
        else
          local annotaction = GETDICTIONARY(annot,"A")
          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
              destcountVAR=destcountVAR + 1
              WRITE ( outputKV_goto (destcountVAR) )
            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")
              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
  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}
