%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% pdfbase.sty
%
% driver independent access to low-level pdf features
%
% Copyright 2015--\today, Alexander Grahn
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Support package for media9 and ocgx2.
%
% This package implements commands for the creation of PDF Objects,
% Form XObjects, Image XObjects, annotations, links, marked content (BDC/EMC)
% and for manipulating the PDF catalog.
%
% Supported workflows:
%
%   pdflatex, lualatex
%   latex-->dvips-->ps2pdf or Distiller
%   latex-->dvipdfmx
%   xelatex
%
%
% Package options:
%
%   xetex,
%   dvipdfmx,
%   bigfiles (for embedding large files as stream objects; only relevant for
%     dvips mode, ignored otherwise)
%
% Commands defined:
%
%   \pbs_pdfobj:nnn
%     #1: predefined PDF object ID to be used for the current obj; may be empty
%     #2: type of object (dict|array|stream|fstream)
%     #3: content (dict [key-value], string, file); if #3 && #1 are both empty,
%         an object reference will be reserved for later use as #1
%
%   \pbs_pdflastobj:
%     inserts ID of PDF object created/processed during most recent call of
%     \pbs_pdfobj:nnn
%
%   --------
%
%   \pbs_pdfannot:nnnn
%     #1: width, #2: height, #3: depth, #4: dict (key-value)
%
%   \pbs_pdflastann:
%     inserts ID of PDF object created during most recent call of
%     \pbs_pdfannot:nnnn
%
%   --------
%
%   \pbs_pdflink:nn
%     #1: dict (key-value), #2: text
%
%   --------
%
%   \pbs_pdfxform:nn
%     #1: used as PDF annotation appearance ? (1|0)
%     #2: savebox number
%
%     creates PDF Form XObject from savebox content
%
%   \pbs_pdflastxform:
%     inserts ID of PDF Form XObject created during most recent call of
%     \pbs_pdfxform:nn
%
%   \pbs_pdfrefxform:n
%     #1: xform object ID
%     inserts the PDF Form XObject into the current content stream, that is,
%     typsets the PDF Form XObject
%
%   --------
%
%   \pbs_pdfximage:n
%     #1: bitmap file name
%
%   \pbs_pdflastximage:
%     inserts ID of PDF Image XObject created during most recent call of
%     \pbs_pdfximage:n
%
%   --------
%
%   \pbs_pdfcatalog:n
%     #1: dict (key-value)
%
%   --------
%
%   marked content BDC/EMC operators
%   \pbs_pdfbdc:nn           ...        \pbs_pdfemc:
%     #1: tag, #2: properties dict obj ID
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 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{l3regex}
\RequirePackage{l3keys2e}
\RequirePackage{atbegshi}
\RequirePackage{atenddvi}

\def\g@pbs@date@tl{2016/02/25}
\def\g@pbs@version@tl{0.5}

\ProvidesExplPackage{pdfbase}{\g@pbs@date@tl}{\g@pbs@version@tl}
{driver independent access to low-level PDF features}

%wrong image file type for Image XObject generation
\msg_gset:nnn{pdfbase}{wrong~image~resource}{
  Image~resource~file\\~~'#1'\\has~wrong~type.\\\\
  Driver~#2~only~accepts~files~of~type\\#3\\
  as~image~resources.
}

%pdfTeX version check
\sys_if_engine_pdftex:T{
  \int_compare:nT{\pdftexversion<130}{
    \msg_interrupt:nnn{pdfbase~error}{pdfTeX,~version~>=~1.30~required.}{}
  }
}

%package options

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

\bool_new:N\g_pbs_pkgbigfiles_bool
\bool_new:N\g_pbs_dvipdfmx_bool

\keys_define:nn{pdfbase}{
  xetex .bool_gset:N = \g_pbs_dvipdfmx_bool,
  dvipdfmx .bool_gset:N = \g_pbs_dvipdfmx_bool,

  bigfiles .choice:,
  bigfiles / true .code:n = {\bool_gset_true:N\g_pbs_pkgbigfiles_bool},
  bigfiles / false .code:n = {\bool_gset_false:N\g_pbs_pkgbigfiles_bool},
  bigfiles .default:n = {true},

  unknown .code:n = {
    \msg_error:nnx{pdfbase}{unknown~package~option}{\l_keys_key_tl}
  }
}
\ProcessKeysOptions{pdfbase}
\sys_if_engine_xetex:T{\bool_gset_true:N\g_pbs_dvipdfmx_bool}

%commands for creating PDF objects, annots etc.
\sys_if_output_pdf:TF{
  %helper func to remove `0 R' part from pdf obj reference
  \cs_new_nopar:Nn\pbs_reftonum:n{\_pbs_reftonum:f{#1}}
  %
  \cs_new_nopar:Nn\_pbs_reftonum:n{\exp_after:wN\_pbs_reftonum:w#1}
  \cs_generate_variant:Nn\_pbs_reftonum:n{f}
  \cs_new_nopar:Npn\_pbs_reftonum:w #1~0~R{#1}
  %
  \cs_new_nopar:Nn\pbs_pdfobj:nnn{
    \tl_clear:N\l_pbs_usenum_tl
    \tl_if_blank:oF{#1}{
      \tl_set:Nx\l_pbs_usenum_tl{useobjnum~\pbs_reftonum:n{#1}}
    }
    \bool_if:nTF{\tl_if_blank_p:o{#1}&&\tl_if_blank_p:o{#3}}{
      \pdfobj~reserveobjnum
    }{
      \str_case:nn{#2}{
        {dict}{\immediate\pdfobj~\l_pbs_usenum_tl~{<<#3>>}}
        {array}{\immediate\pdfobj~\l_pbs_usenum_tl~{[#3]}}
        {stream}{\immediate\pdfobj~\l_pbs_usenum_tl~stream~{#3}}
        {fstream}{\immediate\pdfobj~\l_pbs_usenum_tl~stream~file~{#3}}
      }
    }
    \tl_gset:Nx\g_pbs_pdflastobj_tl{\the\pdflastobj\space 0~R}
  }

  \cs_new_nopar:Nn\pbs_pdfannot:nnnn{
    \immediate\pdfannot~width~#1~height~#2~depth~#3 {#4}
    \tl_gset:Nx\g_pbs_pdflastann_tl{\int_use:c{pdflastannot}~0~R}
  }

  \cs_new:Nn\pbs_pdflink:nn{
    \immediate\pdfstartlink~user~{#1}#2\pdfendlink
  }

  \cs_new_nopar:Nn\pbs_pdfxform:nn{
    \int_compare:nT{#1>\c_zero}{\immediate}
    \pdfxform~resources~{\the\pdfpageresources}~#2
    \tl_gset:Nx\g_pbs_pdflastxform_tl{\int_use:c{pdflastxform}~0~R}
  }

  \cs_new:Nn\pbs_pdfrefxform:n{% #1: xform obj ID
    \begin{picture}(0,0)\put(0,0){\pdfrefxform\pbs_reftonum:n{#1}}\end{picture}
  }

  \cs_new_nopar:Nn\pbs_pdfximage:n{
    \filename@parse{#1}
    \tl_set:Nx\l_pbs_ext_tl{\tl_lower_case:n{\filename@ext}}
    \bool_if:nTF{
      \str_if_eq_p:Vn\l_pbs_ext_tl{png}
      ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpg}
      ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpeg}
      ||\str_if_eq_p:Vn\l_pbs_ext_tl{jbig2}
      ||\str_if_eq_p:Vn\l_pbs_ext_tl{jb2}
    }{
      \immediate\pdfximage{#1}
      \tl_gset:Nx\g_pbs_pdflastximage_tl{\int_use:c{pdflastximage}~0~R}
    }{
      \msg_error:nnnnn{pdfbase}{wrong~image~resource}{#1}{pdftex}{
        png,~jpeg~and~jbig2
      }
    }
  }

  \cs_new_nopar:Nn\pbs_pdfrefximage:n{% #1: xform obj ID
    \hbox_overlap_right:n{
      \vbox_to_zero:n{\vss\pdfrefximage\pbs_reftonum:n{#1}}
    }
  }

  \cs_new_nopar:Nn\pbs_pdfcatalog:n{\pdfcatalog{#1}}

  \int_new:N\g_pbs_oc_int % object ID for marked content Properties

  \cs_new_nopar:Nn\pbs_pdfbdc:nn{
    \pdfliteral~page~{#1/rm@oc\int_use:N\g_pbs_oc_int\space BDC}
    \tl_if_empty:NTF\g_pbs_ocmap_tl{
      \tl_gset:Nx\g_pbs_ocmap_tl{/rm@oc\int_use:N\g_pbs_oc_int\space#2}
    }{
      \tl_gput_right:Nx\g_pbs_ocmap_tl{~/rm@oc\int_use:N\g_pbs_oc_int\space#2}
    }
    \tl_set:Nx\l_pbs_pdfpageresources_tl{\the\pdfpageresources}
    \regex_replace_all:NnN\l_pbs_regex{}\l_pbs_pdfpageresources_tl
    \group_begin:
    \tl_set:Nx\l_pbs_temp_tl{\group_end:
      \global\pdfpageresources{
        \l_pbs_pdfpageresources_tl
        /Properties<<\g_pbs_ocmap_tl>>
      }
    }\l_pbs_temp_tl
    \int_gincr:N\g_pbs_oc_int
  }
  \tl_new:N\g_pbs_ocmap_tl
  \regex_const:Nn\l_pbs_regex{\/Properties<<(?:\s*\/rm@oc[^R]+R\s*)+>>}

  \cs_new_nopar:Nn\pbs_pdfemc:{\pdfliteral~page~{EMC}}

  %modify output routine to purge \pdfpageresources from OCG properties
  \AtBeginShipout{\tl_gclear:N\g_pbs_ocmap_tl}
  \tl_gset:Nx\g_pbs_output_tl{\the\output}
  \global\output{
    \g_pbs_output_tl
    \tl_if_blank:VT\g_pbs_ocmap_tl{
      \tl_set:Nx\l_pbs_pdfpageresources_tl{\the\pdfpageresources}
      \regex_replace_all:NnN\l_pbs_regex{}\l_pbs_pdfpageresources_tl
      \group_begin:
      \tl_set:Nx\l_pbs_temp_tl{\group_end:
        \global\pdfpageresources{\l_pbs_pdfpageresources_tl}
      }\l_pbs_temp_tl
    }
  }
}{
  %pgf + transparency related settings
  \bool_new:N\g_pbs_pgfloaded_bool
  \bool_gset_false:N\g_pbs_pgfloaded_bool
  \AtBeginDocument{
    \@ifpackageloaded{pgf}{\bool_gset_true:N\g_pbs_pgfloaded_bool}{}
  }
  \int_new:N\g_pbs_obj_int % object ID
  \bool_if:NTF\g_pbs_dvipdfmx_bool{ %dvipdfmx/XeTeX
    \cs_new_nopar:Nn\pbs_pdfobj:nnn{
      \tl_if_blank:oTF{#1}{
        \tl_set:Nx\l_pbs_usenum_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
        \int_gincr:N\g_pbs_obj_int
      }{
        \tl_set:Nx\l_pbs_usenum_tl{#1}
      }
      \tl_if_blank:oF{#3}{
        \str_case:nn{#2}{
          {dict}{\special{pdf:obj~\l_pbs_usenum_tl\space<<#3>>}}
          {array}{\special{pdf:obj~\l_pbs_usenum_tl\space[#3]}}
          {stream}{\special{pdf:stream~\l_pbs_usenum_tl\space(#3)}}
          {fstream}{
            \message{<#3>}
            \special{pdf:fstream~\l_pbs_usenum_tl\space(#3)}
          }
        }
      }
      \tl_gset_eq:NN\g_pbs_pdflastobj_tl\l_pbs_usenum_tl
    }

    \cs_new_nopar:Nn\pbs_pdfannot:nnnn{
      \special{pdf:ann~@pbs@obj\int_use:N\g_pbs_obj_int\space
        width~#1\space
        height~#2\space
        depth~#3~<<#4>>
      }
      \tl_gset:Nx\g_pbs_pdflastann_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
      \int_gincr:N\g_pbs_obj_int
    }

    \cs_new:Nn\pbs_pdflink:nn{
      \special{pdf:bann~<<#1>>}#2\special{pdf:eann}
    }

    \cs_new_nopar:Nn\pbs_pdfxform:nn{
      \begin{picture}(0,0)
        \put(0,0){
          \special{pdf:bxobj~@pbs@obj\int_use:N\g_pbs_obj_int\space
            width\space \dim_use:N\box_wd:N#2\space
            height\space\dim_use:N\box_ht:N#2\space
            depth\space \dim_use:N\box_dp:N#2
          }
          \box_use:N#2
          \bool_if:NT\g_pbs_pgfloaded_bool{ % transparency et al. for PGF
            \tl_set:Nn\l_pbs_temp_tl{}
            \ifpgf@sys@pdf@extgs@exists
              \tl_set:Nn\l_pbs_temp_tl{/ExtGState~@pgfextgs}
            \fi
            \ifpgf@sys@pdf@patterns@exists
              \tl_put_right:Nn\l_pbs_temp_tl{/Pattern~@pgfpatterns}
            \fi
            \ifpgf@sys@pdf@colorspaces@exists
              \tl_put_right:Nn\l_pbs_temp_tl{/ColorSpace~@pgfcolorspaces}
            \fi
            \tl_if_blank:VF\l_pbs_temp_tl{
              \special{pdf:put~@resources~<<\l_pbs_temp_tl>>}
            }
          }
          \special{pdf:exobj}
        }
      \end{picture}
      \tl_gset:Nx\g_pbs_pdflastxform_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
      \int_gincr:N\g_pbs_obj_int
    }

    \cs_new_nopar:Nn\pbs_pdfrefxform:n{\special{pdf:uxobj~#1}}% #1: xform obj ID

    \cs_new_nopar:Nn\pbs_pdfximage:n{
      \filename@parse{#1}
      \tl_set:Nx\l_pbs_ext_tl{\tl_lower_case:n{\filename@ext}}
      \bool_if:nTF{
          \str_if_eq_p:Vn\l_pbs_ext_tl{png}
        ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpg}
        ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpeg}
      }{
        \special{pdf:image~@pbs@obj\int_use:N\g_pbs_obj_int\space
          %move initial display off the page
          matrix~0.000001~0~0~0.000001~-1000000~-1000000~(#1)}
        \tl_gset:Nx\g_pbs_pdflastximage_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
        \int_gincr:N\g_pbs_obj_int
      }{
        \msg_error:nnnnn{pdfbase}{wrong~image~resource}{#1}{dvipdfmx/xetex}{
          png~and~jpeg
        }
      }
    }

    \cs_gset_eq:NN\pbs_pdfrefximage:n\pbs_pdfrefxform:n

    \cs_new_nopar:Nn\pbs_pdfcatalog:n{\special{pdf:put~@catalog~<<#1>>}}

    \int_new:N\g_pbs_oc_int % object ID
    \cs_new_nopar:Nn\pbs_pdfbdc:nn{
      \special{pdf:code~#1/rm@oc\int_use:N\g_pbs_oc_int\space BDC}
      \special{pdf:put~@resources~<<
        /Properties~<</rm@oc\int_use:N\g_pbs_oc_int\space#2>>>>}
      \int_gincr:N\g_pbs_oc_int
    }
    \cs_new_nopar:Nn\pbs_pdfemc:{\special{pdf:~code~EMC}}
  }{
    % dvips
    \bool_new:N\g_pbs_powerdot_bool %to cope with some powerdot oddity
    \@ifclassloaded{powerdot}{\bool_gset_true:N\g_pbs_powerdot_bool}{}

    \sys_if_engine_pdftex:TF{
      \cs_new_nopar:Nn\pbs_filesize:n{\pdffilesize{#1}}
      \cs_new_nopar:Nn\pbs_filedump:nnn{\pdffiledump~offset~#1~length~#2~{#3}}
    }{
      \sys_if_engine_luatex:T{
        \RequirePackage{pdftexcmds}
        \cs_new_nopar:Nn\pbs_filesize:n{\pdf@filesize{#1}}
        \cs_new_nopar:Nn\pbs_filedump:nnn{\pdf@filedump{#1}{#2}{#3}}
      }
    }

    \@ifpackageloaded{hyperref}{}{
      %define `?pdfmark' operator as in file hdvips.def from package `hyperref'
      \special{!~
        systemdict~/pdfmark~known
        {
          userdict~/?pdfmark~systemdict~/exec~get~put
        }{
          userdict~/?pdfmark~systemdict~/pop~get~put~
          userdict~/pdfmark~systemdict~/cleartomark~get~put
        }
        ifelse~
      }
    }

    \bool_if:NT\g_pbs_pkgbigfiles_bool{
      \special{psfile=\jobname.pbsdat}
      %open auxiliary file \jobname.pbsdat for writing hex encoded streams of the
      %files to be embedded. This file is inserted into PS during dvips run
      \iow_new:N\g_pbs_mstreams_stream
      \iow_open:Nn\g_pbs_mstreams_stream{\jobname.pbsdat}
      \iow_now:Nn\g_pbs_mstreams_stream{
        /M9D~1~dict~def~M9D~begin
        /o{mark/_objdef}bind~def/O{/type/stream/OBJ~pdfmark}bind~def
        /m~systemdict/mark~get~def
        /P{/ASCIIHexDecode~filter/PUT~pdfmark}bind~def
        /C{/CLOSE~pdfmark}bind~def~end
      }
    }

    \cs_new_nopar:Nn\pbs_pdfobj:nnn{
      \tl_clear:N\l_pbs_usenum_tl
      \tl_if_blank:oTF{#1}{
        \tl_set:Nx\l_pbs_usenum_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
        \int_gincr:N\g_pbs_obj_int
      }{
        \tl_set:Nx\l_pbs_usenum_tl{#1}
      }
      \tl_if_blank:oF{#3}{
        \bool_if:nTF{
          \g_pbs_pkgbigfiles_bool &&
          \str_if_eq_p:nn{#2}{fstream}
        }{
          \iow_now:Nx\g_pbs_mstreams_stream{
            M9D~begin~o\l_pbs_usenum_tl O
          }
        }{
          \special{ps:~mark~/_objdef~\l_pbs_usenum_tl\space/type
            \str_case:nn{#2}{
              {dict}{/dict}
              {array}{/array}
              {stream}{/stream}
              {fstream}{/stream}
            }~
            /OBJ~pdfmark~
          }
        }
        \str_case:nn{#2}{
          {dict}{\special{ps:~mark~\l_pbs_usenum_tl~<<#3>>/PUT~pdfmark~}}
          {array}{
            \special{ps:~mark~\l_pbs_usenum_tl~0~[#3]/PUTINTERVAL~pdfmark~}
          }
          {stream}{\special{ps::[nobreak]~mark~\l_pbs_usenum_tl~(#3)/PUT~pdfmark~}}
          {fstream}{
            \tl_set:Nn\l_pbs_offset_tl{0}
            \tl_set:Nx\l_pbs_fsize_tl{\pbs_filesize:n{#3}}
            \message{<#3}
            %embed file in chunks of 32768 Bytes into PS as chunks of
            %65536 Bytes of HEX code
            \bool_while_do:nn{
              \int_compare_p:n{\l_pbs_offset_tl<\l_pbs_fsize_tl}
            }{
              \bool_if:NTF\g_pbs_pkgbigfiles_bool{
                \iow_now:Nx\g_pbs_mstreams_stream{
                  m\l_pbs_usenum_tl
                  (\pbs_filedump:nnn{\l_pbs_offset_tl}{32767}{#3})P
                }
              }{
                \special{ps:~
                  mark~
                    \l_pbs_usenum_tl~
                    (\pbs_filedump:nnn{\l_pbs_offset_tl}{32767}{#3})~
                    /ASCIIHexDecode~filter~/PUT~
                  pdfmark~
                }
              }
              \tl_set:Nx\l_pbs_offset_tl{\int_eval:n{\l_pbs_offset_tl+32767}}
              \message{.}
            }
            \message{>}
            \bool_if:NTF\g_pbs_pkgbigfiles_bool{
              \iow_now:Nx\g_pbs_mstreams_stream{
                m\l_pbs_usenum_tl~C~end
              }
            }{
              \special{ps:~
                mark~\l_pbs_usenum_tl~/CLOSE~pdfmark~
              }
            }
          }
        }
      }
      \tl_gset_eq:NN\g_pbs_pdflastobj_tl\l_pbs_usenum_tl
    }

    \cs_new_nopar:Nn\pbs_pdfannot:nnnn{
      \hbox_set:Nn\l_tmpa_box{
        \box_move_down:nn{#3}{
          \hbox_to_zero:n{
            \special{ps:~currentpoint~/pbs@lly~exch~def~/pbs@llx~exch~def}
          }
        }
        \skip_horizontal:n{#1}
        \box_move_up:nn{#2}{
          \hbox_to_zero:n{
            \special{ps:~currentpoint~/pbs@ury~exch~def~/pbs@urx~exch~def}
          }
        }
      }
      \box_set_wd:Nn\l_tmpa_box{\c_zero_dim}
      \box_set_ht:Nn\l_tmpa_box{\c_zero_dim}
      \box_set_dp:Nn\l_tmpa_box{\c_zero_dim}
      \box_use_clear:N\l_tmpa_box
      \special{ps:~
        mark~
          /_objdef~{pbs@obj\int_use:N\g_pbs_obj_int}
          /Rect~[pbs@llx~pbs@lly~pbs@urx~pbs@ury]
          #4
        /ANN~pdfmark~
      }
      \tl_gset:Nx\g_pbs_pdflastann_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
      \int_gincr:N\g_pbs_obj_int
    }

    \cs_new:Nn\pbs_pdflink:nn{
      \cs_if_exist:NTF\pdfmark{
        \pdfmark[#2]{pdfmark=/ANN,Raw={#1}}
      }{
        \hbox_set:Nn\l_tmpb_box{#2}
        \pbs_pdfannot:nnnn{
          \dim_use:N\box_wd:N\l_tmpb_box}{
          \dim_use:N\box_ht:N\l_tmpb_box}{
          \dim_use:N\box_dp:N\l_tmpb_box
        }{#1}
        \box_use_clear:N\l_tmpb_box
      }
    }

    \cs_new_nopar:Nn\pbs_pdfxform:nn{
      %mark bbox of box#2
      \group_begin:
        \dim_set:Nn\unitlength{1pt}
        \begin{picture}(0,0)
        %mark BBox of the graphics
        \put(0,-\dim_to_decimal:n{\box_dp:N#2}){
          \special{ps:
            currentpoint~/pbs@lly~exch~def~/pbs@llx~exch~def~
          }
        }
        \put(
          \dim_to_decimal:n{\box_wd:N#2},
          \dim_to_decimal:n{\box_ht:N#2}
        ){
          \special{ps:
            currentpoint~/pbs@ury~exch~def~/pbs@urx~exch~def~
          }
        }
        %define some length values in current PS coordinate units
        \put(0,0){
          \special{ps:
            currentpoint~/origin@y~exch~def~/origin@x~exch~def~
          }
        }
        \dim_set:Nn\unitlength{1in}
        \put(1,1){
          \special{ps:
            currentpoint~origin@y~exch~sub~/one@inch@y~exch~def~
            origin@x~sub~/one@inch@x~exch~def~
          }
        }
        \end{picture}
      \group_end:
      \tl_gset:cx{origin:{pbs@obj\int_use:N\g_pbs_obj_int}}{
        origin@pbs@\int_use:N\g_pbs_obj_int
      }
      \special{ps:~
        gsave~
        %keep a record of lower left corner coordinates
        true~setglobal~globaldict~
        /origin@pbs@\int_use:N\g_pbs_obj_int~[currentpoint]~cvx~put~
        false~setglobal~
        %translate graphics to upper left page corner
        \bool_if:NF\g_pbs_powerdot_bool{
          {
            pbs@llx~neg~pbs@ury~neg~translate~
            one@inch@x~DVImag~div~neg~one@inch@y~DVImag~div~neg~translate
          }?pdfmark~
        }
        %distill graphics into XObject
        mark~
          /_objdef~{pbs@obj\int_use:N\g_pbs_obj_int}
          /BBox~[pbs@llx~pbs@lly~pbs@urx~pbs@ury]
        /BP~pdfmark~
        % operations needed if used as annotation appearance
        \int_compare:nT{#1>\c_zero}{
          {
            isls{%landscape mode (powerdot, geometry /w landscape option)
              /pbs@dxdy~{
                pbs@urx~pbs@llx~sub~abs~
                pbs@ury~pbs@lly~sub~abs~div
              }~bind~def
              /pbs@dydx~{1~pbs@dxdy~div}~bind~def
              /pbs@cx~{pbs@urx~pbs@llx~add~2~div}~bind~def
              /pbs@cy~{pbs@ury~pbs@lly~add~2~div}~bind~def
              % graphics needs to be rescaled for some reason ...
              [pbs@dxdy~0~0~pbs@dydx~1~pbs@dxdy~sub~pbs@cx~mul~
                1~pbs@dydx~sub~pbs@cy~mul]~concat
              %... rotated by 270 degrees ...
              [0~-1~1~0~pbs@cx~pbs@cy~sub~pbs@cx~pbs@cy~add]~concat
              %... and flipped around vertical axis
              [-1~0~0~1~pbs@llx~pbs@urx~add~0]~concat
            }{% flip around horizontal axis in portrait mode
              [1~0~0~-1~0~pbs@lly~pbs@ury~add]~concat
            }ifelse
          }?pdfmark~
        }
      }
      \begin{picture}(0,0)\put(0,0){\box_use:N#2}\end{picture}
      \special{ps:~mark~/EP~pdfmark~grestore}
      \tl_gset:Nx\g_pbs_pdflastxform_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
      \int_gincr:N\g_pbs_obj_int
    }

    \cs_new_nopar:Nn\pbs_pdfrefxform:n{% #1: xform obj ID
      \special{ps:~
        gsave~
        currentpoint~pop~\tl_use:c{origin:#1}~pop~sub~
        currentpoint~exch~pop~\tl_use:c{origin:#1}~exch~pop~sub~translate~
        mark~#1~/SP~pdfmark~
        grestore~
      }
    }

    \cs_new_nopar:Nn\pbs_pdfximage:n{
      \filename@parse{#1}
      \tl_set:Nx\l_pbs_ext_tl{\tl_lower_case:n{\filename@ext}}
      \bool_if:nTF{
          \str_if_eq_p:Vn\l_pbs_ext_tl{ps}
        ||\str_if_eq_p:Vn\l_pbs_ext_tl{eps}
      }{
        \special{ps:~
          mark~/_objdef~{pbs@obj\int_use:N\g_pbs_obj_int}~/NI~pdfmark~
        }
        \special{psfile=#1~hsize=0~vsize=0}
        \special{ps:~
          {
            0~0~1~[1~0~0~1~0~0]~{}~image~%empty dummy, in case #1 is not
          }?pdfmark~                     %a valid raster image file
        }
        \tl_gset:Nx\g_pbs_pdflastximage_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
        \int_gincr:N\g_pbs_obj_int
      }{
        \msg_error:nnxxx{pdfbase}{wrong~image~resource}{#1}{dvips}{
          Postscript~(ps/eps)~with~bitmapped~content
        }
      }
    }

    \cs_new_nopar:Nn\pbs_pdfrefximage:n{% #1: ximage obj ID
      %not implemented; how to insert an image XObject with pdfmarks?
      %\special{ps:~
      %  gsave~
      %  mark~#1~/SP~pdfmark~
      %  grestore~
      %}
    }

    \cs_new_nopar:Nn\pbs_pdfcatalog:n{
      \special{ps:~mark~{Catalog}~<<#1>>~/PUT~pdfmark}
    }

    %marked content BDC/EMC operators
    %require Ghostscript v. >= 9.15
    \cs_new_nopar:Nn\pbs_pdfbdc:nn{
      \tl_if_exist:NF\g_pbs_gsoldwarned_tl{
        \tl_new:N\g_pbs_gsoldwarned_tl
        \AtEndDvi{\special{ps::[nobreak]\pbs_gsoldwarning:}}
      }
      \special{ps:~mark~#1~#2~/BDC~pdfmark}
    }
    \cs_new_nopar:Nn\pbs_pdfemc:{\special{ps:~mark~/EMC~pdfmark}}
  }
}
\cs_new_nopar:Nn\pbs_pdflastobj:{\g_pbs_pdflastobj_tl}
\cs_new_nopar:Nn\pbs_pdflastann:{\g_pbs_pdflastann_tl}
\cs_new_nopar:Nn\pbs_pdflastxform:{\g_pbs_pdflastxform_tl}
\cs_new_nopar:Nn\pbs_pdflastximage:{\g_pbs_pdflastximage_tl}

\group_begin:
\char_set_catcode_active:N\+\let+\space
\cs_new_nopar:Nx\pbs_gsoldwarning:{
  {product~(Ghostscript)~search~{pop~pop~pop~true}{pop~false}ifelse~
   revision~915~lt~and~{
  (\token_to_str:N\n
  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n
  @@++++++++++Warning:+Ghostscript+too+old!+++++++++++@@\token_to_str:N\n
  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n
  @@++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
  @@+Ghostscript+version+>=+9.15.+required!+++++++++++@@\token_to_str:N\n
  @@+PDF+features+such+as+Layers+(OCGs)+may+not+work.+@@\token_to_str:N\n
  @@++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
  @@+Get+current+version+from+++++++++++++++++++++++++@@\token_to_str:N\n
  @@+http://www.ghostscript.com/download++++++++++++++@@\token_to_str:N\n
  @@++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n)
  print}~if}~?pdfmark
}
\group_end:

